diff --git a/README.md b/README.md index 1df546920..21029d1ae 100644 --- a/README.md +++ b/README.md @@ -70,5 +70,6 @@ All standards at or past the "Draft" stage are listed here in order of their ICS | -------------------------------------------------------- | ----------------------- | ----- | | [20](spec/app/ics-020-fungible-token-transfer/README.md) | Fungible Token Transfer | Candidate | | [27](spec/app/ics-027-interchain-accounts/README.md) | Interchain Accounts | Draft | +| [28](spec/app/ics-028-cross-chain-validation/README.md) | Cross-Chain Validation | Draft | | [29](spec/app/ics-029-fee-payment) | General Relayer Incentivisation Mechanism | Candidate | | [30](spec/app/ics-030-middleware) | IBC Application Middleware | Candidate | diff --git a/spec/app/ics-028-cross-chain-validation/README.md b/spec/app/ics-028-cross-chain-validation/README.md new file mode 100644 index 000000000..cb75682ca --- /dev/null +++ b/spec/app/ics-028-cross-chain-validation/README.md @@ -0,0 +1,53 @@ +--- +ics: 28 +title: Cross-Chain Validation +stage: draft +category: IBC/APP +requires: 25, 26 +kind: +author: +created: +modified: +--- + + +# Synopsis + +This standard document specifies packet data structure, state machine handling logic, and encoding details for Cross-Chain Validation (CCV). CCV is the specific IBC level protocol that enables *Interchain Security*, a Cosmos-specific category of *Shared Security*. + +At a high level, CCV enables a *provider chain* (e.g., the Cosmos Hub) to provide *security* to multiple *consumer chains*. This means that the validator sets on the consumer chains are chosen from the validator set of the provider chain (for more details, see the [Security Model](./overview_and_basic_concepts.md#security-model) section). + +The communication between the provider and the consumer chains is done through the IBC protocol over a *unique*, *ordered* channel (one for each consumer chain). + +> Throughout this document, we will use the terms chain and blockchain interchangeably. + +## Contents +- [Overview and Basic Concepts](./overview_and_basic_concepts.md) +- [System Model and Properties](./system_model_and_properties.md) +- [Technical Specification: Data Structures and Methods](./technical_specification.md) + + + +## Copyright + +All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/spec/app/ics-028-cross-chain-validation/figures/ccv-init-overview.excalidraw b/spec/app/ics-028-cross-chain-validation/figures/ccv-init-overview.excalidraw new file mode 100644 index 000000000..57381b910 --- /dev/null +++ b/spec/app/ics-028-cross-chain-validation/figures/ccv-init-overview.excalidraw @@ -0,0 +1,2329 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 1672, + "versionNonce": 1363243933, + "isDeleted": false, + "id": "VyRoLp37OULOh8PtwSL1o", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "angle": 0, + "x": 322.05701296942993, + "y": 783.9233360652088, + "strokeColor": "#ced4da", + "backgroundColor": "#ced4da", + "width": 1265.666666666667, + "height": 187.4444444444444, + "seed": 1900272787, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + } + ], + "updated": 1639736972222 + }, + { + "type": "rectangle", + "version": 1489, + "versionNonce": 1475881309, + "isDeleted": false, + "id": "bV3e1QXlGeho4mNT0Md4J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "angle": 0, + "x": 323.168124080541, + "y": 652.8122249540979, + "strokeColor": "#ced4da", + "backgroundColor": "#ced4da", + "width": 1264.5555555555554, + "height": 112.99999999999996, + "seed": 524846941, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + } + ], + "updated": 1639736986991 + }, + { + "type": "rectangle", + "version": 1406, + "versionNonce": 1909827773, + "isDeleted": false, + "id": "cIdDFggUL2V75qBzn7_1U", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "angle": 0, + "x": 325.7236796360967, + "y": 517.3677805096532, + "strokeColor": "#ced4da", + "backgroundColor": "#ced4da", + "width": 1260.1111111111102, + "height": 112.99999999999996, + "seed": 602414589, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + } + ], + "updated": 1639736995713 + }, + { + "type": "text", + "version": 401, + "versionNonce": 317058365, + "isDeleted": false, + "id": "DcYomqVwJx0rUjMvf5S5b", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 733.3333333333333, + "y": 359.55555555555554, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 377, + "height": 36, + "seed": 484186313, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639736454336, + "fontSize": 28, + "fontFamily": 1, + "text": "CCV - Channel Initialization", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 501, + "versionNonce": 2027571517, + "isDeleted": false, + "id": "ghfMBQkamou-QJWwVrzMO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 355.88888888888886, + "y": 400.5, + "strokeColor": "#364fc7", + "backgroundColor": "transparent", + "width": 138, + "height": 26, + "seed": 1747022505, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639736979854, + "fontSize": 20, + "fontFamily": 1, + "text": "Provider Chain", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 1022, + "versionNonce": 1779941427, + "isDeleted": false, + "id": "ONYRFM5fo0s39BeH4ty3j", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1115.333333333333, + "y": 481.8888888888889, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 148, + "height": 26, + "seed": 129325673, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639736346311, + "fontSize": 20, + "fontFamily": 1, + "text": "Consumer Chain", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "diamond", + "version": 686, + "versionNonce": 644623741, + "isDeleted": false, + "id": "D9UpDlJvj-vqduCyp6YiL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 892.3095382219559, + "y": 679.2011138429862, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 84.27272727272718, + "height": 71.99999999999996, + "seed": 1864416436, + "groupIds": [ + "HpIe0q_ivXIeva3ZeZcXq" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "umXKeqaL3A2gsELJxrAno", + "type": "arrow" + }, + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + } + ], + "updated": 1639736044647 + }, + { + "type": "text", + "version": 606, + "versionNonce": 1742025939, + "isDeleted": false, + "id": "olDFMzwulEulDGtmwqPUT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 904.9913564037735, + "y": 704.5647502066226, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 58.90909090909091, + "height": 21.272727272727266, + "seed": 856808204, + "groupIds": [ + "HpIe0q_ivXIeva3ZeZcXq" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + } + ], + "updated": 1639736044647, + "fontSize": 16.363636363636367, + "fontFamily": 1, + "text": "Relayer", + "baseline": 14.272727272727266, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "" + }, + { + "type": "arrow", + "version": 2642, + "versionNonce": 63775475, + "isDeleted": false, + "id": "umXKeqaL3A2gsELJxrAno", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 979.2933899099882, + "y": 716.4011846808819, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 122.3714252018691, + "height": 0.13511637049509773, + "seed": 671433780, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736931474, + "startBinding": { + "elementId": "D9UpDlJvj-vqduCyp6YiL", + "gap": 2.9648550742373234, + "focus": 0.03196298231576972 + }, + "endBinding": { + "elementId": "Li4WakMROOrh4VO9QnPFJ", + "gap": 5.781086746461382, + "focus": 0.36269934220064703 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 122.3714252018691, + 0.13511637049509773 + ] + ] + }, + { + "type": "arrow", + "version": 2914, + "versionNonce": 360491517, + "isDeleted": false, + "id": "DURyamuWvIhE5o-GLqnvp", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 892.3033491011067, + "y": 719.0079234494531, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 127.11210337471994, + "height": 1.3074457047832766, + "seed": 1573053876, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737001794, + "startBinding": { + "elementId": "olDFMzwulEulDGtmwqPUT", + "gap": 12.688007302666689, + "focus": -0.3959249210105361 + }, + "endBinding": { + "elementId": "8j6p54tqGlb2RxbOlwOCj", + "gap": 10.412010534734593, + "focus": -0.195742017137245 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -127.11210337471994, + -1.3074457047832766 + ] + ] + }, + { + "type": "text", + "version": 1753, + "versionNonce": 370324029, + "isDeleted": false, + "id": "BfihjIK4S9ZseoXjeT71N", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 833.4459018583196, + "y": 661.2011138429866, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 206, + "height": 26, + "seed": 1202279692, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736044647, + "fontSize": 20, + "fontFamily": 1, + "text": "connection handshake", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "id": "8j6p54tqGlb2RxbOlwOCj", + "type": "rectangle", + "x": 346.668124080541, + "y": 435.978891620764, + "width": 408.1111111111111, + "height": 698.3333333333335, + "angle": 0, + "strokeColor": "#1864ab", + "backgroundColor": "#1864ab", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 38506771, + "version": 592, + "versionNonce": 1740848627, + "isDeleted": false, + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + }, + { + "id": "gMMtc7H_hFXaSquI_JbUz", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + } + ], + "updated": 1639737001795 + }, + { + "type": "rectangle", + "version": 659, + "versionNonce": 1305786909, + "isDeleted": false, + "id": "Li4WakMROOrh4VO9QnPFJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "angle": 0, + "x": 1107.4459018583188, + "y": 519.0900027318751, + "strokeColor": "#e67700", + "backgroundColor": "#e67700", + "width": 381.11111111111063, + "height": 620.5555555555559, + "seed": 1423042963, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "umXKeqaL3A2gsELJxrAno", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "Hg015OniyGFjM7QYWPufU", + "type": "arrow" + }, + { + "id": "1qXvD7xEgjTf4VUM5wDV3", + "type": "arrow" + } + ], + "updated": 1639736931474 + }, + { + "type": "rectangle", + "version": 2272, + "versionNonce": 1847103155, + "isDeleted": false, + "id": "cN8JVC0YU7ylsJMNyoLMx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1283.4459018583188, + "y": 554.2011138429866, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 175.55555555555574, + "height": 557.7777777777777, + "seed": 1979822931, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "gdGzxModJaJKV9kGsQ0lN" + }, + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "type": "arrow", + "id": "L-m2OjIz_DM9FZL_aw8q1" + }, + { + "type": "arrow", + "id": "MUNWjeP_k1E_jhbGobXH1" + }, + { + "type": "arrow", + "id": "Z366__sEfBTh-UkidbmWs" + }, + { + "id": "L-SUQMkaBFUSpHYaSKHI8", + "type": "arrow" + }, + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "dXiPD3WzFtcIXoXAUQVpo", + "type": "arrow" + }, + { + "id": "dyJw9z0fJ3VE34NAIchq_", + "type": "arrow" + }, + { + "id": "0imTdwpoy104tRk4pp9Cf", + "type": "arrow" + }, + { + "id": "Uv1Gs8hixSdQGVRH6KLVi", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + } + ], + "updated": 1639736346312 + }, + { + "type": "text", + "version": 977, + "versionNonce": 637410611, + "isDeleted": false, + "id": "Iw9eosLogOHYo9M8Jsbr3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1317.4459018583188, + "y": 528.2011138429866, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 112, + "height": 26, + "seed": 378901469, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736346313, + "fontSize": 20, + "fontFamily": 1, + "text": "CCV Module", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV Module" + }, + { + "type": "text", + "version": 2055, + "versionNonce": 1449378685, + "isDeleted": false, + "id": "QU6RqfKS-_y9s-kS2v306", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1307.2792351916519, + "y": 576.3122249540977, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 65, + "height": 26, + "seed": 1163898717, + "groupIds": [ + "ajix7t8vF1k67Cs7w6vm6" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736346313, + "fontSize": 20, + "fontFamily": 1, + "text": "create", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 2175, + "versionNonce": 88735443, + "isDeleted": false, + "id": "ClT976yNQ_fMatiXU52On", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1380.7792351916519, + "y": 576.3122249540977, + "strokeColor": "#364fc7", + "backgroundColor": "transparent", + "width": 52, + "height": 26, + "seed": 87769331, + "groupIds": [ + "ajix7t8vF1k67Cs7w6vm6" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736346313, + "fontSize": 20, + "fontFamily": 1, + "text": "client", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "client" + }, + { + "type": "arrow", + "version": 3500, + "versionNonce": 1981498707, + "isDeleted": false, + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 573.0425709491141, + "y": 560.517342700216, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 526.6808357855633, + "height": 12.6800755795133, + "seed": 1757145171, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736931474, + "startBinding": { + "elementId": "fatyAFozygc0HN-v5nRU0", + "gap": 13.263335757461606, + "focus": -0.7510735329284931 + }, + "endBinding": { + "elementId": "Li4WakMROOrh4VO9QnPFJ", + "gap": 7.722495123641276, + "focus": 0.7983494108465463 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 526.6808357855633, + 12.6800755795133 + ] + ] + }, + { + "type": "text", + "version": 1672, + "versionNonce": 1598858291, + "isDeleted": false, + "id": "obb5_vEy0bQhgCVmGyg2U", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 822.9165850810258, + "y": 541.8677805096532, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 217, + "height": 26, + "seed": 1080124915, + "groupIds": [ + "3L8N_qI1cIkTh8PTodA99" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736393092, + "fontSize": 20, + "fontFamily": 1, + "text": "start consumer chain ", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 1772, + "versionNonce": 73739389, + "isDeleted": false, + "id": "Y8isoLMncxer7kjVuxmbv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 776.4165850810263, + "y": 572.8677805096532, + "strokeColor": "#364fc7", + "backgroundColor": "transparent", + "width": 132, + "height": 26, + "seed": 2101765821, + "groupIds": [ + "3L8N_qI1cIkTh8PTodA99" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736393092, + "fontSize": 20, + "fontFamily": 1, + "text": "validator set", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 1776, + "versionNonce": 1093641683, + "isDeleted": false, + "id": "gEd9zB-Zfifc6sNT9rzro", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 942.3054739699143, + "y": 572.7566693985422, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 132, + "height": 26, + "seed": 767963027, + "groupIds": [ + "3L8N_qI1cIkTh8PTodA99" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736393092, + "fontSize": 20, + "fontFamily": 1, + "text": "validator set", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 1761, + "versionNonce": 1734644957, + "isDeleted": false, + "id": "brcIV75-78ydMBBS09vp6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 916.4165850810258, + "y": 568.8677805096532, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 17, + "height": 36, + "seed": 480544541, + "groupIds": [ + "3L8N_qI1cIkTh8PTodA99" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736393092, + "fontSize": 28, + "fontFamily": 1, + "text": "=", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "rectangle", + "version": 2012, + "versionNonce": 1803494173, + "isDeleted": false, + "id": "fatyAFozygc0HN-v5nRU0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 376.4459018583192, + "y": 483.3122249540977, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 183.3333333333332, + "height": 613.3333333333335, + "seed": 135825021, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "gdGzxModJaJKV9kGsQ0lN" + }, + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "type": "arrow", + "id": "L-m2OjIz_DM9FZL_aw8q1" + }, + { + "type": "arrow", + "id": "MUNWjeP_k1E_jhbGobXH1" + }, + { + "type": "arrow", + "id": "Z366__sEfBTh-UkidbmWs" + }, + { + "id": "L-SUQMkaBFUSpHYaSKHI8", + "type": "arrow" + }, + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "jz87Jfblhql4prXUNauec", + "type": "arrow" + }, + { + "id": "XqKtKE8gPBp_eAGHN-Ucc", + "type": "arrow" + }, + { + "id": "Uv1Gs8hixSdQGVRH6KLVi", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + }, + { + "id": "UqXi4k7D7LBkElLUl9bRY", + "type": "arrow" + } + ], + "updated": 1639736404648 + }, + { + "type": "arrow", + "version": 2899, + "versionNonce": 1553147965, + "isDeleted": false, + "id": "UqXi4k7D7LBkElLUl9bRY", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 263.12712540922803, + "y": 546.9045895841169, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 104.20635802675358, + "height": 2.070126621603208, + "seed": 679224755, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736365248, + "startBinding": null, + "endBinding": { + "elementId": "AH2OVrsDWHqzavMP7Tnh1", + "focus": -1.569688502462398, + "gap": 15.970878263057813 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 104.20635802675358, + -2.070126621603208 + ] + ] + }, + { + "type": "text", + "version": 1486, + "versionNonce": 544333907, + "isDeleted": false, + "id": "AH2OVrsDWHqzavMP7Tnh1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 141.72367963609685, + "y": 476.863584699456, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 221, + "height": 52, + "seed": 1225294589, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + }, + { + "id": "UqXi4k7D7LBkElLUl9bRY", + "type": "arrow" + } + ], + "updated": 1639736365118, + "fontSize": 20, + "fontFamily": 1, + "text": "Governance Proposal \n\"spawn consumer chain\"", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 1577, + "versionNonce": 1644139677, + "isDeleted": false, + "id": "U0Bl8vDZOyZGGb-5ZZTLb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 407.11256852498553, + "y": 543.4233360652089, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 65, + "height": 26, + "seed": 1174218099, + "groupIds": [ + "WKHwDuPMLSCmZygHZrpto" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736397425, + "fontSize": 20, + "fontFamily": 1, + "text": "create", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 1657, + "versionNonce": 1860652979, + "isDeleted": false, + "id": "OuxN9IY6BSSbtbdmqHSto", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 479.61256852498553, + "y": 543.4233360652089, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 52, + "height": 26, + "seed": 2145935165, + "groupIds": [ + "WKHwDuPMLSCmZygHZrpto" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736397425, + "fontSize": 20, + "fontFamily": 1, + "text": "client", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "client" + }, + { + "type": "text", + "version": 916, + "versionNonce": 1423141213, + "isDeleted": false, + "id": "DqEozYcKYxDC28R2NnxW0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 411.5570129694301, + "y": 446.2011138429867, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 112, + "height": 26, + "seed": 279128861, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736413106, + "fontSize": 20, + "fontFamily": 1, + "text": "CCV Module", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV Module" + }, + { + "type": "text", + "version": 2029, + "versionNonce": 1744253533, + "isDeleted": false, + "id": "T1izXFIsp7tDrPbX0eIxf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1517.9736796360964, + "y": 566.3122249540976, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 59, + "height": 26, + "seed": 586769011, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736936916, + "fontSize": 20, + "fontFamily": 1, + "text": "ICS-2", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "ICS-2" + }, + { + "type": "text", + "version": 2074, + "versionNonce": 470904307, + "isDeleted": false, + "id": "t_J1VfmHv9u7EE-WzNnYj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1517.9736796360964, + "y": 695.2011138429867, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 59, + "height": 26, + "seed": 561448477, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736936916, + "fontSize": 20, + "fontFamily": 1, + "text": "ICS-3", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "ICS-3" + }, + { + "type": "text", + "version": 1948, + "versionNonce": 2128218099, + "isDeleted": false, + "id": "6aTTvXXhRp_Om_uUJ18Yj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 864.2434333771513, + "y": 816.9788916207647, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 178, + "height": 26, + "seed": 180182749, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + } + ], + "updated": 1639736603203, + "fontSize": 20, + "fontFamily": 1, + "text": "channel handshake", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "channel handshake" + }, + { + "type": "text", + "version": 1070, + "versionNonce": 1369229373, + "isDeleted": false, + "id": "ZUY3S70EAhRzG6opdy45g", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1110.5152089590333, + "y": 792.3373046083263, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 151, + "height": 26, + "seed": 1975458397, + "groupIds": [ + "MKCuh3Ezvdojoiw9C9BJi" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1639736346313, + "fontSize": 20, + "fontFamily": 1, + "text": "OnChanOpenInit", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "OnChanOpenInit" + }, + { + "type": "arrow", + "version": 3631, + "versionNonce": 1955562643, + "isDeleted": false, + "id": "dyJw9z0fJ3VE34NAIchq_", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1113.8275853573923, + "y": 835.3690346047606, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 160.64561032458118, + "height": 0.9170114886804868, + "seed": 488375795, + "groupIds": [ + "MKCuh3Ezvdojoiw9C9BJi" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736346334, + "startBinding": null, + "endBinding": { + "elementId": "cN8JVC0YU7ylsJMNyoLMx", + "focus": -0.002898245662532866, + "gap": 8.972706176345355 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 160.64561032458118, + -0.9170114886804868 + ] + ] + }, + { + "type": "text", + "version": 935, + "versionNonce": 716355741, + "isDeleted": false, + "id": "OLVnAm75K0c9kLnrUS8a3", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1112.881446312339, + "y": 875.6852488447693, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 148, + "height": 26, + "seed": 1042999091, + "groupIds": [ + "zm2GFhRP96ueQ_yNG_kXo" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "1qXvD7xEgjTf4VUM5wDV3", + "type": "arrow" + } + ], + "updated": 1639736346313, + "fontSize": 20, + "fontFamily": 1, + "text": "OnChanOpenAck", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "OnChanOpenAck" + }, + { + "type": "arrow", + "version": 3253, + "versionNonce": 25625117, + "isDeleted": false, + "id": "0imTdwpoy104tRk4pp9Cf", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1114.693822710698, + "y": 918.7169788412034, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 160.03082565981413, + "height": 0.42343729684955633, + "seed": 1002477949, + "groupIds": [ + "zm2GFhRP96ueQ_yNG_kXo" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736346335, + "startBinding": null, + "endBinding": { + "elementId": "cN8JVC0YU7ylsJMNyoLMx", + "focus": -0.3092053337297252, + "gap": 8.721253487806734 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 160.03082565981413, + 0.42343729684955633 + ] + ] + }, + { + "type": "text", + "version": 876, + "versionNonce": 2141720349, + "isDeleted": false, + "id": "1i9SM2FmlaLGLKGuMadr3", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 566.4522437933846, + "y": 899.8135754416973, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 184, + "height": 26, + "seed": 23310525, + "groupIds": [ + "CNZ2bx0Kmyw6NZbWgff33" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "gMMtc7H_hFXaSquI_JbUz", + "type": "arrow" + } + ], + "updated": 1639736365118, + "fontSize": 20, + "fontFamily": 1, + "text": "OnChanOpenConfirm", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "OnChanOpenConfirm" + }, + { + "type": "arrow", + "version": 3204, + "versionNonce": 1817163261, + "isDeleted": false, + "id": "jz87Jfblhql4prXUNauec", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 749.1827595896756, + "y": 943.83156341232, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 178.93092685289037, + "height": 1.1889953727586544, + "seed": 2088404371, + "groupIds": [ + "CNZ2bx0Kmyw6NZbWgff33" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736438043, + "startBinding": null, + "endBinding": { + "elementId": "fatyAFozygc0HN-v5nRU0", + "focus": 0.4946206816622181, + "gap": 10.472597545132885 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -178.93092685289037, + -1.1889953727586544 + ] + ] + }, + { + "type": "text", + "version": 897, + "versionNonce": 1232216957, + "isDeleted": false, + "id": "tFHjTvU-Pd7PxTc9LTr4w", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 596.945901858319, + "y": 815.5254531910084, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 149, + "height": 26, + "seed": 19313843, + "groupIds": [ + "vXfReGIEP1cvs4DENwZ3s" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1639736365118, + "fontSize": 20, + "fontFamily": 1, + "text": "OnChanOpenTry", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "OnChanOpenTry" + }, + { + "type": "arrow", + "version": 3240, + "versionNonce": 1004297341, + "isDeleted": false, + "id": "XqKtKE8gPBp_eAGHN-Ucc", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 753.0653065434987, + "y": 859.5434411616313, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 178.69352318632787, + "height": 2.0139876494251894, + "seed": 290612221, + "groupIds": [ + "vXfReGIEP1cvs4DENwZ3s" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736434812, + "startBinding": null, + "endBinding": { + "elementId": "fatyAFozygc0HN-v5nRU0", + "focus": 0.21564184502015712, + "gap": 14.592548165518451 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -178.69352318632787, + -2.0139876494251894 + ] + ] + }, + { + "type": "text", + "version": 1090, + "versionNonce": 1617665277, + "isDeleted": false, + "id": "NMo9QrIPRXbhU3R-JdTPp", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1117.4966157758395, + "y": 532.240804400325, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 109.23098275619242, + "height": 25.818232287827296, + "seed": 1048478237, + "groupIds": [ + "qkwovs2kx-e3xwC2tuyLC" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1639736346313, + "fontSize": 19.860178682944078, + "fontFamily": 1, + "text": "InitGenesis", + "baseline": 17.818232287827296, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "InitGenesis" + }, + { + "type": "arrow", + "version": 3689, + "versionNonce": 74325565, + "isDeleted": false, + "id": "dXiPD3WzFtcIXoXAUQVpo", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1111.360489377364, + "y": 574.978794786868, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 160.39008828337637, + "height": 1.7439226024890786, + "seed": 1345845811, + "groupIds": [ + "qkwovs2kx-e3xwC2tuyLC" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736618805, + "startBinding": null, + "endBinding": { + "elementId": "cN8JVC0YU7ylsJMNyoLMx", + "focus": 0.9324386355587645, + "gap": 11.69532419757843 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 160.39008828337637, + -1.7439226024890786 + ] + ] + }, + { + "type": "text", + "version": 2098, + "versionNonce": 1246295389, + "isDeleted": false, + "id": "UvkrqEakekLXYkvozfkIV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1293.8347907472075, + "y": 807.4233360652089, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 157, + "height": 52, + "seed": 192105491, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + } + ], + "updated": 1639736346313, + "fontSize": 20, + "fontFamily": 1, + "text": "channel status \nINITIALIZING", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "channel status \nINITIALIZING" + }, + { + "type": "text", + "version": 2376, + "versionNonce": 230337501, + "isDeleted": false, + "id": "-7TC_TwN07Wu7u0mWo-Tj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 389.39034630276393, + "y": 832.2011138429868, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 157, + "height": 52, + "seed": 1440011997, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + } + ], + "updated": 1639736365118, + "fontSize": 20, + "fontFamily": 1, + "text": "channel status \nINITIALIZING", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "channel status \nINITIALIZING" + }, + { + "type": "text", + "version": 2430, + "versionNonce": 1098665075, + "isDeleted": false, + "id": "rwTeOg38ovocY7JAorrzM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 391.61256852498576, + "y": 912.8677805096535, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 157, + "height": 52, + "seed": 1111421299, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + } + ], + "updated": 1639736365120, + "fontSize": 20, + "fontFamily": 1, + "text": "channel status \nVALIDATING", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "channel status \nVALIDATING" + }, + { + "type": "diamond", + "version": 942, + "versionNonce": 681234547, + "isDeleted": false, + "id": "ufedJ6N2PtgoNmILgLeR9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 892.7539826664, + "y": 843.3122249540979, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 84.27272727272718, + "height": 71.99999999999996, + "seed": 1428807773, + "groupIds": [ + "IcSLxZ0ZPqcKuPbEB_1Pa" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "umXKeqaL3A2gsELJxrAno", + "type": "arrow" + }, + { + "id": "gMMtc7H_hFXaSquI_JbUz", + "type": "arrow" + } + ], + "updated": 1639736044657 + }, + { + "type": "text", + "version": 865, + "versionNonce": 1603200061, + "isDeleted": false, + "id": "s7XdvhPQZXbsLet4haeHa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 905.4358008482176, + "y": 868.6758613177344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 58.90909090909091, + "height": 21.272727272727266, + "seed": 1395171315, + "groupIds": [ + "IcSLxZ0ZPqcKuPbEB_1Pa" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + }, + { + "id": "1qXvD7xEgjTf4VUM5wDV3", + "type": "arrow" + }, + { + "id": "gMMtc7H_hFXaSquI_JbUz", + "type": "arrow" + } + ], + "updated": 1639736044657, + "fontSize": 16.363636363636367, + "fontFamily": 1, + "text": "Relayer", + "baseline": 14.272727272727266, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "" + }, + { + "type": "arrow", + "version": 2902, + "versionNonce": 1722961043, + "isDeleted": false, + "id": "1qXvD7xEgjTf4VUM5wDV3", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 978.8130053975287, + "y": 880.6132211993461, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 123.2005808003687, + "height": 2.304857840268369, + "seed": 1799383101, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736931474, + "startBinding": { + "elementId": "s7XdvhPQZXbsLet4haeHa", + "gap": 14.46811364022036, + "focus": 0.04298058670290918 + }, + "endBinding": { + "elementId": "Li4WakMROOrh4VO9QnPFJ", + "gap": 5.432315660421394, + "focus": -0.18233773650121993 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 123.2005808003687, + 2.304857840268369 + ] + ] + }, + { + "type": "arrow", + "version": 3188, + "versionNonce": 867197533, + "isDeleted": false, + "id": "gMMtc7H_hFXaSquI_JbUz", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 891.4192255211542, + "y": 879.8675078950861, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 128.6227925647902, + "height": 1.7720335439950077, + "seed": 1850003037, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737001795, + "startBinding": { + "elementId": "s7XdvhPQZXbsLet4haeHa", + "gap": 14.016575327063348, + "focus": -0.016408382124069115 + }, + "endBinding": { + "elementId": "8j6p54tqGlb2RxbOlwOCj", + "gap": 8.017197764711954, + "focus": 0.28650989237142727 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -128.6227925647902, + 1.7720335439950077 + ] + ] + }, + { + "type": "text", + "version": 2088, + "versionNonce": 2054872765, + "isDeleted": false, + "id": "ZzAiVIq7d_EX6jT-x-V8F", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1518.4736796360964, + "y": 856.3122249540979, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 58, + "height": 26, + "seed": 11980573, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + } + ], + "updated": 1639736936916, + "fontSize": 20, + "fontFamily": 1, + "text": "ICS-4", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "ICS-4" + }, + { + "type": "text", + "version": 2537, + "versionNonce": 1647092979, + "isDeleted": false, + "id": "0rLEyoXAuW-fiLkQc16EL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1291.612568524986, + "y": 1034.5344471763203, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 157, + "height": 52, + "seed": 1106024147, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "id": "MUNWjeP_k1E_jhbGobXH1", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + } + ], + "updated": 1639736633733, + "fontSize": 20, + "fontFamily": 1, + "text": "channel status \nVALIDATING", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "channel status \nVALIDATING" + }, + { + "type": "arrow", + "version": 1238, + "versionNonce": 1195870707, + "isDeleted": false, + "id": "Uv1Gs8hixSdQGVRH6KLVi", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 564.4731252919429, + "y": 1005.2089960900624, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 717.7372110734237, + "height": 0.3898469010362078, + "seed": 618800925, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736404648, + "startBinding": { + "elementId": "fatyAFozygc0HN-v5nRU0", + "gap": 4.6938901002905595, + "focus": 0.703170244964692 + }, + "endBinding": { + "elementId": "cN8JVC0YU7ylsJMNyoLMx", + "gap": 1.2355654929522188, + "focus": -0.618625198886333 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 717.7372110734237, + 0.3898469010362078 + ] + ] + }, + { + "type": "arrow", + "version": 1296, + "versionNonce": 1011340179, + "isDeleted": false, + "id": "qUiA0J-sfT84YpVQWlEot", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 568.5308006200647, + "y": 1038.5732629779552, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 706.5167965065758, + "height": 20.51830254733295, + "seed": 11275571, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639736404648, + "startBinding": { + "elementId": "fatyAFozygc0HN-v5nRU0", + "gap": 8.751565428412391, + "focus": 0.7953347606778761 + }, + "endBinding": { + "elementId": "cN8JVC0YU7ylsJMNyoLMx", + "gap": 8.398304731678309, + "focus": -0.812948462577421 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 706.5167965065758, + 20.51830254733295 + ] + ] + }, + { + "type": "text", + "version": 823, + "versionNonce": 1432821821, + "isDeleted": false, + "id": "EbcfOtMt-Y6UtduNdcfzz", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 767.112568524986, + "y": 1059.6455582874312, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 26, + "seed": 1448330877, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639736463164, + "fontSize": 20, + "fontFamily": 1, + "text": "first VSCPacket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "first VSCPacket" + }, + { + "type": "text", + "version": 385, + "versionNonce": 820718237, + "isDeleted": false, + "id": "-Xq5mD2Jj21BEPy1zjD4F", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 884.7792351916523, + "y": 974.4233360652095, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 118, + "height": 26, + "seed": 1370762163, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639736103330, + "fontSize": 20, + "fontFamily": 1, + "text": "CCV Channel", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV Channel" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/spec/app/ics-028-cross-chain-validation/figures/ccv-init-overview.png b/spec/app/ics-028-cross-chain-validation/figures/ccv-init-overview.png new file mode 100644 index 000000000..adbbb609c Binary files /dev/null and b/spec/app/ics-028-cross-chain-validation/figures/ccv-init-overview.png differ diff --git a/spec/app/ics-028-cross-chain-validation/figures/ccv-vsc-overview.excalidraw b/spec/app/ics-028-cross-chain-validation/figures/ccv-vsc-overview.excalidraw new file mode 100644 index 000000000..aeedc2596 --- /dev/null +++ b/spec/app/ics-028-cross-chain-validation/figures/ccv-vsc-overview.excalidraw @@ -0,0 +1,1061 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 818, + "versionNonce": 1168306251, + "isDeleted": false, + "id": "Lzfre0sNq-j_kaO2bid-V", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "angle": 0, + "x": 1029.80728608124, + "y": 370.7566693985417, + "strokeColor": "#e67700", + "backgroundColor": "#e67700", + "width": 299.9999999999998, + "height": 425.0000000000002, + "seed": 875787883, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "umXKeqaL3A2gsELJxrAno", + "type": "arrow" + }, + { + "id": "JdK3ZV05Kgdm0t1Wbwel3", + "type": "arrow" + }, + { + "id": "Hg015OniyGFjM7QYWPufU", + "type": "arrow" + }, + { + "id": "1qXvD7xEgjTf4VUM5wDV3", + "type": "arrow" + } + ], + "updated": 1639738032458 + }, + { + "type": "rectangle", + "version": 757, + "versionNonce": 1380852165, + "isDeleted": false, + "id": "tiEtkt2XHCbZp9ZIwwoVN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 20, + "angle": 0, + "x": 562.0850638590175, + "y": 364.8677805096529, + "strokeColor": "#1864ab", + "backgroundColor": "#1864ab", + "width": 298.33333333333326, + "height": 430.55555555555543, + "seed": 1114932363, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "DURyamuWvIhE5o-GLqnvp", + "type": "arrow" + }, + { + "id": "-sQ1lqt81ejZKXt6Q2HFf", + "type": "arrow" + }, + { + "id": "gMMtc7H_hFXaSquI_JbUz", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + } + ], + "updated": 1639737978946 + }, + { + "type": "text", + "version": 447, + "versionNonce": 880535685, + "isDeleted": false, + "id": "DcYomqVwJx0rUjMvf5S5b", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 770.5555555555554, + "y": 263.77777777777777, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 391, + "height": 36, + "seed": 484186313, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639738049788, + "fontSize": 28, + "fontFamily": 1, + "text": "CCV - Validator Set Update", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV - Validator Set Update" + }, + { + "type": "rectangle", + "version": 2207, + "versionNonce": 1255900293, + "isDeleted": false, + "id": "LZ-zpiSZrRKjK21SSB5me", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 757.6961749701286, + "y": 419.36778050965296, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 60.33333333333322, + "height": 343.3333333333332, + "seed": 689622821, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "gdGzxModJaJKV9kGsQ0lN" + }, + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "type": "arrow", + "id": "L-m2OjIz_DM9FZL_aw8q1" + }, + { + "type": "arrow", + "id": "MUNWjeP_k1E_jhbGobXH1" + }, + { + "type": "arrow", + "id": "Z366__sEfBTh-UkidbmWs" + }, + { + "id": "L-SUQMkaBFUSpHYaSKHI8", + "type": "arrow" + }, + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "jz87Jfblhql4prXUNauec", + "type": "arrow" + }, + { + "id": "XqKtKE8gPBp_eAGHN-Ucc", + "type": "arrow" + }, + { + "id": "Uv1Gs8hixSdQGVRH6KLVi", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + }, + { + "id": "UqXi4k7D7LBkElLUl9bRY", + "type": "arrow" + }, + { + "id": "WEyZi3lMqkJ8FhlR5xFl1", + "type": "arrow" + }, + { + "id": "soJdQZVXQySCwfBjVwVtd", + "type": "arrow" + }, + { + "id": "0s6Gf0lhs4RxTdpZNUqsV", + "type": "arrow" + } + ], + "updated": 1639737943282 + }, + { + "type": "rectangle", + "version": 2244, + "versionNonce": 1722365067, + "isDeleted": false, + "id": "bV7tck2JEoFVq7rV8HSWu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1066.9739527479064, + "y": 426.03444717631953, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 60.33333333333322, + "height": 344.4444444444449, + "seed": 1430831243, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "gdGzxModJaJKV9kGsQ0lN" + }, + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + }, + { + "type": "arrow", + "id": "L-m2OjIz_DM9FZL_aw8q1" + }, + { + "type": "arrow", + "id": "MUNWjeP_k1E_jhbGobXH1" + }, + { + "type": "arrow", + "id": "Z366__sEfBTh-UkidbmWs" + }, + { + "id": "L-SUQMkaBFUSpHYaSKHI8", + "type": "arrow" + }, + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + }, + { + "id": "jz87Jfblhql4prXUNauec", + "type": "arrow" + }, + { + "id": "XqKtKE8gPBp_eAGHN-Ucc", + "type": "arrow" + }, + { + "id": "Uv1Gs8hixSdQGVRH6KLVi", + "type": "arrow" + }, + { + "id": "qUiA0J-sfT84YpVQWlEot", + "type": "arrow" + }, + { + "id": "UqXi4k7D7LBkElLUl9bRY", + "type": "arrow" + }, + { + "id": "soJdQZVXQySCwfBjVwVtd", + "type": "arrow" + }, + { + "id": "pFUtKITgaRpYy9le7pyUv", + "type": "arrow" + }, + { + "id": "0s6Gf0lhs4RxTdpZNUqsV", + "type": "arrow" + } + ], + "updated": 1639737885278 + }, + { + "type": "text", + "version": 332, + "versionNonce": 1963104741, + "isDeleted": false, + "id": "Tk2sS1hhVtDPd_0ZZPV5A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 883.6961749701285, + "y": 418.9233360652086, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 118, + "height": 26, + "seed": 1397655621, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639737792689, + "fontSize": 20, + "fontFamily": 1, + "text": "CCV Channel", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV Channel" + }, + { + "type": "arrow", + "version": 1307, + "versionNonce": 205471595, + "isDeleted": false, + "id": "soJdQZVXQySCwfBjVwVtd", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 827.9648271771262, + "y": 456.76727084030205, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 230.2388007650577, + "height": 1.199251209361762, + "seed": 888195723, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639738081704, + "startBinding": { + "elementId": "LZ-zpiSZrRKjK21SSB5me", + "focus": -0.7802079726658296, + "gap": 9.9353188736643 + }, + "endBinding": { + "elementId": "bV7tck2JEoFVq7rV8HSWu", + "focus": 0.8289360654308046, + "gap": 8.770324805722453 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 230.2388007650577, + -1.199251209361762 + ] + ] + }, + { + "type": "text", + "version": 1277, + "versionNonce": 1778767302, + "isDeleted": false, + "id": "fYIVRw6i_u72mXigtRsA1", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 611.641637493901, + "y": 490.7765630941593, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 113, + "height": 25, + "seed": 112951915, + "groupIds": [ + "K8nZE1MmcDWYA2kWmR_eG" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1642169450939, + "fontSize": 19.860178682944078, + "fontFamily": 1, + "text": "provide VSC", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "provide VSC" + }, + { + "type": "arrow", + "version": 3884, + "versionNonce": 1769463531, + "isDeleted": false, + "id": "jxiIfnYuV__VAbb-JgmY7", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 607.3900197173294, + "y": 533.5145534807024, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 130.15389165851616, + "height": 0.7185972546147923, + "seed": 1988390341, + "groupIds": [ + "K8nZE1MmcDWYA2kWmR_eG" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737810404, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 130.15389165851616, + -0.7185972546147923 + ] + ] + }, + { + "type": "text", + "version": 1149, + "versionNonce": 1883141413, + "isDeleted": false, + "id": "SX9_IjHVCeJsw5zjihNjk", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 605.7013096558908, + "y": 621.3587865243416, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 129, + "height": 52, + "seed": 786765227, + "groupIds": [ + "hv2luiwiY1Fn7podKPU9I" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1639737972442, + "fontSize": 20, + "fontFamily": 1, + "text": "register \nVSC maturity", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "register \nVSC maturity" + }, + { + "type": "arrow", + "version": 3542, + "versionNonce": 283810731, + "isDeleted": false, + "id": "jxc2-4ezQeQEtWLvEmWmn", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 739.7928637114015, + "y": 691.4171997739187, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 142.84735305604875, + "height": 1, + "seed": 1843441285, + "groupIds": [ + "hv2luiwiY1Fn7podKPU9I" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737972442, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -142.84735305604875, + -1 + ] + ] + }, + { + "type": "text", + "version": 1383, + "versionNonce": 2037642053, + "isDeleted": false, + "id": "NQpfW4a7Hwtvr-8EPhJwk", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1146.3083041605673, + "y": 519.2210075386038, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 93, + "height": 26, + "seed": 38614437, + "groupIds": [ + "i37LDlVmVV-RTaLNpmAyT" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1639738027560, + "fontSize": 19.860178682944078, + "fontFamily": 1, + "text": "apply VSC", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "apply VSC" + }, + { + "type": "arrow", + "version": 4028, + "versionNonce": 2120978315, + "isDeleted": false, + "id": "HHI5Vc7fqPn6VuRzXoIaY", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1145.0566863839958, + "y": 561.9589979251468, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 112.37753627426332, + "height": 0.14750106233304905, + "seed": 2089165611, + "groupIds": [ + "i37LDlVmVV-RTaLNpmAyT" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639738027560, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 112.37753627426332, + -0.14750106233304905 + ] + ] + }, + { + "type": "text", + "version": 943, + "versionNonce": 742587973, + "isDeleted": false, + "id": "hlLx6LN5_BUDJUO2t7mzz", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 733.3628416367951, + "y": 381.0344471763196, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 112, + "height": 26, + "seed": 1737169669, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737575703, + "fontSize": 20, + "fontFamily": 1, + "text": "CCV Module", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV Module" + }, + { + "type": "text", + "version": 554, + "versionNonce": 2008417445, + "isDeleted": false, + "id": "yqi6dzvpzblq17ysowkTo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 646.9183971923508, + "y": 326.03444717631965, + "strokeColor": "#364fc7", + "backgroundColor": "transparent", + "width": 138, + "height": 26, + "seed": 578772677, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639738044934, + "fontSize": 20, + "fontFamily": 1, + "text": "Provider Chain", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Provider Chain" + }, + { + "type": "text", + "version": 993, + "versionNonce": 321838219, + "isDeleted": false, + "id": "tLJ6siXC9VChVdOcHR37l", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1052.2517305256843, + "y": 387.1455582874308, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 112, + "height": 26, + "seed": 2057296357, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737605484, + "fontSize": 20, + "fontFamily": 1, + "text": "CCV Module", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "CCV Module" + }, + { + "type": "text", + "version": 1131, + "versionNonce": 1794981669, + "isDeleted": false, + "id": "Y19fC1uuzabCFyV0s97Ip", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1106.4739527479067, + "y": 337.14555828743073, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 148, + "height": 26, + "seed": 1161289003, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639738043115, + "fontSize": 20, + "fontFamily": 1, + "text": "Consumer Chain", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Consumer Chain" + }, + { + "type": "text", + "version": 1307, + "versionNonce": 2012879595, + "isDeleted": false, + "id": "_72T1dsBHZpELeZ7ogywj", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1152.2505196648644, + "y": 656.2274627737531, + "strokeColor": "#000000", + "backgroundColor": "#e67700", + "width": 129, + "height": 52, + "seed": 1106855979, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "Mi1I3PpLY1MQGIPxt7fC1", + "type": "arrow" + } + ], + "updated": 1639738011223, + "fontSize": 20, + "fontFamily": 1, + "text": "notify \nVSC maturity", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "notify \nVSC maturity" + }, + { + "type": "arrow", + "version": 3677, + "versionNonce": 644692101, + "isDeleted": false, + "id": "XoX-hR8ww2fkzA2itN0ni", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1175.6410803550361, + "y": 667.6706739726183, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 104.36858191293186, + "height": 0.16257572706570045, + "seed": 1799283717, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639738005691, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -104.36858191293186, + -0.16257572706570045 + ] + ] + }, + { + "type": "text", + "version": 751, + "versionNonce": 1655403435, + "isDeleted": false, + "id": "USOjGct8Q4FclCqyREAjw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1093.4180655283808, + "y": 583.8332539375602, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 86, + "height": 52, + "seed": 1928733029, + "groupIds": [ + "TQm0hBeKsv0wgVtm-4fl4" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639737953138, + "fontSize": 20, + "fontFamily": 1, + "text": "unbonding\nperiod", + "baseline": 44, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "unbonding\nperiod" + }, + { + "type": "arrow", + "version": 1038, + "versionNonce": 112668805, + "isDeleted": false, + "id": "dYJRFcc1PLcSHVaOF3szx", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1079.9457793756937, + "y": 558.2437284418835, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 1.0563467444264916, + "height": 101.58143746887185, + "seed": 47081765, + "groupIds": [ + "TQm0hBeKsv0wgVtm-4fl4" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737953138, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "bar", + "endArrowhead": "bar", + "points": [ + [ + 0, + 0 + ], + [ + 1.0563467444264916, + 101.58143746887185 + ] + ] + }, + { + "type": "text", + "version": 708, + "versionNonce": 484556363, + "isDeleted": false, + "id": "RzN1Ejg0DNhbMX38KakLy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 869.5850638590175, + "y": 504.923336065209, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 104, + "height": 26, + "seed": 253438597, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1639737822982, + "fontSize": 20, + "fontFamily": 1, + "text": "VSCPacket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "VSCPacket" + }, + { + "type": "text", + "version": 734, + "versionNonce": 1150499979, + "isDeleted": false, + "id": "sQR88kHWeEXVsXgq8udT2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 865.140619414573, + "y": 631.5900027318755, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 26, + "seed": 894891877, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "UdbNDUj-KimwGAw0JZmAm" + } + ], + "updated": 1639738095313, + "fontSize": 20, + "fontFamily": 1, + "text": " VSCPacket-ACK", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": " VSCPacket-ACK" + }, + { + "type": "arrow", + "version": 1349, + "versionNonce": 546561963, + "isDeleted": false, + "id": "pFUtKITgaRpYy9le7pyUv", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 834.2710945360454, + "y": 543.3834209497197, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 223.68812838986992, + "height": 9.194889183243276, + "seed": 1819697989, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639737943282, + "startBinding": null, + "endBinding": { + "elementId": "bV7tck2JEoFVq7rV8HSWu", + "focus": 0.2540482168158336, + "gap": 9.014729821990954 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 223.68812838986992, + 9.194889183243276 + ] + ] + }, + { + "type": "arrow", + "version": 1410, + "versionNonce": 664121893, + "isDeleted": false, + "id": "0s6Gf0lhs4RxTdpZNUqsV", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1054.7490645492014, + "y": 669.4161730806497, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 220.74579277912267, + "height": 10.675950532715547, + "seed": 105495787, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1639738019004, + "startBinding": { + "elementId": "bV7tck2JEoFVq7rV8HSWu", + "focus": -0.39790907943453063, + "gap": 12.224888198704889 + }, + "endBinding": { + "elementId": "LZ-zpiSZrRKjK21SSB5me", + "focus": 0.5273001796810546, + "gap": 15.973763466616901 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + -220.74579277912267, + 10.675950532715547 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/spec/app/ics-028-cross-chain-validation/figures/ccv-vsc-overview.png b/spec/app/ics-028-cross-chain-validation/figures/ccv-vsc-overview.png new file mode 100644 index 000000000..5adf805a8 Binary files /dev/null and b/spec/app/ics-028-cross-chain-validation/figures/ccv-vsc-overview.png differ diff --git a/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md b/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md new file mode 100644 index 000000000..2c5a71c01 --- /dev/null +++ b/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md @@ -0,0 +1,167 @@ + +# CCV: Overview and Basic Concepts +[↑ Back to main document](./README.md) + + +## Outline +- [Security Model](#security-model) +- [Motivation](#motivation) +- [Definitions and Overview](#definitions-and-overview) + - [Channel Initialization](#channel-initialization) + - [Validator Set Update](#validator-set-update) + + + +# Security Model +[↑ Back to Outline](#outline) + +We consider chains that use a proof of stake mechanism based on the model of [weak subjectivity](https://blog.ethereum.org/2014/11/25/proof-stake-learned-love-weak-subjectivity/) +in order to strengthen the assumptions required by the underlying consensus engine +(e.g., [Tendermint](https://arxiv.org/pdf/1807.04938.pdf) requires that less than a third of the voting power is Byzantine). + +> **Background**: The next block in a blockchain is *validated* and *voted* upon by a set of pre-determined *full nodes*; these pre-determined full nodes are also known as *validators*. +We refer to the validators eligible to validate a block as that block's *validator set*. +To be part of the validator set, a validator needs to *bond* (i.e., lock, stake) an amount of tokens for a (minimum) period of time, known as the *unbonding period*. +The amount of tokens bonded gives a validator's *voting power*. +When a validator starts unbonding some of its tokens, its voting power is reduced immediately, +but the tokens are unbonded (i.e., unlocked) only after the unbonding period has elapsed. +If a validator misbehaves (e.g., validates two different blocks at the same height), then the system can slash the validator's bonded tokens that gave its voting power during the misbehavior. +This prevents validators from misbehaving and immediately exiting with their tokens, +i.e., the unbonding period enables the system to punish misbehaving validators after the misbehaviors are committed. +For more details, take a look at the [Tendermint Specification](https://github.com/tendermint/spec/blob/master/spec/core/data_structures.md) +and the [Light Client Specification](https://github.com/tendermint/spec/blob/master/spec/light-client/verification/verification_002_draft.md#part-i---tendermint-blockchain). + +In the context of CCV, the validator sets of the consumer chains are chosen based on the tokens validators bonded on the provider chain, +i.e., are chosen from the validator set of the provider chain. +When validators misbehave on the consumer chains, their tokens bonded on the provider chain are slashed. +As a result, the security gained from the value of the tokens bonded on the provider chain is shared with the consumer chains. + +Similarly to the single-chain approach, when a validator starts unbonding some of its bonded tokens, its voting power is reduced on all chains (i.e., provider chain and consumer chains); +yet, due to delays in the communication over the IBC protocol (e.g., due to relaying packets), the voting power is not reduced immediately on the consumer chains. +A further consequence of CCV is that the tokens are unbonded only after the unbonding period has elapsed on all chains starting from the moment the corresponding voting power was reduced. +Thus, CCV may delay the unbonding of tokens validators bonded on the provider chain. +For more details, take a look at the [Interchain Security light paper](https://github.com/cosmos/gaia/blob/main/docs/interchain-security.md). + +# Motivation +[↑ Back to Outline](#outline) + +CCV is a primitive (i.e., a building block) that enables arbitrary shared security models: The security of a chain can be composed of security transferred from multiple provider chains including the chain itself (a consumer chain can be its own provider). As a result, CCV enables chains to borrow security from more established chains (e.g., Cosmos Hub), in order to boost their own security, i.e., increase the cost of attacking their networks. +> **Intuition**: For example, for chains based on Tendermint consensus, a variety of attacks against the network are possible if an attacker acquire 1/3+ or 2/3+ of all bonded tokens. Since the market cap of newly created chains could be relatively low, an attacker could realistically acquire sufficient tokens to pass these thresholds. As a solution, CCV allows the newly created chains to use validators that have stake on chains with a much larger market cap and, as a result, increase the cost an attacker would have to pay. + +Moreover, CCV enables *hub minimalism*. In a nutshell, hub minimalism entails keeping a hub in the Cosmos network (e.g., the Cosmos Hub) as simple as possible, with as few features as possible in order to decrease the attack surface. CCV enables moving distinct features (e.g., DEX) to independent chains that are validated by the same set of validators as the hub. + +> **Versioning**: Note that CCV will be developed progressively. +> - The V1 release will require the validator set of a consumer chain to be entirely provided by the provider chain. In other words, once a provider chain agrees to provide security to a consumer chain, the entire validator set of the provider chain MUST validate also on the consumer chain. +> - The V2 release will allow the provider chain validators to opt-in to participate as validators on the consumer chain. It is up to each consumer chain to establish the benefits that provider chain validators receive for their participation. +> +> For more details on the planned releases, take a look at the [Interchain Security light paper](https://github.com/cosmos/gaia/blob/main/docs/interchain-security.md#the-interchain-security-stack). + +# Definitions and Overview +[↑ Back to Outline](#outline) + +This section defines the new terms and concepts introduced by CCV and provides an overview of CCV. + +**Provider Chain**: The blockchain that provides security, i.e., manages the validator set of the consumer chain. + +**Consumer Chain**: The blockchain that consumes security, i.e., enables the provider chain to manage its validator set. + +> Note that in the current version the validator set of the consumer chain is entirely provided by the provider chain. + +Both the provider and the consumer chains are [application-specific blockchains](https://docs.cosmos.network/v0.44/intro/why-app-specific.html), +i.e., the state-machine is typically connected to the underlying consensus engine via an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/). +Thus, we refer to the state-machine as an ABCI application. +For ease of presentation, this specification considers a modular paradigm, +i.e., the functionality of the ABCI application is separated into multiple modules, like the approach adopted by [Cosmos SDK](https://docs.cosmos.network/v0.44/basics/app-anatomy.html#modules). + +**CCV Module**: The module that implements the CCV protocol. Both the provider and the consumer chains have each their own CCV module. +Furthermore, the functionalities provided by the CCV module differ between the provider chain and the consumer chains. +For brevity, we use *provider CCV module* and *consumer CCV module* to refer to the CCV modules on the provider chain and on the consumer chains, respectively. + +**CCV Channel**: A unique, ordered IBC channel that is used by the provider CCV module to exchange IBC packets with a consumer CCV module. +Note that there is a separate CCV channel for every consumer chain. + +> Note that the IBC handler interface, the IBC relayer module interface, and both IBC channels and IBC packets are as defined in [ICS 25](../../core/ics-025-handler-interface), [ICS 26](../../core/ics-026-routing-module), and [ICS 4](../../core/ics-004-channel-and-packet-semantics), respectively. + +**Validator Set Change (VSC)**: A change in the validator set of the provider chain that must be reflected in the validator sets of the consumer chains. +A VSC consists of a batch of validator updates, i.e., changes in the voting power granted to validators on the provider chain and, due to CCV, also on the consumer chains. + +> **Background**: In the context of single-chain validation, the changes of the validator set are triggered by the *Staking module*, +> i.e., a module of the ABCI application that implements the proof of stake mechanism needed by the [security model](#security-model). +> For more details, take a look at the [Staking module documentation](https://docs.cosmos.network/master/modules/staking/) of Cosmos SDK. + +Every VSC consists of a batch of validator updates, some of which can decrease the voting power granted to validators. +These decreases may be a consequence of unbonding operations (e.g., unbonding delegations) on the provider chain. +which MUST NOT complete before reaching maturity on both the provider and all the consumer chains, +i.e., the *unbonding period* (denoted as `UnbondingPeriod`) has elapsed on both the provider and all the consumer chains. +Thus, a *VSC reaching maturity* on a consumer chain means that all the unbonding operations that resulted in validator updates included in that VSC have matured on the consumer chain. + +> **Background**: An *unbonding operation* is any operation of unbonding an amount of the tokens a validator bonded. Note that the bonded tokens correspond to the validator's voting power. Unbonding operations have two components: +> - The *initiation*, e.g., a delegator requests their delegated tokens to be unbonded. The initiation of an operation of unbonding an amount of the tokens a validator bonded results in a change in the voting power of that validator. +> - The *completion*, e.g., the tokens are actually unbonded and transferred back to the delegator. To complete, unbonding operations must reach *maturity*, i.e., `UnbondingPeriod` must elapse since the operations were initiated. + +> **Note**: Time periods are measured in terms of the block time, i.e., `currentTimestamp()` (as defined in [ICS 24](../../core/ics-024-host-requirements)). +> As a result, a consumer chain MAY start the unbonding period for every VSC that it applies in a block at any point during that block. + +CCV must handle the following types of operations: +- **Channel Initialization**: Create unique, ordered IBC channels between the provider chain and every consumer chain. +- **Validator Set Update**: It is a two-part operation, i.e., + - update the validator sets of all the consumer chains based on the information obtained from the *provider Staking module* (i.e., the Staking module on the provider chain) on the amount of tokens bonded by validators on the provider chain; + - and enable the timely completion (cf. the unbonding periods on the consumer chains) of unbonding operations (i.e., operations of unbonding bonded tokens). + +## Channel Initialization +[↑ Back to Outline](#outline) + +The following Figure shows an overview of the CCV Channel initialization. + +![Channel Initialization Overview](./figures/ccv-init-overview.png?raw=true) + +Consumer chains are created through governance proposals. For details on how governance proposals work, take a look at the [Governance module documentation](https://docs.cosmos.network/master/modules/gov/) of Cosmos SDK. + +The channel initialization consists of four phases: +- **Create clients**: The provider CCV module handles every passed proposal to spawn a new consumer chain. + Once it receives a proposal, it creates a client of the consumer chain (as defined in [ICS 2](../../core/ics-002-client-semantics)). + Then, the operators of validators in the validator set of the provider chain must each start a full node (i.e., a validator) of the consumer chain. + Once the consumer chain starts, the application receives an `InitChain` message from the consensus engine + (for more details, take a look at the [ABCI documentation](https://docs.tendermint.com/v0.34/spec/abci/abci.html#initchain)). + The `InitChain` message triggers the call to the `InitGenesis()` method of the consumer CCV module, which creates a client of the provider chain. + For client creation, both a `ClientState` and a `ConsensusState` are necessary (as defined in [ICS 2](../../core/ics-002-client-semantics)); + both are contained in the `GenesisState` of the consumer CCV module. + The `GenesisState` is distributed to all operators that need to start a full node of the consumer chain + (the mechanism of distributing the `GenesisState` is outside the scope of this specification). + > Note that although the mechanism of distributing the `GenesisState` is outside the scope of this specification, + a possible approach would entail the creator of the proposal to spawn the new consumer chain to distribute the `GenesisState` via the gossip network + and to add the hash of `GenesisState` in the proposal. + > + > Note that at genesis, the validator set of the consumer chain matches the validator set of the provider chain. +- **Connection handshake**: A relayer (as defined in [ICS 18](../../relayer/ics-018-relayer-algorithms)) is responsible for initiating the connection handshake (as defined in [ICS 3](../../core/ics-003-connection-semantics)). +- **Channel handshake**: A relayer is responsible for initiating the channel handshake (as defined in [ICS 4](../../core/ics-004-channel-and-packet-semantics)). + The channel handshake must be initiated on the consumer chain. + The handshake consists of four messages that need to be received for a channel built on top of the expected clients. + We omit the `ChanOpenAck` message since it is not relevant for the overview. + - *OnChanOpenInit*: On receiving the *FIRST* `ChanOpenInit` message, the consumer CCV module sets the status of its end of the CCV channel to `INITIALIZING`. + - *OnChanOpenTry*: On receiving the *FIRST* `ChanOpenTry` message, the provider CCV module sets the status of its end of the CCV channel to `INITIALIZING`. + - *OnChanOpenConfirm*: On receiving the *FIRST* `ChanOpenConfirm` message, the provider CCV module sets the status of its end of the CCV channel to `VALIDATING`. +- **Channel completion**: Once the provider chain sets the status of the CCV channel to `VALIDATING`, + it provides a VSC (i.e., validator set change) to the consumer chain (see [next section](#validator-set-update)). + On receiving the *FIRST* `VSCPacket`, the consumer CCV module sets the status of its end of the CCV channel to `VALIDATING`. + +> **Discussion**: As long as the [assumptions required by CCV](./system_model_and_properties.md#assumptions) hold (e.g., *Correct Relayer*), every governance proposal to spawn a new consumer chain that passes on the provider chain eventually results in a CCV channel being created. Furthermore, the "*FIRST*" keyword in the above description ensures the uniqueness of the CCV channel, i.e., all subsequent attempts to create another CCV channel to the same consumer chain will fail. + +For a more detailed description of Channel Initialization, take a look at the [technical specification](./technical_specification.md#initialization). + +## Validator Set Update +[↑ Back to Outline](#outline) + +In the context of VSCs, the CCV module enables the following functionalities: + - On the provider chain, + - **provide** VSCs to the consumer chains, for them to update their validator sets according to the validator set of the provider chain; + - **register** VSC maturity notifications from the consumer chain. + - On every consumer chain, + - **apply** the VSCs provided by the provider chain to the validator set of the consumer chain; + - **notify** the provider chain that the provided VSCs have matured on this consumer chain. + +These functionalities are depicted in the following Figure that shows an overview of the Validator Set Update operation of CCV. + +![Validator Set Update Overview](./figures/ccv-vsc-overview.png?raw=true) + +For a more detailed description of Validator Set Update, take a look at the [technical specification](./technical_specification.md#validator-set-update). \ No newline at end of file diff --git a/spec/app/ics-028-cross-chain-validation/system_model_and_properties.md b/spec/app/ics-028-cross-chain-validation/system_model_and_properties.md new file mode 100644 index 000000000..4af60d623 --- /dev/null +++ b/spec/app/ics-028-cross-chain-validation/system_model_and_properties.md @@ -0,0 +1,281 @@ + +# CCV: System Model and Properties +[↑ Back to main document](./README.md) + + +## Outline +- [Assumptions](#assumptions) +- [Desired Properties](#desired-properties) + - [Validator sets, validator updates and VSCs](#validator-sets-validator-updates-and-vscs) + - [Staking Module Interface](#staking-module-interface) + - [Validator Set Update](#validator-set-update) +- [Correctness Reasoning](#correctness-reasoning) + +## Assumptions +[↑ Back to Outline](#outline) + +As part of an ABCI application, CCV interacts with both the consensus engine (via ABCI) and other application modules, such as the Staking module. +As an IBC application, CCV interacts with external relayers (defined in [ICS 18](../../relayer/ics-018-relayer-algorithms)). +In this section we specify what we assume about these other components, +i.e., CCV relies on the following assumptions: *Safe Blockchain*, *Live Blockchain*, *Correct Relayer*, *Validator Update Provision*, and *Unbonding Safety*. + +Intuitively, CCV safety relies on the *Safe Blockchain* assumption, +i.e., neither *Live Blockchain* and *Correct Relayer* are required for safety. +Note though that CCV liveness relies on both *Live Blockchain* and *Correct Relayer* assumptions; +furthermore, the *Correct Relayer* assumption relies on both *Safe Blockchain* and *Live Blockchain* assumptions. +The *Validator Update Provision* and *Unbonding Safety* assumptions define what is needed from the provider Staking module. +A more thorough discussion of the environment in which CCV operates is given in the section [Placing CCV within an ABCI Application](./technical_specification.md#placing-ccv-within-and-abci-application). + +- ***Safe Blockchain***: Both the provider and the consumer chains are *safe*. This means that, for every chain, the underlying consensus engine satisfies safety (e.g., the chain does not fork) and the execution of the state machine follows the described protocol. + +- ***Live Blockchain***: Both the provider and the consumer chains are *live*. This means that, for every chain, the underlying consensus engine satisfies liveness (i.e., new blocks are eventually added to the chain). + +> **Note**: Both *Safe Blockchain* and *Live Blockchain* assumptions require the consensus engine's assumptions to hold, e.g., less than a third of the voting power is Byzantine. For more details, take a look at the [Tendermint Paper](https://arxiv.org/pdf/1807.04938.pdf). + +- ***Correct Relayer***: There is at least one *correct*, *live* relayer between the provider and consumer chains -- every packet sent on the CCV channel is relayed to the receiving end before the packet timeout elapses. + Clearly, the CCV protocol is responsible of setting the packet timeouts (i.e., `timeoutHeight` and `timeoutTimestamp`) such that the *Correct Relayer* assumption is feasible. + +> **Discussion**: IBC relies on timeouts to signal that a sent packet is not going to be received on the other end. +> Once an ordered IBC channel timeouts, the channel is closed (see [ICS 4](../../core/ics-004-channel-and-packet-semantics)). +> The *Correct Relayer* assumption is necessary to ensure that the CCV channel **cannot** ever timeout and, as a result, cannot transit to the closed state. +> +> **In practice**, the *Correct Relayer* assumption is realistic since any validator could play the role of the relayer and it is in the best interest of correct validators to successfully relay packets. +> +> The following strategy is a practical example of how to ensure the *Correct Relayer* assumption holds. +> Let S denote the sending chain and D the destination chain; +> and let `drift(S,D)` be the time drift between S and D, +> i.e., `drift(S,D) = S.currentTimestamp() - D.currentTimestamp()` (`drift(S,D) > 0` means that S is "ahead" of D). +> For every packet, S only sets `timeoutTimestamp = S.currentTimestamp() + to`, with `to` an application-level parameter. +> The `timeoutTimestamp` indicates *a timestamp on the destination chain* after which the packet will no longer be processed (cf. [ICS 4](../../core/ics-004-channel-and-packet-semantics)). +> Therefore, the packet MUST be relayed within a time period of `to - drift(S,D)`, +> i.e., `to - drift(S,D) > RTmax`, where `RTmax` is the maximum relaying time across all packet. +> Theoretically, choosing the value of `to` requires knowing the value of `drift(S,D)` (i.e., `to > drift(S,D)`); +> yet, `drift(S,D)` is not known at a chain level. +> In practice, choosing `to` such that `to >> drift(S,D)` and `to >> RTmax`, e.g., `to = 4 weeks`, makes the *Correct Relayer* assumption feasible. + +The following assumptions define the guarantees CCV expects from the provider Staking module. +- ***Validator Update Provision***: Let `{U1, U2, ..., Ui}` be a batch of validator updates applied (by the provider Staking module) to the validator set of the provider chain at the end of a block `B` with timestamp `t`. + Then, the *first* batch of validator updates obtained (by the provider CCV module) from the provider Staking module at time `t` MUST be exactly the batch `{U1, U2, ..., Ui}`. + +- ***Unbonding Safety***: Let `uo` be any unbonding operation that starts with an unbonding transaction being executed + and completes with the event that returns the corresponding stake; + let `U(uo)` be the validator update caused by initiating `uo`; + let `vsc(uo)` be the VSC that contains `U(uo)`. + Then, + - (*unbonding initiation*) the provider CCV module MUST be notified of `uo`'s initiation before receiving `U(uo)`; + - (*unbonding completion*) `uo` MUST NOT complete on the provider chain before the provider chain registers notifications of `vsc(uo)`'s maturity from all consumer chains. + +> **Note**: Depending on the implementation, the (*unbonding initiation*) part of the *Unbonding Safety* MAY NOT be necessary for validator unbonding operations. + +## Desired Properties +[↑ Back to Outline](#outline) + +The following properties are concerned with **one provider chain** providing security to **multiple consumer chains**. +Between the provider chain and each consumer chain, a separate (unique) CCV channel is established. + +First, we define the properties for the CCV channels. Then, we define the guarantees provided by CCV. + +- ***Channel Uniqueness***: The channel between the provider chain and a consumer chain MUST be unique. +- ***Channel Validity***: If a packet `P` is received by one end of a CCV channel, then `P` MUST have been sent by the other end of the channel. +- ***Channel Order***: If a packet `P1` is sent over a CCV channel before a packet `P2`, then `P2` MUST NOT be received by the other end of the channel before `P1`. +- ***Channel Liveness***: Every packet sent over a CCV channel MUST eventually be received by the other end of the channel. + +CCV provides the following system invariants: +- ***Validator Set Invariant***: Every validator set on any consumer chain MUST either be or have been a validator set on the provider chain. +- ***Voting Power Invariant***: Let + - `pBonded(t,val)` be the number of tokens bonded by validator `val` on the provider chain at time `t`; + note that `pBonded(t,val)` includes also unbonding tokens (i.e., tokens in the process of being unbonded); + - `VP(T)` be the voting power associated to a number `T` of tokens; + - `Power(cc,t,val)` be the voting power granted to a validator `val` on a consumer chain `cc` at time `t`. + + Then, for all times `t` and `s`, all consumer chains `cc`, and all validators `val`, + ``` + t <= s <= t + UnbondingPeriod: Power(cc,t,val) <= VP(pBonded(s,val)) + ``` + + > **Intuition**: + > - The *Voting Power Invariant* ensures that validators that validate on the consumer chain have enough tokens bonded on the provider chain for a sufficient amount of time such that the security model holds. + > This means that if the validators misbehave on the consumer chain, their tokens bonded on the provider chain can be slashed during the unbonding period. + > For example, if one unit of voting power requires `1.000.000` bonded tokens (i.e., `VP(1.000.000)=1`), + > then a validator that gets one unit of voting power on the consumer chain must have at least `1.000.000` tokens bonded on the provider chain for at least `UnbondingPeriod`. + +Before we define the properties of CCV needed for these invariants to hold, +we provide a short discussion on how the validator set, the validator updates, and the VSCs relates in the context of multiple chains. + +### Validator sets, validator updates and VSCs +[↑ Back to Outline](#outline) + +Every chain consists of a sequence of blocks. +At the end of each block, validator updates (i.e., changes in the validators voting power) results in changes in the validator set of the next block. +Thus, the sequence of blocks produces a sequence of validator updates and a sequence of validator sets. +Furthermore, the sequence of validator updates on the provider chain results in a sequence of VSCs to all consumer chains. +Ideally, this sequence of VSCs is applied by every consumer chain, resulting in a sequence of validator sets identical to the one on the provider chain. +However, in general this need not be the case. The reason is twofold: +- first, given any two chains `A` and `B`, we cannot assume that `A`'s rate of adding new block is the same as `B`'s rate + (i.e., we consider the sequences of blocks of any two chains to be completely asynchronous); +- and second, due to relaying delays, we cannot assume that the rate of sending VSCs matches the rate of receiving VSCs. + +As a result, is it possible for multiple VSCs to be received by a consumer chain within the same block and be applied together at the end of the block, +i.e., the validator updates within the VSCs are being *aggregated* by keeping only the latest update per validator. +As a consequence, some validator sets on the provider chain are not existing on all consumer chains. +In other words, the validator sets on each consumer chain form a *subsequence* of the validator sets on the provider chain. +Nonetheless, as a **requirement of CCV**, *all the validator updates on the provider chain MUST be included in the sequence of validator sets on all consumer chains*. + +This is possible since every validator update contains *the absolute voting power* of that validator. +Given a validator `val`, the sequence of validator updates targeting `val` (i.e., updates of the voting power of `val`) is the prefix sum of the sequence of relative changes of the voting power of `val`. +Thus, given a validator update `U` targeting `val` that occurs at at a time `t`, +`U` *sums up* all the relative changes of the voting power of `val` that occur until `t`, +i.e., `U = c_1+c_2+...+c_i`, such that `c_i` is the last relative change that occurs by `t`. +Note that relative changes are integer values. + +As a consequence, CCV can rely on the following property: +- ***Validator Update Inclusion***: Let `U1` and `U2` be two validator updates targeting the same validator `val`. + If `U1` occurs before `U2`, then `U2` sums up all the changes of the voting power of `val` that are summed up by `U1`, i.e., + - `U1 = c_1+c_2+...+c_i` and + - `U2 = c_1+c_2+...+c_i+c_(i+1)+...+c_j`. + +The *Validator Update Inclusion* property enables CCV to aggregate multiple VSCs. +It is sufficient for the consumer chains to apply only the last update per validator. +Since the last update of a validator *includes* all the previous updates of that validator, once it is applied, all the previous updates are also applied. + +### Staking Module Interface +[↑ Back to Outline](#outline) + +The following properties define the guarantees of CCV on *providing* VSCs to the consumer chains as a consequence of validator updates on the provider chain. +- ***Validator Update To VSC Validity***: Every VSC provided to a consumer chain MUST contain only validator updates that were applied to the validator set of the provider chain (i.e., resulted from a change in the amount of bonded tokens on the provider chain). +- ***Validator Update To VSC Order***: Let `U1` and `U2` be two validator updates on the provider chain. If `U1` occurs before `U2`, then `U2` MUST NOT be included in a provided VSC before `U1`. Note that the order within a single VSC is not relevant. +- ***Validator Update To VSC Liveness***: Every update of a validator in the validator set of the provider chain MUST eventually be included in a VSC provided to all consumer chains. + +Note that as a consequence of the *Validator Update To VSC Liveness* property, CCV guarantees the following property: +- **Provide VSC uniformity**: If the provider chain provides a VSC to a consumer chain, then it MUST eventually provide that VSC to all consumer chains. + +### Validator Set Update +[↑ Back to Outline](#outline) + +The provider chain providing VSCs to the consumer chains has two desired outcomes: the consumer chains apply the VSCs; and the provider chain registers VSC maturity notifications from every consumer chain. +Thus, for clarity, we split the properties of VSCs in two: properties of applying provided VSCs on the consumer chains; and properties of registering VSC maturity notifications on the provider chain. +For simplicity, we focus on a single consumer chain. + +The following properties define the guarantees of CCV on *applying* on the consumer chain VSCs *provided* by the provider chain. +- ***Apply VSC Validity***: Every VSC applied by the consumer chain MUST be provided by the provider chain. +- ***Apply VSC Order***: If a VSC `vsc1` is provided by the provider chain before a VSC `vsc2`, then the consumer chain MUST NOT apply the validator updates included in `vsc2` before the validator updates included in `vsc1`. +- ***Apply VSC Liveness***: If the provider chain provides a VSC `vsc`, then the consumer chain MUST eventually apply all validator updates included in `vsc`. + +The following properties define the guarantees of CCV on *registering* on the provider chain maturity notifications (from the consumer chain) of VSCs *provided* by the provider chain to the consumer chain. +- ***Register Maturity Validity***: If the provider chain registers a maturity notification of a VSC from the consumer chain, then the provider chain MUST have provided that VSC to the consumer chain. +- ***Register Maturity Timeliness***: The provider chain MUST NOT register a maturity notification of a VSC `vsc` before `UnbondingPeriod` has elapsed on the consumer chain since the consumer chain applied `vsc`. +- ***Register Maturity Order***: If a VSC `vsc1` was provided by the provider chain before another VSC `vsc2`, then the provider chain MUST NOT register the maturity notification of `vsc2` before the maturity notification of `vsc1`. +- ***Register Maturity Liveness***: If the provider chain provides a VSC `vsc` to the consumer chain, then the provider chain MUST eventually register a maturity notification of `vsc` from the consumer chain. + +> Note that, except for *Apply VSC Liveness* and *Register Maturity Liveness*, none of the properties of CCV require the *Correct Relayer* assumption to hold. + +## Correctness Reasoning +[↑ Back to Outline](#outline) + +In this section we argue the correctness of the CCV protocol described in the [Technical Specification](./technical_specification.md), +i.e., we informally prove the properties described in the [previous section](#desired-properties). + +- ***Channel Uniqueness*:** The provider chain sets the CCV channel when receiving (from the consumer chain) the first `ChanOpenConfirm` message and it marks the channel as `INVALID` when receiving any subsequent `ChanOpenConfirm` messages (cf. *Safe Blockchain*). + Similarly, the consumer chain sets the CCV channel when receiving the first `VSCPacket` and ignores any packets received on different channels (cf. *Safe Blockchain*). + +- ***Channel Validity*:** Follows directly from the *Safe Blockchain* assumption. + +- ***Channel Order*:** The provider chain accepts only ordered channels when receiving a `ChanOpenTry` message (cf. *Safe Blockchain*). + Similarly, the consumer chain accepts only ordered channels when receiving `ChanOpenInit` messages (cf. *Safe Blockchain*). + Thus, the property follows directly from the fact that the CCV channel is ordered. + +- ***Channel Liveness*:** The property follows from the *Correct Relayer* assumption. + +- ***Validator Update To VSC Validity***: The provider CCV module provides only VSCs that contain validator updates obtained from the Staking module, + i.e., by calling the `GetValidatorUpdates()` method (cf. *Safe Blockchain*). + Furthermore, these validator updates were applied to the validator set of the provider chain (cf. *Validator Update Provision*). + +- ***Validator Update To VSC Order***: We prove the property through contradiction. + Given two validator updates `U1` and `U2`, with `U1` occurring on the provider chain before `U2`, we assume `U2` is included in a provided VSC before `U1`. + However, `U2` could not have been obtained by the provider CCV module before `U1` (cf. *Validator Update Provision*). + Thus, the provider CCV module could not have provided a VSC that contains `U2` before a VSC that contains `U1` (cf. *Safe Blockchain*), which contradicts the initial assumption. + +- ***Validator Update To VSC Liveness***: The provider CCV module eventually provides to all consumer chains VSCs containing all validator updates obtained from the provider Staking module (cf. *Safe Blockchain*, *Life Blockchain*). + Thus, it is sufficient to prove that every update of a validator in the validator set of the provider chain MUST eventually be obtained from the provider Staking module. + We prove this through contradiction. Given a validator update `U` that is applied to the validator set of the provider chain at the end of a block `B` with timestamp `t`, we assume `U` is never obtained by the provider CCV module. + However, at time `t`, the provider CCV module tries to obtain a new batch of validator updates from the provider Staking module (cf. *Safe Blockchain*). + Thus, this batch of validator updates MUST contain all validator updates applied to the validator set of the provider chain at the end of block `B`, including `U` (cf. *Validator Update Provision*), which contradicts the initial assumption. + +- ***Apply VSC Validity*:** The property follows from the following two assertions. + - The consumer chain only applies VSCs received in `VSCPacket`s through the CCV channel (cf. *Safe Blockchain*). + - The provider chain only sends `VSCPacket`s containing provided VSCs (cf. *Safe Blockchain*). + +- ***Apply VSC Order*:** We prove the property through contradiction. + Given two VSCs `vsc1` and `vsc2` such that the provider chain provides `vsc1` before `vsc2`, we assume the consumer chain applies the validator updates included in `vsc2` before the validator updates included in `vsc1`. + The following sequence of assertions leads to a contradiction. + - The provider chain could not have sent a `VSCPacket` `P2` containing `vsc2` before a `VSCPacket` `P1` containing `vsc1` (cf. *Safe Blockchain*). + - The consumer chain could not have received `P2` before `P1` (cf. *Channel Order*). + - Given the *Safe Blockchain* assumption, we distinguish two cases. + - First, the consumer chain receives `P1` during block `B1` and `P2` during block `B2` (with `B1` < `B2`). + Then, it applies the validator updates included in `vsc1` at the end of `B1` and the validator updates included in `vsc2` at the end of `B2` (cf. *Validator Update Inclusion*), which contradicts the initial assumption. + - Second, the consumer chain receives both `P1` and `P2` during the same block. + Then, it applies the validator updates included in both `vsc1` and `vsc2` at the end of the block. + Thus, it could not have apply the validator updates included in `vsc2` before. + +- ***Apply VSC Liveness*:** The provider chain eventually sends over the CCV channel a `VSCPacket` containing `vsc` (cf. *Safe Blockchain*, *Life Blockchain*). + As a result, the consumer chain eventually receives this packet (cf. *Channel Liveness*). + Then, the consumer chain aggregates all received VSCs at the end of the block and applies all the aggregated updates (cf. *Safe Blockchain*, *Life Blockchain*). + As a result, the consumer chain applies all validator updates in `vsc` (cf. *Validator Update Inclusion*). + +- ***Register Maturity Validity***: The property follows from the following sequence of assertions. + - The provider chain only registers VSC maturity notifications when receiving on the CCV channel acknowledgements of `VSCPacket`s (cf. *Safe Blockchain*). + - The provider chain receives on the CCV channel only packets sent by the consumer chain (cf. *Channel Validity*). + - The consumer chain only acknowledges `VSCPacket`s that it receives on the CCV channel (cf. *Safe Blockchain*). + - The consumer chain receives on the CCV channel only packets sent by the provider chain (cf. *Channel Validity*). + - The provider chain only sends `VSCPacket`s containing provided VSCs (cf. *Safe Blockchain*). + +- ***Register Maturity Timeliness*:** We prove the property through contradiction. + Given a VSC `vsc` provided by the provider chain to the consumer chain, we assume that the provider chain registers a maturity notification of `vsc` before `UnbondingPeriod` has elapsed on the consumer chain since the consumer chain applied `vsc`. + The following sequence of assertions leads to a contradiction. + - The provider chain could not have register a maturity notification of `vsc` before receiving on the CCV channel an acknowledgements of a `VSCPacket` `P` with `P.updates = C` (cf. *Safe Blockchain*). + - The provider chain could not have received an acknowledgement of `P` on the CCV channel before the consumer chain sent it (cf. *Channel Validity*). + - The consumer chain could not have sent an acknowledgement of `P` before at least `UnbondingPeriod` has elapsed since receiving `P` on the CCV channel (cf. *Safe Blockchain*). + Note that since time is measured in terms of the block time, the time of receiving `P` is the same as the time of applying `vsc`. + - The consumer chain could not have received `P` on the CCV channel before the provider chain sent it (cf. *Channel Validity*). + - The provider chain could not have sent `P` before providing `vsc`. + - Since the duration of sending packets through the CCV channel cannot be negative, the provider chain could not have registered a maturity notification of `vsc` before `UnbondingPeriod` has elapsed on the consumer chain since the consumer chain applied `vsc`. + +- ***Register Maturity Order*:** We prove the property through contradiction. Given two VSCs `vsc1` and `vsc2` such that the provider chain provides `vsc1` before `vsc2`, we assume the provider chain registers the maturity notification of `vsc2` before the maturity notification of `vsc1`. + The following sequence of assertions leads to a contradiction. + - The provider chain could not have sent a `VSCPacket` `P2`, with `P2.updates = C2`, before a `VSCPacket` `P1`, with `P1.updates = C1` (cf. *Safe Blockchain*). + - The consumer chain could not have received `P2` before `P1` (cf. *Channel Order*). + - The consumer chain could not have sent the acknowledgment of `P2` before the acknowledgement of `P1` (cf. *Safe Blockchain*). + - The provider chain could not have received the acknowledgment of `P2` before the acknowledgement of `P1` (cf. *Channel Order*). + - The provider chain could not have registered the maturity notification of `vsc2` before the maturity notification of `vsc1` (cf. *Safe Blockchain*). + +- ***Register Maturity Liveness*:** The property follows from the following sequence of assertions. + - The provider chain eventually sends on the CCV channel a `VSCPacket` `P`, with `P.updates = C` (cf. *Safe Blockchain*, *Life Blockchain*). + - The consumer chain eventually receives `P` on the CCV channel (cf. *Channel Liveness*). + - The consumer chain eventually sends an acknowledgement of `P` on the CCV channel (cf. *Safe Blockchain*, *Life Blockchain*). + - The provider chain eventually receives the acknowledgement of `P` on the CCV channel (cf. *Channel Liveness*). + - The provider chain eventually registers the maturity notification of `vsc` (cf. *Safe Blockchain*, *Life Blockchain*). + + +- ***Validator Set Invariant***: The invariant follows from the *Safe Blockchain* assumption and both the *Apply VSC Validity* and *Validator Update To VSC Validity* properties. + +- ***Voting Power Invariant***: To prove the invariant, we use the following property that follows directly from the design of the protocol (cf. *Safe Blockchain*, *Life Blockchain*). + - *Property1*: Let `val` be a validator; let `Ua` and `Ub` be two updates of `val` that are applied subsequently by a consumer chain `cc`, at times `ta` and `tb`, respectively (i.e., no other updates of `val` are applied in between). + Then, `Power(cc,ta,val) = Power(cc,t,val)`, for all times `t`, such that `ta <= t < tb` (i.e., the voting power granted to `val` on `cc` in the period between `ta` and `tb` is constant). + + We prove the invariant through contradiction. + Given a consumer chain `cc`, a validator `val`, and times `t` and `s` such that `t <= s <= t + UnbondingPeriod`, we assume `Power(cc,t,val) > VP(pBonded(s,val))`. + The following sequence of assertions leads to a contradiction. + - Let `U1` be the latest update of `val` that is applied by `cc` before or not later than time `t` + (i.e., `U1` is the update that sets `Power(cc,t,val)` for `val`). + Let `t1` be the time `U1` occurs on the provider chain; let `t2` be the time `U1` is applied on `cc`. + Then, `t1 <= t2 <= t` and `Power(cc,t2,val) = Power(cc,t,val)`. + This means that some of the tokens bonded by `val` at time `t1` (i.e., `pBonded(t1,val)`) were *completely* unbonded before or not later than time `s` (cf. `pBonded(s,val) < pBonded(t1,val)`). + - Let `uo` be the first such unbonding operation that is initiated on the provider chain at a time `t3`, such that `t1 < t3 <= s`. + Note that at time `t3`, the tokens unbonded by `uo` are still part of `pBonded(t1,val)`. + Let `U2` be the validator update caused by initiating `uo`. + Let `t4` be the time `U2` is applied on `cc`; clearly, `t3 <= t4` and `Power(cc,t4,val) < Power(cc,t,val)`. + Note that the existence of `t4` is ensured by *Validator Update To VSC Liveness* and *Apply VSC Liveness*. + Then, `t4 > t2` (cf. `t3 > t1`, *Validator Update To VSC Order*, *Apply VSC Order*). + - `Power(cc,t,val) = Power(cc,t2,val) = Power(cc,t',val)`, for all times `t'`, such that `t2 <= t' < t4` (cf. *Property1*). + Thus, `t4 > t` (cf. `Power(cc,t4,val) < Power(cc,t,val)`). + - `uo` cannot complete before `t4 + UnbondingPeriod`, which means it cannot complete before `s` (cf. `t4 > t`, `s <= t + UnbondingPeriod`). \ No newline at end of file diff --git a/spec/app/ics-028-cross-chain-validation/technical_specification.md b/spec/app/ics-028-cross-chain-validation/technical_specification.md new file mode 100644 index 000000000..f7545eac5 --- /dev/null +++ b/spec/app/ics-028-cross-chain-validation/technical_specification.md @@ -0,0 +1,1100 @@ + +# CCV: Technical Specification +[↑ Back to main document](./README.md) + + +## Outline +- [Placing CCV within an ABCI Application](#placing-ccv-within-an-abci-application) +- [Data Structures](#data-structures) + - [External Data Structures](#external-data-structures) + - [CCV Data Structures](#ccv-data-structures) + - [CCV Packets](#ccv-packets) + - [CCV State](#ccv-state) +- [Sub-protocols](#sub-protocols) + - [Initialization](#initialization) + - [Channel Closing Handshake](#channel-closing-handshake) + - [Packet Relay](#packet-relay) + - [Validator Set Update](#validator-set-update) + +## Placing CCV within an ABCI Application +[↑ Back to Outline](#outline) + +Before describing the data structures and sub-protocols of the CCV protocol, we provide a short overview of the interfaces the CCV module implements and the interactions with the other ABCI application modules. + + +### Implemented Interfaces + +- CCV is an **ABCI application module**, which means it MUST implement the logic to handle some of the messages received from the consensus engine via ABCI, e.g., `InitChain`, `EndBlock` + (for more details, take a look at the [ABCI documentation](https://docs.tendermint.com/v0.34/spec/abci/abci.html)). + In this specification we define the following methods that handle messages that are of particular interest to the CCV protocol: + - `InitGenesis()` -- Called when the chain is first started, on receiving an `InitChain` message from the consensus engine. + - `EndBlock()` -- Contains logic that is automatically triggered at the end of each block. + This is also where the module can inform the underlying consensus engine of changes in the validator set. + +> TODO: `ExportGenesis()` and handling hard forks. + +- CCV is an **IBC module**, which means it MUST implement the module callbacks interface defined in [ICS 26](../../core/ics-026-routing-module/README.md#module-callback-interface). The interface consists of a set of callbacks for + - channel opening handshake, which we describe in the [Initialization](#initialization) section; + - channel closing handshake, which we describe in the [Channel Closing Handshake](#channel-closing-handshake) section; + - and packet relay, which we describe in the [Packet Relay](#packet-relay) section. + + +### Interfacing Other Modules + +- As an ABCI application module, the CCV module interacts with the underlying consensus engine through ABCI: + - On the provider chain, + - it initializes the application (e.g., binds to the expected IBC port) in the `InitGenesis()` method. + - On the consumer chain, + - it initializes the application (e.g., binds to the expected IBC port, creates a client of the provider chain) in the `InitGenesis()` method; + - it provides the validator updates in the `EndBlock()` method. + +- As an IBC module, the CCV module interacts with Core IBC for functionalities regarding + - port allocation ([ICS 5](../../core/ics-005-port-allocation)) via `portKeeper`; + - channels and packet semantics ([ICS 4](../../core/ics-004-channel-and-packet-semantics)) via `channelKeeper`; + - connection semantics ([ICS 3](../../core/ics-003-connection-semantics)) via `connectionKeeper`; + - client semantics ([ICS 2](../../core/ics-002-client-semantics)) via `clientKeeper`. + +- For the [Initialization sub-protocol](#initialization), the provider CCV module interacts with a Governance module by handling governance proposals to spawn new consumer chains. + If such proposals pass, then all validators on the provider chain MUST validate the consumer chain at spawn time; + otherwise they get slashed. + For an example of how governance proposals work, take a look at the [Governance module documentation](https://docs.cosmos.network/master/modules/gov/) of Cosmos SDK. + +- For the [Validator Set Update sub-protocol](#validator-set-update), the provider CCV module interacts with a Staking module on the provider chain. + For an example of how staking works, take a look at the [Staking module documentation](https://docs.cosmos.network/master/modules/staking/) of Cosmos SDK. + The interaction is defined by the following interface: + ```typescript + interface StakingKeeper { + // get UnbondingPeriod from the provider Staking module + UnbondingTime(): Duration + + // get validator updates from the provider Staking module + GetValidatorUpdates(chainID: string): [ValidatorUpdate] + + // notify the provider Staking module of matured CCV unbonding operations + UnbondValidators(chainID: string, valUpdates [ValidatorUpdate]) + } + ``` + +## Data Structures + +### External Data Structures +[↑ Back to Outline](#outline) + +This section describes external data structures used by the CCV module. + +The CCV module uses the ABCI `ValidatorUpdate` data structure, which consists of a validator address (i.e., the hash of its public key) and its power, i.e., +```typescript +interface ValidatorUpdate { + address: string + power: int64 +} +``` +The provider chain sends to the consumer chain a list of `ValidatorUpdate`s, containing an entry for every validator that had its power updated. + +The data structures required for creating clients (i.e., `ClientState`, `ConsensusState`) are defined in [ICS 2](../../core/ics-002-client-semantics). +Specifically for Tendermint clients, the data structures are defined in [ICS 7](../../client/ics-007-tendermint-client). + +### CCV Data Structures +[↑ Back to Outline](#outline) + +The CCV channel state is indicated by `ChannelStatus`, which is defined as +```typescript +enum ChannelStatus { + UNINITIALIZED // default state + INITIALIZING // the channel is in handshake process + VALIDATING // the channel is open and validating + INVALID // the channel is invalid and can no longer process packets +} +``` + +The CCV module is initialized through the `InitGenesis` method when the chain is first started. The initialization is done from a genesis state. This is the case for both provider and consumer chains: +- On the provider chain, the genesis state is described by the following interface: + ```typescript + interface ProviderGenesisState { + // a list of existing consumer chains + consumerStates: [ConsumerState] + } + ``` + with `ConsumerState` defined as + ```typescript + interface ConsumerState { + chainId: string + channelId: Identifier + status: ChannelStatus + } + ``` +- On the consumer chain, the genesis state is described by the following interface: + ```typescript + interface ConsumerGenesisState { + providerClientState: ClientState + providerConsensusState: ConsensusState + } + ``` + +The provider CCV module handles governance proposals to spawn new consumer chains. The structure of these proposals is defined by the `Proposal` interface in the [Governance module documentation](https://docs.cosmos.network/master/modules/gov/). The content of these proposals is described by the following interface (we omit typical fields such as title and description): + ```typescript + interface CreateConsumerChainProposal { + // The proposed chain ID of the new consumer chain. + // Must be different from all other consumer chain IDs + // of the executing proposer chain. + chainId: string + + // The proposed initial height of new consumer chain. + // For a completely new chain, this will be {0,1}; + // however, it may be different if this is a chain + // that is converting to a consumer chain. + initialHeight: Height + + // Spawn time is the time on the provider chain at which + // the consumer chain genesis is finalized and all validators + // will be responsible for starting their consumer chain + // validator node. + spawnTime: Timestamp + + // the hash of the genesis state for the consumer chain; + // the full genesis state MAY be disseminated off-chain + genesisHash: [byte] + } + ``` + Note that `Height` is defined in [ICS 7](../../client/ics-007-tendermint-client). + +### CCV Packets +[↑ Back to Outline](#outline) + +The structure of the packets sent through the CCV channel is defined by the `Packet` interface in [ICS 4](../../core/ics-004-channel-and-packet-semantics). Packets are acknowledged by the remote side by sending back an `Acknowledgement` that contains either a result, created with `NewResultAcknowledgement()`, or an error, created with `NewErrorAcknowledgement()`. + +The following packet data types are required by the CCV module: +- `VSCPacketData` contains a list of validator updates, i.e., + ```typescript + interface VSCPacketData { + updates: [ValidatorUpdate] + } + ``` +> Note that for brevity we use e.g., `VSCPacket` to refer to a packet with `VSCPacketData` as its data. + +### CCV State +[↑ Back to Outline](#outline) + +This section describes the internal state of the CCV module. For simplicity, the state is described by a set of variables; for each variable, both the type and a brief description is provided. In practice, all the state (except for hardcoded constants, e.g., `ProviderPortId`) is stored in a key/value store (KVS). The host state machine provides a KVS interface with three functions, i.e., `get()`, `set()`, and `delete()` (as defined in [ICS 24](../../core/ics-024-host-requirements)). + +- `ProviderPortId = "provider"` is the port ID the provider CCV module is expected to bind to. +- `ConsumerPortId = "consumer"` is the port ID the consumer CCV module is expected to bind to. +- `[ValidatorUpdate]` is a list of `ValidatorUpdate`s. It exposes the following interface: + ```typescript + interface [ValidatorUpdate] { + // append updates to the list, i.e., more recent + // updates are added to the end of the list; + // the list is modified + Append(updates: [ValidatorUpdate]) + + // return an aggregated list of updates, i.e., + // keep only the most recent update per validator; + // the original list is not modified + Aggregate(): [ValidatorUpdate] + + // remove all the updates from the list; + // the list is modified + RemoveAll() + } + + +#### State on the provider chain + +- `pendingClient: Map<(Timestamp, string), Height>` is a mapping from `(timestamp, chainId)` tuples to the initial height of pending clients, i.e., belonging to consumer chains that were not yet spawned, but for which a `CreateConsumerChainProposal` was received. +- `chainToClient: Map` is a mapping from consumer chain IDs to the associated client IDs. +- `chainToChannel: Map` is a mapping from consumer chain IDs to the CCV channel IDs. +- `channelToChain: Map` is a mapping from CCV channel IDs to consumer chain IDs. +- `channelStatus: Map` is a mapping from CCV channel IDs to CCV channel state, as indicated by `ChannelStatus`. +- `pendingUpdates: Map` is a mapping from consumer chain IDs to a list of pending `ValidatorUpdate`s that must be sent to the consumer chain once the CCV channel is established. + + +#### State on the consumer chain + +- `providerClient: Identifier` identifies the client of the provider chain (on the consumer chain) that the CCV channel is build upon. +- `providerChannel: Identifier` identifies the consumer's channel end of the CCV channel. +- `channelStatus: ChannelStatus` is the status of the CCV channel. +- `pendingChanges: [ValidatorUpdate]` is a list of `ValidatorUpdate`s received, but not yet applied to the validator set. It is emptied on every `EndBlock()`. +- `unbondingPackets: [(Packet, Time)]>` is a list of `(packet, unbondingTime)` tuples, where `packet` is a received `VSCPacket` and `unbondingTime` is the packet's unbonding time. The list is used to keep track of when unbonding operations are matured on the consumer chain. It exposes the following interface: + ```typescript + interface [(Packet, Time)]> { + // add a packet with its unbonding time to the list; + // the list is modified + Add(packet: Packet, unbondingTime: Time) + + // return the list sorted by the unbonding time; + // the original list is not modified + SortedByUnbondingTime(): [(Packet, Time)]> + + // remove (packet, unbondingTime) from the list; + // the list is modified + Remove(packet: Packet, unbondingTime: Time) + } + ``` + +## Sub-protocols + +To express the error conditions, the following specification of the sub-protocols uses the exception system of the host state machine, which is exposed through two functions (as defined in [ICS 24](../../core/ics-024-host-requirements)): `abortTransactionUnless` and `abortSystemUnless`. + +### Initialization +[↑ Back to Outline](#outline) + +The *initialization* sub-protocol enables a provider chain and a consumer chain to create a CCV channel -- a unique, ordered IBC channel for exchanging packets. As a prerequisite, the initialization sub-protocol MUST create two IBC clients, one on the provider chain to the consumer chain and one on the consumer chain to the provider chain. This is necessary to verify the identity of the two chains (as long as the clients are trusted). + + +#### **[CCV-PCF-INITG.1]** +```typescript +// PCF: Provider Chain Function +// implements the AppModule interface +function InitGenesis(state: ProviderGenesisState) { + // bind to ProviderPortId port + err = portKeeper.BindPort(ProviderPortId) + // check whether the capability for the port can be claimed + abortSystemUnless(err == nil) + + foreach cs in state.clientStates { + chainToChannel[cs.chainId] = cs.channelId + channelToChain[cs.channelId] = cc.chainId + channelStatus[cs.channelId] = cc.status + } +} +``` +- Initiator: + - ABCI. +- Expected precondition: + - An `InitChain` message is received from the consensus engine; the `InitChain` message is sent when the provider chain is first started. +- Expected postcondition: + - The capability for the port `ProviderPortId` is claimed. + - For each consumer state in the `ProviderGenesisState`, the initial state is set, i.e., the following mappings `chainToChannel`, `channelToChain`, `channelStatus` are set. +- Error condition: + - The capability for the port `ProviderPortId` cannot be claimed. + + +#### **[CCV-PCF-CCPROP.1]** +```typescript +// PCF: Provider Chain Function +// implements governance proposal Handler +function CreateConsumerChainProposal(p: CreateConsumerChainProposal) { + if currentTimestamp() > p.spawnTime { + // get UnbondingPeriod from provider Staking module + unbondingTime = stakingKeeper.UnbondingTime() + + // create client state as defined in ICS 2 + clientState = ClientState{ + chainId: p.chainId, + trustLevel: DefaultTrustLevel, // 1/3 + trustingPeriod: unbondingTime/2, + unbondingPeriod: unbondingTime, + latestHeight: p.initialHeight, + } + + // create consensus state as defined in ICS 2; + // SentinelRoot is used as a stand-in root value for + // the consensus state set at the upgrade height + consensusState = ConsensusState{ + timestamp: currentTimestamp(), + root: SentinelRoot, + nextValidatorsHash: BlockHeader().NextValidatorsHash, + } + + // create consumer chain client and store it + clientId = clientKeeper.CreateClient(clientState, consensusState) + consumerClient[p.chainId] = clientId + } + else { + // store the client as a pending client + pendingClient[(p.spawnTime, p.chainId)] = p.initialHeight + } +} +``` +- Initiator: + - `EndBlock()` method of Governance module. +- Expected precondition: + - A governance proposal with `CreateConsumerChainProposal` as content has passed (i.e., it got the necessary votes). +- Expected postcondition: + - If the spawn time has already passed, + - `UnbondingPeriod` is retrieved from the provider Staking module; + - a client state is created; + - a consensus state is created; + - a client of the consumer chain is created and the client ID is added to `consumerClient`. + - Otherwise, the client is stored in `pendingClient` as a pending client. +- Error condition: + - None. + +> **Note:** Creating a client of a remote chain requires a `ClientState` and a `ConsensusState` (as defined in [ICS 2](../../core/ics-002-client-semantics)). For Tendermint clients, creating a `ConsensusState` requires setting a validator set of the remote chain (see [ICS 7](../../client/ics-007-tendermint-client/README.md#consensus-state)). The provider chain uses the fact that the validator set of the consumer chain is the same as its own validator set. The rest of information to create a `ClientState` it receives through a governance proposal. + + +#### **[CCV-PCF-COINIT.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onChanOpenInit( + order: ChannelOrder, + connectionHops: [Identifier], + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + version: string) { + // the channel handshake MUST be initiated by consumer chain + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a `ChanOpenInit` message on a port the provider CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the provider chain. + + +#### **[CCV-PCF-COTRY.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onChanOpenTry( + order: ChannelOrder, + connectionHops: [Identifier], + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + version: string, + counterpartyVersion: string) { + // validate parameters: + // - only ordered channels allowed + abortTransactionUnless(order == ORDERED) + // - require the portIdentifier to be the port ID the CCV module is bound to + abortTransactionUnless(portIdentifier == ProviderPortId) + // - require the version to be the expected version + abortTransactionUnless(version == "1") + + // assert that the counterpartyPortIdentifier matches + // the expected consumer port ID + abortTransactionUnless(counterpartyPortIdentifier == ConsumerPortId) + + // assert that the counterpartyVersion matches the local version + abortTransactionUnless(counterpartyVersion == version) + + // set the CCV channel status to INITIALIZING + channelStatus[channelIdentifier] = INITIALIZING + + // get the client state associated with this client ID in order + // to get access to the consumer chain ID + clientId = getClient(channelIdentifier) + clientState = clientKeeper.GetClientState(clientId) + + // require the CCV channel to be built on top + // of the expected client of the consumer chain + abortTransactionUnless(chainToClient[clientState.chainId] == clientId) + + // require that no other CCV channel exists for this consumer chain + abortTransactionUnless(clientState.chainId NOTIN chainToChannel.Keys()) +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a `ChanOpenTry` message on a port the provider CCV module is bounded to. +- Expected postcondition: + - The status of the CCV channel with ID `channelIdentifier` is set to `INITIALIZING`. +- Error condition: + - The channel is not ordered. + - `portIdentifier != ProviderPortId`. + - `version` is not the expected version. + - `counterpartyPortIdentifier != ConsumerPortId`. + - `counterpartyVersion != version`. + - The channel is not built on top of the client created for this consumer chain. + - Another CCV channel for this consumer chain already exists. + + +#### **[CCV-PCF-COACK.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onChanOpenAck( + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyVersion: string) { + // the channel handshake MUST be initiated by consumer chain + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a `ChanOpenAck` message on a port the provider CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the provider chain. + + +#### **[CCV-PCF-COCONFIRM.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onChanOpenConfirm( + portIdentifier: Identifier, + channelIdentifier: Identifier) { + // get the client state associated with this client ID in order + // to get access to the consumer chain ID + clientId = getClient(channelIdentifier) + clientState = clientKeeper.GetClientState(clientId) + + // Verify that there isn't already a CCV channel for the consumer chain + // If there is, then close the channel. + if clientState.chainId IN chainToChannel { + channelStatus[channelIdentifier] = INVALID + channelKeeper.ChanCloseInit(channelIdentifier) + abortTransactionUnless(FALSE) + } + + // set channel mappings + chainToChannel[clientState.chainId] = channelIdentifier + channelToChain[channelIdentifier] = clientState.chainId + + // set CCV channel status to VALIDATING + channelStatus[channelIdentifier] = VALIDATING +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a `ChanOpenConfirm` message on a port the provider CCV module is bounded to. +- Expected postcondition: + - If a CCV channel for this consumer chain already exists, then the channel is invalidated and closed. + - Otherwise, the channel mappings are set and the CCV channel status is set to `VALIDATING`. +- Error condition: + - A CCV channel for this consumer chain already exists. + +--- + + +#### **[CCV-CCF-INITG.1]** +```typescript +// CCF: Consumer Chain Function +// implements the AppModule interface +function InitGenesis(state: ConsumerGenesisState) { + // bind to ConsumerPortId port + err = portKeeper.BindPort(ConsumerPortId) + // check whether the capability for the port can be claimed + abortSystemUnless(err == nil) + + // create client of the provider chain + // using data included in the ConsumerGenesisState + clientId = clientKeeper.CreateClient(state.providerClientState, state.providerConsensusState) + + // store the ID of the client of the provider chain + providerClient = clientId +} +``` +- Initiator: + - ABCI. +- Expected precondition: + - An `InitChain` message is received from the consensus engine; the `InitChain` message is sent when the consumer chain is first started. +- Expected postcondition: + - The capability for the port `ConsumerPortId` is claimed. + - A client of the provider chain is created and the client ID is stored into `providerClient`. +- Error condition: + - The capability for the port `ConsumerPortId` cannot be claimed. + +> **Note**: CCV assumes that `ConsumerGenesisState` is disseminated to all validators. To ensure that validators receive the correct genesis state, the governance proposal also contains a hash of the `ConsumerGenesisState` (see `CreateConsumerChainProposal`). + + +#### **[CCV-CCF-COINIT.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onChanOpenInit( + order: ChannelOrder, + connectionHops: [Identifier], + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + version: string) { + // ensure provider channel hasn't already been created + abortTransactionUnless(providerChannel == "") + + // validate parameters: + // - only ordered channels allowed + abortTransactionUnless(order == ORDERED) + // - require the portIdentifier to be the port ID the CCV module is bound to + abortTransactionUnless(portIdentifier == ConsumerPortId) + // - require the version to be the expected version + abortTransactionUnless(version == "1") + + // assert that the counterpartyPortIdentifier matches + // the expected consumer port ID + abortTransactionUnless(counterpartyPortIdentifier == ProviderPortId) + + // set the CCV channel status to INITIALIZING + channelStatus[channelIdentifier] = INITIALIZING + + // require that the client ID of the client associated + // with this channel matches the expected provider client id + clientId = getClient(channelIdentifier) + abortTransactionUnless(providerClient != clientId) +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a `ChanOpenInit` message on a port the consumer CCV module is bounded to. +- Expected postcondition: + - The status of the CCV channel with ID `channelIdentifier` is set to `INITIALIZING`. +- Error condition: + - `providerChannel` is already set. + - `portIdentifier != ConsumerPortId`. + - `version` is not the expected version. + - `counterpartyPortIdentifier != ProviderPortId`. + - The client associated with this channel is not the expected provider client. + + +#### **[CCV-CCF-COTRY.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onChanOpenTry( + order: ChannelOrder, + connectionHops: [Identifier], + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + version: string, + counterpartyVersion: string) { + // the channel handshake MUST be initiated by consumer chain + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a `ChanOpenTry` message on a port the consumer CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the consumer chain. + + +#### **[CCV-CCF-COACK.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onChanOpenAck( + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyVersion: string) { + // ensure provider channel hasn't already been created + abortTransactionUnless(providerChannel == "") + + // assert that the counterpartyVersion matches the local version + abortTransactionUnless(counterpartyVersion == version) +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a `ChanOpenAck` message on a port the consumer CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - `providerChannel` is already set. + - `counterpartyVersion != version`. + +> **Note:** The initialization sub-protocol on the consumer chain finalizes on receiving the first `VSCPacket` and setting `providerChannel` to the ID of the channel on which it receives the packet (see `onRecvVSCPacket` method). + + +#### **[CCV-CCF-COCONFIRM.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onChanOpenConfirm( + portIdentifier: Identifier, + channelIdentifier: Identifier) { + // the channel handshake MUST be initiated by consumer chain + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a `ChanOpenConfirm` message on a port the consumer CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the consumer chain. + +### Channel Closing Handshake +[↑ Back to Outline](#outline) + + +#### **[CCV-PCF-CCINIT.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onChanCloseInit( + portIdentifier: Identifier, + channelIdentifier: Identifier) { + // Disallow user-initiated channel closing for provider channels + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a `ChanCloseInit` message on a port the provider CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the provider chain. + + +#### **[CCV-PCF-CCCONFIRM.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onChanCloseConfirm( + portIdentifier: Identifier, + channelIdentifier: Identifier) { + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a `ChanCloseConfirm` message on a port the provider CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the provider chain. + +--- + + +#### **[CCV-CCF-CCINIT.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onChanCloseInit( + portIdentifier: Identifier, + channelIdentifier: Identifier) { + // allow relayers to close duplicate OPEN channels, + // if the provider channel has already been established + if providerChannel == "" || providerChannel == channelIdentifier { + // user cannot close channel + abortTransactionUnless(FALSE) + } +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a `ChanCloseInit` message on a port the consumer CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - `providerChannel` is not set or `providerChannel` matches the ID of the channel the `ChanCloseInit` message was received on. + + +#### **[CCV-CCF-CCCONFIRM.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onChanCloseConfirm( + portIdentifier: Identifier, + channelIdentifier: Identifier) { + abortTransactionUnless(FALSE) +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a `ChanCloseConfirm` message on a port the consumer CCV module is bounded to. +- Expected postcondition: + - The state is not changed. +- Error condition: + - Invoked on the consumer chain. + +### Packet Relay +[↑ Back to Outline](#outline) + + +#### **[CCV-PCF-RCVP.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onRecvPacket(packet: Packet): Packet { + switch typeof(packet.data) { + // the provider chain receives no packets + default: + // unexpected packet type + return NewErrorAcknowledgement() + } +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a packet on a channel owned by the provider CCV module. +- Expected postcondition: + - The state is not changed. +- Error condition: + - The packet type is unexpected. + + +#### **[CCV-PCF-ACKP.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onAcknowledgePacket(packet: Packet) { + switch typeof(packet.data) { + case VSCPacketData: + onAcknowledgeVSCPacket(packet) + default: + // unexpected packet type + abortTransactionUnless(FALSE) + } +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received an acknowledgement on a channel owned by the provider CCV module. +- Expected postcondition: + - If the acknowledgement is for a `VSCPacket`, the `onAcknowledgeVSCPacket` method is invoked. +- Error condition: + - The acknowledgement is for an unexpected packet. + + +#### **[CCV-PCF-TOP.1]** +```typescript +// PCF: Provider Chain Function +// implements the ICS26 interface +function onTimeoutPacket(packet Packet) { + switch typeof(packet.data) { + case VSCPacketData: + onTimeoutVSCPacket(packet) + default: + // unexpected packet type + abortTransactionUnless(FALSE) + } +} +``` +- Initiator: + - The IBC module on the provider chain. +- Expected precondition: + - The IBC module on the provider chain received a timeout on a channel owned by the provider CCV module. + - The Correct Relayer assumption is violated. +- Expected postcondition: + - If the timeout is for a `VSCPacket`, the `onTimeoutVSCPacket` method is invoked. +- Error condition: + - The timeout is for an unexpected packet. + +--- + + +#### **[CCV-CCF-RCVP.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onRecvPacket(packet: Packet): Packet { + switch typeof(packet.data) { + case VSCPacketData: + return onRecvVSCPacket(packet) + default: + // unexpected packet type + return NewErrorAcknowledgement() + } +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a packet on a channel owned by the consumer CCV module. +- Expected postcondition: + - If the packet is a `VSCPacket`, the `onRecvVSCPacket` method is invoked. +- Error condition: + - The packet type is unexpected. + + +#### **[CCV-CCF-ACKP.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onAcknowledgePacket(packet: Packet) { + switch typeof(packet.data) { + // the consumer chain sends no packets + default: + // unexpected packet type + abortTransactionUnless(FALSE) + } +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received an acknowledgement on a channel owned by the consumer CCV module. +- Expected postcondition: + - The state is not changed. +- Error condition: + - The acknowledgement is for an unexpected packet. + + +#### **[CCV-CCF-TOP.1]** +```typescript +// CCF: Consumer Chain Function +// implements the ICS26 interface +function onTimeoutPacket(packet Packet) { + switch typeof(packet.data) { + // the consumer chain sends no packets + default: + // unexpected packet type + abortTransactionUnless(FALSE) + } +} +``` +- Initiator: + - The IBC module on the consumer chain. +- Expected precondition: + - The IBC module on the consumer chain received a timeout on a channel owned by the consumer CCV module. + - The Correct Relayer assumption is violated. +- Expected postcondition: + - The state is not changed. +- Error condition: + - The timeout is for an unexpected packet. + +### Validator Set Update +[↑ Back to Outline](#outline) + +The *validator set update* sub-protocol enables the provider chain +- to update the consumer chain on the voting power granted to validators on the provider chain +- and to ensure the correct completion of unbonding operations for validators that produce blocks on the consumer chain. + + +#### **[CCV-PCF-EBLOCK.1]** +```typescript +// PCF: Provider Chain Function +// implements the AppModule interface +function EndBlock(): [ValidatorUpdate] { + // iterate over all consumer chains registered with this provider chain + foreach chainId in chainToClient.Keys() { + // get list of validator updates from the provider Staking module + valUpdates = stakingKeeper.GetValidatorUpdates(chainId) + + // add validator updates to the list of pending updates + pendingUpdates[chainId] = pendingUpdates[chainId].Aggregate(valUpdates) + + // TODO pendingUpdates should be a list of VSCs; + // once the channel is established, all the VSCs are sent, + // so that we can receive ACKs for the unbonding + + // check whether + // - there are pending updates; + // - and there is an established CCV channel to the consumer chain + if len(pendingUpdates[chainId]) != 0 AND chainId IN chainToChannel.Keys() { + // the CCV channel should be in VALIDATING state + abortSystemUnless(channelStatus[chainId] == VALIDATING) + + // create packet data + packetData = VSCPacketData{updates: pendingUpdates[chainId]} + + // gets the channel ID for the given consumer chain ID + channelId = chainToChannel[chainId] + + // create packet and send it using the interface exposed by ICS-4 + packet = Packet{data: packetData, destChannel: channelId} + channelKeeper.SendPacket(packet) + } + } + + // do not return anything to the consensus engine + return [] +} +``` +- Initiator: + - ABCI. +- Expected precondition: + - An `EndBlock` message is received from the consensus engine. + - The provider Staking module has an up-to-date list of validator updates for every consumer chain registered. +- Expected postcondition: + - For every consumer chain with `chainId` + - A list of validator updates is retrieved from the provider Staking module and aggregated to the list of pending updates for the consumer chain with `chainId`, i.e., `pendingUpdates[chainId]`. + - If the list of pending updates is not empty and there is a CCV channel with status `VALIDATING` for the the consumer chain with `chainId`, then + - a `VSCPacketData` is created, with `updates = valUpdates`; + - a packet with the created `VSCPacketData` is sent on the channel associated with the consumer chain with `chainId`. +- Error condition: + - A CCV channel for the consumer chain with `chainId` exists and its status is not set to `VALIDATING`. + +> **Note**: The expected precondition implies that the provider Staking module MUST update its view of the validator sets for each consumer chain before `EndBlock()` in the provider CCV module is invoked. A solution is for the provider Staking module to update its view during `EndBlock()` and then, the `EndBlock()` of the provider Staking module MUST be executed before the `EndBlock()` of the provider CCV module. + + +#### **[CCV-PCF-ACKVSC.1]** +```typescript +// PCF: Provider Chain Function +function onAcknowledgeVSCPacket(packet: Packet) { + // get the channel ID of the CCV channel the packet was sent on + channelId = packet.getDestinationChannel() + + // get the ID of the consumer chain mapped to this channel ID + abortTransactionUnless(channelId IN channelToChain.Keys()) + chainId = channelToChain[packet.getDestinationChannel()] + + stakingKeeper.UnbondValidators(chainID, packet.data.updates) +} +``` +- Initiator: + - The `onAcknowledgePacket()` method. +- Expected precondition: + - The IBC module on the provider chain received an acknowledgement of a `VSCPacket` on a channel owned by the provider CCV module. +- Expected postcondition: + - The `UnbondValidators` method of the provider Staking module is invoked. +- Error condition: + - The ID of the channel on which the `VSCPacket` was sent is not mapped to a chain ID (in `channelToChain`). + + +#### **[CCV-PCF-TOVSC.1]** +```typescript +// PCF: Provider Chain Function +function onTimeoutVSCPacket(packet Packet) { + channelStatus = INVALID + + // TODO: Unbonding everything? +} +``` +- Initiator: + - The `onTimeoutPacket()` method. +- Expected precondition: + - The IBC module on the provider chain received a timeout of a `VSCPacket` on a channel owned by the provider CCV module. +- Expected postcondition: + - `channelStatus` is set to `INVALID` +- Error condition: + - None. + +--- + + +#### **[CCV-CCF-RECVVSC.1]** +```typescript +// CCF: Consumer Chain Function +function onRecvVSCPacket(packet: Packet): Packet { + channelId = packet.getDestinationChannel() + // check whether the packet was sent on the CCV channel + if providerChannel != "" && providerChannel != channelId { + // packet sent on a channel other than the established provider channel; + // close channel and return error acknowledgement + channelKeeper.ChanCloseInit(channelId) + return NewErrorAcknowledgement() + } + + // check whether the status of the CCV channel is VALIDATING + if (channelStatus != VALIDATING) { + // set status to VALIDATING + channelStatus = VALIDATING + + // set the channel as the provider channel + providerChannel = channelId + } + + // store the list of updates from the packet + pendingChanges.Append(packet.data.updates) + + // calculate and store the unbonding time for the packet + unbondingTime = currentTimestamp().Add(UnbondingPeriod) + unbondingPackets.Add(packet, unbondingTime) + + // ack will be sent asynchronously + return nil +} +``` +- Initiator: + - The `onRecvPacket()` method. +- Expected precondition: + - The IBC module on the consumer chain received a `VSCPacket` on a channel owned by the consumer CCV module. +- Expected postcondition: + - If `providerChannel` is set and does not match the channel with ID `channelId` on which the packet was sent, then + - the closing handshake for the channel with ID `channelId` is initiated; + - an error acknowledgement is returned. + - Otherwise, + - if the CCV channel status is not `VALIDATING`, then it is set to `VALIDATING` and the channel ID is set as the provider channel; + - `packet.data.updates` are appended to `pendingChanges`; + - `(packet, unbondingTime)` is added to `unbondingPackets`, where `unbondingTime = currentTimestamp() + UnbondingPeriod`; + - a nil acknowledgement is returned, i.e., the acknowledgement will be sent asynchronously. +- Error condition: + - None. + + +#### **[CCV-CCF-EBLOCK.1]** +```typescript +// CCF: Consumer Chain Function +// implements the AppModule interface +function EndBlock(): [ValidatorUpdate] { + if pendingChanges.IsEmpty() { + // do nothing + return [] + } + // aggregate the pending changes + changes = pendingChanges.Aggregate() + // Note: in the implementation, the aggregation is done directly + // when receiving a VSCPacket via the AccumulateChanges method. + + // remove all pending changes + pendingChanges.RemoveAll() + + // unbond mature packets + UnbondMaturePackets() + + // return the validator set updates to the consensus engine + return changes +} +``` +- Initiator: + - ABCI. +- Expected precondition: + - An `EndBlock` message is received from the consensus engine. +- Expected postcondition: + - If `pendingChanges` is empty, the state is not changed. + - Otherwise, + - the pending changes are aggregated and returned to the consensus engine; + - `pendingChanges` is emptied; + - `UnbondMaturePackets()` is invoked. +- Error condition: + - None. + + +#### **[CCV-CCF-UMP.1]** +```typescript +// CCF: Consumer Chain Function +function UnbondMaturePackets() { + // check if the provider channel is set + if providerChannel != "" { + foreach (packet, unbondingTime) in unbondingPackets.SortedByUnbondingTime() { + if currentTimestamp() >= unbondingTime { + // send acknowledgement to the provider chain + channelKeeper.WriteAcknowledgement(providerChannel, packet, NewResultAcknowledgement()) + + // remove entry from the list + unbondingPackets.Remove(packet, unbondingTime) + } + else { + // stop loop + break + } + } + } +} +``` +- Initiator: + - The `EndBlock()` method. +- Expected precondition: + - None. +- Expected postcondition: + - If the provider channel is set, for each `(packet, unbondingTime)` in the list of unbonding packet sorted by unbonding times + - if `currentTimestamp() >= unbondingTime`, the packet is acknowledged (i.e., `channelKeeper.WriteAcknowledgement()` is invoked) and the tuple is removed from `unbondingPackets`; + - otherwise, stop the loop. + - Otherwise, the state is not changed. +- Error condition: + - None. \ No newline at end of file