{"action":"created","comment":{"url":"https://api.github.com/repos/corda/corda/pulls/comments/235736961","pull_request_review_id":177671051,"id":235736961,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzNTczNjk2MQ==","diff_hunk":"@@ -0,0 +1,145 @@\n+package net.corda.node.internal\n+\n+import net.corda.core.crypto.SecureHash\n+import net.corda.core.internal.DigitalSignatureWithCert\n+import net.corda.core.internal.NamedCacheFactory\n+import net.corda.core.internal.SignedDataWithCert\n+import net.corda.core.node.NetworkParameters\n+import net.corda.core.node.services.NetworkParametersStorage\n+import net.corda.core.serialization.SerializedBytes\n+import net.corda.core.serialization.SingletonSerializeAsToken\n+import net.corda.core.serialization.deserialize\n+import net.corda.core.utilities.MAX_HASH_HEX_SIZE\n+import net.corda.core.utilities.contextLogger\n+import net.corda.core.utilities.trace\n+import net.corda.node.services.network.NetworkMapClient\n+import net.corda.node.utilities.AppendOnlyPersistentMap\n+import net.corda.nodeapi.internal.crypto.X509CertificateFactory\n+import net.corda.nodeapi.internal.crypto.X509Utilities\n+import net.corda.nodeapi.internal.network.SignedNetworkParameters\n+import net.corda.nodeapi.internal.network.verifiedNetworkMapCert\n+import net.corda.nodeapi.internal.persistence.CordaPersistence\n+import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX\n+import org.apache.commons.lang.ArrayUtils\n+import java.security.cert.X509Certificate\n+import javax.persistence.*\n+\n+// TODO NetworkParametersReader + NodeParametersStorage refactor\n+class NodeParametersStorage(\n+        cacheFactory: NamedCacheFactory,\n+        private val database: CordaPersistence,\n+        // TODO It's very inefficient solution (at least at the beginning when node joins without historical data)\n+        // We could have historic parameters endpoint or always add parameters as an attachment to the transaction.\n+        private val networkMapClient: NetworkMapClient?\n+) : NetworkParametersStorage, SingletonSerializeAsToken() {\n+    private lateinit var trustRoot: X509Certificate\n+\n+    companion object {\n+        private val log = contextLogger()\n+\n+        fun createParametersMap(cacheFactory: NamedCacheFactory): AppendOnlyPersistentMap<SecureHash, SignedDataWithCert<NetworkParameters>, PersistentNetworkParameters, String> {\n+            return AppendOnlyPersistentMap(\n+                    cacheFactory = cacheFactory,\n+                    name = \"NodeParametersStorage_networkParametersByHash\",\n+                    toPersistentEntityKey = { it.toString() },\n+                    fromPersistentEntity = {\n+                        Pair(\n+                                SecureHash.parse(it.hash),\n+                                it.signedNetworkParameters\n+                        )\n+                    },\n+                    toPersistentEntity = { key: SecureHash, value: SignedDataWithCert<NetworkParameters> ->\n+                        PersistentNetworkParameters(key.toString(), value.verified().epoch, value.raw.bytes, value.sig.bytes, value.sig.by.encoded,\n+                                X509Utilities.buildCertPath(value.sig.parentCertsChain).encoded)\n+                    },\n+                    persistentEntityClass = PersistentNetworkParameters::class.java\n+            )\n+        }\n+    }\n+\n+    fun start(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) {\n+        this.trustRoot = trustRoot\n+        saveParameters(currentSignedParameters)\n+        _currentHash = currentSignedParameters.raw.hash\n+    }\n+\n+    private lateinit var _currentHash: SecureHash\n+    override val currentParametersHash: SecureHash get() = _currentHash\n+    // TODO Have network map serve special \"starting\" parameters as parameters for resolution for older transactions?\n+    override val defaultParametersHash: SecureHash get() = currentParametersHash\n+\n+    private val hashToParameters = createParametersMap(cacheFactory)\n+\n+    override fun readParametersFromHash(hash: SecureHash): NetworkParameters? {\n+        return database.transaction { hashToParameters[hash]?.raw?.deserialize() } ?: tryDownloadUnknownParameters(hash)\n+    }\n+\n+    override fun getEpochFromHash(hash: SecureHash): Int? = readParametersFromHash(hash)?.epoch\n+\n+    override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) {\n+        log.trace { \"Saving new network parameters to network parameters storage.\" }\n+        val networkParameters = signedNetworkParameters.verified()\n+        val hash = signedNetworkParameters.raw.hash\n+        log.trace { \"Parameters to save $networkParameters with hash $hash\" }\n+        database.transaction {\n+            hashToParameters.addWithDuplicatesAllowed(hash, signedNetworkParameters)\n+        }\n+    }\n+\n+    // TODO For the future we could get them also as signed (by network operator) attachments on transactions.\n+    private fun tryDownloadUnknownParameters(parametersHash: SecureHash): NetworkParameters? {\n+        return if (networkMapClient != null) {\n+            try {\n+                val signedParams = networkMapClient.getNetworkParameters(parametersHash)\n+                val networkParameters = signedParams.verifiedNetworkMapCert(trustRoot)\n+                saveParameters(signedParams)\n+                networkParameters\n+            } catch (e: Exception) {\n+                log.warn(\"Failed to download historical network parameters with hash $parametersHash\", e)\n+                null\n+            }\n+        } else {\n+            log.warn(\"Tried to download historical network parameters with hash $parametersHash, but network map url isn't configured\")","path":"node/src/main/kotlin/net/corda/node/internal/NodeParametersStorage.kt","position":102,"original_position":102,"commit_id":"ca1d838f147ed6252b4c2278b554ba7e11f50b12","original_commit_id":"ca1d838f147ed6252b4c2278b554ba7e11f50b12","user":{"login":"kasiastreich","id":24268146,"node_id":"MDQ6VXNlcjI0MjY4MTQ2","avatar_url":"https://avatars1.githubusercontent.com/u/24268146?v=4","gravatar_id":"","url":"https://api.github.com/users/kasiastreich","html_url":"https://github.com/kasiastreich","followers_url":"https://api.github.com/users/kasiastreich/followers","following_url":"https://api.github.com/users/kasiastreich/following{/other_user}","gists_url":"https://api.github.com/users/kasiastreich/gists{/gist_id}","starred_url":"https://api.github.com/users/kasiastreich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kasiastreich/subscriptions","organizations_url":"https://api.github.com/users/kasiastreich/orgs","repos_url":"https://api.github.com/users/kasiastreich/repos","events_url":"https://api.github.com/users/kasiastreich/events{/privacy}","received_events_url":"https://api.github.com/users/kasiastreich/received_events","type":"User","site_admin":false},"body":"No, for test deployments using Bootstraper there is no NetworkMapClient URL configured.","created_at":"2018-11-22T14:04:57Z","updated_at":"2018-11-22T14:04:57Z","html_url":"https://github.com/corda/corda/pull/4228#discussion_r235736961","pull_request_url":"https://api.github.com/repos/corda/corda/pulls/4228","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/corda/corda/pulls/comments/235736961"},"html":{"href":"https://github.com/corda/corda/pull/4228#discussion_r235736961"},"pull_request":{"href":"https://api.github.com/repos/corda/corda/pulls/4228"}},"in_reply_to_id":235695906},"pull_request":{"url":"https://api.github.com/repos/corda/corda/pulls/4228","id":230501360,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMwNTAxMzYw","html_url":"https://github.com/corda/corda/pull/4228","diff_url":"https://github.com/corda/corda/pull/4228.diff","patch_url":"https://github.com/corda/corda/pull/4228.patch","issue_url":"https://api.github.com/repos/corda/corda/issues/4228","number":4228,"state":"open","locked":false,"title":"CORDA-2089 - network parameters tags - part","user":{"login":"kasiastreich","id":24268146,"node_id":"MDQ6VXNlcjI0MjY4MTQ2","avatar_url":"https://avatars1.githubusercontent.com/u/24268146?v=4","gravatar_id":"","url":"https://api.github.com/users/kasiastreich","html_url":"https://github.com/kasiastreich","followers_url":"https://api.github.com/users/kasiastreich/followers","following_url":"https://api.github.com/users/kasiastreich/following{/other_user}","gists_url":"https://api.github.com/users/kasiastreich/gists{/gist_id}","starred_url":"https://api.github.com/users/kasiastreich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kasiastreich/subscriptions","organizations_url":"https://api.github.com/users/kasiastreich/orgs","repos_url":"https://api.github.com/users/kasiastreich/repos","events_url":"https://api.github.com/users/kasiastreich/events{/privacy}","received_events_url":"https://api.github.com/users/kasiastreich/received_events","type":"User","site_admin":false},"body":"Data structures changes, storage and notarisation.\r\n\r\nTag transactions with network parameters hash that was in force when tx\r\nwas created. Add component group on all core transactions and resolved\r\nparameters on full transactions. The hash should be always visible on\r\nthe filtered versions of transactions. Add\r\nnotarisation check that the parameters are current.\r\nImplement network parameters storage on services for resolution.\r\n\r\nThis is only part of the work, next PR will include changes to\r\nResolveTransactionsFlow to make sure that parameters in the transaction\r\ngraph are ordered (this is to prevent the downgrade attack, when the\r\nmalicious notary and participants sign transaction that shouldn't be\r\nnotarised otherwise).\r\nProbably on network services side we need the default parameters\r\nendpoint for the\r\ntransactions that were created before this change - for now it's default\r\nto the current ones.\r\n","created_at":"2018-11-13T14:46:39Z","updated_at":"2018-11-22T14:04:57Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c408854d471f2173cbca853e038e83c78784703a","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/corda/corda/pulls/4228/commits","review_comments_url":"https://api.github.com/repos/corda/corda/pulls/4228/comments","review_comment_url":"https://api.github.com/repos/corda/corda/pulls/comments{/number}","comments_url":"https://api.github.com/repos/corda/corda/issues/4228/comments","statuses_url":"https://api.github.com/repos/corda/corda/statuses/ca1d838f147ed6252b4c2278b554ba7e11f50b12","head":{"label":"corda:kstreich-net-params-tags","ref":"kstreich-net-params-tags","sha":"ca1d838f147ed6252b4c2278b554ba7e11f50b12","user":{"login":"corda","id":22600631,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIyNjAwNjMx","avatar_url":"https://avatars1.githubusercontent.com/u/22600631?v=4","gravatar_id":"","url":"https://api.github.com/users/corda","html_url":"https://github.com/corda","followers_url":"https://api.github.com/users/corda/followers","following_url":"https://api.github.com/users/corda/following{/other_user}","gists_url":"https://api.github.com/users/corda/gists{/gist_id}","starred_url":"https://api.github.com/users/corda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/corda/subscriptions","organizations_url":"https://api.github.com/users/corda/orgs","repos_url":"https://api.github.com/users/corda/repos","events_url":"https://api.github.com/users/corda/events{/privacy}","received_events_url":"https://api.github.com/users/corda/received_events","type":"Organization","site_admin":false},"repo":{"id":70137417,"node_id":"MDEwOlJlcG9zaXRvcnk3MDEzNzQxNw==","name":"corda","full_name":"corda/corda","private":false,"owner":{"login":"corda","id":22600631,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIyNjAwNjMx","avatar_url":"https://avatars1.githubusercontent.com/u/22600631?v=4","gravatar_id":"","url":"https://api.github.com/users/corda","html_url":"https://github.com/corda","followers_url":"https://api.github.com/users/corda/followers","following_url":"https://api.github.com/users/corda/following{/other_user}","gists_url":"https://api.github.com/users/corda/gists{/gist_id}","starred_url":"https://api.github.com/users/corda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/corda/subscriptions","organizations_url":"https://api.github.com/users/corda/orgs","repos_url":"https://api.github.com/users/corda/repos","events_url":"https://api.github.com/users/corda/events{/privacy}","received_events_url":"https://api.github.com/users/corda/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/corda/corda","description":"Corda is an open source blockchain project, designed for business from the start. Only Corda allows you to build interoperable blockchain networks that transact in strict privacy. Corda's smart contract technology allows businesses to transact directly, with value.","fork":false,"url":"https://api.github.com/repos/corda/corda","forks_url":"https://api.github.com/repos/corda/corda/forks","keys_url":"https://api.github.com/repos/corda/corda/keys{/key_id}","collaborators_url":"https://api.github.com/repos/corda/corda/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/corda/corda/teams","hooks_url":"https://api.github.com/repos/corda/corda/hooks","issue_events_url":"https://api.github.com/repos/corda/corda/issues/events{/number}","events_url":"https://api.github.com/repos/corda/corda/events","assignees_url":"https://api.github.com/repos/corda/corda/assignees{/user}","branches_url":"https://api.github.com/repos/corda/corda/branches{/branch}","tags_url":"https://api.github.com/repos/corda/corda/tags","blobs_url":"https://api.github.com/repos/corda/corda/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/corda/corda/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/corda/corda/git/refs{/sha}","trees_url":"https://api.github.com/repos/corda/corda/git/trees{/sha}","statuses_url":"https://api.github.com/repos/corda/corda/statuses/{sha}","languages_url":"https://api.github.com/repos/corda/corda/languages","stargazers_url":"https://api.github.com/repos/corda/corda/stargazers","contributors_url":"https://api.github.com/repos/corda/corda/contributors","subscribers_url":"https://api.github.com/repos/corda/corda/subscribers","subscription_url":"https://api.github.com/repos/corda/corda/subscription","commits_url":"https://api.github.com/repos/corda/corda/commits{/sha}","git_commits_url":"https://api.github.com/repos/corda/corda/git/commits{/sha}","comments_url":"https://api.github.com/repos/corda/corda/comments{/number}","issue_comment_url":"https://api.github.com/repos/corda/corda/issues/comments{/number}","contents_url":"https://api.github.com/repos/corda/corda/contents/{+path}","compare_url":"https://api.github.com/repos/corda/corda/compare/{base}...{head}","merges_url":"https://api.github.com/repos/corda/corda/merges","archive_url":"https://api.github.com/repos/corda/corda/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/corda/corda/downloads","issues_url":"https://api.github.com/repos/corda/corda/issues{/number}","pulls_url":"https://api.github.com/repos/corda/corda/pulls{/number}","milestones_url":"https://api.github.com/repos/corda/corda/milestones{/number}","notifications_url":"https://api.github.com/repos/corda/corda/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/corda/corda/labels{/name}","releases_url":"https://api.github.com/repos/corda/corda/releases{/id}","deployments_url":"https://api.github.com/repos/corda/corda/deployments","created_at":"2016-10-06T08:46:29Z","updated_at":"2018-11-22T13:53:21Z","pushed_at":"2018-11-22T13:55:05Z","git_url":"git://github.com/corda/corda.git","ssh_url":"git@github.com:corda/corda.git","clone_url":"https://github.com/corda/corda.git","svn_url":"https://github.com/corda/corda","homepage":"https://www.corda.net","size":167211,"stargazers_count":2518,"watchers_count":2518,"language":"Kotlin","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":641,"mirror_url":null,"archived":false,"open_issues_count":95,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":641,"open_issues":95,"watchers":2518,"default_branch":"master"}},"base":{"label":"corda:master","ref":"master","sha":"faa9606deae66f901710bd1e4138b694aee8cbcc","user":{"login":"corda","id":22600631,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIyNjAwNjMx","avatar_url":"https://avatars1.githubusercontent.com/u/22600631?v=4","gravatar_id":"","url":"https://api.github.com/users/corda","html_url":"https://github.com/corda","followers_url":"https://api.github.com/users/corda/followers","following_url":"https://api.github.com/users/corda/following{/other_user}","gists_url":"https://api.github.com/users/corda/gists{/gist_id}","starred_url":"https://api.github.com/users/corda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/corda/subscriptions","organizations_url":"https://api.github.com/users/corda/orgs","repos_url":"https://api.github.com/users/corda/repos","events_url":"https://api.github.com/users/corda/events{/privacy}","received_events_url":"https://api.github.com/users/corda/received_events","type":"Organization","site_admin":false},"repo":{"id":70137417,"node_id":"MDEwOlJlcG9zaXRvcnk3MDEzNzQxNw==","name":"corda","full_name":"corda/corda","private":false,"owner":{"login":"corda","id":22600631,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIyNjAwNjMx","avatar_url":"https://avatars1.githubusercontent.com/u/22600631?v=4","gravatar_id":"","url":"https://api.github.com/users/corda","html_url":"https://github.com/corda","followers_url":"https://api.github.com/users/corda/followers","following_url":"https://api.github.com/users/corda/following{/other_user}","gists_url":"https://api.github.com/users/corda/gists{/gist_id}","starred_url":"https://api.github.com/users/corda/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/corda/subscriptions","organizations_url":"https://api.github.com/users/corda/orgs","repos_url":"https://api.github.com/users/corda/repos","events_url":"https://api.github.com/users/corda/events{/privacy}","received_events_url":"https://api.github.com/users/corda/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/corda/corda","description":"Corda is an open source blockchain project, designed for business from the start. Only Corda allows you to build interoperable blockchain networks that transact in strict privacy. Corda's smart contract technology allows businesses to transact directly, with value.","fork":false,"url":"https://api.github.com/repos/corda/corda","forks_url":"https://api.github.com/repos/corda/corda/forks","keys_url":"https://api.github.com/repos/corda/corda/keys{/key_id}","collaborators_url":"https://api.github.com/repos/corda/corda/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/corda/corda/teams","hooks_url":"https://api.github.com/repos/corda/corda/hooks","issue_events_url":"https://api.github.com/repos/corda/corda/issues/events{/number}","events_url":"https://api.github.com/repos/corda/corda/events","assignees_url":"https://api.github.com/repos/corda/corda/assignees{/user}","branches_url":"https://api.github.com/repos/corda/corda/branches{/branch}","tags_url":"https://api.github.com/repos/corda/corda/tags","blobs_url":"https://api.github.com/repos/corda/corda/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/corda/corda/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/corda/corda/git/refs{/sha}","trees_url":"https://api.github.com/repos/corda/corda/git/trees{/sha}","statuses_url":"https://api.github.com/repos/corda/corda/statuses/{sha}","languages_url":"https://api.github.com/repos/corda/corda/languages","stargazers_url":"https://api.github.com/repos/corda/corda/stargazers","contributors_url":"https://api.github.com/repos/corda/corda/contributors","subscribers_url":"https://api.github.com/repos/corda/corda/subscribers","subscription_url":"https://api.github.com/repos/corda/corda/subscription","commits_url":"https://api.github.com/repos/corda/corda/commits{/sha}","git_commits_url":"https://api.github.com/repos/corda/corda/git/commits{/sha}","comments_url":"https://api.github.com/repos/corda/corda/comments{/number}","issue_comment_url":"https://api.github.com/repos/corda/corda/issues/comments{/number}","contents_url":"https://api.github.com/repos/corda/corda/contents/{+path}","compare_url":"https://api.github.com/repos/corda/corda/compare/{base}...{head}","merges_url":"https://api.github.com/repos/corda/corda/merges","archive_url":"https://api.github.com/repos/corda/corda/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/corda/corda/downloads","issues_url":"https://api.github.com/repos/corda/corda/issues{/number}","pulls_url":"https://api.github.com/repos/corda/corda/pulls{/number}","milestones_url":"https://api.github.com/repos/corda/corda/milestones{/number}","notifications_url":"https://api.github.com/repos/corda/corda/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/corda/corda/labels{/name}","releases_url":"https://api.github.com/repos/corda/corda/releases{/id}","deployments_url":"https://api.github.com/repos/corda/corda/deployments","created_at":"2016-10-06T08:46:29Z","updated_at":"2018-11-22T13:53:21Z","pushed_at":"2018-11-22T13:55:05Z","git_url":"git://github.com/corda/corda.git","ssh_url":"git@github.com:corda/corda.git","clone_url":"https://github.com/corda/corda.git","svn_url":"https://github.com/corda/corda","homepage":"https://www.corda.net","size":167211,"stargazers_count":2518,"watchers_count":2518,"language":"Kotlin","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":641,"mirror_url":null,"archived":false,"open_issues_count":95,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":641,"open_issues":95,"watchers":2518,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/corda/corda/pulls/4228"},"html":{"href":"https://github.com/corda/corda/pull/4228"},"issue":{"href":"https://api.github.com/repos/corda/corda/issues/4228"},"comments":{"href":"https://api.github.com/repos/corda/corda/issues/4228/comments"},"review_comments":{"href":"https://api.github.com/repos/corda/corda/pulls/4228/comments"},"review_comment":{"href":"https://api.github.com/repos/corda/corda/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/corda/corda/pulls/4228/commits"},"statuses":{"href":"https://api.github.com/repos/corda/corda/statuses/ca1d838f147ed6252b4c2278b554ba7e11f50b12"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"kasiastreich"}}