CORDA-2520: Add FetchParametersFlow (#4674)

* CORDA-2520: Add FetchParametersFlow

* Address comments, add test
This commit is contained in:
Katarzyna Streich 2019-01-29 18:16:37 +00:00 committed by GitHub
parent 88e4b85537
commit 6efd54fce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 246 additions and 44 deletions

View File

@ -8,12 +8,9 @@ import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.BackpressureAwareTimedFlow import net.corda.core.internal.*
import net.corda.core.internal.FetchDataFlow
import net.corda.core.internal.NetworkParametersServiceInternal
import net.corda.core.internal.notary.generateSignature import net.corda.core.internal.notary.generateSignature
import net.corda.core.internal.notary.validateSignatures import net.corda.core.internal.notary.validateSignatures
import net.corda.core.internal.pushToLoggingContext
import net.corda.core.transactions.* import net.corda.core.transactions.*
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
@ -107,7 +104,7 @@ class NotaryFlow {
check(stx.coreTransaction is NotaryChangeWireTransaction) { check(stx.coreTransaction is NotaryChangeWireTransaction) {
"Notary $notaryParty is not on the network parameter whitelist. A non-whitelisted notary can only be used for notary change transactions" "Notary $notaryParty is not on the network parameter whitelist. A non-whitelisted notary can only be used for notary change transactions"
} }
val historicNotary = (serviceHub.networkParametersService as NetworkParametersServiceInternal).getHistoricNotary(notaryParty) val historicNotary = (serviceHub.networkParametersService as NetworkParametersStorage).getHistoricNotary(notaryParty)
?: throw IllegalStateException("The notary party $notaryParty specified by transaction ${stx.id}, is not recognised as a current or historic notary.") ?: throw IllegalStateException("The notary party $notaryParty specified by transaction ${stx.id}, is not recognised as a current or historic notary.")
historicNotary.validating historicNotary.validating

View File

@ -3,9 +3,7 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.*
import net.corda.core.internal.RetrieveAnyTransactionPayload
import net.corda.core.internal.readFully
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
@ -42,7 +40,7 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any)
@Suspendable @Suspendable
override fun call(): Void? { override fun call(): Void? {
// The first payload will be the transaction data, subsequent payload will be the transaction/attachment data. // The first payload will be the transaction data, subsequent payload will be the transaction/attachment/network parameters data.
var payload = payload var payload = payload
// Depending on who called this flow, the type of the initial payload is different. // Depending on who called this flow, the type of the initial payload is different.
@ -93,6 +91,10 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any)
serviceHub.attachments.openAttachment(it)?.open()?.readFully() serviceHub.attachments.openAttachment(it)?.open()?.readFully()
?: throw FetchDataFlow.HashNotFound(it) ?: throw FetchDataFlow.HashNotFound(it)
} }
FetchDataFlow.DataType.PARAMETERS -> dataRequest.hashes.map {
(serviceHub.networkParametersService as NetworkParametersStorage).lookupSigned(it)
?: throw FetchDataFlow.MissingNetworkParameters(it)
}
} }
} }
} }

View File

@ -1,6 +1,8 @@
package net.corda.core.internal package net.corda.core.internal
import net.corda.core.contracts.NamedByHash
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.crypto.verify import net.corda.core.crypto.verify
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -43,7 +45,9 @@ Cert path: $fullCertPath
/** Similar to [SignedData] but instead of just attaching the public key, the certificate for the key is attached instead. */ /** Similar to [SignedData] but instead of just attaching the public key, the certificate for the key is attached instead. */
@CordaSerializable @CordaSerializable
class SignedDataWithCert<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSignatureWithCert) { class SignedDataWithCert<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSignatureWithCert): NamedByHash {
override val id: SecureHash get () = raw.hash
fun verified(): T { fun verified(): T {
sig.verify(raw) sig.verify(raw)
return uncheckedCast(raw.deserialize<Any>()) return uncheckedCast(raw.deserialize<Any>())

View File

@ -10,6 +10,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession import net.corda.core.flows.FlowSession
import net.corda.core.internal.FetchDataFlow.DownloadedVsRequestedDataMismatch import net.corda.core.internal.FetchDataFlow.DownloadedVsRequestedDataMismatch
import net.corda.core.internal.FetchDataFlow.HashNotFound import net.corda.core.internal.FetchDataFlow.HashNotFound
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationToken import net.corda.core.serialization.SerializationToken
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
@ -51,6 +52,8 @@ sealed class FetchDataFlow<T : NamedByHash, in W : Any>(
class HashNotFound(val requested: SecureHash) : FlowException() class HashNotFound(val requested: SecureHash) : FlowException()
class MissingNetworkParameters(val requested: SecureHash) : FlowException("Failed to fetch network parameters with hash: $requested")
class IllegalTransactionRequest(val requested: SecureHash) : FlowException("Illegal attempt to request a transaction (${requested}) that is not in the transitive dependency graph of the sent transaction.") class IllegalTransactionRequest(val requested: SecureHash) : FlowException("Illegal attempt to request a transaction (${requested}) that is not in the transitive dependency graph of the sent transaction.")
@CordaSerializable @CordaSerializable
@ -64,11 +67,11 @@ sealed class FetchDataFlow<T : NamedByHash, in W : Any>(
@CordaSerializable @CordaSerializable
enum class DataType { enum class DataType {
TRANSACTION, ATTACHMENT TRANSACTION, ATTACHMENT, PARAMETERS
} }
@Suspendable @Suspendable
@Throws(HashNotFound::class) @Throws(HashNotFound::class, MissingNetworkParameters::class)
override fun call(): Result<T> { override fun call(): Result<T> {
// Load the items we have from disk and figure out which we're missing. // Load the items we have from disk and figure out which we're missing.
val (fromDisk, toFetch) = loadWhatWeHave() val (fromDisk, toFetch) = loadWhatWeHave()
@ -139,7 +142,7 @@ sealed class FetchDataFlow<T : NamedByHash, in W : Any>(
} }
/** /**
* Given a set of hashes either loads from from local storage or requests them from the other peer. Downloaded * Given a set of hashes either loads from local storage or requests them from the other peer. Downloaded
* attachments are saved to local storage automatically. * attachments are saved to local storage automatically.
*/ */
class FetchAttachmentsFlow(requests: Set<SecureHash>, class FetchAttachmentsFlow(requests: Set<SecureHash>,
@ -158,10 +161,10 @@ class FetchAttachmentsFlow(requests: Set<SecureHash>,
} catch (e: FileAlreadyExistsException) { } catch (e: FileAlreadyExistsException) {
// This can happen when another transaction will insert the same attachment during this transaction. // This can happen when another transaction will insert the same attachment during this transaction.
// The outcome is the same (the attachment is imported), so we can ignore this exception. // The outcome is the same (the attachment is imported), so we can ignore this exception.
logger.debug("Attachment ${attachment.id} already inserted.") logger.debug { "Attachment ${attachment.id} already inserted." }
} }
} else { } else {
logger.debug("Attachment ${attachment.id} already exists, skipping.") logger.debug { "Attachment ${attachment.id} already exists, skipping." }
} }
} }
} }
@ -193,3 +196,28 @@ class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: FlowSession) :
override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid) override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid)
} }
/**
* Given a set of hashes either loads from local network parameters storage or requests them from the other peer. Downloaded
* network parameters are saved to local parameters storage automatically. This flow can be used only if the minimumPlatformVersion is >= 4.
* Nodes on lower versions won't respond to this flow.
*/
class FetchNetworkParametersFlow(requests: Set<SecureHash>,
otherSide: FlowSession) : FetchDataFlow<SignedDataWithCert<NetworkParameters>, SignedDataWithCert<NetworkParameters>>(requests, otherSide, DataType.PARAMETERS) {
override fun load(txid: SecureHash): SignedDataWithCert<NetworkParameters>? {
return (serviceHub.networkParametersService as NetworkParametersStorage).lookupSigned(txid)
}
override fun maybeWriteToDisk(downloaded: List<SignedDataWithCert<NetworkParameters>>) {
for (parameters in downloaded) {
with(serviceHub.networkParametersService as NetworkParametersStorage) {
if (!hasParameters(parameters.id)) {
// This will perform the signature check too and throws SignatureVerificationException
saveParameters(parameters)
} else {
logger.debug { "Network parameters ${parameters.id} already exists in storage, skipping." }
}
}
}
}
}

View File

@ -1,12 +1,44 @@
package net.corda.core.internal package net.corda.core.internal
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.NetworkParametersService import net.corda.core.node.services.NetworkParametersService
import java.security.cert.X509Certificate
interface NetworkParametersStorage : NetworkParametersService {
/**
* Return parameters epoch for the given parameters hash. Null if there are no parameters for this hash in the storage and we are unable to
* get them from network map.
*/
fun getEpochFromHash(hash: SecureHash): Int?
/**
* Return signed network parameters with certificate for the given hash. Null if there are no parameters for this hash in the storage.
* (No fallback to network map.)
*/
fun lookupSigned(hash: SecureHash): SignedDataWithCert<NetworkParameters>?
/**
* Checks if parameters with given hash are in the storage.
*/
fun hasParameters(hash: SecureHash): Boolean
interface NetworkParametersServiceInternal : NetworkParametersService {
/** /**
* Returns the [NotaryInfo] for a notary [party] in the current or any historic network parameter whitelist, or null if not found. * Returns the [NotaryInfo] for a notary [party] in the current or any historic network parameter whitelist, or null if not found.
*/ */
fun getHistoricNotary(party: Party): NotaryInfo? fun getHistoricNotary(party: Party): NotaryInfo?
/**
* Save signed network parameters data. Internally network parameters bytes should be stored with the signature.
* It's because of ability of older nodes to function in network where parameters were extended with new fields.
* Hash should always be calculated over the serialized bytes.
*/
fun saveParameters(signedNetworkParameters: SignedDataWithCert<NetworkParameters>)
/**
* Set information that given parameters are current parameters for the network.
*/
fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate)
} }

View File

@ -77,6 +77,8 @@ class ResolveTransactionsFlow(txHashesArg: Set<SecureHash>,
@Suspendable @Suspendable
@Throws(FetchDataFlow.HashNotFound::class, FetchDataFlow.IllegalTransactionRequest::class) @Throws(FetchDataFlow.HashNotFound::class, FetchDataFlow.IllegalTransactionRequest::class)
override fun call() { override fun call() {
val counterpartyPlatformVersion = serviceHub.networkMapCache.getNodeByLegalIdentity(otherSide.counterparty)?.platformVersion ?:
throw FlowException("Couldn't retrieve party's ${otherSide.counterparty} platform version from NetworkMapCache")
val newTxns = ArrayList<SignedTransaction>(txHashes.size) val newTxns = ArrayList<SignedTransaction>(txHashes.size)
// Start fetching data. // Start fetching data.
for (pageNumber in 0..(txHashes.size - 1) / RESOLUTION_PAGE_SIZE) { for (pageNumber in 0..(txHashes.size - 1) / RESOLUTION_PAGE_SIZE) {
@ -85,6 +87,11 @@ class ResolveTransactionsFlow(txHashesArg: Set<SecureHash>,
newTxns += downloadDependencies(page) newTxns += downloadDependencies(page)
val txsWithMissingAttachments = if (pageNumber == 0) signedTransaction?.let { newTxns + it } ?: newTxns else newTxns val txsWithMissingAttachments = if (pageNumber == 0) signedTransaction?.let { newTxns + it } ?: newTxns else newTxns
fetchMissingAttachments(txsWithMissingAttachments) fetchMissingAttachments(txsWithMissingAttachments)
// Fetch missing parameters flow was added in version 4. This check is needed so we don't end up with node V4 sending parameters
// request to node V3 that doesn't know about this protocol.
if (counterpartyPlatformVersion >= 4) {
fetchMissingParameters(txsWithMissingAttachments)
}
} }
otherSide.send(FetchDataFlow.Request.End) otherSide.send(FetchDataFlow.Request.End)
// Finish fetching data. // Finish fetching data.
@ -180,4 +187,13 @@ class ResolveTransactionsFlow(txHashesArg: Set<SecureHash>,
if (missingAttachments.isNotEmpty()) if (missingAttachments.isNotEmpty())
subFlow(FetchAttachmentsFlow(missingAttachments.toSet(), otherSide)) subFlow(FetchAttachmentsFlow(missingAttachments.toSet(), otherSide))
} }
// TODO This can also be done in parallel. See comment to [fetchMissingAttachments] above.
@Suspendable
private fun fetchMissingParameters(downloads: List<SignedTransaction>) {
val parameters = downloads.mapNotNull { it.networkParametersHash }
val missingParameters = parameters.filter { !(serviceHub.networkParametersService as NetworkParametersStorage).hasParameters(it) }
if (missingParameters.isNotEmpty())
subFlow(FetchNetworkParametersFlow(missingParameters.toSet(), otherSide))
}
} }

View File

@ -0,0 +1,132 @@
package net.corda.core.internal
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.SerializationFactory
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.cordappForClasses
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
class NetworkParametersResolutionTest {
private lateinit var params2: NetworkParameters
private val certKeyPair: CertificateAndKeyPair = createDevNetworkMapCa()
private lateinit var mockNet: MockNetwork
private lateinit var notaryNode: StartedMockNode
private lateinit var megaCorpNode: StartedMockNode
private lateinit var miniCorpNode: StartedMockNode
private lateinit var megaCorpParty: Party
private lateinit var miniCorpParty: Party
private lateinit var notaryParty: Party
@Before
fun setup() {
mockNet = MockNetwork(MockNetworkParameters(
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, cordappForClasses(ResolveTransactionsFlowTest.TestFlow::class.java, ResolveTransactionsFlowTest.TestResponseFlow::class.java))))
notaryNode = mockNet.defaultNotaryNode
megaCorpNode = mockNet.createPartyNode(CordaX500Name("MegaCorp", "London", "GB"))
miniCorpNode = mockNet.createPartyNode(CordaX500Name("MiniCorp", "London", "GB"))
notaryParty = mockNet.defaultNotaryIdentity
megaCorpParty = megaCorpNode.info.singleIdentity()
miniCorpParty = miniCorpNode.info.singleIdentity()
params2 = testNetworkParameters(epoch = 2, minimumPlatformVersion = 3, notaries = listOf((NotaryInfo(notaryParty, true))))
}
@After
fun tearDown() {
mockNet.stopNodes()
}
// This function is resolving and signing WireTransaction with special parameters.
private fun TransactionBuilder.toSignedTransactionWithParameters(parameters: NetworkParameters?, services: ServiceHub): SignedTransaction {
val wtx = toWireTransaction(services)
val wtxWithHash = SerializationFactory.defaultFactory.withCurrentContext(null) {
WireTransaction(
createComponentGroups(
wtx.inputs,
wtx.outputs,
wtx.commands,
wtx.attachments,
wtx.notary,
wtx.timeWindow,
wtx.references,
parameters?.serialize()?.hash),
wtx.privacySalt
)
}
val publicKey = services.myInfo.singleIdentity().owningKey
val signatureMetadata = SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(publicKey).schemeNumberID)
val signableData = SignableData(wtxWithHash.id, signatureMetadata)
val sig = services.keyManagementService.sign(signableData, publicKey)
return SignedTransaction(wtxWithHash, listOf(sig))
}
// Similar to ResolveTransactionsFlowTest but creates transactions with given network parameters
// First transaction in pair is dependency of the second one.
private fun makeTransactions(parameters1: NetworkParameters?, parameters2: NetworkParameters?): Pair<SignedTransaction, SignedTransaction> {
// Make a chain of custody of dummy states and insert into node A.
val dummy1: SignedTransaction = DummyContract.generateInitial(0, notaryParty, megaCorpParty.ref(1)).let {
val ptx = it.toSignedTransactionWithParameters(parameters1, megaCorpNode.services)
notaryNode.services.addSignature(ptx, notaryParty.owningKey)
}
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), miniCorpParty).let {
val ptx = it.toSignedTransactionWithParameters(parameters2, megaCorpNode.services)
notaryNode.services.addSignature(ptx, notaryParty.owningKey)
}
megaCorpNode.transaction {
megaCorpNode.services.recordTransactions(dummy1, dummy2)
// Record parameters too.
with(megaCorpNode.services.networkParametersService as NetworkParametersStorage) {
parameters1?.let { saveParameters(certKeyPair.sign(it)) }
parameters2?.let { saveParameters(certKeyPair.sign(it)) }
}
}
return Pair(dummy1, dummy2)
}
@Test
fun `request parameters that are not in the storage`() {
val params1 = miniCorpNode.services.networkParameters
val hash1 = params1.serialize().hash
val hash2 = params2.serialize().hash
// Create two transactions on megaCorpNode
val (stx1, stx2) = makeTransactions(params1, params2)
assertThat(stx1.networkParametersHash).isEqualTo(hash1)
assertThat(stx2.networkParametersHash).isEqualTo(hash2)
miniCorpNode.transaction {
assertThat(miniCorpNode.services.networkParametersService.lookup(hash1)).isNotNull()
assertThat(miniCorpNode.services.networkParametersService.lookup(hash2)).isNull()
}
// miniCorpNode resolves the stx2 from megaCorpParty
val p = ResolveTransactionsFlowTest.TestFlow(setOf(stx2.id), megaCorpParty)
val future = miniCorpNode.startFlow(p)
mockNet.runNetwork()
future.getOrThrow()
miniCorpNode.transaction {
// Check that parameters were downloaded to the storage.
assertThat(miniCorpNode.services.networkParametersService.lookup(hash1)).isEqualTo(params1)
assertThat(miniCorpNode.services.networkParametersService.lookup(hash2)).isEqualTo(params2)
}
}
}

View File

@ -229,7 +229,7 @@ class ResolveTransactionsFlowTest {
@InitiatingFlow @InitiatingFlow
private open class TestFlow(val otherSide: Party, private val resolveTransactionsFlowFactory: (FlowSession) -> ResolveTransactionsFlow, private val txCountLimit: Int? = null) : FlowLogic<Unit>() { open class TestFlow(val otherSide: Party, private val resolveTransactionsFlowFactory: (FlowSession) -> ResolveTransactionsFlow, private val txCountLimit: Int? = null) : FlowLogic<Unit>() {
constructor(txHashes: Set<SecureHash>, otherSide: Party, txCountLimit: Int? = null) : this(otherSide, { ResolveTransactionsFlow(txHashes, it) }, txCountLimit = txCountLimit) constructor(txHashes: Set<SecureHash>, otherSide: Party, txCountLimit: Int? = null) : this(otherSide, { ResolveTransactionsFlow(txHashes, it) }, txCountLimit = txCountLimit)
constructor(stx: SignedTransaction, otherSide: Party) : this(otherSide, { ResolveTransactionsFlow(stx, it) }) constructor(stx: SignedTransaction, otherSide: Party) : this(otherSide, { ResolveTransactionsFlow(stx, it) })
@ -243,7 +243,7 @@ class ResolveTransactionsFlowTest {
} }
@Suppress("unused") @Suppress("unused")
@InitiatedBy(TestFlow::class) @InitiatedBy(TestFlow::class)
private class TestResponseFlow(val otherSideSession: FlowSession) : FlowLogic<Void?>() { class TestResponseFlow(val otherSideSession: FlowSession) : FlowLogic<Void?>() {
@Suspendable @Suspendable
override fun call() = subFlow(TestNoSecurityDataVendingFlow(otherSideSession)) override fun call() = subFlow(TestNoSecurityDataVendingFlow(otherSideSession))
} }

View File

@ -2,10 +2,7 @@ package net.corda.node.internal
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.*
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.SignedDataWithCert
import net.corda.core.internal.NetworkParametersServiceInternal
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo import net.corda.core.node.NotaryInfo
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
@ -26,23 +23,6 @@ import org.apache.commons.lang.ArrayUtils
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import javax.persistence.* import javax.persistence.*
interface NetworkParametersStorage : NetworkParametersServiceInternal {
/**
* Return parameters epoch for the given parameters hash. Null if there are no parameters for this hash in the storage and we are unable to
* get them from network map.
*/
fun getEpochFromHash(hash: SecureHash): Int?
/**
* Save signed network parameters data. Internally network parameters bytes should be stored with the signature.
* It's because of ability of older nodes to function in network where parameters were extended with new fields.
* Hash should always be calculated over the serialized bytes.
*/
fun saveParameters(signedNetworkParameters: SignedDataWithCert<NetworkParameters>)
fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate)
}
class DBNetworkParametersStorage( class DBNetworkParametersStorage(
cacheFactory: NamedCacheFactory, cacheFactory: NamedCacheFactory,
private val database: CordaPersistence, private val database: CordaPersistence,
@ -94,6 +74,12 @@ class DBNetworkParametersStorage(
override fun getEpochFromHash(hash: SecureHash): Int? = lookup(hash)?.epoch override fun getEpochFromHash(hash: SecureHash): Int? = lookup(hash)?.epoch
override fun lookupSigned(hash: SecureHash): SignedDataWithCert<NetworkParameters>? {
return database.transaction { hashToParameters[hash] }
}
override fun hasParameters(hash: SecureHash): Boolean = hash in hashToParameters
override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) { override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) {
log.trace { "Saving new network parameters to network parameters storage." } log.trace { "Saving new network parameters to network parameters storage." }
val networkParameters = signedNetworkParameters.verified() val networkParameters = signedNetworkParameters.verified()
@ -104,7 +90,6 @@ class DBNetworkParametersStorage(
} }
} }
// TODO For the future we could get them also as signed (by network operator) attachments on transactions.
private fun tryDownloadUnknownParameters(parametersHash: SecureHash): NetworkParameters? { private fun tryDownloadUnknownParameters(parametersHash: SecureHash): NetworkParameters? {
return if (networkMapClient != null) { return if (networkMapClient != null) {
try { try {

View File

@ -13,7 +13,6 @@ import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.node.internal.NetworkParametersStorage
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.config.NetworkParameterAcceptanceSettings import net.corda.node.services.config.NetworkParameterAcceptanceSettings
import net.corda.node.utilities.NamedThreadFactory import net.corda.node.utilities.NamedThreadFactory

View File

@ -18,7 +18,6 @@ import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.NetworkParametersStorage
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.config.NetworkParameterAcceptanceSettings import net.corda.node.services.config.NetworkParameterAcceptanceSettings
import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.NodeInfoAndSigned

View File

@ -28,7 +28,6 @@ import net.corda.core.utilities.seconds
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.internal.NetworkParametersStorage
import net.corda.node.internal.NodeFlowManager import net.corda.node.internal.NodeFlowManager
import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.FlowStarter
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal

View File

@ -2,11 +2,12 @@ package net.corda.testing.node.internal
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.NetworkParametersStorage
import net.corda.core.internal.SignedDataWithCert import net.corda.core.internal.SignedDataWithCert
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo import net.corda.core.node.NotaryInfo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.node.internal.NetworkParametersStorage import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.withTestSerializationEnvIfNotSet import net.corda.testing.internal.withTestSerializationEnvIfNotSet
@ -15,6 +16,7 @@ import java.time.Instant
class MockNetworkParametersStorage(private var currentParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN)) : NetworkParametersStorage { class MockNetworkParametersStorage(private var currentParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN)) : NetworkParametersStorage {
private val hashToParametersMap: HashMap<SecureHash, NetworkParameters> = HashMap() private val hashToParametersMap: HashMap<SecureHash, NetworkParameters> = HashMap()
private val hashToSignedParametersMap: HashMap<SecureHash, SignedNetworkParameters> = HashMap()
init { init {
storeCurrentParameters() storeCurrentParameters()
@ -29,6 +31,12 @@ class MockNetworkParametersStorage(private var currentParameters: NetworkParamet
setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkMapCert(trustRoot)) setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkMapCert(trustRoot))
} }
override fun lookupSigned(hash: SecureHash): SignedDataWithCert<NetworkParameters>? {
return hashToSignedParametersMap[hash]
}
override fun hasParameters(hash: SecureHash): Boolean = hash in hashToParametersMap
override val currentHash: SecureHash override val currentHash: SecureHash
get() { get() {
return withTestSerializationEnvIfNotSet { return withTestSerializationEnvIfNotSet {
@ -42,6 +50,7 @@ class MockNetworkParametersStorage(private var currentParameters: NetworkParamet
val networkParameters = signedNetworkParameters.verified() val networkParameters = signedNetworkParameters.verified()
val hash = signedNetworkParameters.raw.hash val hash = signedNetworkParameters.raw.hash
hashToParametersMap[hash] = networkParameters hashToParametersMap[hash] = networkParameters
hashToSignedParametersMap[hash] = signedNetworkParameters
} }
override fun getHistoricNotary(party: Party): NotaryInfo? { override fun getHistoricNotary(party: Party): NotaryInfo? {