From 34168e0f821790c5436615dad64a04296c5f5fe2 Mon Sep 17 00:00:00 2001 From: samricotta <37125168+samricotta@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:56:42 +0200 Subject: [PATCH] refactor: use legacy v0.45 / v0.46 docs (#13) * wip * wip * removal * fix for script + comments * small adjustment to deploy-docs * Update versions.json * Update test-deploy.yml --- .github/workflows/deploy-docs.yml | 7 +- .github/workflows/test-deploy.yml | 6 +- build-docs.sh | 18 + .../version-0.45/develop/_category_.json | 5 - .../develop/advanced-concepts/00-baseapp.md | 395 ---- .../advanced-concepts/01-transactions.md | 155 -- .../develop/advanced-concepts/02-context.md | 100 - .../develop/advanced-concepts/03-node.md | 72 - .../develop/advanced-concepts/04-store.md | 253 -- .../develop/advanced-concepts/05-encoding.md | 257 -- .../develop/advanced-concepts/06-cli.md | 152 -- .../develop/advanced-concepts/07-events.md | 133 -- .../develop/advanced-concepts/08-grpc_rest.md | 112 - .../develop/advanced-concepts/09-ocap.md | 75 - .../develop/advanced-concepts/10-telemetry.md | 141 -- .../advanced-concepts/11-runtx_middleware.md | 69 - .../advanced-concepts/12-simulation.md | 97 - .../develop/advanced-concepts/13-upgrade.md | 156 -- .../baseapp_state-begin_block.png | Bin 20565 -> 0 bytes .../baseapp_state-checktx.png | Bin 82308 -> 0 bytes .../baseapp_state-commit.png | Bin 47662 -> 0 bytes .../baseapp_state-deliver_tx.png | Bin 59007 -> 0 bytes .../baseapp_state-initchain.png | Bin 32800 -> 0 bytes .../advanced-concepts/baseapp_state_types.png | Bin 133747 -> 0 bytes .../advanced-concepts/keeper_dependencies.svg | 102 - .../advanced-concepts/transaction_flow.svg | 48 - .../high-level-concepts/00-overview-app.md | 260 -- .../high-level-concepts/01-tx-lifecycle.md | 252 -- .../high-level-concepts/02-query-lifecycle.md | 148 -- .../high-level-concepts/03-accounts.md | 149 -- .../high-level-concepts/04-gas-fees.md | 85 - .../high-level-concepts/_category_.json | 5 - .../version-0.45/develop/intro/overview.md | 33 - .../develop/intro/sdk-app-architecture.md | 93 - .../version-0.45/develop/intro/sdk-design.md | 91 - .../develop/intro/why-app-specific.md | 77 - .../version-0.45/integrate/_category_.json | 5 - .../integrate/architecture/PROCESS.md | 56 - .../integrate/architecture/README.md | 79 - .../architecture/adr-002-docs-structure.md | 86 - .../adr-003-dynamic-capability-store.md | 344 --- .../adr-004-split-denomination-keys.md | 120 - .../adr-006-secret-store-replacement.md | 54 - .../adr-007-specialization-groups.md | 177 -- .../architecture/adr-008-dCERT-group.md | 171 -- .../architecture/adr-009-evidence-module.md | 182 -- .../adr-010-modular-antehandler.md | 285 --- .../adr-011-generalize-genesis-accounts.md | 170 -- .../architecture/adr-012-state-accessors.md | 155 -- .../integrate/architecture/adr-013-metrics.md | 157 -- .../adr-014-proportional-slashing.md | 85 - ...dr-016-validator-consensus-key-rotation.md | 125 - .../adr-017-historical-header-module.md | 61 - .../adr-018-extendable-voting-period.md | 66 - .../adr-019-protobuf-state-encoding.md | 379 --- .../adr-020-protobuf-transaction-encoding.md | 464 ---- .../adr-021-protobuf-query-encoding.md | 256 -- .../adr-022-custom-panic-handling.md | 213 -- .../architecture/adr-023-protobuf-naming.md | 263 --- .../architecture/adr-024-coin-metadata.md | 139 -- ...27-deterministic-protobuf-serialization.md | 314 --- .../adr-028-public-key-addresses.md | 329 --- .../architecture/adr-029-fee-grant-module.md | 153 -- .../architecture/adr-030-authz-module.md | 249 -- .../architecture/adr-031-msg-service.md | 202 -- .../architecture/adr-032-typed-events.md | 319 --- .../adr-033-protobuf-inter-module-comm.md | 400 ---- .../architecture/adr-034-account-rekeying.md | 76 - .../adr-035-rosetta-api-support.md | 211 -- .../adr-036-arbitrary-signature.md | 132 -- .../architecture/adr-037-gov-split-vote.md | 111 - .../architecture/adr-038-state-listening.md | 569 ----- .../architecture/adr-039-epoched-staking.md | 122 - ...r-040-storage-and-smt-state-commitments.md | 164 -- .../adr-041-in-place-store-migrations.md | 167 -- .../architecture/adr-042-group-module.md | 279 --- .../integrate/architecture/adr-template.md | 60 - .../integrate/building-modules/00-intro.md | 88 - .../building-modules/01-module-manager.md | 146 -- .../02-messages-and-queries.md | 111 - .../building-modules/03-msg-services.md | 130 - .../building-modules/04-query-services.md | 73 - .../05-beginblock-endblock.md | 35 - .../integrate/building-modules/06-keeper.md | 81 - .../building-modules/07-invariants.md | 84 - .../integrate/building-modules/08-genesis.md | 56 - .../building-modules/09-module-interfaces.md | 135 -- .../building-modules/10-structure.md | 95 - .../integrate/building-modules/11-errors.md | 46 - .../integrate/building-modules/12-upgrade.md | 53 - .../building-modules/13-simulator.md | 123 - .../building-modules/_category_.json | 5 - .../building-modules/transaction_flow.svg | 48 - .../integrate/ibc/_category_.json | 5 - .../version-0.45/integrate/ibc/custom.md | 464 ---- .../version-0.45/integrate/ibc/integration.md | 248 -- .../version-0.45/integrate/ibc/overview.md | 149 -- .../version-0.45/integrate/ibc/proposals.md | 38 - .../version-0.45/integrate/ibc/relayer.md | 43 - .../integrate/ibc/upgrades/README.md | 14 - .../integrate/ibc/upgrades/_category_.json | 5 - .../integrate/ibc/upgrades/developer-guide.md | 46 - .../integrate/ibc/upgrades/quick-guide.md | 50 - .../version-0.45/integrate/modules/README.md | 37 - .../version-0.45/integrate/rfc/README.md | 34 - .../version-0.45/user/_category_.json | 5 - .../version-0.45/user/run-node/00-keyring.md | 132 -- .../version-0.45/user/run-node/01-run-node.md | 124 - .../user/run-node/02-interact-node.md | 227 -- .../version-0.45/user/run-node/03-txs.md | 355 --- .../version-0.45/user/run-node/04-rosetta.md | 133 -- .../user/run-node/05-run-testnet.md | 95 - .../user/run-node/_category_.json | 5 - .../version-0.46/develop/_category_.json | 5 - .../develop/advanced-concepts/00-baseapp.md | 406 ---- .../advanced-concepts/01-transactions.md | 171 -- .../develop/advanced-concepts/03-context.md | 99 - .../develop/advanced-concepts/04-node.md | 73 - .../develop/advanced-concepts/05-store.md | 277 --- .../develop/advanced-concepts/07-encoding.md | 288 --- .../develop/advanced-concepts/08-grpc_rest.md | 95 - .../develop/advanced-concepts/09-cli.md | 152 -- .../develop/advanced-concepts/10-events.md | 135 -- .../develop/advanced-concepts/11-telemetry.md | 126 - .../develop/advanced-concepts/12-ocap.md | 74 - .../advanced-concepts/13-runtx_middleware.md | 65 - .../advanced-concepts/13-simulation.md | 97 - .../advanced-concepts/14-proto-docs.md | 3 - .../develop/advanced-concepts/15-tips.md | 202 -- .../develop/advanced-concepts/16-upgrade.md | 156 -- .../develop/advanced-concepts/_category_.json | 5 - .../baseapp_state-begin_block.png | Bin 20565 -> 0 bytes .../baseapp_state-checktx.png | Bin 82308 -> 0 bytes .../baseapp_state-commit.png | Bin 47662 -> 0 bytes .../baseapp_state-deliver_tx.png | Bin 59007 -> 0 bytes .../baseapp_state-initchain.png | Bin 32800 -> 0 bytes .../advanced-concepts/baseapp_state_types.png | Bin 133747 -> 0 bytes .../advanced-concepts/keeper_dependencies.svg | 102 - .../high-level-concepts/00-overview-app.md | 252 -- .../high-level-concepts/01-tx-lifecycle.md | 253 -- .../high-level-concepts/02-query-lifecycle.md | 134 -- .../high-level-concepts/03-accounts.md | 148 -- .../high-level-concepts/04-gas-fees.md | 81 - .../high-level-concepts/_category_.json | 5 - .../develop/intro/00-what-is-sdk.md | 31 - .../develop/intro/01-why-app-specific.md | 77 - .../develop/intro/02-sdk-app-architecture.md | 93 - .../develop/intro/03-sdk-design.md | 93 - .../develop/intro/_category_.json | 5 - .../version-0.46/integrate/CosmWasm/README.md | 13 - .../integrate/CosmWasm/_category_.json | 5 - .../version-0.46/integrate/_category_.json | 5 - .../integrate/architecture/PROCESS.md | 56 - .../integrate/architecture/README.md | 87 - .../integrate/architecture/_category_.json | 5 - .../architecture/adr-002-docs-structure.md | 86 - .../adr-003-dynamic-capability-store.md | 344 --- .../adr-004-split-denomination-keys.md | 120 - .../adr-006-secret-store-replacement.md | 54 - .../adr-007-specialization-groups.md | 177 -- .../architecture/adr-008-dCERT-group.md | 171 -- .../architecture/adr-009-evidence-module.md | 182 -- .../adr-010-modular-antehandler.md | 290 --- .../adr-011-generalize-genesis-accounts.md | 170 -- .../architecture/adr-012-state-accessors.md | 155 -- .../integrate/architecture/adr-013-metrics.md | 157 -- .../adr-014-proportional-slashing.md | 85 - ...dr-016-validator-consensus-key-rotation.md | 125 - .../adr-017-historical-header-module.md | 61 - .../adr-018-extendable-voting-period.md | 66 - .../adr-019-protobuf-state-encoding.md | 379 --- .../adr-020-protobuf-transaction-encoding.md | 464 ---- .../adr-021-protobuf-query-encoding.md | 256 -- .../adr-022-custom-panic-handling.md | 218 -- .../architecture/adr-023-protobuf-naming.md | 263 --- .../architecture/adr-024-coin-metadata.md | 139 -- ...27-deterministic-protobuf-serialization.md | 314 --- .../adr-028-public-key-addresses.md | 329 --- .../architecture/adr-029-fee-grant-module.md | 153 -- .../architecture/adr-030-authz-module.md | 259 -- .../architecture/adr-031-msg-service.md | 202 -- .../architecture/adr-032-typed-events.md | 319 --- .../adr-033-protobuf-inter-module-comm.md | 400 ---- .../architecture/adr-034-account-rekeying.md | 76 - .../adr-035-rosetta-api-support.md | 211 -- .../adr-036-arbitrary-signature.md | 132 -- .../architecture/adr-037-gov-split-vote.md | 111 - .../architecture/adr-038-state-listening.md | 569 ----- .../architecture/adr-039-epoched-staking.md | 122 - ...r-040-storage-and-smt-state-commitments.md | 289 --- .../adr-041-in-place-store-migrations.md | 167 -- .../architecture/adr-042-group-module.md | 279 --- .../architecture/adr-043-nft-module.md | 340 --- .../adr-044-protobuf-updates-guidelines.md | 138 -- .../adr-045-check-delivertx-middlewares.md | 312 --- .../architecture/adr-046-module-params.md | 184 -- .../adr-047-extend-upgrade-plan.md | 245 -- .../architecture/adr-048-consensus-fees.md | 203 -- .../architecture/adr-049-state-sync-hooks.md | 160 -- .../adr-053-go-module-refactoring.md | 109 - .../integrate/architecture/adr-055-orm.md | 111 - .../integrate/architecture/adr-template.md | 60 - .../integrate/building-modules/00-intro.md | 88 - .../building-modules/01-module-manager.md | 147 -- .../02-messages-and-queries.md | 111 - .../building-modules/03-msg-services.md | 96 - .../building-modules/04-query-services.md | 73 - .../05-beginblock-endblock.md | 35 - .../integrate/building-modules/06-keeper.md | 81 - .../building-modules/07-invariants.md | 76 - .../integrate/building-modules/08-genesis.md | 56 - .../building-modules/09-module-interfaces.md | 135 -- .../building-modules/11-structure.md | 95 - .../integrate/building-modules/12-errors.md | 46 - .../integrate/building-modules/13-upgrade.md | 53 - .../building-modules/14-simulator.md | 122 - .../building-modules/_category_.json | 5 - .../building-modules/transaction_flow.svg | 48 - .../version-0.46/integrate/ibc/README.md | 9 - .../integrate/ibc/_category_.json | 5 - .../integrate/migrations/00-pre-upgrade.md | 55 - .../integrate/migrations/_category_.json | 5 - .../integrate/modules/_category_.json | 5 - .../integrate/modules/auth/01_concepts.md | 39 - .../integrate/modules/auth/02_state.md | 69 - .../integrate/modules/auth/03_antehandlers.md | 36 - .../integrate/modules/auth/04_keepers.md | 43 - .../integrate/modules/auth/05_vesting.md | 626 ----- .../integrate/modules/auth/06_params.md | 11 - .../integrate/modules/auth/07_client.md | 417 ---- .../integrate/modules/auth/README.md | 45 - .../integrate/modules/auth/_category_.json | 5 - .../integrate/modules/authz/01_concepts.md | 51 - .../integrate/modules/authz/02_state.md | 19 - .../integrate/modules/authz/03_messages.md | 42 - .../integrate/modules/authz/04_events.md | 3 - .../integrate/modules/authz/05_client.md | 168 -- .../integrate/modules/authz/README.md | 30 - .../integrate/modules/authz/_category_.json | 5 - .../integrate/modules/bank/01_state.md | 19 - .../integrate/modules/bank/02_keepers.md | 135 -- .../integrate/modules/bank/03_messages.md | 28 - .../integrate/modules/bank/04_events.md | 149 -- .../integrate/modules/bank/05_params.md | 24 - .../integrate/modules/bank/06_client.md | 390 --- .../integrate/modules/bank/README.md | 106 - .../integrate/modules/bank/_category_.json | 5 - .../modules/capability/01_concepts.md | 31 - .../integrate/modules/capability/02_state.md | 22 - .../integrate/modules/capability/README.md | 77 - .../modules/capability/_category_.json | 5 - .../integrate/modules/crisis/01_state.md | 13 - .../integrate/modules/crisis/02_messages.md | 21 - .../integrate/modules/crisis/03_events.md | 14 - .../integrate/modules/crisis/04_params.md | 7 - .../integrate/modules/crisis/05_client.md | 27 - .../integrate/modules/crisis/README.md | 26 - .../integrate/modules/crisis/_category_.json | 5 - .../modules/distribution/01_concepts.md | 34 - .../modules/distribution/02_state.md | 65 - .../modules/distribution/03_begin_block.md | 87 - .../modules/distribution/04_messages.md | 122 - .../modules/distribution/05_hooks.md | 59 - .../modules/distribution/06_events.md | 48 - .../modules/distribution/07_params.md | 17 - .../modules/distribution/08_client.md | 469 ---- .../integrate/modules/distribution/README.md | 106 - .../modules/distribution/_category_.json | 5 - .../integrate/modules/evidence/01_concepts.md | 78 - .../integrate/modules/evidence/02_state.md | 19 - .../integrate/modules/evidence/03_messages.md | 55 - .../integrate/modules/evidence/04_events.md | 18 - .../integrate/modules/evidence/05_params.md | 7 - .../modules/evidence/06_begin_block.md | 154 -- .../integrate/modules/evidence/07_client.md | 188 -- .../integrate/modules/evidence/README.md | 40 - .../modules/evidence/_category_.json | 5 - .../integrate/modules/feegrant/01_concepts.md | 93 - .../integrate/modules/feegrant/02_state.md | 23 - .../integrate/modules/feegrant/03_messages.md | 17 - .../integrate/modules/feegrant/04_events.md | 33 - .../integrate/modules/feegrant/05_client.md | 184 -- .../integrate/modules/feegrant/README.md | 37 - .../modules/feegrant/_category_.json | 5 - .../integrate/modules/gov/01_concepts.md | 203 -- .../integrate/modules/gov/02_state.md | 217 -- .../integrate/modules/gov/03_messages.md | 183 -- .../integrate/modules/gov/04_events.md | 65 - .../modules/gov/05_future_improvements.md | 30 - .../integrate/modules/gov/06_params.md | 28 - .../integrate/modules/gov/07_client.md | 1804 -------------- .../integrate/modules/gov/08_metadata.md | 32 - .../integrate/modules/gov/README.md | 65 - .../integrate/modules/gov/_category_.json | 5 - .../integrate/modules/mint/01_concepts.md | 28 - .../integrate/modules/mint/02_state.md | 21 - .../integrate/modules/mint/03_begin_block.md | 66 - .../integrate/modules/mint/04_params.md | 16 - .../integrate/modules/mint/05_events.md | 16 - .../integrate/modules/mint/06_client.md | 224 -- .../integrate/modules/mint/README.md | 26 - .../integrate/modules/mint/_category_.json | 5 - .../integrate/modules/params/01_keeper.md | 19 - .../integrate/modules/params/02_subspace.md | 38 - .../integrate/modules/params/README.md | 29 - .../integrate/modules/params/_category_.json | 5 - .../integrate/modules/slashing/01_concepts.md | 57 - .../integrate/modules/slashing/02_state.md | 51 - .../integrate/modules/slashing/03_messages.md | 51 - .../modules/slashing/04_begin_block.md | 98 - .../integrate/modules/slashing/05_hooks.md | 45 - .../integrate/modules/slashing/06_events.md | 46 - .../modules/slashing/07_tombstone.md | 127 - .../integrate/modules/slashing/08_params.md | 15 - .../integrate/modules/slashing/09_client.md | 294 --- .../integrate/modules/slashing/README.md | 49 - .../modules/slashing/_category_.json | 5 - .../integrate/modules/staking/01_state.md | 215 -- .../modules/staking/02_state_transitions.md | 180 -- .../integrate/modules/staking/03_messages.md | 175 -- .../modules/staking/04_begin_block.md | 16 - .../integrate/modules/staking/05_end_block.md | 77 - .../integrate/modules/staking/06_hooks.md | 27 - .../integrate/modules/staking/07_events.md | 90 - .../integrate/modules/staking/08_params.md | 16 - .../integrate/modules/staking/09_client.md | 2101 ----------------- .../integrate/modules/staking/README.md | 56 - .../integrate/modules/staking/_category_.json | 5 - .../staking/begin_redelegation_sequence.svg | 106 - .../modules/staking/delegation_sequence.svg | 192 -- .../modules/staking/unbond_sequence.svg | 110 - .../integrate/modules/upgrade/01_concepts.md | 104 - .../integrate/modules/upgrade/02_state.md | 20 - .../integrate/modules/upgrade/03_events.md | 8 - .../integrate/modules/upgrade/04_client.md | 459 ---- .../integrate/modules/upgrade/README.md | 31 - .../integrate/modules/upgrade/_category_.json | 5 - .../version-0.46/user/_category_.json | 5 - .../version-0.46/user/run-node/00-keyring.md | 132 -- .../version-0.46/user/run-node/01-run-node.md | 163 -- .../user/run-node/02-interact-node.md | 248 -- .../version-0.46/user/run-node/03-txs.md | 379 --- .../version-0.46/user/run-node/04-rosetta.md | 108 - .../user/run-node/05-run-testnet.md | 95 - .../user/run-node/_category_.json | 5 - versions.json | 4 +- 346 files changed, 30 insertions(+), 43735 deletions(-) create mode 100644 build-docs.sh delete mode 100644 versioned_docs/version-0.45/develop/_category_.json delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/00-baseapp.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/01-transactions.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/02-context.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/03-node.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/04-store.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/05-encoding.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/06-cli.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/07-events.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/08-grpc_rest.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/09-ocap.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/10-telemetry.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/11-runtx_middleware.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/12-simulation.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/13-upgrade.md delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-begin_block.png delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-checktx.png delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-commit.png delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-deliver_tx.png delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-initchain.png delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state_types.png delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/keeper_dependencies.svg delete mode 100644 versioned_docs/version-0.45/develop/advanced-concepts/transaction_flow.svg delete mode 100644 versioned_docs/version-0.45/develop/high-level-concepts/00-overview-app.md delete mode 100644 versioned_docs/version-0.45/develop/high-level-concepts/01-tx-lifecycle.md delete mode 100644 versioned_docs/version-0.45/develop/high-level-concepts/02-query-lifecycle.md delete mode 100644 versioned_docs/version-0.45/develop/high-level-concepts/03-accounts.md delete mode 100644 versioned_docs/version-0.45/develop/high-level-concepts/04-gas-fees.md delete mode 100644 versioned_docs/version-0.45/develop/high-level-concepts/_category_.json delete mode 100644 versioned_docs/version-0.45/develop/intro/overview.md delete mode 100644 versioned_docs/version-0.45/develop/intro/sdk-app-architecture.md delete mode 100644 versioned_docs/version-0.45/develop/intro/sdk-design.md delete mode 100644 versioned_docs/version-0.45/develop/intro/why-app-specific.md delete mode 100644 versioned_docs/version-0.45/integrate/_category_.json delete mode 100644 versioned_docs/version-0.45/integrate/architecture/PROCESS.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/README.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-002-docs-structure.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-003-dynamic-capability-store.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-004-split-denomination-keys.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-006-secret-store-replacement.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-007-specialization-groups.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-008-dCERT-group.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-009-evidence-module.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-010-modular-antehandler.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-011-generalize-genesis-accounts.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-012-state-accessors.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-013-metrics.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-014-proportional-slashing.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-016-validator-consensus-key-rotation.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-017-historical-header-module.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-018-extendable-voting-period.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-019-protobuf-state-encoding.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-020-protobuf-transaction-encoding.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-021-protobuf-query-encoding.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-022-custom-panic-handling.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-023-protobuf-naming.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-024-coin-metadata.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-027-deterministic-protobuf-serialization.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-028-public-key-addresses.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-029-fee-grant-module.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-030-authz-module.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-031-msg-service.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-032-typed-events.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-033-protobuf-inter-module-comm.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-034-account-rekeying.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-035-rosetta-api-support.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-036-arbitrary-signature.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-037-gov-split-vote.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-038-state-listening.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-039-epoched-staking.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-040-storage-and-smt-state-commitments.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-041-in-place-store-migrations.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-042-group-module.md delete mode 100644 versioned_docs/version-0.45/integrate/architecture/adr-template.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/00-intro.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/01-module-manager.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/02-messages-and-queries.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/03-msg-services.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/04-query-services.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/05-beginblock-endblock.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/06-keeper.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/07-invariants.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/08-genesis.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/09-module-interfaces.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/10-structure.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/11-errors.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/12-upgrade.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/13-simulator.md delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/_category_.json delete mode 100644 versioned_docs/version-0.45/integrate/building-modules/transaction_flow.svg delete mode 100644 versioned_docs/version-0.45/integrate/ibc/_category_.json delete mode 100644 versioned_docs/version-0.45/integrate/ibc/custom.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/integration.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/overview.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/proposals.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/relayer.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/upgrades/README.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/upgrades/_category_.json delete mode 100644 versioned_docs/version-0.45/integrate/ibc/upgrades/developer-guide.md delete mode 100644 versioned_docs/version-0.45/integrate/ibc/upgrades/quick-guide.md delete mode 100644 versioned_docs/version-0.45/integrate/modules/README.md delete mode 100644 versioned_docs/version-0.45/integrate/rfc/README.md delete mode 100644 versioned_docs/version-0.45/user/_category_.json delete mode 100644 versioned_docs/version-0.45/user/run-node/00-keyring.md delete mode 100644 versioned_docs/version-0.45/user/run-node/01-run-node.md delete mode 100644 versioned_docs/version-0.45/user/run-node/02-interact-node.md delete mode 100644 versioned_docs/version-0.45/user/run-node/03-txs.md delete mode 100644 versioned_docs/version-0.45/user/run-node/04-rosetta.md delete mode 100644 versioned_docs/version-0.45/user/run-node/05-run-testnet.md delete mode 100644 versioned_docs/version-0.45/user/run-node/_category_.json delete mode 100644 versioned_docs/version-0.46/develop/_category_.json delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/00-baseapp.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/01-transactions.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/03-context.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/04-node.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/05-store.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/07-encoding.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/08-grpc_rest.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/09-cli.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/10-events.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/11-telemetry.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/12-ocap.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/13-runtx_middleware.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/13-simulation.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/14-proto-docs.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/15-tips.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/16-upgrade.md delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/_category_.json delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-begin_block.png delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-checktx.png delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-commit.png delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-deliver_tx.png delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-initchain.png delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state_types.png delete mode 100644 versioned_docs/version-0.46/develop/advanced-concepts/keeper_dependencies.svg delete mode 100644 versioned_docs/version-0.46/develop/high-level-concepts/00-overview-app.md delete mode 100644 versioned_docs/version-0.46/develop/high-level-concepts/01-tx-lifecycle.md delete mode 100644 versioned_docs/version-0.46/develop/high-level-concepts/02-query-lifecycle.md delete mode 100644 versioned_docs/version-0.46/develop/high-level-concepts/03-accounts.md delete mode 100644 versioned_docs/version-0.46/develop/high-level-concepts/04-gas-fees.md delete mode 100644 versioned_docs/version-0.46/develop/high-level-concepts/_category_.json delete mode 100644 versioned_docs/version-0.46/develop/intro/00-what-is-sdk.md delete mode 100644 versioned_docs/version-0.46/develop/intro/01-why-app-specific.md delete mode 100644 versioned_docs/version-0.46/develop/intro/02-sdk-app-architecture.md delete mode 100644 versioned_docs/version-0.46/develop/intro/03-sdk-design.md delete mode 100644 versioned_docs/version-0.46/develop/intro/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/CosmWasm/README.md delete mode 100644 versioned_docs/version-0.46/integrate/CosmWasm/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/architecture/PROCESS.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/README.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-002-docs-structure.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-003-dynamic-capability-store.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-004-split-denomination-keys.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-006-secret-store-replacement.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-007-specialization-groups.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-008-dCERT-group.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-009-evidence-module.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-010-modular-antehandler.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-011-generalize-genesis-accounts.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-012-state-accessors.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-013-metrics.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-014-proportional-slashing.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-016-validator-consensus-key-rotation.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-017-historical-header-module.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-018-extendable-voting-period.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-019-protobuf-state-encoding.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-020-protobuf-transaction-encoding.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-021-protobuf-query-encoding.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-022-custom-panic-handling.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-023-protobuf-naming.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-024-coin-metadata.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-027-deterministic-protobuf-serialization.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-028-public-key-addresses.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-029-fee-grant-module.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-030-authz-module.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-031-msg-service.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-032-typed-events.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-033-protobuf-inter-module-comm.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-034-account-rekeying.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-035-rosetta-api-support.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-036-arbitrary-signature.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-037-gov-split-vote.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-038-state-listening.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-039-epoched-staking.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-040-storage-and-smt-state-commitments.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-041-in-place-store-migrations.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-042-group-module.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-043-nft-module.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-044-protobuf-updates-guidelines.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-045-check-delivertx-middlewares.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-046-module-params.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-047-extend-upgrade-plan.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-048-consensus-fees.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-049-state-sync-hooks.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-053-go-module-refactoring.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-055-orm.md delete mode 100644 versioned_docs/version-0.46/integrate/architecture/adr-template.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/00-intro.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/01-module-manager.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/02-messages-and-queries.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/03-msg-services.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/04-query-services.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/05-beginblock-endblock.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/06-keeper.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/07-invariants.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/08-genesis.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/09-module-interfaces.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/11-structure.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/12-errors.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/13-upgrade.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/14-simulator.md delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/building-modules/transaction_flow.svg delete mode 100644 versioned_docs/version-0.46/integrate/ibc/README.md delete mode 100644 versioned_docs/version-0.46/integrate/ibc/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/migrations/00-pre-upgrade.md delete mode 100644 versioned_docs/version-0.46/integrate/migrations/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/03_antehandlers.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/04_keepers.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/05_vesting.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/06_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/07_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/auth/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/04_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/05_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/authz/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/01_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/02_keepers.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/04_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/05_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/06_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/bank/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/capability/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/capability/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/capability/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/capability/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/01_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/02_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/03_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/04_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/05_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/crisis/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/03_begin_block.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/04_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/05_hooks.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/06_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/07_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/08_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/distribution/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/04_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/05_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/06_begin_block.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/07_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/evidence/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/04_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/05_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/feegrant/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/04_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/05_future_improvements.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/06_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/07_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/08_metadata.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/gov/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/03_begin_block.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/04_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/05_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/06_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/mint/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/params/01_keeper.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/params/02_subspace.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/params/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/params/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/04_begin_block.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/05_hooks.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/06_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/07_tombstone.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/08_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/09_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/slashing/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/01_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/02_state_transitions.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/03_messages.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/04_begin_block.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/05_end_block.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/06_hooks.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/07_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/08_params.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/09_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/_category_.json delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/begin_redelegation_sequence.svg delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/delegation_sequence.svg delete mode 100644 versioned_docs/version-0.46/integrate/modules/staking/unbond_sequence.svg delete mode 100644 versioned_docs/version-0.46/integrate/modules/upgrade/01_concepts.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/upgrade/02_state.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/upgrade/03_events.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/upgrade/04_client.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/upgrade/README.md delete mode 100644 versioned_docs/version-0.46/integrate/modules/upgrade/_category_.json delete mode 100644 versioned_docs/version-0.46/user/_category_.json delete mode 100644 versioned_docs/version-0.46/user/run-node/00-keyring.md delete mode 100644 versioned_docs/version-0.46/user/run-node/01-run-node.md delete mode 100644 versioned_docs/version-0.46/user/run-node/02-interact-node.md delete mode 100644 versioned_docs/version-0.46/user/run-node/03-txs.md delete mode 100644 versioned_docs/version-0.46/user/run-node/04-rosetta.md delete mode 100644 versioned_docs/version-0.46/user/run-node/05-run-testnet.md delete mode 100644 versioned_docs/version-0.46/user/run-node/_category_.json diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 1bd244df6..df9d0f1f9 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -34,6 +34,11 @@ jobs: - name: Install Dependencies run: yarn install --frozen-lockfile + - name: Build Legacy Docs + run: | + chmod +x build-docs.sh + ./build-docs.sh + - name: Build run: yarn build @@ -41,5 +46,5 @@ jobs: uses: JamesIves/github-pages-deploy-action@v4.4.1 with: branch: gh-pages - folder: ~/output + folder: build single-commit: true \ No newline at end of file diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index d90e62f6e..fbaca4039 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -15,5 +15,9 @@ jobs: with: node-version: 16.x cache: npm + - run: | + chmod +x build-docs.sh + ./build-docs.sh + - run: yarn install --frozen-lockfile - - run: yarn build \ No newline at end of file + - run: yarn build diff --git a/build-docs.sh b/build-docs.sh new file mode 100644 index 000000000..bb20225a6 --- /dev/null +++ b/build-docs.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +rm -rf ./cosmos-sdk +git clone https://github.com/cosmos/cosmos-sdk/ cosmos-sdk; +cd cosmos-sdk/docs; + +while read -r branch path_prefix; do + echo "building vuepress $branch docs" + ( + git clean -fdxq && git reset --hard && git checkout $branch && npm install && + VUEPRESS_BASE="/$path_prefix/" + rm basics/app-anatomy.md + npm run build + mkdir -p "../../build/$path_prefix" + + cp -r .vuepress/dist/* "../../build/$path_prefix" + ) +done < vuepress_versions diff --git a/versioned_docs/version-0.45/develop/_category_.json b/versioned_docs/version-0.45/develop/_category_.json deleted file mode 100644 index f40637f4d..000000000 --- a/versioned_docs/version-0.45/develop/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Develop", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/00-baseapp.md b/versioned_docs/version-0.45/develop/advanced-concepts/00-baseapp.md deleted file mode 100644 index 2160150d7..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/00-baseapp.md +++ /dev/null @@ -1,395 +0,0 @@ -# BaseApp - -This document describes `BaseApp`, the abstraction that implements the core functionalities of an SDK application. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK application](../high-level-concepts/00-overview-app.md) {prereq} -- [Lifecycle of an SDK transaction](../high-level-concepts/01-tx-lifecycle.md) {prereq} - -## Introduction - -`BaseApp` is a base type that implements the core of an SDK application, namely: - -- The [Application Blockchain Interface](#abci), for the state-machine to communicate with the underlying consensus engine (e.g. Tendermint). -- [Service Routers](#service-routers), to route messages and queries to the appropriate module. -- Different [states](#states), as the state-machine can have different volatile states updated based on the ABCI message received. - -The goal of `BaseApp` is to provide the fundamental layer of an SDK application -that developers can easily extend to build their own custom application. Usually, -developers will create a custom type for their application, like so: - -```go -type App struct { - // reference to a BaseApp - *baseapp.BaseApp - - // list of application store keys - - // list of application keepers - - // module manager -} -``` - -Extending the application with `BaseApp` gives the former access to all of `BaseApp`'s methods. -This allows developers to compose their custom application with the modules they want, while not -having to concern themselves with the hard work of implementing the ABCI, the service routers and state -management logic. - -## Type Definition - -The `BaseApp` type holds many important parameters for any Cosmos SDK based application. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L46-L131 - -Let us go through the most important components. - -> **Note**: Not all parameters are described, only the most important ones. Refer to the -> type definition for the full list. - -First, the important parameters that are initialized during the bootstrapping of the application: - -- [`CommitMultiStore`](./04-store.md#commitmultistore): This is the main store of the application, - which holds the canonical state that is committed at the [end of each block](#commit). This store - is **not** cached, meaning it is not used to update the application's volatile (un-committed) states. - The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application - uses one or multiple `KVStores` in the multi-store to persist their subset of the state. -- Database: The `db` is used by the `CommitMultiStore` to handle data persistence. -- [`Msg` Service Router](#msg-service-router): The `msgServiceRouter` facilitates the routing of `sdk.Msg` requests to the appropriate - module `Msg` service for processing. Here a `sdk.Msg` refers to the transaction component that needs to be - processed by a service in order to update the application state, and not to ABCI message which implements - the interface between the application and the underlying consensus engine. -- [gRPC Query Router](#grpc-query-router): The `grpcQueryRouter` facilitates the routing of gRPC queries to the - appropriate module for it to be processed. These queries are not ABCI messages themselves, but they - are relayed to the relevant module's gRPC `Query` service. -- [`TxDecoder`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#TxDecoder): It is used to decode - raw transaction bytes relayed by the underlying Tendermint engine. -- [`ParamStore`](#paramstore): The parameter store used to get and set application consensus parameters. -- [`AnteHandler`](#antehandler): This handler is used to handle signature verification, fee payment, - and other pre-message execution checks when a transaction is received. It's executed during - [`CheckTx/RecheckTx`](#checktx) and [`DeliverTx`](#delivertx). -- [`InitChainer`](../high-level-concepts/00-overview-app.md#initchainer), - [`BeginBlocker` and `EndBlocker`](../high-level-concepts/00-overview-app.md#beginblocker-and-endblocker): These are - the functions executed when the application receives the `InitChain`, `BeginBlock` and `EndBlock` - ABCI messages from the underlying Tendermint engine. - -Then, parameters used to define [volatile states](#volatile-states) (i.e. cached states): - -- `checkState`: This state is updated during [`CheckTx`](#checktx), and reset on [`Commit`](#commit). -- `deliverState`: This state is updated during [`DeliverTx`](#delivertx), and set to `nil` on - [`Commit`](#commit) and gets re-initialized on BeginBlock. - -Finally, a few more important parameterd: - -- `voteInfos`: This parameter carries the list of validators whose precommit is missing, either - because they did not vote or because the proposer did not include their vote. This information is - carried by the [Context](#context) and can be used by the application for various things like - punishing absent validators. -- `minGasPrices`: This parameter defines the minimum gas prices accepted by the node. This is a - **local** parameter, meaning each full-node can set a different `minGasPrices`. It is used in the - `AnteHandler` during [`CheckTx`](#checktx), mainly as a spam protection mechanism. The transaction - enters the [mempool](https://tendermint.com/docs/tendermint-core/mempool.html#transaction-ordering) - only if the gas prices of the transaction are greater than one of the minimum gas price in - `minGasPrices` (e.g. if `minGasPrices == 1uatom,1photon`, the `gas-price` of the transaction must be - greater than `1uatom` OR `1photon`). -- `appVersion`: Version of the application. It is set in the - [application's constructor function](../high-level-concepts/00-overview-app.md#constructor-function). - -## Constructor - -```go -func NewBaseApp( - name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp), -) *BaseApp { - - // ... -} -``` - -The `BaseApp` constructor function is pretty straightforward. The only thing worth noting is the -possibility to provide additional [`options`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/options.go) -to the `BaseApp`, which will execute them in order. The `options` are generally `setter` functions -for important parameters, like `SetPruning()` to set pruning options or `SetMinGasPrices()` to set -the node's `min-gas-prices`. - -Naturally, developers can add additional `options` based on their application's needs. - -## State Updates - -The `BaseApp` maintains two primary volatile states and a root or main state. The main state -is the canonical state of the application and the volatile states, `checkState` and `deliverState`, -are used to handle state transitions in-between the main state made during [`Commit`](#commit). - -Internally, there is only a single `CommitMultiStore` which we refer to as the main or root state. -From this root state, we derive two volatile states by using a mechanism called _store branching_ (performed by `CacheWrap` function). -The types can be illustrated as follows: - -![Types](./baseapp_state_types.png) - -### InitChain State Updates - -During `InitChain`, the two volatile states, `checkState` and `deliverState` are set by branching -the root `CommitMultiStore`. Any subsequent reads and writes happen on branched versions of the `CommitMultiStore`. -To avoid unnecessary roundtrip to the main state, all reads to the branched store are cached. - -![InitChain](./baseapp_state-initchain.png) - -### CheckTx State Updates - -During `CheckTx`, the `checkState`, which is based off of the last committed state from the root -store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a service router -exists for every message in the transaction. Note, when we execute the `AnteHandler`, we branch -the already branched `checkState`. This has the side effect that if the `AnteHandler` fails, -the state transitions won't be reflected in the `checkState` -- i.e. `checkState` is only updated on -success. - -![CheckTx](./baseapp_state-checktx.png) - -### BeginBlock State Updates - -During `BeginBlock`, the `deliverState` is set for use in subsequent `DeliverTx` ABCI messages. The -`deliverState` is based off of the last committed state from the root store and is branched. -Note, the `deliverState` is set to `nil` on [`Commit`](#commit). - -![BeginBlock](./baseapp_state-begin_block.png) - -### DeliverTx State Updates - -The state flow for `DeliverTx` is nearly identical to `CheckTx` except state transitions occur on -the `deliverState` and messages in a transaction are executed. Similarly to `CheckTx`, state transitions -occur on a doubly branched state -- `deliverState`. Successful message execution results in -writes being committed to `deliverState`. Note, if message execution fails, state transitions from -the AnteHandler are persisted. - -![DeliverTx](./baseapp_state-deliver_tx.png) - -### Commit State Updates - -During `Commit` all the state transitions that occurred in the `deliverState` are finally written to -the root `CommitMultiStore` which in turn is committed to disk and results in a new application -root hash. These state transitions are now considered final. Finally, the `checkState` is set to the -newly committed state and `deliverState` is set to `nil` to be reset on `BeginBlock`. - -![Commit](./baseapp_state-commit.png) - -## ParamStore - -During `InitChain`, the `RequestInitChain` provides `ConsensusParams` which contains parameters -related to block execution such as maximum gas and size in addition to evidence parameters. If these -parameters are non-nil, they are set in the BaseApp's `ParamStore`. Behind the scenes, the `ParamStore` -is actually managed by an `x/params` module `Subspace`. This allows the parameters to be tweaked via -on-chain governance. - -## Service Routers - -When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via `BaseApp`, which holds a `msgServiceRouter` for messages, and a `grpcQueryRouter` for queries. - -### `Msg` Service Router - -[`sdk.Msg`s](../../integrate/building-modules/02-messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `BaseApp` holds a `msgServiceRouter` which maps fully-qualified service methods (`string`, defined in each module's Protobuf `Msg` service) to the appropriate module's `MsgServer` implementation. - -The [default `msgServiceRouter` included in `BaseApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into each [route handler inside `msgServiceRouter`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go#L31-L32). For a stateless router that doesn't want to make use of this, you can just ignore the `ctx`. - -The application's `msgServiceRouter` is initialized with all the routes using the application's [module manager](../building-modules/01-module-manager.md#manager) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../high-level-concepts/00-overview-app.md#app-constructor). - -### gRPC Query Router - -Similar to `sdk.Msg`s, [`queries`](../building-modules/02-messages-and-queries.md#queries) need to be routed to the appropriate module's [`Query` service](../building-modules/04-query-services.md). To do so, `BaseApp` holds a `grpcQueryRouter`, which maps modules' fully-qualified service methods (`string`, defined in their Protobuf `Query` gRPC) to their `QueryServer` implementation. The `grpcQueryRouter` is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the [`Query` ABCI message](#query) on the Tendermint RPC endpoint. - -Just like the `msgServiceRouter`, the `grpcQueryRouter` is initialized with all the query routes using the application's [module manager](../building-modules/01-module-manager.md) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../high-level-concepts/00-overview-app.md#app-constructor). - -## Main ABCI Messages - -The [Application-Blockchain Interface](https://tendermint.com/docs/spec/abci/) (ABCI) is a generic interface that connects a state-machine with a consensus engine to form a functional full-node. It can be wrapped in any language, and needs to be implemented by each application-specific blockchain built on top of an ABCI-compatible consensus engine like Tendermint. - -The consensus engine handles two main tasks: - -- The networking logic, which mainly consists in gossiping block parts, transactions and consensus votes. -- The consensus logic, which results in the deterministic ordering of transactions in the form of blocks. - -It is **not** the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of `[]bytes`, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on. - -Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as `BaseApp` comes with a built-in implementation of the interface. Let us go through the main ABCI messages that `BaseApp` implements: [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) - -### CheckTx - -`CheckTx` is sent by the underlying consensus engine when a new unconfirmed (i.e. not yet included in a valid block) -transaction is received by a full-node. The role of `CheckTx` is to guard the full-node's mempool -(where unconfirmed transactions are stored until they are included in a block) from spam transactions. -Unconfirmed transactions are relayed to peers only if they pass `CheckTx`. - -`CheckTx()` can perform both _stateful_ and _stateless_ checks, but developers should strive to -make the checks **lightweight** because gas fees are not charged for the resources (CPU, data load...) used during the `CheckTx`. - -In the Cosmos SDK, after [decoding transactions](./05-encoding.md), `CheckTx()` is implemented -to do the following checks: - -1. Extract the `sdk.Msg`s from the transaction. -2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `sdk.Msg`s. This is done - first, as _stateless_ checks are less computationally expensive than _stateful_ checks. If - `ValidateBasic()` fail, `CheckTx` returns before running _stateful_ checks, which saves resources. -3. Perform non-module related _stateful_ checks on the [account](../high-level-concepts/03-accounts.md). This step is mainly about checking - that the `sdk.Msg` signatures are valid, that enough fees are provided and that the sending account - has enough funds to pay for said fees. Note that no precise [`gas`](../high-level-concepts/gas-fees.md) counting occurs here, - as `sdk.Msg`s are not processed. Usually, the [`AnteHandler`](../high-level-concepts/gas-fees.md#antehandler) will check that the `gas` provided - with the transaction is superior to a minimum reference gas amount based on the raw transaction size, - in order to avoid spam with transactions that provide 0 gas. - -`CheckTx` does **not** process `sdk.Msg`s - they only need to be processed when the canonical state need to be updated, which happens during `DeliverTx`. - -Steps 2. and 3. are performed by the [`AnteHandler`](../high-level-concepts/gas-fees.md#antehandler) in the [`RunTx()`](#runtx-antehandler-and-runmsgs) -function, which `CheckTx()` calls with the `runTxModeCheck` mode. During each step of `CheckTx()`, a -special [volatile state](#volatile-states) called `checkState` is updated. This state is used to keep -track of the temporary changes triggered by the `CheckTx()` calls of each transaction without modifying -the [main canonical state](#main-state) . For example, when a transaction goes through `CheckTx()`, the -transaction's fees are deducted from the sender's account in `checkState`. If a second transaction is -received from the same account before the first is processed, and the account has consumed all its -funds in `checkState` during the first transaction, the second transaction will fail `CheckTx`() and -be rejected. In any case, the sender's account will not actually pay the fees until the transaction -is actually included in a block, because `checkState` never gets committed to the main state. The -`checkState` is reset to the latest state of the main state each time a blocks gets [committed](#commit). - -`CheckTx` returns a response to the underlying consensus engine of type [`abci.ResponseCheckTx`](https://tendermint.com/docs/spec/abci/abci.html#messages). -The response contains: - -- `Code (uint32)`: Response Code. `0` if successful. -- `Data ([]byte)`: Result bytes, if any. -- `Log (string):` The output of the application's logger. May be non-deterministic. -- `Info (string):` Additional information. May be non-deterministic. -- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. -- `GasUsed (int64)`: Amount of gas consumed by transaction. During `CheckTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction. Next is an example: - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/ante/basic.go#L104-L105 -- `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./07-events.md) for more. -- `Codespace (string)`: Namespace for the Code. - -#### RecheckTx - -After `Commit`, `CheckTx` is run again on all transactions that remain in the node's local mempool -excluding the transactions that are included in the block. To prevent the mempool from rechecking all transactions -every time a block is committed, the configuration option `mempool.recheck=false` can be set. As of -Tendermint v0.32.1, an additional `Type` parameter is made available to the `CheckTx` function that -indicates whether an incoming transaction is new (`CheckTxType_New`), or a recheck (`CheckTxType_Recheck`). -This allows certain checks like signature verification can be skipped during `CheckTxType_Recheck`. - -### DeliverTx - -When the underlying consensus engine receives a block proposal, each transaction in the block needs to be processed by the application. To that end, the underlying consensus engine sends a `DeliverTx` message to the application for each transaction in a sequential order. - -Before the first transaction of a given block is processed, a [volatile state](#volatile-states) called `deliverState` is intialized during [`BeginBlock`](#beginblock). This state is updated each time a transaction is processed via `DeliverTx`, and committed to the [main state](#main-state) when the block is [committed](#commit), after what is is set to `nil`. - -`DeliverTx` performs the **exact same steps as `CheckTx`**, with a little caveat at step 3 and the addition of a fifth step: - -1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose. -2. For each `sdk.Msg` in the transaction, route to the appropriate module's Protobuf [`Msg` service](../building-modules/03-msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`. - -During the additional fifth step outlined in (2), each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L164-L175 - -At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0` and `DeliverTx` fails. - -`DeliverTx` returns a response to the underlying consensus engine of type [`abci.ResponseDeliverTx`](https://tendermint.com/docs/spec/abci/abci.html#delivertx). The response contains: - -- `Code (uint32)`: Response Code. `0` if successful. -- `Data ([]byte)`: Result bytes, if any. -- `Log (string):` The output of the application's logger. May be non-deterministic. -- `Info (string):` Additional information. May be non-deterministic. -- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. -- `GasUsed (int64)`: Amount of gas consumed by transaction. During `DeliverTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs. -- `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./07-events.md) for more. -- `Codespace (string)`: Namespace for the Code. - -## RunTx, AnteHandler and RunMsgs - -### RunTx - -`RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded. - -The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a branch of the main store, with cache functionality (for query requests), instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../high-level-concepts/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. - -After that, `RunTx()` calls `ValidateBasic()` on each `sdk.Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `sdk.Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. - -Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L623-L630 - -This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./09-ocap.md) of the Cosmos SDK. - -Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `sdk.Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. - -### AnteHandler - -The `AnteHandler` is a special handler that implements the `AnteHandler` interface and is used to authenticate the transaction before the transaction's internal messages are processed. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/handler.go#L6-L8 - -The `AnteHandler` is theoretically optional, but still a very important component of public blockchain networks. It serves 3 primary purposes: - -- Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and [`sequence`](./01-transactions.md#transaction-generation) checking. -- Perform preliminary _stateful_ validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees. -- Play a role in the incentivisation of stakeholders via the collection of transaction fees. - -`BaseApp` holds an `anteHandler` as parameter that is initialized in the [application's constructor](../high-level-concepts/00-overview-app.md#application-constructor). The most widely used `anteHandler` is the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/ante/ante.go). - -Click [here](../high-level-concepts/gas-fees.md#antehandler) for more on the `anteHandler`. - -### RunMsgs - -`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `sdk.Msg`s. - -First, it retrieves the `sdk.Msg`'s fully-qualified type name, by checking the `type_url` of the Protobuf `Any` representing the `sdk.Msg`. Then, using the application's [`msgServiceRouter`](#msg-service-router), it checks for the existence of `Msg` service method related to that `type_url`. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. Otherwise, if `mode == runTxModeDeliver`, the [`Msg` service](../building-modules/03-msg-services.md) RPC is executed, before `RunMsgs` returns. - -## Other ABCI Messages - -### InitChain - -The [`InitChain` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#initchain) is sent from the underlying Tendermint engine when the chain is first started. It is mainly used to **initialize** parameters and state like: - -- [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters) via `setConsensusParams`. -- [`checkState` and `deliverState`](#volatile-states) via `setCheckState` and `setDeliverState`. -- The [block gas meter](../high-level-concepts/gas-fees.md#block-gas-meter), with infinite gas to process genesis transactions. - -Finally, the `InitChain(req abci.RequestInitChain)` method of `BaseApp` calls the [`initChainer()`](../high-level-concepts/00-overview-app.md#initchainer) of the application in order to initialize the main state of the application from the `genesis file` and, if defined, call the [`InitGenesis`](../building-modules/08-genesis.md#initgenesis) function of each of the application's modules. - -### BeginBlock - -The [`BeginBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#beginblock) is sent from the underlying Tendermint engine when a block proposal created by the correct proposer is received, before [`DeliverTx`](#delivertx) is run for each transaction in the block. It allows developers to have logic be executed at the beginning of each block. In the Cosmos SDK, the `BeginBlock(req abci.RequestBeginBlock)` method does the following: - -- Initialize [`deliverState`](#volatile-states) with the latest header using the `req abci.RequestBeginBlock` passed as parameter via the `setDeliverState` function. - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L387-L397 - This function also resets the [main gas meter](../high-level-concepts/gas-fees.md#main-gas-meter). -- Initialize the [block gas meter](../high-level-concepts/gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters. -- Run the application's [`beginBlocker()`](../high-level-concepts/00-overview-app.md#beginblocker-and-endblock), which mainly runs the [`BeginBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. -- Set the [`VoteInfos`](https://tendermint.com/docs/app-dev/abci-spec.html#voteinfo) of the application, i.e. the list of validators whose _precommit_ for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./02-context.md) so that it can be used during `DeliverTx` and `EndBlock`. - -### EndBlock - -The [`EndBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#endblock) is sent from the underlying Tendermint engine after [`DeliverTx`](#delivertx) as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk `EndBlock(req abci.RequestEndBlock)` method is to run the application's [`EndBlocker()`](../high-level-concepts/00-overview-app.md#beginblocker-and-endblock), which mainly runs the [`EndBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. - -### Commit - -The [`Commit` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#commit) is sent from the underlying Tendermint engine after the full-node has received _precommits_ from 2/3+ of validators (weighted by voting power). On the `BaseApp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occured during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block. - -To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a branched multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtbained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`. - -Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block. - -### Info - -The [`Info` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#info) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the `Info(res abci.ResponseInfo)` function from `BaseApp` will return the application's name, version and the hash of the last commit of `app.cms`. - -### Query - -The [`Query` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#query) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It used to be the main entrypoint to build interfaces with the application, but with the introduction of [gRPC queries](../building-modules/04-query-services.md) in Cosmos SDK v0.40, its usage is more limited. The application must respect a few rules when implementing the `Query` method, which are outlined [here](https://tendermint.com/docs/app-dev/abci-spec.html#query). - -Each Tendermint `query` comes with a `path`, which is a `string` which denotes what to query. If the `path` matches a gRPC fully-qualified service method, then `BaseApp` will defer the query to the `grpcQueryRouter` and let it handle it like explained [above](#grpc-query-router). Otherwise, the `path` represents a query that is not (yet) handled by the gRPC router. `BaseApp` splits the `path` string with the `/` delimiter. By convention, the first element of the splitted string (`splitted[0]`) contains the category of `query` (`app`, `p2p`, `store` or `custom` ). The `BaseApp` implementation of the `Query(req abci.RequestQuery)` method is a simple dispatcher serving these 4 main categories of queries: - -- Application-related queries like querying the application's version, which are served via the `handleQueryApp` method. -- Direct queries to the multistore, which are served by the `handlerQueryStore` method. These direct queries are different from custom queries which go through `app.queryRouter`, and are mainly used by third-party service provider like block explorers. -- P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `BaseApp`'s [constructor](#constructor). -- Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the `handleQueryCustom` method. The `handleQueryCustom` branches the multistore before using the `queryRoute` obtained from `app.queryRouter` to map the query to the appropriate module's [legacy `querier`](../building-modules/04-query-services.md#legacy-queriers). - -## Next {hide} - -Learn more about [transactions](./01-transactions.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/01-transactions.md b/versioned_docs/version-0.45/develop/advanced-concepts/01-transactions.md deleted file mode 100644 index 8791e945b..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/01-transactions.md +++ /dev/null @@ -1,155 +0,0 @@ -# Transactions - -`Transactions` are objects created by end-users to trigger state changes in the application. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK Application](../high-level-concepts/overview-app.md) {prereq} - -## Transactions - -Transactions are comprised of metadata held in [contexts](./02-context.md) and [`sdk.Msg`s](../building-modules/02-messages-and-queries.md) that trigger state changes within a module through the module's Protobuf [`Msg` service](../integrate/building-modules/03-msg-services.md). - -When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `sdk.Msg` must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../high-level-concepts/01-tx-lifecycle.md). - -## Type Definition - -Transaction objects are SDK types that implement the `Tx` interface - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/tx_msg.go#L49-L57 - -It contains the following methods: - -- **GetMsgs:** unwraps the transaction and returns a list of contained `sdk.Msg`s - one transaction may have one or multiple messages, which are defined by module developers. -- **ValidateBasic:** lightweight, [_stateless_](../high-level-concepts/01-tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./00-baseapp.md#checktx) and [`DeliverTx`](./00-baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from `sdk.Msg` [`ValidateBasic`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic) methods, which perform basic validity checks on messages only. When [`runTx`](./00-baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself. - -As a developer, you should rarely manipulate `Tx` directly, as `Tx` is really an intermediate type used for transaction generation. Instead, developers should prefer the `TxBuilder` interface, which you can learn more about [below](#transaction-generation). - -### Signing Transactions - -Every message in a transaction must be signed by the addresses specified by its `GetSigners`. The SDK currently allows signing transactions in two different ways. - -#### `SIGN_MODE_DIRECT` (preferred) - -The most used implementation of the `Tx` interface is the Protobuf `Tx` message, which is used in `SIGN_MODE_DIRECT`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L12-L25 - -Because Protobuf serialization is not deterministic, the SDK uses an additional `TxRaw` type to denote the pinned bytes over which a transaction is signed. Any user can generate a valid `body` and `auth_info` for a transaction, and serialize these two messages using Protobuf. `TxRaw` then pins the user's exact binary representation of `body` and `auth_info`, called respectively `body_bytes` and `auth_info_bytes`. The document that is signed by all signers of the transaction is `SignDoc` (deterministically serialized using [ADR-027](../architecture/adr-027-deterministic-protobuf-serialization.md)): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L47-L64 - -Once signed by all signers, the `body_bytes`, `auth_info_bytes` and `signatures` are gathered into `TxRaw`, whose serialized bytes are broadcasted over the network. - -#### `SIGN_MODE_LEGACY_AMINO_JSON` - -The legacy implemention of the `Tx` interface is the `StdTx` struct from `x/auth`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdtx.go#L120-L130 - -The document signed by all signers is `StdSignDoc`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdsign.go#L20-L33 - -which is encoded into bytes using Amino JSON. Once all signatures are gathered into `StdTx`, `StdTx` is serialized using Amino JSON, and these bytes are broadcasted over the network. - -#### Other Sign Modes - -Other sign modes, most notably `SIGN_MODE_TEXTUAL`, are being discussed. If you wish to learn more about them, please refer to [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md). - -## Transaction Process - -The process of an end-user sending a transaction is: - -- decide on the messages to put into the transaction, -- generate the transaction using the SDK's `TxBuilder`, -- broadcast the transaction using one of the available interfaces. - -The next paragraphs will describe each of these components, in this order. - -### Messages - -::: tip -Module `sdk.Msg`s are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers. -::: - -**Messages** (or `sdk.Msg`s) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf [`Msg` service](../building-modules/03-msg-services.md), and also implement the corresponding `MsgServer`. - -Each `sdk.Msg`s is related to exactly one Protobuf [`Msg` service](../building-modules/03-msg-services.md) RPC, defined inside each module's `tx.proto` file. An SKD app router automatically maps every `sdk.Msg` to a corresponding RPC. Protobuf generates a `MsgServer` interface for each module `Msg` service, and the module developer needs to implement this interface. -This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. - -To learn more about Protobuf `Msg` services and how to implement `MsgServer`, click [here](../building-modules/03-msg-services.md). - -While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the `TxBuilder` and `Context`. - -### Transaction Generation - -The `TxBuilder` interface contains data closely related with the generation of transactions, which an end-user can freely set to generate the desired transaction: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/client/tx_config.go#L32-L45 - -- `Msg`s, the array of [messages](#messages) included in the transaction. -- `GasLimit`, option chosen by the users for how to calculate how much gas they will need to pay. -- `Memo`, a note or comment to send with the transaction. -- `FeeAmount`, the maximum amount the user is willing to pay in fees. -- `TimeoutHeight`, block height until which the transaction is valid. -- `Signatures`, the array of signatures from all signers of the transaction. - -As there are currently two sign modes for signing transactions, there are also two implementations of `TxBuilder`: - -- [wrapper](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/tx/builder.go#L19-L33) for creating transactions for `SIGN_MODE_DIRECT`, -- [StdTxBuilder](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdtx_builder.go#L14-L20) for `SIGN_MODE_LEGACY_AMINO_JSON`. - -However, the two implementation of `TxBuilder` should be hidden away from end-users, as they should prefer using the overarching `TxConfig` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/client/tx_config.go#L21-L30 - -`TxConfig` is an app-wide configuration for managing transactions. Most importantly, it holds the information about whether to sign each transaction with `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`. By calling `txBuilder := txConfig.NewTxBuilder()`, a new `TxBuilder` will be created with the appropriate sign mode. - -Once `TxBuilder` is correctly populated with the setters exposed above, `TxConfig` will also take care of correctly encoding the bytes (again, either using `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`). Here's a pseudo-code snippet of how to generate and encode a transaction, using the `TxEncoder()` method: - -```go -txBuilder := txConfig.NewTxBuilder() -txBuilder.SetMsgs(...) // and other setters on txBuilder - -bz, err := txConfig.TxEncoder()(txBuilder.GetTx()) -// bz are bytes to be broadcasted over the network -``` - -### Broadcasting the Transaction - -Once the transaction bytes are generated, there are currently three ways of broadcasting it. - -#### CLI - -Application developers create entrypoints to the application by creating a [command-line interface](../06-cli.md), [gRPC and/or REST interface](../08-grpc_rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line. - -For the [command-line interface](../building-modules/09-module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. CLI commands actually bundle all the steps of transaction processing into one simple command: creating messages, generating transactions and broadcasting. For concrete examples, see the [Interacting with a Node](../run-node/interact-node.md) section. An example transaction made using CLI looks like: - -```bash -simd tx send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake -``` - -#### gRPC - -[gRPC](https://grpc.io) is introduced in Cosmos SDK 0.40 as the main component for the SDK's RPC layer. The principal usage of gRPC is in the context of modules' [`Query` services](../building-modules). However, the SDK also exposes a few other module-agnostic gRPC services, one of them being the `Tx` service: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/service.proto - -The `Tx` service exposes a handful of utility functions, such as simulating a transaction or querying a transaction, and also one method to broadcast transactions. - -Examples of broadcasting and simulating a transaction are shown [here](../../user/run-node/txs.md#programmatically-with-go). - -#### REST - -Each gRPC method has its corresponding REST endpoint, generated using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). Therefore, instead of using gRPC, you can also use HTTP to broadcast the same transaction, on the `POST /cosmos/tx/v1beta1/txs` endpoint. - -An example can be seen [here](../../user/run-node/txs.md#using-rest) - -#### Tendermint RPC - -The three methods presented above are actually higher abstractions over the Tendermint RPC `/broadcast_tx_{async,sync,commit}` endpoints, documented [here](https://docs.tendermint.com/master/rpc/#/Tx). This means that you can use the Tendermint RPC endpoints directly to broadcast the transaction, if you wish so. - -## Next {hide} - -Learn about the [context](./02-context.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/02-context.md b/versioned_docs/version-0.45/develop/advanced-concepts/02-context.md deleted file mode 100644 index 7fcf5202f..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/02-context.md +++ /dev/null @@ -1,100 +0,0 @@ -# Context - -The `context` is a data structure intended to be passed from function to function that carries information about the current state of the application. It provides an access to a branched storage (a safe branch of the entire state) as well as useful objects and information like `gasMeter`, `block height`, `consensus parameters` and more. {synopsis} - -## Pre-requisites Readings - -- [Anatomy of an SDK Application](../high-level-concepts/overview-app.md) {prereq} -- [Lifecycle of a Transaction](../high-level-concepts/01-tx-lifecycle.md) {prereq} - -## Context Definition - -The SDK `Context` is a custom data structure that contains Go's stdlib [`context`](https://golang.org/pkg/context) as its base, and has many additional types within its definition that are specific to the Cosmos SDK. The `Context` is integral to transaction processing in that it allows modules to easily access their respective [store](./04-store.md#base-layer-kvstores) in the [`multistore`](./04-store.md#multistore) and retrieve transactional context such as the block header and gas meter. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/context.go#L16-L39 - -- **Context:** The base type is a Go [Context](https://golang.org/pkg/context), which is explained further in the [Go Context Package](#go-context-package) section below. -- **Multistore:** Every application's `BaseApp` contains a [`CommitMultiStore`](./04-store.md#multistore) which is provided when a `Context` is created. Calling the `KVStore()` and `TransientStore()` methods allows modules to fetch their respective [`KVStore`](./04-store.md#base-layer-kvstores) using their unique `StoreKey`. -- **ABCI Header:** The [header](https://tendermint.com/docs/spec/abci/abci.html#header) is an ABCI type. It carries important information about the state of the blockchain, such as block height and proposer of the current block. -- **Chain ID:** The unique identification number of the blockchain a block pertains to. -- **Transaction Bytes:** The `[]byte` representation of a transaction being processed using the context. Every transaction is processed by various parts of the SDK and consensus engine (e.g. Tendermint) throughout its [lifecycle](../high-level-concepts/01-tx-lifecycle.md), some of which to not have any understanding of transaction types. Thus, transactions are marshaled into the generic `[]byte` type using some kind of [encoding format](./05-encoding.md) such as [Amino](./05-encoding.md). -- **Logger:** A `logger` from the Tendermint libraries. Learn more about logs [here](https://tendermint.com/docs/tendermint-core/how-to-read-logs.html#how-to-read-logs). Modules call this method to create their own unique module-specific logger. -- **VoteInfo:** A list of the ABCI type [`VoteInfo`](https://tendermint.com/docs/spec/abci/abci.html#voteinfo), which includes the name of a validator and a boolean indicating whether they have signed the block. -- **Gas Meters:** Specifically, a [`gasMeter`](../high-level-concepts/gas-fees.md#main-gas-meter) for the transaction currently being processed using the context and a [`blockGasMeter`](../high-level-concepts/gas-fees.md#block-gas-meter) for the entire block it belongs to. Users specify how much in fees they wish to pay for the execution of their transaction; these gas meters keep track of how much [gas](../high-level-concepts/gas-fees.md) has been used in the transaction or block so far. If the gas meter runs out, execution halts. -- **CheckTx Mode:** A boolean value indicating whether a transaction should be processed in `CheckTx` or `DeliverTx` mode. -- **Min Gas Price:** The minimum [gas](../high-level-concepts/gas-fees.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually, and should therefore **not be used in any functions used in sequences leading to state-transitions**. -- **Consensus Params:** The ABCI type [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters), which specify certain limits for the blockchain, such as maximum gas for a block. -- **Event Manager:** The event manager allows any caller with access to a `Context` to emit [`Events`](./07-events.md). Modules may define module specific - `Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`, and `EndBlock` and are returned to Tendermint for indexing. For example: - -```go -ctx.EventManager().EmitEvent(sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory)), -) -``` - -## Go Context Package - -A basic `Context` is defined in the [Golang Context Package](https://golang.org/pkg/context). A `Context` -is an immutable data structure that carries request-scoped data across APIs and processes. Contexts -are also designed to enable concurrency and to be used in goroutines. - -Contexts are intended to be **immutable**; they should never be edited. Instead, the convention is -to create a child context from its parent using a `With` function. For example: - -```go -childCtx = parentCtx.WithBlockHeader(header) -``` - -The [Golang Context Package](https://golang.org/pkg/context) documentation instructs developers to -explicitly pass a context `ctx` as the first argument of a process. - -## Store branching - -The `Context` contains a `MultiStore`, which allows for branchinig and caching functionality using `CacheMultiStore` -(queries in `CacheMultiStore` are cached to avoid future round trips). -Each `KVStore` is branched in a safe and isolated ephemeral storage. Processes are free to write changes to -the `CacheMultiStore`. If a state-transition sequence is performed without issue, the store branch can -be committed to the underlying store at the end of the sequence or disregard them if something -goes wrong. The pattern of usage for a Context is as follows: - -1. A process receives a Context `ctx` from its parent process, which provides information needed to - perform the process. -2. The `ctx.ms` is a **branched store**, i.e. a branch of the [multistore](./04-store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution. -3. The process may read and write from `ctx` as it is executing. It may call a subprocess and pass - `ctx` to it as needed. -4. When a subprocess returns, it checks if the result is a success or failure. If a failure, nothing - needs to be done - the branch `ctx` is simply discarded. If successful, the changes made to - the `CacheMultiStore` can be committed to the original `ctx.ms` via `Write()`. - -For example, here is a snippet from the [`runTx`](./00-baseapp.md#runtx-and-runmsgs) function in -[`baseapp`](./00-baseapp.md): - -```go -runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) -result = app.runMsgs(runMsgCtx, msgs, mode) -result.GasWanted = gasWanted - -if mode != runTxModeDeliver { - return result -} - -if result.IsOK() { - msCache.Write() -} -``` - -Here is the process: - -1. Prior to calling `runMsgs` on the message(s) in the transaction, it uses `app.cacheTxContext()` - to branch and cache the context and multistore. -2. `runMsgCtx` - the context with branched store, is used in `runMsgs` to return a result. -3. If the process is running in [`checkTxMode`](./00-baseapp.md#checktx), there is no need to write the - changes - the result is returned immediately. -4. If the process is running in [`deliverTxMode`](./00-baseapp.md#delivertx) and the result indicates - a successful run over all the messages, the branched multistore is written back to the original. - -## Next {hide} - -Learn about the [node client](./03-node.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/03-node.md b/versioned_docs/version-0.45/develop/advanced-concepts/03-node.md deleted file mode 100644 index 1af7d0ea0..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/03-node.md +++ /dev/null @@ -1,72 +0,0 @@ -=# Node Client (Daemon) - -The main endpoint of an SDK application is the daemon client, otherwise known as the full-node client. The full-node runs the state-machine, starting from a genesis file. It connects to peers running the same client in order to receive and relay transactions, block proposals and signatures. The full-node is constituted of the application, defined with the Cosmos SDK, and of a consensus engine connected to the application via the ABCI. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK application](../high-level-concepts/overview-app.md) {prereq} - -## `main` function - -The full-node client of any SDK application is built by running a `main` function. The client is generally named by appending the `-d` suffix to the application name (e.g. `appd` for an application named `app`), and the `main` function is defined in a `./appd/cmd/main.go` file. Running this function creates an executable `appd` that comes with a set of commands. For an app named `app`, the main command is [`appd start`](#start-command), which starts the full-node. - -In general, developers will implement the `main.go` function with the following structure: - -- First, an [`appCodec`](./05-encoding.md) is instantiated for the application. -- Then, the `config` is retrieved and config parameters are set. This mainly involves setting the Bech32 prefixes for [addresses](../high-level-concepts/03-accounts.md#addresses). - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/config.go#L13-L24 -- Using [cobra](https://github.com/spf13/cobra), the root command of the full-node client is created. After that, all the custom commands of the application are added using the `AddCommand()` method of `rootCmd`. -- Add default server commands to `rootCmd` using the `server.AddCommands()` method. These commands are separated from the ones added above since they are standard and defined at SDK level. They should be shared by all SDK-based applications. They include the most important command: the [`start` command](#start-command). -- Prepare and execute the `executor`. - +++ https://github.com/tendermint/tendermint/blob/v0.34.0-rc6/libs/cli/setup.go#L74-L78 - -See an example of `main` function from the `simapp` application, the SDK's application for demo purposes: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/main.go - -## `start` command - -The `start` command is defined in the `/server` folder of the Cosmos SDK. It is added to the root command of the full-node client in the [`main` function](#main-function) and called by the end-user to start their node: - -```bash -# For an example app named "app", the following command starts the full-node. -appd start - -# Using the SDK's own simapp, the following commands start the simapp node. -simd start -``` - -As a reminder, the full-node is composed of three conceptual layers: the networking layer, the consensus layer and the application layer. The first two are generally bundled together in an entity called the consensus engine (Tendermint Core by default), while the third is the state-machine defined with the help of the Cosmos SDK. Currently, the Cosmos SDK uses Tendermint as the default consensus engine, meaning the start command is implemented to boot up a Tendermint node. - -The flow of the `start` command is pretty straightforward. First, it retrieves the `config` from the `context` in order to open the `db` (a [`leveldb`](https://github.com/syndtr/goleveldb) instance by default). This `db` contains the latest known state of the application (empty if the application is started from the first time. - -With the `db`, the `start` command creates a new instance of the application using an `appCreator` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L227-L228 - -Note that an `appCreator` is a function that fulfills the `AppCreator` signature: -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/types/app.go#L48-L50 - -In practice, the [constructor of the application](../high-level-concepts/overview-app.md#constructor-function) is passed as the `appCreator`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/cmd/root.go#L170-L215 - -Then, the instance of `app` is used to instanciate a new Tendermint node: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L235-L244 - -The Tendermint node can be created with `app` because the latter satisfies the [`abci.Application` interface](https://github.com/tendermint/tendermint/blob/v0.34.0/abci/types/application.go#L7-L32) (given that `app` extends [`baseapp`](./00-baseapp.md)). As part of the `NewNode` method, Tendermint makes sure that the height of the application (i.e. number of blocks since genesis) is equal to the height of the Tendermint node. The difference between these two heights should always be negative or null. If it is strictly negative, `NewNode` will replay blocks until the height of the application reaches the height of the Tendermint node. Finally, if the height of the application is `0`, the Tendermint node will call [`InitChain`](./00-baseapp.md#initchain) on the application to initialize the state from the genesis file. - -Once the Tendermint node is instanciated and in sync with the application, the node can be started: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L250-L252 - -Upon starting, the node will bootstrap its RPC and P2P server and start dialing peers. During handshake with its peers, if the node realizes they are ahead, it will query all the blocks sequentially in order to catch up. Then, it will wait for new block proposals and block signatures from validators in order to make progress. - -## Other commands - -To discover how to concretely run a node and interact with it, please refer to our [Running a Node, API and CLI](../run-node/README.md) guide. - -## Next {hide} - -Learn about the [store](./04-store.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/04-store.md b/versioned_docs/version-0.45/develop/advanced-concepts/04-store.md deleted file mode 100644 index b490bd35b..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/04-store.md +++ /dev/null @@ -1,253 +0,0 @@ -# Store - -A store is a data structure that holds the state of the application. {synopsis} - -### Pre-requisite Readings - -- [Anatomy of an SDK application](../high-level-concepts/overview-app.md) {prereq} - -## Introduction to SDK Stores - -The Cosmos SDK comes with a large set of stores to persist the state of applications. By default, the main store of SDK applications is a `multistore`, i.e. a store of stores. Developers can add any number of key-value stores to the multistore, depending on their application needs. The multistore exists to support the modularity of the Cosmos SDK, as it lets each module declare and manage their own subset of the state. Key-value stores in the multistore can only be accessed with a specific capability `key`, which is typically held in the [`keeper`](../building-modules/06-keeper.md) of the module that declared the store. - -``` -+-----------------------------------------------------+ -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 1 - Manage by keeper of Module 1 | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 2 - Manage by keeper of Module 2 | | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 3 - Manage by keeper of Module 2 | | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 4 - Manage by keeper of Module 3 | | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 5 - Manage by keeper of Module 4 | | -| | | | -| +--------------------------------------------+ | -| | -| Main Multistore | -| | -+-----------------------------------------------------+ - - Application's State -``` - -### Store Interface - -At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and has a `GetStoreType()` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L15-L18 - -The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that implements store read caching and write branching through `Write` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L240-L264 - -Branching and cache is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A storage branch creates an isolated, ephemeral branch of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. Read more about it in [context](./02-context.md#Store-branching) - -### Commit Store - -A commit store is a store that has the ability to commit changes made to the underlying tree or db. The Cosmos SDK differentiates simple stores from commit stores by extending the basic store interfaces with a `Committer`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L29-L33 - -The `Committer` is an interface that defines methods to persist changes to disk: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L20-L27 - -The `CommitID` is a deterministic commit of the state tree. Its hash is returned to the underlying consensus engine and stored in the block header. Note that commit store interfaces exist for various purposes, one of which is to make sure not every object can commit the store. As part of the [object-capabilities model](./09-ocap.md) of the Cosmos SDK, only `baseapp` should have the ability to commit stores. For example, this is the reason why the `ctx.KVStore()` method by which modules typically access stores returns a `KVStore` and not a `CommitKVStore`. - -The Cosmos SDK comes with many types of stores, the most used being [`CommitMultiStore`](#multistore), [`KVStore`](#kvstore) and [`GasKv` store](#gaskv-store). [Other types of stores](#other-stores) include `Transient` and `TraceKV` stores. - -## Multistore - -### Multistore Interface - -Each Cosmos SDK application holds a multistore at its root to persist its state. The multistore is a store of `KVStores` that follows the `Multistore` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L104-L133 - -If tracing is enabled, then branching the multistore will firstly wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store). - -### CommitMultiStore - -The main type of `Multistore` used in the Cosmos SDK is `CommitMultiStore`, which is an extension of the `Multistore` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L141-L184 - -As for concrete implementation, the [`rootMulti.Store`] is the go-to implementation of the `CommitMultiStore` interface. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/rootmulti/store.go#L43-L61 - -The `rootMulti.Store` is a base-layer multistore built around a `db` on top of which multiple `KVStores` can be mounted, and is the default multistore store used in [`baseapp`](./00-baseapp.md). - -### CacheMultiStore - -Whenever the `rootMulti.Store` needs to be branched, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/store/cachemulti/store.go) is used. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/cachemulti/store.go#L17-L28 - -`cachemulti.Store` branches all substores (creates a virtual store for each substore) in its constructor and hold them in `Store.stores`. Moreover caches all read queries. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores. - -## Base-layer KVStores - -### `KVStore` and `CommitKVStore` Interfaces - -A `KVStore` is a simple key-value store used to store and retrieve data. A `CommitKVStore` is a `KVStore` that also implements a `Committer`. By default, stores mounted in `baseapp`'s main `CommitMultiStore` are `CommitKVStore`s. The `KVStore` interface is primarily used to restrict modules from accessing the committer. - -Individual `KVStore`s are used by modules to manage a subset of the global state. `KVStores` can be accessed by objects that hold a specific key. This `key` should only be exposed to the [`keeper`](../building-modules/06-keeper.md) of the module that defines the store. - -`CommitKVStore`s are declared by proxy of their respective `key` and mounted on the application's [multistore](#multistore) in the [main application file](../high-level-concepts/overview-app.md#core-application-file). In the same file, the `key` is also passed to the module's `keeper` that is responsible for managing the store. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L189-L219 - -Apart from the traditional `Get` and `Set` methods, a `KVStore` must provide an `Iterator(start, end)` method which returns an `Iterator` object. It is used to iterate over a range of keys, typically keys that share a common prefix. Below is an example from the bank's module keeper, used to iterate over all account balances: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/keeper/view.go#L115-L134 - -### `IAVL` Store - -The default implementation of `KVStore` and `CommitKVStore` used in `baseapp` is the `iavl.Store`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/iavl/store.go#L37-L40 - -`iavl` stores are based around an [IAVL Tree](https://github.com/tendermint/iavl), a self-balancing binary tree which guarantees that: - -- `Get` and `Set` operations are O(log n), where n is the number of elements in the tree. -- Iteration efficiently returns the sorted elements within the range. -- Each tree version is immutable and can be retrieved even after a commit (depending on the pruning settings). - -The documentation on the IAVL Tree is located [here](https://github.com/cosmos/iavl/blob/v0.15.0-rc5/docs/overview.md). - -### `DbAdapter` Store - -`dbadapter.Store` is a adapter for `dbm.DB` making it fulfilling the `KVStore` interface. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/dbadapter/store.go#L13-L16 - -`dbadapter.Store` embeds `dbm.DB`, meaning most of the `KVStore` interface functions are implemented. The other functions (mostly miscellaneous) are manually implemented. This store is primarily used within [Transient Stores](#transient-stores) - -### `Transient` Store - -`Transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/transient/store.go#L13-L16 - -`Transient.Store` is a `dbadapter.Store` with a `dbm.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, a new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected. - -This type of store is useful to persist information that is only relevant per-block. One example would be to store parameter changes (i.e. a bool set to `true` if a parameter changed in a block). - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/params/types/subspace.go#L20-L30 - -Transient stores are typically accessed via the [`context`](./02-context.md) via the `TransientStore()` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/context.go#L232-L235 - -## KVStore Wrappers - -### CacheKVStore - -`cachekv.Store` is a wrapper `KVStore` which provides buffered writing / cached reading functionalities over the underlying `KVStore`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/cachekv/store.go#L27-L34 - -This is the type used whenever an IAVL Store needs to be branched to create an isolated store (typically when we need to mutate a state that might be reverted later). - -#### `Get` - -`Store.Get()` firstly checks if `Store.cache` has an associated value with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, caches the result in `Store.cache`, and returns it. - -#### `Set` - -`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field dirty bool which indicates whether the cached value is different from the underlying value. When `Store.Set()` caches a new pair, the `cValue.dirty` is set `true` so when `Store.Write()` is called it can be written to the underlying store. - -#### `Iterator` - -`Store.Iterator()` have to traverse on both cached items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPairs`, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. - -### `GasKv` Store - -Cosmos SDK applications use [`gas`](../high-level-concepts/gas-fees.md) to track resources usage and prevent spam. [`GasKv.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/gaskv/store.go) is a `KVStore` wrapper that enables automatic gas consumption each time a read or write to the store is made. It is the solution of choice to track storage usage in Cosmos SDK applications. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/gaskv/store.go#L13-L19 - -When methods of the parent `KVStore` are called, `GasKv.Store` automatically consumes appropriate amount of gas depending on the `Store.gasConfig`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/gas.go#L153-L162 - -By default, all `KVStores` are wrapped in `GasKv.Stores` when retrieved. This is done in the `KVStore()` method of the [`context`](./02-context.md): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/context.go#L227-L230 - -In this case, the default gas configuration is used: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/gas.go#L164-L175 - -### `TraceKv` Store - -`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. It is applied automatically by the Cosmos SDK on all `KVStore` if tracing is enabled on the parent `MultiStore`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/tracekv/store.go#L20-L43 - -When each `KVStore` methods are called, `tracekv.Store` automatically logs `traceOperation` to the `Store.writer`. `traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`. - -### `Prefix` Store - -`prefix.Store` is a wrapper `KVStore` which provides automatic key-prefixing functionalities over the underlying `KVStore`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/prefix/store.go#L15-L21 - -When `Store.{Get, Set}()` is called, the store forwards the call to its parent, with the key prefixed with the `Store.prefix`. - -When `Store.Iterator()` is called, it does not simply prefix the `Store.prefix`, since it does not work as intended. In that case, some of the elements are traversed even they are not starting with the prefix. - -### `ListenKv` Store - -`listenkv.Store` is a wrapper `KVStore` which provides state listening capabilities over the underlying `KVStore`. -It is applied automatically by the Cosmos SDK on any `KVStore` whose `StoreKey` is specified during state streaming configuration. -Additional information about state streaming configuration can be found in the [store/streaming/README.md](../../store/streaming/README.md). - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.44.1/store/listenkv/store.go#L11-L18 - -When `KVStore.Set` or `KVStore.Delete` methods are called, `listenkv.Store` automatically writes the operations to the set of `Store.listeners`. - -## New Store package (`store/v2`) - -The SDK is in the process of transitioning to use the types listed here as the default interface for state storage. At the time of writing, these cannot be used within an application and are not directly compatible with the `CommitMultiStore` and related types. - -### `BasicKVStore` interface - -An interface providing only the basic CRUD functionality (`Get`, `Set`, `Has`, and `Delete` methods), without iteration or caching. This is used to partially expose components of a larger store, such as a `flat.Store`. - -### Flat Store - -`flat.Store` is the new default persistent store, which internally decouples the concerns of state storage and commitment scheme. Values are stored directly in the backing key-value database (the "storage" bucket), while the value's hash is mapped in a separate store which is able to generate a cryptographic commitment (the "state commitment" bucket, implmented with `smt.Store`). - -This can optionally be constructed to use different backend databases for each bucket. - - - -### SMT Store - -A `BasicKVStore` which is used to partially expose functions of an underlying store (for instance, to allow access to the commitment store in `flat.Store`). - -## Next {hide} - -Learn about [encoding](./05-encoding.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/05-encoding.md b/versioned_docs/version-0.45/develop/advanced-concepts/05-encoding.md deleted file mode 100644 index 8f258b37f..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/05-encoding.md +++ /dev/null @@ -1,257 +0,0 @@ -# Encoding - -While encoding in the SDK used to be mainly handled by `go-amino` codec, the SDK is moving towards using `gogoprotobuf` for both state and client-side encoding. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK application](../high-level-concepts/overview-app.md) {prereq} - -## Encoding - -The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) which is an object encoding specification and [Protocol Buffers](https://developers.google.com/protocol-buffers), a subset of Proto3 with an extension for -interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) -for more information on Proto3, which Amino is largely compatible with (but not with Proto2). - -Due to Amino having significant performance drawbacks, being reflection-based, and -not having any meaningful cross-language/client support, Protocol Buffers, specifically -[gogoprotobuf](https://github.com/gogo/protobuf/), is being used in place of Amino. -Note, this process of using Protocol Buffers over Amino is still an ongoing process. - -Binary wire encoding of types in the Cosmos SDK can be broken down into two main -categories, client encoding and store encoding. Client encoding mainly revolves -around transaction processing and signing, whereas store encoding revolves around -types used in state-machine transitions and what is ultimately stored in the Merkle -tree. - -For store encoding, protobuf definitions can exist for any type and will typically -have an Amino-based "intermediary" type. Specifically, the protobuf-based type -definition is used for serialization and persistence, whereas the Amino-based type -is used for business logic in the state-machine where they may converted back-n-forth. -Note, the Amino-based types may slowly be phased-out in the future so developers -should take note to use the protobuf message definitions where possible. - -In the `codec` package, there exists two core interfaces, `Marshaler` and `ProtoMarshaler`, -where the former encapsulates the current Amino interface except it operates on -types implementing the latter instead of generic `interface{}` types. - -In addition, there exists two implementations of `Marshaler`. The first being -`AminoCodec`, where both binary and JSON serialization is handled via Amino. The -second being `ProtoCodec`, where both binary and JSON serialization is handled -via Protobuf. - -This means that modules may use Amino or Protobuf encoding but the types must -implement `ProtoMarshaler`. If modules wish to avoid implementing this interface -for their types, they may use an Amino codec directly. - -### Amino - -Every module uses an Amino codec to serialize types and interfaces. This codec typically -has types and interfaces registered in that module's domain only (e.g. messages), -but there are exceptions like `x/gov`. Each module exposes a `RegisterLegacyAminoCodec` function -that allows a user to provide a codec and have all the types registered. An application -will call this method for each necessary module. - -Where there is no protobuf-based type definition for a module (see below), Amino -is used to encode and decode raw wire bytes to the concrete type or interface: - -```go -bz := keeper.cdc.MustMarshal(typeOrInterface) -keeper.cdc.MustUnmarshal(bz, &typeOrInterface) -``` - -Note, there are length-prefixed variants of the above functionality and this is -typically used for when the data needs to be streamed or grouped together -(e.g. `ResponseDeliverTx.Data`) - -### Gogoproto - -Modules are encouraged to utilize Protobuf encoding for their respective types. In the SDK, we use the [Gogoproto](https://github.com/gogo/protobuf) specific implementation of the Protobuf spec that offers speed and DX improvements compared to the official [Google protobuf implementation](https://github.com/protocolbuffers/protobuf). - -### Guidelines for protobuf message definitions - -In addition to [following official Protocol Buffer guidelines](https://developers.google.com/protocol-buffers/docs/proto3#simple), we recommend using these annotations in .proto files when dealing with interfaces: - -- use `cosmos_proto.accepts_interface` to annote fields that accept interfaces -- pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` -- annotate interface implementations with `cosmos_proto.implements_interface` -- pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` - -### Transaction Encoding - -Another important use of Protobuf is the encoding and decoding of -[transactions](./01-transactions.md). Transactions are defined by the application or -the SDK but are then passed to the underlying consensus engine to be relayed to -other peers. Since the underlying consensus engine is agnostic to the application, -the consensus engine accepts only transactions in the form of raw bytes. - -- The `TxEncoder` object performs the encoding. -- The `TxDecoder` object performs the decoding. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/types/tx_msg.go#L83-L87 - -A standard implementation of both these objects can be found in the [`auth` module](../../x/auth/spec/README.md): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/decoder.go - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/encoder.go - -See [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) for details of how a transaction is encoded. - -### Interface Encoding and Usage of `Any` - -The Protobuf DSL is strongly typed, which can make inserting variable-typed fields difficult. Imagine we want to create a `Profile` protobuf message that serves as a wrapper over [an account](../high-level-concepts/03-accounts.md): - -```proto -message Profile { - // account is the account associated to a profile. - cosmos.auth.v1beta1.BaseAccount account = 1; - // bio is a short description of the account. - string bio = 4; -} -``` - -In this `Profile` example, we hardcoded `account` as a `BaseAccount`. However, there are several other types of [user accounts related to vesting](../../x/auth/spec/05_vesting.md), such as `BaseVestingAccount` or `ContinuousVestingAccount`. All of these accounts are different, but they all implement the `AccountI` interface. How would you create a `Profile` that allows all these types of accounts with an `account` field that accepts an `AccountI` interface? - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/types/account.go#L307-L330 - -In [ADR-019](../architecture/adr-019-protobuf-state-encoding.md), it has been decided to use [`Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto)s to encode interfaces in protobuf. An `Any` contains an arbitrary serialized message as bytes, along with a URL that acts as a globally unique identifier for and resolves to that message's type. This strategy allows us to pack arbitrary Go types inside protobuf messages. Our new `Profile` then looks like: - -```protobuf -message Profile { - // account is the account associated to a profile. - google.protobuf.Any account = 1 [ - (cosmos_proto.accepts_interface) = "AccountI"; // Asserts that this field only accepts Go types implementing `AccountI`. It is purely informational for now. - ]; - // bio is a short description of the account. - string bio = 4; -} -``` - -To add an account inside a profile, we need to "pack" it inside an `Any` first, using `codectypes.NewAnyWithValue`: - -```go -var myAccount AccountI -myAccount = ... // Can be a BaseAccount, a ContinuousVestingAccount or any struct implementing `AccountI` - -// Pack the account into an Any -accAny, err := codectypes.NewAnyWithValue(myAccount) -if err != nil { - return nil, err -} - -// Create a new Profile with the any. -profile := Profile { - Account: accAny, - Bio: "some bio", -} - -// We can then marshal the profile as usual. -bz, err := cdc.Marshal(profile) -jsonBz, err := cdc.MarshalJSON(profile) -``` - -To summarize, to encode an interface, you must 1/ pack the interface into an `Any` and 2/ marshal the `Any`. For convenience, the SDK provides a `MarshalInterface` method to bundle these two steps. Have a look at [a real-life example in the x/auth module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/keeper/keeper.go#L218-L221). - -The reverse operation of retrieving the concrete Go type from inside an `Any`, called "unpacking", is done with the `GetCachedValue()` on `Any`. - -```go -profileBz := ... // The proto-encoded bytes of a Profile, e.g. retrieved through gRPC. -var myProfile Profile -// Unmarshal the bytes into the myProfile struct. -err := cdc.Unmarshal(profilebz, &myProfile) - -// Let's see the types of the Account field. -fmt.Printf("%T\n", myProfile.Account) // Prints "Any" -fmt.Printf("%T\n", myProfile.Account.GetCachedValue()) // Prints "BaseAccount", "ContinuousVestingAccount" or whatever was initially packed in the Any. - -// Get the address of the accountt. -accAddr := myProfile.Account.GetCachedValue().(AccountI).GetAddress() -``` - -It is important to note that for `GetCachedValue()` to work, `Profile` (and any other structs embedding `Profile`) must implement the `UnpackInterfaces` method: - -```go -func (p *Profile) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - if p.Account != nil { - var account AccountI - return unpacker.UnpackAny(p.Account, &account) - } - - return nil -} -``` - -The `UnpackInterfaces` gets called recursively on all structs implementing this method, to allow all `Any`s to have their `GetCachedValue()` correctly populated. - -For more information about interface encoding, and especially on `UnpackInterfaces` and how the `Any`'s `type_url` gets resolved using the `InterfaceRegistry`, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md). - -#### `Any` Encoding in the SDK - -The above `Profile` example is a fictive example used for educational purposes. In the SDK, we use `Any` encoding in several places (non-exhaustive list): - -- the `cryptotypes.PubKey` interface for encoding different types of public keys, -- the `sdk.Msg` interface for encoding different `Msg`s in a transaction, -- the `AccountI` interface for encodinig different types of accounts (similar to the above example) in the x/auth query responses, -- the `Evidencei` interface for encoding different types of evidences in the x/evidence module, -- the `AuthorizationI` interface for encoding different types of x/authz authorizations, -- the [`Validator`](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/x/staking/types/staking.pb.go#L306-L337) struct that contains information about a validator. - -A real-life example of encoding the pubkey as `Any` inside the Validator struct in x/staking is shown in the following example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/types/validator.go#L40-L61 - -## FAQ - -1. How to create modules using protobuf encoding? - -**Defining module types** - -Protobuf types can be defined to encode: - -- state -- [`Msg`s](../building-modules/02-messages-and-queries.md#messages) -- [Query services](../building-modules/04-query-services.md) -- [genesis](../building-modules/08-genesis.md) - -**Naming and conventions** - -We encourage developers to follow industry guidelines: [Protocol Buffers style guide](https://developers.google.com/protocol-buffers/docs/style) -and [Buf](https://buf.build/docs/style-guide), see more details in [ADR 023](../architecture/adr-023-protobuf-naming.md) - -2. How to update modules to protobuf encoding? - -If modules do not contain any interfaces (e.g. `Account` or `Content`), then they -may simply migrate any existing types that -are encoded and persisted via their concrete Amino codec to Protobuf (see 1. for further guidelines) and accept a `Marshaler` as the codec which is implemented via the `ProtoCodec` -without any further customization. - -However, if a module type composes an interface, it must wrap it in the `skd.Any` (from `/types` package) type. To do that, a module-level .proto file must use [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto) for respective message type interface types. - -For example, in the `x/evidence` module defines an `Evidence` interface, which is used by the `MsgSubmitEvidence`. The structure definition must use `sdk.Any` to wrap the evidence file. In the proto file we define it as follows: - -```protobuf -// proto/cosmos/evidence/v1beta1/tx.proto - -message MsgSubmitEvidence { - string submitter = 1; - google.protobuf.Any evidence = 2 [(cosmos_proto.accepts_interface) = "Evidence"]; -} -``` - -The SDK `codec.Codec` interface provides support methods `MarshalInterface` and `UnmarshalInterface` to easy encoding of state to `Any`. - -Module should register interfaces using `InterfaceRegistry` which provides a mechanism for registering interfaces: `RegisterInterface(protoName string, iface interface{})` and implementations: `RegisterImplementations(iface interface{}, impls ...proto.Message)` that can be safely unpacked from Any, similarly to type registration with Amino: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/codec/types/interface_registry.go#L25-L66 - -In addition, an `UnpackInterfaces` phase should be introduced to deserialization to unpack interfaces before they're needed. Protobuf types that contain a protobuf `Any` either directly or via one of their members should implement the `UnpackInterfacesMessage` interface: - -```go -type UnpackInterfacesMessage interface { - UnpackInterfaces(InterfaceUnpacker) error -} -``` - -## Next {hide} - -Learn about [gRPC, REST and other endpoints](./08-grpc_rest.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/06-cli.md b/versioned_docs/version-0.45/develop/advanced-concepts/06-cli.md deleted file mode 100644 index c1eda3484..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/06-cli.md +++ /dev/null @@ -1,152 +0,0 @@ -# Command-Line Interface - -This document describes how commmand-line interface (CLI) works on a high-level, for an [**application**](../high-level-concepts/overview-app.md). A separate document for implementing a CLI for an SDK [**module**](../building-modules/intro.md) can be found [here](../building-modules/09-module-interfaces.md#cli). {synopsis} - -## Command-Line Interface - -### Example Command - -There is no set way to create a CLI, but SDK modules typically use the [Cobra Library](https://github.com/spf13/cobra). Building a CLI with Cobra entails defining commands, arguments, and flags. [**Commands**](#commands) understand the actions users wish to take, such as `tx` for creating a transaction and `query` for querying the application. Each command can also have nested subcommands, necessary for naming the specific transaction type. Users also supply **Arguments**, such as account numbers to send coins to, and [**Flags**](#flags) to modify various aspects of the commands, such as gas prices or which node to broadcast to. - -Here is an example of a command a user might enter to interact with the simapp CLI `simd` in order to send some tokens: - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --gas auto --gas-prices -``` - -The first four strings specify the command: - -- The root command for the entire application `simd`. -- The subcommand `tx`, which contains all commands that let users create transactions. -- The subcommand `bank` to indicate which module to route the command to ([`x/bank`](../../x/bank/spec/README.md) module in this case). -- The type of transaction `send`. - -The next two strings are arguments: the `from_address` the user wishes to send from, the `to_address` of the recipient, and the `amount` they want to send. Finally, the last few strings of the command are optional flags to indicate how much the user is willing to pay in fees (calculated using the amount of gas used to execute the transaction and the gas prices provided by the user). - -The CLI interacts with a [node](../03-node.md) to handle this command. The interface itself is defined in a `main.go` file. - -### Building the CLI - -The `main.go` file needs to have a `main()` function that creates a root command, to which all the application commands will be added as subcommands. The root command additionally handles: - -- **setting configurations** by reading in configuration files (e.g. the sdk config file). -- **adding any flags** to it, such as `--chain-id`. -- **instantiating the `codec`** by calling the application's `MakeCodec()` function (called `MakeTestEncodingConfig` in `simapp`). The [`codec`](../05-encoding.md) is used to encode and decode data structures for the application - stores can only persist `[]byte`s so the developer must define a serialization format for their data structures or use the default, Protobuf. -- **adding subcommand** for all the possible user interactions, including [transaction commands](#transaction-commands) and [query commands](#query-commands). - -The `main()` function finally creates an executor and [execute](https://godoc.org/github.com/spf13/cobra#Command.Execute) the root command. See an example of `main()` function from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/main.go#L12-L24 - -The rest of the document will detail what needs to be implemented for each step and include smaller portions of code from the `simapp` CLI files. - -## Adding Commands to the CLI - -Every application CLI first constructs a root command, then adds functionality by aggregating subcommands (often with further nested subcommands) using `rootCmd.AddCommand()`. The bulk of an application's unique capabilities lies in its transaction and query commands, called `TxCmd` and `QueryCmd` respectively. - -### Root Command - -The root command (called `rootCmd`) is what the user first types into the command line to indicate which application they wish to interact with. The string used to invoke the command (the "Use" field) is typically the name of the application suffixed with `-d`, e.g. `simd` or `gaiad`. The root command typically includes the following commands to support basic functionality in the application. - -- **Status** command from the SDK rpc client tools, which prints information about the status of the connected [`Node`](../03-node.md). The Status of a node includes `NodeInfo`,`SyncInfo` and `ValidatorInfo`. -- **Keys** [commands](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/keys) from the SDK client tools, which includes a collection of subcommands for using the key functions in the SDK crypto tools, including adding a new key and saving it to the keyring, listing all public keys stored in the keyring, and deleting a key. For example, users can type `simd keys add ` to add a new key and save an encrypted copy to the keyring, using the flag `--recover` to recover a private key from a seed phrase or the flag `--multisig` to group multiple keys together to create a multisig key. For full details on the `add` key command, see the code [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/keys/add.go). For more details about usage of `--keyring-backend` for storage of key credentials look at the [keyring docs](../run-node/keyring.md). -- **Server** commands from the SDK server package. These commands are responsible for providing the mechanisms necessary to start an ABCI Tendermint application and provides the CLI framework (based on [cobra](github.com/spf13/cobra)) necessary to fully bootstrap an application. The package exposes two core functions: `StartCmd` and `ExportCmd` which creates commands to start the application and export state respectively. Click [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/server) to learn more. -- [**Transaction**](#transaction-commands) commands. -- [**Query**](#query-commands) commands. - -Next is an example `rootCmd` function from the `simapp` application. It instantiates the root command, adds a [_persistent_ flag](#flags) and `PreRun` function to be run before every execution, and adds all of the necessary subcommands. - -+++ https://github.com/cosmos/cosmos-sdk/blob/4eea4cafd3b8b1c2cd493886db524500c9dd745c/simapp/simd/cmd/root.go#L37-L150 - -`rootCmd` has a function called `initAppConfig()` which is useful for setting the application's custom configs. -By default app uses Tendermint app config template from SDK, which can be over-written via `initAppConfig()`. -Here's an example code to override default `app.toml` template. - -+++ https://github.com/cosmos/cosmos-sdk/blob/4eea4cafd3b8b1c2cd493886db524500c9dd745c/simapp/simd/cmd/root.go#L84-L117 - -The `initAppConfig()` also allows overriding the default SDK's [server config](https://github.com/cosmos/cosmos-sdk/blob/4eea4cafd3b8b1c2cd493886db524500c9dd745c/server/config/config.go#L199). One example is the `min-gas-prices` config, which defines the minimum gas prices a validator is willing to accept for processing a transaction. By default, the SDK sets this parameter to `""` (empty string), which forces all validators to tweak their own `app.toml` and set a non-empty value, or else the node will halt on startup. This might not be the best UX for validators, so the chain developer can set a default `app.toml` value for validators inside this `initAppConfig()` function. - -+++ https://github.com/cosmos/cosmos-sdk/blob/aa9b055ddb46aacd4737335a92d0b8a82d577341/simapp/simd/cmd/root.go#L101-L116 - -The root-level `status` and `keys` subcommands are common across most applications and do not interact with application state. The bulk of an application's functionality - what users can actually _do_ with it - is enabled by its `tx` and `query` commands. - -### Transaction Commands - -[Transactions](./01-transactions.md) are objects wrapping [`Msg`s](../building-modules/02-messages-and-queries.md#messages) that trigger state changes. To enable the creation of transactions using the CLI interface, a function `txCmd` is generally added to the `rootCmd`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L86-L92 - -This `txCmd` function adds all the transaction available to end-users for the application. This typically includes: - -- **Sign command** from the [`auth`](../../x/auth/spec/README.md) module that signs messages in a transaction. To enable multisig, add the `auth` module's `MultiSign` command. Since every transaction requires some sort of signature in order to be valid, the signing command is necessary for every application. -- **Broadcast command** from the SDK client tools, to broadcast transactions. -- **All [module transaction commands](../building-modules/09-module-interfaces.md#transaction-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddTxCommands()` function. - -Here is an example of a `txCmd` aggregating these subcommands from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L123-L149 - -### Query Commands - -[**Queries**](../building-modules/02-messages-and-queries.md#queries) are objects that allow users to retrieve information about the application's state. To enable the creation of transactions using the CLI interface, a function `txCmd` is generally added to the `rootCmd`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L86-L92 - -This `queryCmd` function adds all the queries available to end-users for the application. This typically includes: - -- **QueryTx** and/or other transaction query commands] from the `auth` module which allow the user to search for a transaction by inputting its hash, a list of tags, or a block height. These queries allow users to see if transactions have been included in a block. -- **Account command** from the `auth` module, which displays the state (e.g. account balance) of an account given an address. -- **Validator command** from the SDK rpc client tools, which displays the validator set of a given height. -- **Block command** from the SDK rpc client tools, which displays the block data for a given height. -- **All [module query commands](../building-modules/09-module-interfaces.md#query-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddQueryCommands()` function. - -Here is an example of a `queryCmd` aggregating subcommands from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L99-L121 - -## Flags - -Flags are used to modify commands; developers can include them in a `flags.go` file with their CLI. Users can explicitly include them in commands or pre-configure them by inside their [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). Commonly pre-configured flags include the `--node` to connect to and `--chain-id` of the blockchain the user wishes to interact with. - -A _persistent_ flag (as opposed to a _local_ flag) added to a command transcends all of its children: subcommands will inherit the configured values for these flags. Additionally, all flags have default values when they are added to commands; some toggle an option off but others are empty values that the user needs to override to create valid commands. A flag can be explicitly marked as _required_ so that an error is automatically thrown if the user does not provide a value, but it is also acceptable to handle unexpected missing flags differently. - -Flags are added to commands directly (generally in the [module's CLI file](../building-modules/09-module-interfaces.md#flags) where module commands are defined) and no flag except for the `rootCmd` persistent flags has to be added at application level. It is common to add a _persistent_ flag for `--chain-id`, the unique identifier of the blockchain the application pertains to, to the root command. Adding this flag can be done in the `main()` function. Adding this flag makes sense as the chain ID should not be changing across commands in this application CLI. Here is an example from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L118-L119 - -## Environment variables - -Each flag is bound to it's respecteve named environment variable. Then name of the environment variable consist of two parts - capital case `basename` followed by flag name of the flag. `-` must be substituted with `_`. For example flag `--home` for application with basename `GAIA` is bound to `GAIA_HOME`. It allows to reduce amount of flags typed for routine operations. For example instead of: - -```sh -gaia --home=./ --node= --chain-id="testchain-1" --keyring-backend=test tx ... --from= -``` - -this will be more convinient: - -```sh -# define env variables in .env, .envrc etc -GAIA_HOME= -GAIA_NODE= -GAIA_CHAIN_ID="testchain-1" -GAIA_KEYRING_BACKEND="test" - -# and later just use -gaia tx ... --from= -``` - -## Configurations - -It is vital that the root command of an application uses `PersistentPreRun()` cobra command property for executing the command, so all child commands have access to the server and client contexts. These contexts are set as their default values initially and maybe modified, scoped to the command, in their respective `PersistentPreRun()` functions. Note that the `client.Context` is typically pre-populated with "default" values that may be useful for all commands to inherit and override if necessary. - -Here is an example of an `PersistentPreRun()` function from `simapp``: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L54-L60 - -The `SetCmdClientContextHandler` call reads persistent flags via `ReadPersistentCommandFlags` which creates a `client.Context` and sets that on the root command's `Context`. - -The `InterceptConfigsPreRunHandler` call creates a viper literal, default `server.Context`, and a logger and sets that on the root command's `Context`. The `server.Context` will be modified and saved to disk via the internal `interceptConfigs` call, which either reads or creates a Tendermint configuration based on the home path provided. In addition, `interceptConfigs` also reads and loads the application configuration, `app.toml`, and binds that to the `server.Context` viper literal. This is vital so the application can get access to not only the CLI flags, but also to the application configuration values provided by this file. - -## Next {hide} - -Learn about [events](./07-events.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/07-events.md b/versioned_docs/version-0.45/develop/advanced-concepts/07-events.md deleted file mode 100644 index 1ab265b91..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/07-events.md +++ /dev/null @@ -1,133 +0,0 @@ -# Events - -`Event`s are objects that contain information about the execution of the application. They are mainly used by service providers like block explorers and wallet to track the execution of various messages and index transactions. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK application](../high-level-concepts/overview-app.md) {prereq} -- [Tendermint Documentation on Events](https://docs.tendermint.com/master/spec/abci/abci.html#events) {prereq} - -## Events - -Events are implemented in the Cosmos SDK as an alias of the ABCI `Event` type and -take the form of: `{eventType}.{attributeKey}={attributeValue}`. - -+++ https://github.com/tendermint/tendermint/blob/v0.34.8/proto/tendermint/abci/types.proto#L304-L313 - -An Event contains: - -- A `type` to categorize the Event at a high-level; for example, the SDK uses the `"message"` type to filter Events by `Msg`s. -- A list of `attributes` are key-value pairs that give more information about the categorized Event. For example, for the `"message"` type, we can filter Events by key-value pairs using `message.action={some_action}`, `message.module={some_module}` or `message.sender={some_sender}`. - -::: tip -To parse the attribute values as strings, make sure to add `'` (single quotes) around each attribute value. -::: - -Events, the `type` and `attributes` are defined on a **per-module basis** in the module's -`/types/events.go` file, and triggered from the module's Protobuf [`Msg` service](../building-modules/03-msg-services.md) -by using the [`EventManager`](#eventmanager). In addition, each module documents its Events under -`spec/xx_events.md`. - -Events are returned to the underlying consensus engine in the response of the following ABCI messages: - -- [`BeginBlock`](./00-baseapp.md#beginblock) -- [`EndBlock`](./00-baseapp.md#endblock) -- [`CheckTx`](./00-baseapp.md#checktx) -- [`DeliverTx`](./00-baseapp.md#delivertx) - -### Examples - -The following examples show how to query Events using the SDK. - -| Event | Description | -| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `tx.height=23` | Query all transactions at height 23 | -| `message.action='/cosmos.bank.v1beta1.Msg/Send'` | Query all transactions containing a x/bank `Send` [Service `Msg`](../building-modules/03-msg-services.md). Note the `'`s around the value. | -| `message.action='send'` | Query all transactions containing a x/bank `Send` [legacy `Msg`](../building-modules/03-msg-services.md#legacy-amino-msgs). Note the `'`s around the value. | -| `message.module='bank'` | Query all transactions containing messages from the x/bank module. Note the `'`s around the value. | -| `create_validator.validator='cosmosval1...'` | x/staking-specific Event, see [x/staking SPEC](../../../cosmos-sdk/x/staking/spec/07_events.md). | - -## EventManager - -In Cosmos SDK applications, Events are managed by an abstraction called the `EventManager`. -Internally, the `EventManager` tracks a list of Events for the entire execution flow of a -transaction or `BeginBlock`/`EndBlock`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/types/events.go#L17-L25 - -The `EventManager` comes with a set of useful methods to manage Events. The method -that is used most by module and application developers is `EmitEvent` that tracks -an Event in the `EventManager`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/types/events.go#L33-L37 - -Module developers should handle Event emission via the `EventManager#EmitEvent` in each message -`Handler` and in each `BeginBlock`/`EndBlock` handler. The `EventManager` is accessed via -the [`Context`](./02-context.md), where Event emission generally follows this pattern: - -```go -ctx.EventManager().EmitEvent( - sdk.NewEvent(eventType, sdk.NewAttribute(attributeKey, attributeValue)), -) -``` - -Module's `handler` function should also set a new `EventManager` to the `context` to isolate emitted Events per `message`: - -```go -func NewHandler(keeper Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - switch msg := msg.(type) { -``` - -See the [`Msg` services](../building-modules/03-msg-services.md) concept doc for a more detailed -view on how to typically implement Events and use the `EventManager` in modules. - -## Subscribing to Events - -You can use Tendermint's [Websocket](https://docs.tendermint.com/master/tendermint-core/subscription.html#subscribing-to-events-via-websocket) to subscribe to Events by calling the `subscribe` RPC method: - -```json -{ - "jsonrpc": "2.0", - "method": "subscribe", - "id": "0", - "params": { - "query": "tm.event='eventCategory' AND eventType.eventAttribute='attributeValue'" - } -} -``` - -The main `eventCategory` you can subscribe to are: - -- `NewBlock`: Contains Events triggered during `BeginBlock` and `EndBlock`. -- `Tx`: Contains Events triggered during `DeliverTx` (i.e. transaction processing). -- `ValidatorSetUpdates`: Contains validator set updates for the block. - -These Events are triggered from the `state` package after a block is committed. You can get the -full list of Event categories [on the Tendermint Godoc page](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants). - -The `type` and `attribute` value of the `query` allow you to filter the specific Event you are looking for. For example, a `transfer` transaction triggers an Event of type `Transfer` and has `Recipient` and `Sender` as `attributes` (as defined in the [`events.go` file of the `bank` module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/bank/types/events.go)). Subscribing to this Event would be done like so: - -```json -{ - "jsonrpc": "2.0", - "method": "subscribe", - "id": "0", - "params": { - "query": "tm.event='Tx' AND transfer.sender='senderAddress'" - } -} -``` - -where `senderAddress` is an address following the [`AccAddress`](../high-level-concepts/03-accounts.md#addresses) format. - -## Typed Events (coming soon) - -As previously described, Events are defined on a per-module basis. It is the responsibility of the module developer to define Event types and Event attributes. Except in the `spec/XX_events.md` file, these Event types and attributes are unfortunately not easily discoverable, so the SDK proposes to use Protobuf-defined [Typed Events](../architecture/adr-032-typed-events.md) for emitting and querying Events. - -The Typed Events proposal has not yet been fully implemented. Documentation is not yet available. - -## Next {hide} - -Learn about SDK [telemetry](./11-telemetry.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/08-grpc_rest.md b/versioned_docs/version-0.45/develop/advanced-concepts/08-grpc_rest.md deleted file mode 100644 index f7f581205..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/08-grpc_rest.md +++ /dev/null @@ -1,112 +0,0 @@ -# gRPC, REST, and Tendermint Endpoints - -This document presents an overview of all the endpoints a node exposes: gRPC, REST as well as some other endpoints. {synopsis} - -## An Overview of All Endpoints - -Each node exposes the following endpoints for users to interact with a node, each endpoint is served on a different port. Details on how to configure each endpoint is provided in the endpoint's own section. - -- the gRPC server (default port: `9090`), -- the REST server (default port: `1317`), -- the Tendermint RPC endpoint (default port: `26657`). - -::: tip -The node also exposes some other endpoints, such as the Tendermint P2P endpoint, or the [Prometheus endpoint](https://docs.tendermint.com/master/nodes/metrics.html#metrics), which are not directly related to the Cosmos SDK. Please refer to the [Tendermint documentation](https://docs.tendermint.com/master/tendermint-core/using-tendermint.html#configuration) for more information about these endpoints. -::: - -## gRPC Server - -::: warning -A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. - -To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: - -``` -replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 -``` - -Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. -::: - -Cosmos SDK v0.40 introduced Protobuf as the main [encoding](./encoding) library, and this brings a wide range of Protobuf-based tools that can be plugged into the SDK. One such tool is [gRPC](https://grpc.io), a modern open source high performance RPC framework that has decent client support in several languages. - -Each module exposes a [Protobuf `Query` service](../building-modules/02-messages-and-queries.md#queries) that defines state queries. The `Query` services and a transaction service used to broadcast transactions are hooked up to the gRPC server via the following function inside the application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-rc0/server/types/app.go#L39-L41 - -Note: It is not possible to expose any [Protobuf `Msg` service](../building-modules/02-messages-and-queries.md#messages) endpoints via gRPC. Transactions must be generated and signed using the CLI or programatically before they can be broadcasted using gRPC. See [Generating, Signing, and Broadcasting Transactions](../run-node/txs.html) for more information. - -The `grpc.Server` is a concrete gRPC server, which spawns and serves all gRPC query requests and a broadcast transaction request. This server can be configured inside `~/.simapp/config/app.toml`: - -- `grpc.enable = true|false` field defines if the gRPC server should be enabled. Defaults to `true`. -- `grpc.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `0.0.0.0:9090`. - -:::tip -`~/.simapp` is the directory where the node's configuration and databases are stored. By default, it's set to `~/.{app_name}`. -::: - -Once the gRPC server is started, you can send requests to it using a gRPC client. Some examples are given in our [Interact with the Node](../run-node/interact-node.md#using-grpc) tutorial. - -An overview of all available gRPC endpoints shipped with the Cosmos SDK is [Protobuf documention](./14-proto-docs.md). - -## REST Server - -In Cosmos SDK v0.40, the node continues to serve a REST server. However, the existing routes present in version v0.39 and earlier are now marked as deprecated, and new routes have been added via gRPC-gateway. - -All routes are configured under the following fields in `~/.simapp/config/app.toml`: - -- `api.enable = true|false` field defines if the REST server should be enabled. Defaults to `false`. -- `api.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `tcp://0.0.0.0:1317`. -- some additional API configuration options are defined in `~/.simapp/config/app.toml`, along with comments, please refer to that file directly. - -### gRPC-gateway REST Routes - -If, for various reasons, you cannot use gRPC (for example, you are building a web application, and browsers don't support HTTP2 on which gRPC is built), then the SDK offers REST routes via gRPC-gateway. - -[gRPC-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) is a tool to expose gRPC endpoints as REST endpoints. For each gRPC endpoint defined in a Protobuf `Query` service, the SDK offers a REST equivalent. For instance, querying a balance could be done via the `/cosmos.bank.v1beta1.QueryAllBalances` gRPC endpoint, or alternatively via the gRPC-gateway `"/cosmos/bank/v1beta1/balances/{address}"` REST endpoint: both will return the same result. For each RPC method defined in a Protobuf `Query` service, the corresponding REST endpoint is defined as an option: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/proto/cosmos/bank/v1beta1/query.proto#L19-L22 - -For application developers, gRPC-gateway REST routes needs to be wired up to the REST server, this is done by calling the `RegisterGRPCGatewayRoutes` function on the ModuleManager. - -### Legacy REST API Routes - -The REST routes present in Cosmos SDK v0.39 and earlier are marked as deprecated via a [HTTP deprecation header](https://tools.ietf.org/id/draft-dalal-deprecation-header-01.html). They are still maintained to keep backwards compatibility, but will be removed in v0.44. For updating from Legacy REST routes to new gRPC-gateway REST routes, please refer to our [migration guide](../migrations/rest.md). - -For application developers, Legacy REST API routes needs to be wired up to the REST server, this is done by calling the `RegisterRESTRoutes` function on the ModuleManager. - -### Swagger - -A [Swagger](https://swagger.io/) (or OpenAPIv2) specification file is exposed under the `/swagger` route on the API server. Swagger is an open specification describing the API endpoints a server serves, including description, input arguments, return types and much more about each endpoint. - -Enabling the `/swagger` endpoint is configurable inside `~/.simapp/config/app.toml` via the `api.swagger` field, which is set to true by default. - -For application developers, you may want to generate your own Swagger definitions based on your custom modules. The SDK's [Swagger generation script](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/scripts/protoc-swagger-gen.sh) is a good place to start. - -## Tendermint RPC - -Independently from the Cosmos SDK, Tendermint also exposes a RPC server. This RPC server can be configured by tuning parameters under the `rpc` table in the `~/.simapp/config/config.toml`, the default listening address is `tcp://0.0.0.0:26657`. An OpenAPI specification of all Tendermint RPC endpoints is available [here](https://docs.tendermint.com/master/rpc/). - -Some Tendermint RPC endpoints are directly related to the Cosmos SDK: - -- `/abci_query`: this endpoint will query the application for state. As the `path` parameter, you can send the following strings: - - any Protobuf fully-qualified service method, such as `/cosmos.bank.v1beta1.QueryAllBalances`. The `data` field should then include the method's request parameter(s) encoded as bytes using Protobuf. - - `/app/simulate`: this will simulate a transaction, and return some information such as gas used. - - `/app/version`: this will return the application's version. - - `/store/{path}`: this will query the store directly. - - `/p2p/filter/addr/{port}`: this will return a filtered list of the node's P2P peers by address port. - - `/p2p/filter/id/{id}`: this will return a filtered list of the node's P2P peers by ID. -- `/broadcast_tx_{aync,async,commit}`: these 3 endpoint will broadcast a transaction to other peers. CLI, gRPC and REST expose [a way to broadcast transations](./01-transactions.md#broadcasting-the-transaction), but they all use these 3 Tendermint RPCs under the hood. - -## Comparison Table - -| Name | Advantages | Disadvantages | -| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| gRPC | - can use code-generated stubs in various languages
- supports streaming and bidirectional communication (HTTP2)
- small wire binary sizes, faster transmission | - based on HTTP2, not available in browsers
- learning curve (mostly due to Protobuf) | -| REST | - ubiquitous
- client libraries in all languages, faster implementation
| - only supports unary request-response communication (HTTP1.1)
- bigger over-the-wire message sizes (JSON) | -| CometBFT RPC | - easy to use | - bigger over-the-wire message sizes (JSON) | - - -## Next {hide} - -Learn about [the CLI](./06-cli.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/09-ocap.md b/versioned_docs/version-0.45/develop/advanced-concepts/09-ocap.md deleted file mode 100644 index fb38c755d..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/09-ocap.md +++ /dev/null @@ -1,75 +0,0 @@ -# Object-Capability Model - -## Intro - -When thinking about security, it is good to start with a specific threat model. Our threat model is the following: - -> We assume that a thriving ecosystem of Cosmos-SDK modules that are easy to compose into a blockchain application will contain faulty or malicious modules. - -The Cosmos SDK is designed to address this threat by being the -foundation of an object capability system. - -> The structural properties of object capability systems favor -> modularity in code design and ensure reliable encapsulation in -> code implementation. -> -> These structural properties facilitate the analysis of some -> security properties of an object-capability program or operating -> system. Some of these — in particular, information flow properties -> — can be analyzed at the level of object references and -> connectivity, independent of any knowledge or analysis of the code -> that determines the behavior of the objects. -> -> As a consequence, these security properties can be established -> and maintained in the presence of new objects that contain unknown -> and possibly malicious code. -> -> These structural properties stem from the two rules governing -> access to existing objects: -> -> 1. An object A can send a message to B only if object A holds a -> reference to B. -> 2. An object A can obtain a reference to C only -> if object A receives a message containing a reference to C. As a -> consequence of these two rules, an object can obtain a reference -> to another object only through a preexisting chain of references. -> In short, "Only connectivity begets connectivity." - -For an introduction to object-capabilities, see this [Wikipedia article](https://en.wikipedia.org/wiki/Object-capability_model). - -## Ocaps in practice - -The idea is to only reveal what is necessary to get the work done. - -For example, the following code snippet violates the object capabilities -principle: - -```go -type AppAccount struct {...} -account := &AppAccount{ - Address: pub.Address(), - Coins: sdk.Coins{sdk.NewInt64Coin("ATM", 100)}, -} -sumValue := externalModule.ComputeSumValue(account) -``` - -The method `ComputeSumValue` implies a pure function, yet the implied -capability of accepting a pointer value is the capability to modify that -value. The preferred method signature should take a copy instead. - -```go -sumValue := externalModule.ComputeSumValue(*account) -``` - -In the Cosmos SDK, you can see the application of this principle in the -gaia app. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.41.4/simapp/app.go#L249-L273 - -The following diagram shows the current dependencies between keepers. - -![Keeper dependencies](./keeper_dependencies.svg) - -## Next {hide} - -Learn about the [`runTx` middleware](./12-runtx_middleware.md) {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/10-telemetry.md b/versioned_docs/version-0.45/develop/advanced-concepts/10-telemetry.md deleted file mode 100644 index a9fdb5105..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/10-telemetry.md +++ /dev/null @@ -1,141 +0,0 @@ -# Telemetry - -Gather relevant insights about your application and modules with custom metrics and telemetry. {synopsis} - -The Cosmos SDK enables operators and developers to gain insight into the performance and behavior of -their application through the use of the `telemetry` package. The Cosmos SDK currently supports -enabling in-memory and prometheus as telemetry sinks. This allows the ability to query for and scrape -metrics from a single exposed API endpoint -- `/metrics?format={text|prometheus}`, the default being -`text`. - -If telemetry is enabled via configuration, a single global metrics collector is registered via the -[go-metrics](https://github.com/armon/go-metrics) library. This allows emitting and collecting -metrics through simple API calls. - -Example: - -```go -func EndBlocker(ctx sdk.Context, k keeper.Keeper) { - defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) - - // ... -} -``` - -Developers may use the `telemetry` package directly, which provides wrappers around metric APIs -that include adding useful labels, or they must use the `go-metrics` library directly. It is preferable -to add as much context and adequate dimensionality to metrics as possible, so the `telemetry` package -is advised. Regardless of the package or method used, the Cosmos SDK supports the following metrics -types: - -* gauges -* summaries -* counters - -## Labels - -Certain components of modules will have their name automatically added as a label (e.g. `BeginBlock`). -Operators may also supply the application with a global set of labels that will be applied to all -metrics emitted using the `telemetry` package (e.g. chain-id). Global labels are supplied as a list -of [name, value] tuples. - -Example: - -```toml -global-labels = [ - ["chain_id", "chain-OfXo4V"], -] -``` - -## Cardinality - -Cardinality is key, specifically label and key cardinality. Cardinality is how many unique values of -something there are. So there is naturally a tradeoff between granularity and how much stress is put -on the telemetry sink in terms of indexing, scrape, and query performance. - -Developers should take care to support metrics with enough dimensionality and granularity to be -useful, but not increase the cardinality beyond the sink's limits. A general rule of thumb is to not -exceed a cardinality of 10. - -Consider the following examples with enough granularity and adequate cardinality: - -* begin/end blocker time -* tx gas used -* block gas used -* amount of tokens minted -* amount of accounts created - -The following examples expose too much cardinality and may not even prove to be useful: - -* transfers between accounts with amount -* voting/deposit amount from unique addresses - -## Supported Metrics - -| Metric | Description | Unit | Type | -|:--------------------------------|:------------------------------------------------------------------------------------------|:----------------|:--------| -| `tx_count` | Total number of txs processed via `DeliverTx` | tx | counter | -| `tx_successful` | Total number of successful txs processed via `DeliverTx` | tx | counter | -| `tx_failed` | Total number of failed txs processed via `DeliverTx` | tx | counter | -| `tx_gas_used` | The total amount of gas used by a tx | gas | gauge | -| `tx_gas_wanted` | The total amount of gas requested by a tx | gas | gauge | -| `tx_msg_send` | The total amount of tokens sent in a `MsgSend` (per denom) | token | gauge | -| `tx_msg_withdraw_reward` | The total amount of tokens withdrawn in a `MsgWithdrawDelegatorReward` (per denom) | token | gauge | -| `tx_msg_withdraw_commission` | The total amount of tokens withdrawn in a `MsgWithdrawValidatorCommission` (per denom) | token | gauge | -| `tx_msg_delegate` | The total amount of tokens delegated in a `MsgDelegate` | token | gauge | -| `tx_msg_begin_unbonding` | The total amount of tokens undelegated in a `MsgUndelegate` | token | gauge | -| `tx_msg_begin_begin_redelegate` | The total amount of tokens redelegated in a `MsgBeginRedelegate` | token | gauge | -| `tx_msg_ibc_transfer` | The total amount of tokens transferred via IBC in a `MsgTransfer` (source or sink chain) | token | gauge | -| `ibc_transfer_packet_receive` | The total amount of tokens received in a `FungibleTokenPacketData` (source or sink chain) | token | gauge | -| `new_account` | Total number of new accounts created | account | counter | -| `gov_proposal` | Total number of governance proposals | proposal | counter | -| `gov_vote` | Total number of governance votes for a proposal | vote | counter | -| `gov_deposit` | Total number of governance deposits for a proposal | deposit | counter | -| `staking_delegate` | Total number of delegations | delegation | counter | -| `staking_undelegate` | Total number of undelegations | undelegation | counter | -| `staking_redelegate` | Total number of redelegations | redelegation | counter | -| `ibc_transfer_send` | Total number of IBC transfers sent from a chain (source or sink) | transfer | counter | -| `ibc_transfer_receive` | Total number of IBC transfers received to a chain (source or sink) | transfer | counter | -| `ibc_client_create` | Total number of clients created | create | counter | -| `ibc_client_update` | Total number of client updates | update | counter | -| `ibc_client_upgrade` | Total number of client upgrades | upgrade | counter | -| `ibc_client_misbehaviour` | Total number of client misbehaviours | misbehaviour | counter | -| `ibc_connection_open-init` | Total number of connection `OpenInit` handshakes | handshake | counter | -| `ibc_connection_open-try` | Total number of connection `OpenTry` handshakes | handshake | counter | -| `ibc_connection_open-ack` | Total number of connection `OpenAck` handshakes | handshake | counter | -| `ibc_connection_open-confirm` | Total number of connection `OpenConfirm` handshakes | handshake | counter | -| `ibc_channel_open-init` | Total number of channel `OpenInit` handshakes | handshake | counter | -| `ibc_channel_open-try` | Total number of channel `OpenTry` handshakes | handshake | counter | -| `ibc_channel_open-ack` | Total number of channel `OpenAck` handshakes | handshake | counter | -| `ibc_channel_open-confirm` | Total number of channel `OpenConfirm` handshakes | handshake | counter | -| `ibc_channel_close-init` | Total number of channel `CloseInit` handshakes | handshake | counter | -| `ibc_channel_close-confirm` | Total number of channel `CloseConfirm` handshakes | handshake | counter | -| `tx_msg_ibc_recv_packet` | Total number of IBC packets received | packet | counter | -| `tx_msg_ibc_acknowledge_packet` | Total number of IBC packets acknowledged | acknowledgement | counter | -| `ibc_timeout_packet` | Total number of IBC timeout packets | timeout | counter | -| `abci_check_tx` | Duration of ABCI `CheckTx` | ms | summary | -| `abci_deliver_tx` | Duration of ABCI `DeliverTx` | ms | summary | -| `abci_commit` | Duration of ABCI `Commit` | ms | summary | -| `abci_query` | Duration of ABCI `Query` | ms | summary | -| `abci_begin_block` | Duration of ABCI `BeginBlock` | ms | summary | -| `abci_end_block` | Duration of ABCI `EndBlock` | ms | summary | -| `begin_blocker` | Duration of `BeginBlock` for a given module | ms | summary | -| `end_blocker` | Duration of `EndBlock` for a given module | ms | summary | -| `store_iavl_get` | Duration of an IAVL `Store#Get` call | ms | summary | -| `store_iavl_set` | Duration of an IAVL `Store#Set` call | ms | summary | -| `store_iavl_has` | Duration of an IAVL `Store#Has` call | ms | summary | -| `store_iavl_delete` | Duration of an IAVL `Store#Delete` call | ms | summary | -| `store_iavl_commit` | Duration of an IAVL `Store#Commit` call | ms | summary | -| `store_iavl_query` | Duration of an IAVL `Store#Query` call | ms | summary | -| `store_gaskv_get` | Duration of a GasKV `Store#Get` call | ms | summary | -| `store_gaskv_set` | Duration of a GasKV `Store#Set` call | ms | summary | -| `store_gaskv_has` | Duration of a GasKV `Store#Has` call | ms | summary | -| `store_gaskv_delete` | Duration of a GasKV `Store#Delete` call | ms | summary | -| `store_cachekv_get` | Duration of a CacheKV `Store#Get` call | ms | summary | -| `store_cachekv_set` | Duration of a CacheKV `Store#Set` call | ms | summary | -| `store_cachekv_write` | Duration of a CacheKV `Store#Write` call | ms | summary | -| `store_cachekv_delete` | Duration of a CacheKV `Store#Delete` call | ms | summary | - -## Next {hide} - -Learn about the [object-capability](./09-ocap.md) model {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/11-runtx_middleware.md b/versioned_docs/version-0.45/develop/advanced-concepts/11-runtx_middleware.md deleted file mode 100644 index 3b3535a1a..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/11-runtx_middleware.md +++ /dev/null @@ -1,69 +0,0 @@ -# RunTx recovery middleware - -`BaseApp.runTx()` function handles Golang panics that might occur during transactions execution, for example, keeper has faced an invalid state and paniced. -Depending on the panic type different handler is used, for instance the default one prints an error log message. -Recovery middleware is used to add custom panic recovery for SDK application developers. - -More context could be found in the corresponding [ADR-022](../architecture/adr-022-custom-panic-handling.md). - -Implementation could be found in the [recovery.go](../../baseapp/recovery.go) file. - -## Interface - -```go -type RecoveryHandler func(recoveryObj interface{}) error -``` - -`recoveryObj` is a return value for `recover()` function from the `buildin` Golang package. - -**Contract:** - -- RecoveryHandler returns `nil` if `recoveryObj` wasn't handled and should be passed to the next recovery middleware; -- RecoveryHandler returns a non-nil `error` if `recoveryObj` was handled; - -## Custom RecoveryHandler register - -`BaseApp.AddRunTxRecoveryHandler(handlers ...RecoveryHandler)` - -BaseApp method adds recovery middleware to the default recovery chain. - -## Example - -Lets assume we want to emit the "Consensus failure" chain state if some particular error occurred. - -We have a module keeper that panics: - -```go -func (k FooKeeper) Do(obj interface{}) { - if obj == nil { - // that shouldn't happen, we need to crash the app - err := sdkErrors.Wrap(fooTypes.InternalError, "obj is nil") - panic(err) - } -} -``` - -By default that panic would be recovered and an error message will be printed to log. To override that behaviour we should register a custom RecoveryHandler: - -```go -// SDK application constructor -customHandler := func(recoveryObj interface{}) error { - err, ok := recoveryObj.(error) - if !ok { - return nil - } - - if fooTypes.InternalError.Is(err) { - panic(fmt.Errorf("FooKeeper did panic with error: %w", err)) - } - - return nil -} - -baseApp := baseapp.NewBaseApp(...) -baseApp.AddRunTxRecoveryHandler(customHandler) -``` - -## Next {hide} - -Learn about the [IBC](./../ibc/README.md) protocol {hide} diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/12-simulation.md b/versioned_docs/version-0.45/develop/advanced-concepts/12-simulation.md deleted file mode 100644 index 2ce65a954..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/12-simulation.md +++ /dev/null @@ -1,97 +0,0 @@ -# Cosmos Blockchain Simulator - -The Cosmos SDK offers a full fledged simulation framework to fuzz test every -message defined by a module. - -On the SDK, this functionality is provided by the[`SimApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/app.go), which is a -`Baseapp` application that is used for running the [`simulation`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/simulation) module. -This module defines all the simulation logic as well as the operations for -randomized parameters like accounts, balances etc. - -## Goals - -The blockchain simulator tests how the blockchain application would behave under -real life circumstances by generating and sending randomized messages. -The goal of this is to detect and debug failures that could halt a live chain, -by providing logs and statistics about the operations run by the simulator as -well as exporting the latest application state when a failure was found. - -Its main difference with integration testing is that the simulator app allows -you to pass parameters to customize the chain that's being simulated. -This comes in handy when trying to reproduce bugs that were generated in the -provided operations (randomized or not). - -## Simulation commands - -The simulation app has different commands, each of which tests a different -failure type: - -- `AppImportExport`: The simulator exports the initial app state and then it - creates a new app with the exported `genesis.json` as an input, checking for - inconsistencies between the stores. -- `AppSimulationAfterImport`: Queues two simulations together. The first one provides the app state (_i.e_ genesis) to the second. Useful to test software upgrades or hard-forks from a live chain. -- `AppStateDeterminism`: Checks that all the nodes return the same values, in the same order. -- `BenchmarkInvariants`: Analysis of the performance of running all modules' invariants (_i.e_ sequentially runs a [benchmark](https://golang.org/pkg/testing/#hdr-Benchmarks) test). An invariant checks for - differences between the values that are on the store and the passive tracker. Eg: total coins held by accounts vs total supply tracker. -- `FullAppSimulation`: General simulation mode. Runs the chain and the specified operations for a given number of blocks. Tests that there're no `panics` on the simulation. It does also run invariant checks on every `Period` but they are not benchmarked. - -Each simulation must receive a set of inputs (_i.e_ flags) such as the number of -blocks that the simulation is run, seed, block size, etc. -Check the full list of flags [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/config.go#L32-L55). - -## Simulator Modes - -In addition to the various inputs and commands, the simulator runs in three modes: - -1. Completely random where the initial state, module parameters and simulation - parameters are **pseudo-randomly generated**. -2. From a `genesis.json` file where the initial state and the module parameters are defined. - This mode is helpful for running simulations on a known state such as a live network export where a new (mostly likely breaking) version of the application needs to be tested. -3. From a `params.json` file where the initial state is pseudo-randomly generated but the module and simulation parameters can be provided manually. - This allows for a more controlled and deterministic simulation setup while allowing the state space to still be pseudo-randomly simulated. - The list of available parameters are listed [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/simulation/params.go#L44-L52). - -::: tip -These modes are not mutually exclusive. So you can for example run a randomly -generated genesis state (`1`) with manually generated simulation params (`3`). -::: - -## Usage - -This is a general example of how simulations are run. For more specific examples -check the SDK [Makefile](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/Makefile#L251-L287). - -```bash - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestApp \ - ... - -v -timeout 24h -``` - -## Debugging Tips - -Here are some suggestions when encountering a simulation failure: - -- Export the app state at the height were the failure was found. You can do this - by passing the `-ExportStatePath` flag to the simulator. -- Use `-Verbose` logs. They could give you a better hint on all the operations - involved. -- Reduce the simulation `-Period`. This will run the invariants checks more - frequently. -- Print all the failed invariants at once with `-PrintAllInvariants`. -- Try using another `-Seed`. If it can reproduce the same error and if it fails - sooner you will spend less time running the simulations. -- Reduce the `-NumBlocks` . How's the app state at the height previous to the - failure? -- Run invariants on every operation with `-SimulateEveryOperation`. _Note_: this - will slow down your simulation **a lot**. -- Try adding logs to operations that are not logged. You will have to define a - [Logger](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/staking/keeper/keeper.go#L66-L69) on your `Keeper`. - -## Use simulation in your SDK-based application - -Learn how you can integrate the simulation into your SDK-based application: - -- Application Simulation Manager -- [Building modules: Simulator](../building-modules/simulator.md) -- Simulator tests diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/13-upgrade.md b/versioned_docs/version-0.45/develop/advanced-concepts/13-upgrade.md deleted file mode 100644 index 31ace074f..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/13-upgrade.md +++ /dev/null @@ -1,156 +0,0 @@ -# In-Place Store Migrations - -::: warning -Read and understand all of the in-place store migration documentation before you run a migration on a live chain. -::: - -Upgrade your app modules smoothly with custom in-place store migration logic. {synopsis} - -The Cosmos SDK uses two methods to perform upgrades. - -- Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. See [Chain Upgrade Guide to v0.42](/v0.42/migrations/chain-upgrade-guide-040.html). - -- Version v0.44 and later can perform upgrades in place to significantly decrease the upgrade time for chains with a larger state. Use the [Module Upgrade Guide](../building-modules/13-upgrade.md) to set up your application modules to take advantage of in-place upgrades. - -This document provides steps to use the In-Place Store Migrations upgrade method. - -## Tracking Module Versions - -Each module gets assigned a consensus version by the module developer. The consensus version serves as the breaking change version of the module. The Cosmos SDK keeps track of all module consensus versions in the x/upgrade `VersionMap` store. During an upgrade, the difference between the old `VersionMap` stored in state and the new `VersionMap` is calculated by the Cosmos SDK. For each identified difference, the module-specific migrations are run and the respective consensus version of each upgraded module is incremented. - -### Consensus Version - -The consensus version is defined on each app module by the module developer and serves as the breaking change version of the module. The consensus version informs the SDK on which modules need to be upgraded. For example, if the bank module was version 2 and an upgrade introduces bank module 3, the SDK upgrades the bank module and runs the "version 2 to 3" migration script. - -### Version Map - -The version map is a mapping of module names to consensus versions. The map is persisted to x/upgrade's state for use during in-place migrations. When migrations finish, the updated version map is persisted in the state. - -## Upgrade Handlers - -Upgrades use an `UpgradeHandler` to facilitate migrations. The `UpgradeHandler` functions implemented by the app developer must conform to the following function signature. These functions retrieve the `VersionMap` from x/upgrade's state and return the new `VersionMap` to be stored in x/upgrade after the upgrade. The diff between the two `VersionMap`s determines which modules need upgrading. - -```go -type UpgradeHandler func(ctx sdk.Context, plan Plan, fromVM VersionMap) (VersionMap, error) -``` - -Inside these functions, you must perform any upgrade logic to include in the provided `plan`. All upgrade handler functions must end with the following line of code: - -```go - return app.mm.RunMigrations(ctx, cfg, fromVM) -``` - -## Running Migrations - -Migrations are run inside of an `UpgradeHandler` using `app.mm.RunMigrations(ctx, cfg, vm)`. The `UpgradeHandler` functions describe the functionality to occur during an upgrade. The `RunMigration` function loops through the `VersionMap` argument and runs the migration scripts for all versions that are less than the versions of the new binary app module. After the migrations are finished, a new `VersionMap` is returned to persist the upgraded module versions to state. - -```go -cfg := module.NewConfigurator(...) -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - - // ... - // additional upgrade logic - // ... - - // returns a VersionMap with the updated module ConsensusVersions - return app.mm.RunMigrations(ctx, fromVM) -}) -``` - -To learn more about configuring migration scripts for your modules, see the [Module Upgrade Guide](../building-modules/13-upgrade.md). - -### Order Of Migrations - -By default, all migrations are run in module name alphabetical ascending order, except `x/auth` which is run last. The reason is state dependencies between x/auth and other modules (you can read more in [issue #10606](https://github.com/cosmos/cosmos-sdk/issues/10606)). - -If you want to change the order of migration then you should call `app.mm.SetOrderMigrations(module1, module2, ...)` in your app.go file. The function will panic if you forget to include a module in the argument list. - -## Adding New Modules During Upgrades - -You can introduce entirely new modules to the application during an upgrade. New modules are recognized because they have not yet been registered in `x/upgrade`'s `VersionMap` store. In this case, `RunMigrations` calls the `InitGenesis` function from the corresponding module to set up its initial state. - -### Add StoreUpgrades for New Modules - -All chains preparing to run in-place store migrations will need to manually add store upgrades for new modules and then configure the store loader to apply those upgrades. This ensures that the new module's stores are added to the multistore before the migrations begin. - -```go -upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() -if err != nil { - panic(err) -} - -if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { - storeUpgrades := storetypes.StoreUpgrades{ - // add store upgrades for new modules - // Example: - // Added: []string{"foo", "bar"}, - // ... - } - - // configure store loader that checks if version == upgradeHeight and applies store upgrades - app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) -} -``` - -## Genesis State - -When starting a new chain, the consensus version of each module MUST be saved to state during the application's genesis. To save the consensus version, add the following line to the `InitChainer` method in `app.go`: - -```diff -func (app *MyApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - ... -+ app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) - ... -} -``` - -This information is used by the Cosmos SDK to detect when modules with newer versions are introduced to the app. - -For a new module `foo`, `InitGenesis` is called by `RunMigration` only when `foo` is registered in the module manager but it's not set in the `fromVM`. Therefore, if you want to skip `InitGenesis` when a new module is added to the app, then you should set its module version in `fromVM` to the module consensus version: - -```go -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - // ... - - // Set foo's version to the latest ConsensusVersion in the VersionMap. - // This will skip running InitGenesis on Foo - fromVM[foo.ModuleName] = foo.AppModule{}.ConsensusVersion() - - return app.mm.RunMigrations(ctx, fromVM) -}) -``` - -### Overwriting Genesis Functions - -The Cosmos SDK offers modules that the application developer can import in their app. These modules often have an `InitGenesis` function already defined. - -You can write your own `InitGenesis` function for an imported module. To do this, manually trigger your custom genesis function in the upgrade handler. - -::: warning -You MUST manually set the consensus version in the version map passed to the `UpgradeHandler` function. Without this, the SDK will run the Module's existing `InitGenesis` code even if you triggered your custom function in the `UpgradeHandler`. -::: - -```go -import foo "github.com/my/module/foo" - -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - - // Register the consensus version in the version map - // to avoid the SDK from triggering the default - // InitGenesis function. - fromVM["foo"] = foo.AppModule{}.ConsensusVersion() - - // Run custom InitGenesis for foo - app.mm["foo"].InitGenesis(ctx, app.appCodec, myCustomGenesisState) - - return app.mm.RunMigrations(ctx, cfg, fromVM) -}) -``` - -## Syncing a Full Node to an Upgraded Blockchain - -You can sync a full node to an existing blockchain which has been upgraded using Cosmovisor - -In order to successfully sync, you must start with the initial binary that the blockchain started with at genesis. Cosmovisor will handle downloading and switching to the binaries associated with each sequential upgrade. - -To learn more about Cosmovisor, see the [Cosmovisor Quick Start](../run-node/cosmovisor.md). diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-begin_block.png b/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-begin_block.png deleted file mode 100644 index 745d4a5a971292bb0346c35893b42ebfbcdc206e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20565 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 HU84U7#M|-t diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-checktx.png b/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-checktx.png deleted file mode 100644 index 38b217acdd04fb2430a2332946864de04474ae5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82308 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 N1zA-Y;BI{L{{e+~Yq9_U diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-deliver_tx.png b/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-deliver_tx.png deleted file mode 100644 index f0a54b4ec34bbe282ed6eff81369428d02dec095..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59007 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 E2iU8KRR910 diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-initchain.png b/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state-initchain.png deleted file mode 100644 index d03e88ef2ae0745aaedc52cf72863080388d7e9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32800 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 R0ton{s-*d-Lcucl{{yUICl~+# diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state_types.png b/versioned_docs/version-0.45/develop/advanced-concepts/baseapp_state_types.png deleted file mode 100644 index a7f91a6093e0099f409101276833e80bdc223f78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133747 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 T?XrE0gO|T|pjV|w#G(HHz@)&x diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/keeper_dependencies.svg b/versioned_docs/version-0.45/develop/advanced-concepts/keeper_dependencies.svg deleted file mode 100644 index bac9328e3..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/keeper_dependencies.svg +++ /dev/null @@ -1,102 +0,0 @@ -The dependencies between Keepers (Feb 2021)StakingDistributionSlashingEvidenceBankAuth/AccountGovMint \ No newline at end of file diff --git a/versioned_docs/version-0.45/develop/advanced-concepts/transaction_flow.svg b/versioned_docs/version-0.45/develop/advanced-concepts/transaction_flow.svg deleted file mode 100644 index 1ae962de3..000000000 --- a/versioned_docs/version-0.45/develop/advanced-concepts/transaction_flow.svg +++ /dev/null @@ -1,48 +0,0 @@ -UserUserbaseAppbaseApprouterrouterhandlerhandlermsgServermsgServerkeeperkeeperContext.EventManagerContext.EventManagerTransaction Type<Tx>Route(ctx, msgRoute)handlerMsg<Tx>(Context, Msg(...))<Tx>(Context, Msg)alt[addresses invalid, denominations wrong, etc.]errorperform action, update contextresults, error codeEmit relevant eventsmaybe wrap results in more structureresult, error coderesults, error code \ No newline at end of file diff --git a/versioned_docs/version-0.45/develop/high-level-concepts/00-overview-app.md b/versioned_docs/version-0.45/develop/high-level-concepts/00-overview-app.md deleted file mode 100644 index 2d3b4a105..000000000 --- a/versioned_docs/version-0.45/develop/high-level-concepts/00-overview-app.md +++ /dev/null @@ -1,260 +0,0 @@ -# Anatomy of an SDK Application - -This document describes the core parts of a Cosmos SDK application. Throughout the document, a placeholder application named `app` will be used. {synopsis} - -## Node Client - -The Daemon, or [Full-Node Client](../core/03-node.md), is the core process of an SDK-based blockchain. Participants in the network run this process to initialize their state-machine, connect with other full-nodes and update their state-machine as new blocks come in. - -``` - ^ +-------------------------------+ ^ - | | | | - | | State-machine = Application | | - | | | | Built with Cosmos SDK - | | ^ + | | - | +----------- | ABCI | ----------+ v - | | + v | ^ - | | | | -Blockchain Node | | Consensus | | - | | | | - | +-------------------------------+ | Tendermint Core - | | | | - | | Networking | | - | | | | - v +-------------------------------+ v -``` - -The blockchain full-node presents itself as a binary, generally suffixed by `-d` for "daemon" (e.g. `appd` for `app` or `gaiad` for `gaia`). This binary is built by running a simple [`main.go`](../core/03-node.md#main-function) function placed in `./cmd/appd/`. This operation usually happens through the [Makefile](#dependencies-and-makefile). - -Once the main binary is built, the node can be started by running the [`start` command](../core/03-node.md#start-command). This command function primarily does three things: - -1. Create an instance of the state-machine defined in [`app.go`](#core-application-file). -2. Initialize the state-machine with the latest known state, extracted from the `db` stored in the `~/.app/data` folder. At this point, the state-machine is at height `appBlockHeight`. -3. Create and start a new Tendermint instance. Among other things, the node will perform a handshake with its peers. It will get the latest `blockHeight` from them, and replay blocks to sync to this height if it is greater than the local `appBlockHeight`. If `appBlockHeight` is `0`, the node is starting from genesis and Tendermint sends an `InitChain` message via the ABCI to the `app`, which triggers the [`InitChainer`](#initchainer). - -## Core Application File - -In general, the core of the state-machine is defined in a file called `app.go`. It mainly contains the **type definition of the application** and functions to **create and initialize it**. - -### Type Definition of the Application - -The first thing defined in `app.go` is the `type` of the application. It is generally comprised of the following parts: - -- **A reference to [`baseapp`](../../develop/advanced-concepts/00-baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../../develop/advanced-concepts/00-baseapp.md#routing). -- **A list of store keys**. The [store](../core/04-store.md), which contains the entire state, is implemented as a [`multistore`](../core/04-store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK. -- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/06-keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions. -- **A reference to an [`appCodec`](../core/05-encoding.md).** The application's `appCodec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The default codec is [Protocol Buffers](../core/05-encoding.md). -- **A reference to a [`legacyAmino`](../core/05-encoding.md) codec.** Some parts of the SDK have not been migrated to use the `appCodec` above, and are still hardcoded to use Amino. Other parts explicity use Amino for backwards compatibility. For these reasons, the application still holds a reference to the legacy Amino codec. Please note that the Amino codec will be removed from the SDK in the upcoming releases. -- **A reference to a [module manager](../building-modules/01-module-manager.md#manager)** and a [basic module manager](../building-modules/01-module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering their [`Msg` service](../../develop/advanced-concepts/00-baseapp.md#msg-services) and [gRPC `Query` service](../../develop/advanced-concepts/00-baseapp.md#grpc-query-services), or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker). - -See an example of application type definition from `simapp`, the SDK's own app used for demo and testing purposes: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L145-L187 - -### Constructor Function - -This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/03-node.md#start-command) of the application's daemon command. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/types/app.go#L48-L50 - -Here are the main actions performed by this function: - -- Instantiate a new [`codec`](../core/05-encoding.md) and initialize the `codec` of each of the application's module using the [basic manager](../building-modules/01-module-manager.md#basicmanager) -- Instantiate a new application with a reference to a `baseapp` instance, a codec and all the appropriate store keys. -- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`. -- Instantiate the application's [module manager](../building-modules/01-module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules. -- With the module manager, initialize the application's [`Msg` services](../../develop/advanced-concepts/00-baseapp.md#msg-services), [gRPC `Query` services](../../develop/advanced-concepts/00-baseapp.md#grpc-query-services), [legacy `Msg` routes](../../develop/advanced-concepts/00-baseapp.md#routing) and [legacy query routes](../../develop/advanced-concepts/00-baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`Msg` service](#msg-services) using the routes defined here. Likewise, when a gRPC query request is received by the application, it is routed to the appropriate module's [`gRPC query service`](#grpc-query-services) using the gRPC routes defined here. The SDK still supports legacy `Msg`s and legacy Tendermint queries, which are routed using respectively the legacy `Msg` routes and the legacy query routes. -- With the module manager, register the [application's modules' invariants](../building-modules/07-invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/07-invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix. -- With the module manager, set the order of execution between the `InitGenesis`, `BeginBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions. -- Set the remainer of application's parameters: - - [`InitChainer`](#initchainer): used to initialize the application when it is first started. - - [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block). - - [`anteHandler`](../../develop/advanced-concepts/00-baseapp.md#antehandler): used to handle fees and signature verification. -- Mount the stores. -- Return the application. - -Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.app/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time. - -See an example of application constructor from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L198-L441 - -### InitChainer - -The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method. - -In general, the `InitChainer` is mostly composed of the [`InitGenesis`](../building-modules/08-genesis.md#initgenesis) function of each of the application's modules. This is done by calling the `InitGenesis` function of the module manager, which in turn will call the `InitGenesis` function of each of the modules it contains. Note that the order in which the modules' `InitGenesis` functions must be called has to be set in the module manager using the [module manager's](../building-modules/01-module-manager.md) `SetOrderInitGenesis` method. This is done in the [application's constructor](#application-constructor), and the `SetOrderInitGenesis` has to be called before the `SetInitChainer`. - -See an example of an `InitChainer` from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L464-L471 - -### BeginBlocker and EndBlocker - -The SDK offers developers the possibility to implement automatic execution of code as part of their application. This is implemented through two function called `BeginBlocker` and `EndBlocker`. They are called when the application receives respectively the `BeginBlock` and `EndBlock` messages from the Tendermint engine, which happens at the beginning and at the end of each block. The application must set the `BeginBlocker` and `EndBlocker` in its [constructor](#constructor-function) via the [`SetBeginBlocker`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetBeginBlocker) and [`SetEndBlocker`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetEndBlocker) methods. - -In general, the `BeginBlocker` and `EndBlocker` functions are mostly composed of the [`BeginBlock` and `EndBlock`](../building-modules/beginblock-endblock.md) functions of each of the application's modules. This is done by calling the `BeginBlock` and `EndBlock` functions of the module manager, which in turn will call the `BeginBLock` and `EndBlock` functions of each of the modules it contains. Note that the order in which the modules' `BegingBlock` and `EndBlock` functions must be called has to be set in the module manager using the `SetOrderBeginBlock` and `SetOrderEndBlock` methods respectively. This is done via the [module manager](../building-modules/01-module-manager.md) in the [application's constructor](#application-constructor), and the `SetOrderBeginBlock` and `SetOrderEndBlock` methods have to be called before the `SetBeginBlocker` and `SetEndBlocker` functions. - -As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution. - -See an example of `BeginBlocker` and `EndBlocker` functions from `simapp` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L454-L462 - -### Register Codec - -The `EncodingConfig` structure is the last important part of the `app.go` file. The goal of this structure is to define the codecs that will be used throughout the app. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/params/encoding.go#L9-L16 - -Here are descriptions of what each of the four fields means: - -- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces that are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct that contains a `type_url` (name of a concrete type implementing the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method that can be used to register the module's own interfaces and implementations. - - You can read more about Any in [ADR-19](../architecture/adr-019-protobuf-state-encoding.md#usage-of-any-to-encode-interfaces). - - To go more into details, the SDK uses an implementation of the Protobuf specification called [`gogoprotobuf`](https://github.com/gogo/protobuf). By default, the [gogo protobuf implementation of `Any`](https://godoc.org/github.com/gogo/protobuf/types) uses [global type registration](https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) to decode values packed in `Any` into concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could registry a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the `type_url` field. For more information, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md). -- `Marshaler`: the default codec used throughout the SDK. It is composed of a `BinaryCodec` used to encode and decode state, and a `JSONCodec` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Marshaler`. -- `TxConfig`: `TxConfig` defines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types: `SIGN_MODE_DIRECT` (which uses Protobuf binary as over-the-wire encoding) and `SIGN_MODE_LEGACY_AMINO_JSON` (which depends on Amino). Read more about transactions [here](../core/01-transactions.md). -- `Amino`: Some legacy parts of the SDK still use Amino for backwards-compatibility. Each module exposes a `RegisterLegacyAmino` method to register the module's specific types within Amino. This `Amino` codec should not be used by app developers anymore, and will be removed in future releases. - -The SDK exposes a `MakeTestEncodingConfig` function used to create a `EncodingConfig` for the app constructor (`NewApp`). It uses Protobuf as a default `Marshaler`. -NOTE: this function is marked deprecated and should only be used to create an app or in tests. We are working on refactoring codec management in a post Stargate release. - -See an example of a `MakeTestEncodingConfig` from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/590358652cc1cbc13872ea1659187e073ea38e75/simapp/encoding.go#L8-L19 - -## Modules - -[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../../develop/advanced-concepts/00-baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules). - -### Application Module Interface - -Modules must implement [interfaces](../building-modules/01-module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/01-module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/01-module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`. - -`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/01-module-manager.md#manager), which manages the application's collection of modules. - -### `Msg` Services - -Each module defines two [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services): one `Msg` service to handle messages, and one gRPC `Query` service to handle queries. If we consider the module as a state-machine, then a `Msg` service is a set of state transition RPC methods. -Each Protobuf `Msg` service method is 1:1 related to a Protobuf request type, which must implement `sdk.Msg` interface. -Note that `sdk.Msg`s are bundled in [transactions](../core/01-transactions.md), and each transaction contains one or multiple messages. - -When a valid block of transactions is received by the full-node, Tendermint relays each one to the application via [`DeliverTx`](https://tendermint.com/docs/app-dev/abci-spec.html#delivertx). Then, the application handles the transaction: - -1. Upon receiving the transaction, the application first unmarshalls it from `[]bytes`. -2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the `Msg`(s) contained in the transaction. -3. `sdk.Msg`s are encoded using Protobuf [`Any`s](#register-codec). By analyzing each `Any`'s `type_url`, baseapp's `msgServiceRouter` routes the `sdk.Msg` to the corresponding module's `Msg` service. -4. If the message is successfully processed, the state is updated. - -For a more details look at a transaction [lifecycle](./01-tx-lifecycle.md). - -Module developers create custom `Msg` services when they build their own module. The general practice is to define the `Msg` Protobuf service in a `tx.proto` file. For example, the `x/bank` module defines a service with two methods to transfer tokens: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/bank/v1beta1/tx.proto#L10-L17 - -Service methods use `keeper` in order to update the module state. - -Each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterMsgServer` function provided by the generated Protobuf code. - -### gRPC `Query` Services - -gRPC `Query` services are introduced in the v0.40 Stargate release. They allow users to query the state using [gRPC](https://grpc.io). They are enabled by default, and can be configued under the `grpc.enable` and `grpc.address` fields inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). - -gRPC `Query` services are defined in the module's Protobuf definition files, specifically inside `query.proto`. The `query.proto` definition file exposes a single `Query` [Protobuf service](https://developers.google.com/protocol-buffers/docs/proto#services). Each gRPC query endpoint corresponds to a service method, starting with the `rpc` keyword, inside the `Query` service. - -Protobuf generates a `QueryServer` interface for each module, containing all the service methods. A module's [`keeper`](#keeper) then needs to implement this `QueryServer` interface, by providing the concrete implementation of each service method. This concrete implementation is the handler of the corresponding gRPC query endpoint. - -Finally, each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterQueryServer` function provided by the generated Protobuf code. - -### Keeper - -[`Keepers`](../building-modules/06-keeper.md) are the gatekeepers of their module's store(s). To read or write in a module's store, it is mandatory to go through one of its `keeper`'s methods. This is ensured by the [object-capabilities](../core/ocap.md) model of the Cosmos SDK. Only objects that hold the key to a store can access it, and only the module's `keeper` should hold the key(s) to the module's store(s). - -`Keepers` are generally defined in a file called `keeper.go`. It contains the `keeper`'s type definition and methods. - -The `keeper` type definition generally consists of: - -- **Key(s)** to the module's store(s) in the multistore. -- Reference to **other module's `keepers`**. Only needed if the `keeper` needs to access other module's store(s) (either to read or write from them). -- A reference to the application's **codec**. The `keeper` needs it to marshal structs before storing them, or to unmarshal them when it retrieves them, because stores only accept `[]bytes` as value. - -Along with the type definition, the next important component of the `keeper.go` file is the `keeper`'s constructor function, `NewKeeper`. This function instantiates a new `keeper` of the type defined above, with a `codec`, store `keys` and potentially references to other modules' `keeper`s as parameters. The `NewKeeper` function is called from the [application's constructor](#constructor-function). The rest of the file defines the `keeper`'s methods, primarily getters and setters. - -### Command-Line, gRPC Services and REST Interfaces - -Each module defines command-line commands, gRPC services and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module. - -#### CLI - -Generally, the [commands related to a module](../building-modules/09-module-interfaces.md#cli) are defined in a folder called `client/cli` in the module's folder. The CLI divides commands in two category, transactions and queries, defined in `client/cli/tx.go` and `client/cli/query.go` respectively. Both commands are built on top of the [Cobra Library](https://github.com/spf13/cobra): - -- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata. -- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../../develop/advanced-concepts/00-baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied. - -#### gRPC - -[gRPC](https://grpc.io) is a modern open source high performance RPC framework that has support in multiple languages. It is the recommended way for external clients (such as wallets, browsers and other backend services) to interact with a node. - -Each module can expose gRPC endpoints, called [service methods](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition) and are defined in the [module's Protobuf `query.proto` file](#grpc-query-services). A service method is defined by its name, input arguments and output response. The module then needs to: - -- define a `RegisterGRPCGatewayRoutes` method on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. -- for each service method, define a corresponding handler. The handler implements the core logic necessary to serve the gRPC request, and is located in the `keeper/grpc_query.go` file. - -#### gRPC-gateway REST Endpoints - -Some external clients may not wish to use gRPC. The SDK provides in this case a gRPC gateway service, which exposes each gRPC service as a correspoding REST endpoint. Please refer to the [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) documentation to learn more. - -The REST endpoints are defined in the Protobuf files, along with the gRPC services, using Protobuf annotations. Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods. By default, all REST endpoints defined in the SDK have an URL starting with the `/cosmos/` prefix. - -The SDK also provides a development endpoint to generate [Swagger](https://swagger.io/) definition files for these REST endpoints. This endpoint can be enabled inside the [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file, under the `api.swagger` key. - -#### Legacy API REST Endpoints - -The [module's Legacy REST interface](../building-modules/09-module-interfaces.md#legacy-rest) lets users generate transactions and query the state through REST calls to the application's Legacy API Service. REST routes are defined in a file `client/rest/rest.go`, which is composed of: - -- A `RegisterRoutes` function, which registers each route defined in the file. This function is called from the [main application's interface](#application-interfaces) for each module used within the application. The router used in the SDK is [Gorilla's mux](https://github.com/gorilla/mux). -- Custom request type definitions for each query or transaction creation function that needs to be exposed. These custom request types build on the base `request` type of the Cosmos SDK: - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/rest/rest.go#L62-L76 -- One handler function for each request that can be routed to the given module. These functions implement the core logic necessary to serve the request. - -These Legacy API endpoints are present in the SDK for backward compatibility purposes and will be removed in the next release. - -## Application Interface - -[Interfaces](#command-line-grpc-services-and-rest-interfaces) let end-users interact with full-node clients. This means querying data from the full-node or creating and sending new transactions to be relayed by the full-node and eventually included in a block. - -The main interface is the [Command-Line Interface](../core/06-cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application is the same as the daemon (e.g. `appd`), and defined in a file called `appd/main.go`. The file contains: - -- **A `main()` function**, which is executed to build the `appd` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appd`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`. -- **Query commands** are added by calling the `queryCmd` function. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appd query [query]` of the CLI. -- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appd tx [tx]` of the CLI. - -See an example of an application's main command-line file from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice) - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go - -## Dependencies and Makefile - -::: warning -A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. - -To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: - -``` -replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 -``` - -Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. -::: - -This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): - -+++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/go.mod#L1-L18 - -For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appd`](#application-interface). See an example of Makefile from the [nameservice tutorial](https://tutorials.cosmos.network/nameservice/tutorial/00-intro.html) - -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/Makefile - -## Next {hide} - -Learn more about the [Lifecycle of a transaction](./01-tx-lifecycle.md) {hide} diff --git a/versioned_docs/version-0.45/develop/high-level-concepts/01-tx-lifecycle.md b/versioned_docs/version-0.45/develop/high-level-concepts/01-tx-lifecycle.md deleted file mode 100644 index 06301418f..000000000 --- a/versioned_docs/version-0.45/develop/high-level-concepts/01-tx-lifecycle.md +++ /dev/null @@ -1,252 +0,0 @@ -# Transaction Lifecycle - -This document describes the lifecycle of a transaction from creation to committed state changes. Transaction definition is described in a [different doc](../advanced-concepts/01-transactions.md). The transaction will be referred to as `Tx`. {synopsis} - -### Pre-requisite Readings - -- [Anatomy of an SDK Application](./app-anatomy.md) {prereq} - -## Creation - -### Transaction Creation - -One of the main application interfaces is the command-line interface. The transaction `Tx` can be created by the user inputting a command in the following format from the [command-line](../advanced-concepts/06-cli.md), providing the type of transaction in `[command]`, arguments in `[args]`, and configurations such as gas prices in `[flags]`: - -```bash -[appname] tx [command] [args] [flags] -``` - -This command will automatically **create** the transaction, **sign** it using the account's private key, and **broadcast** it to the specified peer node. - -There are several required and optional flags for transaction creation. The `--from` flag specifies which [account](./03-accounts.md) the transaction is originating from. For example, if the transaction is sending coins, the funds will be drawn from the specified `from` address. - -#### Gas and Fees - -Additionally, there are several [flags](../advanced-concepts/06-cli.md) users can use to indicate how much they are willing to pay in [fees](./gas-fees.md): - -- `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`. -- `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. -- `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. -- `--fees` specifies how much in fees the user is willing to pay in total. -- `--timeout-height` specifies a block timeout height to prevent the tx from being committed past a certain height. - -The ultimate value of the fees paid is equal to the gas multiplied by the gas prices. In other words, `fees = ceil(gas * gasPrices)`. Thus, since fees can be calculated using gas prices and vice versa, the users specify only one of the two. - -Later, validators decide whether or not to include the transaction in their block by comparing the given or calculated `gas-prices` to their local `min-gas-prices`. `Tx` will be rejected if its `gas-prices` is not high enough, so users are incentivized to pay more. - -#### CLI Example - -Users of application `app` can enter the following command into their CLI to generate a transaction to send 1000uatom from a `senderAddress` to a `recipientAddress`. It specifies how much gas they are willing to pay: an automatic estimate scaled up by 1.5 times, with a gas price of 0.025uatom per unit gas. - -```bash -appd tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom -``` - -#### Other Transaction Creation Methods - -The command-line is an easy way to interact with an application, but `Tx` can also be created using a [gRPC or REST interface](../advanced-concepts/08-grpc_rest.md) or some other entrypoint defined by the application developer. From the user's perspective, the interaction depends on the web interface or wallet they are using (e.g. creating `Tx` using [Lunie.io](https://lunie.io/#/) and signing it with a Ledger Nano S). - -## Addition to Mempool - -Each full-node (running Tendermint) that receives a `Tx` sends an [ABCI message](https://tendermint.com/docs/spec/abci/abci.html#messages), -`CheckTx`, to the application layer to check for validity, and receives an `abci.ResponseCheckTx`. If the `Tx` passes the checks, it is held in the nodes' -[**Mempool**](https://tendermint.com/docs/tendermint-core/mempool.html#mempool), an in-memory pool of transactions unique to each node) pending inclusion in a block - honest nodes will discard `Tx` if it is found to be invalid. Prior to consensus, nodes continuously check incoming transactions and gossip them to their peers. - -### Types of Checks - -The full-nodes perform stateless, then stateful checks on `Tx` during `CheckTx`, with the goal to -identify and reject an invalid transaction as early on as possible to avoid wasted computation. - -**_Stateless_** checks do not require nodes to access state - light clients or offline nodes can do -them - and are thus less computationally expensive. Stateless checks include making sure addresses -are not empty, enforcing nonnegative numbers, and other logic specified in the definitions. - -**_Stateful_** checks validate transactions and messages based on a committed state. Examples -include checking that the relevant values exist and are able to be transacted with, the address -has sufficient funds, and the sender is authorized or has the correct ownership to transact. -At any given moment, full-nodes typically have [multiple versions](../../develop/advanced-concepts/00-baseapp.md#volatile-states) -of the application's internal state for different purposes. For example, nodes will execute state -changes while in the process of verifying transactions, but still need a copy of the last committed -state in order to answer queries - they should not respond using state with uncommitted changes. - -In order to verify a `Tx`, full-nodes call `CheckTx`, which includes both _stateless_ and _stateful_ -checks. Further validation happens later in the [`DeliverTx`](#delivertx) stage. `CheckTx` goes -through several steps, beginning with decoding `Tx`. - -### Decoding - -When `Tx` is received by the application from the underlying consensus engine (e.g. Tendermint), it is still in its [encoded](../advanced-concepts/05-encoding.md) `[]byte` form and needs to be unmarshaled in order to be processed. Then, the [`runTx`](../../develop/advanced-concepts/00-baseapp.md#runtx-and-runmsgs) function is called to run in `runTxModeCheck` mode, meaning the function will run all checks but exit before executing messages and writing state changes. - -### ValidateBasic - -Messages ([`sdk.Msg`](../core/01-transactions.md#messages)) are extracted from transactions (`Tx`). The `ValidateBasic` method of the `sdk.Msg` interface implemented by the module developer is run for each transaction. -To discard obviously invalid messages, the BaseApp` type calls the `ValidateBasic` method very early in the processing of the message in the [`CheckTx`](../../develop/advanced-concepts/00-baseapp.md#checktx) and [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx)) transactions. -`ValidateBasic` can include only **stateless** checks (the checks that do not require access to the state). - -#### Guideline - -Gas is not charged when `ValidateBasic` is executed so we recommend only performing all necessary stateless checks to enable middleware operations (for example, parsing the required signer accounts to validate a signature by a middleware) and stateless sanity checks not impacting performance of the CheckTx phase. -Other validation operations must be performed when [handling a message](../building-modules/msg-services#Validation) in a module Msg Server. - -Example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for non-empty addresses and a non-negative coin amount, but does not require knowledge of state such as the account balance of an address. - -See also [Msg Service Validation](../building-modules/03-msg-services.md#Validation). - -### AnteHandler - -After the ValidateBasic checks, the `AnteHandler`s are run. Technically, they are optional, but in practice, they are very often present to perform signature verification, gas calculation, fee deduction and other core operations related to blockchain transactions. - -A copy of the cached context is provided to the `AnteHandler`, which performs limited checks specified for the transaction type. Using a copy allows the AnteHandler to do stateful checks for `Tx` without modifying the last committed state, and revert back to the original if the execution fails. - -For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module `AnteHandler` checks and increments sequence numbers, checks signatures and account numbers, and deducts fees from the first signer of the transaction - all state changes are made using the `checkState`. - -### Gas - -The [`Context`](../core/02-context.md), which keeps a `GasMeter` that will track how much gas has been used during the execution of `Tx`, is initialized. The user-provided amount of gas for `Tx` is known as `GasWanted`. If `GasConsumed`, the amount of gas consumed so during execution, ever exceeds `GasWanted`, the execution will stop and the changes made to the cached copy of the state won't be committed. Otherwise, `CheckTx` sets `GasUsed` equal to `GasConsumed` and returns it in the result. After calculating the gas and fee values, validator-nodes check that the user-specified `gas-prices` is greater than their locally defined `min-gas-prices`. - -### Discard or Addition to Mempool - -If at any point during `CheckTx` the `Tx` fails, it is discarded and the transaction lifecycle ends -there. Otherwise, if it passes `CheckTx` successfully, the default protocol is to relay it to peer -nodes and add it to the Mempool so that the `Tx` becomes a candidate to be included in the next block. - -The **mempool** serves the purpose of keeping track of transactions seen by all full-nodes. -Full-nodes keep a **mempool cache** of the last `mempool.cache_size` transactions they have seen, as a first line of -defense to prevent replay attacks. Ideally, `mempool.cache_size` is large enough to encompass all -of the transactions in the full mempool. If the the mempool cache is too small to keep track of all -the transactions, `CheckTx` is responsible for identifying and rejecting replayed transactions. - -Currently existing preventative measures include fees and a `sequence` (nonce) counter to distinguish -replayed transactions from identical but valid ones. If an attacker tries to spam nodes with many -copies of a `Tx`, full-nodes keeping a mempool cache will reject identical copies instead of running -`CheckTx` on all of them. Even if the copies have incremented `sequence` numbers, attackers are -disincentivized by the need to pay fees. - -Validator nodes keep a mempool to prevent replay attacks, just as full-nodes do, but also use it as -a pool of unconfirmed transactions in preparation of block inclusion. Note that even if a `Tx` -passes all checks at this stage, it is still possible to be found invalid later on, because -`CheckTx` does not fully validate the transaction (i.e. it does not actually execute the messages). - -## Inclusion in a Block - -Consensus, the process through which validator nodes come to agreement on which transactions to -accept, happens in **rounds**. Each round begins with a proposer creating a block of the most -recent transactions and ends with **validators**, special full-nodes with voting power responsible -for consensus, agreeing to accept the block or go with a `nil` block instead. Validator nodes -execute the consensus algorithm, such as [Tendermint BFT](https://tendermint.com/docs/spec/consensus/consensus.html#terms), -confirming the transactions using ABCI requests to the application, in order to come to this agreement. - -The first step of consensus is the **block proposal**. One proposer amongst the validators is chosen -by the consensus algorithm to create and propose a block - in order for a `Tx` to be included, it -must be in this proposer's mempool. - -## State Changes - -The next step of consensus is to execute the transactions to fully validate them. All full-nodes -that receive a block proposal from the correct proposer execute the transactions by calling the ABCI functions -[`BeginBlock`](./app-anatomy.md#beginblocker-and-endblocker), `DeliverTx` for each transaction, -and [`EndBlock`](./app-anatomy.md#beginblocker-and-endblocker). While each full-node runs everything -locally, this process yields a single, unambiguous result, since the messages' state transitions are deterministic and transactions are -explicitly ordered in the block proposal. - -``` - ----------------------- - |Receive Block Proposal| - ----------------------- - | - v - ----------------------- - | BeginBlock | - ----------------------- - | - v - ----------------------- - | DeliverTx(tx0) | - | DeliverTx(tx1) | - | DeliverTx(tx2) | - | DeliverTx(tx3) | - | . | - | . | - | . | - ----------------------- - | - v - ----------------------- - | EndBlock | - ----------------------- - | - v - ----------------------- - | Consensus | - ----------------------- - | - v - ----------------------- - | Commit | - ----------------------- -``` - -### DeliverTx - -The `DeliverTx` ABCI function defined in [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md) does the bulk of the -state transitions: it is run for each transaction in the block in sequential order as committed -to during consensus. Under the hood, `DeliverTx` is almost identical to `CheckTx` but calls the -[`runTx`](../../develop/advanced-concepts/00-baseapp.md#runtx) function in deliver mode instead of check mode. -Instead of using their `checkState`, full-nodes use `deliverState`: - -- **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form. - Nodes first unmarshal the transaction, using the [`TxConfig`](./app-anatomy#register-codec) defined in the app, then call `runTx` in `runTxModeDeliver`, which is very similar to `CheckTx` but also executes and writes state changes. - -- **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check - happens because they may not have seen the same transactions during the addition to Mempool stage\ - and a malicious proposer may have included invalid ones. One difference here is that the - `AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local - to each node - differing values across nodes would yield nondeterministic results. - -- **`MsgServiceRouter`:** While `CheckTx` would have exited, `DeliverTx` continues to run - [`runMsgs`](../../develop/advanced-concepts/00-baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction. - Since the transaction may have messages from different modules, `BaseApp` needs to know which module - to find the appropriate handler. This is achieved using `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's Protobuf [`Msg` service](../building-modules/03-msg-services.md). - For `LegacyMsg` routing, the `Route` function is called via the [module manager](../building-modules/01-module-manager.md) to retrieve the route name and find the legacy [`Handler`](../building-modules/03-msg-services.md#handler-type) within the module. - -- **`Msg` service:** a Protobuf `Msg` service, a step up from `AnteHandler`, is responsible for executing each - message in the `Tx` and causes state transitions to persist in `deliverTxState`. - -- **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much - gas is being used; if execution completes, `GasUsed` is set and returned in the - `abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes - wrong, a deferred function at the end appropriately errors or panics. - -If there are any failed state changes resulting from a `Tx` being invalid or `GasMeter` running out, -the transaction processing terminates and any state changes are reverted. Invalid transactions in a -block proposal cause validator nodes to reject the block and vote for a `nil` block instead. - -### Commit - -The final step is for nodes to commit the block and state changes. Validator nodes -perform the previous step of executing state transitions in order to validate the transactions, -then sign the block to confirm it. Full nodes that are not validators do not -participate in consensus - i.e. they cannot vote - but listen for votes to understand whether or -not they should commit the state changes. - -When they receive enough validator votes (2/3+ _precommits_ weighted by voting power), full nodes commit to a new block to be added to the blockchain and -finalize the state transitions in the application layer. A new state root is generated to serve as -a merkle proof for the state transitions. Applications use the [`Commit`](../../develop/advanced-concepts/00-baseapp.md#commit) -ABCI method inherited from [Baseapp](../../develop/advanced-concepts/00-baseapp.md); it syncs all the state transitions by -writing the `deliverState` into the application's internal state. As soon as the state changes are -committed, `checkState` start afresh from the most recently committed state and `deliverState` -resets to `nil` in order to be consistent and reflect the changes. - -Note that not all blocks have the same number of transactions and it is possible for consensus to -result in a `nil` block or one with none at all. In a public blockchain network, it is also possible -for validators to be **byzantine**, or malicious, which may prevent a `Tx` from being committed in -the blockchain. Possible malicious behaviors include the proposer deciding to censor a `Tx` by -excluding it from the block or a validator voting against the block. - -At this point, the transaction lifecycle of a `Tx` is over: nodes have verified its validity, -delivered it by executing its state changes, and committed those changes. The `Tx` itself, -in `[]byte` form, is stored in a block and appended to the blockchain. - -## Next {hide} - -Learn about [accounts](./03-accounts.md) {hide} diff --git a/versioned_docs/version-0.45/develop/high-level-concepts/02-query-lifecycle.md b/versioned_docs/version-0.45/develop/high-level-concepts/02-query-lifecycle.md deleted file mode 100644 index 75303c44e..000000000 --- a/versioned_docs/version-0.45/develop/high-level-concepts/02-query-lifecycle.md +++ /dev/null @@ -1,148 +0,0 @@ -# Query Lifecycle - -This document describes the lifecycle of a query in a SDK application, from the user interface to application stores and back. {synopsis} - -## Pre-requisite Readings - -- [Transaction Lifecycle](./01-tx-lifecycle.md) {prereq} - -## Query Creation - -A [**query**](../building-modules/02-messages-and-queries.md#queries) is a request for information made by end-users of applications through an interface and processed by a full-node. Users can query information about the network, the application itself, and application state directly from the application's stores or modules. Note that queries are different from [transactions](../advanced-concepts/01-transactions.md) (view the lifecycle [here](./01-tx-lifecycle.md)), particularly in that they do not require consensus to be processed (as they do not trigger state-transitions); they can be fully handled by one full-node. - -For the purpose of explaining the query lifecycle, let's say `MyQuery` is requesting a list of delegations made by a certain delegator address in the application called `simapp`. As to be expected, the [`staking`](../../x/staking/spec/README.md) module handles this query. But first, there are a few ways `MyQuery` can be created by users. - -### CLI - -The main interface for an application is the command-line interface. Users connect to a full-node and run the CLI directly from their machines - the CLI interacts directly with the full-node. To create `MyQuery` from their terminal, users type the following command: - -```bash -simd query staking delegations -``` - -This query command was defined by the [`staking`](../../x/staking/spec/README.md) module developer and added to the list of subcommands by the application developer when creating the CLI. - -Note that the general format is as follows: - -```bash -simd query [moduleName] [command] --flag -``` - -To provide values such as `--node` (the full-node the CLI connects to), the user can use the [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file to set them or provide them as flags. - -The CLI understands a specific set of commands, defined in a hierarchical structure by the application developer: from the [root command](../advanced-concepts/06-cli.md#root-command) (`simd`), the type of command (`Myquery`), the module that contains the command (`staking`), and command itself (`delegations`). Thus, the CLI knows exactly which module handles this command and directly passes the call there. - -### gRPC - -::: warning -A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. - -To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: - -``` -replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 -``` - -Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. -::: - -Another interface through which users can make queries, introduced in Cosmos SDK v0.40, is [gRPC](https://grpc.io) requests to a [gRPC server](../advanced-concepts/08-grpc_rest.md#grpc-server). The endpoints are defined as [Protocol Buffers](https://developers.google.com/protocol-buffers) service methods inside `.proto` files, written in Protobuf's own language-agnostic interface definition language (IDL). The Protobuf ecosystem developed tools for code-generation from `*.proto` files into various languages. These tools allow to build gRPC clients easily. - -One such tool is [grpcurl](https://github.com/fullstorydev/grpcurl), and a gRPC request for `MyQuery` using this client looks like: - -```bash -grpcurl \ - -plaintext # We want results in plain test - -import-path ./proto \ # Import these .proto files - -proto ./proto/cosmos/staking/v1beta1/query.proto \ # Look into this .proto file for the Query protobuf service - -d '{"address":"$MY_DELEGATOR"}' \ # Query arguments - localhost:9090 \ # gRPC server endpoint - cosmos.staking.v1beta1.Query/Delegations # Fully-qualified service method name -``` - -### REST - -Another interface through which users can make queries is through HTTP Requests to a [REST server](../advanced-concepts/08-grpc_rest.md#rest-server). The REST server is fully auto-generated from Protobuf services, using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). - -An example HTTP request for `MyQuery` looks like: - -```bash -GET http://localhost:1317/cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations -``` - -## How Queries are Handled by the CLI - -The examples above show how an external user can interact with a node by querying its state. To understand more in details the exact lifecycle of a query, let's dig into how the CLI prepares the query, and how the node handles it. The interactions from the users' perspective are a bit different, but the underlying functions are almost identical because they are implementations of the same command defined by the module developer. This step of processing happens within the CLI, gRPC or REST server and heavily involves a `client.Context`. - -### Context - -The first thing that is created in the execution of a CLI command is a `client.Context`. A `client.Context` is an object that stores all the data needed to process a request on the user side. In particular, a `client.Context` stores the following: - -- **Codec**: The [encoder/decoder](../advanced-concepts/05-encoding.md) used by the application, used to marshal the parameters and query before making the Tendermint RPC request and unmarshal the returned response into a JSON object. The default codec used by the CLI is Protobuf. -- **Account Decoder**: The account decoder from the [`auth`](../..//x/auth/spec/README.md) module, which translates `[]byte`s into accounts. -- **RPC Client**: The Tendermint RPC Client, or node, to which the request will be relayed to. -- **Keyring**: A [Key Manager](../high-level-concepts/03-accounts.md#keyring) used to sign transactions and handle other operations with keys. -- **Output Writer**: A [Writer](https://golang.org/pkg/io/#Writer) used to output the response. -- **Configurations**: The flags configured by the user for this command, including `--height`, specifying the height of the blockchain to query and `--indent`, which indicates to add an indent to the JSON response. - -The `client.Context` also contains various functions such as `Query()` which retrieves the RPC Client and makes an ABCI call to relay a query to a full-node. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/context.go#L20-L50 - -The `client.Context`'s primary role is to store data used during interactions with the end-user and provide methods to interact with this data - it is used before and after the query is processed by the full-node. Specifically, in handling `MyQuery`, the `client.Context` is utilized to encode the query parameters, retrieve the full-node, and write the output. Prior to being relayed to a full-node, the query needs to be encoded into a `[]byte` form, as full-nodes are application-agnostic and do not understand specific types. The full-node (RPC Client) itself is retrieved using the `client.Context`, which knows which node the user CLI is connected to. The query is relayed to this full-node to be processed. Finally, the `client.Context` contains a `Writer` to write output when the response is returned. These steps are further described in later sections. - -### Arguments and Route Creation - -At this point in the lifecycle, the user has created a CLI command with all of the data they wish to include in their query. A `client.Context` exists to assist in the rest of the `MyQuery`'s journey. Now, the next step is to parse the command or request, extract the arguments, and encode everything. These steps all happen on the user side within the interface they are interacting with. - -#### Encoding - -In our case (querying an address's delegations), `MyQuery` contains an [address](./03-accounts.md#addresses) `delegatorAddress` as its only argument. However, the request can only contain `[]byte`s, as it will be relayed to a consensus engine (e.g. Tendermint Core) of a full-node that has no inherent knowledge of the application types. Thus, the `codec` of `client.Context` is used to marshal the address. - -Here is what the code looks like for the CLI command: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/staking/client/cli/query.go#L324-L327 - -#### gRPC Query Client Creation - -The SDK leverages code generated from Protobuf services to make queries. The `staking` module's `MyQuery` service generates a `queryClient`, which the CLI will use to make queries. Here is the relevant code: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/staking/client/cli/query.go#L318-L342 - -Under the hood, the `client.Context` has a `Query()` function used to retrieve the pre-configured node and relay a query to it; the function takes the query fully-qualified service method name as path (in our case: `/cosmos.staking.v1beta1.Query/Delegations`), and arguments as parameters. It first retrieves the RPC Client (called the [**node**](../advanced-concepts/03-node.md)) configured by the user to relay this query to, and creates the `ABCIQueryOptions` (parameters formatted for the ABCI call). The node is then used to make the ABCI call, `ABCIQueryWithOptions()`. - -Here is what the code looks like: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/query.go#L65-L91 - -## RPC - -With a call to `ABCIQueryWithOptions()`, `MyQuery` is received by a [full-node](../advanced-concepts/05-encoding.md) which will then process the request. Note that, while the RPC is made to the consensus engine (e.g. Tendermint Core) of a full-node, queries are not part of consensus and will not be broadcasted to the rest of the network, as they do not require anything the network needs to agree upon. - -Read more about ABCI Clients and Tendermint RPC in the Tendermint documentation [here](https://tendermint.com/rpc). - -## Application Query Handling - -When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../../develop/advanced-concepts/00-baseapp.md) implements the ABCI [`Query()`](../../develop/advanced-concepts/00-baseapp.md#query) function and handles gRPC queries. The query route is parsed, and it it matches the fully-qualified service method name of an existing service method (most likely in one of the modules), then `baseapp` will relay the request to the relevant module. - -Apart from gRPC routes, `baseapp` also handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [legacy queriers](../building-modules/04-query-services.md#legacy-queriers). To learn more about these queries, please refer to [this guide](../advanced-concepts/08-grpc_rest.md#tendermint-rpc). - -Since `MyQuery` has a Protobuf fully-qualified service method name from the `staking` module (recall `/cosmos.staking.v1beta1.Query/Delegations`), `baseapp` first parses the path, then uses its own internal `GRPCQueryRouter` to retrieve the corresponding gRPC handler, and routes the query to the module. The gRPC handler is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about query services [here](../building-modules/04-query-services.md). - -Once a result is received from the querier, `baseapp` begins the process of returning a response to the user. - -## Response - -Since `Query()` is an ABCI function, `baseapp` returns the response as an [`abci.ResponseQuery`](https://tendermint.com/docs/spec/abci/abci.html#messages) type. The `client.Context` `Query()` routine receives the response and. - -### CLI Response - -The application [`codec`](../advanced-concepts/05-encoding.md) is used to unmarshal the response to a JSON and the `client.Context` prints the output to the command line, applying any configurations such as the output type (text, JSON or YAML). - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/client/context.go#L248-L283 - -And that's a wrap! The result of the query is outputted to the console by the CLI. - -## Next {hide} - -Read more about [accounts](./03-accounts.md). {hide} diff --git a/versioned_docs/version-0.45/develop/high-level-concepts/03-accounts.md b/versioned_docs/version-0.45/develop/high-level-concepts/03-accounts.md deleted file mode 100644 index 6d372400f..000000000 --- a/versioned_docs/version-0.45/develop/high-level-concepts/03-accounts.md +++ /dev/null @@ -1,149 +0,0 @@ -# Accounts - -This document describes the in-built account and public key system of the Cosmos SDK. {synopsis} - -### Pre-requisite Readings - -- [Anatomy of an SDK Application](./app-anatomy.md) {prereq} - -## Account Definition - -In the Cosmos SDK, an _account_ designates a pair of _public key_ `PubKey` and _private key_ `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/02-messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`. - -For HD key derivation the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). The BIP32 allows users to create an HD wallet (as specified in [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) - a set of accounts derived from an initial secret seed. A seed is usually created from a 12- or 24-word mnemonic. A single seed can derive any number of `PrivKey`s using a one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved. - -``` - Account 0 Account 1 Account 2 - -+------------------+ +------------------+ +------------------+ -| | | | | | -| Address 0 | | Address 1 | | Address 2 | -| ^ | | ^ | | ^ | -| | | | | | | | | -| | | | | | | | | -| | | | | | | | | -| + | | + | | + | -| Public key 0 | | Public key 1 | | Public key 2 | -| ^ | | ^ | | ^ | -| | | | | | | | | -| | | | | | | | | -| | | | | | | | | -| + | | + | | + | -| Private key 0 | | Private key 1 | | Private key 2 | -| ^ | | ^ | | ^ | -+------------------+ +------------------+ +------------------+ - | | | - | | | - | | | - +--------------------------------------------------------------------+ - | - | - +---------+---------+ - | | - | Master PrivKey | - | | - +-------------------+ - | - | - +---------+---------+ - | | - | Mnemonic (Seed) | - | | - +-------------------+ -``` - -In the Cosmos SDK, keys are stored and managed by using an object called a [`Keyring`](#keyring). - -## Keys, accounts, addresses, and signatures - -The principal way of authenticating a user is done using [digital signatures](https://en.wikipedia.org/wiki/Digital_signature). Users sign transactions using their own private key. Signature verification is done with the associated public key. For on-chain signature verification purposes, we store the public key in an `Account` object (alongside other data required for a proper transaction validation). - -In the node, all data is stored using Protocol Buffers serialization. - -The Cosmos SDK supports the following digital key schemes for creating digital signatures: - -- `secp256k1`, as implemented in the [SDK's `crypto/keys/secp256k1` package](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/crypto/keys/secp256k1/secp256k1.go). -- `secp256r1`, as implemented in the [SDK's `crypto/keys/secp256r1` package](https://github.com/cosmos/cosmos-sdk/blob/master/crypto/keys/secp256r1/pubkey.go), -- `tm-ed25519`, as implemented in the [SDK `crypto/keys/ed25519` package](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/crypto/keys/ed25519/ed25519.go). This scheme is supported only for the consensus validation. - -| | Address length | Public key length | Used for transaction | Used for consensus | -| | in bytes | in bytes | authentication | (tendermint) | -|--------------+----------------+-------------------+----------------------+--------------------| -| `secp256k1` | 20 | 33 | yes | no | -| `secp256r1` | 32 | 33 | yes | no | -| `tm-ed25519` | -- not used -- | 32 | no | yes | - -## Addresses - -`Addresses` and `PubKey`s are both public information that identifies actors in the application. `Account` is used to store authentication information. The basic account implementation is provided by a `BaseAccount` object. - -Each account is identified using `Address` which is a sequence of bytes derived from a public key. In SDK, we define 3 types of addresses that specify a context where an account is used: - -- `AccAddress` identifies users (the sender of a `message`). -- `ValAddress` identifies validator operators. -- `ConsAddress` identifies validator nodes that are participating in consensus. Validator nodes are derived using the **`ed25519`** curve. - -These types implement the `Address` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/types/address.go#L71-L90 - -Address construction algorithm is defined in [ADR-28](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-028-public-key-addresses.md). -Here is the standard way to obtain an account address from a `pub` public key: - -```go -sdk.AccAddress(pub.Address().Bytes()) -``` - -Of note, the `Marshal()` and `Bytes()` method both return the same raw `[]byte` form of the address. `Marshal()` is required for Protobuf compatibility. - -For user interaction, addresses are formatted using [Bech32](https://en.bitcoin.it/wiki/Bech32) and implemented by the `String` method. The Bech32 method is the only supported format to use when interacting with a blockchain. The Bech32 human-readable part (Bech32 prefix) is used to denote an address type. Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/types/address.go#L230-L244 - -| | Address Bech32 Prefix | -| ------------------ | --------------------- | -| Accounts | cosmos | -| Validator Operator | cosmosvaloper | -| Consensus Nodes | cosmosvalcons | - -### Public Keys - -Public keys in Cosmos SDK are defined by `cryptotypes.PubKey` interface. Since public keys are saved in a store, `cryptotypes.PubKey` extends the `proto.Message` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/crypto/types/types.go#L8-L17 - -A compressed format is used for `secp256k1` and `secp256r1` serialization. - -- The first byte is a `0x02` byte if the `y`-coordinate is the lexicographically largest of the two associated with the `x`-coordinate. -- Otherwise the first byte is a `0x03`. - -This prefix is followed by the `x`-coordinate. - -Public Keys are not used to reference accounts (or users) and in general are not used when composing transaction messages (with few exceptions: `MsgCreateValidator`, `Validator` and `Multisig` messages). -For user interactions, `PubKey` is formatted using Protobufs JSON ([ProtoMarshalJSON](https://github.com/cosmos/cosmos-sdk/blob/release/v0.42.x/codec/json.go#L12) function). Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7568b66/crypto/keyring/output.go#L23-L39 - -## Keyring - -A `Keyring` is an object that stores and manages accounts. In the Cosmos SDK, a `Keyring` implementation follows the `Keyring` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/crypto/keyring/keyring.go#L51-L89 - -The default implementation of `Keyring` comes from the third-party [`99designs/keyring`](https://github.com/99designs/keyring) library. - -A few notes on the `Keyring` methods: - -- `Sign(uid string, payload []byte) ([]byte, sdkcrypto.PubKey, error)` strictly deals with the signature of the `payload` bytes. You must prepare and encode the transaction into a canonical `[]byte` form. Because protobuf is not deterministic, it has been decided in [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) that the canonical `payload` to sign is the `SignDoc` struct, deterministically encoded using [ADR-027](adr-027-deterministic-protobuf-serialization.md). Note that signature verification is not implemented in the SDK by default, it is deferred to the [`anteHandler`](../../develop/advanced-concepts/00-baseapp.md#antehandler). - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/proto/cosmos/tx/v1beta1/tx.proto#L47-L64 - -- `NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk. The `PrivKey` is **never stored unencrypted**, instead it is [encrypted with a passphrase](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/armor.go) before being persisted. In the context of this method, the key type and sequence number refer to the segment of the BIP44 derivation path (for example, `0`, `1`, `2`, ...) that is used to derive a private and a public key from the mnemonic. Using the same mnemonic and derivation path, the same `PrivKey`, `PubKey` and `Address` is generated. The following keys are supported by the keyring: - -- `secp256k1` -- `ed25519` - -- `ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)` exports a private key in ASCII-armored encrypted format using the given passphrase. You can then either import the private key again into the keyring using the `ImportPrivKey(uid, armor, passphrase string)` function or decrypt it into a raw private key using the `UnarmorDecryptPrivKey(armorStr string, passphrase string)` function. - -## Next {hide} - -Learn about [gas and fees](./gas-fees.md) {hide} diff --git a/versioned_docs/version-0.45/develop/high-level-concepts/04-gas-fees.md b/versioned_docs/version-0.45/develop/high-level-concepts/04-gas-fees.md deleted file mode 100644 index f319b6a6d..000000000 --- a/versioned_docs/version-0.45/develop/high-level-concepts/04-gas-fees.md +++ /dev/null @@ -1,85 +0,0 @@ -# Gas and Fees - -This document describes the default strategies to handle gas and fees within a Cosmos SDK application. {synopsis} - -### Pre-requisite Readings - -- [Anatomy of an SDK Application](./app-anatomy.md) {prereq} - -## Introduction to `Gas` and `Fees` - -In the Cosmos SDK, `gas` is a special unit that is used to track the consumption of resources during execution. `gas` is typically consumed whenever read and writes are made to the store, but it can also be consumed if expensive computation needs to be done. It serves two main purposes: - -- Make sure blocks are not consuming too many resources and will be finalized. This is implemented by default in the SDK via the [block gas meter](#block-gas-meter). -- Prevent spam and abuse from end-user. To this end, `gas` consumed during [`message`](../building-modules/02-messages-and-queries.md#messages) execution is typically priced, resulting in a `fee` (`fees = gas * gas-prices`). `fees` generally have to be paid by the sender of the `message`. Note that the SDK does not enforce `gas` pricing by default, as there may be other ways to prevent spam (e.g. bandwidth schemes). Still, most applications will implement `fee` mechanisms to prevent spam. This is done via the [`AnteHandler`](#antehandler). - -## Gas Meter - -In the Cosmos SDK, `gas` is a simple alias for `uint64`, and is managed by an object called a _gas meter_. Gas meters implement the `GasMeter` interface - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L34-L43 - -where: - -- `GasConsumed()` returns the amount of gas that was consumed by the gas meter instance. -- `GasConsumedToLimit()` returns the amount of gas that was consumed by gas meter instance, or the limit if it is reached. -- `Limit()` returns the limit of the gas meter instance. `0` if the gas meter is infinite. -- `ConsumeGas(amount Gas, descriptor string)` consumes the amount of `gas` provided. If the `gas` overflows, it panics with the `descriptor` message. If the gas meter is not infinite, it panics if `gas` consumed goes above the limit. -- `IsPastLimit()` returns `true` if the amount of gas consumed by the gas meter instance is strictly above the limit, `false` otherwise. -- `IsOutOfGas()` returns `true` if the amount of gas consumed by the gas meter instance is above or equal to the limit, `false` otherwise. - -The gas meter is generally held in [`ctx`](../01-tx-lifecycle.md02-context.md), and consuming gas is done with the following pattern: - -```go -ctx.GasMeter().ConsumeGas(amount, "description") -``` - -By default, the Cosmos SDK makes use of two different gas meters, the [main gas meter](#main-gas-metter[) and the [block gas meter](#block-gas-meter). - -### Main Gas Meter - -`ctx.GasMeter()` is the main gas meter of the application. The main gas meter is initialized in `BeginBlock` via `setDeliverState`, and then tracks gas consumption during execution sequences that lead to state-transitions, i.e. those originally triggered by [`BeginBlock`](../../develop/advanced-concepts/00-baseapp.md#beginblock), [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx) and [`EndBlock`](../../develop/advanced-concepts/00-baseapp.md#endblock). At the beginning of each `DeliverTx`, the main gas meter **must be set to 0** in the [`AnteHandler`](#antehandler), so that it can track gas consumption per-transaction. - -Gas consumption can be done manually, generally by the module developer in the [`BeginBlocker`, `EndBlocker`](../building-modules/beginblock-endblock.md) or [`Msg` service](../building-modules/03-msg-services.md), but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called [`GasKv`](../01-tx-lifecycle.md04-store.md#gaskv-store). - -### Block Gas Meter - -`ctx.BlockGasMeter()` is the gas meter used to track gas consumption per block and make sure it does not go above a certain limit. A new instance of the `BlockGasMeter` is created each time [`BeginBlock`](../../develop/advanced-concepts/00-baseapp.md#beginblock) is called. The `BlockGasMeter` is finite, and the limit of gas per block is defined in the application's consensus parameters. By default Cosmos SDK applications use the default consensus parameters provided by Tendermint: - -+++ https://github.com/tendermint/tendermint/blob/v0.34.0-rc6/types/params.go#L34-L41 - -When a new [transaction](../01-tx-lifecycle.md01-transactions.md) is being processed via `DeliverTx`, the current value of `BlockGasMeter` is checked to see if it is above the limit. If it is, `DeliverTx` returns immediately. This can happen even with the first transaction in a block, as `BeginBlock` itself can consume gas. If not, the transaction is processed normally. At the end of `DeliverTx`, the gas tracked by `ctx.BlockGasMeter()` is increased by the amount consumed to process the transaction: - -```go -ctx.BlockGasMeter().ConsumeGas( - ctx.GasMeter().GasConsumedToLimit(), - "block gas meter", -) -``` - -## AnteHandler - -The `AnteHandler` is run for every transaction during `CheckTx` and `DeliverTx`, before a Protobuf `Msg` service method for each `sdk.Msg` in the transaction. `AnteHandler`s have the following signature: - -```go -// AnteHandler authenticates transactions, before their internal messages are handled. -// If newCtx.IsZero(), ctx is used instead. -type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, result Result, abort bool) -``` - -The `anteHandler` is not implemented in the core SDK but in a module. This gives the possibility to developers to choose which version of `AnteHandler` fits their application's needs. That said, most applications today use the default implementation defined in the [`auth` module](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth). Here is what the `anteHandler` is intended to do in a normal Cosmos SDK application: - -- Verify that the transaction are of the correct type. Transaction types are defined in the module that implements the `anteHandler`, and they follow the transaction interface: - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/tx_msg.go#L49-L57 - This enables developers to play with various types for the transaction of their application. In the default `auth` module, the default transaction type is `Tx`: - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L12-L25 -- Verify signatures for each [`message`](../building-modules/02-messages-and-queries.md#messages) contained in the transaction. Each `message` should be signed by one or multiple sender(s), and these signatures must be verified in the `anteHandler`. -- During `CheckTx`, verify that the gas prices provided with the transaction is greater than the local `min-gas-prices` (as a reminder, gas-prices can be deducted from the following equation: `fees = gas * gas-prices`). `min-gas-prices` is a parameter local to each full-node and used during `CheckTx` to discard transactions that do not provide a minimum amount of fees. This ensure that the mempool cannot be spammed with garbage transactions. -- Verify that the sender of the transaction has enough funds to cover for the `fees`. When the end-user generates a transaction, they must indicate 2 of the 3 following parameters (the third one being implicit): `fees`, `gas` and `gas-prices`. This signals how much they are willing to pay for nodes to execute their transaction. The provided `gas` value is stored in a parameter called `GasWanted` for later use. -- Set `newCtx.GasMeter` to 0, with a limit of `GasWanted`. **This step is extremely important**, as it not only makes sure the transaction cannot consume infinite gas, but also that `ctx.GasMeter` is reset in-between each `DeliverTx` (`ctx` is set to `newCtx` after `anteHandler` is run, and the `anteHandler` is run each time `DeliverTx` is called). - -As explained above, the `anteHandler` returns a maximum limit of `gas` the transaction can consume during execution called `GasWanted`. The actual amount consumed in the end is denominated `GasUsed`, and we must therefore have `GasUsed =< GasWanted`. Both `GasWanted` and `GasUsed` are relayed to the underlying consensus engine when [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx) returns. - -## Next {hide} - -Learn about [baseapp](../../develop/advanced-concepts/00-baseapp.md) {hide} diff --git a/versioned_docs/version-0.45/develop/high-level-concepts/_category_.json b/versioned_docs/version-0.45/develop/high-level-concepts/_category_.json deleted file mode 100644 index 2c4105ee6..000000000 --- a/versioned_docs/version-0.45/develop/high-level-concepts/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "High Level Concepts", - "position": 1, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/develop/intro/overview.md b/versioned_docs/version-0.45/develop/intro/overview.md deleted file mode 100644 index 4040cdd99..000000000 --- a/versioned_docs/version-0.45/develop/intro/overview.md +++ /dev/null @@ -1,33 +0,0 @@ -# High-level Overview - -## What is the SDK? - -The [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk) is an open-source framework for building multi-asset public Proof-of-Stake (PoS) blockchains, like the Cosmos Hub, as well as permissioned Proof-Of-Authority (PoA) blockchains. Blockchains built with the Cosmos SDK are generally referred to as **application-specific blockchains**. - -The goal of the Cosmos SDK is to allow developers to easily create custom blockchains from scratch that can natively interoperate with other blockchains. We envision the SDK as the npm-like framework to build secure blockchain applications on top of [Tendermint](https://github.com/tendermint/tendermint). SDK-based blockchains are built out of composable [modules](../building-modules/intro.md), most of which are open source and readily available for any developers to use. Anyone can create a module for the Cosmos-SDK, and integrating already-built modules is as simple as importing them into your blockchain application. What's more, the Cosmos SDK is a capabilities-based system, which allows developers to better reason about the security of interactions between modules. For a deeper look at capabilities, jump to [this section](../01-tx-lifecycle.mdocap.md). - -## What are Application-Specific Blockchains? - -One development paradigm in the blockchain world today is that of virtual-machine blockchains like Ethereum, where development generally revolves around building a decentralised applications on top of an existing blockchain as a set of smart contracts. While smart contracts can be very good for some use cases like single-use applications (e.g. ICOs), they often fall short for building complex decentralised platforms. More generally, smart contracts can be limiting in terms of flexibility, sovereignty and performance. - -Application-specific blockchains offer a radically different development paradigm than virtual-machine blockchains. An application-specific blockchain is a blockchain customized to operate a single application: developers have all the freedom to make the design decisions required for the application to run optimally. They can also provide better sovereignty, security and performance. - -Learn more about [application-specific blockchains](./why-app-specific.md). - -## Why the Cosmos SDK? - -The Cosmos SDK is the most advanced framework for building custom application-specific blockchains today. Here are a few reasons why you might want to consider building your decentralised application with the Cosmos SDK: - -- The default consensus engine available within the SDK is [Tendermint Core](https://github.com/tendermint/tendermint). Tendermint is the most (and only) mature BFT consensus engine in existence. It is widely used across the industry and is considered the gold standard consensus engine for building Proof-of-Stake systems. -- The SDK is open source and designed to make it easy to build blockchains out of composable [modules](../../x/). As the ecosystem of open source SDK modules grows, it will become increasingly easier to build complex decentralised platforms with it. -- The SDK is inspired by capabilities-based security, and informed by years of wrestling with blockchain state-machines. This makes the Cosmos SDK a very secure environment to build blockchains. -- Most importantly, the Cosmos SDK has already been used to build many application-specific blockchains that are already in production. Among others, we can cite [Cosmos Hub](https://hub.cosmos.network), [IRIS Hub](https://irisnet.org), [Binance Chain](https://docs.binance.org/), [Terra](https://terra.money/) or [Kava](https://www.kava.io/). [Many more](https://cosmos.network/ecosystem) are building on the Cosmos SDK. - -## Getting started with the Cosmos SDK - -- Learn more about the [architecture of an SDK application](./sdk-app-architecture.md) -- Learn how to build an application-specific blockchain from scratch with the [SDK Tutorial](https://cosmos.network/docs/tutorial) - -## Next {hide} - -Learn about [application-specific blockchains](./why-app-specific.md) {hide} diff --git a/versioned_docs/version-0.45/develop/intro/sdk-app-architecture.md b/versioned_docs/version-0.45/develop/intro/sdk-app-architecture.md deleted file mode 100644 index 803756d6c..000000000 --- a/versioned_docs/version-0.45/develop/intro/sdk-app-architecture.md +++ /dev/null @@ -1,93 +0,0 @@ -# Blockchain Architecture - -## State machine - -At its core, a blockchain is a [replicated deterministic state machine](https://en.wikipedia.org/wiki/State_machine_replication). - -A state machine is a computer science concept whereby a machine can have multiple states, but only one at any given time. There is a `state`, which describes the current state of the system, and `transactions`, that trigger state transitions. - -Given a state S and a transaction T, the state machine will return a new state S'. - -``` -+--------+ +--------+ -| | | | -| S +---------------->+ S' | -| | apply(T) | | -+--------+ +--------+ -``` - -In practice, the transactions are bundled in blocks to make the process more efficient. Given a state S and a block of transactions B, the state machine will return a new state S'. - -``` -+--------+ +--------+ -| | | | -| S +----------------------------> | S' | -| | For each T in B: apply(T) | | -+--------+ +--------+ -``` - -In a blockchain context, the state machine is deterministic. This means that if a node is started at a given state and replays the same sequence of transactions, it will always end up with the same final state. - -The Cosmos SDK gives developers maximum flexibility to define the state of their application, transaction types and state transition functions. The process of building state-machines with the SDK will be described more in depth in the following sections. But first, let us see how the state-machine is replicated using **Tendermint**. - -## Tendermint - -Thanks to the Cosmos SDK, developers just have to define the state machine, and [*Tendermint*](https://tendermint.com/docs/introduction/what-is-tendermint.html) will handle replication over the network for them. - -``` - ^ +-------------------------------+ ^ - | | | | Built with Cosmos SDK - | | State-machine = Application | | - | | | v - | +-------------------------------+ - | | | ^ -Blockchain node | | Consensus | | - | | | | - | +-------------------------------+ | Tendermint Core - | | | | - | | Networking | | - | | | | - v +-------------------------------+ v -``` - -[Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions. - -The Tendermint [consensus algorithm](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine. - -## ABCI - -Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/), which the application must implement. - -``` - +---------------------+ - | | - | Application | - | | - +--------+---+--------+ - ^ | - | | ABCI - | v - +--------+---+--------+ - | | - | | - | Tendermint | - | | - | | - +---------------------+ -``` - -Note that **Tendermint only handles transaction bytes**. It has no knowledge of what these bytes mean. All Tendermint does is order these transaction bytes deterministically. Tendermint passes the bytes to the application via the ABCI, and expects a return code to inform it if the messages contained in the transactions were successfully processed or not. - -Here are the most important messages of the ABCI: - -- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../high-level-concepts/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/v0.34/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet. -- `DeliverTx`: When a [valid block](https://docs.tendermint.com/v0.34/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service](../building-modules/03-msg-services.md) RPC for each message in the transaction. -- `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transaction or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite. - -Find a more detailed view of the ABCI methods from the [Tendermint docs](https://docs.tendermint.com/v0.34/spec/abci/abci.html#overview). - -Any application built on Tendermint needs to implement the ABCI interface in order to communicate with the underlying local Tendermint engine. Fortunately, you do not have to implement the ABCI interface. The Cosmos SDK provides a boilerplate implementation of it in the form of [baseapp](./sdk-design.md#baseapp). - -## Next {hide} - -Read about the [high-level design principles of the SDK](./sdk-design.md) {hide} diff --git a/versioned_docs/version-0.45/develop/intro/sdk-design.md b/versioned_docs/version-0.45/develop/intro/sdk-design.md deleted file mode 100644 index a598b8ee8..000000000 --- a/versioned_docs/version-0.45/develop/intro/sdk-design.md +++ /dev/null @@ -1,91 +0,0 @@ -# Main Components of the Cosmos SDK - -The Cosmos SDK is a framework that facilitates the development of secure state-machines on top of Tendermint. At its core, the SDK is a boilerplate implementation of the [ABCI](./sdk-app-architecture.md#abci) in Golang. It comes with a [`multistore`](../01-tx-lifecycle.md04-store.md#multistore) to persist data and a [`router`](../../develop/advanced-concepts/00-baseapp.md#routing) to handle transactions. - -Here is a simplified view of how transactions are handled by an application built on top of the Cosmos SDK when transferred from Tendermint via `DeliverTx`: - -1. Decode `transactions` received from the Tendermint consensus engine (remember that Tendermint only deals with `[]bytes`). -2. Extract `messages` from `transactions` and do basic sanity checks. -3. Route each message to the appropriate module so that it can be processed. -4. Commit state changes. - -## `baseapp` - -`baseapp` is the boilerplate implementation of a Cosmos SDK application. It comes with an implementation of the ABCI to handle the connection with the underlying consensus engine. Typically, a Cosmos SDK application extends `baseapp` by embedding it in [`app.go`](../high-level-concepts/app-anatomy.md#core-application-file). See an example of this from the SDK application tutorial: - -+++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/app.go#L72-L92 - -The goal of `baseapp` is to provide a secure interface between the store and the extensible state machine while defining as little about the state machine as possible (staying true to the ABCI). - -For more on `baseapp`, please click [here](../../develop/advanced-concepts/00-baseapp.md). - -## Multistore - -The Cosmos SDK provides a [`multistore`](../01-tx-lifecycle.md04-store.md#multistore) for persisting state. The multistore allows developers to declare any number of [`KVStores`](../01-tx-lifecycle.md04-store.md#base-layer-kvstores). These `KVStores` only accept the `[]byte` type as value and therefore any custom structure needs to be marshalled using [a codec](../advanced-concepts/05-encoding.md) before being stored. - -The multistore abstraction is used to divide the state in distinct compartments, each managed by its own module. For more on the multistore, click [here](../advanced-concepts/04-store.md#multistore) - -## Modules - -The power of the Cosmos SDK lies in its modularity. SDK applications are built by aggregating a collection of interoperable modules. Each module defines a subset of the state and contains its own message/transaction processor, while the SDK is responsible for routing each message to its respective module. - -Here is a simplified view of how a transaction is processed by the application of each full-node when it is received in a valid block: - -``` - + - | - | Transaction relayed from the full-node's - | Tendermint engine to the node's application - | via DeliverTx - | - | - +---------------------v--------------------------+ - | APPLICATION | - | | - | Using baseapp's methods: Decode the Tx, | - | extract and route the message(s) | - | | - +---------------------+--------------------------+ - | - | - | - +---------------------------+ - | - | - | Message routed to - | the correct module - | to be processed - | - | -+----------------+ +---------------+ +----------------+ +------v----------+ -| | | | | | | | -| AUTH MODULE | | BANK MODULE | | STAKING MODULE | | GOV MODULE | -| | | | | | | | -| | | | | | | Handles message,| -| | | | | | | Updates state | -| | | | | | | | -+----------------+ +---------------+ +----------------+ +------+----------+ - | - | - | - | - +--------------------------+ - | - | Return result to Tendermint - | (0=Ok, 1=Err) - v -``` - -Each module can be seen as a little state-machine. Developers need to define the subset of the state handled by the module, as well as custom message types that modify the state (*Note:* `messages` are extracted from `transactions` by `baseapp`). In general, each module declares its own `KVStore` in the `multistore` to persist the subset of the state it defines. Most developers will need to access other 3rd party modules when building their own modules. Given that the Cosmos-SDK is an open framework, some of the modules may be malicious, which means there is a need for security principles to reason about inter-module interactions. These principles are based on [object-capabilities](../advanced-concepts/ocap.md). In practice, this means that instead of having each module keep an access control list for other modules, each module implements special objects called `keepers` that can be passed to other modules to grant a pre-defined set of capabilities. - -SDK modules are defined in the `x/` folder of the SDK. Some core modules include: - -- `x/auth`: Used to manage accounts and signatures. -- `x/bank`: Used to enable tokens and token transfers. -- `x/staking` + `x/slashing`: Used to build Proof-Of-Stake blockchains. - -In addition to the already existing modules in `x/`, that anyone can use in their app, the SDK lets you build your own custom modules. You can check an [example of that in the tutorial](https://cosmos.network/docs/tutorial/keeper.html). - -## Next {hide} - -Learn more about the [anatomy of an SDK application](../high-level-concepts/app-anatomy.md) {hide} diff --git a/versioned_docs/version-0.45/develop/intro/why-app-specific.md b/versioned_docs/version-0.45/develop/intro/why-app-specific.md deleted file mode 100644 index f9d53c232..000000000 --- a/versioned_docs/version-0.45/develop/intro/why-app-specific.md +++ /dev/null @@ -1,77 +0,0 @@ -# Application-Specific Blockchains - -This document explains what application-specific blockchains are, and why developers would want to build one as opposed to writing Smart Contracts. {synopsis} - -## What are application-specific blockchains? - -Application-specific blockchains are blockchains customized to operate a single application. Instead of building a decentralised application on top of an underlying blockchain like Ethereum, developers build their own blockchain from the ground up. This means building a full-node client, a light-client, and all the necessary interfaces (CLI, REST, ...) to interract with the nodes. - -``` - ^ +-------------------------------+ ^ - | | | | Built with Cosmos SDK - | | State-machine = Application | | - | | | v - | +-------------------------------+ - | | | ^ -Blockchain node | | Consensus | | - | | | | - | +-------------------------------+ | Tendermint Core - | | | | - | | Networking | | - | | | | - v +-------------------------------+ v -``` - -## What are the shortcomings of Smart Contracts? - -Virtual-machine blockchains like Ethereum addressed the demand for more programmability back in 2014. At the time, the options available for building decentralised applications were quite limited. Most developers would build on top of the complex and limited Bitcoin scripting language, or fork the Bitcoin codebase which was hard to work with and customize. - -Virtual-machine blockchains came in with a new value proposition. Their state-machine incorporates a virtual-machine that is able to interpret turing-complete programs called Smart Contracts. These Smart Contracts are very good for use cases like one-time events (e.g. ICOs), but they can fall short for building complex decentralised platforms. Here is why: - -- Smart Contracts are generally developed with specific programming languages that can be interpreted by the underlying virtual-machine. These programming languages are often immature and inherently limited by the constraints of the virtual-machine itself. For example, the Ethereum Virtual Machine does not allow developers to implement automatic execution of code. Developers are also limited to the account-based system of the EVM, and they can only choose from a limited set of functions for their cryptographic operations. These are examples, but they hint at the lack of **flexibility** that a smart contract environment often entails. -- Smart Contracts are all run by the same virtual machine. This means that they compete for resources, which can severly restrain **performance**. And even if the state-machine were to be split in multiple subsets (e.g. via sharding), Smart Contracts would still need to be interpeted by a virtual machine, which would limit performance compared to a native application implemented at state-machine level (our benchmarks show an improvement on the order of x10 in performance when the virtual-machine is removed). -- Another issue with the fact that Smart Contracts share the same underlying environment is the resulting limitation in **sovereignty**. A decentralised application is an ecosystem that involves multiple players. If the application is built on a general-purpose virtual-machine blockchain, stakeholders have very limited sovereignty over their application, and are ultimately superseded by the governance of the underlying blockchain. If there is a bug in the application, very little can be done about it. - -Application-Specific Blockchains are designed to address these shortcomings. - -## Application-Specific Blockchains Benefits - -### Flexibility - -Application-specific blockchains give maximum flexibility to developers: - -- In Cosmos blockchains, 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/). This interface can be wrapped in any programming language, meaning developers can build their state-machine in the programming language of their choice. - -- Developers can choose among multiple frameworks to build their state-machine. The most widely used today is the Cosmos SDK, but others exist (e.g. [Lotion](https://github.com/nomic-io/lotion), [Weave](https://github.com/iov-one/weave), ...). The choice will most of the time be done based on the programming language they want to use (Cosmos SDK and Weave are in Golang, Lotion is in Javascript, ...). -- The ABCI also allows developers to swap the consensus engine of their application-specific blockchain. Today, only Tendermint is production-ready, but in the future other consensus engines are expected to emerge. -- Even when they settle for a framework and consensus engine, developers still have the freedom to tweak them if they don't perfectly match their requirements in their pristine forms. -- Developers are free to explore the full spectrum of tradeoffs (e.g. number of validators vs transaction throughput, safety vs availability in asynchrony, ...) and design choices (DB or IAVL tree for storage, UTXO or account model, ...). -- Developers can implement automatic execution of code. In the Cosmos SDK, logic can be automatically triggered at the beginning and the end of each block. They are also free to choose the cryptographic library used in their application, as opposed to being constrained by what is made available by the underlying environment in the case of virtual-machine blockchains. - -The list above contains a few examples that show how much flexibility application-specific blockchains give to developers. The goal of Cosmos and the Cosmos SDK is to make developer tooling as generic and composable as possible, so that each part of the stack can be forked, tweaked and improved without losing compatibility. As the community grows, more alternatives for each of the core building blocks will emerge, giving more options to developers. - -### Performance - -Decentralised applications built with Smart Contracts are inherently capped in performance by the underlying environment. For a decentralised application to optimise performance, it needs to be built as an application-specific blockchains. Next are some of the benefits an application-specific blockchain brings in terms of performance: - -- Developers of application-specific blockchains can choose to operate with a novel consensus engine such as Tendermint BFT. Compared to Proof-of-Work (used by most virtual-machine blockchains today), it offers significant gains in throughput. -- An application-specific blockchain only operates a single application, so that the application does not compete with others for computation and storage. This is the opposite of most non-sharded virtual-machine blockchains today, where smart contracts all compete for computation and storage. -- Even if a virtual-machine blockchain offered application-based sharding coupled with an efficient consensus algorithm, performance would still be limited by the virtual-machine itself. The real throughput bottleneck is the state-machine, and requiring transactions to be interpreted by a virtual-machine significantly increases the computational complexity of processing them. - -### Security - -Security is hard to quantify, and greatly varies from platform to platform. That said here are some important benefits an application-specific blockchain can bring in terms of security: - -- Developers can choose proven programming languages like Golang when building their application-specific blockchains, as opposed to smart contract programming languages that are often more immature. -- Developers are not constrained by the cryptographic functions made available by the underlying virtual-machines. They can use their own custom cryptography, and rely on well-audited crypto libraries. -- Developers do not have to worry about potential bugs or exploitable mechanisms in the underlying virtual-machine, making it easier to reason about the security of the application. - -### Sovereignty - -One of the major benefits of application-specific blockchains is sovereignty. A decentralised application is an ecosystem that involves many actors: users, developers, third-party services, and more. When developers build on virtual-machine blockchain where many decentralised applications coexist, the community of the application is different than the community of the underlying blockchain, and the latter supersedes the former in the governance process. If there is a bug or if a new feature is needed, stakeholders of the application have very little leeway to upgrade the code. If the community of the underlying blockchain refuses to act, nothing can happen. - -The fundamental issue here is that the governance of the application and the governance of the network are not aligned. This issue is solved by application-specific blockchains. Because application-specific blockchains specialize to operate a single application, stakeholders the application has full control over the entire chain. This ensures the community will not be stuck if a bug is discovered, and that it has the entire freedom to choose how it is going to evolve. - -## Next {hide} - -Learn more about the [high-level architecture](./sdk-app-architecture.md) of an SDK application {hide} diff --git a/versioned_docs/version-0.45/integrate/_category_.json b/versioned_docs/version-0.45/integrate/_category_.json deleted file mode 100644 index b860342f4..000000000 --- a/versioned_docs/version-0.45/integrate/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Integrate", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/integrate/architecture/PROCESS.md b/versioned_docs/version-0.45/integrate/architecture/PROCESS.md deleted file mode 100644 index 07008b968..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/PROCESS.md +++ /dev/null @@ -1,56 +0,0 @@ -# ADR Creation Process - -1. Copy the `adr-template.md` file. Use the following filename pattern: `adr-next_number-title.md` -2. Create a draft Pull Request if you want to get an early feedback. -3. Make sure the context and a solution is clear and well documented. -4. Add an entry to a list in the [README](./README.md) file. -5. Create a Pull Request to propose a new ADR. - -## ADR life cycle - -ADR creation is an **iterative** process. Instead of trying to solve all decisions in a single ADR pull request, we MUST firstly understand the problem and collect feedback through a GitHub Issue. - -1. Every proposal SHOULD start with a new GitHub Issue or be a result of existing Issues. The Issue should contain just a brief proposal summary. - -2. Once the motivation is validated, a GitHub Pull Request (PR) is created with a new document based on the `adr-template.md`. - -3. An ADR doesn't have to arrive to `master` with an _accepted_ status in a single PR. If the motivation is clear and the solution is sound, we SHOULD be able to merge it and keep a _proposed_ status. It's preferable to have an iterative approach rather than long, not merged Pull Requests. - -4. If a _proposed_ ADR is merged, then it should clearly document outstanding issues either in ADR document notes or in a GitHub Issue. - -5. The PR SHOULD always be merged. In the case of a faulty ADR, we still prefer to merge it with a _rejected_ status. The only time the ADR SHOULD NOT be merged is if the author abandons it. - -6. Merged ADRs SHOULD NOT be pruned. - -### ADR status - -Status has two components: - -``` -{CONSENSUS STATUS} {IMPLEMENTATION STATUS} -``` - -IMPLEMENTATION STATUS is either `Implemented` or `Not Implemented`. - -#### Consensus Status - -``` -DRAFT -> PROPOSED -> LAST CALL yyyy-mm-dd -> ACCEPTED | REJECTED -> SUPERSEEDED by ADR-xxx - \ | - \ | - v v - ABANDONED -``` - -+ `DRAFT`: [optional] an ADR which is work in progress, not being ready for a general review. This is to present an early work and get an early feedback in a Draft Pull Request form. -+ `PROPOSED`: an ADR covering a full solution architecture and still in the review - project stakeholders haven't reached an agreed yet. -+ `LAST CALL `: [optional] clear notify that we are close to accept updates. Changing a status to `LAST CALL` means that social consensus (of Cosmos SDK maintainers) has been reached and we still want to give it a time to let the community react or analyze. -+ `ACCEPTED`: ADR which will represent a currently implemented or to be implemented architecture design. -+ `REJECTED`: ADR can go from PROPOSED or ACCEPTED to rejected if the consensus among project stakeholders will decide so. -+ `SUPERSEEDED by ADR-xxx`: ADR which has been superseded by a new ADR. -+ `ABANDONED`: the ADR is no longer pursued by the original authors. - -## Language used in ADR - -+ The context/background should be written in the present tense. -+ Avoid using a first, personal form. diff --git a/versioned_docs/version-0.45/integrate/architecture/README.md b/versioned_docs/version-0.45/integrate/architecture/README.md deleted file mode 100644 index 5455df9f8..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/README.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -order: false -parent: - order: false ---- - -# Architecture Decision Records (ADR) - -This is a location to record all high-level architecture decisions in the Cosmos-SDK. - -An Architectural Decision (**AD**) is a software design choice that addresses a functional or non-functional requirement that is architecturally significant. -An Architecturally Significant Requirement (**ASR**) is a requirement that has a measurable effect on a software system’s architecture and quality. -An Architectural Decision Record (**ADR**) captures a single AD, such as often done when writing personal notes or meeting minutes; the collection of ADRs created and maintained in a project constitute its decision log. All these are within the topic of Architectural Knowledge Management (AKM). - -You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t). - -## Rationale - -ADRs are intended to be the primary mechanism for proposing new feature designs and new processes, for collecting community input on an issue, and for documenting the design decisions. -An ADR should provide: - -- Context on the relevant goals and the current state -- Proposed changes to achieve the goals -- Summary of pros and cons -- References -- Changelog - -Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and -justification for a change in architecture, or for the architecture of something -new. The spec is much more compressed and streamlined summary of everything as -it stands today. - -If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match. - -## Creating new ADR - -Read about the [PROCESS](./PROCESS.md). - -#### Use RFC 2119 Keywords - -When writing ADRs, follow the same best practices for writing RFCs. When writing RFCs, key words are used to signify the requirements in the specification. These words are often capitalized: "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL. They are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). - -## ADR Table of Contents - -### Accepted - -- [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md) -- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md) -- [ADR 006: Secret Store Replacement](./adr-006-secret-store-replacement.md) -- [ADR 009: Evidence Module](./adr-009-evidence-module.md) -- [ADR 010: Modular AnteHandler](./adr-010-modular-antehandler.md) -- [ADR 019: Protocol Buffer State Encoding](./adr-019-protobuf-state-encoding.md) -- [ADR 020: Protocol Buffer Transaction Encoding](./adr-020-protobuf-transaction-encoding.md) -- [ADR 021: Protocol Buffer Query Encoding](./adr-021-protobuf-query-encoding.md) -- [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md) -- [ADR 029: Fee Grant Module](./adr-029-fee-grant-module.md) -- [ADR 030: Message Authorization Module](./adr-030-authz-module.md) -- [ADR 031: Protobuf Msg Services](./adr-031-msg-service.md) - -### Proposed - -- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md) -- [ADR 011: Generalize Genesis Accounts](./adr-011-generalize-genesis-accounts.md) -- [ADR 012: State Accessors](./adr-012-state-accessors.md) -- [ADR 013: Metrics](./adr-013-metrics.md) -- [ADR 016: Validator Consensus Key Rotation](./adr-016-validator-consensus-key-rotation.md) -- [ADR 017: Historical Header Module](./adr-017-historical-header-module.md) -- [ADR 018: Extendable Voting Periods](./adr-018-extendable-voting-period.md) -- [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md) -- [ADR 024: Coin Metadata](./adr-024-coin-metadata.md) -- [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md) -- [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md) -- [ADR 032: Typed Events](./adr-032-typed-events.md) -- [ADR 033: Inter-module RPC](./adr-033-protobuf-inter-module-comm.md) -- [ADR 035: Rosetta API Support](./adr-035-rosetta-api-support.md) -- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md) -- [ADR 038: State Listening](./adr-038-state-listening.md) -- [ADR 039: Epoched Staking](./adr-039-epoched-staking.md) -- [ADR 040: Storage and SMT State Commitments](./adr-040-storage-and-smt-state-commitments.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-002-docs-structure.md b/versioned_docs/version-0.45/integrate/architecture/adr-002-docs-structure.md deleted file mode 100644 index 1c8201890..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-002-docs-structure.md +++ /dev/null @@ -1,86 +0,0 @@ -# ADR 002: SDK Documentation Structure - -## Context - -There is a need for a scalable structure of the SDK documentation. Current documentation includes a lot of non-related SDK material, is difficult to maintain and hard to follow as a user. - -Ideally, we would have: - -- All docs related to dev frameworks or tools live in their respective github repos (sdk repo would contain sdk docs, hub repo would contain hub docs, lotion repo would contain lotion docs, etc.) -- All other docs (faqs, whitepaper, high-level material about Cosmos) would live on the website. - -## Decision - -Re-structure the `/docs` folder of the SDK github repo as follows: - -``` -docs/ -├── README -├── intro/ -├── concepts/ -│ ├── baseapp -│ ├── types -│ ├── store -│ ├── server -│ ├── modules/ -│ │ ├── keeper -│ │ ├── handler -│ │ ├── cli -│ ├── gas -│ └── commands -├── clients/ -│ ├── lite/ -│ ├── service-providers -├── modules/ -├── spec/ -├── translations/ -└── architecture/ -``` - -The files in each sub-folders do not matter and will likely change. What matters is the sectioning: - -- `README`: Landing page of the docs. -- `intro`: Introductory material. Goal is to have a short explainer of the SDK and then channel people to the resource they need. The [sdk-tutorial](https://github.com/cosmos/sdk-application-tutorial/) will be highlighted, as well as the `godocs`. -- `concepts`: Contains high-level explanations of the abstractions of the SDK. It does not contain specific code implementation and does not need to be updated often. **It is not an API specification of the interfaces**. API spec is the `godoc`. -- `clients`: Contains specs and info about the various SDK clients. -- `spec`: Contains specs of modules, and others. -- `modules`: Contains links to `godocs` and the spec of the modules. -- `architecture`: Contains architecture-related docs like the present one. -- `translations`: Contains different translations of the documentation. - -Website docs sidebar will only include the following sections: - -- `README` -- `intro` -- `concepts` -- `clients` - -`architecture` need not be displayed on the website. - -## Status - -Accepted - -## Consequences - -### Positive - -- Much clearer organisation of the SDK docs. -- The `/docs` folder now only contains SDK and gaia related material. Later, it will only contain SDK related material. -- Developers only have to update `/docs` folder when they open a PR (and not `/examples` for example). -- Easier for developers to find what they need to update in the docs thanks to reworked architecture. -- Cleaner vuepress build for website docs. -- Will help build an executable doc (cf https://github.com/cosmos/cosmos-sdk/issues/2611) - -### Neutral - -- We need to move a bunch of deprecated stuff to `/_attic` folder. -- We need to integrate content in `docs/sdk/docs/core` in `concepts`. -- We need to move all the content that currently lives in `docs` and does not fit in new structure (like `lotion`, intro material, whitepaper) to the website repository. -- Update `DOCS_README.md` - -## References - -- https://github.com/cosmos/cosmos-sdk/issues/1460 -- https://github.com/cosmos/cosmos-sdk/pull/2695 -- https://github.com/cosmos/cosmos-sdk/issues/2611 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-003-dynamic-capability-store.md b/versioned_docs/version-0.45/integrate/architecture/adr-003-dynamic-capability-store.md deleted file mode 100644 index 3f5c9387c..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-003-dynamic-capability-store.md +++ /dev/null @@ -1,344 +0,0 @@ -# ADR 3: Dynamic Capability Store - -## Changelog - -- 12 December 2019: Initial version -- 02 April 2020: Memory Store Revisions - -## Context - -Full implementation of the [IBC specification](https://github.com/cosmos/ibs) requires the ability to create and authenticate object-capability keys at runtime (i.e., during transaction execution), -as described in [ICS 5](https://github.com/cosmos/ibc/tree/master/spec/core/ics-005-port-allocation#technical-specification). In the IBC specification, capability keys are created for each newly initialised -port & channel, and are used to authenticate future usage of the port or channel. Since channels and potentially ports can be initialised during transaction execution, the state machine must be able to create -object-capability keys at this time. - -At present, the Cosmos SDK does not have the ability to do this. Object-capability keys are currently pointers (memory addresses) of `StoreKey` structs created at application initialisation in `app.go` ([example](https://github.com/cosmos/gaia/blob/dcbddd9f04b3086c0ad07ee65de16e7adedc7da4/app/app.go#L132)) -and passed to Keepers as fixed arguments ([example](https://github.com/cosmos/gaia/blob/dcbddd9f04b3086c0ad07ee65de16e7adedc7da4/app/app.go#L160)). Keepers cannot create or store capability keys during transaction execution — although they could call `NewKVStoreKey` and take the memory address -of the returned struct, storing this in the Merklised store would result in a consensus fault, since the memory address will be different on each machine (this is intentional — were this not the case, the keys would be predictable and couldn't serve as object capabilities). - -Keepers need a way to keep a private map of store keys which can be altered during transaction execution, along with a suitable mechanism for regenerating the unique memory addresses (capability keys) in this map whenever the application is started or restarted, along with a mechanism to revert capability creation on tx failure. -This ADR proposes such an interface & mechanism. - -## Decision - -The SDK will include a new `CapabilityKeeper` abstraction, which is responsible for provisioning, -tracking, and authenticating capabilities at runtime. During application initialisation in `app.go`, -the `CapabilityKeeper` will be hooked up to modules through unique function references -(by calling `ScopeToModule`, defined below) so that it can identify the calling module when later -invoked. - -When the initial state is loaded from disk, the `CapabilityKeeper`'s `Initialise` function will create -new capability keys for all previously allocated capability identifiers (allocated during execution of -past transactions and assigned to particular modes), and keep them in a memory-only store while the -chain is running. - -The `CapabilityKeeper` will include a persistent `KVStore`, a `MemoryStore`, and an in-memory map. -The persistent `KVStore` tracks which capability is owned by which modules. -The `MemoryStore` stores a forward mapping that map from module name, capability tuples to capability names and -a reverse mapping that map from module name, capability name to the capability index. -Since we cannot marshal the capability into a `KVStore` and unmarshal without changing the memory location of the capability, -the reverse mapping in the KVStore will simply map to an index. This index can then be used as a key in the ephemeral -go-map to retrieve the capability at the original memory location. - -The `CapabilityKeeper` will define the following types & functions: - -The `Capability` is similar to `StoreKey`, but has a globally unique `Index()` instead of -a name. A `String()` method is provided for debugging. - -A `Capability` is simply a struct, the address of which is taken for the actual capability. - -```golang -type Capability struct { - index uint64 -} -``` - -A `CapabilityKeeper` contains a persistent store key, memory store key, and mapping of allocated module names. - -```golang -type CapabilityKeeper struct { - persistentKey StoreKey - memKey StoreKey - capMap map[uint64]*Capability - moduleNames map[string]interface{} - sealed bool -} -``` - -The `CapabilityKeeper` provides the ability to create *scoped* sub-keepers which are tied to a -particular module name. These `ScopedCapabilityKeeper`s must be created at application initialisation -and passed to modules, which can then use them to claim capabilities they receive and retrieve -capabilities which they own by name, in addition to creating new capabilities & authenticating capabilities -passed by other modules. - -```golang -type ScopedCapabilityKeeper struct { - persistentKey StoreKey - memKey StoreKey - capMap map[uint64]*Capability - moduleName string -} -``` - -`ScopeToModule` is used to create a scoped sub-keeper with a particular name, which must be unique. -It MUST be called before `InitialiseAndSeal`. - -```golang -func (ck CapabilityKeeper) ScopeToModule(moduleName string) ScopedCapabilityKeeper { - if k.sealed { - panic("cannot scope to module via a sealed capability keeper") - } - - if _, ok := k.scopedModules[moduleName]; ok { - panic(fmt.Sprintf("cannot create multiple scoped keepers for the same module name: %s", moduleName)) - } - - k.scopedModules[moduleName] = struct{}{} - - return ScopedKeeper{ - cdc: k.cdc, - storeKey: k.storeKey, - memKey: k.memKey, - capMap: k.capMap, - module: moduleName, - } -} -``` - -`InitialiseAndSeal` MUST be called exactly once, after loading the initial state and creating all -necessary `ScopedCapabilityKeeper`s, in order to populate the memory store with newly-created -capability keys in accordance with the keys previously claimed by particular modules and prevent the -creation of any new `ScopedCapabilityKeeper`s. - -```golang -func (ck CapabilityKeeper) InitialiseAndSeal(ctx Context) { - if ck.sealed { - panic("capability keeper is sealed") - } - - persistentStore := ctx.KVStore(ck.persistentKey) - map := ctx.KVStore(ck.memKey) - - // initialise memory store for all names in persistent store - for index, value := range persistentStore.Iter() { - capability = &CapabilityKey{index: index} - - for moduleAndCapability := range value { - moduleName, capabilityName := moduleAndCapability.Split("/") - memStore.Set(moduleName + "/fwd/" + capability, capabilityName) - memStore.Set(moduleName + "/rev/" + capabilityName, index) - - ck.capMap[index] = capability - } - } - - ck.sealed = true -} -``` - -`NewCapability` can be called by any module to create a new unique, unforgeable object-capability -reference. The newly created capability is automatically persisted; the calling module need not -call `ClaimCapability`. - -```golang -func (sck ScopedCapabilityKeeper) NewCapability(ctx Context, name string) (Capability, error) { - // check name not taken in memory store - if capStore.Get("rev/" + name) != nil { - return nil, errors.New("name already taken") - } - - // fetch the current index - index := persistentStore.Get("index") - - // create a new capability - capability := &CapabilityKey{index: index} - - // set persistent store - persistentStore.Set(index, Set.singleton(sck.moduleName + "/" + name)) - - // update the index - index++ - persistentStore.Set("index", index) - - // set forward mapping in memory store from capability to name - memStore.Set(sck.moduleName + "/fwd/" + capability, name) - - // set reverse mapping in memory store from name to index - memStore.Set(sck.moduleName + "/rev/" + name, index) - - // set the in-memory mapping from index to capability pointer - capMap[index] = capability - - // return the newly created capability - return capability -} -``` - -`AuthenticateCapability` can be called by any module to check that a capability -does in fact correspond to a particular name (the name can be untrusted user input) -with which the calling module previously associated it. - -```golang -func (sck ScopedCapabilityKeeper) AuthenticateCapability(name string, capability Capability) bool { - // return whether forward mapping in memory store matches name - return memStore.Get(sck.moduleName + "/fwd/" + capability) === name -} -``` - -`ClaimCapability` allows a module to claim a capability key which it has received from another module -so that future `GetCapability` calls will succeed. - -`ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name -in the future. Capabilities are multi-owner, so if multiple modules have a single `Capability` reference, -they will all own it. - -```golang -func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capability, name string) error { - persistentStore := ctx.KVStore(sck.persistentKey) - - // set forward mapping in memory store from capability to name - memStore.Set(sck.moduleName + "/fwd/" + capability, name) - - // set reverse mapping in memory store from name to capability - memStore.Set(sck.moduleName + "/rev/" + name, capability) - - // update owner set in persistent store - owners := persistentStore.Get(capability.Index()) - owners.add(sck.moduleName + "/" + name) - persistentStore.Set(capability.Index(), owners) -} -``` - -`GetCapability` allows a module to fetch a capability which it has previously claimed by name. -The module is not allowed to retrieve capabilities which it does not own. - -```golang -func (sck ScopedCapabilityKeeper) GetCapability(ctx Context, name string) (Capability, error) { - // fetch the index of capability using reverse mapping in memstore - index := memStore.Get(sck.moduleName + "/rev/" + name) - - // fetch capability from go-map using index - capability := capMap[index] - - // return the capability - return capability -} -``` - -`ReleaseCapability` allows a module to release a capability which it had previously claimed. If no -more owners exist, the capability will be deleted globally. - -```golang -func (sck ScopedCapabilityKeeper) ReleaseCapability(ctx Context, capability Capability) err { - persistentStore := ctx.KVStore(sck.persistentKey) - - name := capStore.Get(sck.moduleName + "/fwd/" + capability) - if name == nil { - return error("capability not owned by module") - } - - // delete forward mapping in memory store - memoryStore.Delete(sck.moduleName + "/fwd/" + capability, name) - - // delete reverse mapping in memory store - memoryStore.Delete(sck.moduleName + "/rev/" + name, capability) - - // update owner set in persistent store - owners := persistentStore.Get(capability.Index()) - owners.remove(sck.moduleName + "/" + name) - if owners.size() > 0 { - // there are still other owners, keep the capability around - persistentStore.Set(capability.Index(), owners) - } else { - // no more owners, delete the capability - persistentStore.Delete(capability.Index()) - delete(capMap[capability.Index()]) - } -} -``` - -### Usage patterns - -#### Initialisation - -Any modules which use dynamic capabilities must be provided a `ScopedCapabilityKeeper` in `app.go`: - -```golang -ck := NewCapabilityKeeper(persistentKey, memoryKey) -mod1Keeper := NewMod1Keeper(ck.ScopeToModule("mod1"), ....) -mod2Keeper := NewMod2Keeper(ck.ScopeToModule("mod2"), ....) - -// other initialisation logic ... - -// load initial state... - -ck.InitialiseAndSeal(initialContext) -``` - -#### Creating, passing, claiming and using capabilities - -Consider the case where `mod1` wants to create a capability, associate it with a resource (e.g. an IBC channel) by name, then pass it to `mod2` which will use it later: - -Module 1 would have the following code: - -```golang -capability := scopedCapabilityKeeper.NewCapability(ctx, "resourceABC") -mod2Keeper.SomeFunction(ctx, capability, args...) -``` - -`SomeFunction`, running in module 2, could then claim the capability: - -```golang -func (k Mod2Keeper) SomeFunction(ctx Context, capability Capability) { - k.sck.ClaimCapability(ctx, capability, "resourceABC") - // other logic... -} -``` - -Later on, module 2 can retrieve that capability by name and pass it to module 1, which will authenticate it against the resource: - -```golang -func (k Mod2Keeper) SomeOtherFunction(ctx Context, name string) { - capability := k.sck.GetCapability(ctx, name) - mod1.UseResource(ctx, capability, "resourceABC") -} -``` - -Module 1 will then check that this capability key is authenticated to use the resource before allowing module 2 to use it: - -```golang -func (k Mod1Keeper) UseResource(ctx Context, capability Capability, resource string) { - if !k.sck.AuthenticateCapability(name, capability) { - return errors.New("unauthenticated") - } - // do something with the resource -} -``` - -If module 2 passed the capability key to module 3, module 3 could then claim it and call module 1 just like module 2 did -(in which case module 1, module 2, and module 3 would all be able to use this capability). - -## Status - -Proposed. - -## Consequences - -### Positive - -- Dynamic capability support. -- Allows CapabilityKeeper to return same capability pointer from go-map while reverting any writes to the persistent `KVStore` and in-memory `MemoryStore` on tx failure. - -### Negative - -- Requires an additional keeper. -- Some overlap with existing `StoreKey` system (in the future they could be combined, since this is a superset functionality-wise). -- Requires an extra level of indirection in the reverse mapping, since MemoryStore must map to index which must then be used as key in a go map to retrieve the actual capability - -### Neutral - -(none known) - -## References - -- [Original discussion](https://github.com/cosmos/cosmos-sdk/pull/5230#discussion_r343978513) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-004-split-denomination-keys.md b/versioned_docs/version-0.45/integrate/architecture/adr-004-split-denomination-keys.md deleted file mode 100644 index ae192c632..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-004-split-denomination-keys.md +++ /dev/null @@ -1,120 +0,0 @@ -# ADR 004: Split Denomination Keys - -## Changelog - -- 2020-01-08: Initial version -- 2020-01-09: Alterations to handle vesting accounts -- 2020-01-14: Updates from review feedback -- 2020-01-30: Updates from implementation - -### Glossary - -* denom / denomination key -- unique token identifier. - -## Context - -With permissionless IBC, anyone will be able to send arbitrary denominations to any other account. Currently, all non-zero balances are stored along with the account in an `sdk.Coins` struct, which creates a potential denial-of-service concern, as too many denominations will become expensive to load & store each time the account is modified. See issues [5467](https://github.com/cosmos/cosmos-sdk/issues/5467) and [4982](https://github.com/cosmos/cosmos-sdk/issues/4982) for additional context. - -Simply rejecting incoming deposits after a denomination count limit doesn't work, since it opens up a griefing vector: someone could send a user lots of nonsensical coins over IBC, and then prevent the user from receiving real denominations (such as staking rewards). - -## Decision - -Balances shall be stored per-account & per-denomination under a denomination- and account-unique key, thus enabling O(1) read & write access to the balance of a particular account in a particular denomination. - -### Account interface (x/auth) - -`GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will -now be stored in & managed by the bank module. - -The vesting account interface will replace `SpendableCoins` in favor of `LockedCoins` which does -not require the account balance anymore. In addition, `TrackDelegation()` will now accept the -account balance of all tokens denominated in the vesting balance instead of loading the entire -account balance. - -Vesting accounts will continue to store original vesting, delegated free, and delegated -vesting coins (which is safe since these cannot contain arbitrary denominations). - -### Bank keeper (x/bank) - -The following APIs will be added to the `x/bank` keeper: - -- `GetAllBalances(ctx Context, addr AccAddress) Coins` -- `GetBalance(ctx Context, addr AccAddress, denom string) Coin` -- `SetBalance(ctx Context, addr AccAddress, coin Coin)` -- `LockedCoins(ctx Context, addr AccAddress) Coins` -- `SpendableCoins(ctx Context, addr AccAddress) Coins` - -Additional APIs may be added to facilitate iteration and auxiliary functionality not essential to -core functionality or persistence. - -Balances will be stored first by the address, then by the denomination (the reverse is also possible, -but retrieval of all balances for a single account is presumed to be more frequent): - -```golang -var BalancesPrefix = []byte("balances") - -func (k Keeper) SetBalance(ctx Context, addr AccAddress, balance Coin) error { - if !balance.IsValid() { - return err - } - - store := ctx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) - - bz := Marshal(balance) - accountStore.Set([]byte(balance.Denom), bz) - - return nil -} -``` - -This will result in the balances being indexed by the byte representation of -`balances/{address}/{denom}`. - -`DelegateCoins()` and `UndelegateCoins()` will be altered to only load each individual -account balance by denomination found in the (un)delegation amount. As a result, -any mutations to the account balance by will made by denomination. - -`SubtractCoins()` and `AddCoins()` will be altered to read & write the balances -directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist). - -`trackDelegation()` and `trackUndelegation()` will be altered to no longer update -account balances. - -External APIs will need to scan all balances under an account to retain backwards-compatibility. It -is advised that these APIs use `GetBalance` and `SetBalance` instead of `GetAllBalances` when -possible as to not load the entire account balance. - -### Supply module - -The supply module, in order to implement the total supply invariant, will now need -to scan all accounts & call `GetAllBalances` using the `x/bank` Keeper, then sum -the balances and check that they match the expected total supply. - -## Status - -Accepted. - -## Consequences - -### Positive - -- O(1) reads & writes of balances (with respect to the number of denominations for -which an account has non-zero balances). Note, this does not relate to the actual -I/O cost, rather the total number of direct reads needed. - -### Negative - -- Slightly less efficient reads/writes when reading & writing all balances of a -single account in a transaction. - -### Neutral - -None in particular. - -## References - -- Ref: https://github.com/cosmos/cosmos-sdk/issues/4982 -- Ref: https://github.com/cosmos/cosmos-sdk/issues/5467 -- Ref: https://github.com/cosmos/cosmos-sdk/issues/5492 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-006-secret-store-replacement.md b/versioned_docs/version-0.45/integrate/architecture/adr-006-secret-store-replacement.md deleted file mode 100644 index 8be407727..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-006-secret-store-replacement.md +++ /dev/null @@ -1,54 +0,0 @@ -# ADR 006: Secret Store Replacement - -## Changelog - -- July 29th, 2019: Initial draft -- September 11th, 2019: Work has started -- November 4th: SDK changes merged in -- November 18th: Gaia changes merged in - -## Context - -Currently, an SDK application's CLI directory stores key material and metadata in a plain text database in the user’s home directory. Key material is encrypted by a passphrase, protected by bcrypt hashing algorithm. Metadata (e.g. addresses, public keys, key storage details) is available in plain text. - -This is not desirable for a number of reasons. Perhaps the biggest reason is insufficient security protection of key material and metadata. Leaking the plain text allows an attacker to surveil what keys a given computer controls via a number of techniques, like compromised dependencies without any privilege execution. This could be followed by a more targeted attack on a particular user/computer. - -All modern desktop computers OS (Ubuntu, Debian, MacOS, Windows) provide a built-in secret store that is designed to allow applications to store information that is isolated from all other applications and requires passphrase entry to access the data. - -We are seeking solution that provides a common abstraction layer to the many different backends and reasonable fallback for minimal platforms that don’t provide a native secret store. - -## Decision - -We recommend replacing the current Keybase backend based on LevelDB with [Keyring](https://github.com/99designs/keyring) by 99 designs. This application is designed to provide a common abstraction and uniform interface between many secret stores and is used by AWS Vault application by 99-designs application. - -This appears to fulfill the requirement of protecting both key material and metadata from rouge software on a user’s machine. - -## Status - -Accepted - -## Consequences - -### Positive - -Increased safety for users. - -### Negative - -Users must manually migrate. - -Testing against all supported backends is difficult. - -Running tests locally on a Mac require numerous repetitive password entries. - -### Neutral - -{neutral consequences} - -## References - -- #4754 Switch secret store to the keyring secret store (original PR by @poldsam) [__CLOSED__] -- #5029 Add support for github.com/99designs/keyring-backed keybases [__MERGED__] -- #5097 Add keys migrate command [__MERGED__] -- #5180 Drop on-disk keybase in favor of keyring [_PENDING_REVIEW_] -- cosmos/gaia#164 Drop on-disk keybase in favor of keyring (gaia's changes) [_PENDING_REVIEW_] diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-007-specialization-groups.md b/versioned_docs/version-0.45/integrate/architecture/adr-007-specialization-groups.md deleted file mode 100644 index 8055fc5a2..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-007-specialization-groups.md +++ /dev/null @@ -1,177 +0,0 @@ -# ADR 007: Specialization Groups - -## Changelog - -- 2019 Jul 31: Initial Draft - -## Context - -This idea was first conceived of in order to fulfill the use case of the -creation of a decentralized Computer Emergency Response Team (dCERT), whose -members would be elected by a governing community and would fulfill the role of -coordinating the community under emergency situations. This thinking -can be further abstracted into the conception of "blockchain specialization -groups". - -The creation of these groups are the beginning of specialization capabilities -within a wider blockchain community which could be used to enable a certain -level of delegated responsibilities. Examples of specialization which could be -beneficial to a blockchain community include: code auditing, emergency response, -code development etc. This type of community organization paves the way for -individual stakeholders to delegate votes by issue type, if in the future -governance proposals include a field for issue type. - -## Decision - -A specialization group can be broadly broken down into the following functions -(herein containing examples): - -- Membership Admittance -- Membership Acceptance -- Membership Revocation - - (probably) Without Penalty - - member steps down (self-Revocation) - - replaced by new member from governance - - (probably) With Penalty - - due to breach of soft-agreement (determined through governance) - - due to breach of hard-agreement (determined by code) -- Execution of Duties - - Special transactions which only execute for members of a specialization - group (for example, dCERT members voting to turn off transaction routes in - an emergency scenario) -- Compensation - - Group compensation (further distribution decided by the specialization group) - - Individual compensation for all constituents of a group from the - greater community - -Membership admittance to a specialization group could take place over a wide -variety of mechanisms. The most obvious example is through a general vote among -the entire community, however in certain systems a community may want to allow -the members already in a specialization group to internally elect new members, -or maybe the community may assign a permission to a particular specialization -group to appoint members to other 3rd party groups. The sky is really the limit -as to how membership admittance can be structured. We attempt to capture -some of these possiblities in a common interface dubbed the `Electionator`. For -its initial implementation as a part of this ADR we recommend that the general -election abstraction (`Electionator`) is provided as well as a basic -implementation of that abstraction which allows for a continuous election of -members of a specialization group. - -``` golang -// The Electionator abstraction covers the concept space for -// a wide variety of election kinds. -type Electionator interface { - - // is the election object accepting votes. - Active() bool - - // functionality to execute for when a vote is cast in this election, here - // the vote field is anticipated to be marshalled into a vote type used - // by an election. - // - // NOTE There are no explicit ids here. Just votes which pertain specifically - // to one electionator. Anyone can create and send a vote to the electionator item - // which will presumably attempt to marshal those bytes into a particular struct - // and apply the vote information in some arbitrary way. There can be multiple - // Electionators within the Cosmos-Hub for multiple specialization groups, votes - // would need to be routed to the Electionator upstream of here. - Vote(addr sdk.AccAddress, vote []byte) - - // here lies all functionality to authenticate and execute changes for - // when a member accepts being elected - AcceptElection(sdk.AccAddress) - - // Register a revoker object - RegisterRevoker(Revoker) - - // No more revokers may be registered after this function is called - SealRevokers() - - // register hooks to call when an election actions occur - RegisterHooks(ElectionatorHooks) - - // query for the current winner(s) of this election based on arbitrary - // election ruleset - QueryElected() []sdk.AccAddress - - // query metadata for an address in the election this - // could include for example position that an address - // is being elected for within a group - // - // this metadata may be directly related to - // voting information and/or privileges enabled - // to members within a group. - QueryMetadata(sdk.AccAddress) []byte -} - -// ElectionatorHooks, once registered with an Electionator, -// trigger execution of relevant interface functions when -// Electionator events occur. -type ElectionatorHooks interface { - AfterVoteCast(addr sdk.AccAddress, vote []byte) - AfterMemberAccepted(addr sdk.AccAddress) - AfterMemberRevoked(addr sdk.AccAddress, cause []byte) -} - -// Revoker defines the function required for a membership revocation rule-set -// used by a specialization group. This could be used to create self revoking, -// and evidence based revoking, etc. Revokers types may be created and -// reused for different election types. -// -// When revoking the "cause" bytes may be arbitrarily marshalled into evidence, -// memos, etc. -type Revoker interface { - RevokeName() string // identifier for this revoker type - RevokeMember(addr sdk.AccAddress, cause []byte) error -} -``` - -Certain level of commonality likely exists between the existing code within -`x/governance` and required functionality of elections. This common -functionality should be abstracted during implementation. Similarly for each -vote implementation client CLI/REST functionality should be abstracted -to be reused for multiple elections. - -The specialization group abstraction firstly extends the `Electionator` -but also further defines traits of the group. - -``` golang -type SpecializationGroup interface { - Electionator - GetName() string - GetDescription() string - - // general soft contract the group is expected - // to fulfill with the greater community - GetContract() string - - // messages which can be executed by the members of the group - Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result - - // logic to be executed at endblock, this may for instance - // include payment of a stipend to the group members - // for participation in the security group. - EndBlocker(ctx sdk.Context) -} -``` - -## Status - -> Proposed - -## Consequences - -### Positive - -- increases specialization capabilities of a blockchain -- improve abstractions in `x/gov/` such that they can be used with specialization groups - -### Negative - -- could be used to increase centralization within a community - -### Neutral - -## References - -- [dCERT ADR](./adr-008-dCERT-group.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-008-dCERT-group.md b/versioned_docs/version-0.45/integrate/architecture/adr-008-dCERT-group.md deleted file mode 100644 index a6f0dfb19..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-008-dCERT-group.md +++ /dev/null @@ -1,171 +0,0 @@ -# ADR 008: Decentralized Computer Emergency Response Team (dCERT) Group - -## Changelog - -- 2019 Jul 31: Initial Draft - -## Context - -In order to reduce the number of parties involved with handling sensitive -information in an emergency scenario, we propose the creation of a -specialization group named The Decentralized Computer Emergency Response Team -(dCERT). Initially this group's role is intended to serve as coordinators -between various actors within a blockchain community such as validators, -bug-hunters, and developers. During a time of crisis, the dCERT group would -aggregate and relay input from a variety of stakeholders to the developers who -are actively devising a patch to the software, this way sensitive information -does not need to be publicly disclosed while some input from the community can -still be gained. - -Additionally, a special privilege is proposed for the dCERT group: the capacity -to "circuit-break" (aka. temporarily disable) a particular message path. Note -that this privilege should be enabled/disabled globally with a governance -parameter such that this privilege could start disabled and later be enabled -through a parameter change proposal, once a dCERT group has been established. - -In the future it is foreseeable that the community may wish to expand the roles -of dCERT with further responsibilities such as the capacity to "pre-approve" a -security update on behalf of the community prior to a full community -wide vote whereby the sensitive information would be revealed prior to a -vulnerability being patched on the live network. - -## Decision - -The dCERT group is proposed to include an implementation of a `SpecializationGroup` -as defined in [ADR 007](./adr-007-specialization-groups.md). This will include the -implementation of: - -- continuous voting -- slashing due to breach of soft contract -- revoking a member due to breach of soft contract -- emergency disband of the entire dCERT group (ex. for colluding maliciously) -- compensation stipend from the community pool or other means decided by - governance - -This system necessitates the following new parameters: - -- blockly stipend allowance per dCERT member -- maximum number of dCERT members -- required staked slashable tokens for each dCERT member -- quorum for suspending a particular member -- proposal wager for disbanding the dCERT group -- stabilization period for dCERT member transition -- circuit break dCERT privileges enabled - -These parameters are expected to be implemented through the param keeper such -that governance may change them at any given point. - -### Continuous Voting Electionator - -An `Electionator` object is to be implemented as continuous voting and with the -following specifications: - -- All delegation addresses may submit votes at any point which updates their - preferred representation on the dCERT group. -- Preferred representation may be arbitrarily split between addresses (ex. 50% - to John, 25% to Sally, 25% to Carol) -- In order for a new member to be added to the dCERT group they must - send a transaction accepting their admission at which point the validity of - their admission is to be confirmed. - - A sequence number is assigned when a member is added to dCERT group. - If a member leaves the dCERT group and then enters back, a new sequence number - is assigned. -- Addresses which control the greatest amount of preferred-representation are - eligible to join the dCERT group (up the _maximum number of dCERT members_). - If the dCERT group is already full and new member is admitted, the existing - dCERT member with the lowest amount of votes is kicked from the dCERT group. - - In the split situation where the dCERT group is full but a vying candidate - has the same amount of vote as an existing dCERT member, the existing - member should maintain its position. - - In the split situation where somebody must be kicked out but the two - addresses with the smallest number of votes have the same number of votes, - the address with the smallest sequence number maintains its position. -- A stabilization period can be optionally included to reduce the - "flip-flopping" of the dCERT membership tail members. If a stabilization - period is provided which is greater than 0, when members are kicked due to - insufficient support, a queue entry is created which documents which member is - to replace which other member. While this entry is in the queue, no new entries - to kick that same dCERT member can be made. When the entry matures at the - duration of the stabilization period, the new member is instantiated, and old - member kicked. - -### Staking/Slashing - -All members of the dCERT group must stake tokens _specifically_ to maintain -eligibility as a dCERT member. These tokens can be staked directly by the vying -dCERT member or out of the good will of a 3rd party (who shall gain no on-chain -benefits for doing so). This staking mechanism should use the existing global -unbonding time of tokens staked for network validator security. A dCERT member -can _only be_ a member if it has the required tokens staked under this -mechanism. If those tokens are unbonded then the dCERT member must be -automatically kicked from the group. - -Slashing of a particular dCERT member due to soft-contract breach should be -performed by governance on a per member basis based on the magnitude of the -breach. The process flow is anticipated to be that a dCERT member is suspended -by the dCERT group prior to being slashed by governance. - -Membership suspension by the dCERT group takes place through a voting procedure -by the dCERT group members. After this suspension has taken place, a governance -proposal to slash the dCERT member must be submitted, if the proposal is not -approved by the time the rescinding member has completed unbonding their -tokens, then the tokens are no longer staked and unable to be slashed. - -Additionally in the case of an emergency situation of a colluding and malicious -dCERT group, the community needs the capability to disband the entire dCERT -group and likely fully slash them. This could be achieved though a special new -proposal type (implemented as a general governance proposal) which would halt -the functionality of the dCERT group until the proposal was concluded. This -special proposal type would likely need to also have a fairly large wager which -could be slashed if the proposal creator was malicious. The reason a large -wager should be required is because as soon as the proposal is made, the -capability of the dCERT group to halt message routes is put on temporarily -suspended, meaning that a malicious actor who created such a proposal could -then potentially exploit a bug during this period of time, with no dCERT group -capable of shutting down the exploitable message routes. - -### dCERT membership transactions - -Active dCERT members - -- change of the description of the dCERT group -- circuit break a message route -- vote to suspend a dCERT member. - -Here circuit-breaking refers to the capability to disable a groups of messages, -This could for instance mean: "disable all staking-delegation messages", or -"disable all distribution messages". This could be accomplished by verifying -that the message route has not been "circuit-broken" at CheckTx time (in -`baseapp/baseapp.go`). - -"unbreaking" a circuit is anticipated only to occur during a hard fork upgrade -meaning that no capability to unbreak a message route on a live chain is -required. - -Note also, that if there was a problem with governance voting (for instance a -capability to vote many times) then governance would be broken and should be -halted with this mechanism, it would be then up to the validator set to -coordinate and hard-fork upgrade to a patched version of the software where -governance is re-enabled (and fixed). If the dCERT group abuses this privilege -they should all be severely slashed. - -## Status - -> Proposed - -## Consequences - -### Positive - -- Potential to reduces the number of parties to coordinate with during an emergency -- Reduction in possibility of disclosing sensitive information to malicious parties - -### Negative - -- Centralization risks - -### Neutral - -## References - - [Specialization Groups ADR](./adr-007-specialization-groups.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-009-evidence-module.md b/versioned_docs/version-0.45/integrate/architecture/adr-009-evidence-module.md deleted file mode 100644 index f6f47170c..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-009-evidence-module.md +++ /dev/null @@ -1,182 +0,0 @@ -# ADR 009: Evidence Module - -## Changelog - -- 2019 July 31: Initial draft -- 2019 October 24: Initial implementation - -## Status - -Accepted - -## Context - -In order to support building highly secure, robust and interoperable blockchain -applications, it is vital for the Cosmos SDK to expose a mechanism in which arbitrary -evidence can be submitted, evaluated and verified resulting in some agreed upon -penalty for any misbehavior committed by a validator, such as equivocation (double-voting), -signing when unbonded, signing an incorrect state transition (in the future), etc. -Furthermore, such a mechanism is paramount for any -[IBC](https://github.com/cosmos/ics/blob/master/ibc/2_IBC_ARCHITECTURE.md) or -cross-chain validation protocol implementation in order to support the ability -for any misbehavior to be relayed back from a collateralized chain to a primary -chain so that the equivocating validator(s) can be slashed. - -## Decision - -We will implement an evidence module in the Cosmos SDK supporting the following -functionality: - -- Provide developers with the abstractions and interfaces necessary to define - custom evidence messages, message handlers, and methods to slash and penalize - accordingly for misbehavior. -- Support the ability to route evidence messages to handlers in any module to - determine the validity of submitted misbehavior. -- Support the ability, through governance, to modify slashing penalties of any - evidence type. -- Querier implementation to support querying params, evidence types, params, and - all submitted valid misbehavior. - -### Types - -First, we define the `Evidence` interface type. The `x/evidence` module may implement -its own types that can be used by many chains (e.g. `CounterFactualEvidence`). -In addition, other modules may implement their own `Evidence` types in a similar -manner in which governance is extensible. It is important to note any concrete -type implementing the `Evidence` interface may include arbitrary fields such as -an infraction time. We want the `Evidence` type to remain as flexible as possible. - -When submitting evidence to the `x/evidence` module, the concrete type must provide -the validator's consensus address, which should be known by the `x/slashing` -module (assuming the infraction is valid), the height at which the infraction -occurred and the validator's power at same height in which the infraction occurred. - -```go -type Evidence interface { - Route() string - Type() string - String() string - Hash() HexBytes - ValidateBasic() error - - // The consensus address of the malicious validator at time of infraction - GetConsensusAddress() ConsAddress - - // Height at which the infraction occurred - GetHeight() int64 - - // The total power of the malicious validator at time of infraction - GetValidatorPower() int64 - - // The total validator set power at time of infraction - GetTotalPower() int64 -} -``` - -### Routing & Handling - -Each `Evidence` type must map to a specific unique route and be registered with -the `x/evidence` module. It accomplishes this through the `Router` implementation. - -```go -type Router interface { - AddRoute(r string, h Handler) Router - HasRoute(r string) bool - GetRoute(path string) Handler - Seal() -} -``` - -Upon successful routing through the `x/evidence` module, the `Evidence` type -is passed through a `Handler`. This `Handler` is responsible for executing all -corresponding business logic necessary for verifying the evidence as valid. In -addition, the `Handler` may execute any necessary slashing and potential jailing. -Since slashing fractions will typically result from some form of static functions, -allow the `Handler` to do this provides the greatest flexibility. An example could -be `k * evidence.GetValidatorPower()` where `k` is an on-chain parameter controlled -by governance. The `Evidence` type should provide all the external information -necessary in order for the `Handler` to make the necessary state transitions. -If no error is returned, the `Evidence` is considered valid. - -```go -type Handler func(Context, Evidence) error -``` - -### Submission - -`Evidence` is submitted through a `MsgSubmitEvidence` message type which is internally -handled by the `x/evidence` module's `SubmitEvidence`. - -```go -type MsgSubmitEvidence struct { - Evidence -} - -func handleMsgSubmitEvidence(ctx Context, keeper Keeper, msg MsgSubmitEvidence) Result { - if err := keeper.SubmitEvidence(ctx, msg.Evidence); err != nil { - return err.Result() - } - - // emit events... - - return Result{ - // ... - } -} -``` - -The `x/evidence` module's keeper is responsible for matching the `Evidence` against -the module's router and invoking the corresponding `Handler` which may include -slashing and jailing the validator. Upon success, the submitted evidence is persisted. - -```go -func (k Keeper) SubmitEvidence(ctx Context, evidence Evidence) error { - handler := keeper.router.GetRoute(evidence.Route()) - if err := handler(ctx, evidence); err != nil { - return ErrInvalidEvidence(keeper.codespace, err) - } - - keeper.setEvidence(ctx, evidence) - return nil -} -``` - -### Genesis - -Finally, we need to represent the genesis state of the `x/evidence` module. The -module only needs a list of all submitted valid infractions and any necessary params -for which the module needs in order to handle submitted evidence. The `x/evidence` -module will naturally define and route native evidence types for which it'll most -likely need slashing penalty constants for. - -```go -type GenesisState struct { - Params Params - Infractions []Evidence -} -``` - -## Consequences - -### Positive - -- Allows the state machine to process misbehavior submitted on-chain and penalize - validators based on agreed upon slashing parameters. -- Allows evidence types to be defined and handled by any module. This further allows - slashing and jailing to be defined by more complex mechanisms. -- Does not solely rely on Tendermint to submit evidence. - -### Negative - -- No easy way to introduce new evidence types through governance on a live chain - due to the inability to introduce the new evidence type's corresponding handler - -### Neutral - -- Should we persist infractions indefinitely? Or should we rather rely on events? - -## References - -- [ICS](https://github.com/cosmos/ics) -- [IBC Architecture](https://github.com/cosmos/ics/blob/master/ibc/1_IBC_ARCHITECTURE.md) -- [Tendermint Fork Accountability](https://github.com/tendermint/spec/blob/7b3138e69490f410768d9b1ffc7a17abc23ea397/spec/consensus/fork-accountability.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-010-modular-antehandler.md b/versioned_docs/version-0.45/integrate/architecture/adr-010-modular-antehandler.md deleted file mode 100644 index 15d28dbee..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-010-modular-antehandler.md +++ /dev/null @@ -1,285 +0,0 @@ -# ADR 010: Modular AnteHandler - -## Changelog - -- 2019 Aug 31: Initial draft - -## Context - -The current AnteHandler design allows users to either use the default AnteHandler provided in `x/auth` or to build their own AnteHandler from scratch. Ideally AnteHandler functionality is split into multiple, modular functions that can be chained together along with custom ante-functions so that users do not have to rewrite common antehandler logic when they want to implement custom behavior. - -For example, let's say a user wants to implement some custom signature verification logic. In the current codebase, the user would have to write their own Antehandler from scratch largely reimplementing much of the same code and then set their own custom, monolithic antehandler in the baseapp. Instead, we would like to allow users to specify custom behavior when necessary and combine them with default ante-handler functionality in a way that is as modular and flexible as possible. - -## Proposals - -### Per-Module AnteHandler - -One approach is to use the [ModuleManager](https://godoc.org/github.com/cosmos/cosmos-sdk/types/module) and have each module implement its own antehandler if it requires custom antehandler logic. The ModuleManager can then be passed in an AnteHandler order in the same way it has an order for BeginBlockers and EndBlockers. The ModuleManager returns a single AnteHandler function that will take in a tx and run each module's `AnteHandle` in the specified order. The module manager's AnteHandler is set as the baseapp's AnteHandler. - -Pros: - -1. Simple to implement -2. Utilizes the existing ModuleManager architecture - -Cons: - -1. Improves granularity but still cannot get more granular than a per-module basis. e.g. If auth's `AnteHandle` function is in charge of validating memo and signatures, users cannot swap the signature-checking functionality while keeping the rest of auth's `AnteHandle` functionality. -2. Module AnteHandler are run one after the other. There is no way for one AnteHandler to wrap or "decorate" another. - -### Decorator Pattern - -The [weave project](https://github.com/iov-one/weave) achieves AnteHandler modularity through the use of a decorator pattern. The interface is designed as follows: - -```go -// Decorator wraps a Handler to provide common functionality -// like authentication, or fee-handling, to many Handlers -type Decorator interface { - Check(ctx Context, store KVStore, tx Tx, next Checker) (*CheckResult, error) - Deliver(ctx Context, store KVStore, tx Tx, next Deliverer) (*DeliverResult, error) -} -``` - -Each decorator works like a modularized SDK antehandler function, but it can take in a `next` argument that may be another decorator or a Handler (which does not take in a next argument). These decorators can be chained together, one decorator being passed in as the `next` argument of the previous decorator in the chain. The chain ends in a Router which can take a tx and route to the appropriate msg handler. - -A key benefit of this approach is that one Decorator can wrap its internal logic around the next Checker/Deliverer. A weave Decorator may do the following: - -```go -// Example Decorator's Deliver function -func (example Decorator) Deliver(ctx Context, store KVStore, tx Tx, next Deliverer) { - // Do some pre-processing logic - - res, err := next.Deliver(ctx, store, tx) - - // Do some post-processing logic given the result and error -} -``` - -Pros: - -1. Weave Decorators can wrap over the next decorator/handler in the chain. The ability to both pre-process and post-process may be useful in certain settings. -2. Provides a nested modular structure that isn't possible in the solution above, while also allowing for a linear one-after-the-other structure like the solution above. - -Cons: - -1. It is hard to understand at first glance the state updates that would occur after a Decorator runs given the `ctx`, `store`, and `tx`. A Decorator can have an arbitrary number of nested Decorators being called within its function body, each possibly doing some pre- and post-processing before calling the next decorator on the chain. Thus to understand what a Decorator is doing, one must also understand what every other decorator further along the chain is also doing. This can get quite complicated to understand. A linear, one-after-the-other approach while less powerful, may be much easier to reason about. - -### Chained Micro-Functions - -The benefit of Weave's approach is that the Decorators can be very concise, which when chained together allows for maximum customizability. However, the nested structure can get quite complex and thus hard to reason about. - -Another approach is to split the AnteHandler functionality into tightly scoped "micro-functions", while preserving the one-after-the-other ordering that would come from the ModuleManager approach. - -We can then have a way to chain these micro-functions so that they run one after the other. Modules may define multiple ante micro-functions and then also provide a default per-module AnteHandler that implements a default, suggested order for these micro-functions. - -Users can order the AnteHandlers easily by simply using the ModuleManager. The ModuleManager will take in a list of AnteHandlers and return a single AnteHandler that runs each AnteHandler in the order of the list provided. If the user is comfortable with the default ordering of each module, this is as simple as providing a list with each module's antehandler (exactly the same as BeginBlocker and EndBlocker). - -If however, users wish to change the order or add, modify, or delete ante micro-functions in anyway; they can always define their own ante micro-functions and add them explicitly to the list that gets passed into module manager. - -#### Default Workflow - -This is an example of a user's AnteHandler if they choose not to make any custom micro-functions. - -##### SDK code - -```go -// Chains together a list of AnteHandler micro-functions that get run one after the other. -// Returned AnteHandler will abort on first error. -func Chainer(order []AnteHandler) AnteHandler { - return func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - for _, ante := range order { - ctx, err := ante(ctx, tx, simulate) - if err != nil { - return ctx, err - } - } - return ctx, err - } -} -``` - -```go -// AnteHandler micro-function to verify signatures -func VerifySignatures(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // verify signatures - // Returns InvalidSignature Result and abort=true if sigs invalid - // Return OK result and abort=false if sigs are valid -} - -// AnteHandler micro-function to validate memo -func ValidateMemo(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // validate memo -} - -// Auth defines its own default ante-handler by chaining its micro-functions in a recommended order -AuthModuleAnteHandler := Chainer([]AnteHandler{VerifySignatures, ValidateMemo}) -``` - -```go -// Distribution micro-function to deduct fees from tx -func DeductFees(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // Deduct fees from tx - // Abort if insufficient funds in account to pay for fees -} - -// Distribution micro-function to check if fees > mempool parameter -func CheckMempoolFees(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // If CheckTx: Abort if the fees are less than the mempool's minFee parameter -} - -// Distribution defines its own default ante-handler by chaining its micro-functions in a recommended order -DistrModuleAnteHandler := Chainer([]AnteHandler{CheckMempoolFees, DeductFees}) -``` - -```go -type ModuleManager struct { - // other fields - AnteHandlerOrder []AnteHandler -} - -func (mm ModuleManager) GetAnteHandler() AnteHandler { - retun Chainer(mm.AnteHandlerOrder) -} -``` - -##### User Code - -```go -// Note: Since user is not making any custom modifications, we can just SetAnteHandlerOrder with the default AnteHandlers provided by each module in our preferred order -moduleManager.SetAnteHandlerOrder([]AnteHandler(AuthModuleAnteHandler, DistrModuleAnteHandler)) - -app.SetAnteHandler(mm.GetAnteHandler()) -``` - -#### Custom Workflow - -This is an example workflow for a user that wants to implement custom antehandler logic. In this example, the user wants to implement custom signature verification and change the order of antehandler so that validate memo runs before signature verification. - -##### User Code - -```go -// User can implement their own custom signature verification antehandler micro-function -func CustomSigVerify(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // do some custom signature verification logic -} -``` - -```go -// Micro-functions allow users to change order of when they get executed, and swap out default ante-functionality with their own custom logic. -// Note that users can still chain the default distribution module handler, and auth micro-function along with their custom ante function -moduleManager.SetAnteHandlerOrder([]AnteHandler(ValidateMemo, CustomSigVerify, DistrModuleAnteHandler)) -``` - -Pros: - -1. Allows for ante functionality to be as modular as possible. -2. For users that do not need custom ante-functionality, there is little difference between how antehandlers work and how BeginBlock and EndBlock work in ModuleManager. -3. Still easy to understand - -Cons: - -1. Cannot wrap antehandlers with decorators like you can with Weave. - -### Simple Decorators - -This approach takes inspiration from Weave's decorator design while trying to minimize the number of breaking changes to the SDK and maximizing simplicity. Like Weave decorators, this approach allows one `AnteDecorator` to wrap the next AnteHandler to do pre- and post-processing on the result. This is useful since decorators can do defer/cleanups after an AnteHandler returns as well as perform some setup beforehand. Unlike Weave decorators, these `AnteDecorator` functions can only wrap over the AnteHandler rather than the entire handler execution path. This is deliberate as we want decorators from different modules to perform authentication/validation on a `tx`. However, we do not want decorators being capable of wrapping and modifying the results of a `MsgHandler`. - -In addition, this approach will not break any core SDK API's. Since we preserve the notion of an AnteHandler and still set a single AnteHandler in baseapp, the decorator is simply an additional approach available for users that desire more customization. The API of modules (namely `x/auth`) may break with this approach, but the core API remains untouched. - -Allow Decorator interface that can be chained together to create an SDK AnteHandler. - -This allows users to choose between implementing an AnteHandler by themselves and setting it in the baseapp, or use the decorator pattern to chain their custom decorators with SDK provided decorators in the order they wish. - -```go -// An AnteDecorator wraps an AnteHandler, and can do pre- and post-processing on the next AnteHandler -type AnteDecorator interface { - AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) -} -``` - -```go -// ChainAnteDecorators will recursively link all of the AnteDecorators in the chain and return a final AnteHandler function -// This is done to preserve the ability to set a single AnteHandler function in the baseapp. -func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { - if len(chain) == 1 { - return func(ctx Context, tx Tx, simulate bool) { - chain[0].AnteHandle(ctx, tx, simulate, nil) - } - } - return func(ctx Context, tx Tx, simulate bool) { - chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:])) - } -} -``` - -#### Example Code - -Define AnteDecorator functions - -```go -// Setup GasMeter, catch OutOfGasPanic and handle appropriately -type SetUpContextDecorator struct{} - -func (sud SetUpContextDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) { - ctx.GasMeter = NewGasMeter(tx.Gas) - - defer func() { - // recover from OutOfGas panic and handle appropriately - } - - return next(ctx, tx, simulate) -} - -// Signature Verification decorator. Verify Signatures and move on -type SigVerifyDecorator struct{} - -func (svd SigVerifyDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) { - // verify sigs. Return error if invalid - - // call next antehandler if sigs ok - return next(ctx, tx, simulate) -} - -// User-defined Decorator. Can choose to pre- and post-process on AnteHandler -type UserDefinedDecorator struct{ - // custom fields -} - -func (udd UserDefinedDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) { - // pre-processing logic - - ctx, err = next(ctx, tx, simulate) - - // post-processing logic -} -``` - -Link AnteDecorators to create a final AnteHandler. Set this AnteHandler in baseapp. - -```go -// Create final antehandler by chaining the decorators together -antehandler := ChainAnteDecorators(NewSetUpContextDecorator(), NewSigVerifyDecorator(), NewUserDefinedDecorator()) - -// Set chained Antehandler in the baseapp -bapp.SetAnteHandler(antehandler) -``` - -Pros: - -1. Allows one decorator to pre- and post-process the next AnteHandler, similar to the Weave design. -2. Do not need to break baseapp API. Users can still set a single AnteHandler if they choose. - -Cons: - -1. Decorator pattern may have a deeply nested structure that is hard to understand, this is mitigated by having the decorator order explicitly listed in the `ChainAnteDecorators` function. -2. Does not make use of the ModuleManager design. Since this is already being used for BeginBlocker/EndBlocker, this proposal seems unaligned with that design pattern. - -## Consequences - -Since pros and cons are written for each approach, it is omitted from this section - -## References - -- [#4572](https://github.com/cosmos/cosmos-sdk/issues/4572): Modular AnteHandler Issue -- [#4582](https://github.com/cosmos/cosmos-sdk/pull/4583): Initial Implementation of Per-Module AnteHandler Approach -- [Weave Decorator Code](https://github.com/iov-one/weave/blob/master/handler.go#L35) -- [Weave Design Videos](https://vimeo.com/showcase/6189877) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-011-generalize-genesis-accounts.md b/versioned_docs/version-0.45/integrate/architecture/adr-011-generalize-genesis-accounts.md deleted file mode 100644 index db323966e..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-011-generalize-genesis-accounts.md +++ /dev/null @@ -1,170 +0,0 @@ -# ADR 011: Generalize Genesis Accounts - -## Changelog - -- 2019-08-30: initial draft - -## Context - -Currently, the SDK allows for custom account types; the `auth` keeper stores any type fulfilling its `Account` interface. However `auth` does not handle exporting or loading accounts to/from a genesis file, this is done by `genaccounts`, which only handles one of 4 concrete account types (`BaseAccount`, `ContinuousVestingAccount`, `DelayedVestingAccount` and `ModuleAccount`). - -Projects desiring to use custom accounts (say custom vesting accounts) need to fork and modify `genaccounts`. - -## Decision - -In summary, we will (un)marshal all accounts (interface types) directly using amino, rather than converting to `genaccounts`’s `GenesisAccount` type. Since doing this removes the majority of `genaccounts`'s code, we will merge `genaccounts` into `auth`. Marshalled accounts will be stored in `auth`'s genesis state. - -Detailed changes: - -### 1) (Un)Marshal accounts directly using amino - -The `auth` module's `GenesisState` gains a new field `Accounts`. Note these aren't of type `exported.Account` for reasons outlined in section 3. - -```go -// GenesisState - all auth state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - Accounts []GenesisAccount `json:"accounts" yaml:"accounts"` -} -``` - -Now `auth`'s `InitGenesis` and `ExportGenesis` (un)marshal accounts as well as the defined params. - -```go -// InitGenesis - Init store state from genesis data -func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) { - ak.SetParams(ctx, data.Params) - // load the accounts - for _, a := range data.Accounts { - acc := ak.NewAccount(ctx, a) // set account number - ak.SetAccount(ctx, acc) - } -} - -// ExportGenesis returns a GenesisState for a given context and keeper -func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState { - params := ak.GetParams(ctx) - - var genAccounts []exported.GenesisAccount - ak.IterateAccounts(ctx, func(account exported.Account) bool { - genAccount := account.(exported.GenesisAccount) - genAccounts = append(genAccounts, genAccount) - return false - }) - - return NewGenesisState(params, genAccounts) -} -``` - -### 2) Register custom account types on the `auth` codec - -The `auth` codec must have all custom account types registered to marshal them. We will follow the pattern established in `gov` for proposals. - -An example custom account definition: - -```go -import authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - -// Register the module account type with the auth module codec so it can decode module accounts stored in a genesis file -func init() { - authtypes.RegisterAccountTypeCodec(ModuleAccount{}, "cosmos-sdk/ModuleAccount") -} - -type ModuleAccount struct { - ... -``` - -The `auth` codec definition: - -```go -var ModuleCdc *codec.LegacyAmino - -func init() { - ModuleCdc = codec.NewLegacyAmino() - // register module msg's and Account interface - ... - // leave the codec unsealed -} - -// RegisterAccountTypeCodec registers an external account type defined in another module for the internal ModuleCdc. -func RegisterAccountTypeCodec(o interface{}, name string) { - ModuleCdc.RegisterConcrete(o, name, nil) -} -``` - -### 3) Genesis validation for custom account types - -Modules implement a `ValidateGenesis` method. As `auth` does not know of account implementations, accounts will need to validate themselves. - -We will unmarshal accounts into a `GenesisAccount` interface that includes a `Validate` method. - -```go -type GenesisAccount interface { - exported.Account - Validate() error -} -``` - -Then the `auth` `ValidateGenesis` function becomes: - -```go -// ValidateGenesis performs basic validation of auth genesis data returning an -// error for any failed validation criteria. -func ValidateGenesis(data GenesisState) error { - // Validate params - ... - - // Validate accounts - addrMap := make(map[string]bool, len(data.Accounts)) - for _, acc := range data.Accounts { - - // check for duplicated accounts - addrStr := acc.GetAddress().String() - if _, ok := addrMap[addrStr]; ok { - return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) - } - addrMap[addrStr] = true - - // check account specific validation - if err := acc.Validate(); err != nil { - return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error()) - } - - } - return nil -} -``` - -### 4) Move add-genesis-account cli to `auth` - -The `genaccounts` module contains a cli command to add base or vesting accounts to a genesis file. - -This will be moved to `auth`. We will leave it to projects to write their own commands to add custom accounts. An extensible cli handler, similar to `gov`, could be created but it is not worth the complexity for this minor use case. - -### 5) Update module and vesting accounts - -Under the new scheme, module and vesting account types need some minor updates: - -- Type registration on `auth`'s codec (shown above) -- A `Validate` method for each `Account` concrete type - -## Status - -Proposed - -## Consequences - -### Positive - -- custom accounts can be used without needing to fork `genaccounts` -- reduction in lines of code - -### Negative - -### Neutral - -- `genaccounts` module no longer exists -- accounts in genesis files are stored under `accounts` in `auth` rather than in the `genaccounts` module. --`add-genesis-account` cli command now in `auth` - -## References diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-012-state-accessors.md b/versioned_docs/version-0.45/integrate/architecture/adr-012-state-accessors.md deleted file mode 100644 index b66e23eb6..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-012-state-accessors.md +++ /dev/null @@ -1,155 +0,0 @@ -# ADR 012: State Accessors - -## Changelog - -- 2019 Sep 04: Initial draft - -## Context - -SDK modules currently use the `KVStore` interface and `Codec` to access their respective state. While -this provides a large degree of freedom to module developers, it is hard to modularize and the UX is -mediocre. - -First, each time a module tries to access the state, it has to marshal the value and set or get the -value and finally unmarshal. Usually this is done by declaring `Keeper.GetXXX` and `Keeper.SetXXX` functions, -which are repetitive and hard to maintain. - -Second, this makes it harder to align with the object capability theorem: the right to access the -state is defined as a `StoreKey`, which gives full access on the entire Merkle tree, so a module cannot -send the access right to a specific key-value pair (or a set of key-value pairs) to another module safely. - -Finally, because the getter/setter functions are defined as methods of a module's `Keeper`, the reviewers -have to consider the whole Merkle tree space when they reviewing a function accessing any part of the state. -There is no static way to know which part of the state that the function is accessing (and which is not). - -## Decision - -We will define a type named `Value`: - -```go -type Value struct { - m Mapping - key []byte -} -``` - -The `Value` works as a reference for a key-value pair in the state, where `Value.m` defines the key-value -space it will access and `Value.key` defines the exact key for the reference. - -We will define a type named `Mapping`: - -```go -type Mapping struct { - storeKey sdk.StoreKey - cdc *codec.LegacyAmino - prefix []byte -} -``` - -The `Mapping` works as a reference for a key-value space in the state, where `Mapping.storeKey` defines -the IAVL (sub-)tree and `Mapping.prefix` defines the optional subspace prefix. - -We will define the following core methods for the `Value` type: - -```go -// Get and unmarshal stored data, noop if not exists, panic if cannot unmarshal -func (Value) Get(ctx Context, ptr interface{}) {} - -// Get and unmarshal stored data, return error if not exists or cannot unmarshal -func (Value) GetSafe(ctx Context, ptr interface{}) {} - -// Get stored data as raw byte slice -func (Value) GetRaw(ctx Context) []byte {} - -// Marshal and set a raw value -func (Value) Set(ctx Context, o interface{}) {} - -// Check if a raw value exists -func (Value) Exists(ctx Context) bool {} - -// Delete a raw value value -func (Value) Delete(ctx Context) {} -``` - -We will define the following core methods for the `Mapping` type: - -```go -// Constructs key-value pair reference corresponding to the key argument in the Mapping space -func (Mapping) Value(key []byte) Value {} - -// Get and unmarshal stored data, noop if not exists, panic if cannot unmarshal -func (Mapping) Get(ctx Context, key []byte, ptr interface{}) {} - -// Get and unmarshal stored data, return error if not exists or cannot unmarshal -func (Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) - -// Get stored data as raw byte slice -func (Mapping) GetRaw(ctx Context, key []byte) []byte {} - -// Marshal and set a raw value -func (Mapping) Set(ctx Context, key []byte, o interface{}) {} - -// Check if a raw value exists -func (Mapping) Has(ctx Context, key []byte) bool {} - -// Delete a raw value value -func (Mapping) Delete(ctx Context, key []byte) {} -``` - -Each method of the `Mapping` type that is passed the arugments `ctx`, `key`, and `args...` will proxy -the call to `Mapping.Value(key)` with arguments `ctx` and `args...`. - -In addition, we will define and provide a common set of types derived from the `Value` type: - -```go -type Boolean struct { Value } -type Enum struct { Value } -type Integer struct { Value; enc IntEncoding } -type String struct { Value } -// ... -``` - -Where the encoding schemes can be different, `o` arguments in core methods are typed, and `ptr` arguments -in core methods are replaced by explicit return types. - -Finally, we will define a family of types derived from the `Mapping` type: - -```go -type Indexer struct { - m Mapping - enc IntEncoding -} -``` - -Where the `key` argument in core method is typed. - -Some of the properties of the accessor types are: - -- State access happens only when a function which takes a `Context` as an argument is invoked -- Accessor type structs give rights to access the state only that the struct is referring, no other -- Marshalling/Unmarshalling happens implicitly within the core methods - -## Status - -Proposed - -## Consequences - -### Positive - -- Serialization will be done automatically -- Shorter code size, less boilerplate, better UX -- References to the state can be transferred safely -- Explicit scope of accessing - -### Negative - -- Serialization format will be hidden -- Different architecture from the current, but the use of accessor types can be opt-in -- Type-specific types (e.g. `Boolean` and `Integer`) have to be defined manually - -### Neutral - -## References - -- [#4554](https://github.com/cosmos/cosmos-sdk/issues/4554) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-013-metrics.md b/versioned_docs/version-0.45/integrate/architecture/adr-013-metrics.md deleted file mode 100644 index cf27d8964..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-013-metrics.md +++ /dev/null @@ -1,157 +0,0 @@ -# ADR 013: Observability - -## Changelog - -- 20-01-2020: Initial Draft - -## Status - -Proposed - -## Context - -Telemetry is paramount into debugging and understanding what the application is doing and how it is -performing. We aim to expose metrics from modules and other core parts of the Cosmos SDK. - -In addition, we should aim to support multiple configurable sinks that an operator may choose from. -By default, when telemetry is enabled, the application should track and expose metrics that are -stored in-memory. The operator may choose to enable additional sinks, where we support only -[Prometheus](https://prometheus.io/) for now, as it's battle-tested, simple to setup, open source, -and is rich with ecosystem tooling. - -We must also aim to integrate metrics into the Cosmos SDK in the most seamless way possible such that -metrics may be added or removed at will and without much friction. To do this, we will use the -[go-metrics](https://github.com/armon/go-metrics) library. - -Finally, operators may enable telemetry along with specific configuration options. If enabled, metrics -will be exposed via `/metrics?format={text|prometheus}` via the API server. - -## Decision - -We will add an additional configuration block to `app.toml` that defines telemetry settings: - -```toml -############################################################################### -### Telemetry Configuration ### -############################################################################### - -[telemetry] - -# Prefixed with keys to separate services -service-name = {{ .Telemetry.ServiceName }} - -# Enabled enables the application telemetry functionality. When enabled, -# an in-memory sink is also enabled by default. Operators may also enabled -# other sinks such as Prometheus. -enabled = {{ .Telemetry.Enabled }} - -# Enable prefixing gauge values with hostname -enable-hostname = {{ .Telemetry.EnableHostname }} - -# Enable adding hostname to labels -enable-hostname-label = {{ .Telemetry.EnableHostnameLabel }} - -# Enable adding service to labels -enable-service-label = {{ .Telemetry.EnableServiceLabel }} - -# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. -prometheus-retention-time = {{ .Telemetry.PrometheusRetentionTime }} -``` - -The given configuration allows for two sinks -- in-memory and Prometheus. We create a `Metrics` -type that performs all the bootstrapping for the operator, so capturing metrics becomes seamless. - -```go -// Metrics defines a wrapper around application telemetry functionality. It allows -// metrics to be gathered at any point in time. When creating a Metrics object, -// internally, a global metrics is registered with a set of sinks as configured -// by the operator. In addition to the sinks, when a process gets a SIGUSR1, a -// dump of formatted recent metrics will be sent to STDERR. -type Metrics struct { - memSink *metrics.InmemSink - prometheusEnabled bool -} - -// Gather collects all registered metrics and returns a GatherResponse where the -// metrics are encoded depending on the type. Metrics are either encoded via -// Prometheus or JSON if in-memory. -func (m *Metrics) Gather(format string) (GatherResponse, error) { - switch format { - case FormatPrometheus: - return m.gatherPrometheus() - - case FormatText: - return m.gatherGeneric() - - case FormatDefault: - return m.gatherGeneric() - - default: - return GatherResponse{}, fmt.Errorf("unsupported metrics format: %s", format) - } -} -``` - -In addition, `Metrics` allows us to gather the current set of metrics at any given point in time. An -operator may also choose to send a signal, SIGUSR1, to dump and print formatted metrics to STDERR. - -During an application's bootstrapping and construction phase, if `Telemetry.Enabled` is `true`, the -API server will create an instance of a reference to `Metrics` object and will register a metrics -handler accordingly. - -```go -func (s *Server) Start(cfg config.Config) error { - // ... - - if cfg.Telemetry.Enabled { - m, err := telemetry.New(cfg.Telemetry) - if err != nil { - return err - } - - s.metrics = m - s.registerMetrics() - } - - // ... -} - -func (s *Server) registerMetrics() { - metricsHandler := func(w http.ResponseWriter, r *http.Request) { - format := strings.TrimSpace(r.FormValue("format")) - - gr, err := s.metrics.Gather(format) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to gather metrics: %s", err)) - return - } - - w.Header().Set("Content-Type", gr.ContentType) - _, _ = w.Write(gr.Metrics) - } - - s.Router.HandleFunc("/metrics", metricsHandler).Methods("GET") -} -``` - -Application developers may track counters, gauges, summaries, and key/value metrics. There is no -additional lifting required by modules to leverage profiling metrics. To do so, it's as simple as: - -```go -func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { - defer metrics.MeasureSince(time.Now(), "MintCoins") - // ... -} -``` - -## Consequences - -### Positive - -- Exposure into the performance and behavior of an application - -### Negative - -### Neutral - -## References diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-014-proportional-slashing.md b/versioned_docs/version-0.45/integrate/architecture/adr-014-proportional-slashing.md deleted file mode 100644 index 643b022e1..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-014-proportional-slashing.md +++ /dev/null @@ -1,85 +0,0 @@ -# ADR 14: Proportional Slashing - -## Changelog - -- 2019-10-15: Initial draft -- 2020-05-25: Removed correlation root slashing -- 2020-07-01: Updated to include S-curve function instead of linear - -## Context - -In Proof of Stake-based chains, centralization of consensus power amongst a small set of validators can cause harm to the network due to increased risk of censorship, liveness failure, fork attacks, etc. However, while this centralization causes a negative externality to the network, it is not directly felt by the delegators contributing towards delegating towards already large validators. We would like a way to pass on the negative externality cost of centralization onto those large validators and their delegators. - -## Decision - -### Design - -To solve this problem, we will implement a procedure called Proportional Slashing. The desire is that the larger a validator is, the more they should be slashed. The first naive attempt is to make a validator's slash percent proportional to their share of consensus voting power. - -``` -slash_amount = k * power // power is the faulting validator's voting power and k is some on-chain constant -``` - -However, this will incentivize validators with large amounts of stake to split up their voting power amongst accounts (sybil attack), so that if they fault, they all get slashed at a lower percent. The solution to this is to take into account not just a validator's own voting percentage, but also the voting percentage of all the other validators who get slashed in a specified time frame. - -``` -slash_amount = k * (power_1 + power_2 + ... + power_n) // where power_i is the voting power of the ith validator faulting in the specified time frame and k is some on-chain constant -``` - -Now, if someone splits a validator of 10% into two validators of 5% each which both fault, then they both fault in the same time frame, they both will get slashed at the sum 10% amount. - -However in practice, we likely don't want a linear relation between amount of stake at fault, and the percentage of stake to slash. In particular, solely 5% of stake double signing effectively did nothing to majorly threaten security, whereas 30% of stake being at fault clearly merits a large slashing factor, due to being very close to the point at which Tendermint security is threatened. A linear relation would require a factor of 6 gap between these two, whereas the difference in risk posed to the network is much larger. We propose using S-curves (formally [logistic functions](https://en.wikipedia.org/wiki/Logistic_function) to solve this). S-Curves capture the desired criterion quite well. They allow the slashing factor to be minimal for small values, and then grow very rapidly near some threshold point where the risk posed becomes notable. - -#### Parameterization - -This requires parameterizing a logistic function. It is very well understood how to parameterize this. It has four parameters: - -1) A minimum slashing factor -2) A maximum slashing factor -3) The inflection point of the S-curve (essentially where do you want to center the S) -4) The rate of growth of the S-curve (How elongated is the S) - -#### Correlation across non-sybil validators - -One will note, that this model doesn't differentiate between multiple validators run by the same operators vs validators run by different operators. This can be seen as an additional benefit in fact. It incentivizes validators to differentiate their setups from other validators, to avoid having correlated faults with them or else they risk a higher slash. So for example, operators should avoid using the same popular cloud hosting platforms or using the same Staking as a Service providers. This will lead to a more resilient and decentralized network. - -#### Griefing - -Griefing, the act of intentionally getting oneself slashed in order to make another's slash worse, could be a concern here. However, using the protocol described here, the attacker also gets equally impacted by the grief as the victim, so it would not provide much benefit to the griefer. - -### Implementation - -In the slashing module, we will add two queues that will track all of the recent slash events. For double sign faults, we will define "recent slashes" as ones that have occured within the last `unbonding period`. For liveness faults, we will define "recent slashes" as ones that have occured withing the last `jail period`. - -``` -type SlashEvent struct { - Address sdk.ValAddress - ValidatorVotingPercent sdk.Dec - SlashedSoFar sdk.Dec -} -``` - -These slash events will be pruned from the queue once they are older than their respective "recent slash period". - -Whenever a new slash occurs, a `SlashEvent` struct is created with the faulting validator's voting percent and a `SlashedSoFar` of 0. Because recent slash events are pruned before the unbonding period and unjail period expires, it should not be possible for the same validator to have multiple SlashEvents in the same Queue at the same time. - -We then will iterate over all the SlashEvents in the queue, adding their `ValidatorVotingPercent` to calculate the new percent to slash all the validators in the queue at, using the "Square of Sum of Roots" formula introduced above. - -Once we have the `NewSlashPercent`, we then iterate over all the `SlashEvent`s in the queue once again, and if `NewSlashPercent > SlashedSoFar` for that SlashEvent, we call the `staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent)` (we pass in the power of the validator before any slashes occured, so that we slash the right amount of tokens). We then set `SlashEvent.SlashedSoFar` amount to `NewSlashPercent`. - -## Status - -Proposed - -## Consequences - -### Positive - -- Increases decentralization by disincentivizing delegating to large validators -- Incentivizes Decorrelation of Validators -- More severely punishes attacks than accidental faults -- More flexibility in slashing rates parameterization - -### Negative - -- More computationally expensive than current implementation. Will require more data about "recent slashing events" to be stored on chain. diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-016-validator-consensus-key-rotation.md b/versioned_docs/version-0.45/integrate/architecture/adr-016-validator-consensus-key-rotation.md deleted file mode 100644 index 9b5d77e7d..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-016-validator-consensus-key-rotation.md +++ /dev/null @@ -1,125 +0,0 @@ -# ADR 016: Validator Consensus Key Rotation - -## Changelog - -- 2019 Oct 23: Initial draft -- 2019 Nov 28: Add key rotation fee - -## Context - -Validator consensus key rotation feature has been discussed and requested for a long time, for the sake of safer validator key management policy (e.g. https://github.com/tendermint/tendermint/issues/1136). So, we suggest one of the simplest form of validator consensus key rotation implementation mostly onto Cosmos-SDK. - -We don't need to make any update on consensus logic in Tendermint because Tendermint does not have any mapping information of consensus key and validator operator key, meaning that from Tendermint point of view, a consensus key rotation of a validator is simply a replacement of a consensus key to another. - -Also, it should be noted that this ADR includes only the simplest form of consensus key rotation without considering multiple consensus keys concept. Such multiple consensus keys concept shall remain a long term goal of Tendermint and Cosmos-SDK. - -## Decision - -### Pseudo procedure for consensus key rotation - -- create new random consensus key. -- create and broadcast a transaction with a `MsgRotateConsPubKey` that states the new consensus key is now coupled with the validator operator with signature from the validator's operator key. -- old consensus key becomes unable to participate on consensus immediately after the update of key mapping state on-chain. -- start validating with new consensus key. -- validators using HSM and KMS should update the consensus key in HSM to use the new rotated key after the height `h` when `MsgRotateConsPubKey` committed to the blockchain. - -### Considerations - -- consensus key mapping information management strategy - - store history of each key mapping changes in the kvstore. - - the state machine can search corresponding consensus key paired with given validator operator for any arbitrary height in a recent unbonding period. - - the state machine does not need any historical mapping information which is past more than unbonding period. -- key rotation costs related to LCD and IBC - - LCD and IBC will have traffic/computation burden when there exists frequent power changes - - In current Tendermint design, consensus key rotations are seen as power changes from LCD or IBC perspective - - Therefore, to minimize unnecessary frequent key rotation behavior, we limited maximum number of rotation in recent unbonding period and also applied exponentially increasing rotation fee -- limits - - a validator cannot rotate its consensus key more than `MaxConsPubKeyRotations` time for any unbonding period, to prevent spam. - - parameters can be decided by governance and stored in genesis file. -- key rotation fee - - a validator should pay `KeyRotationFee` to rotate the consensus key which is calculated as below - - `KeyRotationFee` = (max(`VotingPowerPercentage` *100, 1)* `InitialKeyRotationFee`) * 2^(number of rotations in `ConsPubKeyRotationHistory` in recent unbonding period) -- evidence module - - evidence module can search corresponding consensus key for any height from slashing keeper so that it can decide which consensus key is supposed to be used for given height. -- abci.ValidatorUpdate - - tendermint already has ability to change a consensus key by ABCI communication(`ValidatorUpdate`). - - validator consensus key update can be done via creating new + delete old by change the power to zero. - - therefore, we expect we even do not need to change tendermint codebase at all to implement this feature. -- new genesis parameters in `staking` module - - `MaxConsPubKeyRotations` : maximum number of rotation can be executed by a validator in recent unbonding period. default value 10 is suggested(11th key rotation will be rejected) - - `InitialKeyRotationFee` : the initial key rotation fee when no key rotation has happened in recent unbonding period. default value 1atom is suggested(1atom fee for the first key rotation in recent unbonding period) - -### Workflow - -1. The validator generates a new consensus keypair. -2. The validator generates and signs a `MsgRotateConsPubKey` tx with their operator key and new ConsPubKey - - ```go - type MsgRotateConsPubKey struct { - ValidatorAddress sdk.ValAddress - NewPubKey crypto.PubKey - } - ``` - -3. `handleMsgRotateConsPubKey` gets `MsgRotateConsPubKey`, calls `RotateConsPubKey` with emits event -4. `RotateConsPubKey` - - checks if `NewPubKey` is not duplicated on `ValidatorsByConsAddr` - - checks if the validator is does not exceed parameter `MaxConsPubKeyRotations` by iterating `ConsPubKeyRotationHistory` - - checks if the signing account has enough balance to pay `KeyRotationFee` - - pays `KeyRotationFee` to community fund - - overwrites `NewPubKey` in `validator.ConsPubKey` - - deletes old `ValidatorByConsAddr` - - `SetValidatorByConsAddr` for `NewPubKey` - - Add `ConsPubKeyRotationHistory` for tracking rotation - - ```go - type ConsPubKeyRotationHistory struct { - OperatorAddress sdk.ValAddress - OldConsPubKey crypto.PubKey - NewConsPubKey crypto.PubKey - RotatedHeight int64 - } - ``` - -5. `ApplyAndReturnValidatorSetUpdates` checks if there is `ConsPubKeyRotationHistory` with `ConsPubKeyRotationHistory.RotatedHeight == ctx.BlockHeight()` and if so, generates 2 `ValidatorUpdate` , one for a remove validator and one for create new validator - - ```go - abci.ValidatorUpdate{ - PubKey: tmtypes.TM2PB.PubKey(OldConsPubKey), - Power: 0, - } - - abci.ValidatorUpdate{ - PubKey: tmtypes.TM2PB.PubKey(NewConsPubKey), - Power: v.ConsensusPower(), - } - ``` - -6. at `previousVotes` Iteration logic of `AllocateTokens`, `previousVote` using `OldConsPubKey` match up with `ConsPubKeyRotationHistory`, and replace validator for token allocation -7. Migrate `ValidatorSigningInfo` and `ValidatorMissedBlockBitArray` from `OldConsPubKey` to `NewConsPubKey` - -- Note : All above features shall be implemented in `staking` module. - -## Status - -Proposed - -## Consequences - -### Positive - -- Validators can immediately or periodically rotate their consensus key to have better security policy -- improved security against Long-Range attacks (https://nearprotocol.com/blog/long-range-attacks-and-a-new-fork-choice-rule) given a validator throws away the old consensus key(s) - -### Negative - -- Slash module needs more computation because it needs to lookup corresponding consensus key of validators for each height -- frequent key rotations will make light client bisection less efficient - -### Neutral - -## References - -- on tendermint repo : https://github.com/tendermint/tendermint/issues/1136 -- on cosmos-sdk repo : https://github.com/cosmos/cosmos-sdk/issues/5231 -- about multiple consensus keys : https://github.com/tendermint/tendermint/issues/1758#issuecomment-545291698 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-017-historical-header-module.md b/versioned_docs/version-0.45/integrate/architecture/adr-017-historical-header-module.md deleted file mode 100644 index f0cdf4c07..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-017-historical-header-module.md +++ /dev/null @@ -1,61 +0,0 @@ -# ADR 17: Historical Header Module - -## Changelog - -- 26 November 2019: Start of first version -- 2 December 2019: Final draft of first version - -## Context - -In order for the Cosmos SDK to implement the [IBC specification](https://github.com/cosmos/ics), modules within the SDK must have the ability to introspect recent consensus states (validator sets & commitment roots) as proofs of these values on other chains must be checked during the handshakes. - -## Decision - -The application MUST store the most recent `n` headers in a persistent store. At first, this store MAY be the current Merklised store. A non-Merklised store MAY be used later as no proofs are necessary. - -The application MUST store this information by storing new headers immediately when handling `abci.RequestBeginBlock`: - -```golang -func BeginBlock(ctx sdk.Context, keeper HistoricalHeaderKeeper, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - info := HistoricalInfo{ - Header: ctx.BlockHeader(), - ValSet: keeper.StakingKeeper.GetAllValidators(ctx), // note that this must be stored in a canonical order - } - keeper.SetHistoricalInfo(ctx, ctx.BlockHeight(), info) - n := keeper.GetParamRecentHeadersToStore() - keeper.PruneHistoricalInfo(ctx, ctx.BlockHeight() - n) - // continue handling request -} -``` - -Alternatively, the application MAY store only the hash of the validator set. - -The application MUST make these past `n` committed headers available for querying by SDK modules through the `Keeper`'s `GetHistoricalInfo` function. This MAY be implemented in a new module, or it MAY also be integrated into an existing one (likely `x/staking` or `x/ibc`). - -`n` MAY be configured as a parameter store parameter, in which case it could be changed by `ParameterChangeProposal`s, although it will take some blocks for the stored information to catch up if `n` is increased. - -## Status - -Proposed. - -## Consequences - -Implementation of this ADR will require changes to the Cosmos SDK. It will not require changes to Tendermint. - -### Positive - -- Easy retrieval of headers & state roots for recent past heights by modules anywhere in the SDK. -- No RPC calls to Tendermint required. -- No ABCI alterations required. - -### Negative - -- Duplicates `n` headers data in Tendermint & the application (additional disk usage) - in the long term, an approach such as [this](https://github.com/tendermint/tendermint/issues/4210) might be preferable. - -### Neutral - -(none known) - -## References - -- [ICS 2: "Consensus state introspection"](https://github.com/cosmos/ibc/tree/master/spec/core/ics-002-client-semantics#consensus-state-introspection) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-018-extendable-voting-period.md b/versioned_docs/version-0.45/integrate/architecture/adr-018-extendable-voting-period.md deleted file mode 100644 index edd392b5e..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-018-extendable-voting-period.md +++ /dev/null @@ -1,66 +0,0 @@ -# ADR 18: Extendable Voting Periods - -## Changelog - -- 1 January 2020: Start of first version - -## Context - -Currently the voting period for all governance proposals is the same. However, this is suboptimal as all governance proposals do not require the same time period. For more non-contentious proposals, they can be dealt with more efficently with a faster period, while more contentious or complex proposals may need a longer period for extended discussion/consideration. - -## Decision - -We would like to design a mechanism for making the voting period of a governance proposal variable based on the demand of voters. We would like it to be based on the view of the governance participants, rather than just the proposer of a governance proposal (thus, allowing the proposer to select the voting period length is not sufficient). - -However, we would like to avoid the creation of an entire second voting process to determine the length of the voting period, as it just pushed the problem to determining the length of that first voting period. - -Thus, we propose the following mechanism: - -### Params - -- The current gov param `VotingPeriod` is to be replaced by a `MinVotingPeriod` param. This is the the default voting period that all governance proposal voting periods start with. -- There is a new gov param called `MaxVotingPeriodExtension`. - -### Mechanism - -There is a new `Msg` type called `MsgExtendVotingPeriod`, which can be sent by any staked account during a proposal's voting period. It allows the sender to unilaterally extend the length of the voting period by `MaxVotingPeriodExtension * sender's share of voting power`. Every address can only call `MsgExtendVotingPeriod` once per proposal. - -So for example, if the `MaxVotingPeriodExtension` is set to 100 Days, then anyone with 1% of voting power can extend the voting power by 1 day. If 33% of voting power has sent the message, the voting period will be extended by 33 days. Thus, if absolutely everyone chooses to extend the voting period, the absolute maximum voting period will be `MinVotingPeriod + MaxVotingPeriodExtension`. - -This system acts as a sort of distributed coordination, where individual stakers choosing to extend or not, allows the system the guage the conentiousness/complexity of the proposal. It is extremely unlikely that many stakers will choose to extend at the exact same time, it allows stakers to view how long others have already extended thus far, to decide whether or not to extend further. - -### Dealing with Unbonding/Redelegation - -There is one thing that needs to be addressed. How to deal with redelegation/unbonding during the voting period. If a staker of 5% calls `MsgExtendVotingPeriod` and then unbonds, does the voting period then decrease by 5 days again? This is not good as it can give people a false sense of how long they have to make their decision. For this reason, we want to design it such that the voting period length can only be extended, not shortened. To do this, the current extension amount is based on the highest percent that voted extension at any time. This is best explained by example: - -1. Let's say 2 stakers of voting power 4% and 3% respectively vote to extend. The voting period will be extended by 7 days. -2. Now the staker of 3% decides to unbond before the end of the voting period. The voting period extension remains 7 days. -3. Now, let's say another staker of 2% voting power decides to extend voting period. There is now 6% of active voting power choosing the extend. The voting power remains 7 days. -4. If a fourth staker of 10% chooses to extend now, there is a total of 16% of active voting power wishing to extend. The voting period will be extended to 16 days. - -### Delegators - -Just like votes in the actual voting period, delegators automatically inherit the extension of their validators. If their validator chooses to extend, their voting power will be used in the validator's extension. However, the delegator is unable to override their validator and "unextend" as that would contradict the "voting power length can only be ratcheted up" principle described in the previous section. However, a delegator may choose the extend using their personal voting power, if their validator has not done so. - -## Status - -Proposed - -## Consequences - -### Positive - -- More complex/contentious governance proposals will have more time to properly digest and deliberate - -### Negative - -- Governance process becomes more complex and requires more understanding to interact with effectively -- Can no longer predict when a governance proposal will end. Can't assume order in which governance proposals will end. - -### Neutral - -- The minimum voting period can be made shorter - -## References - -- [Cosmos Forum post where idea first originated](https://forum.cosmos.network/t/proposal-draft-reduce-governance-voting-period-to-7-days/3032/9) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-019-protobuf-state-encoding.md b/versioned_docs/version-0.45/integrate/architecture/adr-019-protobuf-state-encoding.md deleted file mode 100644 index cddf7092f..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-019-protobuf-state-encoding.md +++ /dev/null @@ -1,379 +0,0 @@ -# ADR 019: Protocol Buffer State Encoding - -## Changelog - -- 2020 Feb 15: Initial Draft -- 2020 Feb 24: Updates to handle messages with interface fields -- 2020 Apr 27: Convert usages of `oneof` for interfaces to `Any` -- 2020 May 15: Describe `cosmos_proto` extensions and amino compatibility -- 2020 Dec 4: Move and rename `MarshalAny` and `UnmarshalAny` into the `codec.Codec` interface. -- 2021 Feb 24: Remove mentions of `HybridCodec`, which has been abandoned in [#6843](https://github.com/cosmos/cosmos-sdk/pull/6843). - -## Status - -Accepted - -## Context - -Currently, the Cosmos SDK utilizes [go-amino](https://github.com/tendermint/go-amino/) for binary -and JSON object encoding over the wire bringing parity between logical objects and persistence objects. - -From the Amino docs: - -> Amino is an object encoding specification. It is a subset of Proto3 with an extension for interface -> support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) for more -> information on Proto3, which Amino is largely compatible with (but not with Proto2). -> -> The goal of the Amino encoding protocol is to bring parity into logic objects and persistence objects. - -Amino also aims to have the following goals (not a complete list): - -- Binary bytes must be decode-able with a schema. -- Schema must be upgradeable. -- The encoder and decoder logic must be reasonably simple. - -However, we believe that Amino does not fulfill these goals completely and does not fully meet the -needs of a truly flexible cross-language and multi-client compatible encoding protocol in the Cosmos SDK. -Namely, Amino has proven to be a big pain-point in regards to supporting object serialization across -clients written in various languages while providing virtually little in the way of true backwards -compatibility and upgradeability. Furthermore, through profiling and various benchmarks, Amino has -been shown to be an extremely large performance bottleneck in the Cosmos SDK 1. This is -largely reflected in the performance of simulations and application transaction throughput. - -Thus, we need to adopt an encoding protocol that meets the following criteria for state serialization: - -- Language agnostic -- Platform agnostic -- Rich client support and thriving ecosystem -- High performance -- Minimal encoded message size -- Codegen-based over reflection-based -- Supports backward and forward compatibility - -Note, migrating away from Amino should be viewed as a two-pronged approach, state and client encoding. -This ADR focuses on state serialization in the Cosmos SDK state machine. A corresponding ADR will be -made to address client-side encoding. - -## Decision - -We will adopt [Protocol Buffers](https://developers.google.com/protocol-buffers) for serializing -persisted structured data in the Cosmos SDK while providing a clean mechanism and developer UX for -applications wishing to continue to use Amino. We will provide this mechanism by updating modules to -accept a codec interface, `Marshaler`, instead of a concrete Amino codec. Furthermore, the Cosmos SDK -will provide two concrete implementations of the `Marshaler` interface: `AminoCodec` and `ProtoCodec`. - -- `AminoCodec`: Uses Amino for both binary and JSON encoding. -- `ProtoCodec`: Uses Protobuf for both binary and JSON encoding. - -Modules will use whichever codec that is instantiated in the app. By default, the SDK's `simapp` -instantiates a `ProtoCodec` as the concrete implementation of `Marshaler`, inside the `MakeTestEncodingConfig` -function. This can be easily overwritten by app developers if they so desire. - -The ultimate goal will be to replace Amino JSON encoding with Protobuf encoding and thus have -modules accept and/or extend `ProtoCodec`. Until then, Amino JSON is still provided for legacy use-cases. -A handful of places in the SDK still have Amino JSON hardcoded, such as the Legacy API REST endpoints -and the `x/params` store. They are planned to be converted to Protobuf in a gradual manner. - -### Module Codecs - -Modules that do not require the ability to work with and serialize interfaces, the path to Protobuf -migration is pretty straightforward. These modules are to simply migrate any existing types that -are encoded and persisted via their concrete Amino codec to Protobuf and have their keeper accept a -`Marshaler` that will be a `ProtoCodec`. This migration is simple as things will just work as-is. - -Note, any business logic that needs to encode primitive types like `bool` or `int64` should use -[gogoprotobuf](https://github.com/gogo/protobuf) Value types. - -Example: - -```go - ts, err := gogotypes.TimestampProto(completionTime) - if err != nil { - // ... - } - - bz := cdc.MustMarshal(ts) -``` - -However, modules can vary greatly in purpose and design and so we must support the ability for modules -to be able to encode and work with interfaces (e.g. `Account` or `Content`). For these modules, they -must define their own codec interface that extends `Marshaler`. These specific interfaces are unique -to the module and will contain method contracts that know how to serialize the needed interfaces. - -Example: - -```go -// x/auth/types/codec.go - -type Codec interface { - codec.Codec - - MarshalAccount(acc exported.Account) ([]byte, error) - UnmarshalAccount(bz []byte) (exported.Account, error) - - MarshalAccountJSON(acc exported.Account) ([]byte, error) - UnmarshalAccountJSON(bz []byte) (exported.Account, error) -} -``` - -### Usage of `Any` to encode interfaces - -In general, module-level .proto files should define messages which encode interfaces -using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). -After [extension discussion](https://github.com/cosmos/cosmos-sdk/issues/6030), -this was chosen as the preferred alternative to application-level `oneof`s -as in our original protobuf design. The arguments in favor of `Any` can be -summarized as follows: - -* `Any` provides a simpler, more consistent client UX for dealing with -interfaces than app-level `oneof`s that will need to be coordinated more -carefully across applications. Creating a generic transaction -signing library using `oneof`s may be cumbersome and critical logic may need -to be reimplemented for each chain -* `Any` provides more resistance against human error than `oneof` -* `Any` is generally simpler to implement for both modules and apps - -The main counter-argument to using `Any` centers around its additional space -and possibly performance overhead. The space overhead could be dealt with using -compression at the persistence layer in the future and the performance impact -is likely to be small. Thus, not using `Any` is seem as a pre-mature optimization, -with user experience as the higher order concern. - -Note, that given the SDK's decision to adopt the `Codec` interfaces described -above, apps can still choose to use `oneof` to encode state and transactions -but it is not the recommended approach. If apps do choose to use `oneof`s -instead of `Any` they will likely lose compatibility with client apps that -support multiple chains. Thus developers should think carefully about whether -they care more about what is possibly a pre-mature optimization or end-user -and client developer UX. - -### Safe usage of `Any` - -By default, the [gogo protobuf implementation of `Any`](https://godoc.org/github.com/gogo/protobuf/types) -uses [global type registration]( https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) -to decode values packed in `Any` into concrete -go types. This introduces a vulnerability where any malicious module -in the dependency tree could registry a type with the global protobuf registry -and cause it to be loaded and unmarshaled by a transaction that referenced -it in the `type_url` field. - -To prevent this, we introduce a type registration mechanism for decoding `Any` -values into concrete types through the `InterfaceRegistry` interface which -bears some similarity to type registration with Amino: - -```go -type InterfaceRegistry interface { - // RegisterInterface associates protoName as the public name for the - // interface passed in as iface - // Ex: - // registry.RegisterInterface("cosmos_sdk.Msg", (*sdk.Msg)(nil)) - RegisterInterface(protoName string, iface interface{}) - - // RegisterImplementations registers impls as a concrete implementations of - // the interface iface - // Ex: - // registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{}) - RegisterImplementations(iface interface{}, impls ...proto.Message) - -} -``` - -In addition to serving as a whitelist, `InterfaceRegistry` can also serve -to communicate the list of concrete types that satisfy an interface to clients. - -In .proto files: - -* fields which accept interfaces should be annotated with `cosmos_proto.accepts_interface` -using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface` -* interface implementations should be annotated with `cosmos_proto.implements_interface` -using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface` - -In the future, `protoName`, `cosmos_proto.accepts_interface`, `cosmos_proto.implements_interface` -may be used via code generation, reflection &/or static linting. - -The same struct that implements `InterfaceRegistry` will also implement an -interface `InterfaceUnpacker` to be used for unpacking `Any`s: - -```go -type InterfaceUnpacker interface { - // UnpackAny unpacks the value in any to the interface pointer passed in as - // iface. Note that the type in any must have been registered with - // RegisterImplementations as a concrete type for that interface - // Ex: - // var msg sdk.Msg - // err := ctx.UnpackAny(any, &msg) - // ... - UnpackAny(any *Any, iface interface{}) error -} -``` - -Note that `InterfaceRegistry` usage does not deviate from standard protobuf -usage of `Any`, it just introduces a security and introspection layer for -golang usage. - -`InterfaceRegistry` will be a member of `ProtoCodec` -described above. In order for modules to register interface types, app modules -can optionally implement the following interface: - -```go -type InterfaceModule interface { - RegisterInterfaceTypes(InterfaceRegistry) -} -``` - -The module manager will include a method to call `RegisterInterfaceTypes` on -every module that implements it in order to populate the `InterfaceRegistry`. - -### Using `Any` to encode state - -The SDK will provide support methods `MarshalInterface` and `UnmarshalInterface` to hide a complexity of wrapping interface types into `Any` and allow easy serialization. - -```go -import "github.com/cosmos/cosmos-sdk/codec" - -// note: eviexported.Evidence is an interface type -func MarshalEvidence(cdc codec.BinaryCodec, e eviexported.Evidence) ([]byte, error) { - return cdc.MarshalInterface(e) -} - -func UnmarshalEvidence(cdc codec.BinaryCodec, bz []byte) (eviexported.Evidence, error) { - var evi eviexported.Evidence - err := cdc.UnmarshalInterface(&evi, bz) - return err, nil -} -``` - -### Using `Any` in `sdk.Msg`s - -A similar concept is to be applied for messages that contain interfaces fields. -For example, we can define `MsgSubmitEvidence` as follows where `Evidence` is -an interface: - -```protobuf -// x/evidence/types/types.proto - -message MsgSubmitEvidence { - bytes submitter = 1 - [ - (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress" - ]; - google.protobuf.Any evidence = 2; -} -``` - -Note that in order to unpack the evidence from `Any` we do need a reference to -`InterfaceRegistry`. In order to reference evidence in methods like -`ValidateBasic` which shouldn't have to know about the `InterfaceRegistry`, we -introduce an `UnpackInterfaces` phase to deserialization which unpacks -interfaces before they're needed. - -### Unpacking Interfaces - -To implement the `UnpackInterfaces` phase of deserialization which unpacks -interfaces wrapped in `Any` before they're needed, we create an interface -that `sdk.Msg`s and other types can implement: - -```go -type UnpackInterfacesMessage interface { - UnpackInterfaces(InterfaceUnpacker) error -} -``` - -We also introduce a private `cachedValue interface{}` field onto the `Any` -struct itself with a public getter `GetCachedValue() interface{}`. - -The `UnpackInterfaces` method is to be invoked during message deserialization right -after `Unmarshal` and any interface values packed in `Any`s will be decoded -and stored in `cachedValue` for reference later. - -Then unpacked interface values can safely be used in any code afterwards -without knowledge of the `InterfaceRegistry` -and messages can introduce a simple getter to cast the cached value to the -correct interface type. - -This has the added benefit that unmarshaling of `Any` values only happens once -during initial deserialization rather than every time the value is read. Also, -when `Any` values are first packed (for instance in a call to -`NewMsgSubmitEvidence`), the original interface value is cached so that -unmarshaling isn't needed to read it again. - -`MsgSubmitEvidence` could implement `UnpackInterfaces`, plus a convenience getter -`GetEvidence` as follows: - -```go -func (msg MsgSubmitEvidence) UnpackInterfaces(ctx sdk.InterfaceRegistry) error { - var evi eviexported.Evidence - return ctx.UnpackAny(msg.Evidence, *evi) -} - -func (msg MsgSubmitEvidence) GetEvidence() eviexported.Evidence { - return msg.Evidence.GetCachedValue().(eviexported.Evidence) -} -``` - -### Amino Compatibility - -Our custom implementation of `Any` can be used transparently with Amino if used -with the proper codec instance. What this means is that interfaces packed within -`Any`s will be amino marshaled like regular Amino interfaces (assuming they -have been registered properly with Amino). - -In order for this functionality to work: - -- **all legacy code must use `*codec.LegacyAmino` instead of `*amino.Codec` which is - now a wrapper which properly handles `Any`** -- **all new code should use `Marshaler` which is compatible with both amino and - protobuf** -- Also, before v0.39, `codec.LegacyAmino` will be renamed to `codec.LegacyAmino`. - -### Why Wasn't X Chosen Instead - -For a more complete comparison to alternative protocols, see [here](https://codeburst.io/json-vs-protocol-buffers-vs-flatbuffers-a4247f8bda6f). - -### Cap'n Proto - -While [Cap’n Proto](https://capnproto.org/) does seem like an advantageous alternative to Protobuf -due to it's native support for interfaces/generics and built in canonicalization, it does lack the -rich client ecosystem compared to Protobuf and is a bit less mature. - -### FlatBuffers - -[FlatBuffers](https://google.github.io/flatbuffers/) is also a potentially viable alternative, with the -primary difference being that FlatBuffers does not need a parsing/unpacking step to a secondary -representation before you can access data, often coupled with per-object memory allocation. - -However, it would require great efforts into research and full understanding the scope of the migration -and path forward -- which isn't immediately clear. In addition, FlatBuffers aren't designed for -untrusted inputs. - -## Future Improvements & Roadmap - -In the future we may consider a compression layer right above the persistence -layer which doesn't change tx or merkle tree hashes, but reduces the storage -overhead of `Any`. In addition, we may adopt protobuf naming conventions which -make type URLs a bit more concise while remaining descriptive. - -Additional code generation support around the usage of `Any` is something that -could also be explored in the future to make the UX for go developers more -seamless. - -## Consequences - -### Positive - -- Significant performance gains. -- Supports backward and forward type compatibility. -- Better support for cross-language clients. - -### Negative - -- Learning curve required to understand and implement Protobuf messages. -- Slightly larger message size due to use of `Any`, although this could be offset - by a compression layer in the future - -### Neutral - -## References - -1. https://github.com/cosmos/cosmos-sdk/issues/4977 -2. https://github.com/cosmos/cosmos-sdk/issues/5444 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-020-protobuf-transaction-encoding.md b/versioned_docs/version-0.45/integrate/architecture/adr-020-protobuf-transaction-encoding.md deleted file mode 100644 index 2efe583cf..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-020-protobuf-transaction-encoding.md +++ /dev/null @@ -1,464 +0,0 @@ -# ADR 020: Protocol Buffer Transaction Encoding - -## Changelog - -- 2020 March 06: Initial Draft -- 2020 March 12: API Updates -- 2020 April 13: Added details on interface `oneof` handling -- 2020 April 30: Switch to `Any` -- 2020 May 14: Describe public key encoding -- 2020 June 08: Store `TxBody` and `AuthInfo` as bytes in `SignDoc`; Document `TxRaw` as broadcast and storage type. -- 2020 August 07: Use ADR 027 for serializing `SignDoc`. -- 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`, as discussed in [#6966](https://github.com/cosmos/cosmos-sdk/issues/6966). -- 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`. -- 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface. -- 2021 Feb 24: The SDK does not use Tendermint's `PubKey` interface anymore, but its own `cryptotypes.PubKey`. Updates to reflect this. -- 2021 May 3: Rename `clientCtx.JSONMarshaler` to `clientCtx.JSONCodec`. -- 2021 June 10: Add `clientCtx.Codec: codec.Codec`. - -## Status - -Accepted - -## Context - -This ADR is a continuation of the motivation, design, and context established in -[ADR 019](./adr-019-protobuf-state-encoding.md), namely, we aim to design the -Protocol Buffer migration path for the client-side of the Cosmos SDK. - -Specifically, the client-side migration path primarily includes tx generation and -signing, message construction and routing, in addition to CLI & REST handlers and -business logic (i.e. queriers). - -With this in mind, we will tackle the migration path via two main areas, txs and -querying. However, this ADR solely focuses on transactions. Querying should be -addressed in a future ADR, but it should build off of these proposals. - -Based on detailed discussions ([\#6030](https://github.com/cosmos/cosmos-sdk/issues/6030) -and [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078)), the original -design for transactions was changed substantially from an `oneof` /JSON-signing -approach to the approach described below. - -## Decision - -### Transactions - -Since interface values are encoded with `google.protobuf.Any` in state (see [ADR 019](adr-019-protobuf-state-encoding.md)), -`sdk.Msg`s are encoding with `Any` in transactions. - -One of the main goals of using `Any` to encode interface values is to have a -core set of types which is reused by apps so that -clients can safely be compatible with as many chains as possible. - -It is one of the goals of this specification to provide a flexible cross-chain transaction -format that can serve a wide variety of use cases without breaking client -compatibility. - -In order to facilitate signing, transactions are separated into `TxBody`, -which will be re-used by `SignDoc` below, and `signatures`: - -```proto -// types/types.proto -package cosmos_sdk.v1; - -message Tx { - TxBody body = 1; - AuthInfo auth_info = 2; - // A list of signatures that matches the length and order of AuthInfo's signer_infos to - // allow connecting signature meta information like public key and signing mode by position. - repeated bytes signatures = 3; -} - -// A variant of Tx that pins the signer's exact binary represenation of body and -// auth_info. This is used for signing, broadcasting and verification. The binary -// `serialize(tx: TxRaw)` is stored in Tendermint and the hash `sha256(serialize(tx: TxRaw))` -// becomes the "txhash", commonly used as the transaction ID. -message TxRaw { - // A protobuf serialization of a TxBody that matches the representation in SignDoc. - bytes body = 1; - // A protobuf serialization of an AuthInfo that matches the representation in SignDoc. - bytes auth_info = 2; - // A list of signatures that matches the length and order of AuthInfo's signer_infos to - // allow connecting signature meta information like public key and signing mode by position. - repeated bytes signatures = 3; -} - -message TxBody { - // A list of messages to be executed. The required signers of those messages define - // the number and order of elements in AuthInfo's signer_infos and Tx's signatures. - // Each required signer address is added to the list only the first time it occurs. - // - // By convention, the first required signer (usually from the first message) is referred - // to as the primary signer and pays the fee for the whole transaction. - repeated google.protobuf.Any messages = 1; - string memo = 2; - int64 timeout_height = 3; - repeated google.protobuf.Any extension_options = 1023; -} - -message AuthInfo { - // This list defines the signing modes for the required signers. The number - // and order of elements must match the required signers from TxBody's messages. - // The first element is the primary signer and the one which pays the fee. - repeated SignerInfo signer_infos = 1; - // The fee can be calculated based on the cost of evaluating the body and doing signature verification of the signers. This can be estimated via simulation. - Fee fee = 2; -} - -message SignerInfo { - // The public key is optional for accounts that already exist in state. If unset, the - // verifier can use the required signer address for this position and lookup the public key. - google.protobuf.Any public_key = 1; - // ModeInfo describes the signing mode of the signer and is a nested - // structure to support nested multisig pubkey's - ModeInfo mode_info = 2; - // sequence is the sequence of the account, which describes the - // number of committed transactions signed by a given address. It is used to prevent - // replay attacks. - uint64 sequence = 3; -} - -message ModeInfo { - oneof sum { - Single single = 1; - Multi multi = 2; - } - - // Single is the mode info for a single signer. It is structured as a message - // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the future - message Single { - SignMode mode = 1; - } - - // Multi is the mode info for a multisig public key - message Multi { - // bitarray specifies which keys within the multisig are signing - CompactBitArray bitarray = 1; - // mode_infos is the corresponding modes of the signers of the multisig - // which could include nested multisig public keys - repeated ModeInfo mode_infos = 2; - } -} - -enum SignMode { - SIGN_MODE_UNSPECIFIED = 0; - - SIGN_MODE_DIRECT = 1; - - SIGN_MODE_TEXTUAL = 2; - - SIGN_MODE_LEGACY_AMINO_JSON = 127; -} -``` - -As will be discussed below, in order to include as much of the `Tx` as possible -in the `SignDoc`, `SignerInfo` is separated from signatures so that only the -raw signatures themselves live outside of what is signed over. - -Because we are aiming for a flexible, extensible cross-chain transaction -format, new transaction processing options should be added to `TxBody` as soon -those use cases are discovered, even if they can't be implemented yet. - -Because there is coordination overhead in this, `TxBody` includes an -`extension_options` field which can be used for any transaction processing -options that are not already covered. App developers should, nevertheless, -attempt to upstream important improvements to `Tx`. - -### Signing - -All of the signing modes below aim to provide the following guarantees: - -- **No Malleability**: `TxBody` and `AuthInfo` cannot change once the transaction - is signed -- **Predictable Gas**: if I am signing a transaction where I am paying a fee, - the final gas is fully dependent on what I am signing - -These guarantees give the maximum amount confidence to message signers that -manipulation of `Tx`s by intermediaries can't result in any meaningful changes. - -#### `SIGN_MODE_DIRECT` - -The "direct" signing behavior is to sign the raw `TxBody` bytes as broadcast over -the wire. This has the advantages of: - -- requiring the minimum additional client capabilities beyond a standard protocol - buffers implementation -- leaving effectively zero holes for transaction malleability (i.e. there are no - subtle differences between the signing and encoding formats which could - potentially be exploited by an attacker) - -Signatures are structured using the `SignDoc` below which reuses the serialization of -`TxBody` and `AuthInfo` and only adds the fields which are needed for signatures: - -```proto -// types/types.proto -message SignDoc { - // A protobuf serialization of a TxBody that matches the representation in TxRaw. - bytes body = 1; - // A protobuf serialization of an AuthInfo that matches the representation in TxRaw. - bytes auth_info = 2; - string chain_id = 3; - uint64 account_number = 4; -} -``` - -In order to sign in the default mode, clients take the following steps: - -1. Serialize `TxBody` and `AuthInfo` using any valid protobuf implementation. -2. Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). -3. Sign the encoded `SignDoc` bytes. -4. Build a `TxRaw` and serialize it for broadcasting. - -Signature verification is based on comparing the raw `TxBody` and `AuthInfo` -bytes encoded in `TxRaw` not based on any ["canonicalization"](https://github.com/regen-network/canonical-proto3) -algorithm which creates added complexity for clients in addition to preventing -some forms of upgradeability (to be addressed later in this document). - -Signature verifiers do: - -1. Deserialize a `TxRaw` and pull out `body` and `auth_info`. -2. Create a list of required signer addresses from the messages. -3. For each required signer: - - Pull account number and sequence from the state. - - Obtain the public key either from state or `AuthInfo`'s `signer_infos`. - - Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). - - Verify the signature at the the same list position against the serialized `SignDoc`. - -#### `SIGN_MODE_LEGACY_AMINO` - -In order to support legacy wallets and exchanges, Amino JSON will be temporarily -supported transaction signing. Once wallets and exchanges have had a -chance to upgrade to protobuf based signing, this option will be disabled. In -the meantime, it is foreseen that disabling the current Amino signing would cause -too much breakage to be feasible. Note that this is mainly a requirement of the -Cosmos Hub and other chains may choose to disable Amino signing immediately. - -Legacy clients will be able to sign a transaction using the current Amino -JSON format and have it encoded to protobuf using the REST `/tx/encode` -endpoint before broadcasting. - -#### `SIGN_MODE_TEXTUAL` - -As was discussed extensively in [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078), -there is a desire for a human-readable signing encoding, especially for hardware -wallets like the [Ledger](https://www.ledger.com) which display -transaction contents to users before signing. JSON was an attempt at this but -falls short of the ideal. - -`SIGN_MODE_TEXTUAL` is intended as a placeholder for a human-readable -encoding which will replace Amino JSON. This new encoding should be even more -focused on readability than JSON, possibly based on formatting strings like -[MessageFormat](http://userguide.icu-project.org/formatparse/messages). - -In order to ensure that the new human-readable format does not suffer from -transaction malleability issues, `SIGN_MODE_TEXTUAL` -requires that the _human-readable bytes are concatenated with the raw `SignDoc`_ -to generate sign bytes. - -Multiple human-readable formats (maybe even localized messages) may be supported -by `SIGN_MODE_TEXTUAL` when it is implemented. - -### Unknown Field Filtering - -Unknown fields in protobuf messages should generally be rejected by transaction -processors because: - -- important data may be present in the unknown fields, that if ignored, will - cause unexpected behavior for clients -- they present a malleability vulnerability where attackers can bloat tx size - by adding random uninterpreted data to unsigned content (i.e. the master `Tx`, - not `TxBody`) - -There are also scenarios where we may choose to safely ignore unknown fields -(https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-624400188) to -provide graceful forwards compatibility with newer clients. - -We propose that field numbers with bit 11 set (for most use cases this is -the range of 1024-2047) be considered non-critical fields that can safely be -ignored if unknown. - -To handle this we will need a unknown field filter that: - -- always rejects unknown fields in unsigned content (i.e. top-level `Tx` and - unsigned parts of `AuthInfo` if present based on the signing mode) -- rejects unknown fields in all messages (including nested `Any`s) other than - fields with bit 11 set - -This will likely need to be a custom protobuf parser pass that takes message bytes -and `FileDescriptor`s and returns a boolean result. - -### Public Key Encoding - -Public keys in the Cosmos SDK implement the `cryptotypes.PubKey` interface. -We propose to use `Any` for protobuf encoding as we are doing with other interfaces (for example, in `BaseAccount.PubKey` and `SignerInfo.PublicKey`). -The following public keys are implemented: secp256k1, secp256r1, ed25519 and legacy-multisignature. - -Ex: - -```proto -message PubKey { - bytes key = 1; -} -``` - -`multisig.LegacyAminoPubKey` has an array of `Any`'s member to support any -protobuf public key type. - -Apps should only attempt to handle a registered set of public keys that they -have tested. The provided signature verification ante handler decorators will -enforce this. - -### CLI & REST - -Currently, the REST and CLI handlers encode and decode types and txs via Amino -JSON encoding using a concrete Amino codec. Being that some of the types dealt with -in the client can be interfaces, similar to how we described in [ADR 019](./adr-019-protobuf-state-encoding.md), -the client logic will now need to take a codec interface that knows not only how -to handle all the types, but also knows how to generate transactions, signatures, -and messages. - -```go -type AccountRetriever interface { - GetAccount(clientCtx Context, addr sdk.AccAddress) (client.Account, error) - GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (client.Account, int64, error) - EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error - GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) -} - -type Generator interface { - NewTx() TxBuilder - NewFee() ClientFee - NewSignature() ClientSignature - MarshalTx(tx types.Tx) ([]byte, error) -} - -type TxBuilder interface { - GetTx() sdk.Tx - - SetMsgs(...sdk.Msg) error - GetSignatures() []sdk.Signature - SetSignatures(...sdk.Signature) - GetFee() sdk.Fee - SetFee(sdk.Fee) - GetMemo() string - SetMemo(string) -} -``` - -We then update `Context` to have new fields: `Codec`, `TxGenerator`, -and `AccountRetriever`, and we update `AppModuleBasic.GetTxCmd` to take -a `Context` which should have all of these fields pre-populated. - -Each client method should then use one of the `Init` methods to re-initialize -the pre-populated `Context`. `tx.GenerateOrBroadcastTx` can be used to -generate or broadcast a transaction. For example: - -```go -import "github.com/spf13/cobra" -import "github.com/cosmos/cosmos-sdk/client" -import "github.com/cosmos/cosmos-sdk/client/tx" - -func NewCmdDoSomething(clientCtx client.Context) *cobra.Command { - return &cobra.Command{ - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := ctx.InitWithInput(cmd.InOrStdin()) - msg := NewSomeMsg{...} - tx.GenerateOrBroadcastTx(clientCtx, msg) - }, - } -} -``` - -## Future Improvements - -### `SIGN_MODE_TEXTUAL` specification - -A concrete specification and implementation of `SIGN_MODE_TEXTUAL` is intended -as a near-term future improvement so that the ledger app and other wallets -can gracefully transition away from Amino JSON. - -### `SIGN_MODE_DIRECT_AUX` - -(\*Documented as option (3) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933) - -We could add a mode `SIGN_MODE_DIRECT_AUX` -to support scenarios where multiple signatures -are being gathered into a single transaction but the message composer does not -yet know which signatures will be included in the final transaction. For instance, -I may have a 3/5 multisig wallet and want to send a `TxBody` to all 5 -signers to see who signs first. As soon as I have 3 signatures then I will go -ahead and build the full transaction. - -With `SIGN_MODE_DIRECT`, each signer needs -to sign the full `AuthInfo` which includes the full list of all signers and -their signing modes, making the above scenario very hard. - -`SIGN_MODE_DIRECT_AUX` would allow "auxiliary" signers to create their signature -using only `TxBody` and their own `PublicKey`. This allows the full list of -signers in `AuthInfo` to be delayed until signatures have been collected. - -An "auxiliary" signer is any signer besides the primary signer who is paying -the fee. For the primary signer, the full `AuthInfo` is actually needed to calculate gas and fees -because that is dependent on how many signers and which key types and signing -modes they are using. Auxiliary signers, however, do not need to worry about -fees or gas and thus can just sign `TxBody`. - -To generate a signature in `SIGN_MODE_DIRECT_AUX` these steps would be followed: - -1. Encode `SignDocAux` (with the same requirement that fields must be serialized - in order): - -```proto -// types/types.proto -message SignDocAux { - bytes body_bytes = 1; - // PublicKey is included in SignDocAux : - // 1. as a special case for multisig public keys. For multisig public keys, - // the signer should use the top-level multisig public key they are signing - // against, not their own public key. This is to prevent against a form - // of malleability where a signature could be taken out of context of the - // multisig key that was intended to be signed for - // 2. to guard against scenario where configuration information is encoded - // in public keys (it has been proposed) such that two keys can generate - // the same signature but have different security properties - // - // By including it here, the composer of AuthInfo cannot reference the - // a public key variant the signer did not intend to use - PublicKey public_key = 2; - string chain_id = 3; - uint64 account_number = 4; -} -``` - -2. Sign the encoded `SignDocAux` bytes -3. Send their signature and `SignerInfo` to primary signer who will then - sign and broadcast the final transaction (with `SIGN_MODE_DIRECT` and `AuthInfo` - added) once enough signatures have been collected - -### `SIGN_MODE_DIRECT_RELAXED` - -(_Documented as option (1)(a) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933_) - -This is a variation of `SIGN_MODE_DIRECT` where multiple signers wouldn't need to -coordinate public keys and signing modes in advance. It would involve an alternate -`SignDoc` similar to `SignDocAux` above with fee. This could be added in the future -if client developers found the burden of collecting public keys and modes in advance -too burdensome. - -## Consequences - -### Positive - -- Significant performance gains. -- Supports backward and forward type compatibility. -- Better support for cross-language clients. -- Multiple signing modes allow for greater protocol evolution - -### Negative - -- `google.protobuf.Any` type URLs increase transaction size although the effect - may be negligible or compression may be able to mitigate it. - -### Neutral - -## References diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-021-protobuf-query-encoding.md b/versioned_docs/version-0.45/integrate/architecture/adr-021-protobuf-query-encoding.md deleted file mode 100644 index 60830de3c..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-021-protobuf-query-encoding.md +++ /dev/null @@ -1,256 +0,0 @@ -# ADR 021: Protocol Buffer Query Encoding - -## Changelog - -- 2020 March 27: Initial Draft - -## Status - -Accepted - -## Context - -This ADR is a continuation of the motivation, design, and context established in -[ADR 019](./adr-019-protobuf-state-encoding.md) and -[ARD 020](./adr-019-protobuf-transaction-encoding.md), namely, we aim to design the -Protocol Buffer migration path for the client-side of the Cosmos SDK. - -This ADR continues from [ARD 020](./adr-020-protobuf-transaction-encoding.md) -to specify the encoding of queries. - -## Decision - -### Custom Query Definition - -Modules define custom queries through a protocol buffers `service` definition. -These `service` definitions are generally associated with and used by the -GRPC protocol. However, the protocol buffers specification indicates that -they can be used more generically by any request/response protocol that uses -protocol buffer encoding. Thus, we can use `service` definitions for specifying -custom ABCI queries and even reuse a substantial amount of the GRPC infrastructure. - -Each module with custom queries should define a service canonically named `Query`: - -```proto -// x/bank/types/types.proto - -service Query { - rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { } - rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { } -} -``` - -#### Handling of Interface Types - -Modules that use interface types and need true polymorphism generally force a -`oneof` up to the app-level that provides the set of concrete implementations of -that interface that the app supports. While app's are welcome to do the same for -queries and implement an app-level query service, it is recommended that modules -provide query methods that expose these interfaces via `google.protobuf.Any`. -There is a concern on the transaction level that the overhead of `Any` is too -high to justify its usage. However for queries this is not a concern, and -providing generic module-level queries that use `Any` does not preclude apps -from also providing app-level queries that return use the app-level `oneof`s. - -A hypothetical example for the `gov` module would look something like: - -```proto -// x/gov/types/types.proto - -import "google/protobuf/any.proto"; - -service Query { - rpc GetProposal(GetProposalParams) returns (AnyProposal) { } -} - -message AnyProposal { - ProposalBase base = 1; - google.protobuf.Any content = 2; -} -``` - -### Custom Query Implementation - -In order to implement the query service, we can reuse the existing [gogo protobuf](https://github.com/gogo/protobuf) -grpc plugin, which for a service named `Query` generates an interface named -`QueryServer` as below: - -```go -type QueryServer interface { - QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) - QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) -} -``` - -The custom queries for our module are implemented by implementing this interface. - -The first parameter in this generated interface is a generic `context.Context`, -whereas querier methods generally need an instance of `sdk.Context` to read -from the store. Since arbitrary values can be attached to `context.Context` -using the `WithValue` and `Value` methods, the SDK should provide a function -`sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided -`context.Context`. - -An example implementation of `QueryBalance` for the bank module as above would -look something like: - -```go -type Querier struct { - Keeper -} - -func (q Querier) QueryBalance(ctx context.Context, params *types.QueryBalanceParams) (*sdk.Coin, error) { - balance := q.GetBalance(sdk.UnwrapSDKContext(ctx), params.Address, params.Denom) - return &balance, nil -} -``` - -### Custom Query Registration and Routing - -Query server implementations as above would be registered with `AppModule`s using -a new method `RegisterQueryService(grpc.Server)` which could be implemented simply -as below: - -```go -// x/bank/module.go -func (am AppModule) RegisterQueryService(server grpc.Server) { - types.RegisterQueryServer(server, keeper.Querier{am.keeper}) -} -``` - -Underneath the hood, a new method `RegisterService(sd *grpc.ServiceDesc, handler interface{})` -will be added to the existing `baseapp.QueryRouter` to add the queries to the custom -query routing table (with the routing method being described below). -The signature for this method matches the existing -`RegisterServer` method on the GRPC `Server` type where `handler` is the custom -query server implementation described above. - -GRPC-like requests are routed by the service name (ex. `cosmos_sdk.x.bank.v1.Query`) -and method name (ex. `QueryBalance`) combined with `/`s to form a full -method name (ex. `/cosmos_sdk.x.bank.v1.Query/QueryBalance`). This gets translated -into an ABCI query as `custom/cosmos_sdk.x.bank.v1.Query/QueryBalance`. Service handlers -registered with `QueryRouter.RegisterService` will be routed this way. - -Beyond the method name, GRPC requests carry a protobuf encoded payload, which maps naturally -to `RequestQuery.Data`, and receive a protobuf encoded response or error. Thus -there is a quite natural mapping of GRPC-like rpc methods to the existing -`sdk.Query` and `QueryRouter` infrastructure. - -This basic specification allows us to reuse protocol buffer `service` definitions -for ABCI custom queries substantially reducing the need for manual decoding and -encoding in query methods. - -### GRPC Protocol Support - -In addition to providing an ABCI query pathway, we can easily provide a GRPC -proxy server that routes requests in the GRPC protocol to ABCI query requests -under the hood. In this way, clients could use their host languages' existing -GRPC implementations to make direct queries against Cosmos SDK app's using -these `service` definitions. In order for this server to work, the `QueryRouter` -on `BaseApp` will need to expose the service handlers registered with -`QueryRouter.RegisterService` to the proxy server implementation. Nodes could -launch the proxy server on a separate port in the same process as the ABCI app -with a command-line flag. - -### REST Queries and Swagger Generation - -[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) is a project that -translates REST calls into GRPC calls using special annotations on service -methods. Modules that want to expose REST queries should add `google.api.http` -annotations to their `rpc` methods as in this example below. - -```proto -// x/bank/types/types.proto - -service Query { - rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { - option (google.api.http) = { - get: "/x/bank/v1/balance/{address}/{denom}" - }; - } - rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { - option (google.api.http) = { - get: "/x/bank/v1/balances/{address}" - }; - } -} -``` - -grpc-gateway will work direcly against the GRPC proxy described above which will -translate requests to ABCI queries under the hood. grpc-gateway can also -generate Swagger definitions automatically. - -In the current implementation of REST queries, each module needs to implement -REST queries manually in addition to ABCI querier methods. Using the grpc-gateway -approach, there will be no need to generate separate REST query handlers, just -query servers as described above as grpc-gateway handles the translation of protobuf -to REST as well as Swagger definitions. - -The SDK should provide CLI commands for apps to start GRPC gateway either in -a separate process or the same process as the ABCI app, as well as provide a -command for generating grpc-gateway proxy `.proto` files and the `swagger.json` -file. - -### Client Usage - -The gogo protobuf grpc plugin generates client interfaces in addition to server -interfaces. For the `Query` service defined above we would get a `QueryClient` -interface like: - -```go -type QueryClient interface { - QueryBalance(ctx context.Context, in *QueryBalanceParams, opts ...grpc.CallOption) (*types.Coin, error) - QueryAllBalances(ctx context.Context, in *QueryAllBalancesParams, opts ...grpc.CallOption) (*QueryAllBalancesResponse, error) -} -``` - -Via a small patch to gogo protobuf ([gogo/protobuf#675](https://github.com/gogo/protobuf/pull/675)) -we have tweaked the grpc codegen to use an interface rather than concrete type -for the generated client struct. This allows us to also reuse the GRPC infrastructure -for ABCI client queries. - -1Context`will receive a new method`QueryConn`that returns a`ClientConn` -that routes calls to ABCI queries - -Clients (such as CLI methods) will then be able to call query methods like this: - -```go -clientCtx := client.NewContext() -queryClient := types.NewQueryClient(clientCtx.QueryConn()) -params := &types.QueryBalanceParams{addr, denom} -result, err := queryClient.QueryBalance(gocontext.Background(), params) -``` - -### Testing - -Tests would be able to create a query client directly from keeper and `sdk.Context` -references using a `QueryServerTestHelper` as below: - -```go -queryHelper := baseapp.NewQueryServerTestHelper(ctx) -types.RegisterQueryServer(queryHelper, keeper.Querier{app.BankKeeper}) -queryClient := types.NewQueryClient(queryHelper) -``` - -## Future Improvements - -## Consequences - -### Positive - -* greatly simplified querier implementation (no manual encoding/decoding) -* easy query client generation (can use existing grpc and swagger tools) -* no need for REST query implementations -* type safe query methods (generated via grpc plugin) -* going forward, there will be less breakage of query methods because of the -backwards compatibility guarantees provided by buf - -### Negative - -* all clients using the existing ABCI/REST queries will need to be refactored -for both the new GRPC/REST query paths as well as protobuf/proto-json encoded -data, but this is more or less unavoidable in the protobuf refactoring - -### Neutral - -## References diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-022-custom-panic-handling.md b/versioned_docs/version-0.45/integrate/architecture/adr-022-custom-panic-handling.md deleted file mode 100644 index 034f2e734..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-022-custom-panic-handling.md +++ /dev/null @@ -1,213 +0,0 @@ -# ADR 022: Custom BaseApp panic handling - -## Changelog - -- 2020 Apr 24: Initial Draft - -## Context - -The current implementation of BaseApp does not allow developers to write custom error handlers during panic recovery -[runTx()](https://github.com/cosmos/cosmos-sdk/blob/bad4ca75f58b182f600396ca350ad844c18fc80b/baseapp/baseapp.go#L539) -method. We think that this method can be more flexible and can give SDK users more options for customizations without -the need to rewrite whole BaseApp. Also there's one special case for `sdk.ErrorOutOfGas` error handling, that case -might be handled in a "standard" way (middleware) alongside the others. - -We propose middleware-solution, which could help developers implement the following cases: - -* add external logging (let's say sending reports to external services like [Sentry](https://sentry.io)); -* call panic for specific error cases; - -It will also make `OutOfGas` case and `default` case one of the middlewares. -`Default` case wraps recovery object to an error and logs it ([example middleware implementation](#Recovery-middleware)). - -Our project has a sidecar service running alongside the blockchain node (smart contracts virtual machine). It is -essential that node <-> sidecar connectivity stays stable for TXs processing. So when the communication breaks we need -to crash the node and reboot it once the problem is solved. That behaviour makes node's state machine execution -deterministic. As all keeper panics are caught by runTx's `defer()` handler, we have to adjust the BaseApp code -in order to customize it. - -## Decision - -### Design - -#### Overview - -Instead of hardcoding custom error handling into BaseApp we suggest using set of middlewares which can be customized -externally and will allow developers use as many custom error handlers as they want. Implementation with tests -can be found [here](https://github.com/cosmos/cosmos-sdk/pull/6053). - -#### Implementation details - -##### Recovery handler - -New `RecoveryHandler` type added. `recoveryObj` input argument is an object returned by the standard Go function -`recover()` from the `builtin` package. - -```go -type RecoveryHandler func(recoveryObj interface{}) error -``` - -Handler should type assert (or other methods) an object to define if object should be handled. -`nil` should be returned if input object can't be handled by that `RecoveryHandler` (not a handler's target type). -Not `nil` error should be returned if input object was handled and middleware chain execution should be stopped. - -An example: - -```go -func exampleErrHandler(recoveryObj interface{}) error { - err, ok := recoveryObj.(error) - if !ok { return nil } - - if someSpecificError.Is(err) { - panic(customPanicMsg) - } else { - return nil - } -} -``` - -This example breaks the application execution, but it also might enrich the error's context like the `OutOfGas` handler. - -##### Recovery middleware - -We also add a middleware type (decorator). That function type wraps `RecoveryHandler` and returns the next middleware in -execution chain and handler's `error`. Type is used to separate actual `recovery()` object handling from middleware -chain processing. - -```go -type recoveryMiddleware func(recoveryObj interface{}) (recoveryMiddleware, error) - -func newRecoveryMiddleware(handler RecoveryHandler, next recoveryMiddleware) recoveryMiddleware { - return func(recoveryObj interface{}) (recoveryMiddleware, error) { - if err := handler(recoveryObj); err != nil { - return nil, err - } - return next, nil - } -} -``` - -Function receives a `recoveryObj` object and returns: - -* (next `recoveryMiddleware`, `nil`) if object wasn't handled (not a target type) by `RecoveryHandler`; -* (`nil`, not nil `error`) if input object was handled and other middlewares in the chain should not be executed; -* (`nil`, `nil`) in case of invalid behavior. Panic recovery might not have been properly handled; -this can be avoided by always using a `default` as a rightmost middleware in the chain (always returns an `error`'); - -`OutOfGas` middleware example: - -```go -func newOutOfGasRecoveryMiddleware(gasWanted uint64, ctx sdk.Context, next recoveryMiddleware) recoveryMiddleware { - handler := func(recoveryObj interface{}) error { - err, ok := recoveryObj.(sdk.ErrorOutOfGas) - if !ok { return nil } - - return sdkerrors.Wrap( - sdkerrors.ErrOutOfGas, fmt.Sprintf( - "out of gas in location: %v; gasWanted: %d, gasUsed: %d", err.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(), - ), - ) - } - - return newRecoveryMiddleware(handler, next) -} -``` - -`Default` middleware example: - -```go -func newDefaultRecoveryMiddleware() recoveryMiddleware { - handler := func(recoveryObj interface{}) error { - return sdkerrors.Wrap( - sdkerrors.ErrPanic, fmt.Sprintf("recovered: %v\nstack:\n%v", recoveryObj, string(debug.Stack())), - ) - } - - return newRecoveryMiddleware(handler, nil) -} -``` - -##### Recovery processing - -Basic chain of middlewares processing would look like: - -```go -func processRecovery(recoveryObj interface{}, middleware recoveryMiddleware) error { - if middleware == nil { return nil } - - next, err := middleware(recoveryObj) - if err != nil { return err } - if next == nil { return nil } - - return processRecovery(recoveryObj, next) -} -``` - -That way we can create a middleware chain which is executed from left to right, the rightmost middleware is a -`default` handler which must return an `error`. - -##### BaseApp changes - -The `default` middleware chain must exist in a `BaseApp` object. `Baseapp` modifications: - -```go -type BaseApp struct { - // ... - runTxRecoveryMiddleware recoveryMiddleware -} - -func NewBaseApp(...) { - // ... - app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware() -} - -func (app *BaseApp) runTx(...) { - // ... - defer func() { - if r := recover(); r != nil { - recoveryMW := newOutOfGasRecoveryMiddleware(gasWanted, ctx, app.runTxRecoveryMiddleware) - err, result = processRecovery(r, recoveryMW), nil - } - - gInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed()} - }() - // ... -} -``` - -Developers can add their custom `RecoveryHandler`s by providing `AddRunTxRecoveryHandler` as a BaseApp option parameter to the `NewBaseapp` constructor: - -```go -func (app *BaseApp) AddRunTxRecoveryHandler(handlers ...RecoveryHandler) { - for _, h := range handlers { - app.runTxRecoveryMiddleware = newRecoveryMiddleware(h, app.runTxRecoveryMiddleware) - } -} -``` - -This method would prepend handlers to an existing chain. - -## Consequences - -### Positive - -- Developers of Cosmos SDK based projects can add custom panic handlers to: - * add error context for custom panic sources (panic inside of custom keepers); - * emit `panic()`: passthrough recovery object to the Tendermint core; - * other necessary handling; -- Developers can use standard Cosmos SDK `BaseApp` implementation, rather that rewriting it in their projects; -- Proposed solution doesn't break the current "standard" `runTx()` flow; - -### Negative - -- Introduces changes to the execution model design. - -### Neutral - -- `OutOfGas` error handler becomes one of the middlewares; -- Default panic handler becomes one of the middlewares; - -## References - -- [PR-6053 with proposed solution](https://github.com/cosmos/cosmos-sdk/pull/6053) -- [Similar solution. ADR-010 Modular AnteHandler](https://github.com/cosmos/cosmos-sdk/blob/v0.38.3/docs/architecture/adr-010-modular-antehandler.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-023-protobuf-naming.md b/versioned_docs/version-0.45/integrate/architecture/adr-023-protobuf-naming.md deleted file mode 100644 index 6e9ead13c..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-023-protobuf-naming.md +++ /dev/null @@ -1,263 +0,0 @@ -# ADR 023: Protocol Buffer Naming and Versioning Conventions - -## Changelog - -- 2020 April 27: Initial Draft -- 2020 August 5: Update guidelines - -## Status - -Accepted - -## Context - -Protocol Buffers provide a basic [style guide](https://developers.google.com/protocol-buffers/docs/style) -and [Buf](https://buf.build/docs/style-guide) builds upon that. To the -extent possible, we want to follow industry accepted guidelines and wisdom for -the effective usage of protobuf, deviating from those only when there is clear -rationale for our use case. - -### Adoption of `Any` - -The adoption of `google.protobuf.Any` as the recommended approach for encoding -interface types (as opposed to `oneof`) makes package naming a central part -of the encoding as fully-qualified message names now appear in encoded -messages. - -### Current Directory Organization - -Thus far we have mostly followed [Buf's](https://buf.build) [DEFAULT](https://buf.build/docs/lint-checkers#default) -recommendations, with the minor deviation of disabling [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout) -which although being convenient for developing code comes with the warning -from Buf that: - -> you will have a very bad time with many Protobuf plugins across various languages if you do not do this - -### Adoption of gRPC Queries - -In [ADR 021](adr-021-protobuf-query-encoding.md), gRPC was adopted for Protobuf -native queries. The full gRPC service path thus becomes a key part of ABCI query -path. In the future, gRPC queries may be allowed from within persistent scripts -by technologies such as CosmWasm and these query routes would be stored within -script binaries. - -## Decision - -The goal of this ADR is to provide thoughtful naming conventions that: - -* encourage a good user experience for when users interact directly with -.proto files and fully-qualified protobuf names -* balance conciseness against the possibility of either over-optimizing (making -names too short and cryptic) or under-optimizing (just accepting bloated names -with lots of redundant information) - -These guidelines are meant to act as a style guide for both the SDK and -third-party modules. - -As a starting point, we should adopt all of the [DEFAULT](https://buf.build/docs/lint-checkers#default) -checkers in [Buf's](https://buf.build) including [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout), -except: - -* [PACKAGE_VERSION_SUFFIX](https://buf.build/docs/lint-checkers#package_version_suffix) -* [SERVICE_SUFFIX](https://buf.build/docs/lint-checkers#service_suffix) - -Further guidelines to be described below. - -### Principles - -#### Concise and Descriptive Names - -Names should be descriptive enough to convey their meaning and distinguish -them from other names. - -Given that we are using fully-qualifed names within -`google.protobuf.Any` as well as within gRPC query routes, we should aim to -keep names concise, without going overboard. The general rule of thumb should -be if a shorter name would convey more or else the same thing, pick the shorter -name. - -For instance, `cosmos.bank.MsgSend` (19 bytes) conveys roughly the same information -as `cosmos_sdk.x.bank.v1.MsgSend` (28 bytes) but is more concise. - -Such conciseness makes names both more pleasant to work with and take up less -space within transactions and on the wire. - -We should also resist the temptation to over-optimize, by making names -cryptically short with abbreviations. For instance, we shouldn't try to -reduce `cosmos.bank.MsgSend` to `csm.bk.MSnd` just to save a few bytes. - -The goal is to make names **_concise but not cryptic_**. - -#### Names are for Clients First - -Package and type names should be chosen for the benefit of users, not -necessarily because of legacy concerns related to the go code-base. - -#### Plan for Longevity - -In the interests of long-term support, we should plan on the names we do -choose to be in usage for a long time, so now is the opportunity to make -the best choices for the future. - -### Versioning - -#### Guidelines on Stable Package Versions - -In general, schema evolution is the way to update protobuf schemas. That means that new fields, -messages, and RPC methods are _added_ to existing schemas and old fields, messages and RPC methods -are maintained as long as possible. - -Breaking things is often unacceptable in a blockchain scenario. For instance, immutable smart contracts -may depend on certain data schemas on the host chain. If the host chain breaks those schemas, the smart -contract may be irreparably broken. Even when things can be fixed (for instance in client software), -this often comes at a high cost. - -Instead of breaking things, we should make every effort to evolve schemas rather than just breaking them. -[Buf](https://buf.build) breaking change detection should be used on all stable (non-alpha or beta) packages -to prevent such breakage. - -With that in mind, different stable versions (i.e. `v1` or `v2`) of a package should more or less be considered -different packages and this should be last resort approach for upgrading protobuf schemas. Scenarios where creating -a `v2` may make sense are: - -* we want to create a new module with similar functionality to an existing module and adding `v2` is the most natural -way to do this. In that case, there are really just two different, but similar modules with different APIs. -* we want to add a new revamped API for an existing module and it's just too cumbersome to add it to the existing package, -so putting it in `v2` is cleaner for users. In this case, care should be made to not deprecate support for -`v1` if it is actively used in immutable smart contracts. - -#### Guidelines on unstable (alpha and beta) package versions - -The following guidelines are recommended for marking packages as alpha or beta: - -* marking something as `alpha` or `beta` should be a last resort and just putting something in the -stable package (i.e. `v1` or `v2`) should be preferred -* a package *should* be marked as `alpha` *if and only if* there are active discussions to remove -or significantly alter the package in the near future -* a package *should* be marked as `beta` *if and only if* there is an active discussion to -significantly refactor/rework the functionality in the near future but not remove it -* modules *can and should* have types in both stable (i.e. `v1` or `v2`) and unstable (`alpha` or `beta`) packages. - -*`alpha` and `beta` should not be used to avoid responsibility for maintaining compatibility.* -Whenever code is released into the wild, especially on a blockchain, there is a high cost to changing things. In some -cases, for instance with immutable smart contracts, a breaking change may be impossible to fix. - -When marking something as `alpha` or `beta`, maintainers should ask the questions: - -* what is the cost of asking others to change their code vs the benefit of us maintaining the optionality to change it? -* what is the plan for moving this to `v1` and how will that affect users? - -`alpha` or `beta` should really be used to communicate "changes are planned". - -As a case study, gRPC reflection is in the package `grpc.reflection.v1alpha`. It hasn't been changed since -2017 and it is now used in other widely used software like gRPCurl. Some folks probably use it in production services -and so if they actually went and changed the package to `grpc.reflection.v1`, some software would break and -they probably don't want to do that... So now the `v1alpha` package is more or less the de-facto `v1`. Let's not do that. - -The following are guidelines for working with non-stable packages: - -* [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix) -(ex. `v1alpha1`) _should_ be used for non-stable packages -* non-stable packages should generally be excluded from breaking change detection -* immutable smart contract modules (i.e. CosmWasm) _should_ block smart contracts/persistent -scripts from interacting with `alpha`/`beta` packages - -#### Omit v1 suffix - -Instead of using [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix), -we can omit `v1` for packages that don't actually have a second version. This -allows for more concise names for common use cases like `cosmos.bank.Send`. -Packages that do have a second or third version can indicate that with `.v2` -or `.v3`. - -### Package Naming - -#### Adopt a short, unique top-level package name - -Top-level packages should adopt a short name that is known to not collide with -other names in common usage within the Cosmos ecosystem. In the near future, a -registry should be created to reserve and index top-level package names used -within the Cosmos ecosystem. Because the Cosmos SDK is intended to provide -the top-level types for the Cosmos project, the top-level package name `cosmos` -is recommended for usage within the Cosmos SDK instead of the longer `cosmos_sdk`. -[ICS](https://github.com/cosmos/ics) specifications could consider a -short top-level package like `ics23` based upon the standard number. - -#### Limit sub-package depth - -Sub-package depth should be increased with caution. Generally a single -sub-package is needed for a module or a library. Even though `x` or `modules` -is used in source code to denote modules, this is often unnecessary for .proto -files as modules are the primary thing sub-packages are used for. Only items which -are known to be used infrequently should have deep sub-package depths. - -For the Cosmos SDK, it is recommended that that we simply write `cosmos.bank`, -`cosmos.gov`, etc. rather than `cosmos.x.bank`. In practice, most non-module -types can go straight in the `cosmos` package or we can introduce a -`cosmos.base` package if needed. Note that this naming _will not_ change -go package names, i.e. the `cosmos.bank` protobuf package will still live in -`x/bank`. - -### Message Naming - -Message type names should be as concise possible without losing clarity. `sdk.Msg` -types which are used in transactions will retain the `Msg` prefix as that provides -helpful context. - -### Service and RPC Naming - -[ADR 021](adr-021-protobuf-query-encoding.md) specifies that modules should -implement a gRPC query service. We should consider the principle of conciseness -for query service and RPC names as these may be called from persistent script -modules such as CosmWasm. Also, users may use these query paths from tools like -[gRPCurl](https://github.com/fullstorydev/grpcurl). As an example, we can shorten -`/cosmos_sdk.x.bank.v1.QueryService/QueryBalance` to -`/cosmos.bank.Query/Balance` without losing much useful information. - -RPC request and response types _should_ follow the `ServiceNameMethodNameRequest`/ -`ServiceNameMethodNameResponse` naming convention. i.e. for an RPC method named `Balance` -on the `Query` service, the request and response types would be `QueryBalanceRequest` -and `QueryBalanceResponse`. This will be more self-explanatory than `BalanceRequest` -and `BalanceResponse`. - -#### Use just `Query` for the query service - -Instead of [Buf's default service suffix recommendation](https://github.com/cosmos/cosmos-sdk/pull/6033), -we should simply use the shorter `Query` for query services. - -For other types of gRPC services, we should consider sticking with Buf's -default recommendation. - -#### Omit `Get` and `Query` from query service RPC names - -`Get` and `Query` should be omitted from `Query` service names because they are -redundant in the fully-qualified name. For instance, `/cosmos.bank.Query/QueryBalance` -just says `Query` twice without any new information. - -## Future Improvements - -A registry of top-level package names should be created to coordinate naming -across the ecosystem, prevent collisions, and also help developers discover -useful schemas. A simple starting point would be a git repository with -community-based governance. - -## Consequences - -### Positive - -* names will be more concise and easier to read and type -* all transactions using `Any` will be at shorter (`_sdk.x` and `.v1` will be removed) -* `.proto` file imports will be more standard (without `"third_party/proto"` in -the path) -* code generation will be easier for clients because .proto files will be -in a single `proto/` directory which can be copied rather than scattered -throughout the SDK - -### Negative - -### Neutral - -* `.proto` files will need to be reorganized and refactored -* some modules may need to be marked as alpha or beta - -## References diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-024-coin-metadata.md b/versioned_docs/version-0.45/integrate/architecture/adr-024-coin-metadata.md deleted file mode 100644 index 5195a9c40..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-024-coin-metadata.md +++ /dev/null @@ -1,139 +0,0 @@ -# ADR 024: Coin Metadata - -## Changelog - -- 05/19/2020: Initial draft - -## Status - -Proposed - -## Context - -Assets in the Cosmos SDK are represented via a `Coins` type that consists of an `amount` and a `denom`, -where the `amount` can be any arbitrarily large or small value. In addition, the Cosmos SDK uses an -account-based model where there are two types of primary accounts -- basic accounts and module accounts. -All account types have a set of balances that are composed of `Coins`. The `x/bank` module keeps -track of all balances for all accounts and also keeps track of the total supply of balances in an -application. - -With regards to a balance `amount`, the Cosmos SDK assumes a static and fixed unit of denomination, -regardless of the denomination itself. In other words, clients and apps built atop a Cosmos-SDK-based -chain may choose to define and use arbitrary units of denomination to provide a richer UX, however, by -the time a tx or operation reaches the Cosmos SDK state machine, the `amount` is treated as a single -unit. For example, for the Cosmos Hub (Gaia), clients assume 1 ATOM = 10^6 uatom, and so all txs and -operations in the Cosmos SDK work off of units of 10^6. - -This clearly provides a poor and limited UX especially as interoperability of networks increases and -as a result the total amount of asset types increases. We propose to have `x/bank` additionally keep -track of metadata per `denom` in order to help clients, wallet providers, and explorers improve their -UX and remove the requirement for making any assumptions on the unit of denomination. - -## Decision - -The `x/bank` module will be updated to store and index metadata by `denom`, specifically the "base" or -smallest unit -- the unit the Cosmos SDK state-machine works with. - -Metadata may also include a non-zero length list of denominations. Each entry contains the name of -the denomination `denom`, the exponent to the base and a list of aliases. An entry is to be -interpreted as `1 denom = 10^exponent base_denom` (e.g. `1 ETH = 10^18 wei` and `1 uatom = 10^0 uatom`). - -There are two denominations that are of high importance for clients: the `base`, which is the smallest -possible unit and the `display`, which is the unit that is commonly referred to in human communication -and on exchanges. The values in those fields link to an entry in the list of denominations. - -The list in `denom_units` and the `display` entry may be changed via governance. - -As a result, we can define the type as follows: - -```protobuf -message DenomUnit { - string denom = 1; - uint32 exponent = 2; - repeated string aliases = 3; -} - -message Metadata { - string description = 1; - repeated DenomUnit denom_units = 2; - string base = 3; - string display = 4; -} -``` - -As an example, the ATOM's metadata can be defined as follows: - -```json -{ - "description": "The native staking token of the Cosmos Hub.", - "denom_units": [ - { - "denom": "uatom", - "exponent": 0, - "aliases": [ - "microatom" - ], - }, - { - "denom": "matom", - "exponent": 3, - "aliases": [ - "milliatom" - ] - }, - { - "denom": "atom", - "exponent": 6, - } - ], - "base": "uatom", - "display": "atom", -} -``` - -Given the above metadata, a client may infer the following things: - -- 4.3atom = 4.3 * (10^6) = 4,300,000uatom -- The string "atom" can be used as a display name in a list of tokens. -- The balance 4300000 can be displayed as 4,300,000uatom or 4,300matom or 4.3atom. - The `display` denomination 4.3atom is a good default if the authors of the client don't make - an explicit decision to choose a different representation. - -A client should be able to query for metadata by denom both via the CLI and REST interfaces. In -addition, we will add handlers to these interfaces to convert from any unit to another given unit, -as the base framework for this already exists in the Cosmos SDK. - -Finally, we need to ensure metadata exists in the `GenesisState` of the `x/bank` module which is also -indexed by the base `denom`. - -```go -type GenesisState struct { - SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` - Balances []Balance `json:"balances" yaml:"balances"` - Supply sdk.Coins `json:"supply" yaml:"supply"` - DenomMetadata []Metadata `json:"denom_metadata" yaml:"denom_metadata"` -} -``` - -## Future Work - -In order for clients to avoid having to convert assets to the base denomination -- either manually or -via an endpoint, we may consider supporting automatic conversion of a given unit input. - -## Consequences - -### Positive - -- Provides clients, wallet providers and block explorers with additional data on - asset denomination to improve UX and remove any need to make assumptions on - denomination units. - -### Negative - -- A small amount of required additional storage in the `x/bank` module. The amount - of additional storage should be minimal as the amount of total assets should not - be large. - -### Neutral - -## References diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-027-deterministic-protobuf-serialization.md b/versioned_docs/version-0.45/integrate/architecture/adr-027-deterministic-protobuf-serialization.md deleted file mode 100644 index ab303e8d0..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-027-deterministic-protobuf-serialization.md +++ /dev/null @@ -1,314 +0,0 @@ -# ADR 027: Deterministic Protobuf Serialization - -## Changelog - -- 2020-08-07: Initial Draft -- 2020-09-01: Further clarify rules - -## Status - -Proposed - -## Abstract - -Fully deterministic structure serialization, which works across many languages and clients, -is needed when signing messages. We need to be sure that whenever we serialize -a data structure, no matter in which supported language, the raw bytes -will stay the same. -[Protobuf](https://developers.google.com/protocol-buffers/docs/proto3) -serialization is not bijective (i.e. there exist a practically unlimited number of -valid binary representations for a given protobuf document)1. - -This document describes a deterministic serialization scheme for -a subset of protobuf documents, that covers this use case but can be reused in -other cases as well. - -### Context - -For signature verification in Cosmos SDK, the signer and verifier need to agree on -the same serialization of a `SignDoc` as defined in -[ADR-020](./adr-020-protobuf-transaction-encoding.md) without transmitting the -serialization. - -Currently, for block signatures we are using a workaround: we create a new [TxRaw](https://github.com/cosmos/cosmos-sdk/blob/9e85e81e0e8140067dd893421290c191529c148c/proto/cosmos/tx/v1beta1/tx.proto#L30) -instance (as defined in [adr-020-protobuf-transaction-encoding](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md#transactions)) -by converting all [Tx](https://github.com/cosmos/cosmos-sdk/blob/9e85e81e0e8140067dd893421290c191529c148c/proto/cosmos/tx/v1beta1/tx.proto#L13) -fields to bytes on the client side. This adds an additional manual -step when sending and signing transactions. - -### Decision - -The following encoding scheme is to be used by other ADRs, -and in particular for `SignDoc` serialization. - -## Specification - -### Scope - -This ADR defines a protobuf3 serializer. The output is a valid protobuf -serialization, such that every protobuf parser can parse it. - -No maps are supported in version 1 due to the complexity of defining a -deterministic serialization. This might change in future. Implementations must -reject documents containing maps as invalid input. - -### Background - Protobuf3 Encoding - -Most numeric types in protobuf3 are encoded as -[varints](https://developers.google.com/protocol-buffers/docs/encoding#varints). -Varints are at most 10 bytes, and since each varint byte has 7 bits of data, -varints are a representation of `uint70` (70-bit unsigned integer). When -encoding, numeric values are casted from their base type to `uint70`, and when -decoding, the parsed `uint70` is casted to the appropriate numeric type. - -The maximum valid value for a varint that complies with protobuf3 is -`FF FF FF FF FF FF FF FF FF 7F` (i.e. `2**70 -1`). If the field type is -`{,u,s}int64`, the highest 6 bits of the 70 are dropped during decoding, -introducing 6 bits of malleability. If the field type is `{,u,s}int32`, the -highest 38 bits of the 70 are dropped during decoding, introducing 38 bits of -malleability. - -Among other sources of non-determinism, this ADR eliminates the possibility of -encoding malleability. - -### Serialization rules - -The serialization is based on the -[protobuf3 encoding](https://developers.google.com/protocol-buffers/docs/encoding) -with the following additions: - -1. Fields must be serialized only once in ascending order -2. Extra fields or any extra data must not be added -3. [Default values](https://developers.google.com/protocol-buffers/docs/proto3#default) - must be omitted -4. `repeated` fields of scalar numeric types must use - [packed encoding](https://developers.google.com/protocol-buffers/docs/encoding#packed) -5. Varint encoding must not be longer than needed: - * No trailing zero bytes (in little endian, i.e. no leading zeroes in big - endian). Per rule 3 above, the default value of `0` must be omitted, so - this rule does not apply in such cases. - * The maximum value for a varint must be `FF FF FF FF FF FF FF FF FF 01`. - In other words, when decoded, the highest 6 bits of the 70-bit unsigned - integer must be `0`. (10-byte varints are 10 groups of 7 bits, i.e. - 70 bits, of which only the lowest 70-6=64 are useful.) - * The maximum value for 32-bit values in varint encoding must be `FF FF FF FF 0F` - with one exception (below). In other words, when decoded, the highest 38 - bits of the 70-bit unsigned integer must be `0`. - * The one exception to the above is _negative_ `int32`, which must be - encoded using the full 10 bytes for sign extension2. - * The maximum value for Boolean values in varint encoding must be `01` (i.e. - it must be `0` or `1`). Per rule 3 above, the default value of `0` must - be omitted, so if a Boolean is included it must have a value of `1`. - -While rule number 1. and 2. should be pretty straight forward and describe the -default behavior of all protobuf encoders the author is aware of, the 3rd rule -is more interesting. After a protobuf3 deserialization you cannot differentiate -between unset fields and fields set to the default value3. At -serialization level however, it is possible to set the fields with an empty -value or omitting them entirely. This is a significant difference to e.g. JSON -where a property can be empty (`""`, `0`), `null` or undefined, leading to 3 -different documents. - -Omitting fields set to default values is valid because the parser must assign -the default value to fields missing in the serialization4. For scalar -types, omitting defaults is required by the spec5. For `repeated` -fields, not serializing them is the only way to express empty lists. Enums must -have a first element of numeric value 0, which is the default6. And -message fields default to unset7. - -Omitting defaults allows for some amount of forward compatibility: users of -newer versions of a protobuf schema produce the same serialization as users of -older versions as long as newly added fields are not used (i.e. set to their -default value). - -### Implementation - -There are three main implementation strategies, ordered from the least to the -most custom development: - -- **Use a protobuf serializer that follows the above rules by default.** E.g. - [gogoproto](https://pkg.go.dev/github.com/gogo/protobuf/gogoproto) is known to - be compliant by in most cases, but not when certain annotations such as - `nullable = false` are used. It might also be an option to configure an - existing serializer accordingly. -- **Normalize default values before encoding them.** If your serializer follows - rule 1. and 2. and allows you to explicitly unset fields for serialization, - you can normalize default values to unset. This can be done when working with - [protobuf.js](https://www.npmjs.com/package/protobufjs): - - ```js - const bytes = SignDoc.encode({ - bodyBytes: body.length > 0 ? body : null, // normalize empty bytes to unset - authInfoBytes: authInfo.length > 0 ? authInfo : null, // normalize empty bytes to unset - chainId: chainId || null, // normalize "" to unset - accountNumber: accountNumber || null, // normalize 0 to unset - accountSequence: accountSequence || null, // normalize 0 to unset - }).finish(); - ``` - -- **Use a hand-written serializer for the types you need.** If none of the above - ways works for you, you can write a serializer yourself. For SignDoc this - would look something like this in Go, building on existing protobuf utilities: - - ```go - if !signDoc.body_bytes.empty() { - buf.WriteUVarInt64(0xA) // wire type and field number for body_bytes - buf.WriteUVarInt64(signDoc.body_bytes.length()) - buf.WriteBytes(signDoc.body_bytes) - } - - if !signDoc.auth_info.empty() { - buf.WriteUVarInt64(0x12) // wire type and field number for auth_info - buf.WriteUVarInt64(signDoc.auth_info.length()) - buf.WriteBytes(signDoc.auth_info) - } - - if !signDoc.chain_id.empty() { - buf.WriteUVarInt64(0x1a) // wire type and field number for chain_id - buf.WriteUVarInt64(signDoc.chain_id.length()) - buf.WriteBytes(signDoc.chain_id) - } - - if signDoc.account_number != 0 { - buf.WriteUVarInt64(0x20) // wire type and field number for account_number - buf.WriteUVarInt(signDoc.account_number) - } - - if signDoc.account_sequence != 0 { - buf.WriteUVarInt64(0x28) // wire type and field number for account_sequence - buf.WriteUVarInt(signDoc.account_sequence) - } - ``` - -### Test vectors - -Given the protobuf definition `Article.proto` - -```protobuf -package blog; -syntax = "proto3"; - -enum Type { - UNSPECIFIED = 0; - IMAGES = 1; - NEWS = 2; -}; - -enum Review { - UNSPECIFIED = 0; - ACCEPTED = 1; - REJECTED = 2; -}; - -message Article { - string title = 1; - string description = 2; - uint64 created = 3; - uint64 updated = 4; - bool public = 5; - bool promoted = 6; - Type type = 7; - Review review = 8; - repeated string comments = 9; - repeated string backlinks = 10; -}; -``` - -serializing the values - -```yaml -title: "The world needs change 🌳" -description: "" -created: 1596806111080 -updated: 0 -public: true -promoted: false -type: Type.NEWS -review: Review.UNSPECIFIED -comments: ["Nice one", "Thank you"] -backlinks: [] -``` - -must result in the serialization - -``` -0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75 -``` - -When inspecting the serialized document, you see that every second field is -omitted: - -``` -$ echo 0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75 | xxd -r -p | protoc --decode_raw -1: "The world needs change \360\237\214\263" -3: 1596806111080 -5: 1 -7: 2 -9: "Nice one" -9: "Thank you" -``` - -## Consequences - -Having such an encoding available allows us to get deterministic serialization -for all protobuf documents we need in the context of Cosmos SDK signing. - -### Positive - -- Well defined rules that can be verified independent of a reference - implementation -- Simple enough to keep the barrier to implement transaction signing low -- It allows us to continue to use 0 and other empty values in SignDoc, avoiding - the need to work around 0 sequences. This does not imply the change from - https://github.com/cosmos/cosmos-sdk/pull/6949 should not be merged, but not - too important anymore. - -### Negative - -- When implementing transaction signing, the encoding rules above must be - understood and implemented. -- The need for rule number 3. adds some complexity to implementations. -- Some data structures may require custom code for serialization. Thus - the code is not very portable - it will require additional work for each - client implementing serialization to properly handle custom data structures. - -### Neutral - -### Usage in SDK - -For the reasons mentioned above ("Negative" section) we prefer to keep workarounds -for shared data structure. Example: the aforementioned `TxRaw` is using raw bytes -as a workaround. This allows them to use any valid Protobuf library without -the need of implementing a custom serializer that adheres to this standard (and related risks of bugs). - -## References - -- 1 _When a message is serialized, there is no guaranteed order for - how its known or unknown fields should be written. Serialization order is an - implementation detail and the details of any particular implementation may - change in the future. Therefore, protocol buffer parsers must be able to parse - fields in any order._ from - https://developers.google.com/protocol-buffers/docs/encoding#order -- 2 https://developers.google.com/protocol-buffers/docs/encoding#signed_integers -- 3 _Note that for scalar message fields, once a message is parsed - there's no way of telling whether a field was explicitly set to the default - value (for example whether a boolean was set to false) or just not set at all: - you should bear this in mind when defining your message types. For example, - don't have a boolean that switches on some behavior when set to false if you - don't want that behavior to also happen by default._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -- 4 _When a message is parsed, if the encoded message does not - contain a particular singular element, the corresponding field in the parsed - object is set to the default value for that field._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -- 5 _Also note that if a scalar message field is set to its default, - the value will not be serialized on the wire._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -- 6 _For enums, the default value is the first defined enum value, - which must be 0._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -- 7 _For message fields, the field is not set. Its exact value is - language-dependent._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -- Encoding rules and parts of the reasoning taken from - [canonical-proto3 Aaron Craelius](https://github.com/regen-network/canonical-proto3) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-028-public-key-addresses.md b/versioned_docs/version-0.45/integrate/architecture/adr-028-public-key-addresses.md deleted file mode 100644 index d86a0426f..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-028-public-key-addresses.md +++ /dev/null @@ -1,329 +0,0 @@ -# ADR 028: Public Key Addresses - -## Changelog - -- 2020/08/18: Initial version -- 2021/01/15: Analysis and algorithm update - -## Status - -Proposed - -## Abstract - -This ADR defines an address format for all addressable SDK accounts. That includes: new public key algorithms, multisig public keys, and module accounts. - -## Context - -Issue [\#3685](https://github.com/cosmos/cosmos-sdk/issues/3685) identified that public key -address spaces are currently overlapping. We confirmed that it significantly decreases security of Cosmos SDK. - -### Problem - -An attacker can control an input for an address generation function. This leads to a birthday attack, which significantly decreases the security space. -To overcome this, we need to separate the inputs for different kind of account types: -a security break of one account type shouldn't impact the security of other account types. - -### Initial proposals - -One initial proposal was extending the address length and -adding prefixes for different types of addresses. - -@ethanfrey explained an alternate approach originally used in https://github.com/iov-one/weave: - -> I spent quite a bit of time thinking about this issue while building weave... The other cosmos Sdk. -> Basically I define a condition to be a type and format as human readable string with some binary data appended. This condition is hashed into an Address (again at 20 bytes). The use of this prefix makes it impossible to find a preimage for a given address with a different condition (eg ed25519 vs secp256k1). -> This is explained in depth here https://weave.readthedocs.io/en/latest/design/permissions.html -> And the code is here, look mainly at the top where we process conditions. https://github.com/iov-one/weave/blob/master/conditions.go - -And explained how this approach should be sufficiently collision resistant: - -> Yeah, AFAIK, 20 bytes should be collision resistance when the preimages are unique and not malleable. A space of 2^160 would expect some collision to be likely around 2^80 elements (birthday paradox). And if you want to find a collision for some existing element in the database, it is still 2^160. 2^80 only is if all these elements are written to state. -> The good example you brought up was eg. a public key bytes being a valid public key on two algorithms supported by the codec. Meaning if either was broken, you would break accounts even if they were secured with the safer variant. This is only as the issue when no differentiating type info is present in the preimage (before hashing into an address). -> I would like to hear an argument if the 20 bytes space is an actual issue for security, as I would be happy to increase my address sizes in weave. I just figured cosmos and ethereum and bitcoin all use 20 bytes, it should be good enough. And the arguments above which made me feel it was secure. But I have not done a deeper analysis. - -This led to the first proposal (which we proved to be not good enough): -we concatenate a key type with a public key, hash it and take the first 20 bytes of that hash, summarized as `sha256(keyTypePrefix || keybytes)[:20]`. - -### Review and Discussions - -In [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694) we discussed various solutions. -We agreed that 20 bytes it's not future proof, and extending the address length is the only way to allow addresses of different types, various signature types, etc. -This disqualifies the initial proposal. - -In the issue we discussed various modifications: - -+ Choice of the hash function. -+ Move the prefix out of the hash function: `keyTypePrefix + sha256(keybytes)[:20]` [post-hash-prefix-proposal]. -+ Use double hashing: `sha256(keyTypePrefix + sha256(keybytes)[:20])`. -+ Increase to keybytes hash slice from 20 byte to 32 or 40 bytes. We concluded that 32 bytes, produced by a good hash functions is future secure. - -### Requirements - -+ Support currently used tools - we don't want to break an ecosystem, or add a long adaptation period. Ref: https://github.com/cosmos/cosmos-sdk/issues/8041 -+ Try to keep the address length small - addresses are widely used in state, both as part of a key and object value. - -### Scope - -This ADR only defines a process for the generation of address bytes. For end-user interactions with addresses (through the API, or CLI, etc.), we still use bech32 to format these addresses as strings. This ADR doesn't change that. -Using Bech32 for string encoding gives us support for checksum error codes and handling of user typos. - -## Decision - -We define the following account types, for which we define the address function: - -1. simple accounts: represented by a regular public key (ie: secp256k1, sr25519) -2. naive multisig: accounts composed by other addressable objects (ie: naive multisig) -3. composed accounts with a native address key (ie: bls, group module accounts) -4. module accounts: basically any accounts which cannot sign transactions and which are managed internally by modules - -### Legacy Public Key Addresses Don't Change - -Currently (Jan 2021), the only officially supported SDK user accounts are `secp256k1` basic accounts and legacy amino multisig. -They are used in existing Cosmos SDK zones. They use the following address formats: - -- secp256k1: `ripemd160(sha256(pk_bytes))[:20]` -- legacy amino multisig: `sha256(aminoCdc.Marshal(pk))[:20]` - -We don't want to change existing addresses. So the addresses for these two key types will remain the same. - -The current multisig public keys use amino serialization to generate the address. We will retain -those public keys and their address formatting, and call them "legacy amino" multisig public keys -in protobuf. We will also create multisig public keys without amino addresses to be described below. - -### Hash Function Choice - -As in other parts of the Cosmos SDK, we will use `sha256`. - -### Basic Address - -We start with defining a base hash algorithm for generating addresses. Notably, it's used for accounts represented by a single key pair. For each public key schema we have to have an associated `typ` string, which we discuss in a section below. `hash` is the cryptographic hash function defined in the previous section. - -```go -const A_LEN = 32 - -func Hash(typ string, key []byte) []byte { - return hash(hash(typ) + key)[:A_LEN] -} -``` - -The `+` is bytes concatenation, which doesn't use any separator. - -This algorithm is the outcome of a consultation session with a professional cryptographer. -Motivation: this algorithm keeps the address relatively small (length of the `typ` doesn't impact the length of the final address) -and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 bytes of a pubkey hash, significantly reducing the address space). -Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack. - -We use the `address.Hash` function for generating addresses for all accounts represented by a single key: - -* simple public keys: `address.Hash(keyType, pubkey)` - -+ aggregated keys (eg: BLS): `address.Hash(keyType, aggregatedPubKey)` -+ modules: `address.Hash("module", moduleName)` - -### Composed Addresses - -For simple composed accounts (like new naive multisig), we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address. - -```go -// We don't need a PubKey interface - we need anything which is addressable. -type Addressable interface { - Address() []byte -} - -func Composed(typ string, subaccounts []Addressable) []byte { - addresses = map(subaccounts, \a -> LengthPrefix(a.Address())) - addresses = sort(addresses) - return address.Hash(typ, addresses[0] + ... + addresses[n]) -} -``` - -The `typ` parameter should be a schema descriptor, containing all significant attributes with deterministic serialization (eg: utf8 string). -`LengthPrefix` is a function which prepends 1 byte to the address. The value of that byte is the length of the address bits before prepending. The address must be at most 255 bits long. -We are using `LengthPrefix` to eliminate conflicts - it assures, that for 2 lists of addresses: `as = {a1, a2, ..., an}` and `bs = {b1, b2, ..., bm}` such that every `bi` and `ai` is at most 255 long, `concatenate(map(as, \a -> LengthPrefix(a))) = map(bs, \b -> LengthPrefix(b))` iff `as = bs`. - -Implementation Tip: account implementations should cache addresses. - -#### Multisig Addresses - -For new multisig public keys, we define the `typ` parameter not based on any encoding scheme (amino or protobuf). This avoids issues with non-determinism in the encoding scheme. - -Example: - -```proto -package cosmos.crypto.multisig; - -message PubKey { - uint32 threshold = 1; - repeated google.protobuf.Any pubkeys = 2; -} -``` - -```go -func (multisig PubKey) Address() { - // first gather all nested pub keys - var keys []address.Addressable // cryptotypes.PubKey implements Addressable - for _, _key := range multisig.Pubkeys { - keys = append(keys, key.GetCachedValue().(cryptotypes.PubKey)) - } - - // form the type from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together - prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold) - - // use the Composed function defined above - return address.Composed(prefix, keys) -} -``` - -#### Module Account Addresses - -NOTE: this section is not finalize and it's in active discussion. - -In Basic Address section we defined a module account address as: - -```go -address.Hash("module", moduleName) -``` - -We use `"module"` as a schema type for all module derived addresses. Module accounts can have sub accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key. -Module account addresses are heavily used in the SDK so it makes sense to optimize the derivation process: instead of using of using `LengthPrefix` for the module name, we use a null byte (`'\x00'`) as a separator. This works, because null byte is not a part of a valid module name. - -```go -func Module(moduleName string, key []byte) []byte{ - return Hash("module", []byte(moduleName) + 0 + key) -} -``` - -**Example** A lending BTC pool address would be: - -``` -btcPool := address.Module("lending", btc.Addrress()}) -``` - -If we want to create an address for a module account depending on more than one key, we can concatenate them: - -``` -btcAtomAMM := address.Module("amm", btc.Addrress() + atom.Address()}) -``` - -#### Derived Addresses - -We must be able to cryptographically derive one address from another one. The derivation process must guarantee hash properties, hence we use the already defined `Hash` function: - -```go -func Derive(address []byte, derivationKey []byte) []byte { - return Hash(addres, derivationKey) -} -``` - -Note: `Module` is a special case of the more general _derived_ address, where we set the `"module"` string for the _from address_. - -**Example** For a cosmwasm smart-contract address we could use the following construction: - -``` -smartContractAddr := Derived(Module("cosmwasm", smartContractsNamespace), []{smartContractKey}) -``` - -### Schema Types - -A `typ` parameter used in `Hash` function SHOULD be unique for each account type. -Since all SDK account types are serialized in the state, we propose to use the protobuf message name string. - -Example: all public key types have a unique protobuf message type similar to: - -```proto -package cosmos.crypto.sr25519; - -message PubKey { - bytes key = 1; -} -``` - -All protobuf messages have unique fully qualified names, in this example `cosmos.crypto.sr25519.PubKey`. -These names are derived directly from .proto files in a standardized way and used -in other places such as the type URL in `Any`s. We can easily obtain the name using -`proto.MessageName(msg)`. - -## Consequences - -### Backwards Compatibility - -This ADR is compatible with what was committed and directly supported in the SDK repository. - -### Positive - -- a simple algorithm for generating addresses for new public keys, complex accounts and modules -- the algorithm generalizes _native composed keys_ -- increased security and collision resistance of addresses -- the approach is extensible for future use-cases - one can use other address types, as long as they don't conflict with the address length specified here (20 or 32 bytes). -- support new account types. - -### Negative - -- addresses do not communicate key type, a prefixed approach would have done this -- addresses are 60% longer and will consume more storage space -- requires a refactor of KVStore store keys to handle variable length addresses - -### Neutral - -- protobuf message names are used as key type prefixes - -## Further Discussions - -Some accounts can have a fixed name or may be constructed in other way (eg: modules). We were discussing an idea of an account with a predefined name (eg: `me.regen`), which could be used by institutions. -Without going into details, these kinds of addresses are compatible with the hash based addresses described here as long as they don't have the same length. -More specifically, any special account address must not have a length equal to 20 or 32 bytes. - -## Appendix: Consulting session - -End of Dec 2020 we had a session with [Alan Szepieniec](https://scholar.google.be/citations?user=4LyZn8oAAAAJ&hl=en) to consult the approach presented above. - -Alan general observations: - -+ we don’t need 2-preimage resistance -+ we need 32bytes address space for collision resistance -+ when an attacker can control an input for object with an address then we have a problem with birthday attack -+ there is an issue with smart-contracts for hashing -+ sha2 mining can be use to breaking address pre-image - -Hashing algorithm - -+ any attack breaking blake3 will break blake2 -+ Alan is pretty confident about the current security analysis of the blake hash algorithm. It was a finalist, and the author is well known in security analysis. - -Algorithm: - -+ Alan recommends to hash the prefix: `address(pub_key) = hash(hash(key_type) + pub_key)[:32]`, main benefits: - + we are free to user arbitrary long prefix names - + we still don’t risk collisions - + switch tables -+ discussion about penalization -> about adding prefix post hash -+ Aaron asked about post hash prefixes (`address(pub_key) = key_type + hash(pub_key)`) and differences. Alan noted that this approach has longer address space and it’s stronger. - -Algorithm for complex / composed keys: - -+ merging tree like addresses with same algorithm are fine - -Module addresses: Should module addresses have different size to differentiate it? - -+ we will need to set a pre-image prefix for module addresse to keept them in 32-byte space: `hash(hash('module') + module_key)` -+ Aaron observation: we already need to deal with variable length (to not break secp256k1 keys). - -Discssion about arithmetic hash function for ZKP - -+ Posseidon / Rescue -+ Problem: much bigger risk because we don’t know much techniques and history of crypto-analysis of arithmetic constructions. It’s still a new ground and area of active research. - -Post quantum signature size - -+ Alan suggestion: Falcon: speed / size ration - very good. -+ Aaron - should we think about it? - Alan: based on early extrapolation this thing will get able to break EC cryptography in 2050 . But that’s a lot of uncertainty. But there is magic happening with recurions / linking / simulation and that can speedup the progress. - -Other ideas - -+ Let’s say we use same key and two different address algorithms for 2 different use cases. Is it still safe to use it? Alan: if we want to hide the public key (which is not our use case), then it’s less secure but there are fixes. - -### References - -+ [Notes](https://hackmd.io/_NGWI4xZSbKzj1BkCqyZMw) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-029-fee-grant-module.md b/versioned_docs/version-0.45/integrate/architecture/adr-029-fee-grant-module.md deleted file mode 100644 index f6e9a4883..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-029-fee-grant-module.md +++ /dev/null @@ -1,153 +0,0 @@ -# ADR 029: Fee Grant Module - -## Changelog - -- 2020/08/18: Initial Draft -- 2021/05/05: Removed height based expiration support and simplified naming. - -## Status - -Accepted - -## Context - -In order to make blockchain transactions, the signing account must possess a sufficient balance of the right denomination -in order to pay fees. There are classes of transactions where needing to maintain a wallet with sufficient fees is a -barrier to adoption. - -For instance, when proper permissions are setup, someone may temporarily delegate the ability to vote on proposals to -a "burner" account that is stored on a mobile phone with only minimal security. - -Other use cases include workers tracking items in a supply chain or farmers submitting field data for analytics -or compliance purposes. - -For all of these use cases, UX would be significantly enhanced by obviating the need for these accounts to always -maintain the appropriate fee balance. This is especially true if we wanted to achieve enterprise adoption for something -like supply chain tracking. - -While one solution would be to have a service that fills up these accounts automatically with the appropriate fees, a better UX -would be provided by allowing these accounts to pull from a common fee pool account with proper spending limits. -A single pool would reduce the churn of making lots of small "fill up" transactions and also more effectively leverages -the resources of the organization setting up the pool. - -## Decision - -As a solution we propose a module, `x/feegrant` which allows one account, the "granter" to grant another account, the "grantee" -an allowance to spend the granter's account balance for fees within certain well-defined limits. - -Fee allowances are defined by the extensible `FeeAllowanceI` interface: - -```go -type FeeAllowanceI { - // Accept can use fee payment requested as well as timestamp of the current block - // to determine whether or not to process this. This is checked in - // Keeper.UseGrantedFees and the return values should match how it is handled there. - // - // If it returns an error, the fee payment is rejected, otherwise it is accepted. - // The FeeAllowance implementation is expected to update it's internal state - // and will be saved again after an acceptance. - // - // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage - // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) - Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (remove bool, err error) - - // ValidateBasic should evaluate this FeeAllowance for internal consistency. - // Don't allow negative amounts, or negative periods for example. - ValidateBasic() error -} -``` - -Two basic fee allowance types, `BasicAllowance` and `PeriodicAllowance` are defined to support known use cases: - -```proto -// BasicAllowance implements FeeAllowanceI with a one-time grant of tokens -// that optionally expires. The delegatee can use up to SpendLimit to cover fees. -message BasicAllowance { - // spend_limit specifies the maximum amount of tokens that can be spent - // by this allowance and will be updated as tokens are spent. If it is - // empty, there is no spend limit and any amount of coins can be spent. - repeated cosmos_sdk.v1.Coin spend_limit = 1; - - // expiration specifies an optional time when this allowance expires - google.protobuf.Timestamp expiration = 2; -} - -// PeriodicAllowance extends FeeAllowanceI to allow for both a maximum cap, -// as well as a limit per time period. -message PeriodicAllowance { - BasicAllowance basic = 1; - - // period specifies the time duration in which period_spend_limit coins can - // be spent before that allowance is reset - google.protobuf.Duration period = 2; - - // period_spend_limit specifies the maximum number of coins that can be spent - // in the period - repeated cosmos_sdk.v1.Coin period_spend_limit = 3; - - // period_can_spend is the number of coins left to be spent before the period_reset time - repeated cosmos_sdk.v1.Coin period_can_spend = 4; - - // period_reset is the time at which this period resets and a new one begins, - // it is calculated from the start time of the first transaction after the - // last period ended - google.protobuf.Timestamp period_reset = 5; -} - -``` - -Allowances can be granted and revoked using `MsgGrantAllowance` and `MsgRevokeAllowance`: - -```proto -// MsgGrantAllowance adds permission for Grantee to spend up to Allowance -// of fees from the account of Granter. -message MsgGrantAllowance { - string granter = 1; - string grantee = 2; - google.protobuf.Any allowance = 3; - } - - // MsgRevokeAllowance removes any existing FeeAllowance from Granter to Grantee. - message MsgRevokeAllowance { - string granter = 1; - string grantee = 2; - } -``` - -In order to use allowances in transactions, we add a new field `granter` to the transaction `Fee` type: - -```proto -package cosmos.tx.v1beta1; - -message Fee { - repeated cosmos.base.v1beta1.Coin amount = 1; - uint64 gas_limit = 2; - string payer = 3; - string granter = 4; -} -``` - -`granter` must either be left empty or must correspond to an account which has granted -a fee allowance to fee payer (either the first signer or the value of the `payer` field). - -A new `AnteDecorator` named `DeductGrantedFeeDecorator` will be created in order to process transactions with `fee_payer` -set and correctly deduct fees based on fee allowances. - -## Consequences - -### Positive - -- improved UX for use cases where it is cumbersome to maintain an account balance just for fees - -### Negative - -### Neutral - -- a new field must be added to the transaction `Fee` message and a new `AnteDecorator` must be -created to use it - -## References - -- Blog article describing initial work: https://medium.com/regen-network/hacking-the-cosmos-cosmwasm-and-key-management-a08b9f561d1b -- Initial public specification: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56 -- Original subkeys proposal from B-harvest which influenced this design: https://github.com/cosmos/cosmos-sdk/issues/4480 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-030-authz-module.md b/versioned_docs/version-0.45/integrate/architecture/adr-030-authz-module.md deleted file mode 100644 index eccbabf16..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-030-authz-module.md +++ /dev/null @@ -1,249 +0,0 @@ -# ADR 030: Authorization Module - -## Changelog - -- 2019-11-06: Initial Draft -- 2020-10-12: Updated Draft -- 2020-11-13: Accepted -- 2020-05-06: proto API updates, use `sdk.Msg` instead of `sdk.ServiceMsg` (the latter concept was removed from SDK) - -## Status - -Accepted - -## Abstract - -This ADR defines the `x/authz` module which allows accounts to grant authorizations to perform actions -on behalf of that account to other accounts. - -## Context - -The concrete use cases which motivated this module include: - -- the desire to delegate the ability to vote on proposals to other accounts besides the account which one has -delegated stake -- "sub-keys" functionality, as originally proposed in [\#4480](https://github.com/cosmos/cosmos-sdk/issues/4480) which -is a term used to describe the functionality provided by this module together with -the `fee_grant` module from [ADR 029](./adr-029-fee-grant-module.md) and the [group module](https://github.com/regen-network/cosmos-modules/tree/master/incubator/group). - -The "sub-keys" functionality roughly refers to the ability for one account to grant some subset of its capabilities to -other accounts with possibly less robust, but easier to use security measures. For instance, a master account representing -an organization could grant the ability to spend small amounts of the organization's funds to individual employee accounts. -Or an individual (or group) with a multisig wallet could grant the ability to vote on proposals to any one of the member -keys. - -The current -implementation is based on work done by the [Gaian's team at Hackatom Berlin 2019](https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation). - -## Decision - -We will create a module named `authz` which provides functionality for -granting arbitrary privileges from one account (the _granter_) to another account (the _grantee_). Authorizations -must be granted for a particular `Msg` service methods one by one using an implementation -of `Authorization` interface. - -### Types - -Authorizations determine exactly what privileges are granted. They are extensible -and can be defined for any `Msg` service method even outside of the module where -the `Msg` method is defined. `Authorization`s reference `Msg`s using their TypeURL. - -#### Authorization - -```go -type Authorization interface { - proto.Message - - // MsgTypeURL returns the fully-qualified Msg TypeURL (as described in ADR 020), - // which will process and accept or reject a request. - MsgTypeURL() string - - // Accept determines whether this grant permits the provided sdk.Msg to be performed, and if - // so provides an upgraded authorization instance. - Accept(ctx sdk.Context, msg sdk.Msg) (AcceptResponse, error) - - // ValidateBasic does a simple validation check that - // doesn't require access to any other information. - ValidateBasic() error -} - -// AcceptResponse instruments the controller of an authz message if the request is accepted -// and if it should be updated or deleted. -type AcceptResponse struct { - // If Accept=true, the controller can accept and authorization and handle the update. - Accept bool - // If Delete=true, the controller must delete the authorization object and release - // storage resources. - Delete bool - // Controller, who is calling Authorization.Accept must check if `Updated != nil`. If yes, - // it must use the updated version and handle the update on the storage level. - Updated Authorization -} -``` - -For example a `SendAuthorization` like this is defined for `MsgSend` that takes -a `SpendLimit` and updates it down to zero: - -```go -type SendAuthorization struct { - // SpendLimit specifies the maximum amount of tokens that can be spent - // by this authorization and will be updated as tokens are spent. If it is - // empty, there is no spend limit and any amount of coins can be spent. - SpendLimit sdk.Coins -} - -func (a SendAuthorization) MsgTypeURL() string { - return sdk.MsgTypeURL(&MsgSend{}) -} - -func (a SendAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { - mSend, ok := msg.(*MsgSend) - if !ok { - return authz.AcceptResponse{}, sdkerrors.ErrInvalidType.Wrap("type mismatch") - } - limitLeft, isNegative := a.SpendLimit.SafeSub(mSend.Amount) - if isNegative { - return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") - } - if limitLeft.IsZero() { - return authz.AcceptResponse{Accept: true, Delete: true}, nil - } - - return authz.AcceptResponse{Accept: true, Delete: false, Updated: &SendAuthorization{SpendLimit: limitLeft}}, nil -} -``` - -A different type of capability for `MsgSend` could be implemented -using the `Authorization` interface with no need to change the underlying -`bank` module. - -### `Msg` Service - -```proto -service Msg { - // Grant grants the provided authorization to the grantee on the granter's - // account with the provided expiration time. - rpc Grant(MsgGrant) returns (MsgGrantResponse); - - // Exec attempts to execute the provided messages using - // authorizations granted to the grantee. Each message should have only - // one signer corresponding to the granter of the authorization. - rpc Exec(MsgExec) returns (MsgExecResponse); - - // Revoke revokes any authorization corresponding to the provided method name on the - // granter's account that has been granted to the grantee. - rpc Revoke(MsgRevoke) returns (MsgRevokeResponse); -} - -// Grant gives permissions to execute -// the provided method with expiration time. -message Grant { - google.protobuf.Any authorization = 1 [(cosmos_proto.accepts_interface) = "Authorization"]; - google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; -} - -message MsgGrant { - string granter = 1; - string grantee = 2; - - Grant grant = 3 [(gogoproto.nullable) = false]; -} - -message MsgExecResponse { - cosmos.base.abci.v1beta1.Result result = 1; -} - -message MsgExec { - string grantee = 1; - // Authorization Msg requests to execute. Each msg must implement Authorization interface - repeated google.protobuf.Any msgs = 2 [(cosmos_proto.accepts_interface) = "sdk.Msg"];; -} -``` - -### Router Middleware - -The `authz` `Keeper` will expose a `DispatchActions` method which allows other modules to send `Msg`s -to the router based on `Authorization` grants: - -```go -type Keeper interface { - // DispatchActions routes the provided msgs to their respective handlers if the grantee was granted an authorization - // to send those messages by the first (and only) signer of each msg. - DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.Msg) sdk.Result` -} -``` - -### CLI - -#### `tx exec` Method - -When a CLI user wants to run a transaction on behalf of another account using `MsgExec`, they -can use the `exec` method. For instance `gaiacli tx gov vote 1 yes --from --generate-only | gaiacli tx authz exec --send-as --from ` -would send a transaction like this: - -```go -MsgExec { - Grantee: mykey, - Msgs: []sdk.Msg{ - MsgVote { - ProposalID: 1, - Voter: cosmos3thsdgh983egh823 - Option: Yes - } - } -} -``` - -#### `tx grant --from ` - -This CLI command will send a `MsgGrant` transaction. `authorization` should be encoded as -JSON on the CLI. - -#### `tx revoke --from ` - -This CLI command will send a `MsgRevoke` transaction. - -### Built-in Authorizations - -#### `SendAuthorization` - -```proto -// SendAuthorization allows the grantee to spend up to spend_limit coins from -// the granter's account. -message SendAuthorization { - repeated cosmos.base.v1beta1.Coin spend_limit = 1; -} -``` - -#### `GenericAuthorization` - -```proto -// GenericAuthorization gives the grantee unrestricted permissions to execute -// the provided method on behalf of the granter's account. -message GenericAuthorization { - option (cosmos_proto.implements_interface) = "Authorization"; - - // Msg, identified by it's type URL, to grant unrestricted permissions to execute - string msg = 1; -} -``` - -## Consequences - -### Positive - -- Users will be able to authorize arbitrary actions on behalf of their accounts to other -users, improving key management for many use cases -- The solution is more generic than previously considered approaches and the -`Authorization` interface approach can be extended to cover other use cases by -SDK users - -### Negative - -### Neutral - -## References - -- Initial Hackatom implementation: https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation -- Post-Hackatom spec: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#delegation-module -- B-Harvest subkeys spec: https://github.com/cosmos/cosmos-sdk/issues/4480 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-031-msg-service.md b/versioned_docs/version-0.45/integrate/architecture/adr-031-msg-service.md deleted file mode 100644 index e11813c73..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-031-msg-service.md +++ /dev/null @@ -1,202 +0,0 @@ -# ADR 031: Protobuf Msg Services - -## Changelog - -- 2020-10-05: Initial Draft -- 2021-04-21: Remove `ServiceMsg`s to follow Protobuf `Any`'s spec, see [#9063](https://github.com/cosmos/cosmos-sdk/issues/9063). - -## Status - -Accepted - -## Abstract - -We want to leverage protobuf `service` definitions for defining `Msg`s which will give us significant developer UX -improvements in terms of the code that is generated and the fact that return types will now be well defined. - -## Context - -Currently `Msg` handlers in the Cosmos SDK do have return values that are placed in the `data` field of the response. -These return values, however, are not specified anywhere except in the golang handler code. - -In early conversations [it was proposed](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc/edit) -that `Msg` return types be captured using a protobuf extension field, ex: - -```protobuf -package cosmos.gov; - -message MsgSubmitProposal - option (cosmos_proto.msg_return) = “uint64”; - string delegator_address = 1; - string validator_address = 2; - repeated sdk.Coin amount = 3; -} -``` - -This was never adopted, however. - -Having a well-specified return value for `Msg`s would improve client UX. For instance, -in `x/gov`, `MsgSubmitProposal` returns the proposal ID as a big-endian `uint64`. -This isn’t really documented anywhere and clients would need to know the internals -of the SDK to parse that value and return it to users. - -Also, there may be cases where we want to use these return values programatically. -For instance, https://github.com/cosmos/cosmos-sdk/issues/7093 proposes a method for -doing inter-module Ocaps using the `Msg` router. A well-defined return type would -improve the developer UX for this approach. - -In addition, handler registration of `Msg` types tends to add a bit of -boilerplate on top of keepers and is usually done through manual type switches. -This isn't necessarily bad, but it does add overhead to creating modules. - -## Decision - -We decide to use protobuf `service` definitions for defining `Msg`s as well as -the code generated by them as a replacement for `Msg` handlers. - -Below we define how this will look for the `SubmitProposal` message from `x/gov` module. -We start with a `Msg` `service` definition: - -```proto -package cosmos.gov; - -service Msg { - rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse); -} - -// Note that for backwards compatibility this uses MsgSubmitProposal as the request -// type instead of the more canonical MsgSubmitProposalRequest -message MsgSubmitProposal { - google.protobuf.Any content = 1; - string proposer = 2; -} - -message MsgSubmitProposalResponse { - uint64 proposal_id; -} -``` - -While this is most commonly used for gRPC, overloading protobuf `service` definitions like this does not violate -the intent of the [protobuf spec](https://developers.google.com/protocol-buffers/docs/proto3#services) which says: -> If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation. -With this approach, we would get an auto-generated `MsgServer` interface: - -In addition to clearly specifying return types, this has the benefit of generating client and server code. On the server -side, this is almost like an automatically generated keeper method and could maybe be used intead of keepers eventually -(see [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093)): - -```go -package gov - -type MsgServer interface { - SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) -} -``` - -On the client side, developers could take advantage of this by creating RPC implementations that encapsulate transaction -logic. Protobuf libraries that use asynchronous callbacks, like [protobuf.js](https://github.com/protobufjs/protobuf.js#using-services) -could use this to register callbacks for specific messages even for transactions that include multiple `Msg`s. - -Each `Msg` service method should have exactly one request parameter: its corresponding `Msg` type. For example, the `Msg` service method `/cosmos.gov.v1beta1.Msg/SubmitProposal` above has exactly one request parameter, namely the `Msg` type `/cosmos.gov.v1beta1.MsgSubmitProposal`. It is important the reader understands clearly the nomenclature difference between a `Msg` service (a Protobuf service) and a `Msg` type (a Protobuf message), and the differences in their fully-qualified name. - -This convention has been decided over the more canonical `Msg...Request` names mainly for backwards compatibility, but also for better readability in `TxBody.messages` (see [Encoding section](#encoding) below): transactions containing `/cosmos.gov.MsgSubmitProposal` read better than those containing `/cosmos.gov.v1beta1.MsgSubmitProposalRequest`. - -One consequence of this convention is that each `Msg` type can be the request parameter of only one `Msg` service method. However, we consider this limitation a good practice in explicitness. - -### Encoding - -Encoding of transactions generated with `Msg` services do not differ from current Protobuf transaction encoding as defined in [ADR-020](./adr-020-protobuf-transaction-encoding.md). We are encoding `Msg` types (which are exactly `Msg` service methods' request parameters) as `Any` in `Tx`s which involves packing the -binary-encoded `Msg` with its type URL. - -### Decoding - -Since `Msg` types are packed into `Any`, decoding transactions messages are done by unpacking `Any`s into `Msg` types. For more information, please refer to [ADR-020](./adr-020-protobuf-transaction-encoding.md#transactions). - -### Routing - -We propose to add a `msg_service_router` in BaseApp. This router is a key/value map which maps `Msg` types' `type_url`s to their corresponding `Msg` service method handler. Since there is a 1-to-1 mapping between `Msg` types and `Msg` service method, the `msg_service_router` has exactly one entry per `Msg` service method. - -When a transaction is processed by BaseApp (in CheckTx or in DeliverTx), its `TxBody.messages` are decoded as `Msg`s. Each `Msg`'s `type_url` is matched against an entry in the `msg_service_router`, and the respective `Msg` service method handler is called. - -For backward compatability, the old handlers are not removed yet. If BaseApp receives a legacy `Msg` with no correspoding entry in the `msg_service_router`, it will be routed via its legacy `Route()` method into the legacy handler. - -### Module Configuration - -In [ADR 021](./adr-021-protobuf-query-encoding.md), we introduced a method `RegisterQueryService` -to `AppModule` which allows for modules to register gRPC queriers. - -To register `Msg` services, we attempt a more extensible approach by converting `RegisterQueryService` -to a more generic `RegisterServices` method: - -```go -type AppModule interface { - RegisterServices(Configurator) - ... -} - -type Configurator interface { - QueryServer() grpc.Server - MsgServer() grpc.Server -} - -// example module: -func (am AppModule) RegisterServices(cfg Configurator) { - types.RegisterQueryServer(cfg.QueryServer(), keeper) - types.RegisterMsgServer(cfg.MsgServer(), keeper) -} -``` - -The `RegisterServices` method and the `Configurator` interface are intended to -evolve to satisfy the use cases discussed in [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) -and [\#7122](https://github.com/cosmos/cosmos-sdk/issues/7421). - -When `Msg` services are registered, the framework _should_ verify that all `Msg` types -implement the `sdk.Msg` interface and throw an error during initialization rather -than later when transactions are processed. - -### `Msg` Service Implementation - -Just like query services, `Msg` service methods can retrieve the `sdk.Context` -from the `context.Context` parameter method using the `sdk.UnwrapSDKContext` -method: - -```go -package gov - -func (k Keeper) SubmitProposal(goCtx context.Context, params *types.MsgSubmitProposal) (*MsgSubmitProposalResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - ... -} -``` - -The `sdk.Context` should have an `EventManager` already attached by BaseApp's `msg_service_router`. - -Separate handler definition is no longer needed with this approach. - -## Consequences - -This design changes how a module functionality is exposed and accessed. It deprecates the existing `Handler` interface and `AppModule.Route` in favor of [Protocol Buffer Services](https://developers.google.com/protocol-buffers/docs/proto3#services) and Service Routing described above. This dramatically simplifies the code. We don't need to create handlers and keepers any more. Use of Protocol Buffer auto-generated clients clearly separates the communication interfaces between the module and a modules user. The control logic (aka handlers and keepers) is not exposed any more. A module interface can be seen as a black box accessible through a client API. It's worth to note that the client interfaces are also generated by Protocol Buffers. - -This also allows us to change how we perform functional tests. Instead of mocking AppModules and Router, we will mock a client (server will stay hidden). More specifically: we will never mock `moduleA.MsgServer` in `moduleB`, but rather `moduleA.MsgClient`. One can think about it as working with external services (eg DBs, or online servers...). We assume that the transmission between clients and servers is correctly handled by generated Protocol Buffers. - -Finally, closing a module to client API opens desirable OCAP patterns discussed in ADR-033. Since server implementation and interface is hidden, nobody can hold "keepers"/servers and will be forced to relay on the client interface, which will drive developers for correct encapsulation and software engineering patterns. - -### Pros - -- communicates return type clearly -- manual handler registration and return type marshaling is no longer needed, just implement the interface and register it -- communication interface is automatically generated, the developer can now focus only on the state transition methods - this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that -- generated client code could be useful for clients and tests -- dramatically reduces and simplifies the code - -### Cons - -- using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec) - -## References - -- [Initial Github Issue \#7122](https://github.com/cosmos/cosmos-sdk/issues/7122) -- [proto 3 Language Guide: Defining Services](https://developers.google.com/protocol-buffers/docs/proto3#services) -- [Initial pre-`Any` `Msg` designs](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc) -- [ADR 020](./adr-020-protobuf-transaction-encoding.md) -- [ADR 021](./adr-021-protobuf-query-encoding.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-032-typed-events.md b/versioned_docs/version-0.45/integrate/architecture/adr-032-typed-events.md deleted file mode 100644 index a85126b72..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-032-typed-events.md +++ /dev/null @@ -1,319 +0,0 @@ -# ADR 032: Typed Events - -## Changelog - -- 28-Sept-2020: Initial Draft - -## Authors - -- Anil Kumar (@anilcse) -- Jack Zampolin (@jackzampolin) -- Adam Bozanich (@boz) - -## Status - -Proposed - -## Abstract - -Currently in the SDK, events are defined in the handlers for each message as well as `BeginBlock` and `EndBlock`. Each module doesn't have types defined for each event, they are implemented as `map[string]string`. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team. - -## Context - -Currently in the SDK, events are defined in the handlers for each message, meaning each module doesn't have a cannonical set of types for each event. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team. - -[Our platform](http://github.com/ovrclk/akash) requires a number of programatic on chain interactions both on the provider (datacenter - to bid on new orders and listen for leases created) and user (application developer - to send the app manifest to the provider) side. In addition the Akash team is now maintaining the IBC [`relayer`](https://github.com/ovrclk/relayer), another very event driven process. In working on these core pieces of infrastructure, and integrating lessons learned from Kubernetes developement, our team has developed a standard method for defining and consuming typed events in SDK modules. We have found that it is extremely useful in building this type of event driven application. - -As the SDK gets used more extensively for apps like `peggy`, other peg zones, IBC, DeFi, etc... there will be an exploding demand for event driven applications to support new features desired by users. We propose upstreaming our findings into the SDK to enable all SDK applications to quickly and easily build event driven apps to aid their core application. Wallets, exchanges, explorers, and defi protocols all stand to benefit from this work. - -If this proposal is accepted, users will be able to build event driven SDK apps in go by just writing `EventHandler`s for their specific event types and passing them to `EventEmitters` that are defined in the SDK. - -The end of this proposal contains a detailed example of how to consume events after this refactor. - -This proposal is specifically about how to consume these events as a client of the blockchain, not for intermodule communication. - -## Decision - -__Step-1__: Implement additional functionality in the `types` package: `EmitTypedEvent` and `ParseTypedEvent` functions - -```go -// types/events.go - -// EmitTypedEvent takes typed event and emits converting it into sdk.Event -func (em *EventManager) EmitTypedEvent(event proto.Message) error { - evtType := proto.MessageName(event) - evtJSON, err := codec.ProtoMarshalJSON(event) - if err != nil { - return err - } - - var attrMap map[string]json.RawMessage - err = json.Unmarshal(evtJSON, &attrMap) - if err != nil { - return err - } - - var attrs []abci.EventAttribute - for k, v := range attrMap { - attrs = append(attrs, abci.EventAttribute{ - Key: []byte(k), - Value: v, - }) - } - - em.EmitEvent(Event{ - Type: evtType, - Attributes: attrs, - }) - - return nil -} - -// ParseTypedEvent converts abci.Event back to typed event -func ParseTypedEvent(event abci.Event) (proto.Message, error) { - concreteGoType := proto.MessageType(event.Type) - if concreteGoType == nil { - return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type) - } - - var value reflect.Value - if concreteGoType.Kind() == reflect.Ptr { - value = reflect.New(concreteGoType.Elem()) - } else { - value = reflect.Zero(concreteGoType) - } - - protoMsg, ok := value.Interface().(proto.Message) - if !ok { - return nil, fmt.Errorf("%q does not implement proto.Message", event.Type) - } - - attrMap := make(map[string]json.RawMessage) - for _, attr := range event.Attributes { - attrMap[string(attr.Key)] = attr.Value - } - - attrBytes, err := json.Marshal(attrMap) - if err != nil { - return nil, err - } - - err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg) - if err != nil { - return nil, err - } - - return protoMsg, nil -} -``` - -Here, the `EmitTypedEvent` is a method on `EventManager` which takes typed event as input and apply json serialization on it. Then it maps the JSON key/value pairs to `event.Attributes` and emits it in form of `sdk.Event`. `Event.Type` will be the type URL of the proto message. - -When we subscribe to emitted events on the tendermint websocket, they are emitted in the form of an `abci.Event`. `ParseTypedEvent` parses the event back to it's original proto message. - -__Step-2__: Add proto definitions for typed events for msgs in each module: - -For example, let's take `MsgSubmitProposal` of `gov` module and implement this event's type. - -```protobuf -// proto/cosmos/gov/v1beta1/gov.proto -// Add typed event definition - -package cosmos.gov.v1beta1; - -message EventSubmitProposal { - string from_address = 1; - uint64 proposal_id = 2; - TextProposal proposal = 3; -} -``` - -__Step-3__: Refactor event emission to use the typed event created and emit using `sdk.EmitTypedEvent`: - -```go -// x/gov/handler.go -func handleMsgSubmitProposal(ctx sdk.Context, keeper keeper.Keeper, msg types.MsgSubmitProposalI) (*sdk.Result, error) { - ... - types.Context.EventManager().EmitTypedEvent( - &EventSubmitProposal{ - FromAddress: fromAddress, - ProposalId: id, - Proposal: proposal, - }, - ) - ... -} -``` - -#### How to subscribe to these typed events in `Client` - -> NOTE: Full code example below - -Users will be able to subscribe using `client.Context.Client.Subscribe` and consume events which are emitted using `EventHandler`s. - -Akash Network has built a simple [`pubsub`](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/pubsub/bus.go#L20). This can be used to subscribe to `abci.Events` and [publish](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L21) them as typed events. - -Please see the below code sample for more detail on this flow looks for clients. - -## Consequences - -### Positive - -* Improves consistency of implementation for the events currently in the sdk -* Provides a much more ergonomic way to handle events and facilitates writing event driven applications -* This implementation will support a middleware ecosystem of `EventHandler`s - -### Negative - -## Detailed code example of publishing events - -This ADR also proposes adding affordances to emit and consume these events. This way developers will only need to write -`EventHandler`s which define the actions they desire to take. - -```go -// EventEmitter is a type that describes event emitter functions -// This should be defined in `types/events.go` -type EventEmitter func(context.Context, client.Context, ...EventHandler) error - -// EventHandler is a type of function that handles events coming out of the event bus -// This should be defined in `types/events.go` -type EventHandler func(proto.Message) error - -// Sample use of the functions below -func main() { - ctx, cancel := context.WithCancel(context.Background()) - - if err := TxEmitter(ctx, client.Context{}.WithNodeURI("tcp://localhost:26657"), SubmitProposalEventHandler); err != nil { - cancel() - panic(err) - } - - return -} - -// SubmitProposalEventHandler is an example of an event handler that prints proposal details -// when any EventSubmitProposal is emitted. -func SubmitProposalEventHandler(ev proto.Message) (err error) { - switch event := ev.(type) { - // Handle governance proposal events creation events - case govtypes.EventSubmitProposal: - // Users define business logic here e.g. - fmt.Println(ev.FromAddress, ev.ProposalId, ev.Proposal) - return nil - default: - return nil - } -} - -// TxEmitter is an example of an event emitter that emits just transaction events. This can and -// should be implemented somewhere in the SDK. The SDK can include an EventEmitters for tm.event='Tx' -// and/or tm.event='NewBlock' (the new block events may contain typed events) -func TxEmitter(ctx context.Context, cliCtx client.Context, ehs ...EventHandler) (err error) { - // Instantiate and start tendermint RPC client - client, err := cliCtx.GetNode() - if err != nil { - return err - } - - if err = client.Start(); err != nil { - return err - } - - // Start the pubsub bus - bus := pubsub.NewBus() - defer bus.Close() - - // Initialize a new error group - eg, ctx := errgroup.WithContext(ctx) - - // Publish chain events to the pubsub bus - eg.Go(func() error { - return PublishChainTxEvents(ctx, client, bus, simapp.ModuleBasics) - }) - - // Subscribe to the bus events - subscriber, err := bus.Subscribe() - if err != nil { - return err - } - - // Handle all the events coming out of the bus - eg.Go(func() error { - var err error - for { - select { - case <-ctx.Done(): - return nil - case <-subscriber.Done(): - return nil - case ev := <-subscriber.Events(): - for _, eh := range ehs { - if err = eh(ev); err != nil { - break - } - } - } - } - return nil - }) - - return group.Wait() -} - -// PublishChainTxEvents events using tmclient. Waits on context shutdown signals to exit. -func PublishChainTxEvents(ctx context.Context, client tmclient.EventsClient, bus pubsub.Bus, mb module.BasicManager) (err error) { - // Subscribe to transaction events - txch, err := client.Subscribe(ctx, "txevents", "tm.event='Tx'", 100) - if err != nil { - return err - } - - // Unsubscribe from transaction events on function exit - defer func() { - err = client.UnsubscribeAll(ctx, "txevents") - }() - - // Use errgroup to manage concurrency - g, ctx := errgroup.WithContext(ctx) - - // Publish transaction events in a goroutine - g.Go(func() error { - var err error - for { - select { - case <-ctx.Done(): - break - case ed := <-ch: - switch evt := ed.Data.(type) { - case tmtypes.EventDataTx: - if !evt.Result.IsOK() { - continue - } - // range over events, parse them using the basic manager and - // send them to the pubsub bus - for _, abciEv := range events { - typedEvent, err := sdk.ParseTypedEvent(abciEv) - if err != nil { - return er - } - if err := bus.Publish(typedEvent); err != nil { - bus.Close() - return - } - continue - } - } - } - } - return err - }) - - // Exit on error or context cancelation - return g.Wait() -} -``` - -## References - -- [Publish Custom Events via a bus](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L19-L58) -- [Consuming the events in `Client`](https://github.com/ovrclk/deploy/blob/bf6c633ab6c68f3026df59efd9982d6ca1bf0561/cmd/event-handlers.go#L57) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-033-protobuf-inter-module-comm.md b/versioned_docs/version-0.45/integrate/architecture/adr-033-protobuf-inter-module-comm.md deleted file mode 100644 index 6234c3d10..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-033-protobuf-inter-module-comm.md +++ /dev/null @@ -1,400 +0,0 @@ -# ADR 033: Protobuf-based Inter-Module Communication - -## Changelog - -- 2020-10-05: Initial Draft - -## Status - -Proposed - -## Abstract - -This ADR introduces a system for permissioned inter-module communication leveraging the protobuf `Query` and `Msg` -service definitions defined in [ADR 021](./adr-021-protobuf-query-encoding.md) and -[ADR 031](./adr-031-msg-service.md) which provides: - -- stable protobuf based module interfaces to potentially later replace the keeper paradigm -- stronger inter-module object capabilities (OCAPs) guarantees -- module accounts and sub-account authorization - -## Context - -In the current Cosmos SDK documentation on the [Object-Capability Model](../core/ocap.md), it is stated that: - -> We assume that a thriving ecosystem of Cosmos-SDK modules that are easy to compose into a blockchain application will contain faulty or malicious modules. - -There is currently not a thriving ecosystem of Cosmos SDK modules. We hypothesize that this is in part due to: - -1. lack of a stable v1.0 Cosmos SDK to build modules off of. Module interfaces are changing, sometimes dramatically, from -point release to point release, often for good reasons, but this does not create a stable foundation to build on. -2. lack of a properly implemented object capability or even object-oriented encapsulation system which makes refactors -of module keeper interfaces inevitable because the current interfaces are poorly constrained. - -### `x/bank` Case Study - -Currently the `x/bank` keeper gives pretty much unrestricted access to any module which references it. For instance, the -`SetBalance` method allows the caller to set the balance of any account to anything, bypassing even proper tracking of supply. - -There appears to have been some later attempts to implement some semblance of OCAPs using module-level minting, staking -and burning permissions. These permissions allow a module to mint, burn or delegate tokens with reference to the module’s -own account. These permissions are actually stored as a `[]string` array on the `ModuleAccount` type in state. - -However, these permissions don’t really do much. They control what modules can be referenced in the `MintCoins`, -`BurnCoins` and `DelegateCoins***` methods, but for one there is no unique object capability token that controls access — -just a simple string. So the `x/upgrade` module could mint tokens for the `x/staking` module simple by calling -`MintCoins(“staking”)`. Furthermore, all modules which have access to these keeper methods, also have access to -`SetBalance` negating any other attempt at OCAPs and breaking even basic object-oriented encapsulation. - -## Decision - -Based on [ADR-021](./adr-021-protobuf-query-encoding.md) and [ADR-031](./adr-031-msg-service.md), we introduce the -Inter-Module Communication framework for secure module authorization and OCAPs. -When implemented, this could also serve as an alternative to the existing paradigm of passing keepers between -modules. The approach outlined here-in is intended to form the basis of a Cosmos SDK v1.0 that provides the necessary -stability and encapsulation guarantees that allow a thriving module ecosystem to emerge. - -Of particular note — the decision is to _enable_ this functionality for modules to adopt at their own discretion. -Proposals to migrate existing modules to this new paradigm will have to be a separate conversation, potentially -addressed as amendments to this ADR. - -### New "Keeper" Paradigm - -In [ADR 021](./adr-021-protobuf-query-encoding.md), a mechanism for using protobuf service definitions to define queriers -was introduced and in [ADR 31](./adr-031-msg-service.md), a mechanism for using protobuf service to define `Msg`s was added. -Protobuf service definitions generate two golang interfaces representing the client and server sides of a service plus -some helper code. Here is a minimal example for the bank `cosmos.bank.Msg/Send` message type: - -```go -package bank - -type MsgClient interface { - Send(context.Context, *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error) -} - -type MsgServer interface { - Send(context.Context, *MsgSend) (*MsgSendResponse, error) -} -``` - -[ADR 021](./adr-021-protobuf-query-encoding.md) and [ADR 31](./adr-031-msg-service.md) specifies how modules can implement the generated `QueryServer` -and `MsgServer` interfaces as replacements for the legacy queriers and `Msg` handlers respectively. - -In this ADR we explain how modules can make queries and send `Msg`s to other modules using the generated `QueryClient` -and `MsgClient` interfaces and propose this mechanism as a replacement for the existing `Keeper` paradigm. To be clear, -this ADR does not necessitate the creation of new protobuf definitions or services. Rather, it leverages the same proto -based service interfaces already used by clients for inter-module communication. - -Using this `QueryClient`/`MsgClient` approach has the following key benefits over exposing keepers to external modules: - -1. Protobuf types are checked for breaking changes using [buf](https://buf.build/docs/breaking-overview) and because of -the way protobuf is designed this will give us strong backwards compatibility guarantees while allowing for forward -evolution. -2. The separation between the client and server interfaces will allow us to insert permission checking code in between -the two which checks if one module is authorized to send the specified `Msg` to the other module providing a proper -object capability system (see below). -3. The router for inter-module communication gives us a convenient place to handle rollback of transactions, -enabling atomicy of operations ([currently a problem](https://github.com/cosmos/cosmos-sdk/issues/8030)). Any failure within a module-to-module call would result in a failure of the entire -transaction - -This mechanism has the added benefits of: - -- reducing boilerplate through code generation, and -- allowing for modules in other languages either via a VM like CosmWasm or sub-processes using gRPC - -### Inter-module Communication - -To use the `Client` generated by the protobuf compiler we need a `grpc.ClientConn` [interface](https://github.com/regen-network/protobuf/blob/cosmos/grpc/types.go#L12) -implementation. For this we introduce -a new type, `ModuleKey`, which implements the `grpc.ClientConn` interface. `ModuleKey` can be thought of as the "private -key" corresponding to a module account, where authentication is provided through use of a special `Invoker()` function, -described in more detail below. - -Blockchain users (external clients) use their account's private key to sign transactions containing `Msg`s where they are listed as signers (each -message specifies required signers with `Msg.GetSigner`). The authentication checks is performed by `AnteHandler`. - -Here, we extend this process, by allowing modules to be identified in `Msg.GetSigners`. When a module wants to trigger the execution a `Msg` in another module, -its `ModuleKey` acts as the sender (through the `ClientConn` interface we describe below) and is set as a sole "signer". It's worth to note -that we don't use any cryptographic signature in this case. -For example, module `A` could use its `A.ModuleKey` to create `MsgSend` object for `/cosmos.bank.Msg/Send` transaction. `MsgSend` validation -will assure that the `from` account (`A.ModuleKey` in this case) is the signer. - -Here's an example of a hypothetical module `foo` interacting with `x/bank`: - -```go -package foo - - -type FooMsgServer { - // ... - - bankQuery bank.QueryClient - bankMsg bank.MsgClient -} - -func NewFooMsgServer(moduleKey RootModuleKey, ...) FooMsgServer { - // ... - - return FooMsgServer { - // ... - modouleKey: moduleKey, - bankQuery: bank.NewQueryClient(moduleKey), - bankMsg: bank.NewMsgClient(moduleKey), - } -} - -func (foo *FooMsgServer) Bar(ctx context.Context, req *MsgBarRequest) (*MsgBarResponse, error) { - balance, err := foo.bankQuery.Balance(&bank.QueryBalanceRequest{Address: fooMsgServer.moduleKey.Address(), Denom: "foo"}) - - ... - - res, err := foo.bankMsg.Send(ctx, &bank.MsgSendRequest{FromAddress: fooMsgServer.moduleKey.Address(), ...}) - - ... -} -``` - -This design is also intended to be extensible to cover use cases of more fine grained permissioning like minting by -denom prefix being restricted to certain modules (as discussed in -[#7459](https://github.com/cosmos/cosmos-sdk/pull/7459#discussion_r529545528)). - -### `ModuleKey`s and `ModuleID`s - -A `ModuleKey` can be thought of as a "private key" for a module account and a `ModuleID` can be thought of as the -corresponding "public key". From the [ADR 028](./adr-028-public-key-addresses.md), modules can have both a root module account and any number of sub-accounts -or derived accounts that can be used for different pools (ex. staking pools) or managed accounts (ex. group -accounts). We can also think of module sub-accounts as similar to derived keys - there is a root key and then some -derivation path. `ModuleID` is a simple struct which contains the module name and optional "derivation" path, -and forms its address based on the `AddressHash` method from [the ADR-028](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-028-public-key-addresses.md): - -```go -type ModuleID struct { - ModuleName string - Path []byte -} - -func (key ModuleID) Address() []byte { - return AddressHash(key.ModuleName, key.Path) -} -``` - -In addition to being able to generate a `ModuleID` and address, a `ModuleKey` contains a special function called -`Invoker` which is the key to safe inter-module access. The `Invoker` creates an `InvokeFn` closure which is used as an `Invoke` method in -the `grpc.ClientConn` interface and under the hood is able to route messages to the appropriate `Msg` and `Query` handlers -performing appropriate security checks on `Msg`s. This allows for even safer inter-module access than keeper's whose -private member variables could be manipulated through reflection. Golang does not support reflection on a function -closure's captured variables and direct manipulation of memory would be needed for a truly malicious module to bypass -the `ModuleKey` security. - -The two `ModuleKey` types are `RootModuleKey` and `DerivedModuleKey`: - -```go -type Invoker func(callInfo CallInfo) func(ctx context.Context, request, response interface{}, opts ...interface{}) error - -type CallInfo { - Method string - Caller ModuleID -} - -type RootModuleKey struct { - moduleName string - invoker Invoker -} - -func (rm RootModuleKey) Derive(path []byte) DerivedModuleKey { /* ... */} - -type DerivedModuleKey struct { - moduleName string - path []byte - invoker Invoker -} -``` - -A module can get access to a `DerivedModuleKey`, using the `Derive(path []byte)` method on `RootModuleKey` and then -would use this key to authenticate `Msg`s from a sub-account. Ex: - -```go -package foo - -func (fooMsgServer *MsgServer) Bar(ctx context.Context, req *MsgBar) (*MsgBarResponse, error) { - derivedKey := fooMsgServer.moduleKey.Derive(req.SomePath) - bankMsgClient := bank.NewMsgClient(derivedKey) - res, err := bankMsgClient.Balance(ctx, &bank.MsgSend{FromAddress: derivedKey.Address(), ...}) - ... -} -``` - -In this way, a module can gain permissioned access to a root account and any number of sub-accounts and send -authenticated `Msg`s from these accounts. The `Invoker` `callInfo.Caller` parameter is used under the hood to -distinguish between different module accounts, but either way the function returned by `Invoker` only allows `Msg`s -from either the root or a derived module account to pass through. - -Note that `Invoker` itself returns a function closure based on the `CallInfo` passed in. This will allow client implementations -in the future that cache the invoke function for each method type avoiding the overhead of hash table lookup. -This would reduce the performance overhead of this inter-module communication method to the bare minimum required for -checking permissions. - -To re-iterate, the closure only allows access to authorized calls. There is no access to anything else regardless of any -name impersonation. - -Below is a rough sketch of the implementation of `grpc.ClientConn.Invoke` for `RootModuleKey`: - -```go -func (key RootModuleKey) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...grpc.CallOption) error { - f := key.invoker(CallInfo {Method: method, Caller: ModuleID {ModuleName: key.moduleName}}) - return f(ctx, args, reply) -} -``` - -### `AppModule` Wiring and Requirements - -In [ADR 031](./adr-031-msg-service.md), the `AppModule.RegisterService(Configurator)` method was introduced. To support -inter-module communication, we extend the `Configurator` interface to pass in the `ModuleKey` and to allow modules to -specify their dependencies on other modules using `RequireServer()`: - -```go -type Configurator interface { - MsgServer() grpc.Server - QueryServer() grpc.Server - - ModuleKey() ModuleKey - RequireServer(msgServer interface{}) -} -``` - -The `ModuleKey` is passed to modules in the `RegisterService` method itself so that `RegisterServices` serves as a single -entry point for configuring module services. This is intended to also have the side-effect of greatly reducing boilerplate in -`app.go`. For now, `ModuleKey`s will be created based on `AppModuleBasic.Name()`, but a more flexible system may be -introduced in the future. The `ModuleManager` will handle creation of module accounts behind the scenes. - -Because modules do not get direct access to each other anymore, modules may have unfulfilled dependencies. To make sure -that module dependencies are resolved at startup, the `Configurator.RequireServer` method should be added. The `ModuleManager` -will make sure that all dependencies declared with `RequireServer` can be resolved before the app starts. An example -module `foo` could declare it's dependency on `x/bank` like this: - -```go -package foo - -func (am AppModule) RegisterServices(cfg Configurator) { - cfg.RequireServer((*bank.QueryServer)(nil)) - cfg.RequireServer((*bank.MsgServer)(nil)) -} -``` - -### Security Considerations - -In addition to checking for `ModuleKey` permissions, a few additional security precautions will need to be taken by -the underlying router infrastructure. - -#### Recursion and Re-entry - -Recursive or re-entrant method invocations pose a potential security threat. This can be a problem if Module A -calls Module B and Module B calls module A again in the same call. - -One basic way for the router system to deal with this is to maintain a call stack which prevents a module from -being referenced more than once in the call stack so that there is no re-entry. A `map[string]interface{}` table -in the router could be used to perform this security check. - -#### Queries - -Queries in Cosmos SDK are generally un-permissioned so allowing one module to query another module should not pose -any major security threats assuming basic precautions are taken. The basic precaution that the router system will -need to take is making sure that the `sdk.Context` passed to query methods does not allow writing to the store. This -can be done for now with a `CacheMultiStore` as is currently done for `BaseApp` queries. - -### Internal Methods - -In many cases, we may wish for modules to call methods on other modules which are not exposed to clients at all. For this -purpose, we add the `InternalServer` method to `Configurator`: - -```go -type Configurator interface { - MsgServer() grpc.Server - QueryServer() grpc.Server - InternalServer() grpc.Server -} -``` - -As an example, x/slashing's Slash must call x/staking's Slash, but we don't want to expose x/staking's Slash to end users -and clients. - -Internal protobuf services will be defined in a corresponding `internal.proto` file in the given module's -proto package. - -Services registered against `InternalServer` will be callable from other modules but not by external clients. - -An alternative solution to internal-only methods could involve hooks / plugins as discussed [here](https://github.com/cosmos/cosmos-sdk/pull/7459#issuecomment-733807753). -A more detailed evaluation of a hooks / plugin system will be addressed later in follow-ups to this ADR or as a separate -ADR. - -### Authorization - -By default, the inter-module router requires that messages are sent by the first signer returned by `GetSigners`. The -inter-module router should also accept authorization middleware such as that provided by [ADR 030](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-030-authz-module.md). -This middleware will allow accounts to otherwise specific module accounts to perform actions on their behalf. -Authorization middleware should take into account the need to grant certain modules effectively "admin" privileges to -other modules. This will be addressed in separate ADRs or updates to this ADR. - -### Future Work - -Other future improvements may include: - -* custom code generation that: - * simplifies interfaces (ex. generates code with `sdk.Context` instead of `context.Context`) - * optimizes inter-module calls - for instance caching resolved methods after first invocation -* combining `StoreKey`s and `ModuleKey`s into a single interface so that modules have a single OCAPs handle -* code generation which makes inter-module communication more performant -* decoupling `ModuleKey` creation from `AppModuleBasic.Name()` so that app's can override root module account names -* inter-module hooks and plugins - -## Alternatives - -### MsgServices vs `x/capability` - -The `x/capability` module does provide a proper object-capability implementation that can be used by any module in the -SDK and could even be used for inter-module OCAPs as described in [\#5931](https://github.com/cosmos/cosmos-sdk/issues/5931). - -The advantages of the approach described in this ADR are mostly around how it integrates with other parts of the SDK, -specifically: - -* protobuf so that: - * code generation of interfaces can be leveraged for a better dev UX - * module interfaces are versioned and checked for breakage using [buf](https://docs.buf.build/breaking-overview) -* sub-module accounts as per ADR 028 -* the general `Msg` passing paradigm and the way signers are specified by `GetSigners` - -Also, this is a complete replacement for keepers and could be applied to _all_ inter-module communication whereas the -`x/capability` approach in #5931 would need to be applied method by method. - -## Consequences - -### Backwards Compatibility - -This ADR is intended to provide a pathway to a scenario where there is greater long term compatibility between modules. -In the short-term, this will likely result in breaking certain `Keeper` interfaces which are too permissive and/or -replacing `Keeper` interfaces altogether. - -### Positive - -- an alternative to keepers which can more easily lead to stable inter-module interfaces -- proper inter-module OCAPs -- improved module developer DevX, as commented on by several particpants on - [Architecture Review Call, Dec 3](https://hackmd.io/E0wxxOvRQ5qVmTf6N_k84Q) -- lays the groundwork for what can be a greatly simplified `app.go` -- router can be setup to enforce atomic transactions for moule-to-module calls - -### Negative - -- modules which adopt this will need significant refactoring - -### Neutral - -## Test Cases [optional] - -## References - -- [ADR 021](./adr-021-protobuf-query-encoding.md) -- [ADR 031](./adr-031-msg-service.md) -- [ADR 028](./adr-028-public-key-addresses.md) -- [ADR 030 draft](https://github.com/cosmos/cosmos-sdk/pull/7105) -- [Object-Capability Model](../docs/core/ocap.md) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-034-account-rekeying.md b/versioned_docs/version-0.45/integrate/architecture/adr-034-account-rekeying.md deleted file mode 100644 index d3b54d17f..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-034-account-rekeying.md +++ /dev/null @@ -1,76 +0,0 @@ -# ADR 034: Account Rekeying - -## Changelog - -- 30-09-2020: Initial Draft - -## Status - -PROPOSED - -## Abstract - -Account rekeying is a process hat allows an account to replace its authentication pubkey with a new one. - -## Context - -Currently, in the Cosmos SDK, the address of an auth `BaseAccount` is based on the hash of the public key. Once an account is created, the public key for the account is set in stone, and cannot be changed. This can be a problem for users, as key rotation is a useful security practice, but is not possible currently. Furthermore, as multisigs are a type of pubkey, once a multisig for an account is set, it can not be updated. This is problematic, as multisigs are often used by organizations or companies, who may need to change their set of multisig signers for internal reasons. - -Transferring all the assets of an account to a new account with the updated pubkey is not sufficient, because some "engagements" of an account are not easily transferable. For example, in staking, to transfer bonded Atoms, an account would have to unbond all delegations and wait the three week unbonding period. Even more significantly, for validator operators, ownership over a validator is not transferrable at all, meaning that the operator key for a validator can never be updated, leading to poor operational security for validators. - -## Decision - -We propose the addition of a new feature to `x/auth` that allows accounts to update the public key associated with their account, while keeping the address the same. - -This is possible because the Cosmos SDK `BaseAccount` stores the public key for an account in state, instead of making the assumption that the public key is included in the transaction (whether explicitly or implicitly through the signature) as in other blockchains such as Bitcoin and Ethereum. Because the public key is stored on chain, it is okay for the public key to not hash to the address of an account, as the address is not pertinent to the signature checking process. - -To build this system, we design a new Msg type as follows: - -```protobuf -service Msg { - rpc ChangePubKey(MsgChangePubKey) returns (MsgChangePubKeyResponse); -} - -message MsgChangePubKey { - string address = 1; - google.protobuf.Any pub_key = 2; -} - -message MsgChangePubKeyResponse {} -``` - -The MsgChangePubKey transaction needs to be signed by the existing pubkey in state. - -Once, approved, the handler for this message type, which takes in the AccountKeeper, will update the in-state pubkey for the account and replace it with the pubkey from the Msg. - -An account that has had its pubkey changed cannot be automatically pruned from state. This is because if pruned, the original pubkey of the account would be needed to recreate the same address, but the owner of the address may not have the original pubkey anymore. Currently, we do not automatically prune any accounts anyways, but we would like to keep this option open the road (this is the purpose of account numbers). To resolve this, we charge an additional gas fee for this operation to compensate for this this externality (this bound gas amount is configured as parameter `PubKeyChangeCost`). The bonus gas is charged inside the handler, using the `ConsumeGas` function. Furthermore, in the future, we can allow accounts that have rekeyed manually prune themselves using a new Msg type such as `MsgDeleteAccount`. Manually pruning accounts can give a gas refund as an incentive for performing the action. - -```go - amount := ak.GetParams(ctx).PubKeyChangeCost - ctx.GasMeter().ConsumeGas(amount, "pubkey change fee") -``` - -Everytime a key for an address is changed, we will store a log of this change in the state of the chain, thus creating a stack of all previous keys for an address and the time intervals for which they were active. This allows dapps and clients to easily query past keys for an account which may be useful for features such as verifying timestamped off-chain signed messages. - -## Consequences - -### Positive - -* Will allow users and validator operators to employ better operational security practices with key rotation. -* Will allow organizations or groups to easily change and add/remove multisig signers. - -### Negative - -Breaks the current assumed relationship between address and pubkeys as H(pubkey) = address. This has a couple of consequences. - -* This makes wallets that support this feature more complicated. For example, if an address on chain was updated, the corresponding key in the CLI wallet also needs to be updated. -* Cannot automatically prune accounts with 0 balance that have had their pubkey changed. - -### Neutral - -* While the purpose of this is intended to allow the owner of an account to update to a new pubkey they own, this could technically also be used to transfer ownership of an account to a new owner. For example, this could be use used to sell a staked position without unbonding or an account that has vesting tokens. However, the friction of this is very high as this would essentially have to be done as a very specific OTC trade. Furthermore, additional constraints could be added to prevent accouns with Vesting tokens to use this feature. -* Will require that PubKeys for an account are included in the genesis exports. - -## References - -+ https://www.algorand.com/resources/blog/announcing-rekeying diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-035-rosetta-api-support.md b/versioned_docs/version-0.45/integrate/architecture/adr-035-rosetta-api-support.md deleted file mode 100644 index fe85405a5..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-035-rosetta-api-support.md +++ /dev/null @@ -1,211 +0,0 @@ -# ADR 035: Rosetta API Support - -## Authors - -- Jonathan Gimeno (@jgimeno) -- David Grierson (@senormonito) -- Alessio Treglia (@alessio) -- Frojdy Dymylja (@fdymylja) - -## Changelog - -- 2021-05-12: the external library [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) has been moved within the SDK. - -## Context - -[Rosetta API](https://www.rosetta-api.org/) is an open-source specification and set of tools developed by Coinbase to -standardise blockchain interactions. - -Through the use of a standard API for integrating blockchain applications it will - -* Be easier for a user to interact with a given blockchain -* Allow exchanges to integrate new blockchains quickly and easily -* Enable application developers to build cross-blockchain applications such as block explorers, wallets and dApps at - considerably lower cost and effort. - -## Decision - -It is clear that adding Rosetta API support to the Cosmos SDK will bring value to all the developers and -Cosmos SDK based chains in the ecosystem. How it is implemented is key. - -The driving principles of the proposed design are: - -1. **Extensibility:** it must be as riskless and painless as possible for application developers to set-up network - configurations to expose Rosetta API-compliant services. -2. **Long term support:** This proposal aims to provide support for all the supported Cosmos SDK release series. -3. **Cost-efficiency:** Backporting changes to Rosetta API specifications from `master` to the various stable - branches of Cosmos SDK is a cost that needs to be reduced. - -We will achieve these delivering on these principles by the following: - -1. There will be a package `rosetta/lib` - for the implementation of the core Rosetta API features, particularly: - a. The types and interfaces (`Client`, `OfflineClient`...), this separates design from implementation detail. - b. The `Server` functionality as this is independent of the Cosmos SDK version. - c. The `Online/OfflineNetwork`, which is not exported, and implements the rosetta API using the `Client` interface to query the node, build tx and so on. - d. The `errors` package to extend rosetta errors. -2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface. -3. There will be two options for starting an API service in applications: - a. API shares the application process - b. API-specific process. - -## Architecture - -### The External Repo - -As section will describe the proposed external library, including the service implementation, plus the defined types and interfaces. - -#### Server - -`Server` is a simple `struct` that is started and listens to the port specified in the settings. This is meant to be used across all the Cosmos SDK versions that are actively supported. - -The constructor follows: - -`func NewServer(settings Settings) (Server, error)` - -`Settings`, which are used to construct a new server, are the following: - -```go -// Settings define the rosetta server settings -type Settings struct { - // Network contains the information regarding the network - Network *types.NetworkIdentifier - // Client is the online API handler - Client crgtypes.Client - // Listen is the address the handler will listen at - Listen string - // Offline defines if the rosetta service should be exposed in offline mode - Offline bool - // Retries is the number of readiness checks that will be attempted when instantiating the handler - // valid only for online API - Retries int - // RetryWait is the time that will be waited between retries - RetryWait time.Duration -} -``` - -#### Types - -Package types uses a mixture of rosetta types and custom defined type wrappers, that the client must parse and return while executing operations. - -##### Interfaces - -Every SDK version uses a different format to connect (rpc, gRPC, etc), query and build transactions, we have abstracted this in what is the `Client` interface. -The client uses rosetta types, whilst the `Online/OfflineNetwork` takes care of returning correctly parsed rosetta responses and errors. - -Each Cosmos SDK release series will have their own `Client` implementations. -Developers can implement their own custom `Client`s as required. - -```go -// Client defines the API the client implementation should provide. -type Client interface { - // Needed if the client needs to perform some action before connecting. - Bootstrap() error - // Ready checks if the servicer constraints for queries are satisfied - // for example the node might still not be ready, it's useful in process - // when the rosetta instance might come up before the node itself - // the servicer must return nil if the node is ready - Ready() error - - // Data API - - // Balances fetches the balance of the given address - // if height is not nil, then the balance will be displayed - // at the provided height, otherwise last block balance will be returned - Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error) - // BlockByHashAlt gets a block and its transaction at the provided height - BlockByHash(ctx context.Context, hash string) (BlockResponse, error) - // BlockByHeightAlt gets a block given its height, if height is nil then last block is returned - BlockByHeight(ctx context.Context, height *int64) (BlockResponse, error) - // BlockTransactionsByHash gets the block, parent block and transactions - // given the block hash. - BlockTransactionsByHash(ctx context.Context, hash string) (BlockTransactionsResponse, error) - // BlockTransactionsByHash gets the block, parent block and transactions - // given the block hash. - BlockTransactionsByHeight(ctx context.Context, height *int64) (BlockTransactionsResponse, error) - // GetTx gets a transaction given its hash - GetTx(ctx context.Context, hash string) (*types.Transaction, error) - // GetUnconfirmedTx gets an unconfirmed Tx given its hash - // NOTE(fdymylja): NOT IMPLEMENTED YET! - GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error) - // Mempool returns the list of the current non confirmed transactions - Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error) - // Peers gets the peers currently connected to the node - Peers(ctx context.Context) ([]*types.Peer, error) - // Status returns the node status, such as sync data, version etc - Status(ctx context.Context) (*types.SyncStatus, error) - - // Construction API - - // PostTx posts txBytes to the node and returns the transaction identifier plus metadata related - // to the transaction itself. - PostTx(txBytes []byte) (res *types.TransactionIdentifier, meta map[string]interface{}, err error) - // ConstructionMetadataFromOptions - ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error) - OfflineClient -} - -// OfflineClient defines the functionalities supported without having access to the node -type OfflineClient interface { - NetworkInformationProvider - // SignedTx returns the signed transaction given the tx bytes (msgs) plus the signatures - SignedTx(ctx context.Context, txBytes []byte, sigs []*types.Signature) (signedTxBytes []byte, err error) - // TxOperationsAndSignersAccountIdentifiers returns the operations related to a transaction and the account - // identifiers if the transaction is signed - TxOperationsAndSignersAccountIdentifiers(signed bool, hexBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) - // ConstructionPayload returns the construction payload given the request - ConstructionPayload(ctx context.Context, req *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) - // PreprocessOperationsToOptions returns the options given the preprocess operations - PreprocessOperationsToOptions(ctx context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error) - // AccountIdentifierFromPublicKey returns the account identifier given the public key - AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) -} -``` - -### 2. Cosmos SDK Implementation - -The cosmos sdk implementation, based on version, takes care of satisfying the `Client` interface. -In Stargate, Launchpad and 0.37, we have introduced the concept of rosetta.Msg, this message is not in the shared repository as the sdk.Msg type differs between cosmos-sdk versions. - -The rosetta.Msg interface follows: - -```go -// Msg represents a cosmos-sdk message that can be converted from and to a rosetta operation. -type Msg interface { - sdk.Msg - ToOperations(withStatus, hasError bool) []*types.Operation - FromOperations(ops []*types.Operation) (sdk.Msg, error) -} -``` - -Hence developers who want to extend the rosetta set of supported operations just need to extend their module's sdk.Msgs with the `ToOperations` and `FromOperations` methods. - -### 3. API service invocation - -As stated at the start, application developers will have two methods for invocation of the Rosetta API service: - -1. Shared process for both application and API -2. Standalone API service - -#### Shared Process (Only Stargate) - -Rosetta API service could run within the same execution process as the application. This would be enabled via app.toml settings, and if gRPC is not enabled the rosetta instance would be spinned in offline mode (tx building capabilities only). - -#### Separate API service - -Client application developers can write a new command to launch a Rosetta API server as a separate process too, using the rosetta command contained in the `/server/rosetta` package. Construction of the command depends on cosmos sdk version. Examples can be found inside `simd` for stargate, and `contrib/rosetta/simapp` for other release series. - -## Status - -Proposed - -## Consequences - -### Positive - -- Out-of-the-box Rosetta API support within Cosmos SDK. -- Blockchain interface standardisation - -## References - -- https://www.rosetta-api.org/ diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-036-arbitrary-signature.md b/versioned_docs/version-0.45/integrate/architecture/adr-036-arbitrary-signature.md deleted file mode 100644 index 0d0737bff..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-036-arbitrary-signature.md +++ /dev/null @@ -1,132 +0,0 @@ -# ADR 036: Arbitrary Message Signature Specification - -## Changelog - -- 28/10/2020 - Initial draft - -## Authors - -- Antoine Herzog (@antoineherzog) -- Zaki Manian (@zmanian) -- Aleksandr Bezobchuk (alexanderbez) [1] -- Frojdi Dymylja (@fdymylja) - -## Status - -Draft - -## Abstract - -Currently, in the SDK, there is no convention to sign arbitrary message like on Ethereum. We propose with this specification, for Cosmos SDK ecosystem, a way to sign and validate off-chain arbitrary messages. - -This specification serves the purpose of covering every use case, this means that cosmos-sdk applications developers decide how to serialize and represent `Data` to users. - -## Context - -Having the ability to sign messages off-chain has proven to be a fundamental aspect of nearly any blockchain. The notion of signing messages off-chain has many added benefits such as saving on computational costs and reducing transaction throughput and overhead. Within the context of the Cosmos, some of the major applications of signing such data includes, but is not limited to, providing a cryptographic secure and verifiable means of proving validator identity and possibly associating it with some other framework or organization. In addition, having the ability to sign Cosmos messages with a Ledger or similar HSM device. - -Further context and use cases can be found in the references links. - -## Decision - -The aim is being able to sign arbitrary messages, even using Ledger or similar HSM devices. - -As a result signed messages should look roughly like Cosmos SDK messages but **must not** be a valid on-chain transaction. `chain-id`, `account_number` and `sequence` can all be assigned invalid values. - -Cosmos SDK 0.40 also introduces a concept of “auth_info” this can specify SIGN_MODES. - -A spec should include an `auth_info` that supports SIGN_MODE_DIRECT and SIGN_MODE_LEGACY_AMINO. - -Create the `offchain` proto definitions, we extend the auth module with `offchain` package to offer functionalities to verify and sign offline messages. - -An offchain transaction follows these rules: - -- the memo must be empty -- nonce, sequence number must be equal to 0 -- chain-id must be equal to “” -- fee gas must be equal to 0 -- fee amount must be an empty array - -Verification of an offchain transaction follows the same rules as an onchain one, except for the spec differences highlighted above. - -The first message added to the `offchain` package is `MsgSignData`. - -`MsgSignData` allows developers to sign arbitrary bytes valid offchain only. Where `Signer` is the account address of the signer. `Data` is arbitrary bytes which can represent `text`, `files`, `object`s. It's applications developers decision how `Data` should be deserialized, serialized and the object it can represent in their context. - -It's applications developers decision how `Data` should be treated, by treated we mean the serialization and deserialization process and the Object `Data` should represent. - -Proto definition: - -```proto -// MsgSignData defines an arbitrary, general-purpose, off-chain message -message MsgSignData { - // Signer is the sdk.AccAddress of the message signer - bytes Signer = 1 [(gogoproto.jsontag) = "signer", (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - // Data represents the raw bytes of the content that is signed (text, json, etc) - bytes Data = 2 [(gogoproto.jsontag) = "data"]; -} -``` - -Signed MsgSignData json example: - -```json -{ - "type": "cosmos-sdk/StdTx", - "value": { - "msg": [ - { - "type": "sign/MsgSignData", - "value": { - "signer": "cosmos1hftz5ugqmpg9243xeegsqqav62f8hnywsjr4xr", - "data": "cmFuZG9t" - } - } - ], - "fee": { - "amount": [], - "gas": "0" - }, - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AqnDSiRoFmTPfq97xxEb2VkQ/Hm28cPsqsZm9jEVsYK9" - }, - "signature": "8y8i34qJakkjse9pOD2De+dnlc4KvFgh0wQpes4eydN66D9kv7cmCEouRrkka9tlW9cAkIL52ErB+6ye7X5aEg==" - } - ], - "memo": "" - } -} -``` - -## Consequences - -There is a specification on how messages, that are not meant to be broadcast to a live chain, should be formed. - -### Backwards Compatibility - -Backwards compatibility is maintained as this is a new message spec definition. - -### Positive - -- A common format that can be used by multiple applications to sign and verify off-chain messages. -- The specification is primitive which means it can cover every use case without limiting what is possible to fit inside it. -- It gives room for other off-chain messages specifications that aim to target more specific and common use cases such as off-chain-based authN/authZ layers [2]. - -### Negative - -- Current proposal requires a fixed relationship between an account address and a public key. -- Doesn't work with multisig accounts. - -## Further discussion - -- Regarding security in `MsgSignData`, the developer using `MsgSignData` is in charge of making the content laying in `Data` non-replayable when, and if, needed. -- the offchain package will be further extended with extra messages that target specific use cases such as, but not limited to, authentication in applications, payment channels, L2 solutions in general. - -## References - -1. https://github.com/cosmos/ics/pull/33 -2. https://github.com/cosmos/cosmos-sdk/pull/7727#discussion_r515668204 -3. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-722478477 -4. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-721062923 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-037-gov-split-vote.md b/versioned_docs/version-0.45/integrate/architecture/adr-037-gov-split-vote.md deleted file mode 100644 index 742fdd108..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-037-gov-split-vote.md +++ /dev/null @@ -1,111 +0,0 @@ -# ADR 037: Governance split votes - -## Changelog - -- 2020/10/28: Intial draft - -## Status - -Accepted - -## Abstract - -This ADR defines a modification to the the governance module that would allow a staker to split their votes into several voting options. For example, it could use 70% of its voting power to vote Yes and 30% of its voting power to vote No. - -## Context - -Currently, an address can cast a vote with only one options (Yes/No/Abstain/NoWithVeto) and use their full voting power behind that choice. - -However, often times the entity owning that address might not be a single individual. For example, a company might have different stakeholders who want to vote differently, and so it makes sense to allow them to split their voting power. Another example use case is exchanges. Many centralized exchanges often stake a portion of their users' tokens in their custody. Currently, it is not possible for them to do "passthrough voting" and giving their users voting rights over their tokens. However, with this system, exchanges can poll their users for voting preferences, and then vote on-chain proportionally to the results of the poll. - -## Decision - -We modify the vote structs to be - -``` -type WeightedVoteOption struct { - Option string - Weight sdk.Dec -} - -type Vote struct { - ProposalID int64 - Voter sdk.Address - Options []WeightedVoteOption -} -``` - -And for backwards compatibility, we introduce `MsgVoteWeighted` while keeping `MsgVote`. - -``` -type MsgVote struct { - ProposalID int64 - Voter sdk.Address - Option Option -} - -type MsgVoteWeighted struct { - ProposalID int64 - Voter sdk.Address - Options []WeightedVoteOption -} -``` - -The `ValidateBasic` of a `MsgVoteWeighted` struct would require that - -1. The sum of all the Rates is equal to 1.0 -2. No Option is repeated - -The governance tally function will iterate over all the options in a vote and add to the tally the result of the voter's voting power * the rate for that option. - -``` -tally() { - results := map[types.VoteOption]sdk.Dec - - for _, vote := range votes { - for i, weightedOption := range vote.Options { - results[weightedOption.Option] += getVotingPower(vote.voter) * weightedOption.Weight - } - } -} -``` - -The CLI command for creating a multi-option vote would be as such: - -```sh -simd tx gov vote 1 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05" --from mykey -``` - -To create a single-option vote a user can do either - -``` -simd tx gov vote 1 "yes=1" --from mykey -``` - -or - -```sh -simd tx gov vote 1 yes --from mykey -``` - -to maintain backwards compatibility. - -## Consequences - -### Backwards Compatibility - -- Previous VoteMsg types will remain the same and so clients will not have to update their procedure unless they want to support the WeightedVoteMsg feature. -- When querying a Vote struct from state, its structure will be different, and so clients wanting to display all voters and their respective votes will have to handle the new format and the fact that a single voter can have split votes. -- The result of querying the tally function should have the same API for clients. - -### Positive - -- Can make the voting process more accurate for addresses representing multiple stakeholders, often some of the largest addresses. - -### Negative - -- Is more complex than simple voting, and so may be harder to explain to users. However, this is mostly mitigated because the feature is opt-in. - -### Neutral - -- Relatively minor change to governance tally function. diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-038-state-listening.md b/versioned_docs/version-0.45/integrate/architecture/adr-038-state-listening.md deleted file mode 100644 index 12d0bdb8b..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-038-state-listening.md +++ /dev/null @@ -1,569 +0,0 @@ -# ADR 038: KVStore state listening - -## Changelog - -* 11/23/2020: Initial draft -* 10/14/2022: - * Add `ListenCommit`, flatten the state writes in a block to a single batch. - * Remove listeners from cache stores, should only listen to `rootmulti.Store`. - * Remove `HaltAppOnDeliveryError()`, the errors are propogated by default, the implementations should return nil if don't want to propogate errors. - - -## Status - -Proposed - -## Abstract - -This ADR defines a set of changes to enable listening to state changes of individual KVStores and exposing these data to consumers. - -## Context - -Currently, KVStore data can be remotely accessed through [Queries](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/02-messages-and-queries.md#queries) -which proceed either through Tendermint and the ABCI, or through the gRPC server. -In addition to these request/response queries, it would be beneficial to have a means of listening to state changes as they occur in real time. - -## Decision - -We will modify the `CommitMultiStore` interface and its concrete (`rootmulti`) implementations and introduce a new `listenkv.Store` to allow listening to state changes in underlying KVStores. We don't need to listen to cache stores, because we can't be sure that the writes will be committed eventually, and the writes are duplicated in `rootmulti.Store` eventually, so we should only listen to `rootmulti.Store`. -We will introduce a plugin system for configuring and running streaming services that write these state changes and their surrounding ABCI message context to different destinations. - -### Listening interface - -In a new file, `store/types/listening.go`, we will create a `WriteListener` interface for streaming out state changes from a KVStore. - -```go -// WriteListener interface for streaming data out from a listenkv.Store -type WriteListener interface { - // if value is nil then it was deleted - // storeKey indicates the source KVStore, to facilitate using the same WriteListener across separate KVStores - // delete bool indicates if it was a delete; true: delete, false: set - OnWrite(storeKey StoreKey, key []byte, value []byte, delete bool) error -} -``` - -### Listener type - -We will create two concrete implementations of the `WriteListener` interface in `store/types/listening.go`, that writes out protobuf -encoded KV pairs to an underlying `io.Writer`, and simply accumulate them in memory. - -This will include defining a simple protobuf type for the KV pairs. In addition to the key and value fields this message -will include the StoreKey for the originating KVStore so that we can write out from separate KVStores to the same stream/file -and determine the source of each KV pair. - -```protobuf -message StoreKVPair { - optional string store_key = 1; // the store key for the KVStore this pair originates from - required bool set = 2; // true indicates a set operation, false indicates a delete operation - required bytes key = 3; - required bytes value = 4; -} -``` - -```go -// StoreKVPairWriteListener is used to configure listening to a KVStore by writing out length-prefixed -// protobuf encoded StoreKVPairs to an underlying io.Writer -type StoreKVPairWriteListener struct { - writer io.Writer - marshaller codec.BinaryCodec -} - -// NewStoreKVPairWriteListener wraps creates a StoreKVPairWriteListener with a provdied io.Writer and codec.BinaryCodec -func NewStoreKVPairWriteListener(w io.Writer, m codec.BinaryCodec) *StoreKVPairWriteListener { - return &StoreKVPairWriteListener{ - writer: w, - marshaller: m, - } -} - -// OnWrite satisfies the WriteListener interface by writing length-prefixed protobuf encoded StoreKVPairs -func (wl *StoreKVPairWriteListener) OnWrite(storeKey types.StoreKey, key []byte, value []byte, delete bool) error error { - kvPair := new(types.StoreKVPair) - kvPair.StoreKey = storeKey.Name() - kvPair.Delete = Delete - kvPair.Key = key - kvPair.Value = value - by, err := wl.marshaller.MarshalBinaryLengthPrefixed(kvPair) - if err != nil { - return err - } - if _, err := wl.writer.Write(by); err != nil { - return err - } - return nil -} -``` - -```golang -// MemoryListener listens to the state writes and accumulate the records in memory. -type MemoryListener struct { - key StoreKey - stateCache []StoreKVPair -} - -// NewMemoryListener creates a listener that accumulate the state writes in memory. -func NewMemoryListener(key StoreKey) *MemoryListener { - return &MemoryListener{key: key} -} - -// OnWrite implements WriteListener interface -func (fl *MemoryListener) OnWrite(storeKey StoreKey, key []byte, value []byte, delete bool) error { - fl.stateCache = append(fl.stateCache, StoreKVPair{ - StoreKey: storeKey.Name(), - Delete: delete, - Key: key, - Value: value, - }) - return nil -} - -// PopStateCache returns the current state caches and set to nil -func (fl *MemoryListener) PopStateCache() []StoreKVPair { - res := fl.stateCache - fl.stateCache = nil - return res -} - -// StoreKey returns the storeKey it listens to -func (fl *MemoryListener) StoreKey() StoreKey { - return fl.key -} -``` - -### ListenKVStore - -We will create a new `Store` type `listenkv.Store` that the `MultiStore` wraps around a `KVStore` to enable state listening. -We can configure the `Store` with a set of `WriteListener`s which stream the output to specific destinations. - -```go -// Store implements the KVStore interface with listening enabled. -// Operations are traced on each core KVStore call and written to any of the -// underlying listeners with the proper key and operation permissions -type Store struct { - parent types.KVStore - listeners []types.WriteListener - parentStoreKey types.StoreKey -} - -// NewStore returns a reference to a new traceKVStore given a parent -// KVStore implementation and a buffered writer. -func NewStore(parent types.KVStore, psk types.StoreKey, listeners []types.WriteListener) *Store { - return &Store{parent: parent, listeners: listeners, parentStoreKey: psk} -} - -// Set implements the KVStore interface. It traces a write operation and -// delegates the Set call to the parent KVStore. -func (s *Store) Set(key []byte, value []byte) { - types.AssertValidKey(key) - s.parent.Set(key, value) - s.onWrite(false, key, value) -} - -// Delete implements the KVStore interface. It traces a write operation and -// delegates the Delete call to the parent KVStore. -func (s *Store) Delete(key []byte) { - s.parent.Delete(key) - s.onWrite(true, key, nil) -} - -// onWrite writes a KVStore operation to all the WriteListeners -func (s *Store) onWrite(delete bool, key, value []byte) { - for _, l := range s.listeners { - if err := l.OnWrite(s.parentStoreKey, key, value, delete); err != nil { - // log error - } - } -} -``` - -### MultiStore interface updates - -We will update the `CommitMultiStore` interface to allow us to wrap a set of listeners around a specific `KVStore`. - -```go -type CommitMultiStore interface { - ... - - // ListeningEnabled returns if listening is enabled for the KVStore belonging the provided StoreKey - ListeningEnabled(key StoreKey) bool - - // AddListeners adds WriteListeners for the KVStore belonging to the provided StoreKey - // It appends the listeners to a current set, if one already exists - AddListeners(key StoreKey, listeners []WriteListener) -} -``` - -### MultiStore implementation updates - -We will modify all of the `CommitMultiStore` implementations to satisfy these new interfaces, and adjust the `rootmulti` `GetKVStore` method -to wrap the returned `KVStore` with a `listenkv.Store` if listening is turned on for that `Store`. - -```go -func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { - store := rs.stores[key].(types.KVStore) - - if rs.TracingEnabled() { - store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) - } - if rs.ListeningEnabled(key) { - store = listenkv.NewStore(key, store, rs.listeners[key]) - } - - return store -} -``` - -We will also adjust the `rootmulti` `CacheMultiStore` method to wrap the stores with `listenkv.Store` to enable listening when the cache layer writes. - -```go -func (rs *Store) CacheMultiStore() types.CacheMultiStore { - stores := make(map[types.StoreKey]types.CacheWrapper) - for k, v := range rs.stores { - store := v.(types.KVStore) - // Wire the listenkv.Store to allow listeners to observe the writes from the cache store, - // set same listeners on cache store will observe duplicated writes. - if rs.ListeningEnabled(k) { - store = listenkv.NewStore(store, k, rs.listeners[k]) - } - stores[k] = store - } - return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.getTracingContext()) -} -``` - -### Exposing the data - -#### Streaming service - -We will introduce a new `StreamingService` interface for exposing `WriteListener` data streams to external consumers. -In addition to streaming state changes as `StoreKVPair`s, the interface satisfies an `ABCIListener` interface that plugs -into the BaseApp and relays ABCI requests and responses so that the service can observe those block metadatas as well. - -The `WriteListener`s of `StreamingService` listens to the `rootmulti.Store`, which is only written into at commit event by the cache store of `deliverState`. - -```go -// ABCIListener interface used to hook into the ABCI message processing of the BaseApp -type ABCIListener interface { - // ListenBeginBlock updates the streaming service with the latest BeginBlock messages - ListenBeginBlock(ctx types.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) error - // ListenEndBlock updates the steaming service with the latest EndBlock messages - ListenEndBlock(ctx types.Context, req abci.RequestEndBlock, res abci.ResponseEndBlock) error - // ListenDeliverTx updates the steaming service with the latest DeliverTx messages - ListenDeliverTx(ctx types.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) error - // ListenCommit updates the steaming service with the latest Commit message, - // All the state writes of current block should have notified before this message. - ListenCommit(ctx types.Context, res abci.ResponseCommit) error -} - -// StreamingService interface for registering WriteListeners with the BaseApp and updating the service with the ABCI messages using the hooks -type StreamingService interface { - // Stream is the streaming service loop, awaits kv pairs and writes them to a destination stream or file - Stream(wg *sync.WaitGroup) error - // Listeners returns the streaming service's listeners for the BaseApp to register - Listeners() map[types.StoreKey][]store.WriteListener - // ABCIListener interface for hooking into the ABCI messages from inside the BaseApp - ABCIListener - // Closer interface - io.Closer -} -``` - -#### BaseApp registration - -We will add a new method to the `BaseApp` to enable the registration of `StreamingService`s: - -```go -// SetStreamingService is used to set a streaming service into the BaseApp hooks and load the listeners into the multistore -func (app *BaseApp) SetStreamingService(s StreamingService) { - // add the listeners for each StoreKey - for key, lis := range s.Listeners() { - app.cms.AddListeners(key, lis) - } - // register the StreamingService within the BaseApp - // BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context - app.abciListeners = append(app.abciListeners, s) -} -``` - -We will also modify the `BeginBlock`, `EndBlock`, and `DeliverTx` methods to pass ABCI requests and responses to any streaming service hooks registered -with the `BaseApp`. - -```go -func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { - - ... - - defer func() { - // call the hooks with the BeginBlock messages - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenBeginBlock(app.deliverState.ctx, req, res); err != nil { - panic(sdkerrors.Wrapf(err, "BeginBlock listening hook failed, height: %d", req.Header.Height)) - } - } - }() - - return res -} -``` - -```go -func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { - - ... - - defer func() { - // Call the streaming service hooks with the EndBlock messages - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenEndBlock(app.deliverState.ctx, req, res); err != nil { - panic(sdkerrors.Wrapf(err, "EndBlock listening hook failed, height: %d", req.Height)) - } - } - }() - - return res -} -``` - -```go -func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) { - - defer func() { - // call the hooks with the DeliverTx messages - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenDeliverTx(app.deliverState.ctx, req, res); err != nil { - panic(sdkerrors.Wrap(err, "DeliverTx listening hook failed")) - } - } - }() - - ... - - return res -} -``` - -```golang -func (app *BaseApp) Commit() abci.ResponseCommit { - header := app.deliverState.ctx.BlockHeader() - retainHeight := app.GetBlockRetentionHeight(header.Height) - - // Write the DeliverTx state into branched storage and commit the MultiStore. - // The write to the DeliverTx state writes all state transitions to the root - // MultiStore (app.cms) so when Commit() is called is persists those values. - app.deliverState.ms.Write() - commitID := app.cms.Commit() - - res := abci.ResponseCommit{ - Data: commitID.Hash, - RetainHeight: retainHeight, - } - - // call the hooks with the Commit message - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenCommit(app.deliverState.ctx, res); err != nil { - panic(sdkerrors.Wrapf(err, "Commit listening hook failed, height: %d", header.Height)) - } - } - - app.logger.Info("commit synced", "commit", fmt.Sprintf("%X", commitID)) - ... -} -``` - -#### Error Handling And Async Consumers - -`ABCIListener`s are called synchronously inside the consensus state machine, the returned error causes panic which in turn halt the consensus state machine. The implementer should be careful not to break consensus unexpectedly or slow down it too much. - -For some async use cases, one can spawn a go-routine internanlly to avoid slow down consensus state machine, and handle the errors internally and always returns `nil` to avoid halting consensus state machine on error. - -Furthermore, for most of the cases, we only need to use the builtin file streamer to listen to state changes directly inside cosmos-sdk, the other consumers should subscribe to the file streamer output externally. - -#### File Streamer - -We provide a minimal filesystem based implementation inside cosmos-sdk, and provides options to write output files reliably, the output files can be further consumed by external consumers, so most of the state listeners actually don't need to live inside the sdk and node, which improves the node robustness and simplify sdk internals. - -The file streamer can be wired in app like this: -```golang -exposeStoreKeys := ... // decide the key list to listen -service, err := file.NewStreamingService(streamingDir, "", exposeStoreKeys, appCodec, logger) -bApp.SetStreamingService(service) -``` - -#### Plugin system - -We propose a plugin architecture to load and run `StreamingService` implementations. We will introduce a plugin -loading/preloading system that is used to load, initialize, inject, run, and stop Cosmos-SDK plugins. Each plugin -must implement the following interface: - -```go -// Plugin is the base interface for all kinds of cosmos-sdk plugins -// It will be included in interfaces of different Plugins -type Plugin interface { - // Name should return unique name of the plugin - Name() string - - // Version returns current version of the plugin - Version() string - - // Init is called once when the Plugin is being loaded - // The plugin is passed the AppOptions for configuration - // A plugin will not necessarily have a functional Init - Init(env serverTypes.AppOptions) error - - // Closer interface for shutting down the plugin process - io.Closer -} -``` - -The `Name` method returns a plugin's name. -The `Version` method returns a plugin's version. -The `Init` method initializes a plugin with the provided `AppOptions`. -The io.Closer is used to shut down the plugin service. - -For the purposes of this ADR we introduce a single kind of plugin- a state streaming plugin. -We will define a `StateStreamingPlugin` interface which extends the above `Plugin` interface to support a state streaming service. - -```go -// StateStreamingPlugin interface for plugins that load a baseapp.StreamingService onto a baseapp.BaseApp -type StateStreamingPlugin interface { - // Register configures and registers the plugin streaming service with the BaseApp - Register(bApp *baseapp.BaseApp, marshaller codec.BinaryCodec, keys map[string]*types.KVStoreKey) error - - // Start starts the background streaming process of the plugin streaming service - Start(wg *sync.WaitGroup) error - - // Plugin is the base Plugin interface - Plugin -} -``` - -The `Register` method is used during App construction to register the plugin's streaming service with an App's BaseApp using the BaseApp's `SetStreamingService` method. -The `Start` method is used during App construction to start the registered plugin streaming services and maintain synchronization with them. - -e.g. in `NewSimApp`: - -```go -func NewSimApp( - logger log.Logger, - db dbm.DB, - traceStore io.Writer, - loadLatest bool, - appOpts servertypes.AppOptions, - baseAppOptions ...func(*baseapp.BaseApp), -) *SimApp { - - ... - - keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, - minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, - evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, - ) - - pluginsOnKey := fmt.Sprintf("%s.%s", plugin.PLUGINS_TOML_KEY, plugin.PLUGINS_ON_TOML_KEY) - if cast.ToBool(appOpts.Get(pluginsOnKey)) { - // this loads the preloaded and any plugins found in `plugins.dir` - pluginLoader, err := loader.NewPluginLoader(appOpts, logger) - if err != nil { - // handle error - } - - // initialize the loaded plugins - if err := pluginLoader.Initialize(); err != nil { - // handle error - } - - // register the plugin(s) with the BaseApp - if err := pluginLoader.Inject(bApp, appCodec, keys); err != nil { - // handle error - } - - // start the plugin services, optionally use wg to synchronize shutdown using io.Closer - wg := new(sync.WaitGroup) - if err := pluginLoader.Start(wg); err != nil { - // handler error - } - } - - ... - - return app -} -``` - - -#### Configuration - -The plugin system will be configured within an app's app.toml file. - -```toml -[plugins] - on = false # turn the plugin system, as a whole, on or off - enabled = ["list", "of", "plugin", "names", "to", "enable"] - dir = "the directory to load non-preloaded plugins from; defaults to cosmos-sdk/plugin/plugins" -``` - -There will be three parameters for configuring the plugin system: `plugins.on`, `plugins.enabled` and `plugins.dir`. -`plugins.on` is a bool that turns on or off the plugin system at large, `plugins.dir` directs the system to a directory -to load plugins from, and `plugins.enabled` provides `opt-in` semantics to plugin names to enable (including preloaded plugins). - -Configuration of a given plugin is ultimately specific to the plugin, but we will introduce some standards here: - -Plugin TOML configuration should be split into separate sub-tables for each kind of plugin (e.g. `plugins.streaming`). - -Within these sub-tables, the parameters for a specific plugin of that kind are included in another sub-table (e.g. `plugins.streaming.file`). -It is generally expected, but not required, that a streaming service plugin can be configured with a set of store keys -(e.g. `plugins.streaming.file.keys`) for the stores it listens to and a flag (e.g. `plugins.streaming.file.halt_app_on_delivery_error`) -that signifies whether the service operates in a fire-and-forget capacity, or stop the BaseApp when an error occurs in -any of `ListenBeginBlock`, `ListenEndBlock` and `ListenDeliverTx`. - -e.g. - -```toml -[plugins] - on = false # turn the plugin system, as a whole, on or off - enabled = ["list", "of", "plugin", "names", "to", "enable"] - dir = "the directory to load non-preloaded plugins from; defaults to " - [plugins.streaming] # a mapping of plugin-specific streaming service parameters, mapped to their plugin name - [plugins.streaming.file] # the specific parameters for the file streaming service plugin - keys = ["list", "of", "store", "keys", "we", "want", "to", "expose", "for", "this", "streaming", "service"] - write_dir = "path to the write directory" - prefix = "optional prefix to prepend to the generated file names" - halt_app_on_delivery_error = "false" # false == fire-and-forget; true == stop the application - [plugins.streaming.kafka] - keys = [] - topic_prefix = "block" # Optional prefix for topic names where data will be stored. - flush_timeout_ms = 5000 # Flush and wait for outstanding messages and requests to complete delivery when calling `StreamingService.Close(). (milliseconds) - halt_app_on_delivery_error = true # Whether or not to halt the application when plugin fails to deliver message(s). - ... -``` - -#### Encoding and decoding streams - -ADR-038 introduces the interfaces and types for streaming state changes out from KVStores, associating this -data with their related ABCI requests and responses, and registering a service for consuming this data and streaming it to some destination in a final format. -Instead of prescribing a final data format in this ADR, it is left to a specific plugin implementation to define and document this format. -We take this approach because flexibility in the final format is necessary to support a wide range of streaming service plugins. For example, -the data format for a streaming service that writes the data out to a set of files will differ from the data format that is written to a Kafka topic. - -## Consequences - -These changes will provide a means of subscribing to KVStore state changes in real time. - -### Backwards Compatibility - -* This ADR changes the `CommitMultiStore` interface, implementations supporting the previous version of these interfaces will not support the new ones - -### Positive - -* Ability to listen to KVStore state changes in real time and expose these events to external consumers - -### Negative - -* Changes `CommitMultiStore`interface - -### Neutral - -* Introduces additional- but optional- complexity to configuring and running a cosmos application -* If an application developer opts to use these features to expose data, they need to be aware of the ramifications/risks of that data exposure as it pertains to the specifics of their application diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-039-epoched-staking.md b/versioned_docs/version-0.45/integrate/architecture/adr-039-epoched-staking.md deleted file mode 100644 index 44c1f1408..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-039-epoched-staking.md +++ /dev/null @@ -1,122 +0,0 @@ -# ADR 039: Epoched Staking - -## Changelog - -- 10-Feb-2021: Initial Draft - -## Authors - -- Dev Ojha (@valardragon) -- Sunny Aggarwal (@sunnya97) - -## Status - -Proposed - -## Abstract - -This ADR updates the proof of stake module to buffer the staking weight updates for a number of blocks before updating the consensus' staking weights. The length of the buffer is dubbed an epoch. The prior functionality of the staking module is then a special case of the abstracted module, with the epoch being set to 1 block. - -## Context - -The current proof of stake module takes the design decision to apply staking weight changes to the consensus engine immediately. This means that delegations and unbonds get applied immediately to the validator set. This decision was primarily done as it was implementationally simplest, and because we at the time believed that this would lead to better UX for clients. - -An alternative design choice is to allow buffering staking updates (delegations, unbonds, validators joining) for a number of blocks. This 'epoch'd proof of stake consensus provides the guarantee that the consensus weights for validators will not change mid-epoch, except in the event of a slash condition. - -Additionally, the UX hurdle may not be as significant as was previously thought. This is because it is possible to provide users immediate acknowledgement that their bond was recorded and will be executed. - -Furthermore, it has become clearer over time that immediate execution of staking events comes with limitations, such as: - -* Threshold based cryptography. One of the main limitations is that because the validator set can change so regularly, it makes the running of multiparty computation by a fixed validator set difficult. Many threshold-based cryptographic features for blockchains such as randomness beacons and threshold decryption require a computationally-expensive DKG process (will take much longer than 1 block to create). To productively use these, we need to guarantee that the result of the DKG will be used for a reasonably long time. It wouldn't be feasible to rerun the DKG every block. By epoching staking, it guarantees we'll only need to run a new DKG once every epoch. - -* Light client efficiency. This would lessen the overhead for IBC when there is high churn in the validator set. In the Tendermint light client bisection algorithm, the number of headers you need to verify is related to bounding the difference in validator sets between a trusted header and the latest header. If the difference is too great, you verify more header in between the two. By limiting the frequency of validator set changes, we can reduce the worst case size of IBC lite client proofs, which occurs when a validator set has high churn. - -* Fairness of deterministic leader election. Currently we have no ways of reasoning of fairness of deterministic leader election in the presence of staking changes without epochs (tendermint/spec#217). Breaking fairness of leader election is profitable for validators, as they earn additional rewards from being the proposer. Adding epochs at least makes it easier for our deterministic leader election to match something we can prove secure. (Albeit, we still haven’t proven if our current algorithm is fair with > 2 validators in the presence of stake changes) - -* Staking derivative design. Currently, reward distribution is done lazily using the F1 fee distribution. While saving computational complexity, lazy accounting requires a more stateful staking implementation. Right now, each delegation entry has to track the time of last withdrawal. Handling this can be a challenge for some staking derivatives designs that seek to provide fungibility for all tokens staked to a single validator. Force-withdrawing rewards to users can help solve this, however it is infeasible to force-withdraw rewards to users on a per block basis. With epochs, a chain could more easily alter the design to have rewards be forcefully withdrawn (iterating over delegator accounts only once per-epoch), and can thus remove delegation timing from state. This may be useful for certain staking derivative designs. - -## Design considerations - -### Slashing - -There is a design consideration for whether to apply a slash immediately or at the end of an epoch. A slash event should apply to only members who are actually staked during the time of the infraction, namely during the epoch the slash event occured. - -Applying it immediately can be viewed as offering greater consensus layer security, at potential costs to the aforementioned usecases. The benefits of immediate slashing for consensus layer security can be all be obtained by executing the validator jailing immediately (thus removing it from the validator set), and delaying the actual slash change to the validator's weight until the epoch boundary. For the use cases mentioned above, workarounds can be integrated to avoid problems, as follows: - -- For threshold based cryptography, this setting will have the threshold cryptography use the original epoch weights, while consensus has an update that lets it more rapidly benefit from additional security. If the threshold based cryptography blocks liveness of the chain, then we have effectively raised the liveness threshold of the remaining validators for the rest of the epoch. (Alternatively, jailed nodes could still contribute shares) This plan will fail in the extreme case that more than 1/3rd of the validators have been jailed within a single epoch. For such an extreme scenario, the chain already have its own custom incident response plan, and defining how to handle the threshold cryptography should be a part of that. -- For light client efficiency, there can be a bit included in the header indicating an intra-epoch slash (ala https://github.com/tendermint/spec/issues/199). -- For fairness of deterministic leader election, applying a slash or jailing within an epoch would break the guarantee we were seeking to provide. This then re-introduces a new (but significantly simpler) problem for trying to provide fairness guarantees. Namely, that validators can adversarially elect to remove themself from the set of proposers. From a security perspective, this could potentially be handled by two different mechanisms (or prove to still be too difficult to achieve). One is making a security statement acknowledging the ability for an adversary to force an ahead-of-time fixed threshold of users to drop out of the proposer set within an epoch. The second method would be to parameterize such that the cost of a slash within the epoch far outweights benefits due to being a proposer. However, this latter criterion is quite dubious, since being a proposer can have many advantageous side-effects in chains with complex state machines. (Namely, DeFi games such as Fomo3D) -- For staking derivative design, there is no issue introduced. This does not increase the state size of staking records, since whether a slash has occured is fully queryable given the validator address. - -### Token lockup - -When someone makes a transaction to delegate, even though they are not immediately staked, their tokens should be moved into a pool managed by the staking module which will then be used at the end of an epoch. This prevents concerns where they stake, and then spend those tokens not realizing they were already allocated for staking, and thus having their staking tx fail. - -### Pipelining the epochs - -For threshold based cryptography in particular, we need a pipeline for epoch changes. This is because when we are in epoch N, we want the epoch N+1 weights to be fixed so that the validator set can do the DKG accordingly. So if we are currently in epoch N, the stake weights for epoch N+1 should already be fixed, and new stake changes should be getting applied to epoch N + 2. - -This can be handled by making a parameter for the epoch pipeline length. This parameter should not be alterable except during hard forks, to mitigate implementation complexity of switching the pipeline length. - -With pipeline length 1, if I redelegate during epoch N, then my redelegation is applied prior to the beginning of epoch N+1. -With pipeline length 2, if I redelegate during epoch N, then my redelegation is applied prior to the beginning of epoch N+2. - -### Rewards - -Even though all staking updates are applied at epoch boundaries, rewards can still be distributed immediately when they are claimed. This is because they do not affect the current stake weights, as we do not implement auto-bonding of rewards. If such a feature were to be implemented, it would have to be setup so that rewards are auto-bonded at the epoch boundary. - -### Parameterizing the epoch length - -When choosing the epoch length, there is a trade-off queued state/computation buildup, and countering the previously discussed limitations of immediate execution if they apply to a given chain. - -Until an ABCI mechanism for variable block times is introduced, it is ill-advised to be using high epoch lengths due to the computation buildup. This is because when a block's execution time is greater than the expected block time from Tendermint, rounds may increment. - -## Decision - -__Step-1__: Implement buffering of all staking and slashing messages. - -First we create a pool for storing tokens that are being bonded, but should be applied at the epoch boundary called the `EpochDelegationPool`. Then, we have two separate queues, one for staking, one for slashing. We describe what happens on each message being delivered below: - -### Staking messages - -- **MsgCreateValidator**: Move user's self-bond to `EpochDelegationPool` immediately. Queue a message for the epoch boundary to handle the self-bond, taking the funds from the `EpochDelegationPool`. If Epoch execution fail, return back funds from `EpochDelegationPool` to user's account. -- **MsgEditValidator**: Validate message and if valid queue the message for execution at the end of the Epoch. -- **MsgDelegate**: Move user's funds to `EpochDelegationPool` immediately. Queue a message for the epoch boundary to handle the delegation, taking the funds from the `EpochDelegationPool`. If Epoch execution fail, return back funds from `EpochDelegationPool` to user's account. -- **MsgBeginRedelegate**: Validate message and if valid queue the message for execution at the end of the Epoch. -- **MsgUndelegate**: Validate message and if valid queue the message for execution at the end of the Epoch. - -### Slashing messages - -- **MsgUnjail**: Validate message and if valid queue the message for execution at the end of the Epoch. -- **Slash Event**: Whenever a slash event is created, it gets queued in the slashing module to apply at the end of the epoch. The queues should be setup such that this slash applies immediately. - -### Evidence Messages - -- **MsgSubmitEvidence**: This gets executed immediately, and the validator gets jailed immediately. However in slashing, the actual slash event gets queued. - -Then we add methods to the end blockers, to ensure that at the epoch boundary the queues are cleared and delegation updates are applied. - -__Step-2__: Implement querying of queued staking txs. - -When querying the staking activity of a given address, the status should return not only the amount of tokens staked, but also if there are any queued stake events for that address. This will require more work to be done in the querying logic, to trace the queued upcoming staking events. - -As an initial implementation, this can be implemented as a linear search over all queued staking events. However, for chains that need long epochs, they should eventually build additional support for nodes that support querying to be able to produce results in constant time. (This is do-able by maintaining an auxilliary hashmap for indexing upcoming staking events by address) - -__Step-3__: Adjust gas - -Currently gas represents the cost of executing a transaction when its done immediately. (Merging together costs of p2p overhead, state access overhead, and computational overhead) However, now a transaction can cause computation in a future block, namely at the epoch boundary. - -To handle this, we should initially include parameters for estimating the amount of future computation (denominated in gas), and add that as a flat charge needed for the message. -We leave it as out of scope for how to weight future computation versus current computation in gas pricing, and have it set such that the are weighted equally for now. - -## Consequences - -### Positive - -* Abstracts the proof of stake module that allows retaining the existing functionality -* Enables new features such as validator-set based threshold cryptography - -### Negative - -* Increases complexity of integrating more complex gas pricing mechanisms, as they now have to consider future execution costs as well. -* When epoch > 1, validators can no longer leave the network immediately, and must wait until an epoch boundary. diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-040-storage-and-smt-state-commitments.md b/versioned_docs/version-0.45/integrate/architecture/adr-040-storage-and-smt-state-commitments.md deleted file mode 100644 index 4189922cc..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-040-storage-and-smt-state-commitments.md +++ /dev/null @@ -1,164 +0,0 @@ -# ADR 040: Storage and SMT State Commitments - -## Changelog - -- 2020-01-15: Draft - -## Status - -DRAFT Not Implemented - -## Abstract - -Sparse Merke Tree ([SMT](https://osf.io/8mcnh/)) is a version of a Merkle Tree with various storage and performance optimizations. This ADR defines a separation of state commitments from data storage and the SDK transition from IAVL to SMT. - -## Context - -Currently, Cosmos SDK uses IAVL for both state [commitments](https://cryptography.fandom.com/wiki/Commitment_scheme) and data storage. - -IAVL has effectively become an orphaned project within the Cosmos ecosystem and it's proven to be an inefficient state commitment data structure. -In the current design, IAVL is used for both data storage and as a Merkle Tree for state commitments. IAVL is meant to be a standalone Merkelized key/value database, however it's using a KV DB engine to store all tree nodes. So, each node is stored in a separate record in the KV DB. This causes many inefficiencies and problems: - -+ Each object query requires a tree traversal from the root. Subsequent queries for the same object are cached on the SDK level. -+ Each edge traversal requires a DB query. -+ Creating snapshots is [expensive](https://github.com/cosmos/cosmos-sdk/issues/7215#issuecomment-684804950). It takes about 30 seconds to export less than 100 MB of state (as of March 2020). -+ Updates in IAVL may trigger tree reorganization and possible O(log(n)) hashes re-computation, which can become a CPU bottleneck. -+ The node structure is pretty expensive - it contains a standard tree node elements (key, value, left and right element) and additional metadata such as height, version (which is not required by the SDK). The entire node is hashed, and that hash is used as the key in the underlying database, [ref](https://github.com/cosmos/iavl/blob/master/docs/node/03-node.md -). - -Moreover, the IAVL project lacks support and a maintainer and we already see better and well-established alternatives. Instead of optimizing the IAVL, we are looking into other solutions for both storage and state commitments. - -## Decision - -We propose to separate the concerns of state commitment (**SC**), needed for consensus, and state storage (**SS**), needed for state machine. Finally we replace IAVL with [LazyLedgers' SMT](https://github.com/lazyledger/smt). LazyLedger SMT is based on Diem (called jellyfish) design [*] - it uses a compute-optimised SMT by replacing subtrees with only default values with a single node (same approach is used by Ethereum2) and implements compact proofs. - -The storage model presented here doesn't deal with data structure nor serialization. It's a Key-Value database, where both key and value are binaries. The storage user is responsible for data serialization. - -### Decouple state commitment from storage - -Separation of storage and commitment (by the SMT) will allow the optimization of different components according to their usage and access patterns. - -`SS` (SMT) is used to commit to a data and compute merkle proofs. `SC` is used to directly access data. To avoid collisions, both `SS` and `SC` will use a separate storage namespace (they could use the same database underneath). `SC` will store each `(key, value)` pair directly (map key -> value). - -SMT is a merkle tree structure: we don't store keys directly. For every `(key, value)` pair, `hash(key)` is stored in a path (we hash a key to evenly distribute keys in the tree) and `hash(key, value)` in a leaf. Since we don't know a structure of a value (in particular if it contains the key) we hash both the key and the value in the `SC` leaf. - -For data access we propose 2 additional KV buckets (namespaces for the key-value pairs, sometimes called [column family](https://github.com/facebook/rocksdb/wiki/Terminology)): - -1. B1: `key → value`: the principal object storage, used by a state machine, behind the SDK `KVStore` interface: provides direct access by key and allows prefix iteration (KV DB backend must support it). -2. B2: `hash(key, value) → key`: a reverse index to get a key from an SMT path. Recall that SMT will store `(k, v)` as `(hash(k), hash(key, value))`. So, we can get an object value by composing `SMT_path → B2 → B1`. -3. we could use more buckets to optimize the app usage if needed. - -Above, we propose to use a KV DB. However, for the state machine, we could use an RDBMS, which we discuss below. - -### Requirements - -State Storage requirements: - -+ range queries -+ quick (key, value) access -+ creating a snapshot -+ historical versioning -+ pruning (garbage collection) - -State Commitment requirements: - -+ fast updates -+ tree path should be short -+ pruning (garbage collection) - -### LazyLedger SMT for State Commitment - -A Sparse Merkle tree is based on the idea of a complete Merkle tree of an intractable size. The assumption here is that as the size of the tree is intractable, there would only be a few leaf nodes with valid data blocks relative to the tree size, rendering a sparse tree. - -### Snapshots for storage sync and state versioning - -Below, with simple _snapshot_ we refer to a database snapshot mechanism, not to a _ABCI snapshot sync_. The latter will be referred as _snapshot sync_ (which will directly use DB snapshot as described below). - -Database snapshot is a view of DB state at a certain time or transaction. It's not a full copy of a database (it would be too big), usually a snapshot mechanism is based on a _copy on write_ and it allows to efficiently deliver DB state at a certain stage. -Some DB engines support snapshotting. Hence, we propose to reuse that functionality for the state sync and versioning (described below). It will the supported DB engines to ones which efficiently implement snapshots. In a final section we will discuss evaluated DBs. - -One of the Stargate core features is a _snapshot sync_ delivered in the `/snapshot` package. It provides a way to trustlessly sync a blockchain without repeating all transactions from the genesis. This feature is implemented in SDK and requires storage support. Currently IAVL is the only supported backend. It works by streaming to a client a snapshot of a `SS` at a certain version together with a header chain. - -A new `SS` snapshot will be created in every `EndBlocker` and identified by a block height. The `rootmulti.Store` keeps track of the available snapshots to offer `SS` at a certain version. The `rootmulti.Store` implements the `CommitMultiStore` interface, which encapsulates a `Committer` interface. `Committer` has a `Commit`, `SetPruning`, `GetPruning` functions which will be used for creating and removing snapshots. The `rootStore.Commit` function creates a new snapshot and increments the version on each call, and checks if it needs to remove old versions. We will need to update the SMT interface to implement the `Committer` interface. -NOTE: `Commit` must be called exactly once per block. Otherwise we risk going out of sync for the version number and block height. -NOTE: For the SDK storage, we may consider splitting that interface into `Committer` and `PruningCommitter` - only the multiroot should implement `PruningCommitter` (cache and prefix store don't need pruning). - -Number of historical versions for `abci.Query` and state sync snapshots is part of a node configuration, not a chain configuration (configuration implied by the blockchain consensus). A configuration should allow to specify number of past blocks and number of past blocks modulo some number (eg: 100 past blocks and one snapshot every 100 blocks for past 2000 blocks). Archival nodes can keep all past versions. - -Pruning old snapshots is effectively done by a database. Whenever we update a record in `SC`, SMT won't update nodes - instead it creates new nodes on the update path, without removing the old one. Since we are snapshoting each block, we need to update that mechanism to immediately remove orphaned nodes from the storage. This is a safe operation - snapshots will keep track of the records which should be available for past versions. - -To manage the active snapshots we will either us a DB _max number of snapshots_ option (if available), or will remove snapshots in the `EndBlocker`. The latter option can be done efficiently by identifying snapshots with block height. - -#### Accessing old state versions - -One of the functional requirements is to access old state. This is done through `abci.Query` structure. The version is specified by a block height (so we query for an object by a key `K` at block height `H`). The number of old versions supported for `abci.Query` is configurable. Accessing an old state is done by using available snapshots. -`abci.Query` doesn't need old state of `SC`. So, for efficiency, we should keep `SC` and `SS` in different databases (however using the same DB engine). - -Moreover, SDK could provide a way to directly access the state. However, a state machine shouldn't do that - since the number of snapshots is configurable, it would lead to nondeterministic execution. - -We positively [validated](https://github.com/cosmos/cosmos-sdk/discussions/8297) a versioning and snapshot mechanism for querying old state with regards to the database we evaluated. - -### State Proofs - -For any object stored in State Store (SS), we have corresponding object in `SC`. A proof for object `V` identified by a key `K` is a branch of `SC`, where the path corresponds to the key `hash(K)`, and the leaf is `hash(K, V)`. - -### Rollbacks - -We need to be able to process transactions and roll-back state updates if a transaction fails. This can be done in the following way: during transaction processing, we keep all state change requests (writes) in a `CacheWrapper` abstraction (as it's done today). Once we finish the block processing, in the `Endblocker`, we commit a root store - at that time, all changes are written to the SMT and to the `SS` and a snapshot is created. - -### Committing to an object without saving it - -We identified use-cases, where modules will need to save an object commitment without storing an object itself. Sometimes clients are receiving complex objects, and they have no way to prove a correctness of that object without knowing the storage layout. For those use cases it would be easier to commit to the object without storing it directly. - -## Consequences - -### Backwards Compatibility - -This ADR doesn't introduce any SDK level API changes. - -We change the storage layout of the state machine, a storage hard fork and network upgrade is required to incorporate these changes. SMT provides a merkle proof functionality, however it is not compatible with ICS23. Updating the proofs for ICS23 compatibility is required. - -### Positive - -+ Decoupling state from state commitment introduce better engineering opportunities for further optimizations and better storage patterns. -+ Performance improvements. -+ Joining SMT based camp which has wider and proven adoption than IAVL. Example projects which decided on SMT: Ethereum2, Diem (Libra), Trillan, Tezos, LazyLedger. - -### Negative - -+ Storage migration -+ LL SMT doesn't support pruning - we will need to add and test that functionality. - -### Neutral - -+ Deprecating IAVL, which is one of the core proposals of Cosmos Whitepaper. - -## Alternative designs - -Most of the alternative designs were evaluated in [state commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h). - -Ethereum research published [Verkle Tire](https://notes.ethereum.org/_N1mutVERDKtqGIEYc-Flw#fnref1) - an idea of combining polynomial commitments with merkle tree in order to reduce the tree height. This concept has a very good potential, but we think it's too early to implement it. The current, SMT based design could be easily updated to the Verkle Tire once other research implement all necessary libraries. The main advantage of the design described in this ADR is the separation of state commitments from the data storage and designing a more powerful interface. - -## Further Discussions - -### Evaluated KV Databases - -We verified existing databases KV databases for evaluating snapshot support. The following databases provide efficient snapshot mechanism: Badger, RocksDB, [Pebble](https://github.com/cockroachdb/pebble). Databases which don't provide such support or are not production ready: boltdb, leveldb, goleveldb, membdb, lmdb. - -### RDBMS - -Use of RDBMS instead of simple KV store for state. Use of RDBMS will require an SDK API breaking change (`KVStore` interface), will allow better data extraction and indexing solutions. Instead of saving an object as a single blob of bytes, we could save it as record in a table in the state storage layer, and as a `hash(key, protobuf(object))` in the SMT as outlined above. To verify that an object registered in RDBMS is same as the one committed to SMT, one will need to load it from RDBMS, marshal using protobuf, hash and do SMT search. - -### Off Chain Store - -We were discussing use case where modules can use a support database, which is not automatically committed. Module will responsible for having a sound storage model and can optionally use the feature discussed in __Committing to an object without saving it_ section. - -## References - -+ [IAVL What's Next?](https://github.com/cosmos/cosmos-sdk/issues/7100) -+ [IAVL overview](https://docs.google.com/document/d/16Z_hW2rSAmoyMENO-RlAhQjAG3mSNKsQueMnKpmcBv0/edit#heading=h.yd2th7x3o1iv) of it's state v0.15 -+ [State commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h) -+ [LazyLedger SMT](https://github.com/lazyledger/smt) -+ Facebook Diem (Libra) SMT [design](https://developers.diem.com/papers/jellyfish-merkle-tree/2021-01-14.pdf) -+ [Trillian Revocation Transparency](https://github.com/google/trillian/blob/master/docs/papers/RevocationTransparency.pdf), [Trillian Verifiable Data Structures](https://github.com/google/trillian/blob/master/docs/papers/VerifiableDataStructures.pdf). -+ Design and implementation [discussion](https://github.com/cosmos/cosmos-sdk/discussions/8297). diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-041-in-place-store-migrations.md b/versioned_docs/version-0.45/integrate/architecture/adr-041-in-place-store-migrations.md deleted file mode 100644 index 1cc9ef901..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-041-in-place-store-migrations.md +++ /dev/null @@ -1,167 +0,0 @@ -# ADR 041: In-Place Store Migrations - -## Changelog - -- 17.02.2021: Initial Draft - -## Status - -Accepted - -## Abstract - -This ADR introduces a mechanism to perform in-place state store migrations during chain software upgrades. - -## Context - -When a chain upgrade introduces state-breaking changes inside modules, the current procedure consists of exporting the whole state into a JSON file (via the `simd export` command), running migration scripts on the JSON file (`simd migrate` command), clearing the stores (`simd unsafe-reset-all` command), and starting a new chain with the migrated JSON file as new genesis (optionally with a custom initial block height). An example of such a procedure can be seen [in the Cosmos Hub 3->4 migration guide](https://github.com/cosmos/gaia/blob/v4.0.3/docs/migration/cosmoshub-3.md#upgrade-procedure). - -This procedure is cumbersome for multiple reasons: - -- The procedure takes time. It can take hours to run the `export` command, plus some additional hours to run `InitChain` on the fresh chain using the migrated JSON. -- The exported JSON file can be heavy (~100MB-1GB), making it difficult to view, edit and transfer, which in turn introduces additional work to solve these problems (such as [streaming genesis](https://github.com/cosmos/cosmos-sdk/issues/6936)). - -## Decision - -We propose a migration procedure based on modifying the KV store in-place without involving the JSON export-process-import flow described above. - -### Module `ConsensusVersion` - -We introduce a new method on the `AppModule` interface: - -```go -type AppModule interface { - // --snip-- - ConsensusVersion() uint64 -} -``` - -This methods returns an `uint64` which serves as state-breaking version of the module. It MUST be incremented on each consensus-breaking change introduced by the module. To avoid potential errors with default values, the initial version of a module MUST be set to 1. In the SDK, version 1 corresponds to the modules in the v0.41 series. - -### Module-Specific Migration Functions - -For each consensus-breaking change introduced by the module, a migration script from ConsensusVersion `N` to version `N+1` MUST be registered in the `Configurator` using its newly-added `RegisterMigration` method. All modules receive a reference to the configurator in their `RegisterServices` method on `AppModule`, and this is where the migration functions should be registered. The migration functions should be registered in increasing order. - -```go -func (am AppModule) RegisterServices(cfg module.Configurator) { - // --snip-- - cfg.RegisterMigration(types.ModuleName, 1, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 1 to 2. - }) - cfg.RegisterMigration(types.ModuleName, 2, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 2 to 3. - }) - // etc. -} -``` - -For example, if the new ConsensusVersion of a module is `N` , then `N-1` migration functions MUST be registered in the configurator. - -In the SDK, the migration functions are handled by each module's keeper, because the keeper holds the `sdk.StoreKey` used to perform in-place store migrations. To not overload the keeper, a `Migrator` wrapper is used by each module to handle the migration functions: - -```go -// Migrator is a struct for handling in-place store migrations. -type Migrator struct { - BaseKeeper -} -``` - -Since migration functions manipulate legacy code, they should live inside the `legacy/` folder of each module, and be called by the Migrator's methods. We propose the format `Migrate{M}to{N}` for method names. - -```go -// Migrate1to2 migrates from version 1 to 2. -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v043bank.MigrateStore(ctx, m.keeper.storeKey) // v043bank is package `x/bank/legacy/v043`. -} -``` - -Each module's migration functions are specific to the module's store evolutions, and are not described in this ADR. An example of x/bank store key migrations after the introduction of ADR-028 length-prefixed addresses can be seen in this [store.go code](https://github.com/cosmos/cosmos-sdk/blob/36f68eb9e041e20a5bb47e216ac5eb8b91f95471/x/bank/legacy/v043/store.go#L41-L62). - -### Tracking Module Versions in `x/upgrade` - -We introduce a new prefix store in `x/upgrade`'s store. This store will track each module's current version, it can be modelized as a `map[string]uint64` of module name to module ConsensusVersion, and will be used when running the migrations (see next section for details). The key prefix used is `0x1`, and the key/value format is: - -``` -0x2 | {bytes(module_name)} => BigEndian(module_consensus_version) -``` - -The initial state of the store is set from `app.go`'s `InitChainer` method. - -The UpgradeHandler signature needs to be updated to take a `VersionMap`, as well as return an upgraded `VersionMap` and an error: - -```diff -- type UpgradeHandler func(ctx sdk.Context, plan Plan) -+ type UpgradeHandler func(ctx sdk.Context, plan Plan, versionMap VersionMap) (VersionMap, error) -``` - -To apply an upgrade, we query the `VersionMap` from the `x/upgrade` store and pass it into the handler. The handler runs the actual migration functions (see next section), and if successful, returns an updated `VersionMap` to be stored in state. - -```diff -func (k UpgradeKeeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) { - // --snip-- -- handler(ctx, plan) -+ updatedVM, err := handler(ctx, plan, k.GetModuleVersionMap(ctx)) // k.GetModuleVersionMap() fetches the VersionMap stored in state. -+ if err != nil { -+ return err -+ } -+ -+ // Set the updated consensus versions to state -+ k.SetModuleVersionMap(ctx, updatedVM) -} -``` - -A gRPC query endpoint to query the `VersionMap` stored in `x/upgrade`'s state will also be added, so that app developers can double-check the `VersionMap` before the upgrade handler runs. - -### Running Migrations - -Once all the migration handlers are registered inside the configurator (which happens at startup), running migrations can happen by calling the `RunMigrations` method on `module.Manager`. This function will loop through all modules, and for each module: - -- Get the old ConsensusVersion of the module from its `VersionMap` argument (let's call it `M`). -- Fetch the new ConsensusVersion of the module from the `ConsensusVersion()` method on `AppModule` (call it `N`). -- If `N>M`, run all registered migrations for the module sequentially `M -> M+1 -> M+2...` until `N`. - - There is a special case where there is no ConsensusVersion for the module, as this means that the module has been newly added during the upgrade. In this case, no migration function is run, and the module's current ConsensusVersion is saved to `x/upgrade`'s store. - -If a required migration is missing (e.g. if it has not been registered in the `Configurator`), then the `RunMigrations` function will error. - -In practice, the `RunMigrations` method should be called from inside an `UpgradeHandler`. - -```go -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - return app.mm.RunMigrations(ctx, vm) -}) -``` - -Assuming a chain upgrades at block `n`, the procedure should run as follows: - -- the old binary will halt in `BeginBlock` when starting block `N`. In its store, the ConsensusVersions of the old binary's modules are stored. -- the new binary will start at block `N`. The UpgradeHandler is set in the new binary, so will run at `BeginBlock` of the new binary. Inside `x/upgrade`'s `ApplyUpgrade`, the `VersionMap` will be retrieved from the (old binary's) store, and passed into the `RunMigrations` functon, migrating all module stores in-place before the modules' own `BeginBlock`s. - -## Consequences - -### Backwards Compatibility - -This ADR introduces a new method `ConsensusVersion()` on `AppModule`, which all modules need to implement. It also alters the UpgradeHandler function signature. As such, it is not backwards-compatible. - -While modules MUST register their migration functions when bumping ConsensusVersions, running those scripts using an upgrade handler is optional. An application may perfectly well decide to not call the `RunMigrations` inside its upgrade handler, and continue using the legacy JSON migration path. - -### Positive - -- Perform chain upgrades without manipulating JSON files. -- While no benchmark has been made yet, it is probable that in-place store migrations will take less time than JSON migrations. The main reason supporting this claim is that both the `simd export` command on the old binary and the `InitChain` function on the new binary will be skipped. - -### Negative - -- Module developers MUST correctly track consensus-breaking changes in their modules. If a consensus-breaking change is introduced in a module without its corresponding `ConsensusVersion()` bump, then the `RunMigrations` function won't detect the migration, and the chain upgrade might be unsuccessful. Documentation should clearly reflect this. - -### Neutral - -- The SDK will continue to support JSON migrations via the existing `simd export` and `simd migrate` commands. -- The current ADR does not allow creating, renaming or deleting stores, only modifying existing store keys and values. The SDK already has the `StoreLoader` for those operations. - -## Further Discussions - -## References - -- Initial discussion: https://github.com/cosmos/cosmos-sdk/discussions/8429 -- Implementation of `ConsensusVersion` and `RunMigrations`: https://github.com/cosmos/cosmos-sdk/pull/8485 -- Issue discussing `x/upgrade` design: https://github.com/cosmos/cosmos-sdk/issues/8514 diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-042-group-module.md b/versioned_docs/version-0.45/integrate/architecture/adr-042-group-module.md deleted file mode 100644 index 7d863f958..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-042-group-module.md +++ /dev/null @@ -1,279 +0,0 @@ -# ADR 042: Group Module - -## Changelog - -- 2020/04/09: Initial Draft - -## Status - -Draft - -## Abstract - -This ADR defines the `x/group` module which allows the creation and management of on-chain multi-signature accounts and enables voting for message execution based on configurable decision policies. - -## Context - -The legacy amino multi-signature mechanism of the Cosmos SDK has certain limitations: - -- Key rotation is not possible, although this can be solved with [account rekeying](adr-034-account-rekeying.md). -- Thresholds can't be changed. -- UX is cumbersome for non-technical users ([#5661](https://github.com/cosmos/cosmos-sdk/issues/5661)). -- It requires `legacy_amino` sign mode ([#8141](https://github.com/cosmos/cosmos-sdk/issues/8141)). - -While the group module is not meant to be a total replacement for the current multi-signature accounts, it provides a solution to the limitations described above, with a more flexible key management system where keys can be added, updated or removed, as well as configurable thresholds. -It's meant to be used with other access control modules such as [`x/feegrant`](./adr-029-fee-grant-module.md) ans [`x/authz`](adr-030-authz-module.md) to simplify key management for individuals and organizations. - -The proof of concept of the group module can be found in https://github.com/regen-network/regen-ledger/tree/master/proto/regen/group/v1alpha1 and https://github.com/regen-network/regen-ledger/tree/master/x/group. - -## Decision - -We propose merging the `x/group` module with its supporting [ORM/Table Store package](https://github.com/regen-network/regen-ledger/tree/master/orm) ([#7098](https://github.com/cosmos/cosmos-sdk/issues/7098)) into the Cosmos SDK and continuing development here. There will be a dedicated ADR for the ORM package. - -### Group - -A group is a composition of accounts with associated weights. It is not -an account and doesn't have a balance. It doesn't in and of itself have any -sort of voting or decision weight. -Group members can create proposals and vote on them through group accounts using different decision policies. - -It has an `admin` account which can manage members in the group, update the group -metadata and set a new admin. - -```proto -message GroupInfo { - - // group_id is the unique ID of this group. - uint64 group_id = 1; - - // admin is the account address of the group's admin. - string admin = 2; - - // metadata is any arbitrary metadata to attached to the group. - bytes metadata = 3; - - // version is used to track changes to a group's membership structure that - // would break existing proposals. Whenever a member weight has changed, - // or any member is added or removed, the version is incremented and will - // invalidate all proposals from older versions. - uint64 version = 4; - - // total_weight is the sum of the group members' weights. - string total_weight = 5; -} -``` - -```proto -message GroupMember { - - // group_id is the unique ID of the group. - uint64 group_id = 1; - - // member is the member data. - Member member = 2; -} - -// Member represents a group member with an account address, -// non-zero weight and metadata. -message Member { - - // address is the member's account address. - string address = 1; - - // weight is the member's voting weight that should be greater than 0. - string weight = 2; - - // metadata is any arbitrary metadata to attached to the member. - bytes metadata = 3; -} -``` - -### Group Account - -A group account is an account associated with a group and a decision policy. -A group account does have a balance. - -Group accounts are abstracted from groups because a single group may have -multiple decision policies for different types of actions. Managing group -membership separately from decision policies results in the least overhead -and keeps membership consistent across different policies. The pattern that -is recommended is to have a single master group account for a given group, -and then to create separate group accounts with different decision policies -and delegate the desired permissions from the master account to -those "sub-accounts" using the [`x/authz` module](adr-030-authz-module.md). - -```proto -message GroupAccountInfo { - - // address is the group account address. - string address = 1; - - // group_id is the ID of the Group the GroupAccount belongs to. - uint64 group_id = 2; - - // admin is the account address of the group admin. - string admin = 3; - - // metadata is any arbitrary metadata of this group account. - bytes metadata = 4; - - // version is used to track changes to a group's GroupAccountInfo structure that - // invalidates active proposal from old versions. - uint64 version = 5; - - // decision_policy specifies the group account's decision policy. - google.protobuf.Any decision_policy = 6 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; -} -``` - -Similarly to a group admin, a group account admin can update its metadata, decision policy or set a new group account admin. - -A group account can also be an admin or a member of a group. -For instance, a group admin could be another group account which could "elects" the members or it could be the same group that elects itself. - -### Decision Policy - -A decision policy is the mechanism by which members of a group can vote on -proposals. - -All decision policies should have a minimum and maximum voting window. -The minimum voting window is the minimum duration that must pass in order -for a proposal to potentially pass, and it may be set to 0. The maximum voting -window is the maximum time that a proposal may be voted on and executed if -it reached enough support before it is closed. -Both of these values must be less than a chain-wide max voting window parameter. - -We define the `DecisionPolicy` interface that all decision policies must implement: - -```go -type DecisionPolicy interface { - codec.ProtoMarshaler - - ValidateBasic() error - GetTimeout() types.Duration - Allow(tally Tally, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error) - Validate(g GroupInfo) error -} - -type DecisionPolicyResult struct { - Allow bool - Final bool -} -``` - -#### Threshold decision policy - -A threshold decision policy defines a minimum support votes (_yes_), based on a tally -of voter weights, for a proposal to pass. For -this decision policy, abstain and veto are treated as no support (_no_). - -```proto -message ThresholdDecisionPolicy { - - // threshold is the minimum weighted sum of support votes for a proposal to succeed. - string threshold = 1; - - // voting_period is the duration from submission of a proposal to the end of voting period - // Within this period, votes and exec messages can be submitted. - google.protobuf.Duration voting_period = 2 [(gogoproto.nullable) = false]; -} -``` - -### Proposal - -Any member of a group can submit a proposal for a group account to decide upon. -A proposal consists of a set of `sdk.Msg`s that will be executed if the proposal -passes as well as any metadata associated with the proposal. These `sdk.Msg`s get validated as part of the `Msg/CreateProposal` request validation. They should also have their signer set as the group account. - -Internally, a proposal also tracks: - -- its current `Status`: submitted, closed or aborted -- its `Result`: unfinalized, accepted or rejected -- its `VoteState` in the form of a `Tally`, which is calculated on new votes and when executing the proposal. - -```proto -// Tally represents the sum of weighted votes. -message Tally { - option (gogoproto.goproto_getters) = false; - - // yes_count is the weighted sum of yes votes. - string yes_count = 1; - - // no_count is the weighted sum of no votes. - string no_count = 2; - - // abstain_count is the weighted sum of abstainers. - string abstain_count = 3; - - // veto_count is the weighted sum of vetoes. - string veto_count = 4; -} -``` - -### Voting - -Members of a group can vote on proposals. There are four choices to choose while voting - yes, no, abstain and veto. Not -all decision policies will support them. Votes can contain some optional metadata. -In the current implementation, the voting window begins as soon as a proposal -is submitted. - -Voting internally updates the proposal `VoteState` as well as `Status` and `Result` if needed. - -### Executing Proposals - -Proposals will not be automatically executed by the chain in this current design, -but rather a user must submit a `Msg/Exec` transaction to attempt to execute the -proposal based on the current votes and decision policy. A future upgrade could -automate this and have the group account (or a fee granter) pay. - -#### Changing Group Membership - -In the current implementation, updating a group or a group account after submitting a proposal will make it invalid. It will simply fail if someone calls `Msg/Exec` and will eventually be garbage collected. - -### Notes on current implementation - -This section outlines the current implementation used in the proof of concept of the group module but this could be subject to changes and iterated on. - -#### ORM - -The [ORM package](https://github.com/cosmos/cosmos-sdk/discussions/9156) defines tables, sequences and secondary indexes which are used in the group module. - -Groups are stored in state as part of a `groupTable`, the `group_id` being an auto-increment integer. Group members are stored in a `groupMemberTable`. - -Group accounts are stored in a `groupAccountTable`. The group account address is generated based on an auto-increment integer which is used to derive the group module `RootModuleKey` into a `DerivedModuleKey`, as stated in [ADR-033](adr-033-protobuf-inter-module-comm.md#modulekeys-and-moduleids). The group account is added as a new `ModuleAccount` through `x/auth`. - -Proposals are stored as part of the `proposalTable` using the `Proposal` type. The `proposal_id` is an auto-increment integer. - -Votes are stored in the `voteTable`. The primary key is based on the vote's `proposal_id` and `voter` account address. - -#### ADR-033 to route proposal messages - -Inter-module communication introduced by [ADR-033](adr-033-protobuf-inter-module-comm.md) can be used to route a proposal's messages using the `DerivedModuleKey` corresponding to the proposal's group account. - -## Consequences - -### Positive - -- Improved UX for multi-signature accounts allowing key rotation and custom decision policies. - -### Negative - -### Neutral - -- It uses ADR 033 so it will need to be implemented within the Cosmos SDK, but this doesn't imply necessarily any large refactoring of existing Cosmos SDK modules. -- The current implementation of the group module uses the ORM package. - -## Further Discussions - -- Convergence of `/group` and `x/gov` as both support proposals and voting: https://github.com/cosmos/cosmos-sdk/discussions/9066 -- `x/group` possible future improvements: - - Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288) - - Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41) - - Make `Tally` more flexible and support non-binary choices - -## References - -- Initial specification: - - https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module - - [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236) -- Proposal to add `x/group` into the SDK: [#7633](https://github.com/cosmos/cosmos-sdk/issues/7633) diff --git a/versioned_docs/version-0.45/integrate/architecture/adr-template.md b/versioned_docs/version-0.45/integrate/architecture/adr-template.md deleted file mode 100644 index 191cd8e95..000000000 --- a/versioned_docs/version-0.45/integrate/architecture/adr-template.md +++ /dev/null @@ -1,60 +0,0 @@ -# ADR {ADR-NUMBER}: {TITLE} - -## Changelog - -- {date}: {changelog} - -## Status - -{DRAFT | PROPOSED} Not Implemented - -> Please have a look at the [PROCESS](./PROCESS.md#adr-status) page. -> Use DRAFT if the ADR is in a draft stage (draft PR) or PROPOSED if it's in review. - -## Abstract - -> "If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the ADR. -> A short (~200 word) description of the issue being addressed. - -## Context - -> This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. It should clearly explain the problem and motivation that the proposal aims to resolve. -> {context body} - -## Decision - -> This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..." -> {decision body} - -## Consequences - -> This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future. - -### Backwards Compatibility - -> All ADRs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The ADR must explain how the author proposes to deal with these incompatibilities. ADR submissions without a sufficient backwards compatibility treatise may be rejected outright. - -### Positive - -{positive consequences} - -### Negative - -{negative consequences} - -### Neutral - -{neutral consequences} - -## Further Discussions - -While an ADR is in the DRAFT or PROPOSED stage, this section should contain a summary of issues to be solved in future iterations (usually referencing comments from a pull-request discussion). -Later, this section can optionally list ideas or improvements the author or reviewers found during the analysis of this ADR. - -## Test Cases [optional] - -Test cases for an implementation are mandatory for ADRs that are affecting consensus changes. Other ADRs can choose to include links to test cases if applicable. - -## References - -- {reference link} diff --git a/versioned_docs/version-0.45/integrate/building-modules/00-intro.md b/versioned_docs/version-0.45/integrate/building-modules/00-intro.md deleted file mode 100644 index b47984e12..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/00-intro.md +++ /dev/null @@ -1,88 +0,0 @@ -# Introduction to SDK Modules - -Modules define most of the logic of SDK applications. Developers compose modules together using the Cosmos SDK to build their custom application-specific blockchains. This document outlines the basic concepts behind SDK modules and how to approach module management. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK application](../high-level-concepts/app-anatomy.md) {prereq} -- [Lifecycle of an SDK transaction](../high-level-concepts/01-tx-lifecycle.md) {prereq} - -## Role of Modules in an SDK Application - -The Cosmos SDK can be thought of as the Ruby-on-Rails of blockchain development. It comes with a core that provides the basic functionalities every blockchain application needs, like a [boilerplate implementation of the ABCI](../../develop/advanced-concepts/00-baseapp.md) to communicate with the underlying consensus engine, a [`multistore`](../advanced-concepts/04-store.md#multistore) to persist state, a [server](../advanced-concepts/03-node.md) to form a full-node and [interfaces](./09-module-interfaces.md) to handle queries. - -On top of this core, the Cosmos SDK enables developers to build modules that implement the business logic of their application. In other words, SDK modules implement the bulk of the logic of applications, while the core does the wiring and enables modules to be composed together. The end goal is to build a robust ecosystem of open-source SDK modules, making it increasingly easier to build complex blockchain applications. - -SDK modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one or more `KVStore`s in the [main multistore](../advanced-concepts/04-store.md), as well as a subset of [message types](./02-messages-and-queries.md#messages). These messages are routed by one of the main components of SDK core, [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md), to a module Protobuf [`Msg` service](./03-msg-services.md) that defines them. - -``` - + - | - | Transaction relayed from the full-node's consensus engine - | to the node's application via DeliverTx - | - | - | - +---------------------v--------------------------+ - | APPLICATION | - | | - | Using baseapp's methods: Decode the Tx, | - | extract and route the message(s) | - | | - +---------------------+--------------------------+ - | - | - | - +---------------------------+ - | - | - | - | Message routed to the correct - | module to be processed - | - | -+----------------+ +---------------+ +----------------+ +------v----------+ -| | | | | | | | -| AUTH MODULE | | BANK MODULE | | STAKING MODULE | | GOV MODULE | -| | | | | | | | -| | | | | | | Handles message,| -| | | | | | | Updates state | -| | | | | | | | -+----------------+ +---------------+ +----------------+ +------+----------+ - | - | - | - | - +--------------------------+ - | - | Return result to the underlying consensus engine (e.g. Tendermint) - | (0=Ok, 1=Err) - v -``` - -As a result of this architecture, building an SDK application usually revolves around writing modules to implement the specialized logic of the application, and composing them with existing modules to complete the application. Developers will generally work on modules that implement logic needed for their specific use case that do not exist yet, and will use existing modules for more generic functionalities like staking, accounts or token management. - -## How to Approach Building Modules as a Developer - -While there are no definitive guidelines for writing modules, here are some important design principles developers should keep in mind when building them: - -- **Composability**: SDK applications are almost always composed of multiple modules. This means developers need to carefully consider the integration of their module not only with the core of the Cosmos SDK, but also with other modules. The former is achieved by following standard design patterns outlined [here](#main-components-of-sdk-modules), while the latter is achieved by properly exposing the store(s) of the module via the [`keeper`](./06-keeper.md). -- **Specialization**: A direct consequence of the **composability** feature is that modules should be **specialized**. Developers should carefully establish the scope of their module and not batch multiple functionalities into the same module. This separation of concerns enables modules to be re-used in other projects and improves the upgradability of the application. **Specialization** also plays an important role in the [object-capabilities model](../advanced-concepts/ocap.md) of the Cosmos SDK. -- **Capabilities**: Most modules need to read and/or write to the store(s) of other modules. However, in an open-source environment, it is possible for some modules to be malicious. That is why module developers need to carefully think not only about how their module interacts with other modules, but also about how to give access to the module's store(s). The Cosmos SDK takes a capabilities-oriented approach to inter-module security. This means that each store defined by a module is accessed by a `key`, which is held by the module's [`keeper`](./06-keeper.md). This `keeper` defines how to access the store(s) and under what conditions. Access to the module's store(s) is done by passing a reference to the module's `keeper`. - -## Main Components of SDK Modules - -Modules are by convention defined in the `./x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components: - -- A [`keeper`](./06-keeper.md), used to access the module's store(s) and update the state. -- A [`Msg` service](./02-messages-and-queries.md#messages), used to process messages when they are routed to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#message-routing) and trigger state-transitions. -- A [query service](./04-query-services.md), used to process user queries when they are routed to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#query-routing). -- Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module. - -In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./01-module-manager.md). - -Please refer to the [structure document](./11-structure.md) to learn about the recommended structure of a module's directory. - -## Next {hide} - -Read more on the [`AppModule` interface and the `module manager`](./01-module-manager.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/01-module-manager.md b/versioned_docs/version-0.45/integrate/building-modules/01-module-manager.md deleted file mode 100644 index 9d66fd5fa..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/01-module-manager.md +++ /dev/null @@ -1,146 +0,0 @@ -# Module Manager - -Cosmos SDK modules need to implement the [`AppModule` interfaces](#application-module-interfaces), in order to be managed by the application's [module manager](#module-manager). The module manager plays an important role in [`message` and `query` routing](../../develop/advanced-concepts/00-baseapp.md#routing), and allows application developers to set the order of execution of a variety of functions like [`BeginBlocker` and `EndBlocker`](../high-level-concepts/app-anatomy.md#begingblocker-and-endblocker). {synopsis} - -## Pre-requisite Readings - -- [Introduction to SDK Modules](./intro.md) {prereq} - -## Application Module Interfaces - -Application module interfaces exist to facilitate the composition of modules together to form a functional SDK application. There are 3 main application module interfaces: - -- [`AppModuleBasic`](#appmodulebasic) for independent module functionalities. -- [`AppModule`](#appmodule) for inter-dependent module functionalities (except genesis-related functionalities). -- [`AppModuleGenesis`](#appmodulegenesis) for inter-dependent genesis-related module functionalities. - -The `AppModuleBasic` interface exists to define independent methods of the module, i.e. those that do not depend on other modules in the application. This allows for the construction of the basic application structure early in the application definition, generally in the `init()` function of the [main application file](../high-level-concepts/app-anatomy.md#core-application-file). - -The `AppModule` interface exists to define inter-dependent module methods. Many modules need to interract with other modules, typically through [`keeper`s](./06-keeper.md), which means there is a need for an interface where modules list their `keeper`s and other methods that require a reference to another module's object. `AppModule` interface also enables the module manager to set the order of execution between module's methods like `BeginBlock` and `EndBlock`, which is important in cases where the order of execution between modules matters in the context of the application. - -Lastly the interface for genesis functionality `AppModuleGenesis` is separated out from full module functionality `AppModule` so that modules which -are only used for genesis can take advantage of the `Module` patterns without having to define many placeholder functions. - -### `AppModuleBasic` - -The `AppModuleBasic` interface defines the independent methods modules need to implement. - -+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L49-L63 - -Let us go through the methods: - -- `Name()`: Returns the name of the module as a `string`. -- `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marshal and unmarshal structs to/from `[]byte` in order to persist them in the module's `KVStore`. -- `RegisterInterfaces(codectypes.InterfaceRegistry)`: Registers a module's interface types and their concrete implementations as `proto.Message`. -- `DefaultGenesis(codec.JSONCodec)`: Returns a default [`GenesisState`](./08-genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing. -- `ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./08-genesis.md#validategenesis) function defined by the module developer. -- `RegisterRESTRoutes(client.Context, *mux.Router)`: Registers the REST routes for the module. These routes will be used to map REST request to the module in order to process them. See [gRPC and REST](../advanced-concepts/08-grpc_rest.md) for more. -- `RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux)`: Registers gRPC routes for the module. -- `GetTxCmd()`: Returns the root [`Tx` command](./09-module-interfaces.md#tx) for the module. The subcommands of this root command are used by end-users to generate new transactions containing [`message`s](./02-messages-and-queries.md#queries) defined in the module. -- `GetQueryCmd()`: Return the root [`query` command](./09-module-interfaces.md#query) for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module. - -All the `AppModuleBasic` of an application are managed by the [`BasicManager`](#basicmanager). - -### `AppModuleGenesis` - -The `AppModuleGenesis` interface is a simple embedding of the `AppModuleBasic` interface with two added methods. - -+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L152-L158 - -Let us go through the two added methods: - -- `InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started). -- `ExportGenesis(sdk.Context, codec.JSONCodec)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain. - -It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)` and `ExportGenesis(sdk.Context, codec.JSONCodec)` will generally be defined as methods of the concrete type implementing the `AppModule` interface. - -### `AppModule` - -The `AppModule` interface defines the inter-dependent methods that modules need to implement. - -+++ https://github.com/cosmos/cosmos-sdk/blob/b4cce159bcc6a32ac78245c6866dd87c73f3720d/types/module/module.go#L160-L182 - -`AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter. - -Let us go through the methods of `AppModule`: - -- `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./07-invariants.md) of the module. If an invariant deviates from its predicted value, the [`InvariantRegistry`](./07-invariants.md#registry) triggers appropriate logic (most often the chain will be halted). -- `Route()`: Returns the route for [`message`s](./02-messages-and-queries.md#messages) to be routed to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#message-routing). -- `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./02-messages-and-queries.md#queries) to be routes to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#query-routing). -- `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./04-query-services.md#legacy-queriers) given the query `path`, in order to process the `query`. -- `RegisterServices(Configurator)`: Allows a module to register services. -- `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module. -- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the end of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the end of each block for this module. - -### Implementing the Application Module Interfaces - -Typically, the various application module interfaces are implemented in a file called `module.go`, located in the module's folder (e.g. `./x/module/module.go`). - -Almost every module needs to implement the `AppModuleBasic` and `AppModule` interfaces. If the module is only used for genesis, it will implement `AppModuleGenesis` instead of `AppModule`. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the `Route()` function often calls a `NewMsgServerImpl(k keeper)` function defined in `keeper/msg_server.go` and therefore needs to pass the module's [`keeper`](./06-keeper.md) as a parameter. - -```go -// example -type AppModule struct { - AppModuleBasic - keeper Keeper -} -``` - -In the example above, you can see that the `AppModule` concrete type references an `AppModuleBasic`, and not an `AppModuleGenesis`. That is because `AppModuleGenesis` only needs to be implemented in modules that focus on genesis-related functionalities. In most modules, the concrete `AppModule` type will have a reference to an `AppModuleBasic` and implement the two added methods of `AppModuleGenesis` directly in the `AppModule` type. - -If no parameter is required (which is often the case for `AppModuleBasic`), just declare an empty concrete type like so: - -```go -type AppModuleBasic struct{} -``` - -## Module Managers - -Module managers are used to manage collections of `AppModuleBasic` and `AppModule`. - -### `BasicManager` - -The `BasicManager` is a structure that lists all the `AppModuleBasic` of an application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L65-L66 - -It implements the following methods: - -- `NewBasicManager(modules ...AppModuleBasic)`: Constructor function. It takes a list of the application's `AppModuleBasic` and builds a new `BasicManager`. This function is generally called in the `init()` function of [`app.go`](../high-level-concepts/app-anatomy.md#core-application-file) to quickly initialize the independent elements of the application's modules (click [here](https://github.com/cosmos/gaia/blob/master/app/app.go#L59-L74) to see an example). -- `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec.LegacyAmino`s](../core/05-encoding.md#amino) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../high-level-concepts/app-anatomy.md#constructor). -- `RegisterInterfaces(registry codectypes.InterfaceRegistry)`: Registers interface types and implementations of each of the application's `AppModuleBasic`. -- `DefaultGenesis(cdc codec.JSONCodec)`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis(cdc codec.JSONCodec)`](./08-genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application. -- `ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage)`](./08-genesis.md#validategenesis) function of each module. -- `RegisterRESTRoutes(ctx client.Context, rtr *mux.Router)`: Registers REST routes for modules by calling the [`RegisterRESTRoutes`](./09-module-interfaces.md#register-routes) function of each module. This function is usually called function from the `main.go` function of the [application's command-line interface](../core/06-cli.md). -- `RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux)`: Registers gRPC routes for modules. -- `AddTxCommands(rootTxCmd *cobra.Command)`: Adds modules' transaction commands to the application's [`rootTxCommand`](../core/06-cli.md#transaction-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../core/06-cli.md). -- `AddQueryCommands(rootQueryCmd *cobra.Command)`: Adds modules' query commands to the application's [`rootQueryCommand`](../core/06-cli.md#query-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../core/06-cli.md). - -### `Manager` - -The `Manager` is a structure that holds all the `AppModule` of an application, and defines the order of execution between several key components of these modules: - -+++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L223-L231 - -The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods: - -- `NewManager(modules ...AppModule)`: Constructor function. It takes a list of the application's `AppModule`s and builds a new `Manager`. It is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -- `SetOrderInitGenesis(moduleNames ...string)`: Sets the order in which the [`InitGenesis`](./08-genesis.md#initgenesis) function of each module will be called when the application is first started. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -- `SetOrderExportGenesis(moduleNames ...string)`: Sets the order in which the [`ExportGenesis`](./08-genesis.md#exportgenesis) function of each module will be called in case of an export. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -- `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the end of each block. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -- `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./07-invariants.md) of each module. -- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers legacy [`Msg`](./02-messages-and-queries.md#messages) and [`querier`](./04-query-services.md#legacy-queriers) routes. -- `RegisterServices(cfg Configurator)`: Registers all module services. -- `InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./08-genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates. -- `ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec)`: Calls the [`ExportGenesis`](./08-genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required. -- `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/02-context.md) with an event manager to aggregate [events](../core/07-events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events. -- `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/02-context.md) with an event manager to aggregate [events](../core/07-events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any). - -Here's an example of a concrete integration within an application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/2323f1ac0e9a69a0da6b43693061036134193464/simapp/app.go#L315-L362 - -## Next {hide} - -Learn more about [`message`s and `queries`](./02-messages-and-queries.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/02-messages-and-queries.md b/versioned_docs/version-0.45/integrate/building-modules/02-messages-and-queries.md deleted file mode 100644 index 91a695e0b..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/02-messages-and-queries.md +++ /dev/null @@ -1,111 +0,0 @@ -# Messages and Queries - -`Msg`s and `Queries` are the two primary objects handled by modules. Most of the core components defined in a module, like `Msg` services, `keeper`s and `Query` services, exist to process `message`s and `queries`. {synopsis} - -## Pre-requisite Readings - -- [Introduction to SDK Modules](./intro.md) {prereq} - -## Messages - -`Msg`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../advanced-concepts/01-transactions.md), which may contain one or more of them. - -When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md). Then, each message contained in the transaction is extracted and routed to the appropriate module via `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](./03-msg-services.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../high-level-concepts/01-tx-lifecycle.md). - -### `Msg` Services - -Starting from v0.40, defining Protobuf `Msg` services is the recommended way to handle messages. A Protobuf `Msg` service should be created for each module, typically in `tx.proto` (see more info about [conventions and naming](../advanced-concepts/05-encoding.md#faq)). It must have an RPC service method defined for each message in the module. - -See an example of a `Msg` service definition from `x/bank` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L10-L17 - -Each `Msg` service method must have exactly one argument, which must implement the `sdk.Msg` interface, and a Protobuf response. The naming convention is to call the RPC argument `Msg` and the RPC response `MsgResponse`. For example: - -``` - rpc Send(MsgSend) returns (MsgSendResponse); -``` - -`sdk.Msg` interface is a simplified version of the Amino `LegacyMsg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods. For backwards compatibility with [Amino `LegacyMsg`s](#legacy-amino-msgs), existing `LegacyMsg` types should be used as the request parameter for `service` RPC definitions. Newer `sdk.Msg` types, which only support `service` definitions, should use canonical `Msg...` name. - -Cosmos SDK uses Protobuf definitions to generate client and server code: - -* `MsgServer` interface defines the server API for the `Msg` service and its implementation is described as part of the [`Msg` services](./03-msg-services.md) documentation. -* Structures are generated for all RPC request and response types. - -A `RegisterMsgServer` method is also generated and should be used to register the module's `MsgServer` implementation in `RegisterServices` method from the [`AppModule` interface](./01-module-manager.md#appmodule). - -In order for clients (CLI and grpc-gateway) to have these URLs registered, the SDK provides the function `RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc)` that should be called inside module's [`RegisterInterfaces`](module-manager.md#appmodulebasic) method, using the proto-generated `&_Msg_serviceDesc` as `*grpc.ServiceDesc` argument. - -### Legacy Amino `LegacyMsg`s - -The following way of defining messages is deprecated and using [`Msg` services](#msg-services) is preferred. - -Amino `LegacyMsg`s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message. - -A `LegacyMsg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./09-module-interfaces.md). `message`s also need to implement the `sdk.Msg` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/4a1b2fba43b1052ca162b3a1e0b6db6db9c26656/types/tx_msg.go#L10-L33 - -It extends `proto.Message` and contains the following methods: - -- `Route() string`: Name of the route for this message. Typically all `message`s in a module have the same route, which is most often the module's name. -- `Type() string`: Type of the message, used primarly in [events](../advanced-concepts/07-events.md). This should return a message-specific `string`, typically the denomination of the message itself. -- [`ValidateBasic() error`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic). -- `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature. -- `GetSigners() []AccAddress`: Return the list of signers. The SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method. - -See an example implementation of a `message` from the `gov` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/gov/types/msgs.go#L77-L125 - -## Queries - -A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `BaseApp`'s `queryrouter` so that it can be processed by the module's query service (./04-query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../high-level-concepts/query-lifecycle.md). - -### gRPC Queries - -Starting from v0.40, the prefered way to define queries is by using [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services). A `Query` service should be created per module in `query.proto`. This service lists endpoints starting with `rpc`. - -Here's an example of such a `Query` service definition: - -+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/auth/v1beta1/query.proto#L12-L23 - -As `proto.Message`s, generated `Response` types implement by default `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). - -A `RegisterQueryServer` method is also generated and should be used to register the module's query server in the `RegisterServices` method from the [`AppModule` interface](./01-module-manager.md#appmodule). - -### Legacy Queries - -Before the introduction of Protobuf and gRPC in the SDK, there was usually no specific `query` object defined by module developers, contrary to `message`s. Instead, the SDK took the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following: - -``` -queryCategory/queryRoute/queryType/arg1/arg2/... -``` - -where: - -- `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `BaseApp`'s [`Query` method](../../develop/advanced-concepts/00-baseapp.md#query). -- `queryRoute` is used by `BaseApp`'s [`queryRouter`](../../develop/advanced-concepts/00-baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module. -- `queryType` is used by the module's [`querier`](./04-query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module. -- `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`. - -The `path` for each `query` must be defined by the module developer in the module's [command-line interface file](./09-module-interfaces.md#query-commands).Overall, there are 3 mains components module developers need to implement in order to make the subset of the state defined by their module queryable: - -- A [`querier`](./04-query-services.md#legacy-queriers), to process the `query` once it has been [routed to the module](../../develop/advanced-concepts/00-baseapp.md#query-routing). -- [Query commands](./09-module-interfaces.md#query-commands) in the module's CLI file, where the `path` for each `query` is specified. -- `query` return types. Typically defined in a file `types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). - -### Store Queries - -Store queries query directly for store keys. They use `clientCtx.QueryABCI(req abci.RequestQuery)` to return the full `abci.ResponseQuery` with inclusion Merkle proofs. - -See following examples: - -+++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/x/ibc/advanced-concepts/client/query.go#L36-L46 - -+++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/baseapp/abci.go#L722-L749 - -## Next {hide} - -Learn about [`Msg` services](./03-msg-services.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/03-msg-services.md b/versioned_docs/version-0.45/integrate/building-modules/03-msg-services.md deleted file mode 100644 index 7bf77b70a..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/03-msg-services.md +++ /dev/null @@ -1,130 +0,0 @@ -# `Msg` Services - -A Protobuf `Msg` service processes [messages](./02-messages-and-queries.md#messages). Protobuf `Msg` services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from `BaseApp` during [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx). {synopsis} - -## Pre-requisite Readings - -- [Module Manager](./01-module-manager.md) {prereq} -- [Messages and Queries](./02-messages-and-queries.md) {prereq} - -## Implementation of a module `Msg` service - -Each module should define a Protobuf `Msg` service, which will be responsible for processing requests (implementing `sdk.Msg`) and returning responses. - -As further described in [ADR 031](../architecture/adr-031-msg-service.md), this approach has the advantage of clearly specifying return types and generating server and client code. - -Protobuf generates a `MsgServer` interface based on a definition of `Msg` service. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each `sdk.Msg`. As an example, here is the generated `MsgServer` interface for `x/bank`, which exposes two `sdk.Msg`s: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/bank/types/tx.pb.go#L285-L291 - -When possible, the existing module's [`Keeper`](keeper.md) should implement `MsgServer`, otherwise a `msgServer` struct that embeds the `Keeper` can be created, typically in `./keeper/msg_server.go`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L14-L16 - -`msgServer` methods can retrieve the `sdk.Context` from the `context.Context` parameter method using the `sdk.UnwrapSDKContext`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L27-L28 - -`sdk.Msg` processing usually follows these 3 steps: - -### Validation - -Before a `msgServer` method is executed, the message's [`ValidateBasic()`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic) method has already been called. Since `msg.ValidateBasic()` performs only the most basic checks, this stage must perform all other validation (both *stateful* and *stateless*) to make sure the `message` is valid. Checks performed in the `msgServer` method can be more expensive and the signer is charged gas for these operations. -For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. - -It is recommended to implement all validation checks in a separate function that passes state values as arguments. This implementation simplifies testing. As expected, expensive validation functions charge additional gas. Example: - -```go -ValidateMsgA(msg MsgA, now Time, gm GasMeter) error { - if now.Before(msg.Expire) { - return sdkerrrors.ErrInvalidRequest.Wrap("msg expired") - } - gm.ConsumeGas(1000, "signature verification") - return signatureVerificaton(msg.Prover, msg.Data) -} -``` - -### State Transition - -After the validation is successful, the `msgServer` method uses the [`keeper`](./06-keeper.md) functions to access the state and perform a state transition. - -### Events - -Before returning, `msgServer` methods generally emit one or more [events](../advanced-concepts/07-events.md) by using the `EventManager` held in the `ctx`. Use the new `EmitTypedEvent` function that uses protobuf-based event types: - -``` -ctx.EventManager().EmitTypedEvent( - &group.EventABC{Key1: Value1, Key2, Value2}) -``` - -or the older `EmitEvent` function: - -```go -ctx.EventManager().EmitEvent( - sdk.NewEvent( - eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module - sdk.NewAttribute(key1, value1), - sdk.NewAttribute(key2, value2), - ), -) -``` - -These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../advanced-concepts/07-events.md) to learn more about events. - -The invoked `msgServer` method returns a `proto.Message` response and an `error`. These return values are then wrapped into an `*sdk.Result` or an `error` using `sdk.WrapServiceResult(ctx sdk.Context, res proto.Message, err error)`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/baseapp/msg_service_router.go#L104-L104 - -This method takes care of marshaling the `res` parameter to protobuf and attaching any events on the `ctx.EventManager()` to the `sdk.Result`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95 - -This diagram shows a typical structure of a Protobuf `Msg` service, and how the message propagates through the module. - -![Transaction flow](transaction_flow.svg) - -## Amino `LegacyMsg`s - -### `handler` type - -The `handler` type defined in the Cosmos SDK will be deprecated in favor of [`Msg` Services](#implementation-of-a-module-msg-service). - -Here is the typical structure of a `handler` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/types/handler.go#L4-L4 - -Let us break it down: - -- The [`LegacyMsg`](./02-messages-and-queries.md#messages) is the actual object being processed. -- The [`Context`](../advanced-concepts/02-context.md) contains all the necessary information needed to process the `msg`, as well as a branch of the latest state. If the `msg` is successfully processed, the branched version of the state contained in the `ctx` will be written to the main state (branch). -- The `*Result` returned to `BaseApp` contains (among other things) information on the execution of the `handler` and [events](../advanced-concepts/07-events.md). - -Module `handler`s are typically implemented in a `./handler.go` file inside the module's folder. The [module manager](./01-module-manager.md) is used to add the module's `handler`s to the -[application's `router`](../../develop/advanced-concepts/00-baseapp.md#message-routing) via the `Route()` method. Typically, -the manager's `Route()` method simply constructs a Route that calls a `NewHandler()` method defined in `handler.go`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/x/gov/module.go#L143-L146 - -### Implementation - -`NewHandler` function dispatches a `LegacyMsg` to appropriate handler function, usually by using a switch statement: - -+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/handler.go#L13-L29 - -First, `NewHandler` function sets a new `EventManager` to the context to isolate events per `msg`. -Then, a simple switch calls the appropriate `handler` based on the `LegacyMsg` type. - -In this regard, `handler`s functions need to be implemented for each module `LegacyMsg`. This will also involve manual handler registration of `LegacyMsg` types. -`handler`s functions should return a `*Result` and an `error`. - -## Telemetry - -New [telemetry metrics](../advanced-concepts/11-telemetry.md) can be created from `msgServer` methods when handling messages. - -This is an example from the `x/auth/vesting` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/auth/vesting/msg_server.go#L73-L85 - -## Next {hide} - -Learn about [query services](./04-query-services.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/04-query-services.md b/versioned_docs/version-0.45/integrate/building-modules/04-query-services.md deleted file mode 100644 index 189c1b4b5..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/04-query-services.md +++ /dev/null @@ -1,73 +0,0 @@ -# Query Services - -A Protobuf Query service processes [`queries`](./02-messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `BaseApp`'s [`Query` method](../../develop/advanced-concepts/00-baseapp.md#query). {synopsis} - -## Pre-requisite Readings - -- [Module Manager](./01-module-manager.md) {prereq} -- [Messages and Queries](./02-messages-and-queries.md) {prereq} - -## `Querier` type - -The `querier` type defined in the Cosmos SDK will be deprecated in favor of [gRPC Services](#grpc-service). It specifies the typical structure of a `querier` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/9a183ffbcc0163c8deb71c7fd5f8089a83e58f05/types/queryable.go#L9 - -Let us break it down: - -- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./02-messages-and-queries.md#queries) for more information. -- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. -- The [`Context`](../advanced-concepts/02-context.md) contains all the necessary information needed to process the `query`, as well as a branch of the latest state. It is primarily used by the [`keeper`](./06-keeper.md) to access the state. -- The result `res` returned to `BaseApp`, marshalled using the application's [`codec`](../advanced-concepts/05-encoding.md). - -## Implementation of a module query service - -### gRPC Service - -When defining a Protobuf `Query` service, a `QueryServer` interface is generated for each module with all the service methods: - -```go -type QueryServer interface { - QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) - QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) -} -``` - -These custom queries methods should be implemented by a module's keeper, typically in `./keeper/grpc_query.go`. The first parameter of these methods is a generic `context.Context`, whereas querier methods generally need an instance of `sdk.Context` to read -from the store. Therefore, the SDK provides a function `sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided -`context.Context`. - -Here's an example implementation for the bank module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/keeper/grpc_query.go - -### Legacy Queriers - -Module legacy `querier`s are typically implemented in a `./keeper/querier.go` file inside the module's folder. The [module manager](./01-module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../../develop/advanced-concepts/00-baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following: - -```go -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - case QueryType1: - return queryType1(ctx, path[1:], req, keeper) - - case QueryType2: - return queryType2(ctx, path[1:], req, keeper) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} -``` - -This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../high-level-concepts/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query. - -The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./06-keeper.md). Then, they marshall the value(s) using the [`codec`](../advanced-concepts/05-encoding.md) and return the `[]byte` obtained as result. - -For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/gov/keeper/querier.go) from the bank module. - -## Next {hide} - -Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/05-beginblock-endblock.md b/versioned_docs/version-0.45/integrate/building-modules/05-beginblock-endblock.md deleted file mode 100644 index 233ada4cf..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/05-beginblock-endblock.md +++ /dev/null @@ -1,35 +0,0 @@ -# BeginBlocker and EndBlocker - -`BeginBlocker` and `EndBlocker` are optional methods module developers can implement in their module. They will be triggered at the beginning and at the end of each block respectively, when the [`BeginBlock`](../../develop/advanced-concepts/00-baseapp.md#beginblock) and [`EndBlock`](../../develop/advanced-concepts/00-baseapp.md#endblock) ABCI messages are received from the underlying consensus engine. {synopsis} - -## Pre-requisite Readings - -- [Module Manager](./01-module-manager.md) {prereq} - -## BeginBlocker and EndBlocker - -`BeginBlocker` and `EndBlocker` are a way for module developers to add automatic execution of logic to their module. This is a powerful tool that should be used carefully, as complex automatic functions can slow down or even halt the chain. - -When needed, `BeginBlocker` and `EndBlocker` are implemented as part of the [`AppModule` interface](./01-module-manager.md#appmodule). The `BeginBlock` and `EndBlock` methods of the interface implemented in `module.go` generally defer to `BeginBlocker` and `EndBlocker` methods respectively, which are usually implemented in `abci.go`. - -The actual implementation of `BeginBlocker` and `EndBlocker` in `abci.go` are very similar to that of a [`Msg` service](./03-msg-services.md): - -- They generally use the [`keeper`](./06-keeper.md) and [`ctx`](../01-tx-lifecycle.md02-context.md) to retrieve information about the latest state. -- If needed, they use the `keeper` and `ctx` to trigger state-transitions. -- If needed, they can emit [`events`](../01-tx-lifecycle.md07-events.md) via the `ctx`'s `EventManager`. - -A specificity of the `EndBlocker` is that it can return validator updates to the underlying consensus engine in the form of an [`[]abci.ValidatorUpdates`](https://tendermint.com/docs/app-dev/abci-spec.html#validatorupdate). This is the preferred way to implement custom validator changes. - -It is possible for developers to define the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./01-module-manager.md#manager). - -See an example implementation of `BeginBlocker` from the `distr` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/distribution/abci.go#L14-L38 - -and an example implementation of `EndBlocker` from the `staking` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/staking/abci.go#L22-L27 - -## Next {hide} - -Learn about [`keeper`s](./06-keeper.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/06-keeper.md b/versioned_docs/version-0.45/integrate/building-modules/06-keeper.md deleted file mode 100644 index fb66377aa..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/06-keeper.md +++ /dev/null @@ -1,81 +0,0 @@ -# Keepers - -`Keeper`s refer to a Cosmos SDK abstraction whose role is to manage access to the subset of the state defined by various modules. `Keeper`s are module-specific, i.e. the subset of state defined by a module can only be accessed by a `keeper` defined in said module. If a module needs to access the subset of state defined by another module, a reference to the second module's internal `keeper` needs to be passed to the first one. This is done in `app.go` during the instantiation of module keepers. {synopsis} - -## Pre-requisite Readings - -- [Introduction to SDK Modules](./intro.md) {prereq} - -## Motivation - -The Cosmos SDK is a framework that makes it easy for developers to build complex decentralised applications from scratch, mainly by composing modules together. As the ecosystem of open source modules for the Cosmos SDK expands, it will become increasingly likely that some of these modules contain vulnerabilities, as a result of the negligence or malice of their developer. - -The Cosmos SDK adopts an [object-capabilities-based approach](../advanced-concepts/ocap.md) to help developers better protect their application from unwanted inter-module interactions, and `keeper`s are at the core of this approach. A `keeper` can be thought of quite literally as the gatekeeper of a module's store(s). Each store (typically an [`IAVL` Store](../advanced-concepts/04-store.md#iavl-store)) defined within a module comes with a `storeKey`, which grants unlimited access to it. The module's `keeper` holds this `storeKey` (which should otherwise remain unexposed), and defines [methods](#implementing-methods) for reading and writing to the store(s). - -The core idea behind the object-capabilities approach is to only reveal what is necessary to get the work done. In practice, this means that instead of handling permissions of modules through access-control lists, module `keeper`s are passed a reference to the specific instance of the other modules' `keeper`s that they need to access (this is done in the [application's constructor function](../high-level-concepts/app-anatomy.md#constructor-function)). As a consequence, a module can only interact with the subset of state defined in another module via the methods exposed by the instance of the other module's `keeper`. This is a great way for developers to control the interactions that their own module can have with modules developed by external developers. - -## Type Definition - -`keeper`s are generally implemented in a `/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure: - -```go -type Keeper struct { - // External keepers, if any - - // Store key(s) - - // codec -} -``` - -For example, here is the type definition of the `keeper` from the `staking` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go#L23-L33 - -Let us go through the different parameters: - -- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in an `expected_keepers.go` file in the root of the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself. -- `storeKey`s grant access to the store(s) of the [multistore](../advanced-concepts/04-store.md) managed by the module. They should always remain unexposed to external modules. -- `cdc` is the [codec](../advanced-concepts/05-encoding.md) used to marshall and unmarshall structs to/from `[]byte`. The `cdc` can be any of `codec.BinaryCodec`, `codec.JSONCodec` or `codec.Codec` based on your requirements. It can be either a proto or amino codec as long as they implement these interfaces. - -Of course, it is possible to define different types of internal `keeper`s for the same module (e.g. a read-only `keeper`). Each type of `keeper` comes with its own constructor function, which is called from the [application's constructor function](../high-level-concepts/app-anatomy.md). This is where `keeper`s are instantiated, and where developers make sure to pass correct instances of modules' `keeper`s to other modules that require them. - -## Implementing Methods - -`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./02-messages-and-queries.md#messages) and the [`Msg` server](./03-msg-services.md) when `keeper`s' methods are called. - -Typically, a *getter* method will have the following signature - -```go -func (k Keeper) Get(ctx sdk.Context, key string) returnType -``` - -and the method will go through the following steps: - -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. -2. If it exists, get the `[]byte` value stored at location `[]byte(key)` using the `Get(key []byte)` method of the store. -3. Unmarshall the retrieved value from `[]byte` to `returnType` using the codec `cdc`. Return the value. - -Similarly, a *setter* method will have the following signature - -```go -func (k Keeper) Set(ctx sdk.Context, key string, value valueType) -``` - -and the method will go through the following steps: - -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. It's preferred to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. -2. Marshal `value` to `[]byte` using the codec `cdc`. -3. Set the encoded value in the store at location `key` using the `Set(key []byte, value []byte)` method of the store. - -For more, see an example of `keeper`'s [methods implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go). - -The [module `KVStore`](../core/04-store.md#kvstore-and-commitkvstore-interfaces) also provides an `Iterator()` method which returns an `Iterator` object to iterate over a domain of keys. - -This is an example from the `auth` module to iterate accounts: - -+++ https://github.com/cosmos/cosmos-sdk/blob/bf8809ef9840b4f5369887a38d8345e2380a567f/x/auth/keeper/account.go#L70-L83 - -## Next {hide} - -Learn about [invariants](./07-invariants.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/07-invariants.md b/versioned_docs/version-0.45/integrate/building-modules/07-invariants.md deleted file mode 100644 index 6525111f2..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/07-invariants.md +++ /dev/null @@ -1,84 +0,0 @@ -# Invariants - -An invariant is a property of the application that should always be true. In the context of the Cosmos SDK, an `Invariant` is a function that checks for a particular invariant. These functions are useful to detect bugs early on and act upon them to limit their potential consequences (e.g. by halting the chain). They are also useful in the development process of the application to detect bugs via simulations. {synopsis} - -## Pre-requisite Readings - -- [Keepers](./06-keeper.md) {prereq} - -## Implementing `Invariant`s - -An `Invariant` is a function that checks for a particular invariant within a module. Module `Invariant`s must follow the `Invariant` type: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/invariant.go#L9 - -The `string` return value is the invariant message, which can be used when printing logs, and the `bool` return value is the actual result of the invariant check. - -In practice, each module implements `Invariant`s in a `./keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: - -```go -// Example for an Invariant that checks balance-related invariants - -func BalanceInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - // Implement checks for balance-related invariants - } -} -``` - -Additionally, module developers should generally implement an `AllInvariants` function that runs all the `Invariant`s functions of the module: - -```go -// AllInvariants runs all invariants of the module. -// In this example, the module implements two Invariants: BalanceInvariants and DepositsInvariants - -func AllInvariants(k Keeper) sdk.Invariant { - - return func(ctx sdk.Context) (string, bool) { - res, stop := BalanceInvariants(k)(ctx) - if stop { - return res, stop - } - - return DepositsInvariant(k)(ctx) - } -} -``` - -Finally, module developers need to implement the `RegisterInvariants` method as part of the [`AppModule` interface](./01-module-manager.md#appmodule). Indeed, the `RegisterInvariants` method of the module, implemented in the `module/module.go` file, typically only defers the call to a `RegisterInvariants` method implemented in the `keeper/invariants.go` file. The `RegisterInvariants` method registers a route for each `Invariant` function in the [`InvariantRegistry`](#invariant-registry): - -```go -// RegisterInvariants registers all staking invariants -func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, "module-accounts", - BalanceInvariants(k)) - ir.RegisterRoute(types.ModuleName, "nonnegative-power", - DepositsInvariant(k)) -} -``` - -For more, see an example of [`Invariant`s implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/keeper/invariants.go). - -## Invariant Registry - -The `InvariantRegistry` is a registry where the `Invariant`s of all the modules of an application are registered. There is only one `InvariantRegistry` per **application**, meaning module developers need not implement their own `InvariantRegistry` when building a module. **All module developers need to do is to register their modules' invariants in the `InvariantRegistry`, as explained in the section above**. The rest of this section gives more information on the `InvariantRegistry` itself, and does not contain anything directly relevant to module developers. - -At its core, the `InvariantRegistry` is defined in the SDK as an interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/invariant.go#L14-L17 - -Typically, this interface is implemented in the `keeper` of a specific module. The most used implementation of an `InvariantRegistry` can be found in the `crisis` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/crisis/keeper/keeper.go#L50-L54 - - The `InvariantRegistry` is therefore typically instantiated by instantiating the `keeper` of the `crisis` module in the [application's constructor function](../high-level-concepts/app-anatomy.md#constructor-function). - -`Invariant`s can be checked manually via [`message`s](./02-messages-and-queries.md), but most often they are checked automatically at the end of each block. Here is an example from the `crisis` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/crisis/abci.go#L7-L14 - -In both cases, if one of the `Invariant`s returns false, the `InvariantRegistry` can trigger special logic (e.g. have the application panic and print the `Invariant`s message in the log). - -## Next {hide} - -Learn about [genesis functionalities](./08-genesis.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/08-genesis.md b/versioned_docs/version-0.45/integrate/building-modules/08-genesis.md deleted file mode 100644 index 33a2e10d7..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/08-genesis.md +++ /dev/null @@ -1,56 +0,0 @@ -# Module Genesis - -Modules generally handle a subset of the state and, as such, they need to define the related subset of the genesis file as well as methods to initialize, verify and export it. {synopsis} - -## Pre-requisite Readings - -- [Module Manager](./01-module-manager.md) {prereq} -- [Keepers](./06-keeper.md) {prereq} - -## Type Definition - -The subset of the genesis state defined from a given module is generally defined in a `genesis.proto` file ([more info](../advanced-concepts/05-encoding.md#gogoproto) on how to define protobuf messages). The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. - -See an example of `GenesisState` protobuf message definition from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/a9547b54ffac9729fe1393651126ddfc0d236cff/proto/cosmos/auth/v1beta1/genesis.proto - -Next we present the main genesis-related methods that need to be implemented by module developers in order for their module to be used in Cosmos SDK applications. - -### `DefaultGenesis` - -The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L48-L52 - -### `ValidateGenesis` - -The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameters listed in `GenesisState`. See an example from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/types/genesis.go#L57-L70 - -## Other Genesis Methods - -Other than the methods related directly to `GenesisState`, module developers are expected to implement two other methods as part of the [`AppModuleGenesis` interface](./01-module-manager.md#appmodulegenesis) (only if the module needs to initialize a subset of state in genesis). These methods are [`InitGenesis`](#initgenesis) and [`ExportGenesis`](#exportgenesis). - -### `InitGenesis` - -The `InitGenesis` method is executed during [`InitChain`](../../develop/advanced-concepts/00-baseapp.md#initchain) when the application is first started. Given a `GenesisState`, it initializes the subset of the state managed by the module by using the module's [`keeper`](./06-keeper.md) setter function on each parameter within the `GenesisState`. - -The [module manager](./01-module-manager.md#manager) of the application is responsible for calling the `InitGenesis` method of each of the application's modules in order. This order is set by the application developer via the manager's `SetOrderGenesisMethod`, which is called in the [application's constructor function](../high-level-concepts/app-anatomy.md#constructor-function). - -See an example of `InitGenesis` from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L13-L28 - -### `ExportGenesis` - -The `ExportGenesis` method is executed whenever an export of the state is made. It takes the latest known version of the subset of the state managed by the module and creates a new `GenesisState` out of it. This is mainly used when the chain needs to be upgraded via a hard fork. - -See an example of `ExportGenesis` from the `auth` module. - -+++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L31-L42 - -## Next {hide} - -Learn about [modules interfaces](module-interfaces.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/09-module-interfaces.md b/versioned_docs/version-0.45/integrate/building-modules/09-module-interfaces.md deleted file mode 100644 index 195db13de..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/09-module-interfaces.md +++ /dev/null @@ -1,135 +0,0 @@ -# Module Interfaces - -This document details how to build CLI and REST interfaces for a module. Examples from various SDK modules are included. {synopsis} - -## Prerequisite Readings - -- [Building Modules Intro](./intro.md) {prereq} - -## CLI - -One of the main interfaces for an application is the [command-line interface](../advanced-concepts/06-cli.md). This entrypoint adds commands from the application's modules enabling end-users to create [**messages**](./02-messages-and-queries.md#messages) wrapped in transactions and [**queries**](./02-messages-and-queries.md#queries). The CLI files are typically found in the module's `./client/cli` folder. - -### Transaction Commands - - In order to create messages that trigger state changes, end-users must create [transactions](../core/01-transactions.md) that wrap and deliver the messages. A transaction command creates a transaction that includes one or more messages. - - Transaction commands typically have their own `tx.go` file that lives within the module's `./client/cli` folder. The commands are specified in getter functions and the name of the function should include the name of the command. - -Here is an example from the `x/bank` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/bank/client/cli/tx.go#L28-L63 - -In the example, `NewSendTxCmd()` creates and returns the transaction command for a transaction that wraps and delivers `MsgSend`. `MsgSend` is the message used to send tokens from one account to another. - -In general, the getter function does the following: - -- **Constructs the command:** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) for more detailed information on how to create commands. - - **Use:** Specifies the format of the user input required to invoke the command. In the example above, `send` is the name of the transaction command and `[from_key_or_address]`, `[to_address]`, and `[amount]` are the arguments. - - **Args:** The number of arguments the user provides. In this case, there are exactly three: `[from_key_or_address]`, `[to_address]`, and `[amount]`. - - **Short and Long:** Descriptions for the command. A `Short` description is expected. A `Long` description can be used to provide additional information that is displayed when a user adds the `--help` flag. - - **RunE:** Defines a function that can return an error. This is the function that is called when the command is executed. This function encapsulates all of the logic to create a new transaction. - - The function typically starts by getting the `clientCtx`, which can be done with `client.GetClientTxContext(cmd)`. The `clientCtx` contains information relevant to transaction handling, including information about the user. In this example, the `clientCtx` is used to retrieve the address of the sender by calling `clientCtx.GetFromAddress()`. - - If applicable, the command's arguments are parsed. In this example, the arguments `[to_address]` and `[amount]` are both parsed. - - A [message](./02-messages-and-queries.md) is created using the parsed arguments and information from the `clientCtx`. The constructor function of the message type is called directly. In this case, `types.NewMsgSend(fromAddr, toAddr, amount)`. Its good practice to call [`msg.ValidateBasic()`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic) and other validation methods before broadcasting the message. - - Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `tx.GenerateOrBroadcastTxCLI(clientCtx, flags, msg)`. -- **Adds transaction flags:** All transaction commands must add a set of transaction [flags](#flags). The transaction flags are used to collect additional information from the user (e.g. the amount of fees the user is willing to pay). The transaction flags are added to the constructed command using `AddTxFlagsToCmd(cmd)`. -- **Returns the command:** Finally, the transaction command is returned. - -Each module must implement `NewTxCmd()`, which aggregates all of the transaction commands of the module. Here is an example from the `x/bank` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/bank/client/cli/tx.go#L13-L26 - -Each module must also implement the `GetTxCmd()` method for `AppModuleBasic` that simply returns `NewTxCmd()`. This allows the root command to easily aggregate all of the transaction commands for each module. Here is an example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/bank/module.go#L75-L78 - -### Query Commands - -[Queries](./02-messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module's `./client/cli` folder. Like transaction commands, they are specified in getter functions. Here is an example of a query command from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/auth/client/cli/query.go#L75-L105 - -In the example, `GetAccountCmd()` creates and returns a query command that returns the state of an account based on the provided account address. - -In general, the getter function does the following: - -- **Constructs the command:** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) for more detailed information on how to create commands. - - **Use:** Specifies the format of the user input required to invoke the command. In the example above, `account` is the name of the query command and `[address]` is the argument. - - **Args:** The number of arguments the user provides. In this case, there is exactly one: `[address]`. - - **Short and Long:** Descriptions for the command. A `Short` description is expected. A `Long` description can be used to provide additional information that is displayed when a user adds the `--help` flag. - - **RunE:** Defines a function that can return an error. This is the function that is called when the command is executed. This function encapsulates all of the logic to create a new query. - - The function typically starts by getting the `clientCtx`, which can be done with `client.GetClientQueryContext(cmd)`. The `clientCtx` contains information relevant to query handling. - - If applicable, the command's arguments are parsed. In this example, the argument `[address]` is parsed. - - A new `queryClient` is initialized using `NewQueryClient(clientCtx)`. The `queryClient` is then used to call the appropriate [query](./02-messages-and-queries.md#grpc-queries). - - The `clientCtx.PrintProto` method is used to format the `proto.Message` object so that the results can be printed back to the user. -- **Adds query flags:** All query commands must add a set of query [flags](#flags). The query flags are added to the constructed command using `AddQueryFlagsToCmd(cmd)`. -- **Returns the command:** Finally, the query command is returned. - -Each module must implement `GetQueryCmd()`, which aggregates all of the query commands of the module. Here is an example from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/auth/client/cli/query.go#L25-L42 - -Each module must also implement the `GetQueryCmd()` method for `AppModuleBasic` that returns the `GetQueryCmd()` function. This allows for the root command to easily aggregate all of the query commands for each module. Here is an example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/auth/module.go#L80-L83 - -### Flags - -[Flags](../core/06-cli.md#flags) allow users to customize commands. `--fees` and `--gas-prices` are examples of flags that allow users to set the [fees](../high-level-concepts/gas-fees.md) and gas prices for their transactions. - -Flags that are specific to a module are typically created in a `flags.go` file in the module's `./client/cli` folder. When creating a flag, developers set the value type, the name of the flag, the default value, and a description about the flag. Developers also have the option to mark flags as _required_ so that an error is thrown if the user does not include a value for the flag. - -Here is an example that adds the `--from` flag to a command: - -```go -cmd.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") -``` - -In this example, the value of the flag is a `String`, the name of the flag is `from` (the value of the `FlagFrom` constant), the default value of the flag is `""`, and there is a description that will be displayed when a user adds `--help` to the command. - -Here is an example that marks the `--from` flag as _required_: - -```go -cmd.MarkFlagRequired(FlagFrom) -``` - -For more detailed information on creating flags, visit the [Cobra Documentation](https://github.com/spf13/cobra). - -As mentioned in [transaction commands](#transaction-commands), there is a set of flags that all transaction commands must add. This is done with the `AddTxFlagsToCmd` method defined in the SDK's `./client/flags` package. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/client/flags/flags.go#L94-L120 - -Since `AddTxFlagsToCmd(cmd *cobra.Command)` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). - -Similarly, there is a `AddQueryFlagsToCmd(cmd *cobra.Command)` to add common flags to a module query command. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/client/flags/flags.go#L85-L92 - -## gRPC - -[gRPC](https://grpc.io/) is a Remote Procedure Call (RPC) framework. RPC is the preferred way for external clients like wallets and exchanges to interact with a blockchain. - -In addition to providing an ABCI query pathway, the Cosmos SDK provides a gRPC proxy server that routes gRPC query requests to ABCI query requests. - -In order to do that, modules must implement `RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)` on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. - -Here's an example from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/x/auth/module.go#L68-L73 - -## gRPC-gateway REST - -Applications need to support web services that use HTTP requests (e.g. a web wallet like [Keplr](https://keplr.xyz)). [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) translates REST calls into gRPC calls, which might be useful for clients that do not use gRPC. - -Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods, such as in the example below from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/proto/cosmos/auth/v1beta1/query.proto#L13-L29 - -gRPC gateway is started in-process along with the application and Tendermint. It can be enabled or disabled by setting gRPC Configuration `enable` in [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). - -The SDK provides a command for generating [Swagger](https://swagger.io/) documentation (`protoc-gen-swagger`). Setting `swagger` in [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) defines if swagger documentation should be automatically registered. - -## Next {hide} - -Read about the recommended [module structure](./11-structure.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/10-structure.md b/versioned_docs/version-0.45/integrate/building-modules/10-structure.md deleted file mode 100644 index a43b946c2..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/10-structure.md +++ /dev/null @@ -1,95 +0,0 @@ -# Recommended Folder Structure - -This document outlines the recommended structure of Cosmos SDK modules. These ideas are meant to be applied as suggestions. Application developers are encouraged to improve upon and contribute to module structure and development design. {synopsis} - -## Structure - -A typical Cosmos SDK module can be structured as follows: - -```shell -proto -└── {project_name} -    └── {module_name} -    └── {proto_version} -       ├── {module_name}.proto -       ├── event.proto -       ├── genesis.proto -       ├── query.proto -       └── tx.proto -``` - -- `{module_name}.proto`: The module's common message type definitions. -- `event.proto`: The module's message type definitions related to events. -- `genesis.proto`: The module's message type definitions related to genesis state. -- `query.proto`: The module's Query service and related message type definitions. -- `tx.proto`: The module's Msg service and related message type definitions. - -```shell -x/{module_name} -├── client -│   ├── cli -│   │ ├── query.go -│   │   └── tx.go -│   └── testutil -│   ├── cli_test.go -│   └── suite.go -├── exported -│   └── exported.go -├── keeper -│   ├── genesis.go -│   ├── grpc_query.go -│   ├── hooks.go -│   ├── invariants.go -│   ├── keeper.go -│   ├── keys.go -│   ├── msg_server.go -│   └── querier.go -├── module -│   └── module.go -├── simulation -│   ├── decoder.go -│   ├── genesis.go -│   ├── operations.go -│   └── params.go -├── spec -│   ├── 01_concepts.md -│   ├── 02_state.md -│   ├── 03_messages.md -│   └── 04_events.md -├── {module_name}.pb.go -├── abci.go -├── codec.go -├── errors.go -├── events.go -├── events.pb.go -├── expected_keepers.go -├── genesis.go -├── genesis.pb.go -├── keys.go -├── msgs.go -├── params.go -├── query.pb.go -└── tx.pb.go -``` - -- `client/`: The module's CLI client functionality implementation and the module's integration testing suite. -- `exported/`: The module's exported types - typically interface types. If a module relies on keepers from another module, it is expected to receive the keepers as interface contracts through the `expected_keepers.go` file (see below) in order to avoid a direct dependency on the module implementing the keepers. However, these interface contracts can define methods that operate on and/or return types that are specific to the module that is implementing the keepers and this is where `exported/` comes into play. The interface types that are defined in `exported/` use canonical types, allowing for the module to receive the keepers as interface contracts through the `expected_keepers.go` file. This pattern allows for code to remain DRY and also alleviates import cycle chaos. -- `keeper/`: The module's `Keeper` and `MsgServer` implementation. -- `module/`: The module's `AppModule` and `AppModuleBasic` implementation. -- `simulation/`: The module's [simulation](./simulator.html) package defines functions used by the blockchain simulator application (`simapp`). -- `spec/`: The module's specification documents outlining important concepts, state storage structure, and message and event type definitions. -- The root directory includes type definitions for messages, events, and genesis state, including the type definitions generated by Protocol Buffers. - - `abci.go`: The module's `BeginBlocker` and `EndBlocker` implementations (this file is only required if `BeginBlocker` and/or `EndBlocker` need to be defined). - - `codec.go`: The module's registry methods for interface types. - - `errors.go`: The module's sentinel errors. - - `events.go`: The module's event types and constructors. - - `expected_keepers.go`: The module's [expected keeper](./keeper.html#type-definition) interfaces. - - `genesis.go`: The module's genesis state methods and helper functions. - - `keys.go`: The module's store keys and associated helper functions. - - `msgs.go`: The module's message type definitions and associated methods. - - `params.go`: The module's parameter type definitions and associated methods. - - `*.pb.go`: The module's type definitions generated by Protocol Buffers (as defined in the respective `*.proto` files above). - -## Next {hide} - -Learn about [Errors](./12-errors.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/building-modules/11-errors.md b/versioned_docs/version-0.45/integrate/building-modules/11-errors.md deleted file mode 100644 index 85ca95368..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/11-errors.md +++ /dev/null @@ -1,46 +0,0 @@ -# Errors - -This document outlines the recommended usage and APIs for error handling in Cosmos SDK modules. {synopsis} - -Modules are encouraged to define and register their own errors to provide better -context on failed message or handler execution. Typically, these errors should be -common or general errors which can be further wrapped to provide additional specific -execution context. - -## Registration - -Modules should define and register their custom errors in `x/{module}/errors.go`. Registration -of errors is handled via the `types/errors` package. - -Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.38.1/x/distribution/types/errors.go#L1-L21 - -Each custom module error must provide the codespace, which is typically the module name -(e.g. "distribution") and is unique per module, and a uint32 code. Together, the codespace and code -provide a globally unique SDK error. Typically, the code is monotonically increasing but does not -necessarily have to be. The only restrictions on error codes are the following: - -* Must be greater than one, as a code value of one is reserved for internal errors. -* Must be unique within the module. - -Note, the SDK provides a core set of *common* errors. These errors are defined in `types/errors/errors.go`. - -## Wrapping - -The custom module errors can be returned as their concrete type as they already fulfill the `error` -interface. However, module errors can be wrapped to provide further context and meaning to failed -execution. - -Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/b2d48a9e815fe534a7faeec6ca2adb0874252b81/x/bank/keeper/keeper.go#L85-L122 - -Regardless if an error is wrapped or not, the SDK's `errors` package provides an API to determine if -an error is of a particular kind via `Is`. - -## ABCI - -If a module error is registered, the SDK `errors` package allows ABCI information to be extracted -through the `ABCIInfo` API. The package also provides `ResponseCheckTx` and `ResponseDeliverTx` as -auxiliary APIs to automatically get `CheckTx` and `DeliverTx` responses from an error. diff --git a/versioned_docs/version-0.45/integrate/building-modules/12-upgrade.md b/versioned_docs/version-0.45/integrate/building-modules/12-upgrade.md deleted file mode 100644 index a98eda6c9..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/12-upgrade.md +++ /dev/null @@ -1,53 +0,0 @@ -# Upgrading Modules - -[In-Place Store Migrations](../core/upgrade.html) allow your modules to upgrade to new versions that include breaking changes. This document outlines how to build modules to take advantage of this functionality. {synopsis} - -## Prerequisite Readings - -- [In-Place Store Migration](../core/13-upgrade.md) {prereq} - -## Consensus Version - -Successful upgrades of existing modules require each `AppModule` to implement the function `ConsensusVersion() uint64`. - -- The versions must be hard-coded by the module developer. -- The initial version **must** be set to 1. - -Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes. - -## Registering Migrations - -To register the functionality that takes place during a module upgrade, you must register which migrations you want to take place. - -Migration registration takes place in the `Configurator` using the `RegisterMigration` method. The `AppModule` reference to the configurator is in the `RegisterServices` method. - -You can register one or more migrations. If you register more than one migration script, list the migrations in increasing order and ensure there are enough migrations that lead to the desired consensus version. For example, to migrate to version 3 of a module, register separate migrations for version 1 and version 2 as shown in the following example: - -```golang -func (am AppModule) RegisterServices(cfg module.Configurator) { - // --snip-- - cfg.RegisterMigration(types.ModuleName, 1, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 1 to 2. - }) - cfg.RegisterMigration(types.ModuleName, 2, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 2 to 3. - }) -} -``` - -Since these migrations are functions that need access to a Keeper's store, use a wrapper around the keepers called `Migrator` as shown in this example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/6ac8898fec9bd7ea2c1e5c79e0ed0c3f827beb55/x/bank/keeper/migrations.go#L8-L21 - -## Writing Migration Scripts - -To define the functionality that takes place during an upgrade, write a migration script. Since migration scripts manipulate legacy code, place these functions in a `legacy/` directory. For example, to write migration scripts for the bank module, place the functions in `x/bank/legacy/`. Use the recommended naming convention for these functions. For example, `v043bank` is the script that migrates this legacy package `x/bank/legacy/v043`: - -```golang -// Migrating bank module from version 1 to 2 -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v043bank.MigrateStore(ctx, m.keeper.storeKey) // v043bank is package `x/bank/legacy/v043`. -} -``` - -To see example code of changes that were implemented in a migration of balance keys, check out [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/36f68eb9e041e20a5bb47e216ac5eb8b91f95471/x/bank/legacy/v043/store.go#L41-L62). For context, this code introduced migrations of the bank store that updated addresses to be prefixed by their length in bytes as outlined in [ADR-028](../architecture/adr-028-public-key-addresses.md). diff --git a/versioned_docs/version-0.45/integrate/building-modules/13-simulator.md b/versioned_docs/version-0.45/integrate/building-modules/13-simulator.md deleted file mode 100644 index 1ef579ab6..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/13-simulator.md +++ /dev/null @@ -1,123 +0,0 @@ -# Module Simulation - -## Prerequisites - -* [Cosmos Blockchain Simulator](./../using-the-sdk/simulation.md) - -## Synopsis - -This document details how to define each module simulation functions to be -integrated with the application `SimulationManager`. - -* [Simulation package](#simulation-package) - * [Store decoders](#store-decoders) - * [Randomized genesis](#randomized-genesis) - * [Randomized parameters](#randomized-parameters) - * [Random weighted operations](#random-weighted-operations) - * [Random proposal contents](#random-proposal-contents) -* [Registering the module simulation functions](#registering-simulation-functions) -* [App simulator manager](#app-simulator-manager) -* [Simulation tests](#simulation-tests) - -## Simulation package - -Every module that implements the SDK simulator needs to have a `x//simulation` -package which contains the primary functions required by the fuzz tests: store -decoders, randomized genesis state and parameters, weighted operations and proposal -contents. - -### Store decoders - -Registering the store decoders is required for the `AppImportExport`. This allows -for the key-value pairs from the stores to be decoded (_i.e_ unmarshalled) -to their corresponding types. In particular, it matches the key to a concrete type -and then unmarshals the value from the `KVPair` to the type provided. - -You can use the example [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/distribution/simulation/decoder.go) from the distribution module to implement your store decoders. - -### Randomized genesis - -The simulator tests different scenarios and values for genesis parameters -in order to fully test the edge cases of specific modules. The `simulator` package from each module must expose a `RandomizedGenState` function to generate the initial random `GenesisState` from a given seed. - -Once the module genesis parameter are generated randomly (or with the key and -values defined in a `params` file), they are marshaled to JSON format and added -to the app genesis JSON to use it on the simulations. - -You can check an example on how to create the randomized genesis [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/staking/simulation/genesis.go). - -### Randomized parameter changes - -The simulator is able to test parameter changes at random. The simulator package from each module must contain a `RandomizedParams` func that will simulate parameter changes of the module throughout the simulations lifespan. - -You can see how an example of what is needed to fully test parameter changes [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/staking/simulation/params.go) - -### Random weighted operations - -Operations are one of the crucial parts of the SDK simulation. They are the transactions -(`Msg`) that are simulated with random field values. The sender of the operation -is also assigned randomly. - -Operations on the simulation are simulated using the full [transaction cycle](../core/01-transactions.md) of a -`ABCI` application that exposes the `BaseApp`. - -Shown below is how weights are set: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/simulation/operations.go#L18-L68 - -As you can see, the weights are predefined in this case. Options exist to override this behavior with different weights. One option is to use `*rand.Rand` to define a random weight for the operation, or you can inject your own predefined weights. - -Here is how one can override the above package `simappparams`. - -+++ https://github.com/cosmos/gaia/blob/master/sims.mk#L9-L22 - -For the last test a tool called runsim is used, this is used to parallelize go test instances, provide info to Github and slack integrations to provide information to your team on how the simulations are running. - -### Random proposal contents - -Randomized governance proposals are also supported on the SDK simulator. Each -module must define the governance proposal `Content`s that they expose and register -them to be used on the parameters. - -## Registering simulation functions - -Now that all the required functions are defined, we need to integrate them into the module pattern within the `module.go`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/distribution/module.go - -## App Simulator manager - -The following step is setting up the `SimulatorManager` at the app level. This -is required for the simulation test files on the next step. - -```go -type CustomApp struct { - ... - sm *module.SimulationManager -} -``` - -Then at the instantiation of the application, we create the `SimulationManager` -instance in the same way we create the `ModuleManager` but this time we only pass -the modules that implement the simulation functions from the `AppModuleSimulation` -interface described above. - -```go -func NewCustomApp(...) { - // create the simulation manager and define the order of the modules for deterministic simulations - app.sm = module.NewSimulationManager( - auth.NewAppModule(app.accountKeeper), - bank.NewAppModule(app.bankKeeper, app.accountKeeper), - supply.NewAppModule(app.supplyKeeper, app.accountKeeper), - ov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper), - mint.NewAppModule(app.mintKeeper), - distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper), - staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper), - slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper), - ) - - // register the store decoders for simulation tests - app.sm.RegisterStoreDecoders() - ... -} -``` diff --git a/versioned_docs/version-0.45/integrate/building-modules/_category_.json b/versioned_docs/version-0.45/integrate/building-modules/_category_.json deleted file mode 100644 index 2d50f8b3e..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Building Modules", - "position": 1, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/integrate/building-modules/transaction_flow.svg b/versioned_docs/version-0.45/integrate/building-modules/transaction_flow.svg deleted file mode 100644 index 1ae962de3..000000000 --- a/versioned_docs/version-0.45/integrate/building-modules/transaction_flow.svg +++ /dev/null @@ -1,48 +0,0 @@ -UserUserbaseAppbaseApprouterrouterhandlerhandlermsgServermsgServerkeeperkeeperContext.EventManagerContext.EventManagerTransaction Type<Tx>Route(ctx, msgRoute)handlerMsg<Tx>(Context, Msg(...))<Tx>(Context, Msg)alt[addresses invalid, denominations wrong, etc.]errorperform action, update contextresults, error codeEmit relevant eventsmaybe wrap results in more structureresult, error coderesults, error code \ No newline at end of file diff --git a/versioned_docs/version-0.45/integrate/ibc/_category_.json b/versioned_docs/version-0.45/integrate/ibc/_category_.json deleted file mode 100644 index c56117213..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "IBC", - "position": 1, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/integrate/ibc/custom.md b/versioned_docs/version-0.45/integrate/ibc/custom.md deleted file mode 100644 index 0bef693f1..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/custom.md +++ /dev/null @@ -1,464 +0,0 @@ -# Customization - -Learn how to configure your application to use IBC and send data packets to other chains. {synopsis} - -This document serves as a guide for developers who want to write their own Inter-blockchain -Communication Protocol (IBC) applications for custom [use-cases](https://github.com/cosmos/ics/blob/master/ibc/4_IBC_USECASES.md). - -Due to the modular design of the IBC protocol, IBC -application developers do not need to concern themselves with the low-level details of clients, -connections, and proof verification. Nevertheless a brief explanation of the lower levels of the -stack is given so that application developers may have a high-level understanding of the IBC -protocol. Then the document goes into detail on the abstraction layer most relevant for application -developers (channels and ports), and describes how to define your own custom packets, and -`IBCModule` callbacks. - -To have your module interact over IBC you must: bind to a port(s), define your own packet data and acknolwedgement structs as well as how to encode/decode them, and implement the -`IBCModule` interface. Below is a more detailed explanation of how to write an IBC application -module correctly. - -## Pre-requisites Readings - -- [IBC Overview](./overview.md)) {prereq} -- [IBC default integration](./integration.md) {prereq} - -## Create a custom IBC application module - -### Implement `IBCModule` Interface and callbacks - -The Cosmos SDK expects all IBC modules to implement the [`IBCModule` -interface](https://github.com/cosmos/ibc-go/tree/main/modules/core/05-port/types/module.go). This -interface contains all of the callbacks IBC expects modules to implement. This section will describe -the callbacks that are called during channel handshake execution. - -Here are the channel handshake callbacks that modules are expected to implement: - -```go -// Called by IBC Handler on MsgOpenInit -func (k Keeper) OnChanOpenInit(ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID string, - channelID string, - channelCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version string, -) error { - // OpenInit must claim the channelCapability that IBC passes into the callback - if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return err - } - - // ... do custom initialization logic - - // Use above arguments to determine if we want to abort handshake - // Examples: Abort if order == UNORDERED, - // Abort if version is unsupported - err := checkArguments(args) - return err -} - -// Called by IBC Handler on MsgOpenTry -OnChanOpenTry( - ctx sdk.Context, - order channeltypes.Order, - connectionHops []string, - portID, - channelID string, - channelCap *capabilitytypes.Capability, - counterparty channeltypes.Counterparty, - version, - counterpartyVersion string, -) error { - // Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos - // (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry) - // If the module can already authenticate the capability then the module already owns it so we don't need to claim - // Otherwise, module does not have channel capability and we must claim it from IBC - if !k.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { - // Only claim channel capability passed back by IBC module if we do not already own it - if err := k.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { - return err - } - } - - // ... do custom initialization logic - - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err -} - -// Called by IBC Handler on MsgOpenAck -OnChanOpenAck( - ctx sdk.Context, - portID, - channelID string, - counterpartyVersion string, -) error { - // ... do custom initialization logic - - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err -} - -// Called by IBC Handler on MsgOpenConfirm -OnChanOpenConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - // ... do custom initialization logic - - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err -} -``` - -The channel closing handshake will also invoke module callbacks that can return errors to abort the -closing handshake. Closing a channel is a 2-step handshake, the initiating chain calls -`ChanCloseInit` and the finalizing chain calls `ChanCloseConfirm`. - -```go -// Called by IBC Handler on MsgCloseInit -OnChanCloseInit( - ctx sdk.Context, - portID, - channelID string, -) error { - // ... do custom finalization logic - - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err -} - -// Called by IBC Handler on MsgCloseConfirm -OnChanCloseConfirm( - ctx sdk.Context, - portID, - channelID string, -) error { - // ... do custom finalization logic - - // Use above arguments to determine if we want to abort handshake - err := checkArguments(args) - return err -} -``` - -#### Channel Handshake Version Negotiation - -Application modules are expected to verify versioning used during the channel handshake procedure. - -* `ChanOpenInit` callback should verify that the `MsgChanOpenInit.Version` is valid -* `ChanOpenTry` callback should verify that the `MsgChanOpenTry.Version` is valid and that `MsgChanOpenTry.CounterpartyVersion` is valid. -* `ChanOpenAck` callback should verify that the `MsgChanOpenAck.CounterpartyVersion` is valid and supported. - -Versions must be strings but can implement any versioning structure. If your application plans to -have linear releases then semantic versioning is recommended. If your application plans to release -various features in between major releases then it is advised to use the same versioning scheme -as IBC. This versioning scheme specifies a version identifier and compatible feature set with -that identifier. Valid version selection includes selecting a compatible version identifier with -a subset of features supported by your application for that version. The struct is used for this -scheme can be found in `03-connection/types`. - -Since the version type is a string, applications have the ability to do simple version verification -via string matching or they can use the already impelemented versioning system and pass the proto -encoded version into each handhshake call as necessary. - -ICS20 currently implements basic string matching with a single supported version. - -### Bind Ports - -Currently, ports must be bound on app initialization. A module may bind to ports in `InitGenesis` -like so: - -```go -func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, state types.GenesisState) { - // ... other initialization logic - - // Only try to bind to port if it is not already bound, since we may already own - // port capability from capability InitGenesis - if !isBound(ctx, state.PortID) { - // module binds to desired ports on InitChain - // and claims returned capabilities - cap1 := keeper.IBCPortKeeper.BindPort(ctx, port1) - cap2 := keeper.IBCPortKeeper.BindPort(ctx, port2) - cap3 := keeper.IBCPortKeeper.BindPort(ctx, port3) - - // NOTE: The module's scoped capability keeper must be private - keeper.scopedKeeper.ClaimCapability(cap1) - keeper.scopedKeeper.ClaimCapability(cap2) - keeper.scopedKeeper.ClaimCapability(cap3) - } - - // ... more initialization logic -} -``` - -### Custom Packets - -Modules connected by a channel must agree on what application data they are sending over the -channel, as well as how they will encode/decode it. This process is not specified by IBC as it is up -to each application module to determine how to implement this agreement. However, for most -applications this will happen as a version negotiation during the channel handshake. While more -complex version negotiation is possible to implement inside the channel opening handshake, a very -simple version negotation is implemented in the [ibc-transfer module](https://github.com/cosmos/ibc-go/tree/main/modules/apps/transfer/module.go). - -Thus, a module must define its a custom packet data structure, along with a well-defined way to -encode and decode it to and from `[]byte`. - -```go -// Custom packet data defined in application module -type CustomPacketData struct { - // Custom fields ... -} - -EncodePacketData(packetData CustomPacketData) []byte { - // encode packetData to bytes -} - -DecodePacketData(encoded []byte) (CustomPacketData) { - // decode from bytes to packet data -} -``` - -Then a module must encode its packet data before sending it through IBC. - -```go -// Sending custom application packet data -data := EncodePacketData(customPacketData) -packet.Data = data -IBCChannelKeeper.SendPacket(ctx, packet) -``` - -A module receiving a packet must decode the `PacketData` into a structure it expects so that it can -act on it. - -```go -// Receiving custom application packet data (in OnRecvPacket) -packetData := DecodePacketData(packet.Data) -// handle received custom packet data -``` - -#### Packet Flow Handling - -Just as IBC expected modules to implement callbacks for channel handshakes, IBC also expects modules -to implement callbacks for handling the packet flow through a channel. - -Once a module A and module B are connected to each other, relayers can start relaying packets and -acknowledgements back and forth on the channel. - -![IBC packet flow diagram](https://media.githubusercontent.com/media/cosmos/ics/master/spec/ics-004-channel-and-packet-semantics/packet-state-machine.png) - -Briefly, a successful packet flow works as follows: - -1. module A sends a packet through the IBC module -2. the packet is received by module B -3. if module B writes an acknowledgement of the packet then module A will process the - acknowledgement -4. if the packet is not successfully received before the timeout, then module A processes the - packet's timeout. - -##### Sending Packets - -Modules do not send packets through callbacks, since the modules initiate the action of sending -packets to the IBC module, as opposed to other parts of the packet flow where msgs sent to the IBC -module must trigger execution on the port-bound module through the use of callbacks. Thus, to send a -packet a module simply needs to call `SendPacket` on the `IBCChannelKeeper`. - -```go -// retrieve the dynamic capability for this channel -channelCap := scopedKeeper.GetCapability(ctx, channelCapName) -// Sending custom application packet data -data := EncodePacketData(customPacketData) -packet.Data = data -// Send packet to IBC, authenticating with channelCap -IBCChannelKeeper.SendPacket(ctx, channelCap, packet) -``` - -::: warning -In order to prevent modules from sending packets on channels they do not own, IBC expects -modules to pass in the correct channel capability for the packet's source channel. -::: - -##### Receiving Packets - -To handle receiving packets, the module must implement the `OnRecvPacket` callback. This gets -invoked by the IBC module after the packet has been proved valid and correctly processed by the IBC -keepers. Thus, the `OnRecvPacket` callback only needs to worry about making the appropriate state -changes given the packet data without worrying about whether the packet is valid or not. - -Modules may return an acknowledgement as a byte string and return it to the IBC handler. -The IBC handler will then commit this acknowledgement of the packet so that a relayer may relay the -acknowledgement back to the sender module. - -```go -OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, -) (res *sdk.Result, ack []byte, abort error) { - // Decode the packet data - packetData := DecodePacketData(packet.Data) - - // do application state changes based on packet data - // and return result, acknowledgement and abortErr - // Note: abortErr is only not nil if we need to abort the entire receive packet, and allow a replay of the receive. - // If the application state change failed but we do not want to replay the packet, - // simply encode this failure with relevant information in ack and return nil error - res, ack, abortErr := processPacket(ctx, packet, packetData) - - // if we need to abort the entire receive packet, return error - if abortErr != nil { - return nil, nil, abortErr - } - - // Encode the ack since IBC expects acknowledgement bytes - ackBytes := EncodeAcknowledgement(ack) - - return res, ackBytes, nil -} -``` - -::: warning -`OnRecvPacket` should **only** return an error if we want the entire receive packet execution -(including the IBC handling) to be reverted. This will allow the packet to be replayed in the case -that some mistake in the relaying caused the packet processing to fail. - -If some application-level error happened while processing the packet data, in most cases, we will -not want the packet processing to revert. Instead, we may want to encode this failure into the -acknowledgement and finish processing the packet. This will ensure the packet cannot be replayed, -and will also allow the sender module to potentially remediate the situation upon receiving the -acknowledgement. An example of this technique is in the `ibc-transfer` module's -[`OnRecvPacket`](https://github.com/cosmos/ibc-go/tree/main/modules/apps/transfer/module.go). -::: - -### Acknowledgements - -Modules may commit an acknowledgement upon receiving and processing a packet in the case of synchronous packet processing. -In the case where a packet is processed at some later point after the packet has been received (asynchronous execution), the acknowledgement -will be written once the packet has been processed by the application which may be well after the packet receipt. - -NOTE: Most blockchain modules will want to use the synchronous execution model in which the module processes and writes the acknowledgement -for a packet as soon as it has been received from the IBC module. - -This acknowledgement can then be relayed back to the original sender chain, which can take action -depending on the contents of the acknowledgement. - -Just as packet data was opaque to IBC, acknowledgements are similarly opaque. Modules must pass and -receive acknowledegments with the IBC modules as byte strings. - -Thus, modules must agree on how to encode/decode acknowledgements. The process of creating an -acknowledgement struct along with encoding and decoding it, is very similar to the packet data -example above. [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope) -specifies a recommended format for acknowledgements. This acknowledgement type can be imported from -[channel types](https://github.com/cosmos/ibc-go/tree/main/modules/core/04-channel/types). - -While modules may choose arbitrary acknowledgement structs, a default acknowledgement types is provided by IBC [here](https://github.com/cosmos/ibc-go/blob/main/proto/ibc/core/channel/v1/channel.proto): - -```proto -// Acknowledgement is the recommended acknowledgement format to be used by -// app-specific protocols. -// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental -// conflicts with other protobuf message formats used for acknowledgements. -// The first byte of any message with this format will be the non-ASCII values -// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: -// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope -message Acknowledgement { - // response contains either a result or an error and must be non-empty - oneof response { - bytes result = 21; - string error = 22; - } -} -``` - -#### Acknowledging Packets - -After a module writes an acknowledgement, a relayer can relay back the acknowledgement to the sender module. The sender module can -then process the acknowledgement using the `OnAcknowledgementPacket` callback. The contents of the -acknowledgement is entirely upto the modules on the channel (just like the packet data); however, it -may often contain information on whether the packet was successfully processed along -with some additional data that could be useful for remediation if the packet processing failed. - -Since the modules are responsible for agreeing on an encoding/decoding standard for packet data and -acknowledgements, IBC will pass in the acknowledgements as `[]byte` to this callback. The callback -is responsible for decoding the acknowledgement and processing it. - -```go -OnAcknowledgementPacket( - ctx sdk.Context, - packet channeltypes.Packet, - acknowledgement []byte, -) (*sdk.Result, error) { - // Decode acknowledgement - ack := DecodeAcknowledgement(acknowledgement) - - // process ack - res, err := processAck(ack) - return res, err -} -``` - -#### Timeout Packets - -If the timeout for a packet is reached before the packet is successfully received or the -counterparty channel end is closed before the packet is successfully received, then the receiving -chain can no longer process it. Thus, the sending chain must process the timeout using -`OnTimeoutPacket` to handle this situation. Again the IBC module will verify that the timeout is -indeed valid, so our module only needs to implement the state machine logic for what to do once a -timeout is reached and the packet can no longer be received. - -```go -OnTimeoutPacket( - ctx sdk.Context, - packet channeltypes.Packet, -) (*sdk.Result, error) { - // do custom timeout logic -} -``` - -### Routing - -As mentioned above, modules must implement the IBC module interface (which contains both channel -handshake callbacks and packet handling callbacks). The concrete implementation of this interface -must be registered with the module name as a route on the IBC `Router`. - -```go -// app.go -func NewApp(...args) *App { -// ... - -// Create static IBC router, add module routes, then set and seal it -ibcRouter := port.NewRouter() - -ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferModule) -// Note: moduleCallbacks must implement IBCModule interface -ibcRouter.AddRoute(moduleName, moduleCallbacks) - -// Setting Router will finalize all routes by sealing router -// No more routes can be added -app.IBCKeeper.SetRouter(ibcRouter) -``` - -## Working Example - -For a real working example of an IBC application, you can look through the `ibc-transfer` module -which implements everything discussed above. - -Here are the useful parts of the module to look at: - -[Binding to transfer -port](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/types/genesis.go) - -[Sending transfer -packets](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/keeper/relay.go) - -[Implementing IBC -callbacks](https://github.com/cosmos/ibc-go/blob/main/modules/apps/transfer/module.go) - -## Next {hide} - -Learn about [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/intro.md) {hide} diff --git a/versioned_docs/version-0.45/integrate/ibc/integration.md b/versioned_docs/version-0.45/integrate/ibc/integration.md deleted file mode 100644 index e8506ca6c..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/integration.md +++ /dev/null @@ -1,248 +0,0 @@ -# Integration - -Learn how to integrate IBC to your application and send data packets to other chains. {synopsis} - -This document outlines the required steps to integrate and configure the [IBC -module](https://github.com/cosmos/ibc-go/tree/main/modules/core) to your Cosmos SDK application and -send fungible token transfers to other chains. - -## Integrating the IBC module - -Integrating the IBC module to your SDK-based application is straighforward. The general changes can be summarized in the following steps: - -- Add required modules to the `module.BasicManager` -- Define additional `Keeper` fields for the new modules on the `App` type -- Add the module's `StoreKeys` and initialize their `Keepers` -- Set up corresponding routers and routes for the `ibc` and `evidence` modules -- Add the modules to the module `Manager` -- Add modules to `Begin/EndBlockers` and `InitGenesis` -- Update the module `SimulationManager` to enable simulations - -### Module `BasicManager` and `ModuleAccount` permissions - -The first step is to add the following modules to the `BasicManager`: `x/capability`, `x/ibc`, -`x/evidence` and `x/ibc-transfer`. After that, we need to grant `Minter` and `Burner` permissions to -the `ibc-transfer` `ModuleAccount` to mint and burn relayed tokens. - -```go -// app.go -var ( - - ModuleBasics = module.NewBasicManager( - // ... - capability.AppModuleBasic{}, - ibc.AppModuleBasic{}, - evidence.AppModuleBasic{}, - transfer.AppModuleBasic{}, // i.e ibc-transfer module - ) - - // module account permissions - maccPerms = map[string][]string{ - // other module accounts permissions - // ... - ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, -) -``` - -### Application fields - -Then, we need to register the `Keepers` as follows: - -```go -// app.go -type App struct { - // baseapp, keys and subspaces definitions - - // other keepers - // ... - IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly - EvidenceKeeper evidencekeeper.Keeper // required to set up the client misbehaviour route - TransferKeeper ibctransferkeeper.Keeper // for cross-chain fungible token transfers - - // make scoped keepers public for test purposes - ScopedIBCKeeper capabilitykeeper.ScopedKeeper - ScopedTransferKeeper capabilitykeeper.ScopedKeeper - - /// ... - /// module and simulation manager definitions -} -``` - -### Configure the `Keepers` - -During initialization, besides initializing the IBC `Keepers` (for the `x/ibc`, and -`x/ibc-transfer` modules), we need to grant specific capabilities through the capability module -`ScopedKeepers` so that we can authenticate the object-capability permissions for each of the IBC -channels. - -```go -func NewApp(...args) *App { - // define codecs and baseapp - - // add capability keeper and ScopeToModule for ibc module - app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) - - // grant capabilities for the ibc and ibc-transfer modules - scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) - scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) - - // ... other modules keepers - - // Create IBC Keeper - app.IBCKeeper = ibckeeper.NewKeeper( - appCodec, keys[ibchost.StoreKey], app.StakingKeeper, scopedIBCKeeper, - ) - - // Create Transfer Keepers - app.TransferKeeper = ibctransferkeeper.NewKeeper( - appCodec, keys[ibctransfertypes.StoreKey], - app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, - app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, - ) - transferModule := transfer.NewAppModule(app.TransferKeeper) - - // Create evidence Keeper for to register the IBC light client misbehaviour evidence route - evidenceKeeper := evidencekeeper.NewKeeper( - appCodec, keys[evidencetypes.StoreKey], &app.StakingKeeper, app.SlashingKeeper, - ) - - // .. continues -} -``` - -### Register `Routers` - -IBC needs to know which module is bound to which port so that it can route packets to the -appropriate module and call the appropriate callbacks. The port to module name mapping is handled by -IBC's port `Keeper`. However, the mapping from module name to the relevant callbacks is accomplished -by the port -[`Router`](https://github.com/cosmos/ibc-go/blob/main/modules/core/05-port/types/router.go) on the -IBC module. - -Adding the module routes allows the IBC handler to call the appropriate callback when processing a -channel handshake or a packet. - -The second `Router` that is required is the evidence module router. This router handles genenal -evidence submission and routes the business logic to each registered evidence handler. In the case -of IBC, it is required to submit evidence for [light client -misbehaviour](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#misbehaviour) -in order to freeze a client and prevent further data packets from being sent/received. - -Currently, a `Router` is static so it must be initialized and set correctly on app initialization. -Once the `Router` has been set, no new routes can be added. - -```go -// app.go -func NewApp(...args) *App { - // .. continuation from above - - // Create static IBC router, add ibc-tranfer module route, then set and seal it - ibcRouter := port.NewRouter() - ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferModule) - // Setting Router will finalize all routes by sealing router - // No more routes can be added - app.IBCKeeper.SetRouter(ibcRouter) - - // create static Evidence routers - - evidenceRouter := evidencetypes.NewRouter(). - // add IBC ClientMisbehaviour evidence handler - AddRoute(ibcclient.RouterKey, ibcclient.HandlerClientMisbehaviour(app.IBCKeeper.ClientKeeper)) - - // Setting Router will finalize all routes by sealing router - // No more routes can be added - evidenceKeeper.SetRouter(evidenceRouter) - - // set the evidence keeper from the section above - app.EvidenceKeeper = *evidenceKeeper - - // .. continues -``` - -### Module Managers - -In order to use IBC, we need to add the new modules to the module `Manager` and to the `SimulationManager` in case your application supports [simulations](./../building-modules/simulator.md). - -```go -// app.go -func NewApp(...args) *App { - // .. continuation from above - - app.mm = module.NewManager( - // other modules - // ... - capability.NewAppModule(appCodec, *app.CapabilityKeeper), - evidence.NewAppModule(app.EvidenceKeeper), - ibc.NewAppModule(app.IBCKeeper), - transferModule, - ) - - // ... - - app.sm = module.NewSimulationManager( - // other modules - // ... - capability.NewAppModule(appCodec, *app.CapabilityKeeper), - evidence.NewAppModule(app.EvidenceKeeper), - ibc.NewAppModule(app.IBCKeeper), - transferModule, - ) - - // .. continues -``` - -### Application ABCI Ordering - -One addition from IBC is the concept of `HistoricalEntries` which are stored on the staking module. -Each entry contains the historical information for the `Header` and `ValidatorSet` of this chain which is stored -at each height during the `BeginBlock` call. The historical info is required to introspect the -past historical info at any given height in order to verify the light client `ConsensusState` during the -connection handhake. - -The IBC module also has -[`BeginBlock`](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client/abci.go) logic as -well. This is optional as it is only required if your application uses the [localhost -client](https://github.com/cosmos/ics/blob/master/spec/ics-009-loopback-client) to connect two -different modules from the same chain. - -::: tip -Only register the ibc module to the `SetOrderBeginBlockers` if your application will use the -localhost (_aka_ loopback) client. -::: - -```go -// app.go -func NewApp(...args) *App { - // .. continuation from above - - // add evidence, staking and ibc modules to BeginBlockers - app.mm.SetOrderBeginBlockers( - // other modules ... - evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, - ) - - // ... - - // NOTE: Capability module must occur first so that it can initialize any capabilities - // so that other modules that want to create or claim capabilities afterwards in InitChain - // can do so safely. - app.mm.SetOrderInitGenesis( - capabilitytypes.ModuleName, - // other modules ... - ibchost.ModuleName, evidencetypes.ModuleName, ibctransfertypes.ModuleName, - ) - - // .. continues -``` - -::: warning -**IMPORTANT**: The capability module **must** be declared first in `SetOrderInitGenesis` -::: - -That's it! You have now wired up the IBC module and are now able to send fungible tokens across -different chains. If you want to have a broader view of the changes take a look into the SDK's -[`SimApp`](https://github.com/cosmos/ibc-go/blob/main/testing/simapp/app.go). - -## Next {hide} - -Learn about how to create [custom IBC modules](./custom.md) for your application {hide} diff --git a/versioned_docs/version-0.45/integrate/ibc/overview.md b/versioned_docs/version-0.45/integrate/ibc/overview.md deleted file mode 100644 index 6b739d93d..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/overview.md +++ /dev/null @@ -1,149 +0,0 @@ -# IBC Overview - -Learn what IBC is, its components, and use cases. {synopsis} - -## What is the Inter-Blockchain Communication Protocol (IBC)? - -This document is a guide for developers who want to write their own IBC apps for custom use cases. - -The modular design of the IBC protocol means that IBC app developers do not require in-depth knowledge of the low-level details of clients, connections, and proof verification. This brief explanation of the lower levels of the stack is provided so that app developers can gain a high-level understanding of the IBC protocol. - -The abstraction layer details on channels and ports are relevant for app developers. You can define your own custom packets and IBCModule callbacks. - -The following requirements must be met for a module to interact over IBC: - -- Bind to one or more ports - -- Define the packet data - -- Define optional acknowledgement structures and methods to encode and decode them - -- Implement the IBCModule interface - -## Components Overview - -This section describes the IBC components and links to the repos. - -### [Clients](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client) - -IBC clients are light clients that are identified by a unique client id. IBC clients track the consensus states of other blockchains and the proof specs of those blockchains that are required to properly verify proofs against the client's consensus state. A client can be associated with any number of connections to multiple chains. The supported IBC clients are: - -- [Solo Machine light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/06-solomachine): devices such as phones, browsers, or laptops. -- [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint): The default for Cosmos SDK-based chains. -- [Localhost (loopback) client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/09-localhost): Useful for testing, simulation, and relaying packets to modules on the same application. - -### [Connections](https://github.com/cosmos/ibc-go/blob/main/modules/core/03-connection) - -Connections encapsulate two `ConnectionEnd` objects on two separate blockchains. Each `ConnectionEnd` is associated with a client of the other blockchain (the counterparty blockchain). The connection handshake is responsible for verifying that the light clients on each chain are correct for their respective counterparties. Connections, once established, are responsible for facilitating all cross-chain verification of IBC state. A connection can be associated with any number of channels. - -### [Proofs](https://github.com/cosmos/ibc-go/blob/main/modules/core/23-commitment) and [Paths](https://github.com/cosmos/ibc-go/blob/main/modules/core/24-host) - -In IBC, blockchains do not directly pass messages to each other over the network. - -- To communicate, a blockchain commits some state to a precisely defined path reserved for a specific message type and a specific counterparty. For example, a blockchain that stores a specific connectionEnd as part of a handshake or a packet intended to be relayed to a module on the counterparty chain. - -- A relayer process monitors for updates to these paths and relays messages by submitting the data stored under the path along with a proof of that data to the counterparty chain. - -- The paths that all IBC implementations must support for committing IBC messages are defined in [ICS-24 host requirements](https://github.com/cosmos/ics/tree/master/spec/core/ics-024-host-requirements). - -- The proof format that all implementations must produce and verify is defined in [ICS-23 implementation](https://github.com/confio/ics23). - -### [Capabilities](./ocap.md) - -IBC is intended to work in execution environments where modules do not necessarily trust each other. IBC must authenticate module actions on ports and channels so that only modules with the appropriate permissions can use the channels. This security is accomplished using [dynamic capabilities](../architecture/adr-003-dynamic-capability-store.md). Upon binding to a port or creating a channel for a module, IBC returns a dynamic capability that the module must claim to use that port or channel. This binding strategy prevents other modules from using that port or channel since those modules do not own the appropriate capability. - -While this explanation is useful background information, IBC modules do not need to interact at all with these lower-level abstractions. The relevant abstraction layer for IBC application developers is that of channels and ports. - -Write your IBC applications as self-contained **modules**. A module on one blockchain can communicate with other modules on other blockchains by sending, receiving, and acknowledging packets through channels that are uniquely identified by the `(channelID, portID)` tuple. - -A useful analogy is to consider IBC modules as internet apps on a computer. A channel can then be conceptualized as an IP connection, with the IBC portID is like an IP port, and the IBC channelID is like an IP address. A single instance of an IBC module can communicate on the same port with any number of other modules and IBC correctly routes all packets to the relevant module using the `(channelID, portID)` tuple. An IBC module can also communicate with another IBC module over multiple ports by sending each `(portID<->portID)` packet stream on a different unique channel. - -### [Ports](https://github.com/cosmos/ibc-go/blob/main/modules/core/05-port) - -An IBC module can bind to any number of ports. Each port must be identified by a unique `portID`. Since IBC is designed to be secure with mutually-distrusted modules that operate on the same ledger, binding a port returns the dynamic object capability. To take action on a particular port, for example, to open a channel with its portID, a module must provide the dynamic object capability to the IBC handler. This requirement prevents a malicious module from opening channels with ports it does not own. - -IBC modules are responsible for claiming the capability that is returned on `BindPort`. - -### [Channels](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel) - -An IBC channel can be established between two IBC ports. A port is exclusively owned by a single module. IBC packets are sent over channels. Just as IP packets contain the destination IP address, IP port, the source IP address, and source IP port, IBC packets contain the destination portID, channelID, the source portID, and channelID. The IBC packets enable IBC to correctly route the packets to the destination module, while also allowing modules receiving packets to know the sender module. - -- A channel can be `ORDERED` so that packets from a sending module must be processed by the receiving module in the order they were sent. - -- Recommended, a channel may be `UNORDERED` so that packets from a sending module are processed in the order they arrive, which may not be the order the packets were sent. - -Modules may choose which channels they wish to communicate over with. IBC expects modules to implement callbacks that are called during the channel handshake. These callbacks may do custom channel initialization logic. If an error is returned, the channel handshake fails. By returning errors on callbacks, modules can programmatically reject and accept channels. - -The channel handshake is a 4-step handshake. Briefly, if a given chain A wants to open a channel with chain B using an already established connection: - -1. Chain A sends a `ChanOpenInit` message to signal a channel initialization attempt with chain B. -2. Chain B sends a `ChanOpenTry` message to try opening the channel on chain A. -3. Chain A sends a `ChanOpenAck` message to mark its channel end status as open. -4. Chain B sends a `ChanOpenConfirm` message to mark its channel end status as open. - -If all of these actions happen successfully, the channel is open on both sides. At each step in the handshake, the module associated with the `ChannelEnd` executes its callback for that step of the handshake. So on `ChanOpenInit`, the module on chain A has its callback `OnChanOpenInit` executed. - -Just as ports came with dynamic capabilities, channel initialization returns a dynamic capability that the module **must** claim so that they can pass in a capability to authenticate channel actions like sending packets. The channel capability is passed into the callback on the first parts of the handshake: `OnChanOpenInit` on the initializing chain or `OnChanOpenTry` on the other chain. - -### [Packets](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel) - -Modules communicate with each other by sending packets over IBC channels. All IBC packets contain: - -- Destination `portID` - -- Destination `channelID` - -- Source `portID` - -- Source `channelID` - - These port and channels allow the modules to know the sender module of a given packet. - -- A sequence to optionally enforce ordering - -- `TimeoutTimestamp` and `TimeoutHeight` - - When non-zero, these timeout values determine the deadline before which the receiving module must process a packet. - - If the timeout passes without the packet being successfully received, the sending module can timeout the packet and take appropriate actions. - -Modules send custom application data to each other inside the `Data []byte` field of the IBC packet. Packet data is completely opaque to IBC handlers. The sender module must encode their application-specific packet information into the `Data` field of packets. The receiver module must decode that `Data` back to the original application data. - -### [Receipts and Timeouts](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel) - -Since IBC works over a distributed network and relies on potentially faulty relayers to relay messages between ledgers, IBC must handle the case where a packet does not get sent to its destination in a timely manner or at all. Packets must specify a timeout height or timeout timestamp after which a packet can no longer be successfully received on the destination chain. - -If the timeout is reached, then a proof-of-packet timeout can be submitted to the original chain which can then perform application-specific logic to timeout the packet, perhaps by rolling back the packet send changes (refunding senders any locked funds, and so on). - -In ORDERED channels, a timeout of a single packet in the channel closes the channel. If packet sequence `n` times out, then no packet at sequence `k > n` can be successfully received without violating the contract of ORDERED channels that packets are processed in the order that they are sent. Since ORDERED channels enforce this invariant, a proof that sequence `n` hasn't been received on the destination chain by packet `n`'s specified timeout is sufficient to timeout packet `n` and close the channel. - -In the UNORDERED case, packets can be received in any order. IBC writes a packet receipt for each sequence it has received in the UNORDERED channel. This receipt contains no information and is simply a marker intended to signify that the UNORDERED channel has received a packet at the specified sequence. To timeout a packet on an UNORDERED channel, proof that a packet receipt does not exist is required for the packet's sequence by the specified timeout. Of course, timing out a packet on an UNORDERED channel triggers the application specific timeout logic for that packet, and does not close the channel. - -For this reason, most modules that use UNORDERED channels are recommended as they require less liveness guarantees to function effectively for users of that channel. - -### [Acknowledgements](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel) - -Modules also write application-specific acknowledgements when processing a packet. Acknowledgements can be done: - -- Synchronously on `OnRecvPacket` if the module processes packets as soon as they are received from IBC module. - -- Asynchronously if module processes packets at some later point after receiving the packet. - -This acknowledgement data is opaque to IBC much like the packet `Data` and is treated by IBC as a simple byte string `[]byte`. The receiver modules must encode their acknowledgement so that the sender module can decode it correctly. How the acknowledgement is encoded should be decided through version negotiation during the channel handshake. - -The acknowledgement can encode whether the packet processing succeeded or failed, along with additional information that allows the sender module to take appropriate action. - -After the acknowledgement has been written by the receiving chain, a relayer relays the acknowledgement back to the original sender module which then executes application-specific acknowledgment logic using the contents of the acknowledgement. This acknowledgement can involve rolling back packet-send changes in the case of a failed acknowledgement (refunding senders). - -After an acknowledgement is received successfully on the original sender the chain, the IBC module deletes the corresponding packet commitment as it is no longer needed. - -## Further Readings and Specs - -To learn more about IBC, check out the following specifications: - -- [IBC specs](https://github.com/cosmos/ibc/tree/master/spec) -- [IBC protocol on the Cosmos SDK](https://github.com/cosmos/ibc-go/blob/main/docs/spec.md) - -## Next {hide} - -Learn about how to [integrate](./integration.md) IBC to your application {hide} diff --git a/versioned_docs/version-0.45/integrate/ibc/proposals.md b/versioned_docs/version-0.45/integrate/ibc/proposals.md deleted file mode 100644 index 064858311..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/proposals.md +++ /dev/null @@ -1,38 +0,0 @@ -# Governance Proposals - -In uncommon situations, a highly valued client may become frozen due to uncontrollable -circumstances. A highly valued client might have hundreds of channels being actively used. -Some of those channels might have a significant amount of locked tokens used for ICS 20. - -If the one third of the validator set of the chain the client represents decides to collude, -they can sign off on two valid but conflicting headers each signed by the other one third -of the honest validator set. The light client can now be updated with two valid, but conflicting -headers at the same height. The light client cannot know which header is trustworthy and therefore -evidence of such misbehaviour is likely to be submitted resulting in a frozen light client. - -Frozen light clients cannot be updated under any circumstance except via a governance proposal. -Since a quorum of validators can sign arbitrary state roots which may not be valid executions -of the state machine, a governance proposal has been added to ease the complexity of unfreezing -or updating clients which have become "stuck". Without this mechanism, validator sets would need -to construct a state root to unfreeze the client. Unfreezing clients, re-enables all of the channels -built upon that client. This may result in recovery of otherwise lost funds. - -Tendermint light clients may become expired if the trusting period has passed since their -last update. This may occur if relayers stop submitting headers to update the clients. - -An unplanned upgrade by the counterparty chain may also result in expired clients. If the counterparty -chain undergoes an unplanned upgrade, there may be no commitment to that upgrade signed by the validator -set before the chain-id changes. In this situation, the validator set of the last valid update for the -light client is never expected to produce another valid header since the chain-id has changed, which will -ultimately lead the on-chain light client to become expired. - -In the case that a highly valued light client is frozen, expired, or rendered non-updateable, a -governance proposal may be submitted to update this client, known as the subject client. The -proposal includes the client identifier for the subject, the client identifier for a substitute -client, and an initial height to reference the substitute client from. Light client implementations -may implement custom updating logic, but in most cases, the subject will be updated with information -from the substitute client, if the proposal passes. The substitute client is used as a "stand in" -while the subject is on trial. It is best practice to create a substitute client *after* the subject -has become frozen to avoid the substitute from also becoming frozen. An active substitute client -allows headers to be submitted during the voting period to prevent accidental expiry once the proposal -passes. diff --git a/versioned_docs/version-0.45/integrate/ibc/relayer.md b/versioned_docs/version-0.45/integrate/ibc/relayer.md deleted file mode 100644 index 74e57e65f..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/relayer.md +++ /dev/null @@ -1,43 +0,0 @@ -# Relayer - -## Prerequisites Readings - -- [IBC Overview](./overview.md) {prereq} -- [Events](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/07-events.md) {prereq} - -## Events - -Events are emitted for every transaction processed by the base application to indicate the execution -of some logic clients may want to be aware of. This is extremely useful when relaying IBC packets. -Any message that uses IBC will emit events for the corresponding TAO logic executed as defined in -the [IBC events spec](https://github.com/cosmos/ibc-go/blob/main/modules/core/spec/06_events.md). - -In the SDK, it can be assumed that for every message there is an event emitted with the type `message`, -attribute key `action`, and an attribute value representing the type of message sent -(`channel_open_init` would be the attribute value for `MsgChannelOpenInit`). If a relayer queries -for transaction events, it can split message events using this event Type/Attribute Key pair. - -The Event Type `message` with the Attribute Key `module` may be emitted multiple times for a single -message due to application callbacks. It can be assumed that any TAO logic executed will result in -a module event emission with the attribute value `ibc_` (02-client emits `ibc_client`). - -### Subscribing with Tendermint - -Calling the Tendermint RPC method `Subscribe` via [Tendermint's Websocket](https://docs.tendermint.com/master/rpc/) will return events using -Tendermint's internal representation of them. Instead of receiving back a list of events as they -were emitted, Tendermint will return the type `map[string][]string` which maps a string in the -form `.` to `attribute_value`. This causes extraction of the event -ordering to be non-trivial, but still possible. - -A relayer should use the `message.action` key to extract the number of messages in the transaction -and the type of IBC transactions sent. For every IBC transaction within the string array for -`message.action`, the necessary information should be extracted from the other event fields. If -`send_packet` appears at index 2 in the value for `message.action`, a relayer will need to use the -value at index 2 of the key `send_packet.packet_sequence`. This process should be repeated for each -piece of information needed to relay a packet. - -## Example Implementations - -- [Golang Relayer](https://github.com/iqlusioninc/relayer) -- [Hermes](https://github.com/informalsystems/ibc-rs/tree/master/relayer) -- [Typescript Relayer](https://github.com/confio/ts-relayer) diff --git a/versioned_docs/version-0.45/integrate/ibc/upgrades/README.md b/versioned_docs/version-0.45/integrate/ibc/upgrades/README.md deleted file mode 100644 index bc7c88966..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/upgrades/README.md +++ /dev/null @@ -1,14 +0,0 @@ - - -### Upgrading IBC Chains Overview - -This directory contains information on how to upgrade an IBC chain without breaking counterparty clients and connections. - -IBC-connnected chains must be able to upgrade without breaking connections to other chains. Otherwise there would be a massive disincentive towards upgrading and disrupting high-value IBC connections, thus preventing chains in the IBC ecosystem from evolving and improving. Many chain upgrades may be irrelevant to IBC, however some upgrades could potentially break counterparty clients if not handled correctly. Thus, any IBC chain that wishes to perform a IBC-client-breaking upgrade must perform an IBC upgrade in order to allow counterparty clients to securely upgrade to the new light client. - -1. The [quick-guide](./quick-guide.md) describes how IBC-connected chains can perform client-breaking upgrades and how relayers can securely upgrade counterparty clients using the SDK. -2. The [developer-guide](./developer-guide.md) is a guide for developers intending to develop IBC client implementations with upgrade functionality. diff --git a/versioned_docs/version-0.45/integrate/ibc/upgrades/_category_.json b/versioned_docs/version-0.45/integrate/ibc/upgrades/_category_.json deleted file mode 100644 index 4305ecc6a..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/upgrades/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Upgrades", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/integrate/ibc/upgrades/developer-guide.md b/versioned_docs/version-0.45/integrate/ibc/upgrades/developer-guide.md deleted file mode 100644 index 41990abfe..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/upgrades/developer-guide.md +++ /dev/null @@ -1,46 +0,0 @@ -# IBC Client Developer Guide to Upgrades - -Learn how to implement upgrade functionality for your custom IBC client. {synopsis} - -As mentioned in the [README](./README.md), it is vital that high-value IBC clients can upgrade along with their underlying chains to avoid disruption to the IBC ecosystem. Thus, IBC client developers will want to implement upgrade functionality to enable clients to maintain connections and channels even across chain upgrades. - -The IBC protocol allows client implementations to provide a path to upgrading clients given the upgraded client state, upgraded consensus state and proofs for each. - -```go -// Upgrade functions -// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last -// height committed by the current revision. Clients are responsible for ensuring that the planned last -// height of the current revision is somehow encoded in the proof verification process. -// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty -// may be cancelled or modified before the last planned height. -VerifyUpgradeAndUpdateState( - ctx sdk.Context, - cdc codec.BinaryCodec, - store sdk.KVStore, - newClient ClientState, - newConsState ConsensusState, - proofUpgradeClient, - proofUpgradeConsState []byte, -) (upgradedClient ClientState, upgradedConsensus ConsensusState, err error) -``` - -Note that the clients should have prior knowledge of the merkle path that the upgraded client and upgraded consensus states will use. The height at which the upgrade has occurred should also be encoded in the proof. The Tendermint client implementation accomplishes this by including an `UpgradePath` in the ClientState itself, which is used along with the upgrade height to construct the merkle path under which the client state and consensus state are committed. - -Developers must ensure that the `UpgradeClientMsg` does not pass until the last height of the old chain has been committed, and after the chain upgrades, the `UpgradeClientMsg` should pass once and only once on all counterparty clients. - -Developers must ensure that the new client adopts all of the new Client parameters that must be uniform across every valid light client of a chain (chain-chosen parameters), while maintaining the Client parameters that are customizable by each individual client (client-chosen parameters) from the previous version of the client. - -Upgrades must adhere to the IBC Security Model. IBC does not rely on the assumption of honest relayers for correctness. Thus users should not have to rely on relayers to maintain client correctness and security (though honest relayers must exist to maintain relayer liveness). While relayers may choose any set of client parameters while creating a new `ClientState`, this still holds under the security model since users can always choose a relayer-created client that suits their security and correctness needs or create a Client with their desired parameters if no such client exists. - -However, when upgrading an existing client, one must keep in mind that there are already many users who depend on this client's particular parameters. We cannot give the upgrading relayer free choice over these parameters once they have already been chosen. This would violate the security model since users who rely on the client would have to rely on the upgrading relayer to maintain the same level of security. Thus, developers must make sure that their upgrade mechanism allows clients to upgrade the chain-specified parameters whenever a chain upgrade changes these parameters (examples in the Tendermint client include `UnbondingPeriod`, `ChainID`, `UpgradePath`, etc.), while ensuring that the relayer submitting the `UpgradeClientMsg` cannot alter the client-chosen parameters that the users are relying upon (examples in Tendermint client include `TrustingPeriod`, `TrustLevel`, `MaxClockDrift`, etc). - -Developers should maintain the distinction between Client parameters that are uniform across every valid light client of a chain (chain-chosen parameters), and Client parameters that are customizable by each individual client (client-chosen parameters); since this distinction is necessary to implement the `ZeroCustomFields` method in the `ClientState` interface: - -```go -// Utility function that zeroes out any client customizable fields in client state -// Ledger enforced fields are maintained while all custom fields are zero values -// Used to verify upgrades -ZeroCustomFields() ClientState -``` - -Counterparty clients can upgrade securely by using all of the chain-chosen parameters from the chain-committed `UpgradedClient` and preserving all of the old client-chosen parameters. This enables chains to securely upgrade without relying on an honest relayer, however it can in some cases lead to an invalid final `ClientState` if the new chain-chosen parameters clash with the old client-chosen parameter. This can happen in the Tendermint client case if the upgrading chain lowers the `UnbondingPeriod` (chain-chosen) to a duration below that of a counterparty client's `TrustingPeriod` (client-chosen). Such cases should be clearly documented by developers, so that chains know which upgrades should be avoided to prevent this problem. The final upgraded client should also be validated in `VerifyUpgradeAndUpdateState` before returning to ensure that the client does not upgrade to an invalid `ClientState`. diff --git a/versioned_docs/version-0.45/integrate/ibc/upgrades/quick-guide.md b/versioned_docs/version-0.45/integrate/ibc/upgrades/quick-guide.md deleted file mode 100644 index 22ff139b5..000000000 --- a/versioned_docs/version-0.45/integrate/ibc/upgrades/quick-guide.md +++ /dev/null @@ -1,50 +0,0 @@ -# How to Upgrade IBC Chains and their Clients - -Learn how to upgrade your chain and counterparty clients. {synopsis} - -The information in this doc for upgrading chains is relevant to SDK chains. However, the guide for counterparty clients is relevant to any Tendermint client that enables upgrades. - -### IBC Client Breaking Upgrades - -IBC-connected chains must perform an IBC upgrade if their upgrade will break counterparty IBC clients. The current IBC protocol supports upgrading tendermint chains for a specific subset of IBC-client-breaking upgrades. Here is the exhaustive list of IBC client-breaking upgrades and whether the IBC protocol currently supports such upgrades. - -IBC currently does **NOT** support unplanned upgrades. All of the following upgrades must be planned and committed to in advance by the upgrading chain, in order for counterparty clients to maintain their connections securely. - -Note: Since upgrades are only implemented for Tendermint clients, this doc only discusses upgrades on Tendermint chains that would break counterparty IBC Tendermint Clients. - -1. Changing the Chain-ID: **Supported** -2. Changing the UnbondingPeriod: **Partially Supported**, chains may increase the unbonding period with no issues. However, decreasing the unbonding period may irreversibly break some counterparty clients. Thus, it is **not recommended** that chains reduce the unbonding period. -3. Changing the height (resetting to 0): **Supported**, so long as chains remember to increment the revision number in their chain-id. -4. Changing the ProofSpecs: **Supported**, this should be changed if the proof structure needed to verify IBC proofs is changed across the upgrade. Ex: Switching from an IAVL store, to a SimpleTree Store -5. Changing the UpgradePath: **Supported**, this might involve changing the key under which upgraded clients and consensus states are stored in the upgrade store, or even migrating the upgrade store itself. -6. Migrating the IBC store: **Unsupported**, as the IBC store location is negotiated by the connection. -7. Upgrading to a backwards compatible version of IBC: Supported -8. Upgrading to a non-backwards compatible version of IBC: **Unsupported**, as IBC version is negotiated on connection handshake. -9. Changing the Tendermint LightClient algorithm: **Partially Supported**. Changes to the light client algorithm that do not change the ClientState or ConsensusState struct may be supported, provided that the counterparty is also upgraded to support the new light client algorithm. Changes that require updating the ClientState and ConsensusState structs themselves are theoretically possible by providing a path to translate an older ClientState struct into the new ClientState struct; however this is not currently implemented. - -### Step-by-Step Upgrade Process for SDK chains - -If the IBC-connected chain is conducting an upgrade that will break counterparty clients, it must ensure that the upgrade is first supported by IBC using the list above and then execute the upgrade process described below in order to prevent counterparty clients from breaking. - -1. Create an `UpgradeProposal` with an IBC ClientState in the `UpgradedClientState` field and a `UpgradePlan` in the `Plan` field. Note that the proposal `Plan` must specify an upgrade height **only** (no upgrade time), and the `ClientState` should only include the fields common to all valid clients and zero out any client-customizable fields (such as TrustingPeriod). -2. Vote on and pass the `UpgradeProposal` - -Upon the `UpgradeProposal` passing, the upgrade module will commit the UpgradedClient under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedClient`. On the block right before the upgrade height, the upgrade module will also commit an initial consensus state for the next chain under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedConsState`. - -Once the chain reaches the upgrade height and halts, a relayer can upgrade the counterparty clients to the last block of the old chain. They can then submit the proofs of the `UpgradedClient` and `UpgradedConsensusState` against this last block and upgrade the counterparty client. - -### Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients - -Once the upgrading chain has committed to upgrading, relayers must wait till the chain halts at the upgrade height before upgrading counterparty clients. This is because chains may reschedule or cancel upgrade plans before they occur. Thus, relayers must wait till the chain reaches the upgrade height and halts before they can be sure the upgrade will take place. - -Thus, the upgrade process for relayers trying to upgrade the counterparty clients is as follows: - -1. Wait for the upgrading chain to reach the upgrade height and halt -2. Query a full node for the proofs of `UpgradedClient` and `UpgradedConsensusState` at the last height of the old chain. -3. Update the counterparty client to the last height of the old chain using the `UpdateClient` msg. -4. Submit an `UpgradeClient` msg to the counterparty chain with the `UpgradedClient`, `UpgradedConsensusState` and their respective proofs. -5. Submit an `UpdateClient` msg to the counterparty chain with a header from the new upgraded chain. - -The Tendermint client on the counterparty chain will verify that the upgrading chain did indeed commit to the upgraded client and upgraded consensus state at the upgrade height (since the upgrade height is included in the key). If the proofs are verified against the upgrade height, then the client will upgrade to the new client while retaining all of its client-customized fields. Thus, it will retain its old TrustingPeriod, TrustLevel, MaxClockDrift, etc; while adopting the new chain-specified fields such as UnbondingPeriod, ChainId, UpgradePath, etc. Note, this can lead to an invalid client since the old client-chosen fields may no longer be valid given the new chain-chosen fields. Upgrading chains should try to avoid these situations by not altering parameters that can break old clients. For an example, see the UnbondingPeriod example in the supported upgrades section. - -The upgraded consensus state will serve purely as a basis of trust for future `UpdateClientMsgs` and will not contain a consensus root to perform proof verification against. Thus, relayers must submit an `UpdateClientMsg` with a header from the new chain so that the connection can be used for proof verification again. diff --git a/versioned_docs/version-0.45/integrate/modules/README.md b/versioned_docs/version-0.45/integrate/modules/README.md deleted file mode 100644 index eaa7b96fc..000000000 --- a/versioned_docs/version-0.45/integrate/modules/README.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -sidebar_position: 0 -slug : /modules ---- - -# Module Summary - -Here are some production-grade modules that can be used in Cosmos SDK applications, along with their respective documentation: - -* [Auth](auth/README.md) - Authentication of accounts and transactions for Cosmos SDK applications. -* [Authz](authz/README.md) - Authorization for accounts to perform actions on behalf of other accounts. -* [Bank](bank/README.md) - Token transfer functionalities. -* [Crisis](crisis/README.md) - Halting the blockchain under certain circumstances (e.g. if an invariant is broken). -* [Distribution](distribution/README.md) - Fee distribution, and staking token provision distribution. -* [Evidence](evidence/README.md) - Evidence handling for double signing, misbehaviour, etc. -* [Feegrant](feegrant/README.md) - Grant fee allowances for executing transactions. -* [Governance](gov/README.md) - On-chain proposals and voting. -* [Mint](mint/README.md) - Creation of new units of staking token. -* [Params](params/README.md) - Globally available parameter store. -* [Staking](staking/README.md) - Proof-of-Stake layer for public blockchains. -* [Upgrade](upgrade/README.md) - Software upgrades handling and coordination. - -To learn more about the process of building modules, visit the [building modules reference documentation](https://docs.cosmos.network/main/building-modules/intro). - -## IBC - -The IBC module for the SDK is maintained by the IBC Go team in its [own repository](https://github.com/cosmos/ibc-go). - -Additionally, the [capability module](https://github.com/cosmos/ibc-go/tree/fdd664698d79864f1e00e147f9879e58497b5ef1/modules/capability) is from v0.48+ maintained by the IBC Go team in its [own repository](https://github.com/cosmos/ibc-go/tree/fdd664698d79864f1e00e147f9879e58497b5ef1/modules/capability). - -## CosmWasm - -The CosmWasm module enables smart contracts, learn more by going to their [documentation site](https://book.cosmwasm.com/), or visit [the repository](https://github.com/CosmWasm/cosmwasm). - -## EVM - -Read more about writing smart contracts with solidity at the official [`evm` documentation page](https://docs.evmos.org/modules/evm/). diff --git a/versioned_docs/version-0.45/integrate/rfc/README.md b/versioned_docs/version-0.45/integrate/rfc/README.md deleted file mode 100644 index be74d080d..000000000 --- a/versioned_docs/version-0.45/integrate/rfc/README.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Requests for Comments - -A Request for Comments (RFC) is a record of discussion on an open-ended topic -related to the design and implementation of the Cosmos SDK, for which no -immediate decision is required. - -The purpose of an RFC is to serve as a historical record of a high-level -discussion that might otherwise only be recorded in an ad-hoc way (for example, -via gists or Google docs) that are difficult to discover for someone after the -fact. An RFC _may_ give rise to more specific architectural _decisions_ for -the Cosmos SDK, but those decisions must be recorded separately in -[Architecture Decision Records (ADR)](../architecture). - -As a rule of thumb, if you can articulate a specific question that needs to be -answered, write an ADR. If you need to explore the topic and get input from -others to know what questions need to be answered, an RFC may be appropriate. - -## RFC Content - -An RFC should provide: - -* A **changelog**, documenting when and how the RFC has changed. -* An **abstract**, briefly summarizing the topic so the reader can quickly tell - whether it is relevant to their interest. -* Any **background** a reader will need to understand and participate in the - substance of the discussion (links to other documents are fine here). -* The **discussion**, the primary content of the document. - -The [rfc-template.md](rfc-template.md) file includes placeholders for these -sections. diff --git a/versioned_docs/version-0.45/user/_category_.json b/versioned_docs/version-0.45/user/_category_.json deleted file mode 100644 index ac5540cce..000000000 --- a/versioned_docs/version-0.45/user/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "`User`", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.45/user/run-node/00-keyring.md b/versioned_docs/version-0.45/user/run-node/00-keyring.md deleted file mode 100644 index 81488c81d..000000000 --- a/versioned_docs/version-0.45/user/run-node/00-keyring.md +++ /dev/null @@ -1,132 +0,0 @@ -# Setting up the keyring - -This document describes how to configure and use the keyring and its various backends for an [**application**](../high-level-concepts/app-anatomy.md). {synopsis} - -The keyring holds the private/public keypairs used to interact with a node. For instance, a validator key needs to be set up before running the blockchain node, so that blocks can be correctly signed. The private key can be stored in different locations, called "backends", such as a file or the operating system's own key storage. - -## Available backends for the keyring - -Starting with the v0.38.0 release, Cosmos SDK comes with a new keyring implementation -that provides a set of commands to manage cryptographic keys in a secure fashion. The -new keyring supports multiple storage backends, some of which may not be available on -all operating systems. - -### The `os` backend - -The `os` backend relies on operating system-specific defaults to handle key storage -securely. Typically, an operating system's credential sub-system handles password prompts, -private keys storage, and user sessions according to the user's password policies. Here -is a list of the most popular operating systems and their respective passwords manager: - -- macOS (since Mac OS 8.6): [Keychain](https://support.apple.com/en-gb/guide/keychain-access/welcome/mac) -- Windows: [Credentials Management API](https://docs.microsoft.com/en-us/windows/win32/secauthn/credentials-management) -- GNU/Linux: - - [libsecret](https://gitlab.gnome.org/GNOME/libsecret) - - [kwallet](https://api.kde.org/frameworks/kwallet/html/index.html) - -GNU/Linux distributions that use GNOME as default desktop environment typically come with -[Seahorse](https://wiki.gnome.org/Apps/Seahorse). Users of KDE based distributions are -commonly provided with [KDE Wallet Manager](https://userbase.kde.org/KDE_Wallet_Manager). -Whilst the former is in fact a `libsecret` convenient frontend, the latter is a `kwallet` -client. - -`os` is the default option since operating system's default credentials managers are -designed to meet users' most common needs and provide them with a comfortable -experience without compromising on security. - -The recommended backends for headless environments are `file` and `pass`. - -### The `file` backend - -The `file` backend more closely resembles the keybase implementation used prior to -v0.38.1. It stores the keyring encrypted within the app's configuration directory. This -keyring will request a password each time it is accessed, which may occur multiple -times in a single command resulting in repeated password prompts. If using bash scripts -to execute commands using the `file` option you may want to utilize the following format -for multiple prompts: - -```sh -# assuming that KEYPASSWD is set in the environment -$ gaiacli config keyring-backend file # use file backend -$ (echo $KEYPASSWD; echo $KEYPASSWD) | gaiacli keys add me # multiple prompts -$ echo $KEYPASSWD | gaiacli keys show me # single prompt -``` - -::: tip -The first time you add a key to an empty keyring, you will be prompted to type the password twice. -::: - -### The `pass` backend - -The `pass` backend uses the [pass](https://www.passwordstore.org/) utility to manage on-disk -encryption of keys' sensitive data and metadata. Keys are stored inside `gpg` encrypted files -within app-specific directories. `pass` is available for the most popular UNIX -operating systems as well as GNU/Linux distributions. Please refer to its manual page for -information on how to download and install it. - -::: tip -**pass** uses [GnuPG](https://gnupg.org/) for encryption. `gpg` automatically invokes the `gpg-agent` -daemon upon execution, which handles the caching of GnuPG credentials. Please refer to `gpg-agent` -man page for more information on how to configure cache parameters such as credentials TTL and -passphrase expiration. -::: - -The password store must be set up prior to first use: - -```sh -pass init -``` - -Replace `` with your GPG key ID. You can use your personal GPG key or an alternative -one you may want to use specifically to encrypt the password store. - -### The `kwallet` backend - -The `kwallet` backend uses `KDE Wallet Manager`, which comes installed by default on the -GNU/Linux distributions that ships KDE as default desktop environment. Please refer to -[KWallet Handbook](https://docs.kde.org/stable5/en/kdeutils/kwallet5/index.html) for more -information. - -### The `test` backend - -The `test` backend is a password-less variation of the `file` backend. Keys are stored -unencrypted on disk. - -**Provided for testing purposes only. The `test` backend is not recommended for use in production environments**. - -### The `memory` backend - -The `memory` backend stores keys in memory. The keys are immediately deleted after the program has exited. - -**Provided for testing purposes only. The `memory` backend is not recommended for use in production environments**. - -## Adding keys to the keyring - -::: warning -Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets. -::: - -Applications developed using the Cosmos SDK come with the `keys` subcommand. For the purpose of this tutorial, we're running the `simd` CLI, which is an application built using the Cosmos SDK for testing and educational purposes. For more information, see [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp). - -You can use `simd keys` for help about the keys command and `simd keys [command] --help` for more information about a particular subcommand. - -::: tip -You can also enable auto-completion with the `simd completion` command. For example, at the start of a bash session, run `. <(simd completion)`, and all `simd` subcommands will be auto-completed. -::: - -To create a new key in the keyring, run the `add` subcommand with a `` argument. For the purpose of this tutorial, we will solely use the `test` backend, and call our new key `my_validator`. This key will be used in the next section. - -```bash -$ simd keys add my_validator --keyring-backend test - -# Put the generated address in a variable for later use. -MY_VALIDATOR_ADDRESS=$(simd keys show my_validator -a --keyring-backend test) -``` - -This command generates a new 24-word mnemonic phrase, persists it to the relevant backend, and outputs information about the keypair. If this keypair will be used to hold value-bearing tokens, be sure to write down the mnemonic phrase somewhere safe! - -By default, the keyring generates a `secp256k1` keypair. The keyring also supports `ed25519` keys, which may be created by passing the `--algo ed25519` flag. A keyring can of course hold both types of keys simultaneously, and the Cosmos SDK's `x/auth` module (in particular its [AnteHandlers](../../develop/advanced-concepts/00-baseapp.md#antehandler)) supports natively these two public key algorithms. - -## Next {hide} - -Read about [running a node](./run-node.md) {hide} diff --git a/versioned_docs/version-0.45/user/run-node/01-run-node.md b/versioned_docs/version-0.45/user/run-node/01-run-node.md deleted file mode 100644 index 69fe24f29..000000000 --- a/versioned_docs/version-0.45/user/run-node/01-run-node.md +++ /dev/null @@ -1,124 +0,0 @@ -# Running a Node - -Now that the application is ready and the keyring populated, it's time to see how to run the blockchain node. In this section, the application we are running is called [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp), and its corresponding CLI binary `simd`. {synopsis} - -## Pre-requisite Readings - -- [Anatomy of an SDK Application](../high-level-concepts/app-anatomy.md) {prereq} -- [Setting up the keyring](./keyring.md) {prereq} - -## Initialize the Chain - -::: warning -Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets. -::: - -Before actually running the node, we need to initialize the chain, and most importantly its genesis file. This is done with the `init` subcommand: - -```bash -# The argument is the custom username of your node, it should be human-readable. -simd init --chain-id my-test-chain -``` - -The command above creates all the configuration files needed for your node to run, as well as a default genesis file, which defines the initial state of the network. All these configuration files are in `~/.simapp` by default, but you can overwrite the location of this folder by passing the `--home` flag. - -The `~/.simapp` folder has the following structure: - -```bash -. # ~/.simapp - |- data # Contains the databases used by the node. - |- config/ - |- app.toml # Application-related configuration file. - |- config.toml # Tendermint-related configuration file. - |- genesis.json # The genesis file. - |- node_key.json # Private key to use for node authentication in the p2p protocol. - |- priv_validator_key.json # Private key to use as a validator in the consensus protocol. -``` - -## Updating Some Default Settings - -If you want to change any field values in configuration files (for ex: genesis.json) you can use `jq` ([installation](https://stedolan.github.io/jq/download/) & [docs](https://stedolan.github.io/jq/manual/#Assignment)) & `sed` commands to do that. Few examples are listed here. - -```bash -# to change the chain-id -jq '.chain_id = "testing"' genesis.json > temp.json && mv temp.json genesis.json - -# to enable the api server -sed -i '/\[api\]/,+3 s/enable = false/enable = true/' app.toml - -# to change the voting_period -jq '.app_state.gov.voting_params.voting_period = "600s"' genesis.json > temp.json && mv temp.json genesis.json - -# to change the inflation -jq '.app_state.mint.minter.inflation = "0.300000000000000000"' genesis.json > temp.json && mv temp.json genesis.json -``` - -## Adding Genesis Accounts - -Before starting the chain, you need to populate the state with at least one account. To do so, first [create a new account in the keyring](./keyring.md#adding-keys-to-the-keyring) named `my_validator` under the `test` keyring backend (feel free to choose another name and another backend). - -Now that you have created a local account, go ahead and grant it some `stake` tokens in your chain's genesis file. Doing so will also make sure your chain is aware of this account's existence: - -```bash -simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000000stake -``` - -Recall that `$MY_VALIDATOR_ADDRESS` is a variable that holds the address of the `my_validator` key in the [keyring](./keyring.md#adding-keys-to-the-keyring). Also note that the tokens in the SDK have the `{amount}{denom}` format: `amount` is is a 18-digit-precision decimal number, and `denom` is the unique token identifier with its denomination key (e.g. `atom` or `uatom`). Here, we are granting `stake` tokens, as `stake` is the token identifier used for staking in [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp). For your own chain with its own staking denom, that token identifier should be used instead. - -Now that your account has some tokens, you need to add a validator to your chain. Validators are special full-nodes that participate in the consensus process (implemented in the [underlying consensus engine](../intro/sdk-app-architecture.md#tendermint)) in order to add new blocks to the chain. Any account can declare its intention to become a validator operator, but only those with sufficient delegation get to enter the active set (for example, only the top 125 validator candidates with the most delegation get to be validators in the Cosmos Hub). For this guide, you will add your local node (created via the `init` command above) as a validator of your chain. Validators can be declared before a chain is first started via a special transaction included in the genesis file called a `gentx`: - -```bash -# Create a gentx. -simd gentx my_validator 100000000stake --chain-id my-test-chain --keyring-backend test - -# Add the gentx to the genesis file. -simd collect-gentxs -``` - -A `gentx` does three things: - -1. Registers the `validator` account you created as a validator operator account (i.e. the account that controls the validator). -2. Self-delegates the provided `amount` of staking tokens. -3. Link the operator account with a Tendermint node pubkey that will be used for signing blocks. If no `--pubkey` flag is provided, it defaults to the local node pubkey created via the `simd init` command above. - -For more information on `gentx`, use the following command: - -```bash -simd gentx --help -``` - -## Configuring the Node Using `app.toml` and `config.toml` - -The Cosmos SDK automatically generates two configuration files inside `~/.simapp/config`: - -- `config.toml`: used to configure the Tendermint, learn more on [Tendermint's documentation](https://docs.tendermint.com/master/nodes/configuration.html), -- `app.toml`: generated by the Cosmos SDK, and used to configure your app, such as state pruning strategies, telemetry, gRPC and REST servers configuration, state sync... - -Both files are heavily commented, please refer to them directly to tweak your node. - -One example config to tweak is the `minimum-gas-prices` field inside `app.toml`, which defines the minimum gas prices the validator node is willing to accept for processing a transaction. Depending on the chain, it might be an empty string or not. If it's empty, make sure to edit the field with some value, for example `10token`, or else the node will halt on startup. For the purpose of this tutorial, let's set the minimum gas price to 0: - -```toml - # The minimum gas prices a validator is willing to accept for processing a - # transaction. A transaction's fees must meet the minimum of any denomination - # specified in this config (e.g. 0.25token1;0.0001token2). - minimum-gas-prices = "0stake" -``` - -## Run a Localnet - -Now that everything is set up, you can finally start your node: - -```bash -simd start -``` - -You should see blocks come in. - -The previous command allow you to run a single node. This is enough for the next section on interacting with this node, but you may wish to run multiple nodes at the same time, and see how consensus happens between them. - -The naive way would be to run the same commands again in separate terminal windows. This is possible, however in the SDK, we leverage the power of [Docker Compose](https://docs.docker.com/compose/) to run a localnet. If you need inspiration on how to set up your own localnet with Docker Compose, you can have a look at the SDK's [`docker-compose.yml`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/docker-compose.yml). - -## Next {hide} - -Read about the [Interacting with your Node](./interact-node.md) {hide} diff --git a/versioned_docs/version-0.45/user/run-node/02-interact-node.md b/versioned_docs/version-0.45/user/run-node/02-interact-node.md deleted file mode 100644 index 16dac21d6..000000000 --- a/versioned_docs/version-0.45/user/run-node/02-interact-node.md +++ /dev/null @@ -1,227 +0,0 @@ -# Interacting with the Node - -There are multiple ways to interact with a node: using the CLI, using gRPC or using the REST endpoints. {synopsis} - -## Pre-requisite Readings - -- [gRPC, REST and Tendermint Endpoints](../core/08-grpc_rest.md) {prereq} -- [Running a Node](./run-node.md) {prereq} - -## Using the CLI - -Now that your chain is running, it is time to try sending tokens from the first account you created to a second account. In a new terminal window, start by running the following query command: - -```bash -simd query bank balances $MY_VALIDATOR_ADDRESS --chain-id my-test-chain -``` - -You should see the current balance of the account you created, equal to the original balance of `stake` you granted it minus the amount you delegated via the `gentx`. Now, create a second account: - -```bash -simd keys add recipient --keyring-backend test - -# Put the generated address in a variable for later use. -RECIPIENT=$(simd keys show recipient -a --keyring-backend test) -``` - -The command above creates a local key-pair that is not yet registered on the chain. An account is created the first time it receives tokens from another account. Now, run the following command to send tokens to the `recipient` account: - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000000stake --chain-id my-test-chain --keyring-backend test - -# Check that the recipient account did receive the tokens. -simd query bank balances $RECIPIENT --chain-id my-test-chain -``` - -Finally, delegate some of the stake tokens sent to the `recipient` account to the validator: - -```bash -simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain --keyring-backend test - -# Query the total delegations to `validator`. -simd query staking delegations-to $(simd keys show my_validator --bech val -a --keyring-backend test) --chain-id my-test-chain -``` - -You should see two delegations, the first one made from the `gentx`, and the second one you just performed from the `recipient` account. - -## Using gRPC - -The Protobuf ecosystem developed tools for different use cases, including code-generation from `*.proto` files into various languages. These tools allow the building of clients easily. Often, the client connection (i.e. the transport) can be plugged and replaced very easily. Let's explore one of the most popular transport: [gRPC](../core/08-grpc_rest.md). - -Since the code generation library largely depends on your own tech stack, we will only present three alternatives: - -- `grpcurl` for generic debugging and testing, -- programmatically via Go, -- CosmJS for JavaScript/TypeScript developers. - -### grpcurl - -[grpcurl](https://github.com/fullstorydev/grpcurl) is like `curl` but for gRPC. It is also available as a Go library, but we will use it only as a CLI command for debugging and testing purposes. Follow the instructions in the previous link to install it. - -Assuming you have a local node running (either a localnet, or connected a live network), you should be able to run the following command to list the Protobuf services available (you can replace `localhost:9000` by the gRPC server endpoint of another node, which is configured under the `grpc.address` field inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml)): - -```bash -grpcurl -plaintext localhost:9090 list -``` - -You should see a list of gRPC services, like `cosmos.bank.v1beta1.Query`. This is called reflection, which is a Protobuf endpoint returning a description of all available endpoints. Each of these represents a different Protobuf service, and each service exposes multiple RPC methods you can query against. - -In order to get a description of the service you can run the following command: - -```bash -grpcurl \ - localhost:9090 \ - describe cosmos.bank.v1beta1.Query # Service we want to inspect -``` - -It's also possible to execute an RPC call to query the node for information: - -```bash -grpcurl \ - -plaintext - -d '{"address":"$MY_VALIDATOR"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/AllBalances -``` - -The list of all available gRPC query endpoints is [coming soon](https://github.com/cosmos/cosmos-sdk/issues/7786). - -#### Query for historical state using grpcurl - -You may also query for historical data by passing some [gRPC metadata](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) to the query: the `x-cosmos-block-height` metadata should contain the block to query. Using grpcurl as above, the command looks like: - -```bash -grpcurl \ - -plaintext \ - -H "x-cosmos-block-height: 279256" \ - -d '{"address":"$MY_VALIDATOR"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/AllBalances -``` - -Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response. - -### Programmatically via Go - -The following snippet shows how to query the state using gRPC inside a Go program. The idea is to create a gRPC connection, and use the Protobuf-generated client code to query the gRPC server. - -```go -import ( - "context" - "fmt" - - "google.golang.org/grpc" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/tx" -) - -func queryState() error { - myAddress, err := sdk.AccAddressFromBech32("cosmos1...") - if err != nil { - return err - } - - // Create a connection to the gRPC server. - grpcConn := grpc.Dial( - "127.0.0.1:9090", // your gRPC server address. - grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism. - ) - defer grpcConn.Close() - - // This creates a gRPC client to query the x/bank service. - bankClient := banktypes.NewQueryClient(grpcConn) - bankRes, err := bankClient.Balance( - context.Background(), - &banktypes.QueryBalanceRequest{Address: myAddress, Denom: "atom"}, - ) - if err != nil { - return err - } - - fmt.Println(bankRes.GetBalance()) // Prints the account balance - - return nil -} -``` - -You can replace the query client (here we are using `x/bank`'s) with one generated from any other Protobuf service. The list of all available gRPC query endpoints is [coming soon](https://github.com/cosmos/cosmos-sdk/issues/7786). - -#### Query for historical state using Go - -Querying for historical blocks is done by adding the block height metadata in the gRPC request. - -```go -import ( - "context" - "fmt" - - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - - grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" - "github.com/cosmos/cosmos-sdk/types/tx" -) - -func queryState() error { - // --snip-- - - var header metadata.MD - bankRes, err = bankClient.Balance( - metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, "12"), // Add metadata to request - &banktypes.QueryBalanceRequest{Address: myAddress, Denom: denom}, - grpc.Header(&header), // Retrieve header from response - ) - if err != nil { - return err - } - blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) - - fmt.Println(blockHeight) // Prints the block height (12) - - return nil -} -``` - -### CosmJS - -CosmJS documentation can be found at [https://cosmos.github.io/cosmjs](https://cosmos.github.io/cosmjs). As of January 2021, CosmJS documentation is still work in progress. - -## Using the REST Endpoints - -As described in the [gRPC guide](../core/08-grpc_rest.md), all gRPC services on the Cosmos SDK are made available for more convenient REST-based queries through gRPC-gateway. The format of the URL path is based on the Protobuf service method's full-qualified name, but may contain small customizations so that final URLs look more idiomatic. For example, the REST endpoint for the `cosmos.bank.v1beta1.Query/AllBalances` method is `GET /cosmos/bank/v1beta1/balances/{address}`. Request arguments are passed as query parameters. - -As a concrete example, the `curl` command to make balances request is: - -```bash -curl \ - -X GET \ - -H "Content-Type: application/json" \ - http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR -``` - -Make sure to replace `localhost:1317` with the REST endpoint of your node, configured under the `api.address` field. - -The list of all available REST endpoints is available as a Swagger specification file, it can be viewed at `localhost:1317/swagger`. Make sure that the `api.swagger` field is set to true in your [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) file. - -### Query for historical state using REST - -Querying for historical state is done using the HTTP header `x-cosmos-block-height`. For example, a curl command would look like: - -```bash -curl \ - -X GET \ - -H "Content-Type: application/json" \ - -H "x-cosmos-block-height: 279256" - http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR -``` - -Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response. - -### Cross-Origin Resource Sharing (CORS) - -[CORS policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are not enabled by default to help with security. If you would like to use the rest-server in a public environment we recommend you provide a reverse proxy, this can be done with [nginx](https://www.nginx.com/). For testing and development purposes there is an `enabled-unsafe-cors` field inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). - -## Next {hide} - -Sending transactions using gRPC and REST requires some additional steps: generating the transaction, signing it, and finally broadcasting it. Read about [generating and signing transactions](./txs.md). {hide} diff --git a/versioned_docs/version-0.45/user/run-node/03-txs.md b/versioned_docs/version-0.45/user/run-node/03-txs.md deleted file mode 100644 index e54314630..000000000 --- a/versioned_docs/version-0.45/user/run-node/03-txs.md +++ /dev/null @@ -1,355 +0,0 @@ -# Generating, Signing and Broadcasting Transactions - -This document describes how to generate an (unsigned) transaction, signing it (with one or multiple keys), and broadcasting it to the network. {synopsis} - -## Using the CLI - -The easiest way to send transactions is using the CLI, as we have seen in the previous page when [interacting with a node](./interact-node.md#using-the-cli). For example, running the following command - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --keyring-backend test -``` - -will run the following steps: - -- generate a transaction with one `Msg` (`x/bank`'s `MsgSend`), and print the generated transaction to the console. -- ask the user for confirmation to send the transaction from the `$MY_VALIDATOR_ADDRESS` account. -- fetch `$MY_VALIDATOR_ADDRESS` from the keyring. This is possible because we have [set up the CLI's keyring](./keyring.md) in a previous step. -- sign the generated transaction with the keyring's account. -- broadcast the signed transaction to the network. This is possible because the CLI connects to the node's Tendermint RPC endpoint. - -The CLI bundles all the necessary steps into a simple-to-use user experience. However, it's possible to run all the steps individually too. - -### Generating a Transaction - -Generating a transaction can simply be done by appending the `--generate-only` flag on any `tx` command, e.g.: - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --generate-only -``` - -This will output the unsigned transaction as JSON in the console. We can also save the unsigned transaction to a file (to be passed around between signers more easily) by appending `> unsigned_tx.json` to the above command. - -### Signing a Transaction - -Signing a transaction using the CLI requires the unsigned transaction to be saved in a file. Let's assume the unsigned transaction is in a file called `unsigned_tx.json` in the current directory (see previous paragraph on how to do that). Then, simply run the following command: - -```bash -simd tx sign unsigned_tx.json --chain-id my-test-chain --keyring-backend test --from $MY_VALIDATOR_ADDRESS -``` - -This command will decode the unsigned transaction and sign it with `SIGN_MODE_DIRECT` with `$MY_VALIDATOR_ADDRESS`'s key, which we already set up in the keyring. The signed transaction will be output as JSON to the console, and, as above, we can save it to a file by appending `> signed_tx.json`. - -Some useful flags to consider in the `tx sign` command: - -- `--sign-mode`: you may use `amino-json` to sign the transaction using `SIGN_MODE_LEGACY_AMINO_JSON`, -- `--offline`: sign in offline mode. This means that the `tx sign` command doesn't connect to the node to retrieve the signer's account number and sequence, both needed for signing. In this case, you must manually supply the `--account-number` and `--sequence` flags. This is useful for offline signing, i.e. signing in a secure environment which doesn't have access to the internet. - -#### Signing with Multiple Signers - -::: warning -Please note that signing a transaction with multiple signers or with a multisig account, where at least one signer uses `SIGN_MODE_DIRECT`, is not yet possible. You may follow [this Github issue](https://github.com/cosmos/cosmos-sdk/issues/8141) for more info. -::: - -Signing with multiple signers is done with the `tx multisign` command. This command assumes that all signers use `SIGN_MODE_LEGACY_AMINO_JSON`. The flow is similar to the `tx sign` command flow, but instead of signing an unsigned transaction file, each signer signs the file signed by previous signer(s). The `tx multisign` command will append signatures to the existing transactions. It is important that signers sign the transaction **in the same order** as given by the transaction, which is retrievable using the `GetSigners()` method. - -For example, starting with the `unsigned_tx.json`, and assuming the transaction has 4 signers, we would run: - -```bash -# Let signer1 sign the unsigned tx. -simd tx multisignsign unsigned_tx.json signer_key_1 --chain-id my-test-chain --keyring-backend test > partial_tx_1.json -# Now signer1 will send the partial_tx_1.json to the signer2. -# Signer2 appends their signature: -simd tx multisignsign partial_tx_1.json signer_key_2 --chain-id my-test-chain --keyring-backend test > partial_tx_2.json -# Signer2 sends the partial_tx_2.json file to signer3, and signer3 can append his signature: -simd tx multisignsign partial_tx_2.json signer_key_3 --chain-id my-test-chain --keyring-backend test > partial_tx_3.json -``` - -### Broadcasting a Transaction - -Broadcasting a transaction is done using the following command: - -```bash -simd tx broadcast tx_signed.json -``` - -You may optionally pass the `--broadcast-mode` flag to specify which response to receive from the node: - -- `block`: the CLI waits for the tx to be committed in a block. -- `sync`: the CLI waits for a CheckTx execution response only. -- `async`: the CLI returns immediately (transaction might fail). - -## Programmatically with Go - -It is possible to manipulate transactions programmatically via Go using the Cosmos SDK's `TxBuilder` interface. - -### Generating a Transaction - -Before generating a transaction, a new instance of a `TxBuilder` needs to be created. Since the SDK supports both Amino and Protobuf transactions, the first step would be to decide which encoding scheme to use. All the subsequent steps remain unchanged, whether you're using Amino or Protobuf, as `TxBuilder` abstracts the encoding mechanisms. In the following snippet, we will use Protobuf. - -```go -import ( - "github.com/cosmos/cosmos-sdk/simapp" -) - -func sendTx() error { - // Choose your codec: Amino or Protobuf. Here, we use Protobuf, given by the - // following function. - encCfg := simapp.MakeTestEncodingConfig() - - // Create a new TxBuilder. - txBuilder := encCfg.TxConfig.NewTxBuilder() - - // --snip-- -} -``` - -We can also set up some keys and addresses that will send and receive the transactions. Here, for the purpose of the tutorial, we will be using some dummy data to create keys. - -```go -import ( - "github.com/cosmos/cosmos-sdk/testutil/testdata" -) - -priv1, _, addr1 := testdata.KeyTestPubAddr() -priv2, _, addr2 := testdata.KeyTestPubAddr() -priv3, _, addr3 := testdata.KeyTestPubAddr() -``` - -Populating the `TxBuilder` can be done via its [methods](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/client/tx_config.go#L32-L45): - -```go -import ( - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -func sendTx() error { - // --snip-- - - // Define two x/bank MsgSend messages: - // - from addr1 to addr3, - // - from addr2 to addr3. - // This means that the transactions needs two signers: addr1 and addr2. - msg1 := banktypes.NewMsgSend(addr1, addr3, types.NewCoins(types.NewInt64Coin("atom", 12))) - msg2 := banktypes.NewMsgSend(addr2, addr3, types.NewCoins(types.NewInt64Coin("atom", 34))) - - err := txBuilder.SetMsgs(msg1, msg2) - if err != nil { - return err - } - - txBuilder.SetGasLimit(...) - txBuilder.SetFeeAmount(...) - txBuilder.SetMemo(...) - txBuilder.SetTimeoutHeight(...) -} -``` - -At this point, `TxBuilder`'s underlying transaction is ready to be signed. - -### Signing a Transaction - -We set encoding config to use Protobuf, which will use `SIGN_MODE_DIRECT` by default. As per [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/docs/architecture/adr-020-protobuf-transaction-encoding.md), each signer needs to sign the `SignerInfo`s of all other signers. This means that we need to perform two steps sequentially: - -- for each signer, populate the signer's `SignerInfo` inside `TxBuilder`, -- once all `SignerInfo`s are populated, for each signer, sign the `SignDoc` (the payload to be signed). - -In the current `TxBuilder`'s API, both steps are done using the same method: `SetSignatures()`. The current API requires us to first perform a round of `SetSignatures()` _with empty signatures_, only to populate `SignerInfo`s, and a second round of `SetSignatures()` to actually sign the correct payload. - -```go -import ( - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" -) - -func sendTx() error { - // --snip-- - - privs := []cryptotypes.PrivKey{priv1, priv2} - accNums:= []uint64{..., ...} // The accounts' account numbers - accSeqs:= []uint64{..., ...} // The accounts' sequence numbers - - // First round: we gather all the signer infos. We use the "set empty - // signature" hack to do that. - var sigsV2 []signing.SignatureV2 - for i, priv := range privs { - sigV2 := signing.SignatureV2{ - PubKey: priv.PubKey(), - Data: &signing.SingleSignatureData{ - SignMode: encCfg.TxConfig.SignModeHandler().DefaultMode(), - Signature: nil, - }, - Sequence: accSeqs[i], - } - - sigsV2 = append(sigsV2, sigV2) - } - err := txBuilder.SetSignatures(sigsV2...) - if err != nil { - return err - } - - // Second round: all signer infos are set, so each signer can sign. - sigsV2 = []signing.SignatureV2{} - for i, priv := range privs { - signerData := xauthsigning.SignerData{ - ChainID: chainID, - AccountNumber: accNums[i], - Sequence: accSeqs[i], - } - sigV2, err := tx.SignWithPrivKey( - encCfg.TxConfig.SignModeHandler().DefaultMode(), signerData, - txBuilder, priv, encCfg.TxConfig, accSeqs[i]) - if err != nil { - return nil, err - } - - sigsV2 = append(sigsV2, sigV2) - } - err = txBuilder.SetSignatures(sigsV2...) - if err != nil { - return err - } -} -``` - -The `TxBuilder` is now correctly populated. To print it, you can use the `TxConfig` interface from the initial encoding config `encCfg`: - -```go -func sendTx() error { - // --snip-- - - // Generated Protobuf-encoded bytes. - txBytes, err := encCfg.TxConfig.TxEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - - // Generate a JSON string. - txJSONBytes, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - txJSON := string(txJSONBytes) -} -``` - -### Broadcasting a Transaction - -The preferred way to broadcast a transaction is to use gRPC, though using REST (via `gRPC-gateway`) or the Tendermint RPC is also posible. An overview of the differences between these methods is exposed [here](../core/08-grpc_rest.md). For this tutorial, we will only describe the gRPC method. - -```go -import ( - "context" - "fmt" - - "google.golang.org/grpc" - - "github.com/cosmos/cosmos-sdk/types/tx" -) - -func sendTx(ctx context.Context) error { - // --snip-- - - // Create a connection to the gRPC server. - grpcConn := grpc.Dial( - "127.0.0.1:9090", // Or your gRPC server address. - grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism. - ) - defer grpcConn.Close() - - // Broadcast the tx via gRPC. We create a new client for the Protobuf Tx - // service. - txClient := tx.NewServiceClient(grpcConn) - // We then call the BroadcastTx method on this client. - grpcRes, err := txClient.BroadcastTx( - ctx, - &tx.BroadcastTxRequest{ - Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step. - }, - ) - if err != nil { - return err - } - - fmt.Println(grpcRes.TxResponse.Code) // Should be `0` if the tx is successful - - return nil -} -``` - -#### Simulating a Transaction - -Before broadcasting a transaction, we sometimes may want to dry-run the transaction, to estimate some information about the transaction without actually committing it. This is called simulating a transaction, and can be done as follows: - -```go -import ( - "context" - "fmt" - "testing" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/types/tx" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" -) - -func simulateTx() error { - // --snip-- - - // Simulate the tx via gRPC. We create a new client for the Protobuf Tx - // service. - txClient := tx.NewServiceClient(grpcConn) - txBytes := /* Fill in with your signed transaction bytes. */ - - // We then call the Simulate method on this client. - grpcRes, err := txClient.Simulate( - context.Background(), - &tx.SimulateRequest{ - TxBytes: txBytes, - }, - ) - if err != nil { - return err - } - - fmt.Println(grpcRes.GasInfo) // Prints estimated gas used. - - return nil -} -``` - -## Using gRPC - -It is not possible to generate or sign a transaction using gRPC, only to broadcast one. - -### Broadcasting a Transaction - -Broadcasting a transaction using the gRPC endpoint can be done by sending a `BroadcastTx` request as follows, where the `txBytes` are the protobuf-encoded bytes of a signed transaction: - -```bash -grpcurl -plaintext \ - -d '{"tx_bytes":"{{txBytes}}","mode":"BROADCAST_MODE_SYNC"}' \ - localhost:9090 \ - cosmos.tx.v1beta1.Service/BroadcastTx -``` - -## Using REST - -It is not possible to generate or sign a transaction using REST, only to broadcast one. - -### Broadcasting a Transaction - -Broadcasting a transaction using the REST endpoint (served by `gRPC-gateway`) can be done by sending a POST request as follows, where the `txBytes` are the protobuf-encoded bytes of a signed transaction: - -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -d'{"tx_bytes":"{{txBytes}}","mode":"BROADCAST_MODE_SYNC"}' \ - localhost:1317/cosmos/tx/v1beta1/txs -``` - -## Using CosmJS (JavaScript & TypeScript) - -CosmJS aims to build client libraries in JavaScript that can be embedded in web applications. Please see [https://cosmos.github.io/cosmjs](https://cosmos.github.io/cosmjs) for more information. As of January 2021, CosmJS documentation is still work in progress. diff --git a/versioned_docs/version-0.45/user/run-node/04-rosetta.md b/versioned_docs/version-0.45/user/run-node/04-rosetta.md deleted file mode 100644 index 49f648665..000000000 --- a/versioned_docs/version-0.45/user/run-node/04-rosetta.md +++ /dev/null @@ -1,133 +0,0 @@ -# Rosetta - -The `rosetta` package implements Coinbase's [Rosetta API](https://www.rosetta-api.org). This document provides instructions on how to use the Rosetta API integration. For information about the motivation and design choices, refer to [ADR 035](../architecture/adr-035-rosetta-api-support.md). - -## Add Rosetta Command - -The Rosetta API server is a stand-alone server that connects to a node of a chain developed with Cosmos SDK. - -To enable Rosetta API support, it's required to add the `RosettaCommand` to your application's root command file (e.g. `appd/cmd/root.go`). - -Import the `server` package: - -```go - "github.com/cosmos/cosmos-sdk/server" -``` - -Find the following line: - -```go -initRootCmd(rootCmd, encodingConfig) -``` - -After that line, add the following: - -```go -rootCmd.AddCommand( - server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler) -) -``` - -The `RosettaCommand` function builds the `rosetta` root command and is defined in the `server` package within Cosmos SDK. - -Since we’ve updated the Cosmos SDK to work with the Rosetta API, updating the application's root command file is all you need to do. - -An implementation example can be found in `simapp` package. - -## Use Rosetta Command - -To run Rosetta in your application CLI, use the following command: - -``` -appd rosetta --help -``` - -To test and run Rosetta API endpoints for applications that are running and exposed, use the following command: - -``` -appd rosetta - --blockchain "your application name (ex: gaia)" - --network "your chain identifier (ex: testnet-1)" - --tendermint "tendermint endpoint (ex: localhost:26657)" - --grpc "gRPC endpoint (ex: localhost:9090)" - --addr "rosetta binding address (ex: :8080)" -``` - -## Extension - -There are two ways in which you can customize and extend the implementation with your custom settings. - -### Message extension - -In order to make an `sdk.Msg` understandable by rosetta the only thing which is required is adding the methods to your message that satisfy the `rosetta.Msg` interface. -Examples on how to do so can be found in the staking types such as `MsgDelegate`, or in bank types such as `MsgSend`. - -### Client interface override - -In case more customization is required, it's possible to embed the Client type and override the methods which require customizations. - -Example: - -```go -package custom_client -import ( - -"context" -"github.com/coinbase/rosetta-sdk-go/types" -"github.com/cosmos/cosmos-sdk/server/rosetta/lib" -) - -// CustomClient embeds the standard cosmos client -// which means that it implements the cosmos-rosetta-gateway Client -// interface while at the same time allowing to customize certain methods -type CustomClient struct { - *rosetta.Client -} - -func (c *CustomClient) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) { - // provide custom signature bytes - panic("implement me") -} -``` - -### Error extension - -Since rosetta requires to provide 'returned' errors to network options. In order to declare a new rosetta error, we use the `errors` package in cosmos-rosetta-gateway. - -Example: - -```go -package custom_errors -import crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors" - -var customErrRetriable = true -var CustomError = crgerrs.RegisterError(100, "custom message", customErrRetriable, "description") -``` - -Note: errors must be registered before cosmos-rosetta-gateway's `Server`.`Start` method is called. Otherwise the registration will be ignored. Errors with same code will be ignored too. - -## Integration in app.go - -To integrate rosetta as a command in your application, in app.go, in your root command simply use the `server.RosettaCommand` method. - -Example: - -```go -package app -import ( - -"github.com/cosmos/cosmos-sdk/server" -"github.com/spf13/cobra" -) - -func buildAppCommand(rootCmd *cobra.Command) { - // more app.go init stuff - // ... - // add rosetta command - rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) -} -``` - -A full implementation example can be found in `simapp` package. - -NOTE: when using a customized client, the command cannot be used as the constructors required **may** differ, so it's required to create a new one. We intend to provide a way to init a customized client without writing extra code in the future. diff --git a/versioned_docs/version-0.45/user/run-node/05-run-testnet.md b/versioned_docs/version-0.45/user/run-node/05-run-testnet.md deleted file mode 100644 index 868a8a33b..000000000 --- a/versioned_docs/version-0.45/user/run-node/05-run-testnet.md +++ /dev/null @@ -1,95 +0,0 @@ -# Running a Testnet - -The `simd testnet` subcommand makes it easy to initialize and start a simulated test network for testing purposes. {synopsis} - -In addition to the commands for [running a node](./run-node.html), the `simd` binary also includes a `testnet` command that allows you to start a simulated test network in-process or to initialize files for a simulated test network that runs in a separate process. - -## Initialize Files - -First, let's take a look at the `init-files` subcommand. - -This is similar to the `init` command when initializing a single node, but in this case we are initializing multiple nodes, generating the genesis transactions for each node, and then collecting those transactions. - -The `init-files` subcommand initializes the necessary files to run a test network in a separate process (i.e. using a Docker container). Running this command is not a prerequisite for the `start` subcommand ([see below](#start-testnet)). - -In order to initialize the files for a test network, run the following command: - -```bash -simd testnet init-files -``` - -You should see the following output in your terminal: - -```bash -Successfully initialized 4 node directories -``` - -The default output directory is a relative `.testnets` directory. Let's take a look at the files created within the `.testnets` directory. - -### gentxs - -The `gentxs` directory includes a genesis transaction for each validator node. Each file includes a JSON encoded genesis transaction used to register a validator node at the time of genesis. The genesis transactions are added to the `genesis.json` file within each node directory during the initilization process. - -### nodes - -A node directory is created for each validator node. Within each node directory is a `simd` directory. The `simd` directory is the home directory for each node, which includes the configuration and data files for that node (i.e. the same files included in the default `~/.simapp` directory when running a single node). - -## Start Testnet - -Now, let's take a look at the `start` subcommand. - -The `start` subcommand both initializes and starts an in-process test network. This is the fastest way to spin up a local test network for testing purposes. - -You can start the local test network by running the following command: - -```bash -simd testnet start -``` - -You should see something similar to the following: - -```bash -acquiring test network lock -preparing test network with chain-id "chain-mtoD9v" - - -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -++ THIS MNEMONIC IS FOR TESTING PURPOSES ONLY ++ -++ DO NOT USE IN PRODUCTION ++ -++ ++ -++ sustain know debris minute gate hybrid stereo custom ++ -++ divorce cross spoon machine latin vibrant term oblige ++ -++ moment beauty laundry repeat grab game bronze truly ++ -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - -starting test network... -started test network -press the Enter Key to terminate -``` - -The first validator node is now running in-process, which means the test network will terminate once you either close the terminal window or you press the Enter key. In the output, the mnemonic phrase for the first validator node is provided for testing purposes. The validator node is using the same default addresses being used when initializing and starting a single node (no need to provide a `--node` flag). - -Check the status of the first validator node: - -``` -simd status -``` - -Import the key from the provided mnemonic: - -``` -simd keys add test --recover --keyring-backend test -``` - -Check the balance of the account address: - -``` -simd q bank balances [address] -``` - -Use this test account to manually test against the test network. - -## Testnet Options - -You can customize the configuration of the test network with flags. In order to see all flag options, append the `--help` flag to each command. diff --git a/versioned_docs/version-0.45/user/run-node/_category_.json b/versioned_docs/version-0.45/user/run-node/_category_.json deleted file mode 100644 index 2658bb66c..000000000 --- a/versioned_docs/version-0.45/user/run-node/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Run Node", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/develop/_category_.json b/versioned_docs/version-0.46/develop/_category_.json deleted file mode 100644 index f40637f4d..000000000 --- a/versioned_docs/version-0.46/develop/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Develop", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/00-baseapp.md b/versioned_docs/version-0.46/develop/advanced-concepts/00-baseapp.md deleted file mode 100644 index cce8b5b99..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/00-baseapp.md +++ /dev/null @@ -1,406 +0,0 @@ -# BaseApp - -This document describes `BaseApp`, the abstraction that implements the core functionalities of a Cosmos SDK application. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK application](../high-level-concepts/00-overview-app.md) {prereq} -* [Lifecycle of a Cosmos SDK transaction](../high-level-concepts/01-tx-lifecycle.md) {prereq} - -## Introduction - -`BaseApp` is a base type that implements the core of a Cosmos SDK application, namely: - -* The [Application Blockchain Interface](#main-abci-messages), for the state-machine to communicate with the underlying consensus engine (e.g. Tendermint). -* [Service Routers](#service-routers), to route messages and queries to the appropriate module. -* Different [states](#state-updates), as the state-machine can have different volatile states updated based on the ABCI message received. - -The goal of `BaseApp` is to provide the fundamental layer of a Cosmos SDK application -that developers can easily extend to build their own custom application. Usually, -developers will create a custom type for their application, like so: - -```go -type App struct { - // reference to a BaseApp - *baseapp.BaseApp - - // list of application store keys - - // list of application keepers - - // module manager -} -``` - -Extending the application with `BaseApp` gives the former access to all of `BaseApp`'s methods. -This allows developers to compose their custom application with the modules they want, while not -having to concern themselves with the hard work of implementing the ABCI, the service routers and state -management logic. - -## Type Definition - -The `BaseApp` type holds many important parameters for any Cosmos SDK based application. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/baseapp.go#L45-L137 - -Let us go through the most important components. - -> **Note**: Not all parameters are described, only the most important ones. Refer to the -> type definition for the full list. - -First, the important parameters that are initialized during the bootstrapping of the application: - -* [`CommitMultiStore`](./05-store.md#commitmultistore): This is the main store of the application, - which holds the canonical state that is committed at the [end of each block](#commit). This store - is **not** cached, meaning it is not used to update the application's volatile (un-committed) states. - The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application - uses one or multiple `KVStores` in the multi-store to persist their subset of the state. -* Database: The `db` is used by the `CommitMultiStore` to handle data persistence. -* [`Msg` Service Router](#msg-service-router): The `msgServiceRouter` facilitates the routing of `sdk.Msg` requests to the appropriate - module `Msg` service for processing. Here a `sdk.Msg` refers to the transaction component that needs to be - processed by a service in order to update the application state, and not to ABCI message which implements - the interface between the application and the underlying consensus engine. -* [gRPC Query Router](#grpc-query-router): The `grpcQueryRouter` facilitates the routing of gRPC queries to the - appropriate module for it to be processed. These queries are not ABCI messages themselves, but they - are relayed to the relevant module's gRPC `Query` service. -* [`TxDecoder`](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/types#TxDecoder): It is used to decode - raw transaction bytes relayed by the underlying Tendermint engine. -* [`ParamStore`](#paramstore): The parameter store used to get and set application consensus parameters. -* [`AnteHandler`](#antehandler): This handler is used to handle signature verification, fee payment, - and other pre-message execution checks when a transaction is received. It's executed during - [`CheckTx/RecheckTx`](#checktx) and [`DeliverTx`](#delivertx). -* [`InitChainer`](../high-level-concepts/00-overview-app.md#initchainer), - [`BeginBlocker` and `EndBlocker`](../high-level-concepts/00-overview-app.md#beginblocker-and-endblocker): These are - the functions executed when the application receives the `InitChain`, `BeginBlock` and `EndBlock` - ABCI messages from the underlying Tendermint engine. - -Then, parameters used to define [volatile states](#state-updates) (i.e. cached states): - -* `checkState`: This state is updated during [`CheckTx`](#checktx), and reset on [`Commit`](#commit). -* `deliverState`: This state is updated during [`DeliverTx`](#delivertx), and set to `nil` on - [`Commit`](#commit) and gets re-initialized on BeginBlock. - -Finally, a few more important parameters: - -* `voteInfos`: This parameter carries the list of validators whose precommit is missing, either - because they did not vote or because the proposer did not include their vote. This information is - carried by the [Context](#context) and can be used by the application for various things like - punishing absent validators. -* `minGasPrices`: This parameter defines the minimum gas prices accepted by the node. This is a - **local** parameter, meaning each full-node can set a different `minGasPrices`. It is used in the - `AnteHandler` during [`CheckTx`](#checktx), mainly as a spam protection mechanism. The transaction - enters the [mempool](https://docs.tendermint.com/master/tendermint-core/mempool/) - only if the gas prices of the transaction are greater than one of the minimum gas price in - `minGasPrices` (e.g. if `minGasPrices == 1uatom,1photon`, the `gas-price` of the transaction must be - greater than `1uatom` OR `1photon`). -* `appVersion`: Version of the application. It is set in the - [application's constructor function](../high-level-concepts/00-overview-app.md#constructor-function). - -## Constructor - -```go -func NewBaseApp( - name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp), -) *BaseApp { - - // ... -} -``` - -The `BaseApp` constructor function is pretty straightforward. The only thing worth noting is the -possibility to provide additional [`options`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/options.go) -to the `BaseApp`, which will execute them in order. The `options` are generally `setter` functions -for important parameters, like `SetPruning()` to set pruning options or `SetMinGasPrices()` to set -the node's `min-gas-prices`. - -Naturally, developers can add additional `options` based on their application's needs. - -## State Updates - -The `BaseApp` maintains two primary volatile states and a root or main state. The main state -is the canonical state of the application and the volatile states, `checkState` and `deliverState`, -are used to handle state transitions in-between the main state made during [`Commit`](#commit). - -Internally, there is only a single `CommitMultiStore` which we refer to as the main or root state. -From this root state, we derive two volatile states by using a mechanism called _store branching_ (performed by `CacheWrap` function). -The types can be illustrated as follows: - -![Types](./baseapp_state_types.png) - -### InitChain State Updates - -During `InitChain`, the two volatile states, `checkState` and `deliverState` are set by branching -the root `CommitMultiStore`. Any subsequent reads and writes happen on branched versions of the `CommitMultiStore`. -To avoid unnecessary roundtrip to the main state, all reads to the branched store are cached. - -![InitChain](./baseapp_state-initchain.png) - -### CheckTx State Updates - -During `CheckTx`, the `checkState`, which is based off of the last committed state from the root -store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a service router -exists for every message in the transaction. Note, when we execute the `AnteHandler`, we branch -the already branched `checkState`. -This has the side effect that if the `AnteHandler` fails, the state transitions won't be reflected in the `checkState` --- i.e. `checkState` is only updated on success. - -![CheckTx](./baseapp_state-checktx.png) - -### BeginBlock State Updates - -During `BeginBlock`, the `deliverState` is set for use in subsequent `DeliverTx` ABCI messages. The -`deliverState` is based off of the last committed state from the root store and is branched. -Note, the `deliverState` is set to `nil` on [`Commit`](#commit). - -![BeginBlock](./baseapp_state-begin_block.png) - -### DeliverTx State Updates - -The state flow for `DeliverTx` is nearly identical to `CheckTx` except state transitions occur on -the `deliverState` and messages in a transaction are executed. Similarly to `CheckTx`, state transitions -occur on a doubly branched state -- `deliverState`. Successful message execution results in -writes being committed to `deliverState`. Note, if message execution fails, state transitions from -the AnteHandler are persisted. - -![DeliverTx](./baseapp_state-deliver_tx.png) - -### Commit State Updates - -During `Commit` all the state transitions that occurred in the `deliverState` are finally written to -the root `CommitMultiStore` which in turn is committed to disk and results in a new application -root hash. These state transitions are now considered final. Finally, the `checkState` is set to the -newly committed state and `deliverState` is set to `nil` to be reset on `BeginBlock`. - -![Commit](./baseapp_state-commit.png) - -## ParamStore - -During `InitChain`, the `RequestInitChain` provides `ConsensusParams` which contains parameters -related to block execution such as maximum gas and size in addition to evidence parameters. If these -parameters are non-nil, they are set in the BaseApp's `ParamStore`. Behind the scenes, the `ParamStore` -is actually managed by an `x/params` module `Subspace`. This allows the parameters to be tweaked via -on-chain governance. - -## Service Routers - -When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via `BaseApp`, which holds a `msgServiceRouter` for messages, and a `grpcQueryRouter` for queries. - -### `Msg` Service Router - -[`sdk.Msg`s](#../building-modules/02-messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `BaseApp` holds a `msgServiceRouter` which maps fully-qualified service methods (`string`, defined in each module's Protobuf `Msg` service) to the appropriate module's `MsgServer` implementation. - -The [default `msgServiceRouter` included in `BaseApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/msg_service_router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into each [route handler inside `msgServiceRouter`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/msg_service_router.go#L31-L32). For a stateless router that doesn't want to make use of this, you can just ignore the `ctx`. - -The application's `msgServiceRouter` is initialized with all the routes using the application's [module manager](../building-modules/01-module-manager.md#manager) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../high-level-concepts/00-overview-app.md#constructor-function). - -### gRPC Query Router - -Similar to `sdk.Msg`s, [`queries`](../building-modules/02-messages-and-queries.md#queries) need to be routed to the appropriate module's [`Query` service](../building-modules/04-query-services.md). To do so, `BaseApp` holds a `grpcQueryRouter`, which maps modules' fully-qualified service methods (`string`, defined in their Protobuf `Query` gRPC) to their `QueryServer` implementation. The `grpcQueryRouter` is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the [`Query` ABCI message](#query) on the Tendermint RPC endpoint. - -Just like the `msgServiceRouter`, the `grpcQueryRouter` is initialized with all the query routes using the application's [module manager](../building-modules/01-module-manager.md) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../high-level-concepts/00-overview-app.md#app-constructor). - -## Main ABCI Messages - -The [Application-Blockchain Interface](https://docs.tendermint.com/master/spec/abci/) (ABCI) is a generic interface that connects a state-machine with a consensus engine to form a functional full-node. It can be wrapped in any language, and needs to be implemented by each application-specific blockchain built on top of an ABCI-compatible consensus engine like Tendermint. - -The consensus engine handles two main tasks: - -* The networking logic, which mainly consists in gossiping block parts, transactions and consensus votes. -* The consensus logic, which results in the deterministic ordering of transactions in the form of blocks. - -It is **not** the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of `[]bytes`, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on. - -Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as `BaseApp` comes with a built-in implementation of the interface. Let us go through the main ABCI messages that `BaseApp` implements: [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) - -### CheckTx - -`CheckTx` is sent by the underlying consensus engine when a new unconfirmed (i.e. not yet included in a valid block) -transaction is received by a full-node. The role of `CheckTx` is to guard the full-node's mempool -(where unconfirmed transactions are stored until they are included in a block) from spam transactions. -Unconfirmed transactions are relayed to peers only if they pass `CheckTx`. - -`CheckTx()` can perform both _stateful_ and _stateless_ checks, but developers should strive to -make the checks **lightweight** because gas fees are not charged for the resources (CPU, data load...) used during the `CheckTx`. - -In the Cosmos SDK, after [decoding transactions](./07-encoding.md), `CheckTx()` is implemented -to do the following checks: - -1. Extract the `sdk.Msg`s from the transaction. -2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `sdk.Msg`s. This is done - first, as _stateless_ checks are less computationally expensive than _stateful_ checks. If - `ValidateBasic()` fail, `CheckTx` returns before running _stateful_ checks, which saves resources. -3. Perform non-module related _stateful_ checks on the [account](../high-level-concepts/03-accounts.md). This step is mainly about checking - that the `sdk.Msg` signatures are valid, that enough fees are provided and that the sending account - has enough funds to pay for said fees. Note that no precise [`gas`](../high-level-concepts/gas-fees.md) counting occurs here, - as `sdk.Msg`s are not processed. Usually, the [`AnteHandler`](../high-level-concepts/gas-fees.md#antehandler) will check that the `gas` provided - with the transaction is superior to a minimum reference gas amount based on the raw transaction size, - in order to avoid spam with transactions that provide 0 gas. - -`CheckTx` does **not** process `sdk.Msg`s - they only need to be processed when the canonical state need to be updated, which happens during `DeliverTx`. - -Steps 2. and 3. are performed by the [`AnteHandler`](../high-level-concepts/gas-fees.md#antehandler) in the [`RunTx()`](#runtx-antehandler-and-runmsgs) -function, which `CheckTx()` calls with the `runTxModeCheck` mode. During each step of `CheckTx()`, a -special [volatile state](#state-updates) called `checkState` is updated. This state is used to keep -track of the temporary changes triggered by the `CheckTx()` calls of each transaction without modifying -the [main canonical state](#main-state). For example, when a transaction goes through `CheckTx()`, the -transaction's fees are deducted from the sender's account in `checkState`. If a second transaction is -received from the same account before the first is processed, and the account has consumed all its -funds in `checkState` during the first transaction, the second transaction will fail `CheckTx`() and -be rejected. In any case, the sender's account will not actually pay the fees until the transaction -is actually included in a block, because `checkState` never gets committed to the main state. The -`checkState` is reset to the latest state of the main state each time a blocks gets [committed](#commit). - -`CheckTx` returns a response to the underlying consensus engine of type [`abci.ResponseCheckTx`](https://docs.tendermint.com/master/spec/abci/abci.html#checktx-2). -The response contains: - -* `Code (uint32)`: Response Code. `0` if successful. -* `Data ([]byte)`: Result bytes, if any. -* `Log (string):` The output of the application's logger. May be non-deterministic. -* `Info (string):` Additional information. May be non-deterministic. -* `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. -* `GasUsed (int64)`: Amount of gas consumed by transaction. During `CheckTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction. Next is an example: - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/ante/basic.go#L95-L95 -* `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./10-events.md) for more. -* `Codespace (string)`: Namespace for the Code. - -#### RecheckTx - -After `Commit`, `CheckTx` is run again on all transactions that remain in the node's local mempool -excluding the transactions that are included in the block. To prevent the mempool from rechecking all transactions -every time a block is committed, the configuration option `mempool.recheck=false` can be set. As of -Tendermint v0.32.1, an additional `Type` parameter is made available to the `CheckTx` function that -indicates whether an incoming transaction is new (`CheckTxType_New`), or a recheck (`CheckTxType_Recheck`). -This allows certain checks like signature verification can be skipped during `CheckTxType_Recheck`. - -### DeliverTx - -When the underlying consensus engine receives a block proposal, each transaction in the block needs to be processed by the application. To that end, the underlying consensus engine sends a `DeliverTx` message to the application for each transaction in a sequential order. - -Before the first transaction of a given block is processed, a [volatile state](#state-updates) called `deliverState` is initialized during [`BeginBlock`](#beginblock). This state is updated each time a transaction is processed via `DeliverTx`, and committed to the [main state](#main-state) when the block is [committed](#commit), after what it is set to `nil`. - -`DeliverTx` performs the **exact same steps as `CheckTx`**, with a little caveat at step 3 and the addition of a fifth step: - -1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose. -2. For each `sdk.Msg` in the transaction, route to the appropriate module's Protobuf [`Msg` service](../building-modules/03-msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`. - -During the additional fifth step outlined in (2), each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/gas.go#L230-L241 - -At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0` and `DeliverTx` fails. - -`DeliverTx` returns a response to the underlying consensus engine of type [`abci.ResponseDeliverTx`](https://docs.tendermint.com/master/spec/abci/abci.html#delivertx-2). The response contains: - -* `Code (uint32)`: Response Code. `0` if successful. -* `Data ([]byte)`: Result bytes, if any. -* `Log (string):` The output of the application's logger. May be non-deterministic. -* `Info (string):` Additional information. May be non-deterministic. -* `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. -* `GasUsed (int64)`: Amount of gas consumed by transaction. During `DeliverTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs. -* `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./10-events.md) for more. -* `Codespace (string)`: Namespace for the Code. - -## RunTx, AnteHandler, RunMsgs, PostHandler - -### RunTx - -`RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded. - -The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a branch of the main store, with cache functionality (for query requests), instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../high-level-concepts/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. - -After that, `RunTx()` calls `ValidateBasic()` on each `sdk.Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `sdk.Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. - -Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/baseapp.go#L647-L654 - -This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./12-ocap.md) of the Cosmos SDK. - -Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `sdk.Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. - -### AnteHandler - -The `AnteHandler` is a special handler that implements the `AnteHandler` interface and is used to authenticate the transaction before the transaction's internal messages are processed. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/handler.go#L6-L8 - -The `AnteHandler` is theoretically optional, but still a very important component of public blockchain networks. It serves 3 primary purposes: - -* Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and [`sequence`](./01-transactions.md#transaction-generation) checking. -* Perform preliminary _stateful_ validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees. -* Play a role in the incentivisation of stakeholders via the collection of transaction fees. - -`BaseApp` holds an `anteHandler` as parameter that is initialized in the [application's constructor](../high-level-concepts/00-overview-app.md#application-constructor). The most widely used `anteHandler` is the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/ante/ante.go). - -Click [here](../high-level-concepts/gas-fees.md#antehandler) for more on the `anteHandler`. - -### RunMsgs - -`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `sdk.Msg`s. - -First, it retrieves the `sdk.Msg`'s fully-qualified type name, by checking the `type_url` of the Protobuf `Any` representing the `sdk.Msg`. Then, using the application's [`msgServiceRouter`](#msg-service-router), it checks for the existence of `Msg` service method related to that `type_url`. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. Otherwise, if `mode == runTxModeDeliver`, the [`Msg` service](../building-modules/03-msg-services.md) RPC is executed, before `RunMsgs` returns. - -### PostHandler - -_PostHandler_ are like `AnteHandler` (they share the same signature), but they execute after [`RunMsgs`](#runmsgs). - -Like `AnteHandler`s, `PostHandler`s are theoretically optional, one use case for `PostHandler`s is transaction tips (enabled by default in simapp). -Other use cases like unused gas refund can also be enabled by `PostHandler`s. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/posthandler/post.go#L14:L27 - -Note, when `PostHandler`s fail, the state from `runMsgs` is also reverted, effectively making the transaction fail. - -## Other ABCI Messages - -### InitChain - -The [`InitChain` ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#initchain) is sent from the underlying Tendermint engine when the chain is first started. It is mainly used to **initialize** parameters and state like: - -* [Consensus Parameters](https://docs.tendermint.com/master/spec/abci/apps.html#consensus-parameters) via `setConsensusParams`. -* [`checkState` and `deliverState`](#state-updates) via `setCheckState` and `setDeliverState`. -* The [block gas meter](../high-level-concepts/gas-fees.md#block-gas-meter), with infinite gas to process genesis transactions. - -Finally, the `InitChain(req abci.RequestInitChain)` method of `BaseApp` calls the [`initChainer()`](../high-level-concepts/00-overview-app.md#initchainer) of the application in order to initialize the main state of the application from the `genesis file` and, if defined, call the [`InitGenesis`](../building-modules/08-genesis.md#initgenesis) function of each of the application's modules. - -### BeginBlock - -The [`BeginBlock` ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#beginblock) is sent from the underlying Tendermint engine when a block proposal created by the correct proposer is received, before [`DeliverTx`](#delivertx) is run for each transaction in the block. It allows developers to have logic be executed at the beginning of each block. In the Cosmos SDK, the `BeginBlock(req abci.RequestBeginBlock)` method does the following: - -* Initialize [`deliverState`](#state-updates) with the latest header using the `req abci.RequestBeginBlock` passed as parameter via the `setDeliverState` function. - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/baseapp.go#L386-L396 - This function also resets the [main gas meter](../high-level-concepts/04-gas-fees.md#main-gas-meter). -* Initialize the [block gas meter](../high-level-concepts/04-gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters. -* Run the application's [`beginBlocker()`](../high-level-concepts/00-overview-app.md#beginblocker-and-endblock), which mainly runs the [`BeginBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. -* Set the [`VoteInfos`](https://docs.tendermint.com/master/spec/abci/abci.html#voteinfo) of the application, i.e. the list of validators whose _precommit_ for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./03-context.md) so that it can be used during `DeliverTx` and `EndBlock`. - -### EndBlock - -The [`EndBlock` ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#endblock) is sent from the underlying Tendermint engine after [`DeliverTx`](#delivertx) as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk `EndBlock(req abci.RequestEndBlock)` method is to run the application's [`EndBlocker()`](../high-level-concepts/00-overview-app.md#beginblocker-and-endblock), which mainly runs the [`EndBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. - -### Commit - -The [`Commit` ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#commit) is sent from the underlying Tendermint engine after the full-node has received _precommits_ from 2/3+ of validators (weighted by voting power). On the `BaseApp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occurred during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block. - -To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a branched multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`. - -Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block. - -### Info - -The [`Info` ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#info) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the `Info(res abci.ResponseInfo)` function from `BaseApp` will return the application's name, version and the hash of the last commit of `app.cms`. - -### Query - -The [`Query` ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#query-2) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It used to be the main entrypoint to build interfaces with the application, but with the introduction of [gRPC queries](../building-modules/04-query-services.md) in Cosmos SDK v0.40, its usage is more limited. The application must respect a few rules when implementing the `Query` method, which are outlined [here](https://docs.tendermint.com/master/spec/abci/apps.html#query). - -Each Tendermint `query` comes with a `path`, which is a `string` which denotes what to query. If the `path` matches a gRPC fully-qualified service method, then `BaseApp` will defer the query to the `grpcQueryRouter` and let it handle it like explained [above](#grpc-query-router). Otherwise, the `path` represents a query that is not (yet) handled by the gRPC router. `BaseApp` splits the `path` string with the `/` delimiter. By convention, the first element of the split string (`split[0]`) contains the category of `query` (`app`, `p2p`, `store` or `custom` ). The `BaseApp` implementation of the `Query(req abci.RequestQuery)` method is a simple dispatcher serving these 4 main categories of queries: - -* Application-related queries like querying the application's version, which are served via the `handleQueryApp` method. -* Direct queries to the multistore, which are served by the `handlerQueryStore` method. These direct queries are different from custom queries which go through `app.queryRouter`, and are mainly used by third-party service provider like block explorers. -* P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `BaseApp`'s [constructor](#constructor). -* Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the `handleQueryCustom` method. The `handleQueryCustom` branches the multistore before using the `queryRoute` obtained from `app.queryRouter` to map the query to the appropriate module's [legacy `querier`](../building-modules/04-query-services.md#legacy-queriers). - -## Next {hide} - -Learn more about [transactions](./01-transactions.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/01-transactions.md b/versioned_docs/version-0.46/develop/advanced-concepts/01-transactions.md deleted file mode 100644 index 51628fef3..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/01-transactions.md +++ /dev/null @@ -1,171 +0,0 @@ -# Transactions - -`Transactions` are objects created by end-users to trigger state changes in the application. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK Application](../high-level-concepts/app-anatomy.md) {prereq} - -## Transactions - -Transactions are comprised of metadata held in [contexts](./03-context.md) and [`sdk.Msg`s](../building-modules/02-messages-and-queries.md) that trigger state changes within a module through the module's Protobuf [`Msg` service](../building-modules/03-msg-services.md). - -When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `sdk.Msg` must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../high-level-concepts/01-tx-lifecycle.md). - -## Type Definition - -Transaction objects are Cosmos SDK types that implement the `Tx` interface - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/tx_msg.go#L38-L46 - -It contains the following methods: - -* **GetMsgs:** unwraps the transaction and returns a list of contained `sdk.Msg`s - one transaction may have one or multiple messages, which are defined by module developers. -* **ValidateBasic:** lightweight, [_stateless_](../high-level-concepts/01-tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./00-baseapp.md#checktx) and [`DeliverTx`](./00-baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth) module's `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from `sdk.Msg` [`ValidateBasic`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic) methods, which perform basic validity checks on messages only. When [`runTx`](./00-baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself. - -As a developer, you should rarely manipulate `Tx` directly, as `Tx` is really an intermediate type used for transaction generation. Instead, developers should prefer the `TxBuilder` interface, which you can learn more about [below](#transaction-generation). - -### Signing Transactions - -Every message in a transaction must be signed by the addresses specified by its `GetSigners`. The Cosmos SDK currently allows signing transactions in two different ways. - -#### `SIGN_MODE_DIRECT` (preferred) - -The most used implementation of the `Tx` interface is the Protobuf `Tx` message, which is used in `SIGN_MODE_DIRECT`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L13-L26 - -Because Protobuf serialization is not deterministic, the Cosmos SDK uses an additional `TxRaw` type to denote the pinned bytes over which a transaction is signed. Any user can generate a valid `body` and `auth_info` for a transaction, and serialize these two messages using Protobuf. `TxRaw` then pins the user's exact binary representation of `body` and `auth_info`, called respectively `body_bytes` and `auth_info_bytes`. The document that is signed by all signers of the transaction is `SignDoc` (deterministically serialized using [ADR-027](../architecture/adr-027-deterministic-protobuf-serialization.md)): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L48-L65 - -Once signed by all signers, the `body_bytes`, `auth_info_bytes` and `signatures` are gathered into `TxRaw`, whose serialized bytes are broadcasted over the network. - -#### `SIGN_MODE_LEGACY_AMINO_JSON` - -The legacy implementation of the `Tx` interface is the `StdTx` struct from `x/auth`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/migrations/legacytx/stdtx.go#L82-L92 - -The document signed by all signers is `StdSignDoc`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/migrations/legacytx/stdsign.go#L38-L52 - -which is encoded into bytes using Amino JSON. Once all signatures are gathered into `StdTx`, `StdTx` is serialized using Amino JSON, and these bytes are broadcasted over the network. - -#### Other Sign Modes - -The Cosmos SDK also provides a couple of other sign modes for particular use cases. - -#### `SIGN_MODE_DIRECT_AUX` - -`SIGN_MODE_DIRECT_AUX` is a sign mode released in the Cosmos SDK v0.46 which targets transactions with multiple signers. Whereas `SIGN_MODE_DIRECT` expects each signer to sign over both `TxBody` and `AuthInfo` (which includes all other signers' signer infos, i.e. their account sequence, public key and mode info), `SIGN_MODE_DIRECT_AUX` allows N-1 signers to only sign over `TxBody` and _their own_ signer info. Morever, each auxiliary signer (i.e. a signer using `SIGN_MODE_DIRECT_AUX`) doesn't -need to sign over the fees: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L67-L93 - -The use case is a multi-signer transaction, where one of the signers is appointed to gather all signatures, broadcast the signature and pay for fees, and the others only care about the transaction body. This generally allows for a better multi-signing UX. If Alice, Bob and Charlie are part of a 3-signer transaction, then Alice and Bob can both use `SIGN_MODE_DIRECT_AUX` to sign over the `TxBody` and their own signer info (no need an additional step to gather other signers' ones, like in `SIGN_MODE_DIRECT`), without specifying a fee in their SignDoc. Charlie can then gather both signatures from Alice and Bob, and -create the final transaction by appending a fee. Note that the fee payer of the transaction (in our case Charlie) must sign over the fees, so must use `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`. - -A concrete use case is implemented in [transaction tips](./15-tips.md): the tipper may use `SIGN_MODE_DIRECT_AUX` to specify a tip in the transaction, without signing over the actual transaction fees. Then, the fee payer appends fees inside the tipper's desired `TxBody`, and as an exchange for paying the fees and broadcasting the transaction, receives the tipper's transaction tips as payment. - -#### `SIGN_MODE_TEXTUAL` - -`SIGN_MODE_TEXTUAL` is a new sign mode for delivering a better signing experience on hardware wallets, it is currently still under implementation. If you wish to learn more, please refer to [ADR-050](https://github.com/cosmos/cosmos-sdk/pull/10701). - -## Transaction Process - -The process of an end-user sending a transaction is: - -* decide on the messages to put into the transaction, -* generate the transaction using the Cosmos SDK's `TxBuilder`, -* broadcast the transaction using one of the available interfaces. - -The next paragraphs will describe each of these components, in this order. - -### Messages - -::: tip -Module `sdk.Msg`s are not to be confused with [ABCI Messages](https://docs.tendermint.com/master/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers. -::: - -**Messages** (or `sdk.Msg`s) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf [`Msg` service](../building-modules/03-msg-services.md), and also implement the corresponding `MsgServer`. - -Each `sdk.Msg`s is related to exactly one Protobuf [`Msg` service](../building-modules/03-msg-services.md) RPC, defined inside each module's `tx.proto` file. A SDK app router automatically maps every `sdk.Msg` to a corresponding RPC. Protobuf generates a `MsgServer` interface for each module `Msg` service, and the module developer needs to implement this interface. -This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. - -To learn more about Protobuf `Msg` services and how to implement `MsgServer`, click [here](../building-modules/03-msg-services.md). - -While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the `TxBuilder` and `Context`. - -### Transaction Generation - -The `TxBuilder` interface contains data closely related with the generation of transactions, which an end-user can freely set to generate the desired transaction: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/tx_config.go#L33-L50 - -* `Msg`s, the array of [messages](#messages) included in the transaction. -* `GasLimit`, option chosen by the users for how to calculate how much gas they will need to pay. -* `Memo`, a note or comment to send with the transaction. -* `FeeAmount`, the maximum amount the user is willing to pay in fees. -* `TimeoutHeight`, block height until which the transaction is valid. -* `Signatures`, the array of signatures from all signers of the transaction. - -As there are currently two sign modes for signing transactions, there are also two implementations of `TxBuilder`: - -* [wrapper](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/tx/builder.go#L18-L34) for creating transactions for `SIGN_MODE_DIRECT`, -* [StdTxBuilder](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/migrations/legacytx/stdtx_builder.go#L15-L21) for `SIGN_MODE_LEGACY_AMINO_JSON`. - -However, the two implementation of `TxBuilder` should be hidden away from end-users, as they should prefer using the overarching `TxConfig` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/tx_config.go#L22-L31 - -`TxConfig` is an app-wide configuration for managing transactions. Most importantly, it holds the information about whether to sign each transaction with `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`. By calling `txBuilder := txConfig.NewTxBuilder()`, a new `TxBuilder` will be created with the appropriate sign mode. - -Once `TxBuilder` is correctly populated with the setters exposed above, `TxConfig` will also take care of correctly encoding the bytes (again, either using `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`). Here's a pseudo-code snippet of how to generate and encode a transaction, using the `TxEncoder()` method: - -```go -txBuilder := txConfig.NewTxBuilder() -txBuilder.SetMsgs(...) // and other setters on txBuilder - -bz, err := txConfig.TxEncoder()(txBuilder.GetTx()) -// bz are bytes to be broadcasted over the network -``` - -### Broadcasting the Transaction - -Once the transaction bytes are generated, there are currently three ways of broadcasting it. - -#### CLI - -Application developers create entry points to the application by creating a [command-line interface](../advanced-concepts/06-cli.md), [gRPC and/or REST interface](../advanced-concepts/08-grpc_rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line. - -For the [command-line interface](../building-modules/09-module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. CLI commands actually bundle all the steps of transaction processing into one simple command: creating messages, generating transactions and broadcasting. For concrete examples, see the [Interacting with a Node](../run-node/interact-node.md) section. An example transaction made using CLI looks like: - -```bash -simd tx send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake -``` - -#### gRPC - -[gRPC](https://grpc.io) is the main component for the Cosmos SDK's RPC layer. Its principal usage is in the context of modules' [`Query` services](../building-modules). However, the Cosmos SDK also exposes a few other module-agnostic gRPC services, one of them being the `Tx` service: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/service.proto - -The `Tx` service exposes a handful of utility functions, such as simulating a transaction or querying a transaction, and also one method to broadcast transactions. - -Examples of broadcasting and simulating a transaction are shown [here](../run-node/txs.md#programmatically-with-go). - -#### REST - -Each gRPC method has its corresponding REST endpoint, generated using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). Therefore, instead of using gRPC, you can also use HTTP to broadcast the same transaction, on the `POST /cosmos/tx/v1beta1/txs` endpoint. - -An example can be seen [here](../run-node/txs.md#using-rest) - -#### Tendermint RPC - -The three methods presented above are actually higher abstractions over the Tendermint RPC `/broadcast_tx_{async,sync,commit}` endpoints, documented [here](https://docs.tendermint.com/master/rpc/#/Tx). This means that you can use the Tendermint RPC endpoints directly to broadcast the transaction, if you wish so. - -## Next {hide} - -Learn about the [context](./03-context.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/03-context.md b/versioned_docs/version-0.46/develop/advanced-concepts/03-context.md deleted file mode 100644 index 4f7dff139..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/03-context.md +++ /dev/null @@ -1,99 +0,0 @@ -# Context - -The `context` is a data structure intended to be passed from function to function that carries information about the current state of the application. It provides access to a branched storage (a safe branch of the entire state) as well as useful objects and information like `gasMeter`, `block height`, `consensus parameters` and more. {synopsis} - -## Pre-requisites Readings - -* [Anatomy of a Cosmos SDK Application](../high-level-concepts/app-anatomy.md) {prereq} -* [Lifecycle of a Transaction](../high-level-concepts/01-tx-lifecycle.md) {prereq} - -## Context Definition - -The Cosmos SDK `Context` is a custom data structure that contains Go's stdlib [`context`](https://pkg.go.dev/context) as its base, and has many additional types within its definition that are specific to the Cosmos SDK. The `Context` is integral to transaction processing in that it allows modules to easily access their respective [store](./05-store.md#base-layer-kvstores) in the [`multistore`](./05-store.md#multistore) and retrieve transactional context such as the block header and gas meter. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/context.go#L17-L42 - -* **Base Context:** The base type is a Go [Context](https://pkg.go.dev/context), which is explained further in the [Go Context Package](#go-context-package) section below. -* **Multistore:** Every application's `BaseApp` contains a [`CommitMultiStore`](./05-store.md#multistore) which is provided when a `Context` is created. Calling the `KVStore()` and `TransientStore()` methods allows modules to fetch their respective [`KVStore`](./05-store.md#base-layer-kvstores) using their unique `StoreKey`. -* **Header:** The [header](https://docs.tendermint.com/master/spec/advanced-concepts/data_structures.html#header) is a Blockchain type. It carries important information about the state of the blockchain, such as block height and proposer of the current block. -* **Header Hash:** The current block header hash, obtained during `abci.RequestBeginBlock`. -* **Chain ID:** The unique identification number of the blockchain a block pertains to. -* **Transaction Bytes:** The `[]byte` representation of a transaction being processed using the context. Every transaction is processed by various parts of the Cosmos SDK and consensus engine (e.g. Tendermint) throughout its [lifecycle](../high-level-concepts/01-tx-lifecycle.md), some of which do not have any understanding of transaction types. Thus, transactions are marshaled into the generic `[]byte` type using some kind of [encoding format](./07-encoding.md) such as [Amino](./07-encoding.md). -* **Logger:** A `logger` from the Tendermint libraries. Learn more about logs [here](https://docs.tendermint.com/master/nodes/logging.html). Modules call this method to create their own unique module-specific logger. -* **VoteInfo:** A list of the ABCI type [`VoteInfo`](https://docs.tendermint.com/master/spec/abci/abci.html#voteinfo), which includes the name of a validator and a boolean indicating whether they have signed the block. -* **Gas Meters:** Specifically, a [`gasMeter`](../high-level-concepts/gas-fees.md#main-gas-meter) for the transaction currently being processed using the context and a [`blockGasMeter`](../high-level-concepts/gas-fees.md#block-gas-meter) for the entire block it belongs to. Users specify how much in fees they wish to pay for the execution of their transaction; these gas meters keep track of how much [gas](../high-level-concepts/gas-fees.md) has been used in the transaction or block so far. If the gas meter runs out, execution halts. -* **CheckTx Mode:** A boolean value indicating whether a transaction should be processed in `CheckTx` or `DeliverTx` mode. -* **Min Gas Price:** The minimum [gas](../high-level-concepts/gas-fees.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually, and should therefore **not be used in any functions used in sequences leading to state-transitions**. -* **Consensus Params:** The ABCI type [Consensus Parameters](https://docs.tendermint.com/master/spec/abci/apps.html#consensus-parameters), which specify certain limits for the blockchain, such as maximum gas for a block. -* **Event Manager:** The event manager allows any caller with access to a `Context` to emit [`Events`](./10-events.md). Modules may define module specific - `Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`, and `EndBlock` and are returned to Tendermint for indexing. For example: -* **Priority:** The transaction priority, only relevant in `CheckTx`. - -```go -ctx.EventManager().EmitEvent(sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory)), -) -``` - -## Go Context Package - -A basic `Context` is defined in the [Golang Context Package](https://pkg.go.dev/context). A `Context` -is an immutable data structure that carries request-scoped data across APIs and processes. Contexts -are also designed to enable concurrency and to be used in goroutines. - -Contexts are intended to be **immutable**; they should never be edited. Instead, the convention is -to create a child context from its parent using a `With` function. For example: - -```go -childCtx = parentCtx.WithBlockHeader(header) -``` - -The [Golang Context Package](https://pkg.go.dev/context) documentation instructs developers to -explicitly pass a context `ctx` as the first argument of a process. - -## Store branching - -The `Context` contains a `MultiStore`, which allows for branchinig and caching functionality using `CacheMultiStore` -(queries in `CacheMultiStore` are cached to avoid future round trips). -Each `KVStore` is branched in a safe and isolated ephemeral storage. Processes are free to write changes to -the `CacheMultiStore`. If a state-transition sequence is performed without issue, the store branch can -be committed to the underlying store at the end of the sequence or disregard them if something -goes wrong. The pattern of usage for a Context is as follows: - -1. A process receives a Context `ctx` from its parent process, which provides information needed to - perform the process. -2. The `ctx.ms` is a **branched store**, i.e. a branch of the [multistore](./05-store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution. -3. The process may read and write from `ctx` as it is executing. It may call a subprocess and pass - `ctx` to it as needed. -4. When a subprocess returns, it checks if the result is a success or failure. If a failure, nothing - needs to be done - the branch `ctx` is simply discarded. If successful, the changes made to - the `CacheMultiStore` can be committed to the original `ctx.ms` via `Write()`. - -For example, here is a snippet from the [`runTx`](./00-baseapp.md#runtx-antehandler-runmsgs-posthandler) function in [`baseapp`](./00-baseapp.md): - -```go -runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) -result = app.runMsgs(runMsgCtx, msgs, mode) -result.GasWanted = gasWanted -if mode != runTxModeDeliver { - return result -} -if result.IsOK() { - msCache.Write() -} -``` - -Here is the process: - -1. Prior to calling `runMsgs` on the message(s) in the transaction, it uses `app.cacheTxContext()` - to branch and cache the context and multistore. -2. `runMsgCtx` - the context with branched store, is used in `runMsgs` to return a result. -3. If the process is running in [`checkTxMode`](./00-baseapp.md#checktx), there is no need to write the - changes - the result is returned immediately. -4. If the process is running in [`deliverTxMode`](./00-baseapp.md#delivertx) and the result indicates - a successful run over all the messages, the branched multistore is written back to the original. - -## Next {hide} - -Learn about the [node client](./04-node.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/04-node.md b/versioned_docs/version-0.46/develop/advanced-concepts/04-node.md deleted file mode 100644 index 185f767b7..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/04-node.md +++ /dev/null @@ -1,73 +0,0 @@ -# Node Client (Daemon) - -The main endpoint of a Cosmos SDK application is the daemon client, otherwise known as the full-node client. The full-node runs the state-machine, starting from a genesis file. It connects to peers running the same client in order to receive and relay transactions, block proposals and signatures. The full-node is constituted of the application, defined with the Cosmos SDK, and of a consensus engine connected to the application via the ABCI. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of an SDK application](../high-level-concepts/app-anatomy.md) {prereq} - -## `main` function - -The full-node client of any Cosmos SDK application is built by running a `main` function. The client is generally named by appending the `-d` suffix to the application name (e.g. `appd` for an application named `app`), and the `main` function is defined in a `./appd/cmd/main.go` file. Running this function creates an executable `appd` that comes with a set of commands. For an app named `app`, the main command is [`appd start`](#start-command), which starts the full-node. - -In general, developers will implement the `main.go` function with the following structure: - -* First, an [`encodingCodec`](./07-encoding.md) is instantiated for the application. -* Then, the `config` is retrieved and config parameters are set. This mainly involves setting the Bech32 prefixes for [addresses](../high-level-concepts/03-accounts.md#addresses). - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/config.go#L14-L29 -* Using [cobra](https://github.com/spf13/cobra), the root command of the full-node client is created. After that, all the custom commands of the application are added using the `AddCommand()` method of `rootCmd`. -* Add default server commands to `rootCmd` using the `server.AddCommands()` method. These commands are separated from the ones added above since they are standard and defined at Cosmos SDK level. They should be shared by all Cosmos SDK-based applications. They include the most important command: the [`start` command](#start-command). -* Prepare and execute the `executor`. - +++ https://github.com/tendermint/tendermint/blob/v0.35.4/libs/cli/setup.go#L74-L78 - -See an example of `main` function from the `simapp` application, the Cosmos SDK's application for demo purposes: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/main.go - -## `start` command - -The `start` command is defined in the `/server` folder of the Cosmos SDK. It is added to the root command of the full-node client in the [`main` function](#main-function) and called by the end-user to start their node: - -```bash -# For an example app named "app", the following command starts the full-node. -appd start - -# Using the Cosmos SDK's own simapp, the following commands start the simapp node. -simd start -``` - -As a reminder, the full-node is composed of three conceptual layers: the networking layer, the consensus layer and the application layer. The first two are generally bundled together in an entity called the consensus engine (Tendermint Core by default), while the third is the state-machine defined with the help of the Cosmos SDK. Currently, the Cosmos SDK uses Tendermint as the default consensus engine, meaning the start command is implemented to boot up a Tendermint node. - -The flow of the `start` command is pretty straightforward. First, it retrieves the `config` from the `context` in order to open the `db` (a [`leveldb`](https://github.com/syndtr/goleveldb) instance by default). This `db` contains the latest known state of the application (empty if the application is started from the first time. - -With the `db`, the `start` command creates a new instance of the application using an `appCreator` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/start.go#L209-L209 - -Note that an `appCreator` is a function that fulfills the `AppCreator` signature: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/types/app.go#L57-L59 - -In practice, the [constructor of the application](../high-level-concepts/app-anatomy.md#constructor-function) is passed as the `appCreator`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L246-L295 - -Then, the instance of `app` is used to instantiate a new Tendermint node: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/start.go#L291-L294 - -The Tendermint node can be created with `app` because the latter satisfies the [`abci.Application` interface](https://github.com/tendermint/tendermint/blob/v0.35.4/abci/types/application.go#L7-L32) (given that `app` extends [`baseapp`](./00-baseapp.md)). As part of the `node.New` method, Tendermint makes sure that the height of the application (i.e. number of blocks since genesis) is equal to the height of the Tendermint node. The difference between these two heights should always be negative or null. If it is strictly negative, `node.New` will replay blocks until the height of the application reaches the height of the Tendermint node. Finally, if the height of the application is `0`, the Tendermint node will call [`InitChain`](./00-baseapp.md#initchain) on the application to initialize the state from the genesis file. - -Once the Tendermint node is instantiated and in sync with the application, the node can be started: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/start.go#L296-L298 - -Upon starting, the node will bootstrap its RPC and P2P server and start dialing peers. During handshake with its peers, if the node realizes they are ahead, it will query all the blocks sequentially in order to catch up. Then, it will wait for new block proposals and block signatures from validators in order to make progress. - -## Other commands - -To discover how to concretely run a node and interact with it, please refer to our [Running a Node, API and CLI](../run-node/README.md) guide. - -## Next {hide} - -Learn about the [store](./05-store.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/05-store.md b/versioned_docs/version-0.46/develop/advanced-concepts/05-store.md deleted file mode 100644 index e33046f8a..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/05-store.md +++ /dev/null @@ -1,277 +0,0 @@ -# Store - -A store is a data structure that holds the state of the application. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK application](../high-level-concepts/app-anatomy.md) {prereq} - -## Introduction to Cosmos SDK Stores - -The Cosmos SDK comes with a large set of stores to persist the state of applications. By default, the main store of Cosmos SDK applications is a `multistore`, i.e. a store of stores. Developers can add any number of key-value stores to the multistore, depending on their application needs. The multistore exists to support the modularity of the Cosmos SDK, as it lets each module declare and manage their own subset of the state. Key-value stores in the multistore can only be accessed with a specific capability `key`, which is typically held in the [`keeper`](../building-modules/06-keeper.md) of the module that declared the store. - -```text -+-----------------------------------------------------+ -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 1 - Manage by keeper of Module 1 | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 2 - Manage by keeper of Module 2 | | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 3 - Manage by keeper of Module 2 | | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 4 - Manage by keeper of Module 3 | | -| | | | -| +--------------------------------------------+ | -| | -| +--------------------------------------------+ | -| | | | -| | KVStore 5 - Manage by keeper of Module 4 | | -| | | | -| +--------------------------------------------+ | -| | -| Main Multistore | -| | -+-----------------------------------------------------+ - - Application's State -``` - -### Store Interface - -At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and has a `GetStoreType()` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L16-L19 - -The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that implements store read caching and write branching through `Write` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L247-L277 - -Branching and cache is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A storage branch creates an isolated, ephemeral branch of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. Read more about it in [context](./03-context.md#Store-branching) - -### Commit Store - -A commit store is a store that has the ability to commit changes made to the underlying tree or db. The Cosmos SDK differentiates simple stores from commit stores by extending the basic store interfaces with a `Committer`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L30-L34 - -The `Committer` is an interface that defines methods to persist changes to disk: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L21-L28 - -The `CommitID` is a deterministic commit of the state tree. Its hash is returned to the underlying consensus engine and stored in the block header. Note that commit store interfaces exist for various purposes, one of which is to make sure not every object can commit the store. As part of the [object-capabilities model](./12-ocap.md) of the Cosmos SDK, only `baseapp` should have the ability to commit stores. For example, this is the reason why the `ctx.KVStore()` method by which modules typically access stores returns a `KVStore` and not a `CommitKVStore`. - -The Cosmos SDK comes with many types of stores, the most used being [`CommitMultiStore`](#multistore), [`KVStore`](#kvstore) and [`GasKv` store](#gaskv-store). [Other types of stores](#other-stores) include `Transient` and `TraceKV` stores. - -## Multistore - -### Multistore Interface - -Each Cosmos SDK application holds a multistore at its root to persist its state. The multistore is a store of `KVStores` that follows the `Multistore` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L97-L133 - -If tracing is enabled, then branching the multistore will firstly wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store). - -### CommitMultiStore - -The main type of `Multistore` used in the Cosmos SDK is `CommitMultiStore`, which is an extension of the `Multistore` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L141-L187 - -As for concrete implementation, the [`rootMulti.Store`] is the go-to implementation of the `CommitMultiStore` interface. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/rootmulti/store.go#L38-L61 - -The `rootMulti.Store` is a base-layer multistore built around a `db` on top of which multiple `KVStores` can be mounted, and is the default multistore store used in [`baseapp`](./00-baseapp.md). - -### CacheMultiStore - -Whenever the `rootMulti.Store` needs to be branched, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/cachemulti/store.go) is used. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/cachemulti/store.go#L20-L36 - -`cachemulti.Store` branches all substores (creates a virtual store for each substore) in its constructor and hold them in `Store.stores`. Moreover caches all read queries. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores. - -## Base-layer KVStores - -### `KVStore` and `CommitKVStore` Interfaces - -A `KVStore` is a simple key-value store used to store and retrieve data. A `CommitKVStore` is a `KVStore` that also implements a `Committer`. By default, stores mounted in `baseapp`'s main `CommitMultiStore` are `CommitKVStore`s. The `KVStore` interface is primarily used to restrict modules from accessing the committer. - -Individual `KVStore`s are used by modules to manage a subset of the global state. `KVStores` can be accessed by objects that hold a specific key. This `key` should only be exposed to the [`keeper`](../building-modules/06-keeper.md) of the module that defines the store. - -`CommitKVStore`s are declared by proxy of their respective `key` and mounted on the application's [multistore](#multistore) in the [main application file](../high-level-concepts/app-anatomy.md#core-application-file). In the same file, the `key` is also passed to the module's `keeper` that is responsible for managing the store. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/store.go#L192-L226 - -Apart from the traditional `Get` and `Set` methods, that a `KVStore` must implement via the `BasicKVStore` interface; a `KVStore` must provide an `Iterator(start, end)` method which returns an `Iterator` object. It is used to iterate over a range of keys, typically keys that share a common prefix. Below is an example from the bank's module keeper, used to iterate over all account balances: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/keeper/view.go#L114-L132 - -### `IAVL` Store - -The default implementation of `KVStore` and `CommitKVStore` used in `baseapp` is the `iavl.Store`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/iavl/store.go#L37-L40 - -`iavl` stores are based around an [IAVL Tree](https://github.com/cosmos/iavl), a self-balancing binary tree which guarantees that: - -* `Get` and `Set` operations are O(log n), where n is the number of elements in the tree. -* Iteration efficiently returns the sorted elements within the range. -* Each tree version is immutable and can be retrieved even after a commit (depending on the pruning settings). - -The documentation on the IAVL Tree is located [here](https://github.com/cosmos/iavl/blob/master/docs/overview.md). - -### `DbAdapter` Store - -`dbadapter.Store` is a adapter for `dbm.DB` making it fulfilling the `KVStore` interface. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/dbadapter/store.go#L14-L17 - -`dbadapter.Store` embeds `dbm.DB`, meaning most of the `KVStore` interface functions are implemented. The other functions (mostly miscellaneous) are manually implemented. This store is primarily used within [Transient Stores](#transient-stores) - -### `Transient` Store - -`Transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/transient/store.go#L16-L19 - -`Transient.Store` is a `dbadapter.Store` with a `dbm.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, a new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected. - -This type of store is useful to persist information that is only relevant per-block. One example would be to store parameter changes (i.e. a bool set to `true` if a parameter changed in a block). - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/params/types/subspace.go#L21-L31 - -Transient stores are typically accessed via the [`context`](./03-context.md) via the `TransientStore()` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/context.go#L264-L267 - -## KVStore Wrappers - -### CacheKVStore - -`cachekv.Store` is a wrapper `KVStore` which provides buffered writing / cached reading functionalities over the underlying `KVStore`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/cachekv/store.go#L27-L35 - -This is the type used whenever an IAVL Store needs to be branched to create an isolated store (typically when we need to mutate a state that might be reverted later). - -#### `Get` - -`Store.Get()` firstly checks if `Store.cache` has an associated value with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, caches the result in `Store.cache`, and returns it. - -#### `Set` - -`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field dirty bool which indicates whether the cached value is different from the underlying value. When `Store.Set()` caches a new pair, the `cValue.dirty` is set `true` so when `Store.Write()` is called it can be written to the underlying store. - -#### `Iterator` - -`Store.Iterator()` have to traverse on both cached items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPairs`, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. - -### `GasKv` Store - -Cosmos SDK applications use [`gas`](../high-level-concepts/gas-fees.md) to track resources usage and prevent spam. [`GasKv.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/gaskv/store.go) is a `KVStore` wrapper that enables automatic gas consumption each time a read or write to the store is made. It is the solution of choice to track storage usage in Cosmos SDK applications. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/gaskv/store.go#L11-L17 - -When methods of the parent `KVStore` are called, `GasKv.Store` automatically consumes appropriate amount of gas depending on the `Store.gasConfig`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/gas.go#L219-L228 - -By default, all `KVStores` are wrapped in `GasKv.Stores` when retrieved. This is done in the `KVStore()` method of the [`context`](./03-context.md): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/context.go#L259-L262 - -In this case, the default gas configuration is used: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/gas.go#L230-L241 - -### `TraceKv` Store - -`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. It is applied automatically by the Cosmos SDK on all `KVStore` if tracing is enabled on the parent `MultiStore`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/tracekv/store.go#L20-L43 - -When each `KVStore` methods are called, `tracekv.Store` automatically logs `traceOperation` to the `Store.writer`. `traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`. - -### `Prefix` Store - -`prefix.Store` is a wrapper `KVStore` which provides automatic key-prefixing functionalities over the underlying `KVStore`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/prefix/store.go#L16-L22 - -When `Store.{Get, Set}()` is called, the store forwards the call to its parent, with the key prefixed with the `Store.prefix`. - -When `Store.Iterator()` is called, it does not simply prefix the `Store.prefix`, since it does not work as intended. In that case, some of the elements are traversed even they are not starting with the prefix. - -### `ListenKv` Store - -`listenkv.Store` is a wrapper `KVStore` which provides state listening capabilities over the underlying `KVStore`. -It is applied automatically by the Cosmos SDK on any `KVStore` whose `StoreKey` is specified during state streaming configuration. -Additional information about state streaming configuration can be found in the [store/streaming/README.md](https://github.com/cosmos/cosmos-sdk/tree/v0.46.0-rc1/store/streaming). - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/listenkv/store.go#L11-L18 - -When `KVStore.Set` or `KVStore.Delete` methods are called, `listenkv.Store` automatically writes the operations to the set of `Store.listeners`. - -## New Store package (`store/v2alpha1`) - -The SDK is in the process of transitioning to use the types listed here as the default interface for state storage. At the time of writing, these cannot be used within an application and are not directly compatible with the `CommitMultiStore` and related types. - -These types use the new `db` sub-module of Cosmos-SDK (`github.com/cosmos/cosmos-sdk/db`), rather than `tmdb` (`github.com/tendermint/tm-db`). - -See [ADR-040](../architecture/adr-040-storage-and-smt-state-commitments.md) for the motivations and design specifications of the change. - -## `BasicKVStore` interface - -An interface providing only the basic CRUD functionality (`Get`, `Set`, `Has`, and `Delete` methods), without iteration or caching. This is used to partially expose components of a larger store, such as a `root.Store`. - -## MultiStore - -This is the new interface (or, set of interfaces) for the main client store, replacing the role of `store/types.MultiStore` (v1). There are a few significant differences in behavior compared with v1: - -* Commits are atomic and are performed on the entire store state; individual substores cannot be committed separately and cannot have different version numbers. -* The store's current version and version history track that of the backing `db.DBConnection`. Past versions are accessible read-only. -* The set of valid substores is defined at initialization and cannot be updated dynamically in an existing store instance. - -### `CommitMultiStore` - -This is the main interface for persisent application state, analogous to the original `CommitMultiStore`. - -* Past version views are accessed with `GetVersion`, which returns a `BasicMultiStore`. -* Substores are accessed with `GetKVStore`. Trying to get a substore that was not defined at initialization will cause a panic. -* `Close` must be called to release the DB resources being used by the store. - -### `BasicMultiStore` - -A minimal interface that only allows accessing substores. Note: substores returned by `BasicMultiStore.GetKVStore` are read-only and will panic on `Set` or `Delete` calls. - -### Implementation (`root.Store`) - -The canonical implementation of `MultiStore` is in `store/v2alpha1/root`. It internally decouples the concerns of state storage and state commitment: values are stored in, and read directly from, the backing key-value database (state storage, or *SS*), but are also mapped in a logically separate database which generates cryptographic proofs (for state-commitment or *SC*). - -The state-commitment component of each substore is implemented as an independent `smt.Store` (see below). Internally, each substore is allocated in a logically separate partition within the same backing DB, such that commits apply to the state of all substores. Therefore, views of past versions also include the state of all substores (including *SS* and *SC* data). - -This store can optionally be configured to use a different backend database instance for *SC* (e.g., `badgerdb` for the state storage DB and `memdb` for the state-commitment DB; see `StoreConfig.StateCommitmentDB`). - -## SMT Store - -`store/v2alpha1/smt.Store` maps values into a Sparse Merkle Tree (SMT), and supports a `BasicKVStore` interface as well as methods for cryptographic proof generation. - -## Next {hide} - -Learn about [encoding](./07-encoding.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/07-encoding.md b/versioned_docs/version-0.46/develop/advanced-concepts/07-encoding.md deleted file mode 100644 index 5d531287b..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/07-encoding.md +++ /dev/null @@ -1,288 +0,0 @@ -# Encoding - -While encoding in the Cosmos SDK used to be mainly handled by `go-amino` codec, the Cosmos SDK is moving towards using `gogoprotobuf` for both state and client-side encoding. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK application](../high-level-concepts/app-anatomy.md) {prereq} - -## Encoding - -The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) which is an object encoding specification and [Protocol Buffers](https://developers.google.com/protocol-buffers), a subset of Proto3 with an extension for -interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) -for more information on Proto3, which Amino is largely compatible with (but not with Proto2). - -Due to Amino having significant performance drawbacks, being reflection-based, and -not having any meaningful cross-language/client support, Protocol Buffers, specifically -[gogoprotobuf](https://github.com/gogo/protobuf/), is being used in place of Amino. -Note, this process of using Protocol Buffers over Amino is still an ongoing process. - -Binary wire encoding of types in the Cosmos SDK can be broken down into two main -categories, client encoding and store encoding. Client encoding mainly revolves -around transaction processing and signing, whereas store encoding revolves around -types used in state-machine transitions and what is ultimately stored in the Merkle -tree. - -For store encoding, protobuf definitions can exist for any type and will typically -have an Amino-based "intermediary" type. Specifically, the protobuf-based type -definition is used for serialization and persistence, whereas the Amino-based type -is used for business logic in the state-machine where they may convert back-n-forth. -Note, the Amino-based types may slowly be phased-out in the future, so developers -should take note to use the protobuf message definitions where possible. - -In the `codec` package, there exists two core interfaces, `BinaryCodec` and `JSONCodec`, -where the former encapsulates the current Amino interface except it operates on -types implementing the latter instead of generic `interface{}` types. - -In addition, there exists two implementations of `Codec`. The first being -`AminoCodec`, where both binary and JSON serialization is handled via Amino. The -second being `ProtoCodec`, where both binary and JSON serialization is handled -via Protobuf. - -This means that modules may use Amino or Protobuf encoding, but the types must -implement `ProtoMarshaler`. If modules wish to avoid implementing this interface -for their types, they may use an Amino codec directly. - -### Amino - -Every module uses an Amino codec to serialize types and interfaces. This codec typically -has types and interfaces registered in that module's domain only (e.g. messages), -but there are exceptions like `x/gov`. Each module exposes a `RegisterLegacyAminoCodec` function -that allows a user to provide a codec and have all the types registered. An application -will call this method for each necessary module. - -Where there is no protobuf-based type definition for a module (see below), Amino -is used to encode and decode raw wire bytes to the concrete type or interface: - -```go -bz := keeper.cdc.MustMarshal(typeOrInterface) -keeper.cdc.MustUnmarshal(bz, &typeOrInterface) -``` - -Note, there are length-prefixed variants of the above functionality and this is -typically used for when the data needs to be streamed or grouped together -(e.g. `ResponseDeliverTx.Data`) - -#### Authz authorizations - -Since the `MsgExec` message type can contain different messages instances, it is important that developers -add the following code inside the `init` method of their module's `codec.go` file: - -```go -import authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" - -init() { - // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be - // used to properly serialize MsgGrant and MsgExec instances - RegisterLegacyAminoCodec(authzcodec.Amino) -} -``` - -This will allow the `x/authz` module to properly serialize and de-serializes `MsgExec` instances using Amino, -which is required when signing this kind of messages using a Ledger. - -### Gogoproto - -Modules are encouraged to utilize Protobuf encoding for their respective types. In the Cosmos SDK, we use the [Gogoproto](https://github.com/gogo/protobuf) specific implementation of the Protobuf spec that offers speed and DX improvements compared to the official [Google protobuf implementation](https://github.com/protocolbuffers/protobuf). - -### Guidelines for protobuf message definitions - -In addition to [following official Protocol Buffer guidelines](https://developers.google.com/protocol-buffers/docs/proto3#simple), we recommend using these annotations in .proto files when dealing with interfaces: - -* use `cosmos_proto.accepts_interface` to annote fields that accept interfaces -* pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` -* annotate interface implementations with `cosmos_proto.implements_interface` -* pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` - -### Transaction Encoding - -Another important use of Protobuf is the encoding and decoding of -[transactions](./01-transactions.md). Transactions are defined by the application or -the Cosmos SDK but are then passed to the underlying consensus engine to be relayed to -other peers. Since the underlying consensus engine is agnostic to the application, -the consensus engine accepts only transactions in the form of raw bytes. - -* The `TxEncoder` object performs the encoding. -* The `TxDecoder` object performs the decoding. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/tx_msg.go#L72-L76 - -A standard implementation of both these objects can be found in the [`auth` module](../../x/auth/spec/README.md): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/tx/decoder.go - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/tx/encoder.go - -See [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) for details of how a transaction is encoded. - -### Interface Encoding and Usage of `Any` - -The Protobuf DSL is strongly typed, which can make inserting variable-typed fields difficult. Imagine we want to create a `Profile` protobuf message that serves as a wrapper over [an account](../high-level-concepts/03-accounts.md): - -```proto -message Profile { - // account is the account associated to a profile. - cosmos.auth.v1beta1.BaseAccount account = 1; - // bio is a short description of the account. - string bio = 4; -} -``` - -In this `Profile` example, we hardcoded `account` as a `BaseAccount`. However, there are several other types of [user accounts related to vesting](../../x/auth/spec/05_vesting.md), such as `BaseVestingAccount` or `ContinuousVestingAccount`. All of these accounts are different, but they all implement the `AccountI` interface. How would you create a `Profile` that allows all these types of accounts with an `account` field that accepts an `AccountI` interface? - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/types/account.go#L301-L324 - -In [ADR-019](../architecture/adr-019-protobuf-state-encoding.md), it has been decided to use [`Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto)s to encode interfaces in protobuf. An `Any` contains an arbitrary serialized message as bytes, along with a URL that acts as a globally unique identifier for and resolves to that message's type. This strategy allows us to pack arbitrary Go types inside protobuf messages. Our new `Profile` then looks like: - -```protobuf -message Profile { - // account is the account associated to a profile. - google.protobuf.Any account = 1 [ - (cosmos_proto.accepts_interface) = "AccountI"; // Asserts that this field only accepts Go types implementing `AccountI`. It is purely informational for now. - ]; - // bio is a short description of the account. - string bio = 4; -} -``` - -To add an account inside a profile, we need to "pack" it inside an `Any` first, using `codectypes.NewAnyWithValue`: - -```go -var myAccount AccountI -myAccount = ... // Can be a BaseAccount, a ContinuousVestingAccount or any struct implementing `AccountI` - -// Pack the account into an Any -accAny, err := codectypes.NewAnyWithValue(myAccount) -if err != nil { - return nil, err -} - -// Create a new Profile with the any. -profile := Profile { - Account: accAny, - Bio: "some bio", -} - -// We can then marshal the profile as usual. -bz, err := cdc.Marshal(profile) -jsonBz, err := cdc.MarshalJSON(profile) -``` - -To summarize, to encode an interface, you must 1/ pack the interface into an `Any` and 2/ marshal the `Any`. For convenience, the Cosmos SDK provides a `MarshalInterface` method to bundle these two steps. Have a look at [a real-life example in the x/auth module](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/keeper/keeper.go#L230-L233). - -The reverse operation of retrieving the concrete Go type from inside an `Any`, called "unpacking", is done with the `GetCachedValue()` on `Any`. - -```go -profileBz := ... // The proto-encoded bytes of a Profile, e.g. retrieved through gRPC. -var myProfile Profile -// Unmarshal the bytes into the myProfile struct. -err := cdc.Unmarshal(profilebz, &myProfile) - -// Let's see the types of the Account field. -fmt.Printf("%T\n", myProfile.Account) // Prints "Any" -fmt.Printf("%T\n", myProfile.Account.GetCachedValue()) // Prints "BaseAccount", "ContinuousVestingAccount" or whatever was initially packed in the Any. - -// Get the address of the accountt. -accAddr := myProfile.Account.GetCachedValue().(AccountI).GetAddress() -``` - -It is important to note that for `GetCachedValue()` to work, `Profile` (and any other structs embedding `Profile`) must implement the `UnpackInterfaces` method: - -```go -func (p *Profile) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { - if p.Account != nil { - var account AccountI - return unpacker.UnpackAny(p.Account, &account) - } - - return nil -} -``` - -The `UnpackInterfaces` gets called recursively on all structs implementing this method, to allow all `Any`s to have their `GetCachedValue()` correctly populated. - -For more information about interface encoding, and especially on `UnpackInterfaces` and how the `Any`'s `type_url` gets resolved using the `InterfaceRegistry`, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md). - -#### `Any` Encoding in the Cosmos SDK - -The above `Profile` example is a fictive example used for educational purposes. In the Cosmos SDK, we use `Any` encoding in several places (non-exhaustive list): - -* the `cryptotypes.PubKey` interface for encoding different types of public keys, -* the `sdk.Msg` interface for encoding different `Msg`s in a transaction, -* the `AccountI` interface for encodinig different types of accounts (similar to the above example) in the x/auth query responses, -* the `Evidencei` interface for encoding different types of evidences in the x/evidence module, -* the `AuthorizationI` interface for encoding different types of x/authz authorizations, -* the [`Validator`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/types/staking.pb.go#L306-L339) struct that contains information about a validator. - -A real-life example of encoding the pubkey as `Any` inside the Validator struct in x/staking is shown in the following example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/types/validator.go#L40-L61 - -## FAQ - -### How to create modules using protobuf encoding - -#### Defining module types - -Protobuf types can be defined to encode: - -* state -* [`Msg`s](../building-modules/02-messages-and-queries.md#messages) -* [Query services](../building-modules/04-query-services.md) -* [genesis](../building-modules/08-genesis.md) - -#### Naming and conventions - -We encourage developers to follow industry guidelines: [Protocol Buffers style guide](https://developers.google.com/protocol-buffers/docs/style) -and [Buf](https://buf.build/docs/style-guide), see more details in [ADR 023](../architecture/adr-023-protobuf-naming.md) - -### How to update modules to protobuf encoding - -If modules do not contain any interfaces (e.g. `Account` or `Content`), then they -may simply migrate any existing types that -are encoded and persisted via their concrete Amino codec to Protobuf (see 1. for further guidelines) and accept a `Marshaler` as the codec which is implemented via the `ProtoCodec` -without any further customization. - -However, if a module type composes an interface, it must wrap it in the `sdk.Any` (from `/types` package) type. To do that, a module-level .proto file must use [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto) for respective message type interface types. - -For example, in the `x/evidence` module defines an `Evidence` interface, which is used by the `MsgSubmitEvidence`. The structure definition must use `sdk.Any` to wrap the evidence file. In the proto file we define it as follows: - -```protobuf -// proto/cosmos/evidence/v1beta1/tx.proto - -message MsgSubmitEvidence { - string submitter = 1; - google.protobuf.Any evidence = 2 [(cosmos_proto.accepts_interface) = "Evidence"]; -} -``` - -The Cosmos SDK `codec.Codec` interface provides support methods `MarshalInterface` and `UnmarshalInterface` to easy encoding of state to `Any`. - -Module should register interfaces using `InterfaceRegistry` which provides a mechanism for registering interfaces: `RegisterInterface(protoName string, iface interface{}, impls ...proto.Message)` and implementations: `RegisterImplementations(iface interface{}, impls ...proto.Message)` that can be safely unpacked from Any, similarly to type registration with Amino: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/codec/types/interface_registry.go#L25-L55 - -In addition, an `UnpackInterfaces` phase should be introduced to deserialization to unpack interfaces before they're needed. Protobuf types that contain a protobuf `Any` either directly or via one of their members should implement the `UnpackInterfacesMessage` interface: - -```go -type UnpackInterfacesMessage interface { - UnpackInterfaces(InterfaceUnpacker) error -} -``` - -### Custom Stringer - -Using `option (gogoproto.goproto_stringer) = false;` in a proto message definition leads to unexpected behaviour, like returning wrong output or having missing fields in the output. -For that reason a proto Message's `String()` must not be customized, and the `goproto_stringer` option must be avoided. - -A correct YAML output can be obtained through ProtoJSON, using the `JSONToYAML` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/codec/yaml.go#L8-L20 - -For example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/x/auth/types/account.go#L139-L151 - -## Next {hide} - -Learn about [gRPC, REST and other endpoints](./08-grpc_rest.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/08-grpc_rest.md b/versioned_docs/version-0.46/develop/advanced-concepts/08-grpc_rest.md deleted file mode 100644 index 1adff19e0..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/08-grpc_rest.md +++ /dev/null @@ -1,95 +0,0 @@ -# gRPC, REST, and Tendermint Endpoints - -This document presents an overview of all the endpoints a node exposes: gRPC, REST as well as some other endpoints. {synopsis} - -## An Overview of All Endpoints - -Each node exposes the following endpoints for users to interact with a node, each endpoint is served on a different port. Details on how to configure each endpoint is provided in the endpoint's own section. - -* the gRPC server (default port: `9090`), -* the REST server (default port: `1317`), -* the Tendermint RPC endpoint (default port: `26657`). - -::: tip -The node also exposes some other endpoints, such as the Tendermint P2P endpoint, or the [Prometheus endpoint](https://docs.tendermint.com/master/nodes/metrics.html#metrics), which are not directly related to the Cosmos SDK. Please refer to the [Tendermint documentation](https://docs.tendermint.com/master/tendermint-core/using-tendermint.html#configuration) for more information about these endpoints. -::: - -## gRPC Server - -In the Cosmos SDK, Protobuf is the main [encoding](./07-encoding) library. This brings a wide range of Protobuf-based tools that can be plugged into the Cosmos SDK. One such tool is [gRPC](https://grpc.io), a modern open-source high performance RPC framework that has decent client support in several languages. - -Each module exposes a [Protobuf `Query` service](../building-modules/02-messages-and-queries.md#queries) that defines state queries. The `Query` services and a transaction service used to broadcast transactions are hooked up to the gRPC server via the following function inside the application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/types/app.go#L45-L47 - -Note: It is not possible to expose any [Protobuf `Msg` service](../building-modules/02-messages-and-queries.md#messages) endpoints via gRPC. Transactions must be generated and signed using the CLI or programmatically before they can be broadcasted using gRPC. See [Generating, Signing, and Broadcasting Transactions](../run-node/txs.html) for more information. - -The `grpc.Server` is a concrete gRPC server, which spawns and serves all gRPC query requests and a broadcast transaction request. This server can be configured inside `~/.simapp/config/app.toml`: - -* `grpc.enable = true|false` field defines if the gRPC server should be enabled. Defaults to `true`. -* `grpc.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `0.0.0.0:9090`. - -:::tip -`~/.simapp` is the directory where the node's configuration and databases are stored. By default, it's set to `~/.{app_name}`. -::: - -Once the gRPC server is started, you can send requests to it using a gRPC client. Some examples are given in our [Interact with the Node](../run-node/interact-node.md#using-grpc) tutorial. - -An overview of all available gRPC endpoints shipped with the Cosmos SDK is [Protobuf documentation](https://buf.build/cosmos/cosmos-sdk). - -## REST Server - -Cosmos SDK supports REST routes via gRPC-gateway. - -All routes are configured under the following fields in `~/.simapp/config/app.toml`: - -* `api.enable = true|false` field defines if the REST server should be enabled. Defaults to `false`. -* `api.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `tcp://0.0.0.0:1317`. -* some additional API configuration options are defined in `~/.simapp/config/app.toml`, along with comments, please refer to that file directly. - -### gRPC-gateway REST Routes - -If, for various reasons, you cannot use gRPC (for example, you are building a web application, and browsers don't support HTTP2 on which gRPC is built), then the Cosmos SDK offers REST routes via gRPC-gateway. - -[gRPC-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) is a tool to expose gRPC endpoints as REST endpoints. For each gRPC endpoint defined in a Protobuf `Query` service, the Cosmos SDK offers a REST equivalent. For instance, querying a balance could be done via the `/cosmos.bank.v1beta1.QueryAllBalances` gRPC endpoint, or alternatively via the gRPC-gateway `"/cosmos/bank/v1beta1/balances/{address}"` REST endpoint: both will return the same result. For each RPC method defined in a Protobuf `Query` service, the corresponding REST endpoint is defined as an option: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/bank/v1beta1/query.proto#L19-L22 - -For application developers, gRPC-gateway REST routes needs to be wired up to the REST server, this is done by calling the `RegisterGRPCGatewayRoutes` function on the ModuleManager. - -### Swagger - -A [Swagger](https://swagger.io/) (or OpenAPIv2) specification file is exposed under the `/swagger` route on the API server. Swagger is an open specification describing the API endpoints a server serves, including description, input arguments, return types and much more about each endpoint. - -Enabling the `/swagger` endpoint is configurable inside `~/.simapp/config/app.toml` via the `api.swagger` field, which is set to true by default. - -For application developers, you may want to generate your own Swagger definitions based on your custom modules. -The Cosmos SDK's [Swagger generation script](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/scripts/protoc-swagger-gen.sh) is a good place to start. - -## Tendermint RPC - -Independently from the Cosmos SDK, Tendermint also exposes a RPC server. This RPC server can be configured by tuning parameters under the `rpc` table in the `~/.simapp/config/config.toml`, the default listening address is `tcp://0.0.0.0:26657`. An OpenAPI specification of all Tendermint RPC endpoints is available [here](https://docs.tendermint.com/master/rpc/). - -Some Tendermint RPC endpoints are directly related to the Cosmos SDK: - -* `/abci_query`: this endpoint will query the application for state. As the `path` parameter, you can send the following strings: - * any Protobuf fully-qualified service method, such as `/cosmos.bank.v1beta1.QueryAllBalances`. The `data` field should then include the method's request parameter(s) encoded as bytes using Protobuf. - * `/app/simulate`: this will simulate a transaction, and return some information such as gas used. - * `/app/version`: this will return the application's version. - * `/store/{path}`: this will query the store directly. - * `/p2p/filter/addr/{port}`: this will return a filtered list of the node's P2P peers by address port. - * `/p2p/filter/id/{id}`: this will return a filtered list of the node's P2P peers by ID. -* `/broadcast_tx_{aync,async,commit}`: these 3 endpoint will broadcast a transaction to other peers. CLI, gRPC and REST expose [a way to broadcast transations](./01-transactions.md#broadcasting-the-transaction), but they all use these 3 Tendermint RPCs under the hood. - -## Comparison Table - -| Name | Advantages | Disadvantages | -| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| gRPC | - can use code-generated stubs in various languages
- supports streaming and bidirectional communication (HTTP2)
- small wire binary sizes, faster transmission | - based on HTTP2, not available in browsers
- learning curve (mostly due to Protobuf) | -| REST | - ubiquitous
- client libraries in all languages, faster implementation
| - only supports unary request-response communication (HTTP1.1)
- bigger over-the-wire message sizes (JSON) | -| CometBFT RPC | - easy to use | - bigger over-the-wire message sizes (JSON) | - - -## Next {hide} - -Learn about [the CLI](./09-cli.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/09-cli.md b/versioned_docs/version-0.46/develop/advanced-concepts/09-cli.md deleted file mode 100644 index abac5c67b..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/09-cli.md +++ /dev/null @@ -1,152 +0,0 @@ -# Command-Line Interface - -This document describes how command-line interface (CLI) works on a high-level, for an [**application**](../high-level-concepts/app-anatomy.md). A separate document for implementing a CLI for a Cosmos SDK [**module**](../building-modules/intro.md) can be found [here](../building-modules/09-module-interfaces.md#cli). {synopsis} - -## Command-Line Interface - -### Example Command - -There is no set way to create a CLI, but Cosmos SDK modules typically use the [Cobra Library](https://github.com/spf13/cobra). Building a CLI with Cobra entails defining commands, arguments, and flags. [**Commands**](#commands) understand the actions users wish to take, such as `tx` for creating a transaction and `query` for querying the application. Each command can also have nested subcommands, necessary for naming the specific transaction type. Users also supply **Arguments**, such as account numbers to send coins to, and [**Flags**](#flags) to modify various aspects of the commands, such as gas prices or which node to broadcast to. - -Here is an example of a command a user might enter to interact with the simapp CLI `simd` in order to send some tokens: - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --gas auto --gas-prices -``` - -The first four strings specify the command: - -* The root command for the entire application `simd`. -* The subcommand `tx`, which contains all commands that let users create transactions. -* The subcommand `bank` to indicate which module to route the command to ([`x/bank`](../../x/bank/spec/README.md) module in this case). -* The type of transaction `send`. - -The next two strings are arguments: the `from_address` the user wishes to send from, the `to_address` of the recipient, and the `amount` they want to send. Finally, the last few strings of the command are optional flags to indicate how much the user is willing to pay in fees (calculated using the amount of gas used to execute the transaction and the gas prices provided by the user). - -The CLI interacts with a [node](../advanced-concepts/03-node.md) to handle this command. The interface itself is defined in a `main.go` file. - -### Building the CLI - -The `main.go` file needs to have a `main()` function that creates a root command, to which all the application commands will be added as subcommands. The root command additionally handles: - -* **setting configurations** by reading in configuration files (e.g. the Cosmos SDK config file). -* **adding any flags** to it, such as `--chain-id`. -* **instantiating the `codec`** by calling the application's `MakeCodec()` function (called `MakeTestEncodingConfig` in `simapp`). The [`codec`](../advanced-concepts/05-encoding.md) is used to encode and decode data structures for the application - stores can only persist `[]byte`s so the developer must define a serialization format for their data structures or use the default, Protobuf. -* **adding subcommand** for all the possible user interactions, including [transaction commands](#transaction-commands) and [query commands](#query-commands). - -The `main()` function finally creates an executor and [execute](https://pkg.go.dev/github.com/spf13/cobra#Command.Execute) the root command. See an example of `main()` function from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/main.go#L12-L24 - -The rest of the document will detail what needs to be implemented for each step and include smaller portions of code from the `simapp` CLI files. - -## Adding Commands to the CLI - -Every application CLI first constructs a root command, then adds functionality by aggregating subcommands (often with further nested subcommands) using `rootCmd.AddCommand()`. The bulk of an application's unique capabilities lies in its transaction and query commands, called `TxCmd` and `QueryCmd` respectively. - -### Root Command - -The root command (called `rootCmd`) is what the user first types into the command line to indicate which application they wish to interact with. The string used to invoke the command (the "Use" field) is typically the name of the application suffixed with `-d`, e.g. `simd` or `gaiad`. The root command typically includes the following commands to support basic functionality in the application. - -* **Status** command from the Cosmos SDK rpc client tools, which prints information about the status of the connected [`Node`](../advanced-concepts/03-node.md). The Status of a node includes `NodeInfo`,`SyncInfo` and `ValidatorInfo`. -* **Keys** [commands](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/keys) from the Cosmos SDK client tools, which includes a collection of subcommands for using the key functions in the Cosmos SDK crypto tools, including adding a new key and saving it to the keyring, listing all public keys stored in the keyring, and deleting a key. For example, users can type `simd keys add ` to add a new key and save an encrypted copy to the keyring, using the flag `--recover` to recover a private key from a seed phrase or the flag `--multisig` to group multiple keys together to create a multisig key. For full details on the `add` key command, see the code [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/keys/add.go). For more details about usage of `--keyring-backend` for storage of key credentials look at the [keyring docs](../run-node/keyring.md). -* **Server** commands from the Cosmos SDK server package. These commands are responsible for providing the mechanisms necessary to start an ABCI Tendermint application and provides the CLI framework (based on [cobra](github.com/spf13/cobra)) necessary to fully bootstrap an application. The package exposes two core functions: `StartCmd` and `ExportCmd` which creates commands to start the application and export state respectively. Click [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server) to learn more. -* [**Transaction**](#transaction-commands) commands. -* [**Query**](#query-commands) commands. - -Next is an example `rootCmd` function from the `simapp` application. It instantiates the root command, adds a [_persistent_ flag](#flags) and `PreRun` function to be run before every execution, and adds all of the necessary subcommands. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L39-L85 - -`rootCmd` has a function called `initAppConfig()` which is useful for setting the application's custom configs. -By default app uses Tendermint app config template from Cosmos SDK, which can be over-written via `initAppConfig()`. -Here's an example code to override default `app.toml` template. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L99-L153 - -The `initAppConfig()` also allows overriding the default Cosmos SDK's [server config](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/config/config.go#L235). One example is the `min-gas-prices` config, which defines the minimum gas prices a validator is willing to accept for processing a transaction. By default, the Cosmos SDK sets this parameter to `""` (empty string), which forces all validators to tweak their own `app.toml` and set a non-empty value, or else the node will halt on startup. This might not be the best UX for validators, so the chain developer can set a default `app.toml` value for validators inside this `initAppConfig()` function. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L119-L134 - -The root-level `status` and `keys` subcommands are common across most applications and do not interact with application state. The bulk of an application's functionality - what users can actually _do_ with it - is enabled by its `tx` and `query` commands. - -### Transaction Commands - -[Transactions](./01-transactions.md) are objects wrapping [`Msg`s](../building-modules/02-messages-and-queries.md#messages) that trigger state changes. To enable the creation of transactions using the CLI interface, a function `txCommand` is generally added to the `rootCmd`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L175-L181 - -This `txCommand` function adds all the transaction available to end-users for the application. This typically includes: - -* **Sign command** from the [`auth`](../../x/auth/spec/README.md) module that signs messages in a transaction. To enable multisig, add the `auth` module's `MultiSign` command. Since every transaction requires some sort of signature in order to be valid, the signing command is necessary for every application. -* **Broadcast command** from the Cosmos SDK client tools, to broadcast transactions. -* **All [module transaction commands](../building-modules/09-module-interfaces.md#transaction-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddTxCommands()` function. - -Here is an example of a `txCommand` aggregating these subcommands from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L215-L240 - -### Query Commands - -[**Queries**](../building-modules/02-messages-and-queries.md#queries) are objects that allow users to retrieve information about the application's state. To enable the creation of queries using the CLI interface, a function `queryCommand` is generally added to the `rootCmd`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L175-L181 - -This `queryCommand` function adds all the queries available to end-users for the application. This typically includes: - -* **QueryTx** and/or other transaction query commands] from the `auth` module which allow the user to search for a transaction by inputting its hash, a list of tags, or a block height. These queries allow users to see if transactions have been included in a block. -* **Account command** from the `auth` module, which displays the state (e.g. account balance) of an account given an address. -* **Validator command** from the Cosmos SDK rpc client tools, which displays the validator set of a given height. -* **Block command** from the Cosmos SDK rpc client tools, which displays the block data for a given height. -* **All [module query commands](../building-modules/09-module-interfaces.md#query-commands)** the application is dependent on, retrieved by using the [basic module manager's](../building-modules/01-module-manager.md#basic-manager) `AddQueryCommands()` function. - -Here is an example of a `queryCommand` aggregating subcommands from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L191-L213 - -## Flags - -Flags are used to modify commands; developers can include them in a `flags.go` file with their CLI. Users can explicitly include them in commands or pre-configure them by inside their [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). Commonly pre-configured flags include the `--node` to connect to and `--chain-id` of the blockchain the user wishes to interact with. - -A _persistent_ flag (as opposed to a _local_ flag) added to a command transcends all of its children: subcommands will inherit the configured values for these flags. Additionally, all flags have default values when they are added to commands; some toggle an option off but others are empty values that the user needs to override to create valid commands. A flag can be explicitly marked as _required_ so that an error is automatically thrown if the user does not provide a value, but it is also acceptable to handle unexpected missing flags differently. - -Flags are added to commands directly (generally in the [module's CLI file](../building-modules/09-module-interfaces.md#flags) where module commands are defined) and no flag except for the `rootCmd` persistent flags has to be added at application level. It is common to add a _persistent_ flag for `--chain-id`, the unique identifier of the blockchain the application pertains to, to the root command. Adding this flag can be done in the `main()` function. Adding this flag makes sense as the chain ID should not be changing across commands in this application CLI. Here is an example from the `simapp` application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L210-L210 - -## Environment variables - -Each flag is bound to it's respecteve named environment variable. Then name of the environment variable consist of two parts - capital case `basename` followed by flag name of the flag. `-` must be substituted with `_`. For example flag `--home` for application with basename `GAIA` is bound to `GAIA_HOME`. It allows reducing the amount of flags typed for routine operations. For example instead of: - -```sh -gaia --home=./ --node= --chain-id="testchain-1" --keyring-backend=test tx ... --from= -``` - -this will be more convenient: - -```sh -# define env variables in .env, .envrc etc -GAIA_HOME= -GAIA_NODE= -GAIA_CHAIN_ID="testchain-1" -GAIA_KEYRING_BACKEND="test" - -# and later just use -gaia tx ... --from= -``` - -## Configurations - -It is vital that the root command of an application uses `PersistentPreRun()` cobra command property for executing the command, so all child commands have access to the server and client contexts. These contexts are set as their default values initially and maybe modified, scoped to the command, in their respective `PersistentPreRun()` functions. Note that the `client.Context` is typically pre-populated with "default" values that may be useful for all commands to inherit and override if necessary. - -Here is an example of an `PersistentPreRun()` function from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/simd/cmd/root.go#L56-L79 - -The `SetCmdClientContextHandler` call reads persistent flags via `ReadPersistentCommandFlags` which creates a `client.Context` and sets that on the root command's `Context`. - -The `InterceptConfigsPreRunHandler` call creates a viper literal, default `server.Context`, and a logger and sets that on the root command's `Context`. The `server.Context` will be modified and saved to disk via the internal `interceptConfigs` call, which either reads or creates a Tendermint configuration based on the home path provided. In addition, `interceptConfigs` also reads and loads the application configuration, `app.toml`, and binds that to the `server.Context` viper literal. This is vital so the application can get access to not only the CLI flags, but also to the application configuration values provided by this file. - -## Next {hide} - -Learn about [events](./10-events.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/10-events.md b/versioned_docs/version-0.46/develop/advanced-concepts/10-events.md deleted file mode 100644 index 032bbc3ab..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/10-events.md +++ /dev/null @@ -1,135 +0,0 @@ -# Events - -`Event`s are objects that contain information about the execution of the application. They are mainly used by service providers like block explorers and wallet to track the execution of various messages and index transactions. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK application](../high-level-concepts/app-anatomy.md) {prereq} -* [Tendermint Documentation on Events](https://docs.tendermint.com/master/spec/abci/abci.html#events) {prereq} - -## Typed Events - -Events are implemented in the Cosmos SDK as an alias of the ABCI `Event` type and -take the form of: `{eventType}.{attributeKey}={attributeValue}`. - -+++ https://github.com/tendermint/tendermint/blob/v0.35.4/proto/tendermint/abci/types.proto#L273-L279 - -An Event contains: - -* A `type` to categorize the Event at a high-level; for example, the Cosmos SDK uses the `"message"` type to filter Events by `Msg`s. -* A list of `attributes` are key-value pairs that give more information about the categorized Event. For example, for the `"message"` type, we can filter Events by key-value pairs using `message.action={some_action}`, `message.module={some_module}` or `message.sender={some_sender}`. - -::: tip -To parse the attribute values as strings, make sure to add `'` (single quotes) around each attribute value. -::: - -_Typed Events_ are Protobuf-defined [messages](../architecture/adr-032-typed-events.md) used by the Cosmos SDK -for emitting and querying Events. They are defined in a `event.proto` file, on a **per-module basis**. -They are triggered from the module's Protobuf [`Msg` service](../building-modules/03-msg-services.md) -by using the [`EventManager`](#eventmanager), where they are read as `proto.Message`. - -In addition, each module documents its Events under `spec/xx_events.md`. - -Lastly, Events are returned to the underlying consensus engine in the response of the following ABCI messages: - -* [`BeginBlock`](./00-baseapp.md#beginblock) -* [`EndBlock`](./00-baseapp.md#endblock) -* [`CheckTx`](./00-baseapp.md#checktx) -* [`DeliverTx`](./00-baseapp.md#delivertx) - -### Examples - -The following examples show how to query Events using the Cosmos SDK. - -| Event | Description | -| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `tx.height=23` | Query all transactions at height 23 | -| `message.action='/cosmos.bank.v1beta1.Msg/Send'` | Query all transactions containing a x/bank `Send` [Service `Msg`](../building-modules/03-msg-services.md). Note the `'`s around the value. | -| `message.action='send'` | Query all transactions containing a x/bank `Send` [legacy `Msg`](../building-modules/03-msg-services.md#legacy-amino-msgs). Note the `'`s around the value. | -| `message.module='bank'` | Query all transactions containing messages from the x/bank module. Note the `'`s around the value. | -| `create_validator.validator='cosmosval1...'` | x/staking-specific Event, see [x/staking SPEC](../../../cosmos-sdk/x/staking/spec/07_events.md). | - -## EventManager - -In Cosmos SDK applications, Events are managed by an abstraction called the `EventManager`. -Internally, the `EventManager` tracks a list of Events for the entire execution flow of a -transaction or `BeginBlock`/`EndBlock`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/events.go#L17-L25 - -The `EventManager` comes with a set of useful methods to manage Events. The method -that is used most by module and application developers is `EmitTypedEvent` that tracks -an Event in the `EventManager`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/events.go#L50-L59 - -Module developers should handle Event emission via the `EventManager#EmitTypedEvent` in each message -`Handler` and in each `BeginBlock`/`EndBlock` handler. The `EventManager` is accessed via -the [`Context`](./03-context.md), where Event should be already registered, and emitted like this: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/group/keeper/msg_server.go#L89-L92 - -Module's `handler` function should also set a new `EventManager` to the `context` to isolate emitted Events per `message`: - -```go -func NewHandler(keeper Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - switch msg := msg.(type) { -``` - -See the [`Msg` services](../building-modules/03-msg-services.md) concept doc for a more detailed -view on how to typically implement Events and use the `EventManager` in modules. - -## Subscribing to Events - -You can use Tendermint's [Websocket](https://docs.tendermint.com/master/tendermint-core/subscription.html#subscribing-to-events-via-websocket) to subscribe to Events by calling the `subscribe` RPC method: - -```json -{ - "jsonrpc": "2.0", - "method": "subscribe", - "id": "0", - "params": { - "query": "tm.event='eventCategory' AND eventType.eventAttribute='attributeValue'" - } -} -``` - -The main `eventCategory` you can subscribe to are: - -* `NewBlock`: Contains Events triggered during `BeginBlock` and `EndBlock`. -* `Tx`: Contains Events triggered during `DeliverTx` (i.e. transaction processing). -* `ValidatorSetUpdates`: Contains validator set updates for the block. - -These Events are triggered from the `state` package after a block is committed. You can get the -full list of Event categories [on the Tendermint Go documentation](https://pkg.go.dev/github.com/tendermint/tendermint/types#pkg-constants). - -The `type` and `attribute` value of the `query` allow you to filter the specific Event you are looking for. For example, a `Mint` transaction triggers an Event of type `EventMint` and has an `Id` and an `Owner` as `attributes` (as defined in the [`events.proto` file of the `NFT` module](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/nft/v1beta1/event.proto#L14-L19)). - -Subscribing to this Event would be done like so: - -```json -{ - "jsonrpc": "2.0", - "method": "subscribe", - "id": "0", - "params": { - "query": "tm.event='Tx' AND mint.owner='ownerAddress'" - } -} -``` - -where `ownerAddress` is an address following the [`AccAddress`](../high-level-concepts/03-accounts.md#addresses) format. - -## Events (Deprecated) - -Previously, the Cosmos SDK supported emitting Events that were defined in `types/events.go`. It is the responsibility of the module developer to define Event types and Event attributes. Except in the `spec/XX_events.md` file, these Event types and attributes are unfortunately not easily discoverable, - -This is why this methods as been deprecated, and replaced by _[Typed Events](#typed-events). - -To learn more about the previous way of defining events, please refer to the [previous SDK documentation](https://docs.cosmos.network/v0.45events.html#events-2). - -## Next {hide} - -Learn about Cosmos SDK [telemetry](./11-telemetry.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/11-telemetry.md b/versioned_docs/version-0.46/develop/advanced-concepts/11-telemetry.md deleted file mode 100644 index e0606b6a4..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/11-telemetry.md +++ /dev/null @@ -1,126 +0,0 @@ -# Telemetry - -Gather relevant insights about your application and modules with custom metrics and telemetry. {synopsis} - -The Cosmos SDK enables operators and developers to gain insight into the performance and behavior of -their application through the use of the `telemetry` package. To enable telemetrics, set `telemetry.enabled = true` in the app.toml config file. - -The Cosmos SDK currently supports enabling in-memory and prometheus as telemetry sinks. In-memory sink is always attached (when the telemetry is enabled) with 10 second interval and 1 minute retention. This means that metrics will be aggregated over 10 seconds, and metrics will be kept alive for 1 minute. - -To query active metrics (see retention note above) you have to enable API server (`api.enabled = true` in the app.toml). Single API endpoint is exposed: `http://localhost:1317/metrics?format={text|prometheus}`, the default being `text`. - -## Emitting metrics - -If telemetry is enabled via configuration, a single global metrics collector is registered via the -[go-metrics](https://github.com/armon/go-metrics) library. This allows emitting and collecting -metrics through simple [API](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/telemetry/wrapper.go). Example: - -```go -func EndBlocker(ctx sdk.Context, k keeper.Keeper) { - defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) - - // ... -} -``` - -Developers may use the `telemetry` package directly, which provides wrappers around metric APIs -that include adding useful labels, or they must use the `go-metrics` library directly. It is preferable -to add as much context and adequate dimensionality to metrics as possible, so the `telemetry` package -is advised. Regardless of the package or method used, the Cosmos SDK supports the following metrics -types: - -* gauges -* summaries -* counters - -## Labels - -Certain components of modules will have their name automatically added as a label (e.g. `BeginBlock`). -Operators may also supply the application with a global set of labels that will be applied to all -metrics emitted using the `telemetry` package (e.g. chain-id). Global labels are supplied as a list -of [name, value] tuples. - -Example: - -```toml -global-labels = [ - ["chain_id", "chain-OfXo4V"], -] -``` - -## Cardinality - -Cardinality is key, specifically label and key cardinality. Cardinality is how many unique values of -something there are. So there is naturally a tradeoff between granularity and how much stress is put -on the telemetry sink in terms of indexing, scrape, and query performance. - -Developers should take care to support metrics with enough dimensionality and granularity to be -useful, but not increase the cardinality beyond the sink's limits. A general rule of thumb is to not -exceed a cardinality of 10. - -Consider the following examples with enough granularity and adequate cardinality: - -* begin/end blocker time -* tx gas used -* block gas used -* amount of tokens minted -* amount of accounts created - -The following examples expose too much cardinality and may not even prove to be useful: - -* transfers between accounts with amount -* voting/deposit amount from unique addresses - -## Supported Metrics - -| Metric | Description | Unit | Type | -|:--------------------------------|:------------------------------------------------------------------------------------------|:----------------|:--------| -| `tx_count` | Total number of txs processed via `DeliverTx` | tx | counter | -| `tx_successful` | Total number of successful txs processed via `DeliverTx` | tx | counter | -| `tx_failed` | Total number of failed txs processed via `DeliverTx` | tx | counter | -| `tx_gas_used` | The total amount of gas used by a tx | gas | gauge | -| `tx_gas_wanted` | The total amount of gas requested by a tx | gas | gauge | -| `tx_msg_send` | The total amount of tokens sent in a `MsgSend` (per denom) | token | gauge | -| `tx_msg_withdraw_reward` | The total amount of tokens withdrawn in a `MsgWithdrawDelegatorReward` (per denom) | token | gauge | -| `tx_msg_withdraw_commission` | The total amount of tokens withdrawn in a `MsgWithdrawValidatorCommission` (per denom) | token | gauge | -| `tx_msg_delegate` | The total amount of tokens delegated in a `MsgDelegate` | token | gauge | -| `tx_msg_begin_unbonding` | The total amount of tokens undelegated in a `MsgUndelegate` | token | gauge | -| `tx_msg_begin_begin_redelegate` | The total amount of tokens redelegated in a `MsgBeginRedelegate` | token | gauge | -| `tx_msg_ibc_transfer` | The total amount of tokens transferred via IBC in a `MsgTransfer` (source or sink chain) | token | gauge | -| `ibc_transfer_packet_receive` | The total amount of tokens received in a `FungibleTokenPacketData` (source or sink chain) | token | gauge | -| `new_account` | Total number of new accounts created | account | counter | -| `gov_proposal` | Total number of governance proposals | proposal | counter | -| `gov_vote` | Total number of governance votes for a proposal | vote | counter | -| `gov_deposit` | Total number of governance deposits for a proposal | deposit | counter | -| `staking_delegate` | Total number of delegations | delegation | counter | -| `staking_undelegate` | Total number of undelegations | undelegation | counter | -| `staking_redelegate` | Total number of redelegations | redelegation | counter | -| `ibc_transfer_send` | Total number of IBC transfers sent from a chain (source or sink) | transfer | counter | -| `ibc_transfer_receive` | Total number of IBC transfers received to a chain (source or sink) | transfer | counter | -| `ibc_client_create` | Total number of clients created | create | counter | -| `ibc_client_update` | Total number of client updates | update | counter | -| `ibc_client_upgrade` | Total number of client upgrades | upgrade | counter | -| `ibc_client_misbehaviour` | Total number of client misbehaviours | misbehaviour | counter | -| `ibc_connection_open-init` | Total number of connection `OpenInit` handshakes | handshake | counter | -| `ibc_connection_open-try` | Total number of connection `OpenTry` handshakes | handshake | counter | -| `ibc_connection_open-ack` | Total number of connection `OpenAck` handshakes | handshake | counter | -| `ibc_connection_open-confirm` | Total number of connection `OpenConfirm` handshakes | handshake | counter | -| `ibc_channel_open-init` | Total number of channel `OpenInit` handshakes | handshake | counter | -| `ibc_channel_open-try` | Total number of channel `OpenTry` handshakes | handshake | counter | -| `ibc_channel_open-ack` | Total number of channel `OpenAck` handshakes | handshake | counter | -| `ibc_channel_open-confirm` | Total number of channel `OpenConfirm` handshakes | handshake | counter | -| `ibc_channel_close-init` | Total number of channel `CloseInit` handshakes | handshake | counter | -| `ibc_channel_close-confirm` | Total number of channel `CloseConfirm` handshakes | handshake | counter | -| `tx_msg_ibc_recv_packet` | Total number of IBC packets received | packet | counter | -| `tx_msg_ibc_acknowledge_packet` | Total number of IBC packets acknowledged | acknowledgement | counter | -| `ibc_timeout_packet` | Total number of IBC timeout packets | timeout | counter | -| `store_iavl_get` | Duration of an IAVL `Store#Get` call | ms | summary | -| `store_iavl_set` | Duration of an IAVL `Store#Set` call | ms | summary | -| `store_iavl_has` | Duration of an IAVL `Store#Has` call | ms | summary | -| `store_iavl_delete` | Duration of an IAVL `Store#Delete` call | ms | summary | -| `store_iavl_commit` | Duration of an IAVL `Store#Commit` call | ms | summary | -| `store_iavl_query` | Duration of an IAVL `Store#Query` call | ms | summary | - -## Next {hide} - -Learn about the [object-capability](./12-ocap.md) model {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/12-ocap.md b/versioned_docs/version-0.46/develop/advanced-concepts/12-ocap.md deleted file mode 100644 index 8412f487d..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/12-ocap.md +++ /dev/null @@ -1,74 +0,0 @@ -# Object-Capability Model - -## Intro - -When thinking about security, it is good to start with a specific threat model. Our threat model is the following: - -> We assume that a thriving ecosystem of Cosmos SDK modules that are easy to compose into a blockchain application will contain faulty or malicious modules. - -The Cosmos SDK is designed to address this threat by being the -foundation of an object capability system. - -> The structural properties of object capability systems favor -> modularity in code design and ensure reliable encapsulation in -> code implementation. -> -> These structural properties facilitate the analysis of some -> security properties of an object-capability program or operating -> system. Some of these — in particular, information flow properties -> — can be analyzed at the level of object references and -> connectivity, independent of any knowledge or analysis of the code -> that determines the behavior of the objects. -> -> As a consequence, these security properties can be established -> and maintained in the presence of new objects that contain unknown -> and possibly malicious code. -> -> These structural properties stem from the two rules governing -> access to existing objects: -> -> 1. An object A can send a message to B only if object A holds a -> reference to B. -> 2. An object A can obtain a reference to C only -> if object A receives a message containing a reference to C. As a -> consequence of these two rules, an object can obtain a reference -> to another object only through a preexisting chain of references. -> In short, "Only connectivity begets connectivity." - -For an introduction to object-capabilities, see this [Wikipedia article](https://en.wikipedia.org/wiki/Object-capability_model). - -## Ocaps in practice - -The idea is to only reveal what is necessary to get the work done. - -For example, the following code snippet violates the object capabilities -principle: - -```go -type AppAccount struct {...} -account := &AppAccount{ - Address: pub.Address(), - Coins: sdk.Coins{sdk.NewInt64Coin("ATM", 100)}, -} -sumValue := externalModule.ComputeSumValue(account) -``` - -The method `ComputeSumValue` implies a pure function, yet the implied -capability of accepting a pointer value is the capability to modify that -value. The preferred method signature should take a copy instead. - -```go -sumValue := externalModule.ComputeSumValue(*account) -``` - -In the Cosmos SDK, you can see the application of this principle in simapp. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L258-L283 - -The following diagram shows the current dependencies between keepers. - -![Keeper dependencies](./keeper_dependencies.svg) - -## Next {hide} - -Learn about the [`runTx` middleware](./13-runtx_middleware.md) {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/13-runtx_middleware.md b/versioned_docs/version-0.46/develop/advanced-concepts/13-runtx_middleware.md deleted file mode 100644 index 60e61abcf..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/13-runtx_middleware.md +++ /dev/null @@ -1,65 +0,0 @@ -# RunTx recovery middleware - -`BaseApp.runTx()` function handles Go panics that might occur during transactions execution, for example, keeper has faced an invalid state and paniced. -Depending on the panic type different handler is used, for instance the default one prints an error log message. -Recovery middleware is used to add custom panic recovery for Cosmos SDK application developers. - -More context can found in the corresponding [ADR-022](../architecture/adr-022-custom-panic-handling.md) and the implementation in [recovery.go](https://github.com/cosmos/cosmos-sdk/tree/v0.46.0-rc1/baseapp/recovery.go). - -## Interface - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/recovery.go#L11-L14 - -`recoveryObj` is a return value for `recover()` function from the `buildin` Go package. - -**Contract:** - -* RecoveryHandler returns `nil` if `recoveryObj` wasn't handled and should be passed to the next recovery middleware; -* RecoveryHandler returns a non-nil `error` if `recoveryObj` was handled; - -## Custom RecoveryHandler register - -`BaseApp.AddRunTxRecoveryHandler(handlers ...RecoveryHandler)` - -BaseApp method adds recovery middleware to the default recovery chain. - -## Example - -Lets assume we want to emit the "Consensus failure" chain state if some particular error occurred. - -We have a module keeper that panics: - -```go -func (k FooKeeper) Do(obj interface{}) { - if obj == nil { - // that shouldn't happen, we need to crash the app - err := sdkErrors.Wrap(fooTypes.InternalError, "obj is nil") - panic(err) - } -} -``` - -By default that panic would be recovered and an error message will be printed to log. To override that behaviour we should register a custom RecoveryHandler: - -```go -// Cosmos SDK application constructor -customHandler := func(recoveryObj interface{}) error { - err, ok := recoveryObj.(error) - if !ok { - return nil - } - - if fooTypes.InternalError.Is(err) { - panic(fmt.Errorf("FooKeeper did panic with error: %w", err)) - } - - return nil -} - -baseApp := baseapp.NewBaseApp(...) -baseApp.AddRunTxRecoveryHandler(customHandler) -``` - -## Next {hide} - -Learn about the [IBC](./../ibc/README.md) protocol {hide} diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/13-simulation.md b/versioned_docs/version-0.46/develop/advanced-concepts/13-simulation.md deleted file mode 100644 index 327d9ac8e..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/13-simulation.md +++ /dev/null @@ -1,97 +0,0 @@ -# Cosmos Blockchain Simulator - -The Cosmos SDK offers a full fledged simulation framework to fuzz test every -message defined by a module. - -On the Cosmos SDK, this functionality is provided by the[`SimApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go), which is a -`Baseapp` application that is used for running the [`simulation`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/simulation) module. -This module defines all the simulation logic as well as the operations for -randomized parameters like accounts, balances etc. - -## Goals - -The blockchain simulator tests how the blockchain application would behave under -real life circumstances by generating and sending randomized messages. -The goal of this is to detect and debug failures that could halt a live chain, -by providing logs and statistics about the operations run by the simulator as -well as exporting the latest application state when a failure was found. - -Its main difference with integration testing is that the simulator app allows -you to pass parameters to customize the chain that's being simulated. -This comes in handy when trying to reproduce bugs that were generated in the -provided operations (randomized or not). - -## Simulation commands - -The simulation app has different commands, each of which tests a different -failure type: - -* `AppImportExport`: The simulator exports the initial app state and then it - creates a new app with the exported `genesis.json` as an input, checking for - inconsistencies between the stores. -* `AppSimulationAfterImport`: Queues two simulations together. The first one provides the app state (_i.e_ genesis) to the second. Useful to test software upgrades or hard-forks from a live chain. -* `AppStateDeterminism`: Checks that all the nodes return the same values, in the same order. -* `BenchmarkInvariants`: Analysis of the performance of running all modules' invariants (_i.e_ sequentially runs a [benchmark](https://pkg.go.dev/testing/#hdr-Benchmarks) test). An invariant checks for - differences between the values that are on the store and the passive tracker. Eg: total coins held by accounts vs total supply tracker. -* `FullAppSimulation`: General simulation mode. Runs the chain and the specified operations for a given number of blocks. Tests that there're no `panics` on the simulation. It does also run invariant checks on every `Period` but they are not benchmarked. - -Each simulation must receive a set of inputs (_i.e_ flags) such as the number of -blocks that the simulation is run, seed, block size, etc. -Check the full list of flags [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/config.go#L32-L55). - -## Simulator Modes - -In addition to the various inputs and commands, the simulator runs in three modes: - -1. Completely random where the initial state, module parameters and simulation - parameters are **pseudo-randomly generated**. -2. From a `genesis.json` file where the initial state and the module parameters are defined. - This mode is helpful for running simulations on a known state such as a live network export where a new (mostly likely breaking) version of the application needs to be tested. -3. From a `params.json` file where the initial state is pseudo-randomly generated but the module and simulation parameters can be provided manually. - This allows for a more controlled and deterministic simulation setup while allowing the state space to still be pseudo-randomly simulated. - The list of available parameters are listed [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/config.go#L33-L57). - -::: tip -These modes are not mutually exclusive. So you can for example run a randomly -generated genesis state (`1`) with manually generated simulation params (`3`). -::: - -## Usage - -This is a general example of how simulations are run. For more specific examples -check the Cosmos SDK [Makefile](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/Makefile#L263-L299). - -```bash - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestApp \ - ... - -v -timeout 24h -``` - -## Debugging Tips - -Here are some suggestions when encountering a simulation failure: - -* Export the app state at the height where the failure was found. You can do this - by passing the `-ExportStatePath` flag to the simulator. -* Use `-Verbose` logs. They could give you a better hint on all the operations - involved. -* Reduce the simulation `-Period`. This will run the invariants checks more - frequently. -* Print all the failed invariants at once with `-PrintAllInvariants`. -* Try using another `-Seed`. If it can reproduce the same error and if it fails - sooner, you will spend less time running the simulations. -* Reduce the `-NumBlocks` . How's the app state at the height previous to the - failure? -* Run invariants on every operation with `-SimulateEveryOperation`. _Note_: this - will slow down your simulation **a lot**. -* Try adding logs to operations that are not logged. You will have to define a - [Logger](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/keeper/keeper.go#L60-L63) on your `Keeper`. - -## Use simulation in your Cosmos SDK-based application - -Learn how you can integrate the simulation into your Cosmos SDK-based application: - -* Application Simulation Manager -* [Building modules: Simulator](../building-modules/simulator.md) -* Simulator tests diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/14-proto-docs.md b/versioned_docs/version-0.46/develop/advanced-concepts/14-proto-docs.md deleted file mode 100644 index 8f4da8738..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/14-proto-docs.md +++ /dev/null @@ -1,3 +0,0 @@ -# Protobuf Documentation - -See [Cosmos-SDK Buf Proto-docs](https://buf.build/cosmos/cosmos-sdk/docs/main) diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/15-tips.md b/versioned_docs/version-0.46/develop/advanced-concepts/15-tips.md deleted file mode 100644 index d622b2ce5..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/15-tips.md +++ /dev/null @@ -1,202 +0,0 @@ -# Transaction Tips - -Transaction tips are a mechanism to pay for transaction fees using another denom than the native fee denom of the chain. They are still in beta, and are not included by default in the SDK. {synopsis} - -## Context - -In a Cosmos ecosystem where more and more chains are connected via [IBC](https://ibc.cosmos.network/), it happens that users want to perform actions on chains where they don't have native tokens yet. An example would be an Osmosis user who wants to vote on a proposal on the Cosmos Hub, but they don't have ATOMs in their wallet. A solution would be to swap OSMO for ATOM just for voting on this proposal, but that is cumbersome. Cross-chain DeFi project [Emeris](https://emeris.com/) is another use case. - -Transaction tips is a new solution for cross-chain transaction fees payment, whereby the transaction initiator signs a transaction without specifying fees, but uses a new `Tip` field. They send this signed transaction to a fee relayer who will choose the transaction fees and broadcast the final transaction, and the SDK provides a mechanism that will transfer the pre-defined `Tip` to the fee payer, to cover for fees. - -Assuming we have two chains, A and B, we define the following terms: - -* **the tipper**: this is the initiator of the transaction, who wants to execute a `Msg` on chain A, but doesn't have any native chain A tokens, only chain B tokens. In our example above, the tipper is the Osmosis (chain B) user wanting to vote on a Cosmos Hub (chain A) proposal. -* **the fee payer**: this is the party that will relay and broadcast the final transaction on chain A, and has chain A tokens. The tipper doesn't need to trust the feepayer. -* **the target chain**: the chain where the `Msg` is executed, chain A in this case. - -## Transaction Tips Flow - -The transaction tips flow happens in multiple steps. - -1. The tipper sends via IBC some chain B tokens to chain A. These tokens will cover for fees on the target chain A. This means that chain A's bank module holds some IBC tokens under the tipper's address. - -2. The tipper drafts a transaction to be executed on the chain A. It can include chain A `Msg`s. However, instead of creating a normal transaction, they create the following `AuxSignerData` document: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L230-L249 - -where we have defined `SignDocDirectAux` as: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L67-L93 - -where `Tip` is defined as - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L219-L228 - -Notice that this document doesn't sign over the final chain A fees. Instead, it includes a `Tip` field. It also doesn't include the whole `AuthInfo` object as in `SIGN_MODE_DIRECT`, only the minimum information needed by the tipper - -3. The tipper signs the `SignDocDirectAux` document and attaches the signature to the `AuxSignerData`, then sends the signed `AuxSignerData` to the fee payer. - -4. From the signed `AuxSignerData` document, the fee payer constructs a transaction, using the following algorithm: - -* use as `TxBody` the exact `AuxSignerData.SignDocDirectAux.body_bytes`, to not alter the original intent of the tipper, -* create an `AuthInfo` with: - * `AuthInfo.Tip` copied from `AuxSignerData.SignDocDirectAux.Tip`, - * `AuthInfo.Fee` chosen by the fee payer, which should cover for the transaction gas, but also be small enough so that the tip/fee exchange rate is economically interesting for the fee payer, - * `AuthInfo.SignerInfos` has two signers: the first signer is the tipper, using the public key, sequence and sign mode specified in `AuxSignerData`; and the second signer is the fee payer, using their favorite sign mode, -* a `Signatures` array with two items: the tipper's signature from `AuxSignerData.Sig`, and the final fee payer's signature. - -5. Broadcast the final transaction signed by the two parties to the target chain. Once included, the Cosmos SDK will trigger a transfer of the `Tip` specified in the transaction from the tipper address to the fee payer address. - -### Fee Payers Market - -The benefit of transaction tips for the tipper is clear: there is no need to swap tokens before executing a cross-chain message. - -For the fee payer, the benefit is in the tip v.s. fee exchange. Put simply, the fee payer pays the fees of an unknown tipper's transaction, and gets in exchange the tip that the tipper chose. There is an economic incentive for the fee payer to do so only when the tip is greater than the transaction fees, given the exchange rates between the two tokens. - -In the future, we imagine a market where fee payers will compete to include transactions from tippers, who on their side will optimize by specifying the lowest tip possible. A number of automated services might spin up to perform transaction gas simulation and exchange rate monitoring to optimize both the tip and fee values in real-time. - -### Tipper and Fee Payer Sign Modes - -As we mentioned in the flow above, the tipper signs over the `SignDocDirectAux`, and the fee payer signs over the whole final transaction. As such, both parties might use different sign modes. - -* The tipper MUST use `SIGN_MODE_DIRECT_AUX` or `SIGN_MODE_LEGACY_AMINO_JSON`. That is because the tipper needs to sign over the body, the tip, but not the other signers' information and not over the fee (which is unknown to the tipper). -* The fee payer MUST use `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`. The fee payer signs over the whole transaction. - -For example, if the fee payer signs the whole transaction with `SIGN_MODE_DIRECT_AUX`, it will be rejected by the node, as that would introduce malleability issues (`SIGN_MODE_DIRECT_AUX` doesn't sign over fees). - -In both cases, using `SIGN_MODE_LEGACY_AMINO_JSON` is recommended only if hardware wallet signing is needed. - -## Enabling Tips on your Chain - -The transaction tips functionality is introduced in Cosmos SDK v0.46, so earlier versions do not have support for tips. It is however not included by default in a v0.46 app. Sending a transaction with tips to a chain which didn't enable tips will result in a no-op, i.e. the `tip` field in the transaction will be ignored. - -Enabling tips on a chain is done by adding the `TipDecorator` in the posthandler chain: - -```go -// HandlerOptions are the options required for constructing a SDK PostHandler which supports tips. -type HandlerOptions struct { - BankKeeper types.BankKeeper -} - -// MyPostHandler returns a posthandler chain with the TipDecorator. -func MyPostHandler(options HandlerOptions) (sdk.AnteHandler, error) { - if options.BankKeeper == nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for posthandler") - } - - postDecorators := []sdk.AnteDecorator{ - posthandler.NewTipDecorator(options.bankKeeper), - } - - return sdk.ChainAnteDecorators(postDecorators...), nil -} - -func (app *SimApp) setPostHandler() { - postHandler, err := MyPostHandler( - HandlerOptions{ - BankKeeper: app.BankKeeper, - }, - ) - if err != nil { - panic(err) - } - - app.SetPostHandler(postHandler) -} -``` - -Notice that `NewTipDecorator` needs a reference to the BankKeeper, for transferring the tip to the fee payer. - -## CLI Usage - -The Cosmos SDK also provides some CLI tooling for the transaction tips flow, both for the tipper and for the feepayer. - -For the tipper, the CLI `tx` subcommand has two new flags: `--aux` and `--tip`. The `--aux` flag is used to denote that we are creating an `AuxSignerData` instead of a `Tx`, and the `--tip` is used to populate its `Tip` field. - -```bash -$ simd tx gov vote 16 yes --from --aux --tip 50ibcdenom - - -### Prints the AuxSignerData as JSON: -### {"address":"cosmos1q0ayf5vq6fd2xxrwh30upg05hxdnyw2h5249a2","sign_doc":{"body_bytes":"CosBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmsKLWNvc21vczFxMGF5ZjV2cTZmZDJ4eHJ3aDMwdXBnMDVoeGRueXcyaDUyNDlhMhItY29zbW9zMXdlNWoyZXI2MHV5OXF3YzBta3ptdGdtdHA5Z3F5NXY2bjhnZGdlGgsKBXN0YWtlEgIxMA==","public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AojOF/1luQ5H/nZDSrE1w3CyzGJhJdQuS7hFX5wAA6uJ"},"chain_id":"","account_number":"0","sequence":"1","tip":{"amount":[{"denom":"ibcdenom","amount":"50"}],"tipper":"cosmos1q0ayf5vq6fd2xxrwh30upg05hxdnyw2h5249a2"}},"mode":"SIGN_MODE_DIRECT_AUX","sig":"v/d/bGq9FGdecs6faMG2t//nRirFTiqwFtUB65M6kh0QdUeM6jg3r8oJX1o17xkoDxJ09EyJiSyvo6fbU7vUxg=="} -``` - -It is useful to pipe the JSON output to a file, `> aux_signed_tx.json` - -For the fee payer, the Cosmos SDK added a `tx aux-to-fee` subcommand to include an `AuxSignerData` into a transaction, add fees to it, and broadcast it. - -```bash -$ simd tx aux-to-fee aux_signed_tx.json --from --fees 30atom - -### Prints the broadcasted tx response: -### code: 0 -### codespace: sdk -### data: "" -### events: [] -### gas_used: "0" -### gas_wanted: "0" -### height: "0" -### info: "" -### logs: [] -### timestamp: "" -### tx: null -``` - -Upon completion of the second command, the fee payer's balance will be down the `30atom` fees, and up the `50ibcdenom` tip. - -For both commands, the flag `--sign-mode=amino-json` is still available for hardware wallet signing. - -## Programmatic Usage - -For the tipper, the SDK exposes a new transaction builder, the `AuxTxBuilder`, for generating an `AuxSignerData`. The API of `AuxTxBuilder` is defined [in `client/tx`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/tx/aux_builder.go#L16), and can be used as follows: - -```go -// Note: there's no need to use clientCtx.TxConfig anymore. - -bldr := clienttx.NewAuxTxBuilder() -err := bldr.SetMsgs(msgs...) -bldr.SetAddress("cosmos1...") -bldr.SetMemo(...) -bldr.SetTip(...) -bldr.SetPubKey(...) -err := bldr.SetSignMode(...) // DIRECT_AUX or AMINO, or else error -// ... other setters are also available - -// Get the bytes to sign. -signBz, err := bldr.GetSignBytes() - -// Sign the bz using your favorite method. -sig, err := privKey.sign(signBz) - -// Set the signature -bldr.SetSig(sig) - -// Get the final auxSignerData to be sent to the fee payer -auxSignerData, err:= bldr.GetAuxSignerData() -``` - -For the fee payer, the SDK added a new method on the existing `TxBuilder` to import data from an `AuxSignerData`: - -```go -// get `auxSignerData` from tipper, see code snippet above. - -txBuilder := clientCtx.TxConfig.NewTxBuilder() -err := txBuilder.AddAuxSignerData(auxSignerData) -if err != nil { - return err -} - -// A lot of fields will be populated in txBuilder, such as its Msgs, tip -// memo, etc... - -// The fee payer choses the fee to set on the transaction. -txBuilder.SetFeePayer() -txBuilder.SetFeeAmount(...) -txBuilder.SetGasLimit(...) - -// Usual signing code -err = authclient.SignTx(...) -if err != nil { - return err -} -``` diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/16-upgrade.md b/versioned_docs/version-0.46/develop/advanced-concepts/16-upgrade.md deleted file mode 100644 index a5940dafb..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/16-upgrade.md +++ /dev/null @@ -1,156 +0,0 @@ -# In-Place Store Migrations - -::: warning -Read and understand all the in-place store migration documentation before you run a migration on a live chain. -::: - -Upgrade your app modules smoothly with custom in-place store migration logic. {synopsis} - -The Cosmos SDK uses two methods to perform upgrades: - -* Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. - -* Perform upgrades in place, which significantly decrease the upgrade time for chains with a larger state. Use the [Module Upgrade Guide](../building-modules/13-upgrade.md) to set up your application modules to take advantage of in-place upgrades. - -This document provides steps to use the In-Place Store Migrations upgrade method. - -## Tracking Module Versions - -Each module gets assigned a consensus version by the module developer. The consensus version serves as the breaking change version of the module. The Cosmos SDK keeps track of all module consensus versions in the x/upgrade `VersionMap` store. During an upgrade, the difference between the old `VersionMap` stored in state and the new `VersionMap` is calculated by the Cosmos SDK. For each identified difference, the module-specific migrations are run and the respective consensus version of each upgraded module is incremented. - -### Consensus Version - -The consensus version is defined on each app module by the module developer and serves as the breaking change version of the module. The consensus version informs the Cosmos SDK on which modules need to be upgraded. For example, if the bank module was version 2 and an upgrade introduces bank module 3, the Cosmos SDK upgrades the bank module and runs the "version 2 to 3" migration script. - -### Version Map - -The version map is a mapping of module names to consensus versions. The map is persisted to x/upgrade's state for use during in-place migrations. When migrations finish, the updated version map is persisted in the state. - -## Upgrade Handlers - -Upgrades use an `UpgradeHandler` to facilitate migrations. The `UpgradeHandler` functions implemented by the app developer must conform to the following function signature. These functions retrieve the `VersionMap` from x/upgrade's state and return the new `VersionMap` to be stored in x/upgrade after the upgrade. The diff between the two `VersionMap`s determines which modules need upgrading. - -```go -type UpgradeHandler func(ctx sdk.Context, plan Plan, fromVM VersionMap) (VersionMap, error) -``` - -Inside these functions, you must perform any upgrade logic to include in the provided `plan`. All upgrade handler functions must end with the following line of code: - -```go - return app.mm.RunMigrations(ctx, cfg, fromVM) -``` - -## Running Migrations - -Migrations are run inside of an `UpgradeHandler` using `app.mm.RunMigrations(ctx, cfg, vm)`. The `UpgradeHandler` functions describe the functionality to occur during an upgrade. The `RunMigration` function loops through the `VersionMap` argument and runs the migration scripts for all versions that are less than the versions of the new binary app module. After the migrations are finished, a new `VersionMap` is returned to persist the upgraded module versions to state. - -```go -cfg := module.NewConfigurator(...) -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - - // ... - // additional upgrade logic - // ... - - // returns a VersionMap with the updated module ConsensusVersions - return app.mm.RunMigrations(ctx, fromVM) -}) -``` - -To learn more about configuring migration scripts for your modules, see the [Module Upgrade Guide](../building-modules/13-upgrade.md). - -### Order Of Migrations - -By default, all migrations are run in module name alphabetical ascending order, except `x/auth` which is run last. The reason is state dependencies between x/auth and other modules (you can read more in [issue #10606](https://github.com/cosmos/cosmos-sdk/issues/10606)). - -If you want to change the order of migration, then you should call `app.mm.SetOrderMigrations(module1, module2, ...)` in your app.go file. The function will panic if you forget to include a module in the argument list. - -## Adding New Modules During Upgrades - -You can introduce entirely new modules to the application during an upgrade. New modules are recognized because they have not yet been registered in `x/upgrade`'s `VersionMap` store. In this case, `RunMigrations` calls the `InitGenesis` function from the corresponding module to set up its initial state. - -### Add StoreUpgrades for New Modules - -All chains preparing to run in-place store migrations will need to manually add store upgrades for new modules and then configure the store loader to apply those upgrades. This ensures that the new module's stores are added to the multistore before the migrations begin. - -```go -upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() -if err != nil { - panic(err) -} - -if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { - storeUpgrades := storetypes.StoreUpgrades{ - // add store upgrades for new modules - // Example: - // Added: []string{"foo", "bar"}, - // ... - } - - // configure store loader that checks if version == upgradeHeight and applies store upgrades - app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) -} -``` - -## Genesis State - -When starting a new chain, the consensus version of each module MUST be saved to state during the application's genesis. To save the consensus version, add the following line to the `InitChainer` method in `app.go`: - -```diff -func (app *MyApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - ... -+ app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) - ... -} -``` - -This information is used by the Cosmos SDK to detect when modules with newer versions are introduced to the app. - -For a new module `foo`, `InitGenesis` is called by `RunMigration` only when `foo` is registered in the module manager but it's not set in the `fromVM`. Therefore, if you want to skip `InitGenesis` when a new module is added to the app, then you should set its module version in `fromVM` to the module consensus version: - -```go -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - // ... - - // Set foo's version to the latest ConsensusVersion in the VersionMap. - // This will skip running InitGenesis on Foo - fromVM[foo.ModuleName] = foo.AppModule{}.ConsensusVersion() - - return app.mm.RunMigrations(ctx, fromVM) -}) -``` - -### Overwriting Genesis Functions - -The Cosmos SDK offers modules that the application developer can import in their app. These modules often have an `InitGenesis` function already defined. - -You can write your own `InitGenesis` function for an imported module. To do this, manually trigger your custom genesis function in the upgrade handler. - -::: warning -You MUST manually set the consensus version in the version map passed to the `UpgradeHandler` function. Without this, the SDK will run the Module's existing `InitGenesis` code even if you triggered your custom function in the `UpgradeHandler`. -::: - -```go -import foo "github.com/my/module/foo" - -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - - // Register the consensus version in the version map - // to avoid the SDK from triggering the default - // InitGenesis function. - fromVM["foo"] = foo.AppModule{}.ConsensusVersion() - - // Run custom InitGenesis for foo - app.mm["foo"].InitGenesis(ctx, app.appCodec, myCustomGenesisState) - - return app.mm.RunMigrations(ctx, cfg, fromVM) -}) -``` - -## Syncing a Full Node to an Upgraded Blockchain - -You can sync a full node to an existing blockchain which has been upgraded using Cosmovisor - -To successfully sync, you must start with the initial binary that the blockchain started with at genesis. If all Software Upgrade Plans contain binary instruction, then you can run Cosmovisor with auto-download option to automatically handle downloading and switching to the binaries associated with each sequential upgrade. Otherwise, you need to manually provide all binaries to Cosmovisor. - -To learn more about Cosmovisor, see the [Cosmovisor Quick Start](../run-node/cosmovisor.md). diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/_category_.json b/versioned_docs/version-0.46/develop/advanced-concepts/_category_.json deleted file mode 100644 index b2ac426fb..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Advanced Concepts", - "position": 2, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-begin_block.png b/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-begin_block.png deleted file mode 100644 index 745d4a5a971292bb0346c35893b42ebfbcdc206e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20565 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 HU84U7#M|-t diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-checktx.png b/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-checktx.png deleted file mode 100644 index 38b217acdd04fb2430a2332946864de04474ae5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82308 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 N1zA-Y;BI{L{{e+~Yq9_U diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-deliver_tx.png b/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-deliver_tx.png deleted file mode 100644 index f0a54b4ec34bbe282ed6eff81369428d02dec095..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59007 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 E2iU8KRR910 diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-initchain.png b/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state-initchain.png deleted file mode 100644 index d03e88ef2ae0745aaedc52cf72863080388d7e9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32800 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 R0ton{s-*d-Lcucl{{yUICl~+# diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state_types.png b/versioned_docs/version-0.46/develop/advanced-concepts/baseapp_state_types.png deleted file mode 100644 index a7f91a6093e0099f409101276833e80bdc223f78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133747 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 T?XrE0gO|T|pjV|w#G(HHz@)&x diff --git a/versioned_docs/version-0.46/develop/advanced-concepts/keeper_dependencies.svg b/versioned_docs/version-0.46/develop/advanced-concepts/keeper_dependencies.svg deleted file mode 100644 index bac9328e3..000000000 --- a/versioned_docs/version-0.46/develop/advanced-concepts/keeper_dependencies.svg +++ /dev/null @@ -1,102 +0,0 @@ -The dependencies between Keepers (Feb 2021)StakingDistributionSlashingEvidenceBankAuth/AccountGovMint \ No newline at end of file diff --git a/versioned_docs/version-0.46/develop/high-level-concepts/00-overview-app.md b/versioned_docs/version-0.46/develop/high-level-concepts/00-overview-app.md deleted file mode 100644 index 65037592b..000000000 --- a/versioned_docs/version-0.46/develop/high-level-concepts/00-overview-app.md +++ /dev/null @@ -1,252 +0,0 @@ -# Anatomy of a Cosmos SDK Application - -This document describes the core parts of a Cosmos SDK application. Throughout the document, a placeholder application named `app` will be used. {synopsis} - -## Node Client - -The Daemon, or [Full-Node Client](../advanced-concepts/04-node.md), is the core process of a Cosmos SDK-based blockchain. Participants in the network run this process to initialize their state-machine, connect with other full-nodes and update their state-machine as new blocks come in. - -```text - ^ +-------------------------------+ ^ - | | | | - | | State-machine = Application | | - | | | | Built with Cosmos SDK - | | ^ + | | - | +----------- | ABCI | ----------+ v - | | + v | ^ - | | | | -Blockchain Node | | Consensus | | - | | | | - | +-------------------------------+ | Tendermint Core - | | | | - | | Networking | | - | | | | - v +-------------------------------+ v -``` - -The blockchain full-node presents itself as a binary, generally suffixed by `-d` for "daemon" (e.g. `appd` for `app` or `gaiad` for `gaia`). This binary is built by running a simple [`main.go`](../advanced-concepts/04-node.md#main-function) function placed in `./cmd/appd/`. This operation usually happens through the [Makefile](#dependencies-and-makefile). - -Once the main binary is built, the node can be started by running the [`start` command](../advanced-concepts/04-node.md#start-command). This command function primarily does three things: - -1. Create an instance of the state-machine defined in [`app.go`](#core-application-file). -2. Initialize the state-machine with the latest known state, extracted from the `db` stored in the `~/.app/data` folder. At this point, the state-machine is at height `appBlockHeight`. -3. Create and start a new Tendermint instance. Among other things, the node will perform a handshake with its peers. It will get the latest `blockHeight` from them, and replay blocks to sync to this height if it is greater than the local `appBlockHeight`. If `appBlockHeight` is `0`, the node is starting from genesis and Tendermint sends an `InitChain` message via the ABCI to the `app`, which triggers the [`InitChainer`](#initchainer). - -## Core Application File - -In general, the core of the state-machine is defined in a file called `app.go`. It mainly contains the **type definition of the application** and functions to **create and initialize it**. - -### Type Definition of the Application - -The first thing defined in `app.go` is the `type` of the application. It is generally comprised of the following parts: - -* **A reference to [`baseapp`](../../develop/advanced-concepts/00-baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://docs.tendermint.com/master/spec/abci/abci.html) and the [routing logic](../../develop/advanced-concepts/00-baseapp.md#routing). -* **A list of store keys**. The [store](../advanced-concepts/04-store.md), which contains the entire state, is implemented as a [`multistore`](../advanced-concepts/04-store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../advanced-concepts/ocap.md) of the Cosmos SDK. -* **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/06-keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions. -* **A reference to an [`appCodec`](../advanced-concepts/05-encoding.md).** The application's `appCodec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The default codec is [Protocol Buffers](../advanced-concepts/05-encoding.md). -* **A reference to a [`legacyAmino`](../advanced-concepts/05-encoding.md) codec.** Some parts of the Cosmos SDK have not been migrated to use the `appCodec` above, and are still hardcoded to use Amino. Other parts explicity use Amino for backwards compatibility. For these reasons, the application still holds a reference to the legacy Amino codec. Please note that the Amino codec will be removed from the SDK in the upcoming releases. -* **A reference to a [module manager](../building-modules/01-module-manager.md#manager)** and a [basic module manager](../building-modules/01-module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering their [`Msg` service](../../develop/advanced-concepts/00-baseapp.md#msg-services) and [gRPC `Query` service](../../develop/advanced-concepts/00-baseapp.md#grpc-query-services), or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker). - -See an example of application type definition from `simapp`, the Cosmos SDK's own app used for demo and testing purposes: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L151-L193 - -### Constructor Function - -This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../advanced-concepts/04-node.md#start-command) of the application's daemon command. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/server/types/app.go#L57-L59 - -Here are the main actions performed by this function: - -* Instantiate a new [`codec`](../advanced-concepts/05-encoding.md) and initialize the `codec` of each of the application's module using the [basic manager](../building-modules/01-module-manager.md#basicmanager) -* Instantiate a new application with a reference to a `baseapp` instance, a codec and all the appropriate store keys. -* Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`. -* Instantiate the application's [module manager](../../integrate/building-modules/01-module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules. -* With the module manager, initialize the application's [`Msg` services](../../develop/advanced-concepts/00-baseapp.md#msg-services), [gRPC `Query` services](../../develop/advanced-concepts/00-baseapp.md#grpc-query-services), [legacy `Msg` routes](../../develop/advanced-concepts/00-baseapp.md#routing) and [legacy query routes](../../develop/advanced-concepts/00-baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`Msg` service](#msg-services) using the routes defined here. Likewise, when a gRPC query request is received by the application, it is routed to the appropriate module's [`gRPC query service`](#grpc-query-services) using the gRPC routes defined here. The Cosmos SDK still supports legacy `Msg`s and legacy Tendermint queries, which are routed using respectively the legacy `Msg` routes and the legacy query routes. -* With the module manager, register the [application's modules' invariants](../../integrate/building-modules/07-invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../../integratebuilding-modules/07-invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix. -* With the module manager, set the order of execution between the `InitGenesis`, `BeginBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions. -* Set the remainder of application's parameters: - * [`InitChainer`](#initchainer): used to initialize the application when it is first started. - * [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block). - * [`anteHandler`](../../develop/advanced-concepts/00-baseapp.md#antehandler): used to handle fees and signature verification. -* Mount the stores. -* Return the application. - -Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.app/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time. - -See an example of application constructor from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L204-L474 - -### InitChainer - -The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method. - -In general, the `InitChainer` is mostly composed of the [`InitGenesis`](../../integrate/building-modules/08-genesis.md#initgenesis) function of each of the application's modules. This is done by calling the `InitGenesis` function of the module manager, which in turn will call the `InitGenesis` function of each of the modules it contains. Note that the order in which the modules' `InitGenesis` functions must be called has to be set in the module manager using the [module manager's](../../integrate/building-modules/01-module-manager.md) `SetOrderInitGenesis` method. This is done in the [application's constructor](#application-constructor), and the `SetOrderInitGenesis` has to be called before the `SetInitChainer`. - -See an example of an `InitChainer` from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L524-L532 - -### BeginBlocker and EndBlocker - -The Cosmos SDK offers developers the possibility to implement automatic execution of code as part of their application. This is implemented through two function called `BeginBlocker` and `EndBlocker`. They are called when the application receives respectively the `BeginBlock` and `EndBlock` messages from the Tendermint engine, which happens at the beginning and at the end of each block. The application must set the `BeginBlocker` and `EndBlocker` in its [constructor](#constructor-function) via the [`SetBeginBlocker`](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetBeginBlocker) and [`SetEndBlocker`](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetEndBlocker) methods. - -In general, the `BeginBlocker` and `EndBlocker` functions are mostly composed of the [`BeginBlock` and `EndBlock`](../../integratebuilding-modules/beginblock-endblock.md) functions of each of the application's modules. This is done by calling the `BeginBlock` and `EndBlock` functions of the module manager, which in turn will call the `BeginBlock` and `EndBlock` functions of each of the modules it contains. Note that the order in which the modules' `BeginBlock` and `EndBlock` functions must be called has to be set in the module manager using the `SetOrderBeginBlockers` and `SetOrderEndBlockers` methods respectively. This is done via the [module manager](../../integrate/building-modules/01-module-manager.md) in the [application's constructor](#application-constructor), and the `SetOrderBeginBlockers` and `SetOrderEndBlockers` methods have to be called before the `SetBeginBlocker` and `SetEndBlocker` functions. - -As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution. - -See an example of `BeginBlocker` and `EndBlocker` functions from `simapp` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L514-L522 - -### Register Codec - -The `EncodingConfig` structure is the last important part of the `app.go` file. The goal of this structure is to define the codecs that will be used throughout the app. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/params/encoding.go#L9-L16 - -Here are descriptions of what each of the four fields means: - -* `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces that are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct that contains a `type_url` (name of a concrete type implementing the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method that can be used to register the module's own interfaces and implementations. - * You can read more about Any in [ADR-19](../architecture/adr-019-protobuf-state-encoding.md#usage-of-any-to-encode-interfaces). - * To go more into details, the Cosmos SDK uses an implementation of the Protobuf specification called [`gogoprotobuf`](https://github.com/gogo/protobuf). By default, the [gogo protobuf implementation of `Any`](https://pkg.go.dev/github.com/gogo/protobuf/types) uses [global type registration](https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) to decode values packed in `Any` into concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could register a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the `type_url` field. For more information, please refer to [ADR-019](../../integrate/architecture/adr-019-protobuf-state-encoding.md). -* `Codec`: the default codec used throughout the Cosmos SDK. It is composed of a `BinaryCodec` used to encode and decode state, and a `JSONCodec` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Codec`. -* `TxConfig`: `TxConfig` defines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types: `SIGN_MODE_DIRECT` (which uses Protobuf binary as over-the-wire encoding) and `SIGN_MODE_LEGACY_AMINO_JSON` (which depends on Amino). Read more about transactions [here](../advanced-concepts/01-transactions.md). -* `Amino`: Some legacy parts of the Cosmos SDK still use Amino for backwards-compatibility. Each module exposes a `RegisterLegacyAmino` method to register the module's specific types within Amino. This `Amino` codec should not be used by app developers anymore, and will be removed in future releases. - -The Cosmos SDK exposes a `MakeTestEncodingConfig` function used to create a `EncodingConfig` for the app constructor (`NewApp`). It uses Protobuf as a default `Codec`. - -NOTE: This function is deprecated and should only be used to create an app or in tests. - -See an example of a `MakeTestEncodingConfig` from `simapp`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/encoding.go#L8-L19 - -## Modules - -[Modules](../building-modules/intro.md) are the heart and soul of Cosmos SDK applications. They can be considered as state-machines nested within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../../develop/advanced-concepts/00-baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. **For developers, most of the work involved in building a Cosmos SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application**. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the Cosmos SDK's `x/` folder, which contains already-built modules). - -### Application Module Interface - -Modules must implement [interfaces](../../integratebuilding-modules/01-module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../../integrate/building-modules/01-module-manager.md#appmodulebasic) and [`AppModule`](../../integrate/building-modules/01-module-manager.md#appmodule). The former implements basic non-dependent elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are, by convention, defined in a file called `module.go`. - -`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are called from the [`module manager`](../building-modules/01-module-manager.md#manager), which manages the application's collection of modules. - -### `Msg` Services - -Each module defines two [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services): one `Msg` service to handle messages, and one gRPC `Query` service to handle queries. If we consider the module as a state-machine, then a `Msg` service is a set of state transition RPC methods. -Each Protobuf `Msg` service method is 1:1 related to a Protobuf request type, which must implement `sdk.Msg` interface. -Note that `sdk.Msg`s are bundled in [transactions](../high-level-concepts/01-tx-lifecycle.md), and each transaction contains one or multiple messages. - -When a valid block of transactions is received by the full-node, Tendermint relays each one to the application via [`DeliverTx`](https://docs.tendermint.com/master/spec/abci/apps.html#delivertx). Then, the application handles the transaction: - -1. Upon receiving the transaction, the application first unmarshalls it from `[]byte`. -2. Then, it verifies a few things about the transaction like [fee payment and signatures](./gas-fees.md#antehandler) before extracting the `Msg`(s) contained in the transaction. -3. `sdk.Msg`s are encoded using Protobuf [`Any`s](#register-codec). By analyzing each `Any`'s `type_url`, baseapp's `msgServiceRouter` routes the `sdk.Msg` to the corresponding module's `Msg` service. -4. If the message is successfully processed, the state is updated. - -For a more details look at a transaction [lifecycle](./01-tx-lifecycle.md). - -Module developers create custom `Msg` services when they build their own module. The general practice is to define the `Msg` Protobuf service in a `tx.proto` file. For example, the `x/bank` module defines a service with two methods to transfer tokens: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L12-L19 - -Service methods use `keeper` in order to update the module state. - -Each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterMsgServer` function provided by the generated Protobuf code. - -### gRPC `Query` Services - -gRPC `Query` services allows users to query the state using [gRPC](https://grpc.io). They are enabled by default, and can be configured under the `grpc.enable` and `grpc.address` fields inside [`app.toml`](../../user/run-node.md#configuring-the-node-using-apptoml). - -gRPC `Query` services are defined in the module's Protobuf definition files, specifically inside `query.proto`. The `query.proto` definition file exposes a single `Query` [Protobuf service](https://developers.google.com/protocol-buffers/docs/proto#services). Each gRPC query endpoint corresponds to a service method, starting with the `rpc` keyword, inside the `Query` service. - -Protobuf generates a `QueryServer` interface for each module, containing all the service methods. A module's [`keeper`](#keeper) then needs to implement this `QueryServer` interface, by providing the concrete implementation of each service method. This concrete implementation is the handler of the corresponding gRPC query endpoint. - -Finally, each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterQueryServer` function provided by the generated Protobuf code. - -### Keeper - -[`Keepers`](../../integratebuilding-modules/06-keeper.md) are the gatekeepers of their module's store(s). To read or write in a module's store, it is mandatory to go through one of its `keeper`'s methods. This is ensured by the [object-capabilities](../advanced-concepts/12-ocap.md) model of the Cosmos SDK. Only objects that hold the key to a store can access it, and only the module's `keeper` should hold the key(s) to the module's store(s). - -`Keepers` are generally defined in a file called `keeper.go`. It contains the `keeper`'s type definition and methods. - -The `keeper` type definition generally consists of: - -* **Key(s)** to the module's store(s) in the multistore. -* Reference to **other module's `keepers`**. Only needed if the `keeper` needs to access other module's store(s) (either to read or write from them). -* A reference to the application's **codec**. The `keeper` needs it to marshal structs before storing them, or to unmarshal them when it retrieves them, because stores only accept `[]bytes` as value. - -Along with the type definition, the next important component of the `keeper.go` file is the `keeper`'s constructor function, `NewKeeper`. This function instantiates a new `keeper` of the type defined above, with a `codec`, store `keys` and potentially references to other modules' `keeper`s as parameters. The `NewKeeper` function is called from the [application's constructor](#constructor-function). The rest of the file defines the `keeper`'s methods, primarily getters and setters. - -### Command-Line, gRPC Services and REST Interfaces - -Each module defines command-line commands, gRPC services and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module. - -#### CLI - -Generally, the [commands related to a module](../building-modules/09-module-interfaces.md#cli) are defined in a folder called `client/cli` in the module's folder. The CLI divides commands in two category, transactions and queries, defined in `client/cli/tx.go` and `client/cli/query.go` respectively. Both commands are built on top of the [Cobra Library](https://github.com/spf13/cobra): - -* Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The Cosmos SDK handles signing and the addition of other transaction metadata. -* Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../../develop/advanced-concepts/00-baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied. - -#### gRPC - -[gRPC](https://grpc.io) is a modern open-source high performance RPC framework that has support in multiple languages. It is the recommended way for external clients (such as wallets, browsers and other backend services) to interact with a node. - -Each module can expose gRPC endpoints, called [service methods](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition) and are defined in the [module's Protobuf `query.proto` file](#grpc-query-services). A service method is defined by its name, input arguments and output response. The module then needs to: - -* define a `RegisterGRPCGatewayRoutes` method on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. -* for each service method, define a corresponding handler. The handler implements the core logic necessary to serve the gRPC request, and is located in the `keeper/grpc_query.go` file. - -#### gRPC-gateway REST Endpoints - -Some external clients may not wish to use gRPC. The Cosmos SDK provides in this case a gRPC gateway service, which exposes each gRPC service as a correspoding REST endpoint. Please refer to the [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) documentation to learn more. - -The REST endpoints are defined in the Protobuf files, along with the gRPC services, using Protobuf annotations. Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods. By default, all REST endpoints defined in the SDK have an URL starting with the `/cosmos/` prefix. - -The Cosmos SDK also provides a development endpoint to generate [Swagger](https://swagger.io/) definition files for these REST endpoints. This endpoint can be enabled inside the [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file, under the `api.swagger` key. - -## Application Interface - -[Interfaces](#command-line-grpc-services-and-rest-interfaces) let end-users interact with full-node clients. This means querying data from the full-node or creating and sending new transactions to be relayed by the full-node and eventually included in a block. - -The main interface is the [Command-Line Interface](../01-tx-lifecycle.md06-cli.md). The CLI of a Cosmos SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application is the same as the daemon (e.g. `appd`), and defined in a file called `appd/main.go`. The file contains: - -* **A `main()` function**, which is executed to build the `appd` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appd`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`. -* **Query commands** are added by calling the `queryCmd` function. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appd query [query]` of the CLI. -* **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appd tx [tx]` of the CLI. - -See an example of an application's main command-line file from the [Cosmos Hub](https://github.com/cosmos/gaia) - -+++ https://github.com/cosmos/gaia/blob/Theta-main/cmd/gaiad/cmd/root.go#L39-L77 - -## Dependencies and Makefile - -::: warning -A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the Cosmos SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. - -To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: - -```go -replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 -``` - -Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. -::: - -This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. - -Below, the `go.mod` of the [Cosmos Hub](https://github.com/cosmos/gaia) is provided as an example. - -+++ https://github.com/cosmos/gaia/blob/Theta-main/go.mod#L1-L20 - -For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appd`](#application-interface). - -Here is an example of the [Cosmos Hub Makefile](https://github.com/cosmos/gaia/blob/Theta-main/Makefile). - -## Next {hide} - -Learn more about the [Lifecycle of a transaction](./01-tx-lifecycle.md) {hide} diff --git a/versioned_docs/version-0.46/develop/high-level-concepts/01-tx-lifecycle.md b/versioned_docs/version-0.46/develop/high-level-concepts/01-tx-lifecycle.md deleted file mode 100644 index 6a4f040a6..000000000 --- a/versioned_docs/version-0.46/develop/high-level-concepts/01-tx-lifecycle.md +++ /dev/null @@ -1,253 +0,0 @@ -# Transaction Lifecycle - -This document describes the lifecycle of a transaction from creation to committed state changes. Transaction definition is described in a [different doc](../advanced-concepts/01-transactions.md). The transaction will be referred to as `Tx`. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK Application](./app-anatomy.md) {prereq} - -## Creation - -### Transaction Creation - -One of the main application interfaces is the command-line interface. The transaction `Tx` can be created by the user inputting a command in the following format from the [command-line](../advanced-concepts/06-cli.md), providing the type of transaction in `[command]`, arguments in `[args]`, and configurations such as gas prices in `[flags]`: - -```bash -[appname] tx [command] [args] [flags] -``` - -This command will automatically **create** the transaction, **sign** it using the account's private key, and **broadcast** it to the specified peer node. - -There are several required and optional flags for transaction creation. The `--from` flag specifies which [account](./03-accounts.md) the transaction is originating from. For example, if the transaction is sending coins, the funds will be drawn from the specified `from` address. - -#### Gas and Fees - -Additionally, there are several [flags](../advanced-concepts/06-cli.md) users can use to indicate how much they are willing to pay in [fees](./gas-fees.md): - -* `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`. -* `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. -* `--gas-prices` specifies how much the user is willing to pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. -* `--fees` specifies how much in fees the user is willing to pay in total. -* `--timeout-height` specifies a block timeout height to prevent the tx from being committed past a certain height. - -The ultimate value of the fees paid is equal to the gas multiplied by the gas prices. In other words, `fees = ceil(gas * gasPrices)`. Thus, since fees can be calculated using gas prices and vice versa, the users specify only one of the two. - -Later, validators decide whether or not to include the transaction in their block by comparing the given or calculated `gas-prices` to their local `min-gas-prices`. `Tx` will be rejected if its `gas-prices` is not high enough, so users are incentivized to pay more. - -#### CLI Example - -Users of the application `app` can enter the following command into their CLI to generate a transaction to send 1000uatom from a `senderAddress` to a `recipientAddress`. It specifies how much gas they are willing to pay: an automatic estimate scaled up by 1.5 times, with a gas price of 0.025uatom per unit gas. - -```bash -appd tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom -``` - -#### Other Transaction Creation Methods - -The command-line is an easy way to interact with an application, but `Tx` can also be created using a [gRPC or REST interface](../advanced-concepts/08-grpc_rest.md) or some other entry point defined by the application developer. From the user's perspective, the interaction depends on the web interface or wallet they are using (e.g. creating `Tx` using [Lunie.io](https://lunie.io/#/) and signing it with a Ledger Nano S). - -## Addition to Mempool - -Each full-node (running Tendermint) that receives a `Tx` sends an [ABCI message](https://docs.tendermint.com/master/spec/abci/abci.html#messages), -`CheckTx`, to the application layer to check for validity, and receives an `abci.ResponseCheckTx`. If the `Tx` passes the checks, it is held in the nodes' -[**Mempool**](https://docs.tendermint.com/master/tendermint-core/mempool/), an in-memory pool of transactions unique to each node, pending inclusion in a block - honest nodes will discard `Tx` if it is found to be invalid. Prior to consensus, nodes continuously check incoming transactions and gossip them to their peers. - -### Types of Checks - -The full-nodes perform stateless, then stateful checks on `Tx` during `CheckTx`, with the goal to -identify and reject an invalid transaction as early on as possible to avoid wasted computation. - -**_Stateless_** checks do not require nodes to access state - light clients or offline nodes can do -them - and are thus less computationally expensive. Stateless checks include making sure addresses -are not empty, enforcing nonnegative numbers, and other logic specified in the definitions. - -**_Stateful_** checks validate transactions and messages based on a committed state. Examples -include checking that the relevant values exist and can be transacted with, the address -has sufficient funds, and the sender is authorized or has the correct ownership to transact. -At any given moment, full-nodes typically have [multiple versions](../../develop/advanced-concepts/00-baseapp.md#state-updates) -of the application's internal state for different purposes. For example, nodes will execute state -changes while in the process of verifying transactions, but still need a copy of the last committed -state in order to answer queries - they should not respond using state with uncommitted changes. - -In order to verify a `Tx`, full-nodes call `CheckTx`, which includes both _stateless_ and _stateful_ -checks. Further validation happens later in the [`DeliverTx`](#delivertx) stage. `CheckTx` goes -through several steps, beginning with decoding `Tx`. - -### Decoding - -When `Tx` is received by the application from the underlying consensus engine (e.g. Tendermint), it is still in its [encoded](../advanced-concepts/05-encoding.md) `[]byte` form and needs to be unmarshaled in order to be processed. Then, the [`runTx`](../../develop/advanced-concepts/00-baseapp.md#runtx-antehandler-runmsgs-posthandler) function is called to run in `runTxModeCheck` mode, meaning the function will run all checks but exit before executing messages and writing state changes. - -### ValidateBasic - -Messages ([`sdk.Msg`](../advanced-concepts/01-transactions.md#messages)) are extracted from transactions (`Tx`). The `ValidateBasic` method of the `sdk.Msg` interface implemented by the module developer is run for each transaction. -To discard obviously invalid messages, the `BaseApp` type calls the `ValidateBasic` method very early in the processing of the message in the [`CheckTx`](../../develop/advanced-concepts/00-baseapp.md#checktx) and [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx) transactions. -`ValidateBasic` can include only **stateless** checks (the checks that do not require access to the state). - -#### Guideline - -Gas is not charged when `ValidateBasic` is executed, so we recommend only performing all necessary stateless checks to enable middleware operations (for example, parsing the required signer accounts to validate a signature by a middleware) and stateless sanity checks not impacting performance of the CheckTx phase. -Other validation operations must be performed when [handling a message](../building-modules/msg-services#Validation) in a module Msg Server. - -Example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for non-empty addresses and a non-negative coin amount, but does not require knowledge of state such as the account balance of an address. - -See also [Msg Service Validation](../building-modules/03-msg-services.md#Validation). - -### AnteHandler - -After the ValidateBasic checks, the `AnteHandler`s are run. Technically, they are optional, but in practice, they are very often present to perform signature verification, gas calculation, fee deduction and other core operations related to blockchain transactions. - -A copy of the cached context is provided to the `AnteHandler`, which performs limited checks specified for the transaction type. Using a copy allows the `AnteHandler` to do stateful checks for `Tx` without modifying the last committed state, and revert back to the original if the execution fails. - -For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth/spec) module `AnteHandler` checks and increments sequence numbers, checks signatures and account numbers, and deducts fees from the first signer of the transaction - all state changes are made using the `checkState`. - -### Gas - -The [`Context`](../advanced-concepts/02-context.md), which keeps a `GasMeter` that will track how much gas has been used during the execution of `Tx`, is initialized. The user-provided amount of gas for `Tx` is known as `GasWanted`. If `GasConsumed`, the amount of gas consumed so during execution, ever exceeds `GasWanted`, the execution will stop and the changes made to the cached copy of the state won't be committed. Otherwise, `CheckTx` sets `GasUsed` equal to `GasConsumed` and returns it in the result. After calculating the gas and fee values, validator-nodes check that the user-specified `gas-prices` is greater than their locally defined `min-gas-prices`. - -### Discard or Addition to Mempool - -If at any point during `CheckTx` the `Tx` fails, it is discarded and the transaction lifecycle ends -there. Otherwise, if it passes `CheckTx` successfully, the default protocol is to relay it to peer -nodes and add it to the Mempool so that the `Tx` becomes a candidate to be included in the next block. - -The **mempool** serves the purpose of keeping track of transactions seen by all full-nodes. -Full-nodes keep a **mempool cache** of the last `mempool.cache_size` transactions they have seen, as a first line of -defense to prevent replay attacks. Ideally, `mempool.cache_size` is large enough to encompass all -of the transactions in the full mempool. If the mempool cache is too small to keep track of all -the transactions, `CheckTx` is responsible for identifying and rejecting replayed transactions. - -Currently existing preventative measures include fees and a `sequence` (nonce) counter to distinguish -replayed transactions from identical but valid ones. If an attacker tries to spam nodes with many -copies of a `Tx`, full-nodes keeping a mempool cache will reject identical copies instead of running -`CheckTx` on all of them. Even if the copies have incremented `sequence` numbers, attackers are -disincentivized by the need to pay fees. - -Validator nodes keep a mempool to prevent replay attacks, just as full-nodes do, but also use it as -a pool of unconfirmed transactions in preparation of block inclusion. Note that even if a `Tx` -passes all checks at this stage, it is still possible to be found invalid later on, because -`CheckTx` does not fully validate the transaction (i.e. it does not actually execute the messages). - -## Inclusion in a Block - -Consensus, the process through which validator nodes come to agreement on which transactions to -accept, happens in **rounds**. Each round begins with a proposer creating a block of the most -recent transactions and ends with **validators**, special full-nodes with voting power responsible -for consensus, agreeing to accept the block or go with a `nil` block instead. Validator nodes -execute the consensus algorithm, such as [Tendermint BFT](https://docs.tendermint.com/master/spec/consensus/consensus.html#terms), -confirming the transactions using ABCI requests to the application, in order to come to this agreement. - -The first step of consensus is the **block proposal**. One proposer amongst the validators is chosen -by the consensus algorithm to create and propose a block - in order for a `Tx` to be included, it -must be in this proposer's mempool. - -## State Changes - -The next step of consensus is to execute the transactions to fully validate them. All full-nodes -that receive a block proposal from the correct proposer execute the transactions by calling the ABCI functions -[`BeginBlock`](./app-anatomy.md#beginblocker-and-endblocker), `DeliverTx` for each transaction, -and [`EndBlock`](./app-anatomy.md#beginblocker-and-endblocker). While each full-node runs everything -locally, this process yields a single, unambiguous result, since the messages' state transitions are deterministic and transactions are -explicitly ordered in the block proposal. - -```text - ----------------------- - |Receive Block Proposal| - ----------------------- - | - v - ----------------------- - | BeginBlock | - ----------------------- - | - v - ----------------------- - | DeliverTx(tx0) | - | DeliverTx(tx1) | - | DeliverTx(tx2) | - | DeliverTx(tx3) | - | . | - | . | - | . | - ----------------------- - | - v - ----------------------- - | EndBlock | - ----------------------- - | - v - ----------------------- - | Consensus | - ----------------------- - | - v - ----------------------- - | Commit | - ----------------------- -``` - -### DeliverTx - -The `DeliverTx` ABCI function defined in [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md) does the bulk of the -state transitions: it is run for each transaction in the block in sequential order as committed -to during consensus. Under the hood, `DeliverTx` is almost identical to `CheckTx` but calls the -[`runTx`](../../develop/advanced-concepts/00-baseapp.md#runtx) function in deliver mode instead of check mode. -Instead of using their `checkState`, full-nodes use `deliverState`: - -* **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form. - Nodes first unmarshal the transaction, using the [`TxConfig`](./app-anatomy#register-codec) defined in the app, then call `runTx` in `runTxModeDeliver`, which is very similar to `CheckTx` but also executes and writes state changes. - -* **Checks and AnteHandler:** Full-nodes call `validateBasicMsgs` and `AnteHandler` again. This second check - happens because they may not have seen the same transactions during the addition to Mempool stage - and a malicious proposer may have included invalid ones. One difference here is that the - `AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local - to each node - differing values across nodes would yield nondeterministic results. - -* **`MsgServiceRouter`:** While `CheckTx` would have exited, `DeliverTx` continues to run - [`runMsgs`](../../develop/advanced-concepts/00-baseapp.md#runtx-antehandler-runmsgs-posthandler) to fully execute each `Msg` within the transaction. - Since the transaction may have messages from different modules, `BaseApp` needs to know which module - to find the appropriate handler. This is achieved using `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's Protobuf [`Msg` service](../building-modules/03-msg-services.md). - For `LegacyMsg` routing, the `Route` function is called via the [module manager](../building-modules/01-module-manager.md) to retrieve the route name and find the legacy [`Handler`](../building-modules/03-msg-services.md#handler-type) within the module. - -* **`Msg` service:** Protobuf `Msg` service is responsible for executing each message in the `Tx` and causes state transitions to persist in `deliverTxState`. - -* **PostHandlers:** [`PostHandler`](../../develop/advanced-concepts/00-baseapp.md#posthandler)s run after the execution of the message. If they fail, the state change of `runMsgs`, as well of `PostHandlers` are both reverted. - -* **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much - gas is being used; if execution completes, `GasUsed` is set and returned in the - `abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes - wrong, a deferred function at the end appropriately errors or panics. - -If there are any failed state changes resulting from a `Tx` being invalid or `GasMeter` running out, -the transaction processing terminates and any state changes are reverted. Invalid transactions in a -block proposal cause validator nodes to reject the block and vote for a `nil` block instead. - -### Commit - -The final step is for nodes to commit the block and state changes. Validator nodes -perform the previous step of executing state transitions in order to validate the transactions, -then sign the block to confirm it. Full nodes that are not validators do not -participate in consensus - i.e. they cannot vote - but listen for votes to understand whether or -not they should commit the state changes. - -When they receive enough validator votes (2/3+ _precommits_ weighted by voting power), full nodes commit to a new block to be added to the blockchain and -finalize the state transitions in the application layer. A new state root is generated to serve as -a merkle proof for the state transitions. Applications use the [`Commit`](../../develop/advanced-concepts/00-baseapp.md#commit) -ABCI method inherited from [Baseapp](../../develop/advanced-concepts/00-baseapp.md); it syncs all the state transitions by -writing the `deliverState` into the application's internal state. As soon as the state changes are -committed, `checkState` start afresh from the most recently committed state and `deliverState` -resets to `nil` in order to be consistent and reflect the changes. - -Note that not all blocks have the same number of transactions and it is possible for consensus to -result in a `nil` block or one with none at all. In a public blockchain network, it is also possible -for validators to be **byzantine**, or malicious, which may prevent a `Tx` from being committed in -the blockchain. Possible malicious behaviors include the proposer deciding to censor a `Tx` by -excluding it from the block or a validator voting against the block. - -At this point, the transaction lifecycle of a `Tx` is over: nodes have verified its validity, -delivered it by executing its state changes, and committed those changes. The `Tx` itself, -in `[]byte` form, is stored in a block and appended to the blockchain. - -## Next {hide} - -Learn about [accounts](./03-accounts.md) {hide} diff --git a/versioned_docs/version-0.46/develop/high-level-concepts/02-query-lifecycle.md b/versioned_docs/version-0.46/develop/high-level-concepts/02-query-lifecycle.md deleted file mode 100644 index 757cb47d8..000000000 --- a/versioned_docs/version-0.46/develop/high-level-concepts/02-query-lifecycle.md +++ /dev/null @@ -1,134 +0,0 @@ -# Query Lifecycle - -This document describes the lifecycle of a query in a Cosmos SDK application, from the user interface to application stores and back. {synopsis} - -## Pre-requisite Readings - -* [Transaction Lifecycle](./01-tx-lifecycle.md) {prereq} - -## Query Creation - -A [**query**](../building-modules/02-messages-and-queries.md#queries) is a request for information made by end-users of applications through an interface and processed by a full-node. Users can query information about the network, the application itself, and application state directly from the application's stores or modules. Note that queries are different from [transactions](../advanced-concepts/01-transactions.md) (view the lifecycle [here](./01-tx-lifecycle.md)), particularly in that they do not require consensus to be processed (as they do not trigger state-transitions); they can be fully handled by one full-node. - -For the purpose of explaining the query lifecycle, let's say `MyQuery` is requesting a list of delegations made by a certain delegator address in the application called `simapp`. As to be expected, the [`staking`](../../x/staking/spec/README.md) module handles this query. But first, there are a few ways `MyQuery` can be created by users. - -### CLI - -The main interface for an application is the command-line interface. Users connect to a full-node and run the CLI directly from their machines - the CLI interacts directly with the full-node. To create `MyQuery` from their terminal, users type the following command: - -```bash -simd query staking delegations -``` - -This query command was defined by the [`staking`](../../x/staking/spec/README.md) module developer and added to the list of subcommands by the application developer when creating the CLI. - -Note that the general format is as follows: - -```bash -simd query [moduleName] [command] --flag -``` - -To provide values such as `--node` (the full-node the CLI connects to), the user can use the [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file to set them or provide them as flags. - -The CLI understands a specific set of commands, defined in a hierarchical structure by the application developer: from the [root command](../advanced-concepts/06-cli.md#root-command) (`simd`), the type of command (`Myquery`), the module that contains the command (`staking`), and command itself (`delegations`). Thus, the CLI knows exactly which module handles this command and directly passes the call there. - -Another interface through which users can make queries is [gRPC](https://grpc.io) requests to a [gRPC server](../advanced-concepts/08-grpc_rest.md#grpc-server). The endpoints are defined as [Protocol Buffers](https://developers.google.com/protocol-buffers) service methods inside `.proto` files, written in Protobuf's own language-agnostic interface definition language (IDL). The Protobuf ecosystem developed tools for code-generation from `*.proto` files into various languages. These tools allow to build gRPC clients easily. - -One such tool is [grpcurl](https://github.com/fullstorydev/grpcurl), and a gRPC request for `MyQuery` using this client looks like: - -```bash -grpcurl \ - -plaintext # We want results in plain test - -import-path ./proto \ # Import these .proto files - -proto ./proto/cosmos/staking/v1beta1/query.proto \ # Look into this .proto file for the Query protobuf service - -d '{"address":"$MY_DELEGATOR"}' \ # Query arguments - localhost:9090 \ # gRPC server endpoint - cosmos.staking.v1beta1.Query/Delegations # Fully-qualified service method name -``` - -### REST - -Another interface through which users can make queries is through HTTP Requests to a [REST server](../advanced-concepts/08-grpc_rest.md#rest-server). The REST server is fully auto-generated from Protobuf services, using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). - -An example HTTP request for `MyQuery` looks like: - -```bash -GET http://localhost:1317/cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations -``` - -## How Queries are Handled by the CLI - -The examples above show how an external user can interact with a node by querying its state. To understand more in details the exact lifecycle of a query, let's dig into how the CLI prepares the query, and how the node handles it. The interactions from the users' perspective are a bit different, but the underlying functions are almost identical because they are implementations of the same command defined by the module developer. This step of processing happens within the CLI, gRPC or REST server and heavily involves a `client.Context`. - -### Context - -The first thing that is created in the execution of a CLI command is a `client.Context`. A `client.Context` is an object that stores all the data needed to process a request on the user side. In particular, a `client.Context` stores the following: - -* **Codec**: The [encoder/decoder](../advanced-concepts/05-encoding.md) used by the application, used to marshal the parameters and query before making the Tendermint RPC request and unmarshal the returned response into a JSON object. The default codec used by the CLI is Protobuf. -* **Account Decoder**: The account decoder from the [`auth`](../../x/auth/spec/README.md) module, which translates `[]byte`s into accounts. -* **RPC Client**: The Tendermint RPC Client, or node, to which the request will be relayed to. -* **Keyring**: A [Key Manager](../high-level-concepts/03-accounts.md#keyring) used to sign transactions and handle other operations with keys. -* **Output Writer**: A [Writer](https://pkg.go.dev/io/#Writer) used to output the response. -* **Configurations**: The flags configured by the user for this command, including `--height`, specifying the height of the blockchain to query and `--indent`, which indicates to add an indent to the JSON response. - -The `client.Context` also contains various functions such as `Query()` which retrieves the RPC Client and makes an ABCI call to relay a query to a full-node. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/context.go#L25-L63 - -The `client.Context`'s primary role is to store data used during interactions with the end-user and provide methods to interact with this data - it is used before and after the query is processed by the full-node. Specifically, in handling `MyQuery`, the `client.Context` is utilized to encode the query parameters, retrieve the full-node, and write the output. Prior to being relayed to a full-node, the query needs to be encoded into a `[]byte` form, as full-nodes are application-agnostic and do not understand specific types. The full-node (RPC Client) itself is retrieved using the `client.Context`, which knows which node the user CLI is connected to. The query is relayed to this full-node to be processed. Finally, the `client.Context` contains a `Writer` to write output when the response is returned. These steps are further described in later sections. - -### Arguments and Route Creation - -At this point in the lifecycle, the user has created a CLI command with all of the data they wish to include in their query. A `client.Context` exists to assist in the rest of the `MyQuery`'s journey. Now, the next step is to parse the command or request, extract the arguments, and encode everything. These steps all happen on the user side within the interface they are interacting with. - -#### Encoding - -In our case (querying an address's delegations), `MyQuery` contains an [address](./03-accounts.md#addresses) `delegatorAddress` as its only argument. However, the request can only contain `[]byte`s, as it will be relayed to a consensus engine (e.g. Tendermint Core) of a full-node that has no inherent knowledge of the application types. Thus, the `codec` of `client.Context` is used to marshal the address. - -Here is what the code looks like for the CLI command: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/client/cli/query.go#L323-L326 - -#### gRPC Query Client Creation - -The Cosmos SDK leverages code generated from Protobuf services to make queries. The `staking` module's `MyQuery` service generates a `queryClient`, which the CLI will use to make queries. Here is the relevant code: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/client/cli/query.go#L317-L341 - -Under the hood, the `client.Context` has a `Query()` function used to retrieve the pre-configured node and relay a query to it; the function takes the query fully-qualified service method name as path (in our case: `/cosmos.staking.v1beta1.Query/Delegations`), and arguments as parameters. It first retrieves the RPC Client (called the [**node**](../advanced-concepts/03-node.md)) configured by the user to relay this query to, and creates the `ABCIQueryOptions` (parameters formatted for the ABCI call). The node is then used to make the ABCI call, `ABCIQueryWithOptions()`. - -Here is what the code looks like: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/query.go#L80-L114 - -## RPC - -With a call to `ABCIQueryWithOptions()`, `MyQuery` is received by a [full-node](../advanced-concepts/05-encoding.md) which will then process the request. Note that, while the RPC is made to the consensus engine (e.g. Tendermint Core) of a full-node, queries are not part of consensus and will not be broadcasted to the rest of the network, as they do not require anything the network needs to agree upon. - -Read more about ABCI Clients and Tendermint RPC in the [Tendermint documentation](https://docs.tendermint.com/master/rpc/). - -## Application Query Handling - -When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../../develop/advanced-concepts/00-baseapp.md) implements the ABCI [`Query()`](../../develop/advanced-concepts/00-baseapp.md#query) function and handles gRPC queries. The query route is parsed, and it matches the fully-qualified service method name of an existing service method (most likely in one of the modules), then `baseapp` will relay the request to the relevant module. - -Apart from gRPC routes, `baseapp` also handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [legacy queriers](../building-modules/04-query-services.md#legacy-queriers). To learn more about these queries, please refer to [this guide](../advanced-concepts/08-grpc_rest.md#tendermint-rpc). - -Since `MyQuery` has a Protobuf fully-qualified service method name from the `staking` module (recall `/cosmos.staking.v1beta1.Query/Delegations`), `baseapp` first parses the path, then uses its own internal `GRPCQueryRouter` to retrieve the corresponding gRPC handler, and routes the query to the module. The gRPC handler is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about query services [here](../building-modules/04-query-services.md). - -Once a result is received from the querier, `baseapp` begins the process of returning a response to the user. - -## Response - -Since `Query()` is an ABCI function, `baseapp` returns the response as an [`abci.ResponseQuery`](https://docs.tendermint.com/master/spec/abci/abci.html#query-2) type. The `client.Context` `Query()` routine receives the response and. - -### CLI Response - -The application [`codec`](../advanced-concepts/05-encoding.md) is used to unmarshal the response to a JSON and the `client.Context` prints the output to the command line, applying any configurations such as the output type (text, JSON or YAML). - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/context.go#L315-L343 - -And that's a wrap! The result of the query is outputted to the console by the CLI. - -## Next {hide} - -Read more about [accounts](./03-accounts.md). {hide} diff --git a/versioned_docs/version-0.46/develop/high-level-concepts/03-accounts.md b/versioned_docs/version-0.46/develop/high-level-concepts/03-accounts.md deleted file mode 100644 index 97b32f914..000000000 --- a/versioned_docs/version-0.46/develop/high-level-concepts/03-accounts.md +++ /dev/null @@ -1,148 +0,0 @@ -# Accounts - -This document describes the in-built account and public key system of the Cosmos SDK. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK Application](./app-anatomy.md) {prereq} - -## Account Definition - -In the Cosmos SDK, an _account_ designates a pair of _public key_ `PubKey` and _private key_ `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/02-messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`. - -For HD key derivation the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). The BIP32 allows users to create an HD wallet (as specified in [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) - a set of accounts derived from an initial secret seed. A seed is usually created from a 12- or 24-word mnemonic. A single seed can derive any number of `PrivKey`s using a one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved. - -```text - Account 0 Account 1 Account 2 - -+------------------+ +------------------+ +------------------+ -| | | | | | -| Address 0 | | Address 1 | | Address 2 | -| ^ | | ^ | | ^ | -| | | | | | | | | -| | | | | | | | | -| | | | | | | | | -| + | | + | | + | -| Public key 0 | | Public key 1 | | Public key 2 | -| ^ | | ^ | | ^ | -| | | | | | | | | -| | | | | | | | | -| | | | | | | | | -| + | | + | | + | -| Private key 0 | | Private key 1 | | Private key 2 | -| ^ | | ^ | | ^ | -+------------------+ +------------------+ +------------------+ - | | | - | | | - | | | - +--------------------------------------------------------------------+ - | - | - +---------+---------+ - | | - | Master PrivKey | - | | - +-------------------+ - | - | - +---------+---------+ - | | - | Mnemonic (Seed) | - | | - +-------------------+ -``` - -In the Cosmos SDK, keys are stored and managed by using an object called a [`Keyring`](#keyring). - -## Keys, accounts, addresses, and signatures - -The principal way of authenticating a user is done using [digital signatures](https://en.wikipedia.org/wiki/Digital_signature). Users sign transactions using their own private key. Signature verification is done with the associated public key. For on-chain signature verification purposes, we store the public key in an `Account` object (alongside other data required for a proper transaction validation). - -In the node, all data is stored using Protocol Buffers serialization. - -The Cosmos SDK supports the following digital key schemes for creating digital signatures: - -* `secp256k1`, as implemented in the [Cosmos SDK's `crypto/keys/secp256k1` package](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/keys/secp256k1/secp256k1.go). -* `secp256r1`, as implemented in the [Cosmos SDK's `crypto/keys/secp256r1` package](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/keys/secp256r1/pubkey.go), -* `tm-ed25519`, as implemented in the [Cosmos SDK `crypto/keys/ed25519` package](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/keys/ed25519/ed25519.go). This scheme is supported only for the consensus validation. - -| | Address length in bytes | Public key length in bytes | Used for transaction authentication | Used for consensus (tendermint) | -| :----------: | :---------------------: | :------------------------: | :---------------------------------: | :-----------------------------: | -| `secp256k1` | 20 | 33 | yes | no | -| `secp256r1` | 32 | 33 | yes | no | -| `tm-ed25519` | -- not used -- | 32 | no | yes | - -## Addresses - -`Addresses` and `PubKey`s are both public information that identifies actors in the application. `Account` is used to store authentication information. The basic account implementation is provided by a `BaseAccount` object. - -Each account is identified using `Address` which is a sequence of bytes derived from a public key. In the Cosmos SDK, we define 3 types of addresses that specify a context where an account is used: - -* `AccAddress` identifies users (the sender of a `message`). -* `ValAddress` identifies validator operators. -* `ConsAddress` identifies validator nodes that are participating in consensus. Validator nodes are derived using the **`ed25519`** curve. - -These types implement the `Address` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/address.go#L108-L125 - -Address construction algorithm is defined in [ADR-28](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-028-public-key-addresses.md). -Here is the standard way to obtain an account address from a `pub` public key: - -```go -sdk.AccAddress(pub.Address().Bytes()) -``` - -Of note, the `Marshal()` and `Bytes()` method both return the same raw `[]byte` form of the address. `Marshal()` is required for Protobuf compatibility. - -For user interaction, addresses are formatted using [Bech32](https://en.bitcoin.it/wiki/Bech32) and implemented by the `String` method. The Bech32 method is the only supported format to use when interacting with a blockchain. The Bech32 human-readable part (Bech32 prefix) is used to denote an address type. Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/address.go#L272-L286 - -| | Address Bech32 Prefix | -| ------------------ | --------------------- | -| Accounts | cosmos | -| Validator Operator | cosmosvaloper | -| Consensus Nodes | cosmosvalcons | - -### Public Keys - -Public keys in Cosmos SDK are defined by `cryptotypes.PubKey` interface. Since public keys are saved in a store, `cryptotypes.PubKey` extends the `proto.Message` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/types/types.go#L8-L17 - -A compressed format is used for `secp256k1` and `secp256r1` serialization. - -* The first byte is a `0x02` byte if the `y`-coordinate is the lexicographically largest of the two associated with the `x`-coordinate. -* Otherwise the first byte is a `0x03`. - -This prefix is followed by the `x`-coordinate. - -Public Keys are not used to reference accounts (or users) and in general are not used when composing transaction messages (with few exceptions: `MsgCreateValidator`, `Validator` and `Multisig` messages). -For user interactions, `PubKey` is formatted using Protobufs JSON ([ProtoMarshalJSON](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/codec/json.go#L14-L34) function). Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/keyring/output.go#L23-L39 - -## Keyring - -A `Keyring` is an object that stores and manages accounts. In the Cosmos SDK, a `Keyring` implementation follows the `Keyring` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/keyring/keyring.go#L54-L101 - -The default implementation of `Keyring` comes from the third-party [`99designs/keyring`](https://github.com/99designs/keyring) library. - -A few notes on the `Keyring` methods: - -* `Sign(uid string, msg []byte) ([]byte, types.PubKey, error)` strictly deals with the signature of the `msg` bytes. You must prepare and encode the transaction into a canonical `[]byte` form. Because protobuf is not deterministic, it has been decided in [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) that the canonical `payload` to sign is the `SignDoc` struct, deterministically encoded using [ADR-027](../architecture/adr-027-deterministic-protobuf-serialization.md). Note that signature verification is not implemented in the Cosmos SDK by default, it is deferred to the [`anteHandler`](../../develop/advanced-concepts/00-baseapp.md#antehandler). - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L48-L65 - -* `NewAccount(uid, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (*Record, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk. The `PrivKey` is **never stored unencrypted**, instead it is [encrypted with a passphrase](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/crypto/armor.go) before being persisted. In the context of this method, the key type and sequence number refer to the segment of the BIP44 derivation path (for example, `0`, `1`, `2`, ...) that is used to derive a private and a public key from the mnemonic. Using the same mnemonic and derivation path, the same `PrivKey`, `PubKey` and `Address` is generated. The following keys are supported by the keyring: - -* `secp256k1` -* `ed25519` - -* `ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)` exports a private key in ASCII-armored encrypted format using the given passphrase. You can then either import the private key again into the keyring using the `ImportPrivKey(uid, armor, passphrase string)` function or decrypt it into a raw private key using the `UnarmorDecryptPrivKey(armorStr string, passphrase string)` function. - -## Next {hide} - -Learn about [gas and fees](./gas-fees.md) {hide} diff --git a/versioned_docs/version-0.46/develop/high-level-concepts/04-gas-fees.md b/versioned_docs/version-0.46/develop/high-level-concepts/04-gas-fees.md deleted file mode 100644 index ffdab45bc..000000000 --- a/versioned_docs/version-0.46/develop/high-level-concepts/04-gas-fees.md +++ /dev/null @@ -1,81 +0,0 @@ -# Gas and Fees - -This document describes the default strategies to handle gas and fees within a Cosmos SDK application. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK Application](./app-anatomy.md) {prereq} - -## Introduction to `Gas` and `Fees` - -In the Cosmos SDK, `gas` is a special unit that is used to track the consumption of resources during execution. `gas` is typically consumed whenever read and writes are made to the store, but it can also be consumed if expensive computation needs to be done. It serves two main purposes: - -* Make sure blocks are not consuming too many resources and will be finalized. This is implemented by default in the Cosmos SDK via the [block gas meter](#block-gas-meter). -* Prevent spam and abuse from end-user. To this end, `gas` consumed during [`message`](../building-modules/02-messages-and-queries.md#messages) execution is typically priced, resulting in a `fee` (`fees = gas * gas-prices`). `fees` generally have to be paid by the sender of the `message`. Note that the Cosmos SDK does not enforce `gas` pricing by default, as there may be other ways to prevent spam (e.g. bandwidth schemes). Still, most applications will implement `fee` mechanisms to prevent spam. This is done via the [`AnteHandler`](#antehandler). - -## Gas Meter - -In the Cosmos SDK, `gas` is a simple alias for `uint64`, and is managed by an object called a _gas meter_. Gas meters implement the `GasMeter` interface - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/store/types/gas.go#L40-L51 - -where: - -* `GasConsumed()` returns the amount of gas that was consumed by the gas meter instance. -* `GasConsumedToLimit()` returns the amount of gas that was consumed by gas meter instance, or the limit if it is reached. -* `GasRemaining()` returns the gas left in the GasMeter. -* `Limit()` returns the limit of the gas meter instance. `0` if the gas meter is infinite. -* `ConsumeGas(amount Gas, descriptor string)` consumes the amount of `gas` provided. If the `gas` overflows, it panics with the `descriptor` message. If the gas meter is not infinite, it panics if `gas` consumed goes above the limit. -* `RefundGas()` deducts the given amount from the gas consumed. This functionality enables refunding gas to the transaction or block gas pools so that EVM-compatible chains can fully support the go-ethereum StateDB interface. -* `IsPastLimit()` returns `true` if the amount of gas consumed by the gas meter instance is strictly above the limit, `false` otherwise. -* `IsOutOfGas()` returns `true` if the amount of gas consumed by the gas meter instance is above or equal to the limit, `false` otherwise. - -The gas meter is generally held in [`ctx`](../advanced-concepts/02-context.md), and consuming gas is done with the following pattern: - -```go -ctx.GasMeter().ConsumeGas(amount, "description") -``` - -By default, the Cosmos SDK makes use of two different gas meters, the [main gas meter](#main-gas-metter) and the [block gas meter](#block-gas-meter). - -### Main Gas Meter - -`ctx.GasMeter()` is the main gas meter of the application. The main gas meter is initialized in `BeginBlock` via `setDeliverState`, and then tracks gas consumption during execution sequences that lead to state-transitions, i.e. those originally triggered by [`BeginBlock`](../../develop/advanced-concepts/00-baseapp.md#beginblock), [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx) and [`EndBlock`](../../develop/advanced-concepts/00-baseapp.md#endblock). At the beginning of each `DeliverTx`, the main gas meter **must be set to 0** in the [`AnteHandler`](#antehandler), so that it can track gas consumption per-transaction. - -Gas consumption can be done manually, generally by the module developer in the [`BeginBlocker`, `EndBlocker`](../building-modules/beginblock-endblock.md) or [`Msg` service](../building-modules/03-msg-services.md), but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called [`GasKv`](../advanced-concepts/04-store.md#gaskv-store). - -### Block Gas Meter - -`ctx.BlockGasMeter()` is the gas meter used to track gas consumption per block and make sure it does not go above a certain limit. A new instance of the `BlockGasMeter` is created each time [`BeginBlock`](../../develop/advanced-concepts/00-baseapp.md#beginblock) is called. The `BlockGasMeter` is finite, and the limit of gas per block is defined in the application's consensus parameters. By default, Cosmos SDK applications use the default consensus parameters provided by Tendermint: - -+++ https://github.com/tendermint/tendermint/blob/v0.35.4/types/params.go#L78-L117 - -When a new [transaction](../advanced-concepts/01-transactions.md) is being processed via `DeliverTx`, the current value of `BlockGasMeter` is checked to see if it is above the limit. If it is, `DeliverTx` returns immediately. This can happen even with the first transaction in a block, as `BeginBlock` itself can consume gas. If not, the transaction is processed normally. At the end of `DeliverTx`, the gas tracked by `ctx.BlockGasMeter()` is increased by the amount consumed to process the transaction: - -```go -ctx.BlockGasMeter().ConsumeGas( - ctx.GasMeter().GasConsumedToLimit(), - "block gas meter", -) -``` - -## AnteHandler - -The `AnteHandler` is run for every transaction during `CheckTx` and `DeliverTx`, before a Protobuf `Msg` service method for each `sdk.Msg` in the transaction. - -The anteHandler is not implemented in the core Cosmos SDK but in a module. That said, most applications today use the default implementation defined in the [`auth` module](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth). Here is what the `anteHandler` is intended to do in a normal Cosmos SDK application: - -* Verify that the transactions are of the correct type. Transaction types are defined in the module that implements the `anteHandler`, and they follow the transaction interface: - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/tx_msg.go#L38-L46 - This enables developers to play with various types for the transaction of their application. In the default `auth` module, the default transaction type is `Tx`: - +++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L13-L26 -* Verify signatures for each [`message`](../building-modules/02-messages-and-queries.md#messages) contained in the transaction. Each `message` should be signed by one or multiple sender(s), and these signatures must be verified in the `anteHandler`. -* During `CheckTx`, verify that the gas prices provided with the transaction is greater than the local `min-gas-prices` (as a reminder, gas-prices can be deducted from the following equation: `fees = gas * gas-prices`). `min-gas-prices` is a parameter local to each full-node and used during `CheckTx` to discard transactions that do not provide a minimum amount of fees. This ensures that the mempool cannot be spammed with garbage transactions. -* Verify that the sender of the transaction has enough funds to cover for the `fees`. When the end-user generates a transaction, they must indicate 2 of the 3 following parameters (the third one being implicit): `fees`, `gas` and `gas-prices`. This signals how much they are willing to pay for nodes to execute their transaction. The provided `gas` value is stored in a parameter called `GasWanted` for later use. -* Set `newCtx.GasMeter` to 0, with a limit of `GasWanted`. **This step is crucial**, as it not only makes sure the transaction cannot consume infinite gas, but also that `ctx.GasMeter` is reset in-between each `DeliverTx` (`ctx` is set to `newCtx` after `anteHandler` is run, and the `anteHandler` is run each time `DeliverTx` is called). - -As explained above, the `anteHandler` returns a maximum limit of `gas` the transaction can consume during execution called `GasWanted`. The actual amount consumed in the end is denominated `GasUsed`, and we must therefore have `GasUsed =< GasWanted`. Both `GasWanted` and `GasUsed` are relayed to the underlying consensus engine when [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx) returns. - -## Next {hide} - -Learn about [baseapp](../../develop/advanced-concepts/00-baseapp.md) {hide} diff --git a/versioned_docs/version-0.46/develop/high-level-concepts/_category_.json b/versioned_docs/version-0.46/develop/high-level-concepts/_category_.json deleted file mode 100644 index 2c4105ee6..000000000 --- a/versioned_docs/version-0.46/develop/high-level-concepts/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "High Level Concepts", - "position": 1, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/develop/intro/00-what-is-sdk.md b/versioned_docs/version-0.46/develop/intro/00-what-is-sdk.md deleted file mode 100644 index beb2ba555..000000000 --- a/versioned_docs/version-0.46/develop/intro/00-what-is-sdk.md +++ /dev/null @@ -1,31 +0,0 @@ -# What is the Cosmos SDK - -The [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) is an open-source framework for building multi-asset public Proof-of-Stake (PoS) blockchains, like the Cosmos Hub, as well as permissioned Proof-of-Authority (PoA) blockchains. Blockchains built with the Cosmos SDK are generally referred to as **application-specific blockchains**. - -The goal of the Cosmos SDK is to allow developers to easily create custom blockchains from scratch that can natively interoperate with other blockchains. We envision the Cosmos SDK as the npm-like framework to build secure blockchain applications on top of [Tendermint](https://github.com/tendermint/tendermint). SDK-based blockchains are built out of composable [modules](../building-modules/intro.md), most of which are open-source and readily available for any developers to use. Anyone can create a module for the Cosmos SDK, and integrating already-built modules is as simple as importing them into your blockchain application. What's more, the Cosmos SDK is a capabilities-based system that allows developers to better reason about the security of interactions between modules. For a deeper look at capabilities, jump to [Object-Capability Model](../advanced-concepts/ocap.md). - -## What are Application-Specific Blockchains - -One development paradigm in the blockchain world today is that of virtual-machine blockchains like Ethereum, where development generally revolves around building decentralized applications on top of an existing blockchain as a set of smart contracts. While smart contracts can be very good for some use cases like single-use applications (e.g. ICOs), they often fall short for building complex decentralized platforms. More generally, smart contracts can be limiting in terms of flexibility, sovereignty and performance. - -Application-specific blockchains offer a radically different development paradigm than virtual-machine blockchains. An application-specific blockchain is a blockchain customized to operate a single application: developers have all the freedom to make the design decisions required for the application to run optimally. They can also provide better sovereignty, security and performance. - -Learn more about [application-specific blockchains](./why-app-specific.md). - -## Why the Cosmos SDK - -The Cosmos SDK is the most advanced framework for building custom application-specific blockchains today. Here are a few reasons why you might want to consider building your decentralized application with the Cosmos SDK: - -* The default consensus engine available within the Cosmos SDK is [Tendermint Core](https://github.com/tendermint/tendermint). Tendermint is the most (and only) mature BFT consensus engine in existence. It is widely used across the industry and is considered the gold standard consensus engine for building Proof-of-Stake systems. -* The Cosmos SDK is open-source and designed to make it easy to build blockchains out of composable [modules](../../x/). As the ecosystem of open-source Cosmos SDK modules grows, it will become increasingly easier to build complex decentralized platforms with it. -* The Cosmos SDK is inspired by capabilities-based security, and informed by years of wrestling with blockchain state-machines. This makes the Cosmos SDK a very secure environment to build blockchains. -* Most importantly, the Cosmos SDK has already been used to build many application-specific blockchains that are already in production. Among others, we can cite [Cosmos Hub](https://hub.cosmos.network), [IRIS Hub](https://irisnet.org), [Binance Chain](https://docs.binance.org/), [Terra](https://terra.money/) or [Kava](https://www.kava.io/). [Many more](https://cosmos.network/ecosystem) are building on the Cosmos SDK. - -## Getting started with the Cosmos SDK - -* Learn more about the [architecture of a Cosmos SDK application](./sdk-app-architecture.md) -* Learn how to build an application-specific blockchain from scratch with the [Cosmos SDK Tutorial](https://cosmos.network/docs/tutorial) - -## Next {hide} - -Learn about [application-specific blockchains](./why-app-specific.md) {hide} diff --git a/versioned_docs/version-0.46/develop/intro/01-why-app-specific.md b/versioned_docs/version-0.46/develop/intro/01-why-app-specific.md deleted file mode 100644 index 3222f5b31..000000000 --- a/versioned_docs/version-0.46/develop/intro/01-why-app-specific.md +++ /dev/null @@ -1,77 +0,0 @@ -# Application-Specific Blockchains - -This document explains what application-specific blockchains are, and why developers would want to build one as opposed to writing Smart Contracts. {synopsis} - -## What are application-specific blockchains - -Application-specific blockchains are blockchains customized to operate a single application. Instead of building a decentralized application on top of an underlying blockchain like Ethereum, developers build their own blockchain from the ground up. This means building a full-node client, a light-client, and all the necessary interfaces (CLI, REST, ...) to interact with the nodes. - -```text - ^ +-------------------------------+ ^ - | | | | Built with Cosmos SDK - | | State-machine = Application | | - | | | v - | +-------------------------------+ - | | | ^ -Blockchain node | | Consensus | | - | | | | - | +-------------------------------+ | Tendermint Core - | | | | - | | Networking | | - | | | | - v +-------------------------------+ v -``` - -## What are the shortcomings of Smart Contracts - -Virtual-machine blockchains like Ethereum addressed the demand for more programmability back in 2014. At the time, the options available for building decentralized applications were quite limited. Most developers would build on top of the complex and limited Bitcoin scripting language, or fork the Bitcoin codebase which was hard to work with and customize. - -Virtual-machine blockchains came in with a new value proposition. Their state-machine incorporates a virtual-machine that is able to interpret turing-complete programs called Smart Contracts. These Smart Contracts are very good for use cases like one-time events (e.g. ICOs), but they can fall short for building complex decentralized platforms. Here is why: - -* Smart Contracts are generally developed with specific programming languages that can be interpreted by the underlying virtual-machine. These programming languages are often immature and inherently limited by the constraints of the virtual-machine itself. For example, the Ethereum Virtual Machine does not allow developers to implement automatic execution of code. Developers are also limited to the account-based system of the EVM, and they can only choose from a limited set of functions for their cryptographic operations. These are examples, but they hint at the lack of **flexibility** that a smart contract environment often entails. -* Smart Contracts are all run by the same virtual machine. This means that they compete for resources, which can severely restrain **performance**. And even if the state-machine were to be split in multiple subsets (e.g. via sharding), Smart Contracts would still need to be interpreted by a virtual machine, which would limit performance compared to a native application implemented at state-machine level (our benchmarks show an improvement on the order of 10x in performance when the virtual-machine is removed). -* Another issue with the fact that Smart Contracts share the same underlying environment is the resulting limitation in **sovereignty**. A decentralized application is an ecosystem that involves multiple players. If the application is built on a general-purpose virtual-machine blockchain, stakeholders have very limited sovereignty over their application, and are ultimately superseded by the governance of the underlying blockchain. If there is a bug in the application, very little can be done about it. - -Application-Specific Blockchains are designed to address these shortcomings. - -## Application-Specific Blockchains Benefits - -### Flexibility - -Application-specific blockchains give maximum flexibility to developers: - -* In Cosmos blockchains, 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/). This interface can be wrapped in any programming language, meaning developers can build their state-machine in the programming language of their choice. - -* Developers can choose among multiple frameworks to build their state-machine. The most widely used today is the Cosmos SDK, but others exist (e.g. [Lotion](https://github.com/nomic-io/lotion), [Weave](https://github.com/iov-one/weave), ...). Typically the choice will be made based on the programming language they want to use (Cosmos SDK and Weave are in Golang, Lotion is in Javascript, ...). -* The ABCI also allows developers to swap the consensus engine of their application-specific blockchain. Today, only Tendermint is production-ready, but in the future other consensus engines are expected to emerge. -* Even when they settle for a framework and consensus engine, developers still have the freedom to tweak them if they don't perfectly match their requirements in their pristine forms. -* Developers are free to explore the full spectrum of tradeoffs (e.g. number of validators vs transaction throughput, safety vs availability in asynchrony, ...) and design choices (DB or IAVL tree for storage, UTXO or account model, ...). -* Developers can implement automatic execution of code. In the Cosmos SDK, logic can be automatically triggered at the beginning and the end of each block. They are also free to choose the cryptographic library used in their application, as opposed to being constrained by what is made available by the underlying environment in the case of virtual-machine blockchains. - -The list above contains a few examples that show how much flexibility application-specific blockchains give to developers. The goal of Cosmos and the Cosmos SDK is to make developer tooling as generic and composable as possible, so that each part of the stack can be forked, tweaked and improved without losing compatibility. As the community grows, more alternatives for each of the core building blocks will emerge, giving more options to developers. - -### Performance - -decentralized applications built with Smart Contracts are inherently capped in performance by the underlying environment. For a decentralized application to optimise performance, it needs to be built as an application-specific blockchain. Next are some of the benefits an application-specific blockchain brings in terms of performance: - -* Developers of application-specific blockchains can choose to operate with a novel consensus engine such as Tendermint BFT. Compared to Proof-of-Work (used by most virtual-machine blockchains today), it offers significant gains in throughput. -* An application-specific blockchain only operates a single application, so that the application does not compete with others for computation and storage. This is the opposite of most non-sharded virtual-machine blockchains today, where smart contracts all compete for computation and storage. -* Even if a virtual-machine blockchain offered application-based sharding coupled with an efficient consensus algorithm, performance would still be limited by the virtual-machine itself. The real throughput bottleneck is the state-machine, and requiring transactions to be interpreted by a virtual-machine significantly increases the computational complexity of processing them. - -### Security - -Security is hard to quantify, and greatly varies from platform to platform. That said here are some important benefits an application-specific blockchain can bring in terms of security: - -* Developers can choose proven programming languages like Go when building their application-specific blockchains, as opposed to smart contract programming languages that are often more immature. -* Developers are not constrained by the cryptographic functions made available by the underlying virtual-machines. They can use their own custom cryptography, and rely on well-audited crypto libraries. -* Developers do not have to worry about potential bugs or exploitable mechanisms in the underlying virtual-machine, making it easier to reason about the security of the application. - -### Sovereignty - -One of the major benefits of application-specific blockchains is sovereignty. A decentralized application is an ecosystem that involves many actors: users, developers, third-party services, and more. When developers build on virtual-machine blockchain where many decentralized applications coexist, the community of the application is different than the community of the underlying blockchain, and the latter supersedes the former in the governance process. If there is a bug or if a new feature is needed, stakeholders of the application have very little leeway to upgrade the code. If the community of the underlying blockchain refuses to act, nothing can happen. - -The fundamental issue here is that the governance of the application and the governance of the network are not aligned. This issue is solved by application-specific blockchains. Because application-specific blockchains specialize to operate a single application, stakeholders of the application have full control over the entire chain. This ensures that the community will not be stuck if a bug is discovered, and that it has the freedom to choose how it is going to evolve. - -## Next {hide} - -Learn more about the [high-level architecture](./sdk-app-architecture.md) of a Cosmos SDK application {hide} diff --git a/versioned_docs/version-0.46/develop/intro/02-sdk-app-architecture.md b/versioned_docs/version-0.46/develop/intro/02-sdk-app-architecture.md deleted file mode 100644 index 626c02fc4..000000000 --- a/versioned_docs/version-0.46/develop/intro/02-sdk-app-architecture.md +++ /dev/null @@ -1,93 +0,0 @@ -# Blockchain Architecture - -## State machine - -At its core, a blockchain is a [replicated deterministic state machine](https://en.wikipedia.org/wiki/State_machine_replication). - -A state machine is a computer science concept whereby a machine can have multiple states, but only one at any given time. There is a `state`, which describes the current state of the system, and `transactions`, that trigger state transitions. - -Given a state S and a transaction T, the state machine will return a new state S'. - -```text -+--------+ +--------+ -| | | | -| S +---------------->+ S' | -| | apply(T) | | -+--------+ +--------+ -``` - -In practice, the transactions are bundled in blocks to make the process more efficient. Given a state S and a block of transactions B, the state machine will return a new state S'. - -```text -+--------+ +--------+ -| | | | -| S +----------------------------> | S' | -| | For each T in B: apply(T) | | -+--------+ +--------+ -``` - -In a blockchain context, the state machine is deterministic. This means that if a node is started at a given state and replays the same sequence of transactions, it will always end up with the same final state. - -The Cosmos SDK gives developers maximum flexibility to define the state of their application, transaction types and state transition functions. The process of building state-machines with the Cosmos SDK will be described more in depth in the following sections. But first, let us see how the state-machine is replicated using **Tendermint**. - -## Tendermint - -Thanks to the Cosmos SDK, developers just have to define the state machine, and [*Tendermint*](https://docs.tendermint.com/master/introduction/what-is-tendermint.html) will handle replication over the network for them. - -```text - ^ +-------------------------------+ ^ - | | | | Built with Cosmos SDK - | | State-machine = Application | | - | | | v - | +-------------------------------+ - | | | ^ -Blockchain node | | Consensus | | - | | | | - | +-------------------------------+ | Tendermint Core - | | | | - | | Networking | | - | | | | - v +-------------------------------+ v -``` - -[Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions. - -The Tendermint [consensus algorithm](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a `prevote` and a `precommit` on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine. - -## ABCI - -Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/master/spec/abci/), which the application must implement. - -```text - +---------------------+ - | | - | Application | - | | - +--------+---+--------+ - ^ | - | | ABCI - | v - +--------+---+--------+ - | | - | | - | Tendermint | - | | - | | - +---------------------+ -``` - -Note that **Tendermint only handles transaction bytes**. It has no knowledge of what these bytes mean. All Tendermint does is order these transaction bytes deterministically. Tendermint passes the bytes to the application via the ABCI, and expects a return code to inform it if the messages contained in the transactions were successfully processed or not. - -Here are the most important messages of the ABCI: - -* `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. . A special handler called the [`AnteHandler`](../high-level-concepts/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/v0.34/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet. -* `DeliverTx`: When a [valid block](https://docs.tendermint.com/v0.34/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again, along with the actual [`Msg` service](../building-modules/03-msg-services.md) RPC for each message in the transaction. -* `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transactions or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite. - -Find a more detailed view of the ABCI methods from the [Tendermint docs](https://docs.tendermint.com/v0.35/introduction/what-is-tendermint.html#abci-overview). - -Any application built on Tendermint needs to implement the ABCI interface in order to communicate with the underlying local Tendermint engine. Fortunately, you do not have to implement the ABCI interface. The Cosmos SDK provides a boilerplate implementation of it in the form of [baseapp](./sdk-design.md#baseapp). - -## Next {hide} - -Read about the [high-level design principles of the Cosmos SDK](./sdk-design.md) {hide} diff --git a/versioned_docs/version-0.46/develop/intro/03-sdk-design.md b/versioned_docs/version-0.46/develop/intro/03-sdk-design.md deleted file mode 100644 index 7d9d81654..000000000 --- a/versioned_docs/version-0.46/develop/intro/03-sdk-design.md +++ /dev/null @@ -1,93 +0,0 @@ -# Main Components of the Cosmos SDK - -The Cosmos SDK is a framework that facilitates the development of secure state-machines on top of Tendermint. At its core, the Cosmos SDK is a boilerplate implementation of the [ABCI](./sdk-app-architecture.md#abci) in Golang. It comes with a [`multistore`](../advanced-concepts/04-store.md#multistore) to persist data and a [`router`](../advanced-concepts/00-baseapp.md#routing) to handle transactions. - -Here is a simplified view of how transactions are handled by an application built on top of the Cosmos SDK when transferred from Tendermint via `DeliverTx`: - -1. Decode `transactions` received from the Tendermint consensus engine (remember that Tendermint only deals with `[]bytes`). -2. Extract `messages` from `transactions` and do basic sanity checks. -3. Route each message to the appropriate module so that it can be processed. -4. Commit state changes. - -## `baseapp` - -`baseapp` is the boilerplate implementation of a Cosmos SDK application. It comes with an implementation of the ABCI to handle the connection with the underlying consensus engine. Typically, a Cosmos SDK application extends `baseapp` by embedding it in [`app.go`](../high-level-concepts/00-overview-app.md#core-application-file). - -Here is an example of this from `simapp`, the Cosmos SDK demonstration app: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L154-L193 - -The goal of `baseapp` is to provide a secure interface between the store and the extensible state machine while defining as little about the state machine as possible (staying true to the ABCI). - -For more on `baseapp`, please click [here](../../develop/advanced-concepts/00-baseapp.md). - -## Multistore - -The Cosmos SDK provides a [`multistore`](../advanced-concepts/04-store.md#multistore) for persisting state. The multistore allows developers to declare any number of [`KVStores`](../advanced-concepts/04-store.md#base-layer-kvstores). These `KVStores` only accept the `[]byte` type as value and therefore any custom structure needs to be marshalled using [a codec](../advanced-concepts/05-encoding.md) before being stored. - -The multistore abstraction is used to divide the state in distinct compartments, each managed by its own module. For more on the multistore, click [here](../advanced-concepts/04-store.md#multistore) - -## Modules - -The power of the Cosmos SDK lies in its modularity. Cosmos SDK applications are built by aggregating a collection of interoperable modules. Each module defines a subset of the state and contains its own message/transaction processor, while the Cosmos SDK is responsible for routing each message to its respective module. - -Here is a simplified view of how a transaction is processed by the application of each full-node when it is received in a valid block: - -```text - + - | - | Transaction relayed from the full-node's - | Tendermint engine to the node's application - | via DeliverTx - | - | - +---------------------v--------------------------+ - | APPLICATION | - | | - | Using baseapp's methods: Decode the Tx, | - | extract and route the message(s) | - | | - +---------------------+--------------------------+ - | - | - | - +---------------------------+ - | - | - | Message routed to - | the correct module - | to be processed - | - | -+----------------+ +---------------+ +----------------+ +------v----------+ -| | | | | | | | -| AUTH MODULE | | BANK MODULE | | STAKING MODULE | | GOV MODULE | -| | | | | | | | -| | | | | | | Handles message,| -| | | | | | | Updates state | -| | | | | | | | -+----------------+ +---------------+ +----------------+ +------+----------+ - | - | - | - | - +--------------------------+ - | - | Return result to Tendermint - | (0=Ok, 1=Err) - v -``` - -Each module can be seen as a little state-machine. Developers need to define the subset of the state handled by the module, as well as custom message types that modify the state (*Note:* `messages` are extracted from `transactions` by `baseapp`). In general, each module declares its own `KVStore` in the `multistore` to persist the subset of the state it defines. Most developers will need to access other 3rd party modules when building their own modules. Given that the Cosmos SDK is an open framework, some of the modules may be malicious, which means there is a need for security principles to reason about inter-module interactions. These principles are based on [object-capabilities](../advanced-concepts/ocap.md). In practice, this means that instead of having each module keep an access control list for other modules, each module implements special objects called `keepers` that can be passed to other modules to grant a pre-defined set of capabilities. - -Cosmos SDK modules are defined in the `x/` folder of the Cosmos SDK. Some core modules include: - -* `x/auth`: Used to manage accounts and signatures. -* `x/bank`: Used to enable tokens and token transfers. -* `x/staking` + `x/slashing`: Used to build Proof-Of-Stake blockchains. - -In addition to the already existing modules in `x/`, that anyone can use in their app, the Cosmos SDK lets you build your own custom modules. You can check an [example of that in the tutorial](https://tutorials.cosmos.network/). - -## Next {hide} - -Learn more about the [anatomy of a Cosmos SDK application](../high-level-concepts/app-anatomy.md) {hide} diff --git a/versioned_docs/version-0.46/develop/intro/_category_.json b/versioned_docs/version-0.46/develop/intro/_category_.json deleted file mode 100644 index b218fe9be..000000000 --- a/versioned_docs/version-0.46/develop/intro/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Introduction", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/CosmWasm/README.md b/versioned_docs/version-0.46/integrate/CosmWasm/README.md deleted file mode 100644 index b6a14810b..000000000 --- a/versioned_docs/version-0.46/integrate/CosmWasm/README.md +++ /dev/null @@ -1,13 +0,0 @@ - - -# CosmWasm smart contracts - ->CosmWasm is a smart contracting platform built for the Cosmos ecosystem. Simply put, it's the Cosmos (Cosm) way of using WebAssembly (Wasm) hence the name. - ->CosmWasm is written as a module that can plug into the Cosmos SDK. This means that anyone currently building a blockchain using the Cosmos SDK can quickly and easily add CosmWasm smart contracting support to their chain, without adjusting existing logic. - -Read more about writing smart contracts with CosmWasm at their [documentation site](https://book.cosmwasm.com/), or visit [the repository](https://github.com/CosmWasm/cosmwasm). diff --git a/versioned_docs/version-0.46/integrate/CosmWasm/_category_.json b/versioned_docs/version-0.46/integrate/CosmWasm/_category_.json deleted file mode 100644 index e4e9f1986..000000000 --- a/versioned_docs/version-0.46/integrate/CosmWasm/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "CosmWasm", - "position": 4, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/_category_.json b/versioned_docs/version-0.46/integrate/_category_.json deleted file mode 100644 index b860342f4..000000000 --- a/versioned_docs/version-0.46/integrate/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Integrate", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/architecture/PROCESS.md b/versioned_docs/version-0.46/integrate/architecture/PROCESS.md deleted file mode 100644 index c5140bbe4..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/PROCESS.md +++ /dev/null @@ -1,56 +0,0 @@ -# ADR Creation Process - -1. Copy the `adr-template.md` file. Use the following filename pattern: `adr-next_number-title.md` -2. Create a draft Pull Request if you want to get an early feedback. -3. Make sure the context and a solution is clear and well documented. -4. Add an entry to a list in the [README](./README.md) file. -5. Create a Pull Request to propose a new ADR. - -## ADR life cycle - -ADR creation is an **iterative** process. Instead of trying to solve all decisions in a single ADR pull request, we MUST firstly understand the problem and collect feedback through a GitHub Issue. - -1. Every proposal SHOULD start with a new GitHub Issue or be a result of existing Issues. The Issue should contain just a brief proposal summary. - -2. Once the motivation is validated, a GitHub Pull Request (PR) is created with a new document based on the `adr-template.md`. - -3. An ADR doesn't have to arrive to `main` with an _accepted_ status in a single PR. If the motivation is clear and the solution is sound, we SHOULD be able to merge it and keep a _proposed_ status. It's preferable to have an iterative approach rather than long, not merged Pull Requests. - -4. If a _proposed_ ADR is merged, then it should clearly document outstanding issues either in ADR document notes or in a GitHub Issue. - -5. The PR SHOULD always be merged. In the case of a faulty ADR, we still prefer to merge it with a _rejected_ status. The only time the ADR SHOULD NOT be merged is if the author abandons it. - -6. Merged ADRs SHOULD NOT be pruned. - -### ADR status - -Status has two components: - -```text -{CONSENSUS STATUS} {IMPLEMENTATION STATUS} -``` - -IMPLEMENTATION STATUS is either `Implemented` or `Not Implemented`. - -#### Consensus Status - -```text -DRAFT -> PROPOSED -> LAST CALL yyyy-mm-dd -> ACCEPTED | REJECTED -> SUPERSEDED by ADR-xxx - \ | - \ | - v v - ABANDONED -``` - -* `DRAFT`: [optional] an ADR which is work in progress, not being ready for a general review. This is to present an early work and get an early feedback in a Draft Pull Request form. -* `PROPOSED`: an ADR covering a full solution architecture and still in the review - project stakeholders haven't reached an agreed yet. -* `LAST CALL `: [optional] clear notify that we are close to accept updates. Changing a status to `LAST CALL` means that social consensus (of Cosmos SDK maintainers) has been reached and we still want to give it a time to let the community react or analyze. -* `ACCEPTED`: ADR which will represent a currently implemented or to be implemented architecture design. -* `REJECTED`: ADR can go from PROPOSED or ACCEPTED to rejected if the consensus among project stakeholders will decide so. -* `SUPERSEEDED by ADR-xxx`: ADR which has been superseded by a new ADR. -* `ABANDONED`: the ADR is no longer pursued by the original authors. - -## Language used in ADR - -* The context/background should be written in the present tense. -* Avoid using a first, personal form. diff --git a/versioned_docs/version-0.46/integrate/architecture/README.md b/versioned_docs/version-0.46/integrate/architecture/README.md deleted file mode 100644 index 4257d1e87..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/README.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -order: false -parent: - order: false ---- - -# Architecture Decision Records (ADR) - -This is a location to record all high-level architecture decisions in the Cosmos-SDK. - -An Architectural Decision (**AD**) is a software design choice that addresses a functional or non-functional requirement that is architecturally significant. -An Architecturally Significant Requirement (**ASR**) is a requirement that has a measurable effect on a software system’s architecture and quality. -An Architectural Decision Record (**ADR**) captures a single AD, such as often done when writing personal notes or meeting minutes; the collection of ADRs created and maintained in a project constitute its decision log. All these are within the topic of Architectural Knowledge Management (AKM). - -You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t). - -## Rationale - -ADRs are intended to be the primary mechanism for proposing new feature designs and new processes, for collecting community input on an issue, and for documenting the design decisions. -An ADR should provide: - -* Context on the relevant goals and the current state -* Proposed changes to achieve the goals -* Summary of pros and cons -* References -* Changelog - -Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and -justification for a change in architecture, or for the architecture of something -new. The spec is much more compressed and streamlined summary of everything as -it stands today. - -If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match. - -## Creating new ADR - -Read about the [PROCESS](./PROCESS.md). - -### Use RFC 2119 Keywords - -When writing ADRs, follow the same best practices for writing RFCs. When writing RFCs, key words are used to signify the requirements in the specification. These words are often capitalized: "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL. They are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). - -## ADR Table of Contents - -### Accepted - -* [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md) -* [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md) -* [ADR 006: Secret Store Replacement](./adr-006-secret-store-replacement.md) -* [ADR 009: Evidence Module](./adr-009-evidence-module.md) -* [ADR 010: Modular AnteHandler](./adr-010-modular-antehandler.md) -* [ADR 019: Protocol Buffer State Encoding](./adr-019-protobuf-state-encoding.md) -* [ADR 020: Protocol Buffer Transaction Encoding](./adr-020-protobuf-transaction-encoding.md) -* [ADR 021: Protocol Buffer Query Encoding](./adr-021-protobuf-query-encoding.md) -* [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md) -* [ADR 029: Fee Grant Module](./adr-029-fee-grant-module.md) -* [ADR 030: Message Authorization Module](./adr-030-authz-module.md) -* [ADR 031: Protobuf Msg Services](./adr-031-msg-service.md) -* [ADR 055: ORM](./adr-055-orm.md) - -### Proposed - -* [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md) -* [ADR 011: Generalize Genesis Accounts](./adr-011-generalize-genesis-accounts.md) -* [ADR 012: State Accessors](./adr-012-state-accessors.md) -* [ADR 013: Metrics](./adr-013-metrics.md) -* [ADR 016: Validator Consensus Key Rotation](./adr-016-validator-consensus-key-rotation.md) -* [ADR 017: Historical Header Module](./adr-017-historical-header-module.md) -* [ADR 018: Extendable Voting Periods](./adr-018-extendable-voting-period.md) -* [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md) -* [ADR 024: Coin Metadata](./adr-024-coin-metadata.md) -* [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md) -* [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md) -* [ADR 032: Typed Events](./adr-032-typed-events.md) -* [ADR 033: Inter-module RPC](./adr-033-protobuf-inter-module-comm.md) -* [ADR 035: Rosetta API Support](./adr-035-rosetta-api-support.md) -* [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md) -* [ADR 038: State Listening](./adr-038-state-listening.md) -* [ADR 039: Epoched Staking](./adr-039-epoched-staking.md) -* [ADR 040: Storage and SMT State Commitments](./adr-040-storage-and-smt-state-commitments.md) -* [ADR 046: Module Params](./adr-046-module-params.md) - -### Draft - -- [ADR 044: Guidelines for Updating Protobuf Definitions](./adr-044-protobuf-updates-guidelines.md) -- [ADR 047: Extend Upgrade Plan](./adr-047-extend-upgrade-plan.md) -- [ADR 053: Go Module Refactoring](./adr-053-go-module-refactoring.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/_category_.json b/versioned_docs/version-0.46/integrate/architecture/_category_.json deleted file mode 100644 index f613ef834..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Architecture", - "position": 6, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-002-docs-structure.md b/versioned_docs/version-0.46/integrate/architecture/adr-002-docs-structure.md deleted file mode 100644 index 5819151fc..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-002-docs-structure.md +++ /dev/null @@ -1,86 +0,0 @@ -# ADR 002: SDK Documentation Structure - -## Context - -There is a need for a scalable structure of the Cosmos SDK documentation. Current documentation includes a lot of non-related Cosmos SDK material, is difficult to maintain and hard to follow as a user. - -Ideally, we would have: - -* All docs related to dev frameworks or tools live in their respective github repos (sdk repo would contain sdk docs, hub repo would contain hub docs, lotion repo would contain lotion docs, etc.) -* All other docs (faqs, whitepaper, high-level material about Cosmos) would live on the website. - -## Decision - -Re-structure the `/docs` folder of the Cosmos SDK github repo as follows: - -```text -docs/ -├── README -├── intro/ -├── concepts/ -│ ├── baseapp -│ ├── types -│ ├── store -│ ├── server -│ ├── modules/ -│ │ ├── keeper -│ │ ├── handler -│ │ ├── cli -│ ├── gas -│ └── commands -├── clients/ -│ ├── lite/ -│ ├── service-providers -├── modules/ -├── spec/ -├── translations/ -└── architecture/ -``` - -The files in each sub-folders do not matter and will likely change. What matters is the sectioning: - -* `README`: Landing page of the docs. -* `intro`: Introductory material. Goal is to have a short explainer of the Cosmos SDK and then channel people to the resource they need. The [Cosmos SDK tutorial](https://github.com/cosmos/sdk-application-tutorial/) will be highlighted, as well as the `godocs`. -* `concepts`: Contains high-level explanations of the abstractions of the Cosmos SDK. It does not contain specific code implementation and does not need to be updated often. **It is not an API specification of the interfaces**. API spec is the `godoc`. -* `clients`: Contains specs and info about the various Cosmos SDK clients. -* `spec`: Contains specs of modules, and others. -* `modules`: Contains links to `godocs` and the spec of the modules. -* `architecture`: Contains architecture-related docs like the present one. -* `translations`: Contains different translations of the documentation. - -Website docs sidebar will only include the following sections: - -* `README` -* `intro` -* `concepts` -* `clients` - -`architecture` need not be displayed on the website. - -## Status - -Accepted - -## Consequences - -### Positive - -* Much clearer organisation of the Cosmos SDK docs. -* The `/docs` folder now only contains Cosmos SDK and gaia related material. Later, it will only contain Cosmos SDK related material. -* Developers only have to update `/docs` folder when they open a PR (and not `/examples` for example). -* Easier for developers to find what they need to update in the docs thanks to reworked architecture. -* Cleaner vuepress build for website docs. -* Will help build an executable doc (cf https://github.com/cosmos/cosmos-sdk/issues/2611) - -### Neutral - -* We need to move a bunch of deprecated stuff to `/_attic` folder. -* We need to integrate content in `docs/sdk/docs/core` in `concepts`. -* We need to move all the content that currently lives in `docs` and does not fit in new structure (like `lotion`, intro material, whitepaper) to the website repository. -* Update `DOCS_README.md` - -## References - -* https://github.com/cosmos/cosmos-sdk/issues/1460 -* https://github.com/cosmos/cosmos-sdk/pull/2695 -* https://github.com/cosmos/cosmos-sdk/issues/2611 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-003-dynamic-capability-store.md b/versioned_docs/version-0.46/integrate/architecture/adr-003-dynamic-capability-store.md deleted file mode 100644 index 02f2a20b1..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-003-dynamic-capability-store.md +++ /dev/null @@ -1,344 +0,0 @@ -# ADR 3: Dynamic Capability Store - -## Changelog - -* 12 December 2019: Initial version -* 02 April 2020: Memory Store Revisions - -## Context - -Full implementation of the [IBC specification](https://github.com/cosmos/ibs) requires the ability to create and authenticate object-capability keys at runtime (i.e., during transaction execution), -as described in [ICS 5](https://github.com/cosmos/ibc/tree/master/spec/01-tx-lifecycle.mdics-005-port-allocation#technical-specification). In the IBC specification, capability keys are created for each newly initialised -port & channel, and are used to authenticate future usage of the port or channel. Since channels and potentially ports can be initialised during transaction execution, the state machine must be able to create -object-capability keys at this time. - -At present, the Cosmos SDK does not have the ability to do this. Object-capability keys are currently pointers (memory addresses) of `StoreKey` structs created at application initialisation in `app.go` ([example](https://github.com/cosmos/gaia/blob/dcbddd9f04b3086c0ad07ee65de16e7adedc7da4/app/app.go#L132)) -and passed to Keepers as fixed arguments ([example](https://github.com/cosmos/gaia/blob/dcbddd9f04b3086c0ad07ee65de16e7adedc7da4/app/app.go#L160)). Keepers cannot create or store capability keys during transaction execution — although they could call `NewKVStoreKey` and take the memory address -of the returned struct, storing this in the Merklised store would result in a consensus fault, since the memory address will be different on each machine (this is intentional — were this not the case, the keys would be predictable and couldn't serve as object capabilities). - -Keepers need a way to keep a private map of store keys which can be altered during transaction execution, along with a suitable mechanism for regenerating the unique memory addresses (capability keys) in this map whenever the application is started or restarted, along with a mechanism to revert capability creation on tx failure. -This ADR proposes such an interface & mechanism. - -## Decision - -The Cosmos SDK will include a new `CapabilityKeeper` abstraction, which is responsible for provisioning, -tracking, and authenticating capabilities at runtime. During application initialisation in `app.go`, -the `CapabilityKeeper` will be hooked up to modules through unique function references -(by calling `ScopeToModule`, defined below) so that it can identify the calling module when later -invoked. - -When the initial state is loaded from disk, the `CapabilityKeeper`'s `Initialise` function will create -new capability keys for all previously allocated capability identifiers (allocated during execution of -past transactions and assigned to particular modes), and keep them in a memory-only store while the -chain is running. - -The `CapabilityKeeper` will include a persistent `KVStore`, a `MemoryStore`, and an in-memory map. -The persistent `KVStore` tracks which capability is owned by which modules. -The `MemoryStore` stores a forward mapping that map from module name, capability tuples to capability names and -a reverse mapping that map from module name, capability name to the capability index. -Since we cannot marshal the capability into a `KVStore` and unmarshal without changing the memory location of the capability, -the reverse mapping in the KVStore will simply map to an index. This index can then be used as a key in the ephemeral -go-map to retrieve the capability at the original memory location. - -The `CapabilityKeeper` will define the following types & functions: - -The `Capability` is similar to `StoreKey`, but has a globally unique `Index()` instead of -a name. A `String()` method is provided for debugging. - -A `Capability` is simply a struct, the address of which is taken for the actual capability. - -```golang -type Capability struct { - index uint64 -} -``` - -A `CapabilityKeeper` contains a persistent store key, memory store key, and mapping of allocated module names. - -```golang -type CapabilityKeeper struct { - persistentKey StoreKey - memKey StoreKey - capMap map[uint64]*Capability - moduleNames map[string]interface{} - sealed bool -} -``` - -The `CapabilityKeeper` provides the ability to create *scoped* sub-keepers which are tied to a -particular module name. These `ScopedCapabilityKeeper`s must be created at application initialisation -and passed to modules, which can then use them to claim capabilities they receive and retrieve -capabilities which they own by name, in addition to creating new capabilities & authenticating capabilities -passed by other modules. - -```golang -type ScopedCapabilityKeeper struct { - persistentKey StoreKey - memKey StoreKey - capMap map[uint64]*Capability - moduleName string -} -``` - -`ScopeToModule` is used to create a scoped sub-keeper with a particular name, which must be unique. -It MUST be called before `InitialiseAndSeal`. - -```golang -func (ck CapabilityKeeper) ScopeToModule(moduleName string) ScopedCapabilityKeeper { - if k.sealed { - panic("cannot scope to module via a sealed capability keeper") - } - - if _, ok := k.scopedModules[moduleName]; ok { - panic(fmt.Sprintf("cannot create multiple scoped keepers for the same module name: %s", moduleName)) - } - - k.scopedModules[moduleName] = struct{}{} - - return ScopedKeeper{ - cdc: k.cdc, - storeKey: k.storeKey, - memKey: k.memKey, - capMap: k.capMap, - module: moduleName, - } -} -``` - -`InitialiseAndSeal` MUST be called exactly once, after loading the initial state and creating all -necessary `ScopedCapabilityKeeper`s, in order to populate the memory store with newly-created -capability keys in accordance with the keys previously claimed by particular modules and prevent the -creation of any new `ScopedCapabilityKeeper`s. - -```golang -func (ck CapabilityKeeper) InitialiseAndSeal(ctx Context) { - if ck.sealed { - panic("capability keeper is sealed") - } - - persistentStore := ctx.KVStore(ck.persistentKey) - map := ctx.KVStore(ck.memKey) - - // initialise memory store for all names in persistent store - for index, value := range persistentStore.Iter() { - capability = &CapabilityKey{index: index} - - for moduleAndCapability := range value { - moduleName, capabilityName := moduleAndCapability.Split("/") - memStore.Set(moduleName + "/fwd/" + capability, capabilityName) - memStore.Set(moduleName + "/rev/" + capabilityName, index) - - ck.capMap[index] = capability - } - } - - ck.sealed = true -} -``` - -`NewCapability` can be called by any module to create a new unique, unforgeable object-capability -reference. The newly created capability is automatically persisted; the calling module need not -call `ClaimCapability`. - -```golang -func (sck ScopedCapabilityKeeper) NewCapability(ctx Context, name string) (Capability, error) { - // check name not taken in memory store - if capStore.Get("rev/" + name) != nil { - return nil, errors.New("name already taken") - } - - // fetch the current index - index := persistentStore.Get("index") - - // create a new capability - capability := &CapabilityKey{index: index} - - // set persistent store - persistentStore.Set(index, Set.singleton(sck.moduleName + "/" + name)) - - // update the index - index++ - persistentStore.Set("index", index) - - // set forward mapping in memory store from capability to name - memStore.Set(sck.moduleName + "/fwd/" + capability, name) - - // set reverse mapping in memory store from name to index - memStore.Set(sck.moduleName + "/rev/" + name, index) - - // set the in-memory mapping from index to capability pointer - capMap[index] = capability - - // return the newly created capability - return capability -} -``` - -`AuthenticateCapability` can be called by any module to check that a capability -does in fact correspond to a particular name (the name can be untrusted user input) -with which the calling module previously associated it. - -```golang -func (sck ScopedCapabilityKeeper) AuthenticateCapability(name string, capability Capability) bool { - // return whether forward mapping in memory store matches name - return memStore.Get(sck.moduleName + "/fwd/" + capability) === name -} -``` - -`ClaimCapability` allows a module to claim a capability key which it has received from another module -so that future `GetCapability` calls will succeed. - -`ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name -in the future. Capabilities are multi-owner, so if multiple modules have a single `Capability` reference, -they will all own it. - -```golang -func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capability, name string) error { - persistentStore := ctx.KVStore(sck.persistentKey) - - // set forward mapping in memory store from capability to name - memStore.Set(sck.moduleName + "/fwd/" + capability, name) - - // set reverse mapping in memory store from name to capability - memStore.Set(sck.moduleName + "/rev/" + name, capability) - - // update owner set in persistent store - owners := persistentStore.Get(capability.Index()) - owners.add(sck.moduleName + "/" + name) - persistentStore.Set(capability.Index(), owners) -} -``` - -`GetCapability` allows a module to fetch a capability which it has previously claimed by name. -The module is not allowed to retrieve capabilities which it does not own. - -```golang -func (sck ScopedCapabilityKeeper) GetCapability(ctx Context, name string) (Capability, error) { - // fetch the index of capability using reverse mapping in memstore - index := memStore.Get(sck.moduleName + "/rev/" + name) - - // fetch capability from go-map using index - capability := capMap[index] - - // return the capability - return capability -} -``` - -`ReleaseCapability` allows a module to release a capability which it had previously claimed. If no -more owners exist, the capability will be deleted globally. - -```golang -func (sck ScopedCapabilityKeeper) ReleaseCapability(ctx Context, capability Capability) err { - persistentStore := ctx.KVStore(sck.persistentKey) - - name := capStore.Get(sck.moduleName + "/fwd/" + capability) - if name == nil { - return error("capability not owned by module") - } - - // delete forward mapping in memory store - memoryStore.Delete(sck.moduleName + "/fwd/" + capability, name) - - // delete reverse mapping in memory store - memoryStore.Delete(sck.moduleName + "/rev/" + name, capability) - - // update owner set in persistent store - owners := persistentStore.Get(capability.Index()) - owners.remove(sck.moduleName + "/" + name) - if owners.size() > 0 { - // there are still other owners, keep the capability around - persistentStore.Set(capability.Index(), owners) - } else { - // no more owners, delete the capability - persistentStore.Delete(capability.Index()) - delete(capMap[capability.Index()]) - } -} -``` - -### Usage patterns - -#### Initialisation - -Any modules which use dynamic capabilities must be provided a `ScopedCapabilityKeeper` in `app.go`: - -```golang -ck := NewCapabilityKeeper(persistentKey, memoryKey) -mod1Keeper := NewMod1Keeper(ck.ScopeToModule("mod1"), ....) -mod2Keeper := NewMod2Keeper(ck.ScopeToModule("mod2"), ....) - -// other initialisation logic ... - -// load initial state... - -ck.InitialiseAndSeal(initialContext) -``` - -#### Creating, passing, claiming and using capabilities - -Consider the case where `mod1` wants to create a capability, associate it with a resource (e.g. an IBC channel) by name, then pass it to `mod2` which will use it later: - -Module 1 would have the following code: - -```golang -capability := scopedCapabilityKeeper.NewCapability(ctx, "resourceABC") -mod2Keeper.SomeFunction(ctx, capability, args...) -``` - -`SomeFunction`, running in module 2, could then claim the capability: - -```golang -func (k Mod2Keeper) SomeFunction(ctx Context, capability Capability) { - k.sck.ClaimCapability(ctx, capability, "resourceABC") - // other logic... -} -``` - -Later on, module 2 can retrieve that capability by name and pass it to module 1, which will authenticate it against the resource: - -```golang -func (k Mod2Keeper) SomeOtherFunction(ctx Context, name string) { - capability := k.sck.GetCapability(ctx, name) - mod1.UseResource(ctx, capability, "resourceABC") -} -``` - -Module 1 will then check that this capability key is authenticated to use the resource before allowing module 2 to use it: - -```golang -func (k Mod1Keeper) UseResource(ctx Context, capability Capability, resource string) { - if !k.sck.AuthenticateCapability(name, capability) { - return errors.New("unauthenticated") - } - // do something with the resource -} -``` - -If module 2 passed the capability key to module 3, module 3 could then claim it and call module 1 just like module 2 did -(in which case module 1, module 2, and module 3 would all be able to use this capability). - -## Status - -Proposed. - -## Consequences - -### Positive - -* Dynamic capability support. -* Allows CapabilityKeeper to return same capability pointer from go-map while reverting any writes to the persistent `KVStore` and in-memory `MemoryStore` on tx failure. - -### Negative - -* Requires an additional keeper. -* Some overlap with existing `StoreKey` system (in the future they could be combined, since this is a superset functionality-wise). -* Requires an extra level of indirection in the reverse mapping, since MemoryStore must map to index which must then be used as key in a go map to retrieve the actual capability - -### Neutral - -(none known) - -## References - -* [Original discussion](https://github.com/cosmos/cosmos-sdk/pull/5230#discussion_r343978513) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-004-split-denomination-keys.md b/versioned_docs/version-0.46/integrate/architecture/adr-004-split-denomination-keys.md deleted file mode 100644 index 3012901b8..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-004-split-denomination-keys.md +++ /dev/null @@ -1,120 +0,0 @@ -# ADR 004: Split Denomination Keys - -## Changelog - -* 2020-01-08: Initial version -* 2020-01-09: Alterations to handle vesting accounts -* 2020-01-14: Updates from review feedback -* 2020-01-30: Updates from implementation - -### Glossary - -* denom / denomination key -- unique token identifier. - -## Context - -With permissionless IBC, anyone will be able to send arbitrary denominations to any other account. Currently, all non-zero balances are stored along with the account in an `sdk.Coins` struct, which creates a potential denial-of-service concern, as too many denominations will become expensive to load & store each time the account is modified. See issues [5467](https://github.com/cosmos/cosmos-sdk/issues/5467) and [4982](https://github.com/cosmos/cosmos-sdk/issues/4982) for additional context. - -Simply rejecting incoming deposits after a denomination count limit doesn't work, since it opens up a griefing vector: someone could send a user lots of nonsensical coins over IBC, and then prevent the user from receiving real denominations (such as staking rewards). - -## Decision - -Balances shall be stored per-account & per-denomination under a denomination- and account-unique key, thus enabling O(1) read & write access to the balance of a particular account in a particular denomination. - -### Account interface (x/auth) - -`GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will -now be stored in & managed by the bank module. - -The vesting account interface will replace `SpendableCoins` in favor of `LockedCoins` which does -not require the account balance anymore. In addition, `TrackDelegation()` will now accept the -account balance of all tokens denominated in the vesting balance instead of loading the entire -account balance. - -Vesting accounts will continue to store original vesting, delegated free, and delegated -vesting coins (which is safe since these cannot contain arbitrary denominations). - -### Bank keeper (x/bank) - -The following APIs will be added to the `x/bank` keeper: - -* `GetAllBalances(ctx Context, addr AccAddress) Coins` -* `GetBalance(ctx Context, addr AccAddress, denom string) Coin` -* `SetBalance(ctx Context, addr AccAddress, coin Coin)` -* `LockedCoins(ctx Context, addr AccAddress) Coins` -* `SpendableCoins(ctx Context, addr AccAddress) Coins` - -Additional APIs may be added to facilitate iteration and auxiliary functionality not essential to -core functionality or persistence. - -Balances will be stored first by the address, then by the denomination (the reverse is also possible, -but retrieval of all balances for a single account is presumed to be more frequent): - -```golang -var BalancesPrefix = []byte("balances") - -func (k Keeper) SetBalance(ctx Context, addr AccAddress, balance Coin) error { - if !balance.IsValid() { - return err - } - - store := ctx.KVStore(k.storeKey) - balancesStore := prefix.NewStore(store, BalancesPrefix) - accountStore := prefix.NewStore(balancesStore, addr.Bytes()) - - bz := Marshal(balance) - accountStore.Set([]byte(balance.Denom), bz) - - return nil -} -``` - -This will result in the balances being indexed by the byte representation of -`balances/{address}/{denom}`. - -`DelegateCoins()` and `UndelegateCoins()` will be altered to only load each individual -account balance by denomination found in the (un)delegation amount. As a result, -any mutations to the account balance by will made by denomination. - -`SubtractCoins()` and `AddCoins()` will be altered to read & write the balances -directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist). - -`trackDelegation()` and `trackUndelegation()` will be altered to no longer update -account balances. - -External APIs will need to scan all balances under an account to retain backwards-compatibility. It -is advised that these APIs use `GetBalance` and `SetBalance` instead of `GetAllBalances` when -possible as to not load the entire account balance. - -### Supply module - -The supply module, in order to implement the total supply invariant, will now need -to scan all accounts & call `GetAllBalances` using the `x/bank` Keeper, then sum -the balances and check that they match the expected total supply. - -## Status - -Accepted. - -## Consequences - -### Positive - -* O(1) reads & writes of balances (with respect to the number of denominations for -which an account has non-zero balances). Note, this does not relate to the actual -I/O cost, rather the total number of direct reads needed. - -### Negative - -* Slightly less efficient reads/writes when reading & writing all balances of a -single account in a transaction. - -### Neutral - -None in particular. - -## References - -* Ref: https://github.com/cosmos/cosmos-sdk/issues/4982 -* Ref: https://github.com/cosmos/cosmos-sdk/issues/5467 -* Ref: https://github.com/cosmos/cosmos-sdk/issues/5492 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-006-secret-store-replacement.md b/versioned_docs/version-0.46/integrate/architecture/adr-006-secret-store-replacement.md deleted file mode 100644 index fe2e25467..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-006-secret-store-replacement.md +++ /dev/null @@ -1,54 +0,0 @@ -# ADR 006: Secret Store Replacement - -## Changelog - -* July 29th, 2019: Initial draft -* September 11th, 2019: Work has started -* November 4th: Cosmos SDK changes merged in -* November 18th: Gaia changes merged in - -## Context - -Currently, a Cosmos SDK application's CLI directory stores key material and metadata in a plain text database in the user’s home directory. Key material is encrypted by a passphrase, protected by bcrypt hashing algorithm. Metadata (e.g. addresses, public keys, key storage details) is available in plain text. - -This is not desirable for a number of reasons. Perhaps the biggest reason is insufficient security protection of key material and metadata. Leaking the plain text allows an attacker to surveil what keys a given computer controls via a number of techniques, like compromised dependencies without any privilege execution. This could be followed by a more targeted attack on a particular user/computer. - -All modern desktop computers OS (Ubuntu, Debian, MacOS, Windows) provide a built-in secret store that is designed to allow applications to store information that is isolated from all other applications and requires passphrase entry to access the data. - -We are seeking solution that provides a common abstraction layer to the many different backends and reasonable fallback for minimal platforms that don’t provide a native secret store. - -## Decision - -We recommend replacing the current Keybase backend based on LevelDB with [Keyring](https://github.com/99designs/keyring) by 99 designs. This application is designed to provide a common abstraction and uniform interface between many secret stores and is used by AWS Vault application by 99-designs application. - -This appears to fulfill the requirement of protecting both key material and metadata from rouge software on a user’s machine. - -## Status - -Accepted - -## Consequences - -### Positive - -Increased safety for users. - -### Negative - -Users must manually migrate. - -Testing against all supported backends is difficult. - -Running tests locally on a Mac require numerous repetitive password entries. - -### Neutral - -{neutral consequences} - -## References - -* #4754 Switch secret store to the keyring secret store (original PR by @poldsam) [__CLOSED__] -* #5029 Add support for github.com/99designs/keyring-backed keybases [__MERGED__] -* #5097 Add keys migrate command [__MERGED__] -* #5180 Drop on-disk keybase in favor of keyring [_PENDING_REVIEW_] -* cosmos/gaia#164 Drop on-disk keybase in favor of keyring (gaia's changes) [_PENDING_REVIEW_] diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-007-specialization-groups.md b/versioned_docs/version-0.46/integrate/architecture/adr-007-specialization-groups.md deleted file mode 100644 index 58f78abf5..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-007-specialization-groups.md +++ /dev/null @@ -1,177 +0,0 @@ -# ADR 007: Specialization Groups - -## Changelog - -* 2019 Jul 31: Initial Draft - -## Context - -This idea was first conceived of in order to fulfill the use case of the -creation of a decentralized Computer Emergency Response Team (dCERT), whose -members would be elected by a governing community and would fulfill the role of -coordinating the community under emergency situations. This thinking -can be further abstracted into the conception of "blockchain specialization -groups". - -The creation of these groups are the beginning of specialization capabilities -within a wider blockchain community which could be used to enable a certain -level of delegated responsibilities. Examples of specialization which could be -beneficial to a blockchain community include: code auditing, emergency response, -code development etc. This type of community organization paves the way for -individual stakeholders to delegate votes by issue type, if in the future -governance proposals include a field for issue type. - -## Decision - -A specialization group can be broadly broken down into the following functions -(herein containing examples): - -* Membership Admittance -* Membership Acceptance -* Membership Revocation - * (probably) Without Penalty - * member steps down (self-Revocation) - * replaced by new member from governance - * (probably) With Penalty - * due to breach of soft-agreement (determined through governance) - * due to breach of hard-agreement (determined by code) -* Execution of Duties - * Special transactions which only execute for members of a specialization - group (for example, dCERT members voting to turn off transaction routes in - an emergency scenario) -* Compensation - * Group compensation (further distribution decided by the specialization group) - * Individual compensation for all constituents of a group from the - greater community - -Membership admittance to a specialization group could take place over a wide -variety of mechanisms. The most obvious example is through a general vote among -the entire community, however in certain systems a community may want to allow -the members already in a specialization group to internally elect new members, -or maybe the community may assign a permission to a particular specialization -group to appoint members to other 3rd party groups. The sky is really the limit -as to how membership admittance can be structured. We attempt to capture -some of these possiblities in a common interface dubbed the `Electionator`. For -its initial implementation as a part of this ADR we recommend that the general -election abstraction (`Electionator`) is provided as well as a basic -implementation of that abstraction which allows for a continuous election of -members of a specialization group. - -``` golang -// The Electionator abstraction covers the concept space for -// a wide variety of election kinds. -type Electionator interface { - - // is the election object accepting votes. - Active() bool - - // functionality to execute for when a vote is cast in this election, here - // the vote field is anticipated to be marshalled into a vote type used - // by an election. - // - // NOTE There are no explicit ids here. Just votes which pertain specifically - // to one electionator. Anyone can create and send a vote to the electionator item - // which will presumably attempt to marshal those bytes into a particular struct - // and apply the vote information in some arbitrary way. There can be multiple - // Electionators within the Cosmos-Hub for multiple specialization groups, votes - // would need to be routed to the Electionator upstream of here. - Vote(addr sdk.AccAddress, vote []byte) - - // here lies all functionality to authenticate and execute changes for - // when a member accepts being elected - AcceptElection(sdk.AccAddress) - - // Register a revoker object - RegisterRevoker(Revoker) - - // No more revokers may be registered after this function is called - SealRevokers() - - // register hooks to call when an election actions occur - RegisterHooks(ElectionatorHooks) - - // query for the current winner(s) of this election based on arbitrary - // election ruleset - QueryElected() []sdk.AccAddress - - // query metadata for an address in the election this - // could include for example position that an address - // is being elected for within a group - // - // this metadata may be directly related to - // voting information and/or privileges enabled - // to members within a group. - QueryMetadata(sdk.AccAddress) []byte -} - -// ElectionatorHooks, once registered with an Electionator, -// trigger execution of relevant interface functions when -// Electionator events occur. -type ElectionatorHooks interface { - AfterVoteCast(addr sdk.AccAddress, vote []byte) - AfterMemberAccepted(addr sdk.AccAddress) - AfterMemberRevoked(addr sdk.AccAddress, cause []byte) -} - -// Revoker defines the function required for a membership revocation rule-set -// used by a specialization group. This could be used to create self revoking, -// and evidence based revoking, etc. Revokers types may be created and -// reused for different election types. -// -// When revoking the "cause" bytes may be arbitrarily marshalled into evidence, -// memos, etc. -type Revoker interface { - RevokeName() string // identifier for this revoker type - RevokeMember(addr sdk.AccAddress, cause []byte) error -} -``` - -Certain level of commonality likely exists between the existing code within -`x/governance` and required functionality of elections. This common -functionality should be abstracted during implementation. Similarly for each -vote implementation client CLI/REST functionality should be abstracted -to be reused for multiple elections. - -The specialization group abstraction firstly extends the `Electionator` -but also further defines traits of the group. - -``` golang -type SpecializationGroup interface { - Electionator - GetName() string - GetDescription() string - - // general soft contract the group is expected - // to fulfill with the greater community - GetContract() string - - // messages which can be executed by the members of the group - Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result - - // logic to be executed at endblock, this may for instance - // include payment of a stipend to the group members - // for participation in the security group. - EndBlocker(ctx sdk.Context) -} -``` - -## Status - -> Proposed - -## Consequences - -### Positive - -* increases specialization capabilities of a blockchain -* improve abstractions in `x/gov/` such that they can be used with specialization groups - -### Negative - -* could be used to increase centralization within a community - -### Neutral - -## References - -* [dCERT ADR](./adr-008-dCERT-group.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-008-dCERT-group.md b/versioned_docs/version-0.46/integrate/architecture/adr-008-dCERT-group.md deleted file mode 100644 index 2b2d2b826..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-008-dCERT-group.md +++ /dev/null @@ -1,171 +0,0 @@ -# ADR 008: Decentralized Computer Emergency Response Team (dCERT) Group - -## Changelog - -* 2019 Jul 31: Initial Draft - -## Context - -In order to reduce the number of parties involved with handling sensitive -information in an emergency scenario, we propose the creation of a -specialization group named The Decentralized Computer Emergency Response Team -(dCERT). Initially this group's role is intended to serve as coordinators -between various actors within a blockchain community such as validators, -bug-hunters, and developers. During a time of crisis, the dCERT group would -aggregate and relay input from a variety of stakeholders to the developers who -are actively devising a patch to the software, this way sensitive information -does not need to be publicly disclosed while some input from the community can -still be gained. - -Additionally, a special privilege is proposed for the dCERT group: the capacity -to "circuit-break" (aka. temporarily disable) a particular message path. Note -that this privilege should be enabled/disabled globally with a governance -parameter such that this privilege could start disabled and later be enabled -through a parameter change proposal, once a dCERT group has been established. - -In the future it is foreseeable that the community may wish to expand the roles -of dCERT with further responsibilities such as the capacity to "pre-approve" a -security update on behalf of the community prior to a full community -wide vote whereby the sensitive information would be revealed prior to a -vulnerability being patched on the live network. - -## Decision - -The dCERT group is proposed to include an implementation of a `SpecializationGroup` -as defined in [ADR 007](./adr-007-specialization-groups.md). This will include the -implementation of: - -* continuous voting -* slashing due to breach of soft contract -* revoking a member due to breach of soft contract -* emergency disband of the entire dCERT group (ex. for colluding maliciously) -* compensation stipend from the community pool or other means decided by - governance - -This system necessitates the following new parameters: - -* blockly stipend allowance per dCERT member -* maximum number of dCERT members -* required staked slashable tokens for each dCERT member -* quorum for suspending a particular member -* proposal wager for disbanding the dCERT group -* stabilization period for dCERT member transition -* circuit break dCERT privileges enabled - -These parameters are expected to be implemented through the param keeper such -that governance may change them at any given point. - -### Continuous Voting Electionator - -An `Electionator` object is to be implemented as continuous voting and with the -following specifications: - -* All delegation addresses may submit votes at any point which updates their - preferred representation on the dCERT group. -* Preferred representation may be arbitrarily split between addresses (ex. 50% - to John, 25% to Sally, 25% to Carol) -* In order for a new member to be added to the dCERT group they must - send a transaction accepting their admission at which point the validity of - their admission is to be confirmed. - * A sequence number is assigned when a member is added to dCERT group. - If a member leaves the dCERT group and then enters back, a new sequence number - is assigned. -* Addresses which control the greatest amount of preferred-representation are - eligible to join the dCERT group (up the _maximum number of dCERT members_). - If the dCERT group is already full and new member is admitted, the existing - dCERT member with the lowest amount of votes is kicked from the dCERT group. - * In the split situation where the dCERT group is full but a vying candidate - has the same amount of vote as an existing dCERT member, the existing - member should maintain its position. - * In the split situation where somebody must be kicked out but the two - addresses with the smallest number of votes have the same number of votes, - the address with the smallest sequence number maintains its position. -* A stabilization period can be optionally included to reduce the - "flip-flopping" of the dCERT membership tail members. If a stabilization - period is provided which is greater than 0, when members are kicked due to - insufficient support, a queue entry is created which documents which member is - to replace which other member. While this entry is in the queue, no new entries - to kick that same dCERT member can be made. When the entry matures at the - duration of the stabilization period, the new member is instantiated, and old - member kicked. - -### Staking/Slashing - -All members of the dCERT group must stake tokens _specifically_ to maintain -eligibility as a dCERT member. These tokens can be staked directly by the vying -dCERT member or out of the good will of a 3rd party (who shall gain no on-chain -benefits for doing so). This staking mechanism should use the existing global -unbonding time of tokens staked for network validator security. A dCERT member -can _only be_ a member if it has the required tokens staked under this -mechanism. If those tokens are unbonded then the dCERT member must be -automatically kicked from the group. - -Slashing of a particular dCERT member due to soft-contract breach should be -performed by governance on a per member basis based on the magnitude of the -breach. The process flow is anticipated to be that a dCERT member is suspended -by the dCERT group prior to being slashed by governance. - -Membership suspension by the dCERT group takes place through a voting procedure -by the dCERT group members. After this suspension has taken place, a governance -proposal to slash the dCERT member must be submitted, if the proposal is not -approved by the time the rescinding member has completed unbonding their -tokens, then the tokens are no longer staked and unable to be slashed. - -Additionally in the case of an emergency situation of a colluding and malicious -dCERT group, the community needs the capability to disband the entire dCERT -group and likely fully slash them. This could be achieved though a special new -proposal type (implemented as a general governance proposal) which would halt -the functionality of the dCERT group until the proposal was concluded. This -special proposal type would likely need to also have a fairly large wager which -could be slashed if the proposal creator was malicious. The reason a large -wager should be required is because as soon as the proposal is made, the -capability of the dCERT group to halt message routes is put on temporarily -suspended, meaning that a malicious actor who created such a proposal could -then potentially exploit a bug during this period of time, with no dCERT group -capable of shutting down the exploitable message routes. - -### dCERT membership transactions - -Active dCERT members - -* change of the description of the dCERT group -* circuit break a message route -* vote to suspend a dCERT member. - -Here circuit-breaking refers to the capability to disable a groups of messages, -This could for instance mean: "disable all staking-delegation messages", or -"disable all distribution messages". This could be accomplished by verifying -that the message route has not been "circuit-broken" at CheckTx time (in -`baseapp/baseapp.go`). - -"unbreaking" a circuit is anticipated only to occur during a hard fork upgrade -meaning that no capability to unbreak a message route on a live chain is -required. - -Note also, that if there was a problem with governance voting (for instance a -capability to vote many times) then governance would be broken and should be -halted with this mechanism, it would be then up to the validator set to -coordinate and hard-fork upgrade to a patched version of the software where -governance is re-enabled (and fixed). If the dCERT group abuses this privilege -they should all be severely slashed. - -## Status - -> Proposed - -## Consequences - -### Positive - -* Potential to reduces the number of parties to coordinate with during an emergency -* Reduction in possibility of disclosing sensitive information to malicious parties - -### Negative - -* Centralization risks - -### Neutral - -## References - - [Specialization Groups ADR](./adr-007-specialization-groups.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-009-evidence-module.md b/versioned_docs/version-0.46/integrate/architecture/adr-009-evidence-module.md deleted file mode 100644 index ded04a142..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-009-evidence-module.md +++ /dev/null @@ -1,182 +0,0 @@ -# ADR 009: Evidence Module - -## Changelog - -* 2019 July 31: Initial draft -* 2019 October 24: Initial implementation - -## Status - -Accepted - -## Context - -In order to support building highly secure, robust and interoperable blockchain -applications, it is vital for the Cosmos SDK to expose a mechanism in which arbitrary -evidence can be submitted, evaluated and verified resulting in some agreed upon -penalty for any misbehavior committed by a validator, such as equivocation (double-voting), -signing when unbonded, signing an incorrect state transition (in the future), etc. -Furthermore, such a mechanism is paramount for any -[IBC](https://github.com/cosmos/ics/blob/master/ibc/2_IBC_ARCHITECTURE.md) or -cross-chain validation protocol implementation in order to support the ability -for any misbehavior to be relayed back from a collateralized chain to a primary -chain so that the equivocating validator(s) can be slashed. - -## Decision - -We will implement an evidence module in the Cosmos SDK supporting the following -functionality: - -* Provide developers with the abstractions and interfaces necessary to define - custom evidence messages, message handlers, and methods to slash and penalize - accordingly for misbehavior. -* Support the ability to route evidence messages to handlers in any module to - determine the validity of submitted misbehavior. -* Support the ability, through governance, to modify slashing penalties of any - evidence type. -* Querier implementation to support querying params, evidence types, params, and - all submitted valid misbehavior. - -### Types - -First, we define the `Evidence` interface type. The `x/evidence` module may implement -its own types that can be used by many chains (e.g. `CounterFactualEvidence`). -In addition, other modules may implement their own `Evidence` types in a similar -manner in which governance is extensible. It is important to note any concrete -type implementing the `Evidence` interface may include arbitrary fields such as -an infraction time. We want the `Evidence` type to remain as flexible as possible. - -When submitting evidence to the `x/evidence` module, the concrete type must provide -the validator's consensus address, which should be known by the `x/slashing` -module (assuming the infraction is valid), the height at which the infraction -occurred and the validator's power at same height in which the infraction occurred. - -```go -type Evidence interface { - Route() string - Type() string - String() string - Hash() HexBytes - ValidateBasic() error - - // The consensus address of the malicious validator at time of infraction - GetConsensusAddress() ConsAddress - - // Height at which the infraction occurred - GetHeight() int64 - - // The total power of the malicious validator at time of infraction - GetValidatorPower() int64 - - // The total validator set power at time of infraction - GetTotalPower() int64 -} -``` - -### Routing & Handling - -Each `Evidence` type must map to a specific unique route and be registered with -the `x/evidence` module. It accomplishes this through the `Router` implementation. - -```go -type Router interface { - AddRoute(r string, h Handler) Router - HasRoute(r string) bool - GetRoute(path string) Handler - Seal() -} -``` - -Upon successful routing through the `x/evidence` module, the `Evidence` type -is passed through a `Handler`. This `Handler` is responsible for executing all -corresponding business logic necessary for verifying the evidence as valid. In -addition, the `Handler` may execute any necessary slashing and potential jailing. -Since slashing fractions will typically result from some form of static functions, -allow the `Handler` to do this provides the greatest flexibility. An example could -be `k * evidence.GetValidatorPower()` where `k` is an on-chain parameter controlled -by governance. The `Evidence` type should provide all the external information -necessary in order for the `Handler` to make the necessary state transitions. -If no error is returned, the `Evidence` is considered valid. - -```go -type Handler func(Context, Evidence) error -``` - -### Submission - -`Evidence` is submitted through a `MsgSubmitEvidence` message type which is internally -handled by the `x/evidence` module's `SubmitEvidence`. - -```go -type MsgSubmitEvidence struct { - Evidence -} - -func handleMsgSubmitEvidence(ctx Context, keeper Keeper, msg MsgSubmitEvidence) Result { - if err := keeper.SubmitEvidence(ctx, msg.Evidence); err != nil { - return err.Result() - } - - // emit events... - - return Result{ - // ... - } -} -``` - -The `x/evidence` module's keeper is responsible for matching the `Evidence` against -the module's router and invoking the corresponding `Handler` which may include -slashing and jailing the validator. Upon success, the submitted evidence is persisted. - -```go -func (k Keeper) SubmitEvidence(ctx Context, evidence Evidence) error { - handler := keeper.router.GetRoute(evidence.Route()) - if err := handler(ctx, evidence); err != nil { - return ErrInvalidEvidence(keeper.codespace, err) - } - - keeper.setEvidence(ctx, evidence) - return nil -} -``` - -### Genesis - -Finally, we need to represent the genesis state of the `x/evidence` module. The -module only needs a list of all submitted valid infractions and any necessary params -for which the module needs in order to handle submitted evidence. The `x/evidence` -module will naturally define and route native evidence types for which it'll most -likely need slashing penalty constants for. - -```go -type GenesisState struct { - Params Params - Infractions []Evidence -} -``` - -## Consequences - -### Positive - -* Allows the state machine to process misbehavior submitted on-chain and penalize - validators based on agreed upon slashing parameters. -* Allows evidence types to be defined and handled by any module. This further allows - slashing and jailing to be defined by more complex mechanisms. -* Does not solely rely on Tendermint to submit evidence. - -### Negative - -* No easy way to introduce new evidence types through governance on a live chain - due to the inability to introduce the new evidence type's corresponding handler - -### Neutral - -* Should we persist infractions indefinitely? Or should we rather rely on events? - -## References - -* [ICS](https://github.com/cosmos/ics) -* [IBC Architecture](https://github.com/cosmos/ics/blob/master/ibc/1_IBC_ARCHITECTURE.md) -* [Tendermint Fork Accountability](https://github.com/tendermint/spec/blob/7b3138e69490f410768d9b1ffc7a17abc23ea397/spec/consensus/fork-accountability.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-010-modular-antehandler.md b/versioned_docs/version-0.46/integrate/architecture/adr-010-modular-antehandler.md deleted file mode 100644 index 386af1a77..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-010-modular-antehandler.md +++ /dev/null @@ -1,290 +0,0 @@ -# ADR 010: Modular AnteHandler - -## Changelog - -* 2019 Aug 31: Initial draft -* 2021 Sep 14: Superseded by ADR-045 - -## Status - -SUPERSEDED by ADR-045 - -## Context - -The current AnteHandler design allows users to either use the default AnteHandler provided in `x/auth` or to build their own AnteHandler from scratch. Ideally AnteHandler functionality is split into multiple, modular functions that can be chained together along with custom ante-functions so that users do not have to rewrite common antehandler logic when they want to implement custom behavior. - -For example, let's say a user wants to implement some custom signature verification logic. In the current codebase, the user would have to write their own Antehandler from scratch largely reimplementing much of the same code and then set their own custom, monolithic antehandler in the baseapp. Instead, we would like to allow users to specify custom behavior when necessary and combine them with default ante-handler functionality in a way that is as modular and flexible as possible. - -## Proposals - -### Per-Module AnteHandler - -One approach is to use the [ModuleManager](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/types/module) and have each module implement its own antehandler if it requires custom antehandler logic. The ModuleManager can then be passed in an AnteHandler order in the same way it has an order for BeginBlockers and EndBlockers. The ModuleManager returns a single AnteHandler function that will take in a tx and run each module's `AnteHandle` in the specified order. The module manager's AnteHandler is set as the baseapp's AnteHandler. - -Pros: - -1. Simple to implement -2. Utilizes the existing ModuleManager architecture - -Cons: - -1. Improves granularity but still cannot get more granular than a per-module basis. e.g. If auth's `AnteHandle` function is in charge of validating memo and signatures, users cannot swap the signature-checking functionality while keeping the rest of auth's `AnteHandle` functionality. -2. Module AnteHandler are run one after the other. There is no way for one AnteHandler to wrap or "decorate" another. - -### Decorator Pattern - -The [weave project](https://github.com/iov-one/weave) achieves AnteHandler modularity through the use of a decorator pattern. The interface is designed as follows: - -```go -// Decorator wraps a Handler to provide common functionality -// like authentication, or fee-handling, to many Handlers -type Decorator interface { - Check(ctx Context, store KVStore, tx Tx, next Checker) (*CheckResult, error) - Deliver(ctx Context, store KVStore, tx Tx, next Deliverer) (*DeliverResult, error) -} -``` - -Each decorator works like a modularized Cosmos SDK antehandler function, but it can take in a `next` argument that may be another decorator or a Handler (which does not take in a next argument). These decorators can be chained together, one decorator being passed in as the `next` argument of the previous decorator in the chain. The chain ends in a Router which can take a tx and route to the appropriate msg handler. - -A key benefit of this approach is that one Decorator can wrap its internal logic around the next Checker/Deliverer. A weave Decorator may do the following: - -```go -// Example Decorator's Deliver function -func (example Decorator) Deliver(ctx Context, store KVStore, tx Tx, next Deliverer) { - // Do some pre-processing logic - - res, err := next.Deliver(ctx, store, tx) - - // Do some post-processing logic given the result and error -} -``` - -Pros: - -1. Weave Decorators can wrap over the next decorator/handler in the chain. The ability to both pre-process and post-process may be useful in certain settings. -2. Provides a nested modular structure that isn't possible in the solution above, while also allowing for a linear one-after-the-other structure like the solution above. - -Cons: - -1. It is hard to understand at first glance the state updates that would occur after a Decorator runs given the `ctx`, `store`, and `tx`. A Decorator can have an arbitrary number of nested Decorators being called within its function body, each possibly doing some pre- and post-processing before calling the next decorator on the chain. Thus to understand what a Decorator is doing, one must also understand what every other decorator further along the chain is also doing. This can get quite complicated to understand. A linear, one-after-the-other approach while less powerful, may be much easier to reason about. - -### Chained Micro-Functions - -The benefit of Weave's approach is that the Decorators can be very concise, which when chained together allows for maximum customizability. However, the nested structure can get quite complex and thus hard to reason about. - -Another approach is to split the AnteHandler functionality into tightly scoped "micro-functions", while preserving the one-after-the-other ordering that would come from the ModuleManager approach. - -We can then have a way to chain these micro-functions so that they run one after the other. Modules may define multiple ante micro-functions and then also provide a default per-module AnteHandler that implements a default, suggested order for these micro-functions. - -Users can order the AnteHandlers easily by simply using the ModuleManager. The ModuleManager will take in a list of AnteHandlers and return a single AnteHandler that runs each AnteHandler in the order of the list provided. If the user is comfortable with the default ordering of each module, this is as simple as providing a list with each module's antehandler (exactly the same as BeginBlocker and EndBlocker). - -If however, users wish to change the order or add, modify, or delete ante micro-functions in anyway; they can always define their own ante micro-functions and add them explicitly to the list that gets passed into module manager. - -#### Default Workflow - -This is an example of a user's AnteHandler if they choose not to make any custom micro-functions. - -##### Cosmos SDK code - -```go -// Chains together a list of AnteHandler micro-functions that get run one after the other. -// Returned AnteHandler will abort on first error. -func Chainer(order []AnteHandler) AnteHandler { - return func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - for _, ante := range order { - ctx, err := ante(ctx, tx, simulate) - if err != nil { - return ctx, err - } - } - return ctx, err - } -} -``` - -```go -// AnteHandler micro-function to verify signatures -func VerifySignatures(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // verify signatures - // Returns InvalidSignature Result and abort=true if sigs invalid - // Return OK result and abort=false if sigs are valid -} - -// AnteHandler micro-function to validate memo -func ValidateMemo(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // validate memo -} - -// Auth defines its own default ante-handler by chaining its micro-functions in a recommended order -AuthModuleAnteHandler := Chainer([]AnteHandler{VerifySignatures, ValidateMemo}) -``` - -```go -// Distribution micro-function to deduct fees from tx -func DeductFees(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // Deduct fees from tx - // Abort if insufficient funds in account to pay for fees -} - -// Distribution micro-function to check if fees > mempool parameter -func CheckMempoolFees(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // If CheckTx: Abort if the fees are less than the mempool's minFee parameter -} - -// Distribution defines its own default ante-handler by chaining its micro-functions in a recommended order -DistrModuleAnteHandler := Chainer([]AnteHandler{CheckMempoolFees, DeductFees}) -``` - -```go -type ModuleManager struct { - // other fields - AnteHandlerOrder []AnteHandler -} - -func (mm ModuleManager) GetAnteHandler() AnteHandler { - retun Chainer(mm.AnteHandlerOrder) -} -``` - -##### User Code - -```go -// Note: Since user is not making any custom modifications, we can just SetAnteHandlerOrder with the default AnteHandlers provided by each module in our preferred order -moduleManager.SetAnteHandlerOrder([]AnteHandler(AuthModuleAnteHandler, DistrModuleAnteHandler)) - -app.SetAnteHandler(mm.GetAnteHandler()) -``` - -#### Custom Workflow - -This is an example workflow for a user that wants to implement custom antehandler logic. In this example, the user wants to implement custom signature verification and change the order of antehandler so that validate memo runs before signature verification. - -##### User Code - -```go -// User can implement their own custom signature verification antehandler micro-function -func CustomSigVerify(ctx Context, tx Tx, simulate bool) (newCtx Context, err error) { - // do some custom signature verification logic -} -``` - -```go -// Micro-functions allow users to change order of when they get executed, and swap out default ante-functionality with their own custom logic. -// Note that users can still chain the default distribution module handler, and auth micro-function along with their custom ante function -moduleManager.SetAnteHandlerOrder([]AnteHandler(ValidateMemo, CustomSigVerify, DistrModuleAnteHandler)) -``` - -Pros: - -1. Allows for ante functionality to be as modular as possible. -2. For users that do not need custom ante-functionality, there is little difference between how antehandlers work and how BeginBlock and EndBlock work in ModuleManager. -3. Still easy to understand - -Cons: - -1. Cannot wrap antehandlers with decorators like you can with Weave. - -### Simple Decorators - -This approach takes inspiration from Weave's decorator design while trying to minimize the number of breaking changes to the Cosmos SDK and maximizing simplicity. Like Weave decorators, this approach allows one `AnteDecorator` to wrap the next AnteHandler to do pre- and post-processing on the result. This is useful since decorators can do defer/cleanups after an AnteHandler returns as well as perform some setup beforehand. Unlike Weave decorators, these `AnteDecorator` functions can only wrap over the AnteHandler rather than the entire handler execution path. This is deliberate as we want decorators from different modules to perform authentication/validation on a `tx`. However, we do not want decorators being capable of wrapping and modifying the results of a `MsgHandler`. - -In addition, this approach will not break any core Cosmos SDK API's. Since we preserve the notion of an AnteHandler and still set a single AnteHandler in baseapp, the decorator is simply an additional approach available for users that desire more customization. The API of modules (namely `x/auth`) may break with this approach, but the core API remains untouched. - -Allow Decorator interface that can be chained together to create a Cosmos SDK AnteHandler. - -This allows users to choose between implementing an AnteHandler by themselves and setting it in the baseapp, or use the decorator pattern to chain their custom decorators with the Cosmos SDK provided decorators in the order they wish. - -```go -// An AnteDecorator wraps an AnteHandler, and can do pre- and post-processing on the next AnteHandler -type AnteDecorator interface { - AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) -} -``` - -```go -// ChainAnteDecorators will recursively link all of the AnteDecorators in the chain and return a final AnteHandler function -// This is done to preserve the ability to set a single AnteHandler function in the baseapp. -func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { - if len(chain) == 1 { - return func(ctx Context, tx Tx, simulate bool) { - chain[0].AnteHandle(ctx, tx, simulate, nil) - } - } - return func(ctx Context, tx Tx, simulate bool) { - chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:])) - } -} -``` - -#### Example Code - -Define AnteDecorator functions - -```go -// Setup GasMeter, catch OutOfGasPanic and handle appropriately -type SetUpContextDecorator struct{} - -func (sud SetUpContextDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) { - ctx.GasMeter = NewGasMeter(tx.Gas) - - defer func() { - // recover from OutOfGas panic and handle appropriately - } - - return next(ctx, tx, simulate) -} - -// Signature Verification decorator. Verify Signatures and move on -type SigVerifyDecorator struct{} - -func (svd SigVerifyDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) { - // verify sigs. Return error if invalid - - // call next antehandler if sigs ok - return next(ctx, tx, simulate) -} - -// User-defined Decorator. Can choose to pre- and post-process on AnteHandler -type UserDefinedDecorator struct{ - // custom fields -} - -func (udd UserDefinedDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) { - // pre-processing logic - - ctx, err = next(ctx, tx, simulate) - - // post-processing logic -} -``` - -Link AnteDecorators to create a final AnteHandler. Set this AnteHandler in baseapp. - -```go -// Create final antehandler by chaining the decorators together -antehandler := ChainAnteDecorators(NewSetUpContextDecorator(), NewSigVerifyDecorator(), NewUserDefinedDecorator()) - -// Set chained Antehandler in the baseapp -bapp.SetAnteHandler(antehandler) -``` - -Pros: - -1. Allows one decorator to pre- and post-process the next AnteHandler, similar to the Weave design. -2. Do not need to break baseapp API. Users can still set a single AnteHandler if they choose. - -Cons: - -1. Decorator pattern may have a deeply nested structure that is hard to understand, this is mitigated by having the decorator order explicitly listed in the `ChainAnteDecorators` function. -2. Does not make use of the ModuleManager design. Since this is already being used for BeginBlocker/EndBlocker, this proposal seems unaligned with that design pattern. - -## Consequences - -Since pros and cons are written for each approach, it is omitted from this section - -## References - -* [#4572](https://github.com/cosmos/cosmos-sdk/issues/4572): Modular AnteHandler Issue -* [#4582](https://github.com/cosmos/cosmos-sdk/pull/4583): Initial Implementation of Per-Module AnteHandler Approach -* [Weave Decorator Code](https://github.com/iov-one/weave/blob/master/handler.go#L35) -* [Weave Design Videos](https://vimeo.com/showcase/6189877) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-011-generalize-genesis-accounts.md b/versioned_docs/version-0.46/integrate/architecture/adr-011-generalize-genesis-accounts.md deleted file mode 100644 index 92a704ba6..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-011-generalize-genesis-accounts.md +++ /dev/null @@ -1,170 +0,0 @@ -# ADR 011: Generalize Genesis Accounts - -## Changelog - -* 2019-08-30: initial draft - -## Context - -Currently, the Cosmos SDK allows for custom account types; the `auth` keeper stores any type fulfilling its `Account` interface. However `auth` does not handle exporting or loading accounts to/from a genesis file, this is done by `genaccounts`, which only handles one of 4 concrete account types (`BaseAccount`, `ContinuousVestingAccount`, `DelayedVestingAccount` and `ModuleAccount`). - -Projects desiring to use custom accounts (say custom vesting accounts) need to fork and modify `genaccounts`. - -## Decision - -In summary, we will (un)marshal all accounts (interface types) directly using amino, rather than converting to `genaccounts`’s `GenesisAccount` type. Since doing this removes the majority of `genaccounts`'s code, we will merge `genaccounts` into `auth`. Marshalled accounts will be stored in `auth`'s genesis state. - -Detailed changes: - -### 1) (Un)Marshal accounts directly using amino - -The `auth` module's `GenesisState` gains a new field `Accounts`. Note these aren't of type `exported.Account` for reasons outlined in section 3. - -```go -// GenesisState - all auth state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - Accounts []GenesisAccount `json:"accounts" yaml:"accounts"` -} -``` - -Now `auth`'s `InitGenesis` and `ExportGenesis` (un)marshal accounts as well as the defined params. - -```go -// InitGenesis - Init store state from genesis data -func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) { - ak.SetParams(ctx, data.Params) - // load the accounts - for _, a := range data.Accounts { - acc := ak.NewAccount(ctx, a) // set account number - ak.SetAccount(ctx, acc) - } -} - -// ExportGenesis returns a GenesisState for a given context and keeper -func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState { - params := ak.GetParams(ctx) - - var genAccounts []exported.GenesisAccount - ak.IterateAccounts(ctx, func(account exported.Account) bool { - genAccount := account.(exported.GenesisAccount) - genAccounts = append(genAccounts, genAccount) - return false - }) - - return NewGenesisState(params, genAccounts) -} -``` - -### 2) Register custom account types on the `auth` codec - -The `auth` codec must have all custom account types registered to marshal them. We will follow the pattern established in `gov` for proposals. - -An example custom account definition: - -```go -import authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - -// Register the module account type with the auth module codec so it can decode module accounts stored in a genesis file -func init() { - authtypes.RegisterAccountTypeCodec(ModuleAccount{}, "cosmos-sdk/ModuleAccount") -} - -type ModuleAccount struct { - ... -``` - -The `auth` codec definition: - -```go -var ModuleCdc *codec.LegacyAmino - -func init() { - ModuleCdc = codec.NewLegacyAmino() - // register module msg's and Account interface - ... - // leave the codec unsealed -} - -// RegisterAccountTypeCodec registers an external account type defined in another module for the internal ModuleCdc. -func RegisterAccountTypeCodec(o interface{}, name string) { - ModuleCdc.RegisterConcrete(o, name, nil) -} -``` - -### 3) Genesis validation for custom account types - -Modules implement a `ValidateGenesis` method. As `auth` does not know of account implementations, accounts will need to validate themselves. - -We will unmarshal accounts into a `GenesisAccount` interface that includes a `Validate` method. - -```go -type GenesisAccount interface { - exported.Account - Validate() error -} -``` - -Then the `auth` `ValidateGenesis` function becomes: - -```go -// ValidateGenesis performs basic validation of auth genesis data returning an -// error for any failed validation criteria. -func ValidateGenesis(data GenesisState) error { - // Validate params - ... - - // Validate accounts - addrMap := make(map[string]bool, len(data.Accounts)) - for _, acc := range data.Accounts { - - // check for duplicated accounts - addrStr := acc.GetAddress().String() - if _, ok := addrMap[addrStr]; ok { - return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) - } - addrMap[addrStr] = true - - // check account specific validation - if err := acc.Validate(); err != nil { - return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error()) - } - - } - return nil -} -``` - -### 4) Move add-genesis-account cli to `auth` - -The `genaccounts` module contains a cli command to add base or vesting accounts to a genesis file. - -This will be moved to `auth`. We will leave it to projects to write their own commands to add custom accounts. An extensible cli handler, similar to `gov`, could be created but it is not worth the complexity for this minor use case. - -### 5) Update module and vesting accounts - -Under the new scheme, module and vesting account types need some minor updates: - -* Type registration on `auth`'s codec (shown above) -* A `Validate` method for each `Account` concrete type - -## Status - -Proposed - -## Consequences - -### Positive - -* custom accounts can be used without needing to fork `genaccounts` -* reduction in lines of code - -### Negative - -### Neutral - -* `genaccounts` module no longer exists -* accounts in genesis files are stored under `accounts` in `auth` rather than in the `genaccounts` module. --`add-genesis-account` cli command now in `auth` - -## References diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-012-state-accessors.md b/versioned_docs/version-0.46/integrate/architecture/adr-012-state-accessors.md deleted file mode 100644 index 93600000f..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-012-state-accessors.md +++ /dev/null @@ -1,155 +0,0 @@ -# ADR 012: State Accessors - -## Changelog - -* 2019 Sep 04: Initial draft - -## Context - -Cosmos SDK modules currently use the `KVStore` interface and `Codec` to access their respective state. While -this provides a large degree of freedom to module developers, it is hard to modularize and the UX is -mediocre. - -First, each time a module tries to access the state, it has to marshal the value and set or get the -value and finally unmarshal. Usually this is done by declaring `Keeper.GetXXX` and `Keeper.SetXXX` functions, -which are repetitive and hard to maintain. - -Second, this makes it harder to align with the object capability theorem: the right to access the -state is defined as a `StoreKey`, which gives full access on the entire Merkle tree, so a module cannot -send the access right to a specific key-value pair (or a set of key-value pairs) to another module safely. - -Finally, because the getter/setter functions are defined as methods of a module's `Keeper`, the reviewers -have to consider the whole Merkle tree space when they reviewing a function accessing any part of the state. -There is no static way to know which part of the state that the function is accessing (and which is not). - -## Decision - -We will define a type named `Value`: - -```go -type Value struct { - m Mapping - key []byte -} -``` - -The `Value` works as a reference for a key-value pair in the state, where `Value.m` defines the key-value -space it will access and `Value.key` defines the exact key for the reference. - -We will define a type named `Mapping`: - -```go -type Mapping struct { - storeKey sdk.StoreKey - cdc *codec.LegacyAmino - prefix []byte -} -``` - -The `Mapping` works as a reference for a key-value space in the state, where `Mapping.storeKey` defines -the IAVL (sub-)tree and `Mapping.prefix` defines the optional subspace prefix. - -We will define the following core methods for the `Value` type: - -```go -// Get and unmarshal stored data, noop if not exists, panic if cannot unmarshal -func (Value) Get(ctx Context, ptr interface{}) {} - -// Get and unmarshal stored data, return error if not exists or cannot unmarshal -func (Value) GetSafe(ctx Context, ptr interface{}) {} - -// Get stored data as raw byte slice -func (Value) GetRaw(ctx Context) []byte {} - -// Marshal and set a raw value -func (Value) Set(ctx Context, o interface{}) {} - -// Check if a raw value exists -func (Value) Exists(ctx Context) bool {} - -// Delete a raw value value -func (Value) Delete(ctx Context) {} -``` - -We will define the following core methods for the `Mapping` type: - -```go -// Constructs key-value pair reference corresponding to the key argument in the Mapping space -func (Mapping) Value(key []byte) Value {} - -// Get and unmarshal stored data, noop if not exists, panic if cannot unmarshal -func (Mapping) Get(ctx Context, key []byte, ptr interface{}) {} - -// Get and unmarshal stored data, return error if not exists or cannot unmarshal -func (Mapping) GetSafe(ctx Context, key []byte, ptr interface{}) - -// Get stored data as raw byte slice -func (Mapping) GetRaw(ctx Context, key []byte) []byte {} - -// Marshal and set a raw value -func (Mapping) Set(ctx Context, key []byte, o interface{}) {} - -// Check if a raw value exists -func (Mapping) Has(ctx Context, key []byte) bool {} - -// Delete a raw value value -func (Mapping) Delete(ctx Context, key []byte) {} -``` - -Each method of the `Mapping` type that is passed the arguments `ctx`, `key`, and `args...` will proxy -the call to `Mapping.Value(key)` with arguments `ctx` and `args...`. - -In addition, we will define and provide a common set of types derived from the `Value` type: - -```go -type Boolean struct { Value } -type Enum struct { Value } -type Integer struct { Value; enc IntEncoding } -type String struct { Value } -// ... -``` - -Where the encoding schemes can be different, `o` arguments in core methods are typed, and `ptr` arguments -in core methods are replaced by explicit return types. - -Finally, we will define a family of types derived from the `Mapping` type: - -```go -type Indexer struct { - m Mapping - enc IntEncoding -} -``` - -Where the `key` argument in core method is typed. - -Some of the properties of the accessor types are: - -* State access happens only when a function which takes a `Context` as an argument is invoked -* Accessor type structs give rights to access the state only that the struct is referring, no other -* Marshalling/Unmarshalling happens implicitly within the core methods - -## Status - -Proposed - -## Consequences - -### Positive - -* Serialization will be done automatically -* Shorter code size, less boilerplate, better UX -* References to the state can be transferred safely -* Explicit scope of accessing - -### Negative - -* Serialization format will be hidden -* Different architecture from the current, but the use of accessor types can be opt-in -* Type-specific types (e.g. `Boolean` and `Integer`) have to be defined manually - -### Neutral - -## References - -* [#4554](https://github.com/cosmos/cosmos-sdk/issues/4554) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-013-metrics.md b/versioned_docs/version-0.46/integrate/architecture/adr-013-metrics.md deleted file mode 100644 index 33849b56c..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-013-metrics.md +++ /dev/null @@ -1,157 +0,0 @@ -# ADR 013: Observability - -## Changelog - -* 20-01-2020: Initial Draft - -## Status - -Proposed - -## Context - -Telemetry is paramount into debugging and understanding what the application is doing and how it is -performing. We aim to expose metrics from modules and other core parts of the Cosmos SDK. - -In addition, we should aim to support multiple configurable sinks that an operator may choose from. -By default, when telemetry is enabled, the application should track and expose metrics that are -stored in-memory. The operator may choose to enable additional sinks, where we support only -[Prometheus](https://prometheus.io/) for now, as it's battle-tested, simple to setup, open source, -and is rich with ecosystem tooling. - -We must also aim to integrate metrics into the Cosmos SDK in the most seamless way possible such that -metrics may be added or removed at will and without much friction. To do this, we will use the -[go-metrics](https://github.com/armon/go-metrics) library. - -Finally, operators may enable telemetry along with specific configuration options. If enabled, metrics -will be exposed via `/metrics?format={text|prometheus}` via the API server. - -## Decision - -We will add an additional configuration block to `app.toml` that defines telemetry settings: - -```toml -############################################################################### -### Telemetry Configuration ### -############################################################################### - -[telemetry] - -# Prefixed with keys to separate services -service-name = {{ .Telemetry.ServiceName }} - -# Enabled enables the application telemetry functionality. When enabled, -# an in-memory sink is also enabled by default. Operators may also enabled -# other sinks such as Prometheus. -enabled = {{ .Telemetry.Enabled }} - -# Enable prefixing gauge values with hostname -enable-hostname = {{ .Telemetry.EnableHostname }} - -# Enable adding hostname to labels -enable-hostname-label = {{ .Telemetry.EnableHostnameLabel }} - -# Enable adding service to labels -enable-service-label = {{ .Telemetry.EnableServiceLabel }} - -# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. -prometheus-retention-time = {{ .Telemetry.PrometheusRetentionTime }} -``` - -The given configuration allows for two sinks -- in-memory and Prometheus. We create a `Metrics` -type that performs all the bootstrapping for the operator, so capturing metrics becomes seamless. - -```go -// Metrics defines a wrapper around application telemetry functionality. It allows -// metrics to be gathered at any point in time. When creating a Metrics object, -// internally, a global metrics is registered with a set of sinks as configured -// by the operator. In addition to the sinks, when a process gets a SIGUSR1, a -// dump of formatted recent metrics will be sent to STDERR. -type Metrics struct { - memSink *metrics.InmemSink - prometheusEnabled bool -} - -// Gather collects all registered metrics and returns a GatherResponse where the -// metrics are encoded depending on the type. Metrics are either encoded via -// Prometheus or JSON if in-memory. -func (m *Metrics) Gather(format string) (GatherResponse, error) { - switch format { - case FormatPrometheus: - return m.gatherPrometheus() - - case FormatText: - return m.gatherGeneric() - - case FormatDefault: - return m.gatherGeneric() - - default: - return GatherResponse{}, fmt.Errorf("unsupported metrics format: %s", format) - } -} -``` - -In addition, `Metrics` allows us to gather the current set of metrics at any given point in time. An -operator may also choose to send a signal, SIGUSR1, to dump and print formatted metrics to STDERR. - -During an application's bootstrapping and construction phase, if `Telemetry.Enabled` is `true`, the -API server will create an instance of a reference to `Metrics` object and will register a metrics -handler accordingly. - -```go -func (s *Server) Start(cfg config.Config) error { - // ... - - if cfg.Telemetry.Enabled { - m, err := telemetry.New(cfg.Telemetry) - if err != nil { - return err - } - - s.metrics = m - s.registerMetrics() - } - - // ... -} - -func (s *Server) registerMetrics() { - metricsHandler := func(w http.ResponseWriter, r *http.Request) { - format := strings.TrimSpace(r.FormValue("format")) - - gr, err := s.metrics.Gather(format) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to gather metrics: %s", err)) - return - } - - w.Header().Set("Content-Type", gr.ContentType) - _, _ = w.Write(gr.Metrics) - } - - s.Router.HandleFunc("/metrics", metricsHandler).Methods("GET") -} -``` - -Application developers may track counters, gauges, summaries, and key/value metrics. There is no -additional lifting required by modules to leverage profiling metrics. To do so, it's as simple as: - -```go -func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { - defer metrics.MeasureSince(time.Now(), "MintCoins") - // ... -} -``` - -## Consequences - -### Positive - -* Exposure into the performance and behavior of an application - -### Negative - -### Neutral - -## References diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-014-proportional-slashing.md b/versioned_docs/version-0.46/integrate/architecture/adr-014-proportional-slashing.md deleted file mode 100644 index 63cd04dee..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-014-proportional-slashing.md +++ /dev/null @@ -1,85 +0,0 @@ -# ADR 14: Proportional Slashing - -## Changelog - -* 2019-10-15: Initial draft -* 2020-05-25: Removed correlation root slashing -* 2020-07-01: Updated to include S-curve function instead of linear - -## Context - -In Proof of Stake-based chains, centralization of consensus power amongst a small set of validators can cause harm to the network due to increased risk of censorship, liveness failure, fork attacks, etc. However, while this centralization causes a negative externality to the network, it is not directly felt by the delegators contributing towards delegating towards already large validators. We would like a way to pass on the negative externality cost of centralization onto those large validators and their delegators. - -## Decision - -### Design - -To solve this problem, we will implement a procedure called Proportional Slashing. The desire is that the larger a validator is, the more they should be slashed. The first naive attempt is to make a validator's slash percent proportional to their share of consensus voting power. - -```text -slash_amount = k * power // power is the faulting validator's voting power and k is some on-chain constant -``` - -However, this will incentivize validators with large amounts of stake to split up their voting power amongst accounts (sybil attack), so that if they fault, they all get slashed at a lower percent. The solution to this is to take into account not just a validator's own voting percentage, but also the voting percentage of all the other validators who get slashed in a specified time frame. - -```text -slash_amount = k * (power_1 + power_2 + ... + power_n) // where power_i is the voting power of the ith validator faulting in the specified time frame and k is some on-chain constant -``` - -Now, if someone splits a validator of 10% into two validators of 5% each which both fault, then they both fault in the same time frame, they both will get slashed at the sum 10% amount. - -However in practice, we likely don't want a linear relation between amount of stake at fault, and the percentage of stake to slash. In particular, solely 5% of stake double signing effectively did nothing to majorly threaten security, whereas 30% of stake being at fault clearly merits a large slashing factor, due to being very close to the point at which Tendermint security is threatened. A linear relation would require a factor of 6 gap between these two, whereas the difference in risk posed to the network is much larger. We propose using S-curves (formally [logistic functions](https://en.wikipedia.org/wiki/Logistic_function) to solve this). S-Curves capture the desired criterion quite well. They allow the slashing factor to be minimal for small values, and then grow very rapidly near some threshold point where the risk posed becomes notable. - -#### Parameterization - -This requires parameterizing a logistic function. It is very well understood how to parameterize this. It has four parameters: - -1) A minimum slashing factor -2) A maximum slashing factor -3) The inflection point of the S-curve (essentially where do you want to center the S) -4) The rate of growth of the S-curve (How elongated is the S) - -#### Correlation across non-sybil validators - -One will note, that this model doesn't differentiate between multiple validators run by the same operators vs validators run by different operators. This can be seen as an additional benefit in fact. It incentivizes validators to differentiate their setups from other validators, to avoid having correlated faults with them or else they risk a higher slash. So for example, operators should avoid using the same popular cloud hosting platforms or using the same Staking as a Service providers. This will lead to a more resilient and decentralized network. - -#### Griefing - -Griefing, the act of intentionally getting oneself slashed in order to make another's slash worse, could be a concern here. However, using the protocol described here, the attacker also gets equally impacted by the grief as the victim, so it would not provide much benefit to the griefer. - -### Implementation - -In the slashing module, we will add two queues that will track all of the recent slash events. For double sign faults, we will define "recent slashes" as ones that have occurred within the last `unbonding period`. For liveness faults, we will define "recent slashes" as ones that have occurred withing the last `jail period`. - -```go -type SlashEvent struct { - Address sdk.ValAddress - ValidatorVotingPercent sdk.Dec - SlashedSoFar sdk.Dec -} -``` - -These slash events will be pruned from the queue once they are older than their respective "recent slash period". - -Whenever a new slash occurs, a `SlashEvent` struct is created with the faulting validator's voting percent and a `SlashedSoFar` of 0. Because recent slash events are pruned before the unbonding period and unjail period expires, it should not be possible for the same validator to have multiple SlashEvents in the same Queue at the same time. - -We then will iterate over all the SlashEvents in the queue, adding their `ValidatorVotingPercent` to calculate the new percent to slash all the validators in the queue at, using the "Square of Sum of Roots" formula introduced above. - -Once we have the `NewSlashPercent`, we then iterate over all the `SlashEvent`s in the queue once again, and if `NewSlashPercent > SlashedSoFar` for that SlashEvent, we call the `staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent)` (we pass in the power of the validator before any slashes occurred, so that we slash the right amount of tokens). We then set `SlashEvent.SlashedSoFar` amount to `NewSlashPercent`. - -## Status - -Proposed - -## Consequences - -### Positive - -* Increases decentralization by disincentivizing delegating to large validators -* Incentivizes Decorrelation of Validators -* More severely punishes attacks than accidental faults -* More flexibility in slashing rates parameterization - -### Negative - -* More computationally expensive than current implementation. Will require more data about "recent slashing events" to be stored on chain. diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-016-validator-consensus-key-rotation.md b/versioned_docs/version-0.46/integrate/architecture/adr-016-validator-consensus-key-rotation.md deleted file mode 100644 index 0510c2b9b..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-016-validator-consensus-key-rotation.md +++ /dev/null @@ -1,125 +0,0 @@ -# ADR 016: Validator Consensus Key Rotation - -## Changelog - -* 2019 Oct 23: Initial draft -* 2019 Nov 28: Add key rotation fee - -## Context - -Validator consensus key rotation feature has been discussed and requested for a long time, for the sake of safer validator key management policy (e.g. https://github.com/tendermint/tendermint/issues/1136). So, we suggest one of the simplest form of validator consensus key rotation implementation mostly onto Cosmos SDK. - -We don't need to make any update on consensus logic in Tendermint because Tendermint does not have any mapping information of consensus key and validator operator key, meaning that from Tendermint point of view, a consensus key rotation of a validator is simply a replacement of a consensus key to another. - -Also, it should be noted that this ADR includes only the simplest form of consensus key rotation without considering multiple consensus keys concept. Such multiple consensus keys concept shall remain a long term goal of Tendermint and Cosmos SDK. - -## Decision - -### Pseudo procedure for consensus key rotation - -* create new random consensus key. -* create and broadcast a transaction with a `MsgRotateConsPubKey` that states the new consensus key is now coupled with the validator operator with signature from the validator's operator key. -* old consensus key becomes unable to participate on consensus immediately after the update of key mapping state on-chain. -* start validating with new consensus key. -* validators using HSM and KMS should update the consensus key in HSM to use the new rotated key after the height `h` when `MsgRotateConsPubKey` committed to the blockchain. - -### Considerations - -* consensus key mapping information management strategy - * store history of each key mapping changes in the kvstore. - * the state machine can search corresponding consensus key paired with given validator operator for any arbitrary height in a recent unbonding period. - * the state machine does not need any historical mapping information which is past more than unbonding period. -* key rotation costs related to LCD and IBC - * LCD and IBC will have traffic/computation burden when there exists frequent power changes - * In current Tendermint design, consensus key rotations are seen as power changes from LCD or IBC perspective - * Therefore, to minimize unnecessary frequent key rotation behavior, we limited maximum number of rotation in recent unbonding period and also applied exponentially increasing rotation fee -* limits - * a validator cannot rotate its consensus key more than `MaxConsPubKeyRotations` time for any unbonding period, to prevent spam. - * parameters can be decided by governance and stored in genesis file. -* key rotation fee - * a validator should pay `KeyRotationFee` to rotate the consensus key which is calculated as below - * `KeyRotationFee` = (max(`VotingPowerPercentage` *100, 1)* `InitialKeyRotationFee`) * 2^(number of rotations in `ConsPubKeyRotationHistory` in recent unbonding period) -* evidence module - * evidence module can search corresponding consensus key for any height from slashing keeper so that it can decide which consensus key is supposed to be used for given height. -* abci.ValidatorUpdate - * tendermint already has ability to change a consensus key by ABCI communication(`ValidatorUpdate`). - * validator consensus key update can be done via creating new + delete old by change the power to zero. - * therefore, we expect we even do not need to change tendermint codebase at all to implement this feature. -* new genesis parameters in `staking` module - * `MaxConsPubKeyRotations` : maximum number of rotation can be executed by a validator in recent unbonding period. default value 10 is suggested(11th key rotation will be rejected) - * `InitialKeyRotationFee` : the initial key rotation fee when no key rotation has happened in recent unbonding period. default value 1atom is suggested(1atom fee for the first key rotation in recent unbonding period) - -### Workflow - -1. The validator generates a new consensus keypair. -2. The validator generates and signs a `MsgRotateConsPubKey` tx with their operator key and new ConsPubKey - - ```go - type MsgRotateConsPubKey struct { - ValidatorAddress sdk.ValAddress - NewPubKey crypto.PubKey - } - ``` - -3. `handleMsgRotateConsPubKey` gets `MsgRotateConsPubKey`, calls `RotateConsPubKey` with emits event -4. `RotateConsPubKey` - * checks if `NewPubKey` is not duplicated on `ValidatorsByConsAddr` - * checks if the validator is does not exceed parameter `MaxConsPubKeyRotations` by iterating `ConsPubKeyRotationHistory` - * checks if the signing account has enough balance to pay `KeyRotationFee` - * pays `KeyRotationFee` to community fund - * overwrites `NewPubKey` in `validator.ConsPubKey` - * deletes old `ValidatorByConsAddr` - * `SetValidatorByConsAddr` for `NewPubKey` - * Add `ConsPubKeyRotationHistory` for tracking rotation - - ```go - type ConsPubKeyRotationHistory struct { - OperatorAddress sdk.ValAddress - OldConsPubKey crypto.PubKey - NewConsPubKey crypto.PubKey - RotatedHeight int64 - } - ``` - -5. `ApplyAndReturnValidatorSetUpdates` checks if there is `ConsPubKeyRotationHistory` with `ConsPubKeyRotationHistory.RotatedHeight == ctx.BlockHeight()` and if so, generates 2 `ValidatorUpdate` , one for a remove validator and one for create new validator - - ```go - abci.ValidatorUpdate{ - PubKey: tmtypes.TM2PB.PubKey(OldConsPubKey), - Power: 0, - } - - abci.ValidatorUpdate{ - PubKey: tmtypes.TM2PB.PubKey(NewConsPubKey), - Power: v.ConsensusPower(), - } - ``` - -6. at `previousVotes` Iteration logic of `AllocateTokens`, `previousVote` using `OldConsPubKey` match up with `ConsPubKeyRotationHistory`, and replace validator for token allocation -7. Migrate `ValidatorSigningInfo` and `ValidatorMissedBlockBitArray` from `OldConsPubKey` to `NewConsPubKey` - -* Note : All above features shall be implemented in `staking` module. - -## Status - -Proposed - -## Consequences - -### Positive - -* Validators can immediately or periodically rotate their consensus key to have better security policy -* improved security against Long-Range attacks (https://nearprotocol.com/blog/long-range-attacks-and-a-new-fork-choice-rule) given a validator throws away the old consensus key(s) - -### Negative - -* Slash module needs more computation because it needs to lookup corresponding consensus key of validators for each height -* frequent key rotations will make light client bisection less efficient - -### Neutral - -## References - -* on tendermint repo : https://github.com/tendermint/tendermint/issues/1136 -* on cosmos-sdk repo : https://github.com/cosmos/cosmos-sdk/issues/5231 -* about multiple consensus keys : https://github.com/tendermint/tendermint/issues/1758#issuecomment-545291698 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-017-historical-header-module.md b/versioned_docs/version-0.46/integrate/architecture/adr-017-historical-header-module.md deleted file mode 100644 index 8cce973b0..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-017-historical-header-module.md +++ /dev/null @@ -1,61 +0,0 @@ -# ADR 17: Historical Header Module - -## Changelog - -* 26 November 2019: Start of first version -* 2 December 2019: Final draft of first version - -## Context - -In order for the Cosmos SDK to implement the [IBC specification](https://github.com/cosmos/ics), modules within the Cosmos SDK must have the ability to introspect recent consensus states (validator sets & commitment roots) as proofs of these values on other chains must be checked during the handshakes. - -## Decision - -The application MUST store the most recent `n` headers in a persistent store. At first, this store MAY be the current Merklised store. A non-Merklised store MAY be used later as no proofs are necessary. - -The application MUST store this information by storing new headers immediately when handling `abci.RequestBeginBlock`: - -```golang -func BeginBlock(ctx sdk.Context, keeper HistoricalHeaderKeeper, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - info := HistoricalInfo{ - Header: ctx.BlockHeader(), - ValSet: keeper.StakingKeeper.GetAllValidators(ctx), // note that this must be stored in a canonical order - } - keeper.SetHistoricalInfo(ctx, ctx.BlockHeight(), info) - n := keeper.GetParamRecentHeadersToStore() - keeper.PruneHistoricalInfo(ctx, ctx.BlockHeight() - n) - // continue handling request -} -``` - -Alternatively, the application MAY store only the hash of the validator set. - -The application MUST make these past `n` committed headers available for querying by Cosmos SDK modules through the `Keeper`'s `GetHistoricalInfo` function. This MAY be implemented in a new module, or it MAY also be integrated into an existing one (likely `x/staking` or `x/ibc`). - -`n` MAY be configured as a parameter store parameter, in which case it could be changed by `ParameterChangeProposal`s, although it will take some blocks for the stored information to catch up if `n` is increased. - -## Status - -Proposed. - -## Consequences - -Implementation of this ADR will require changes to the Cosmos SDK. It will not require changes to Tendermint. - -### Positive - -* Easy retrieval of headers & state roots for recent past heights by modules anywhere in the Cosmos SDK. -* No RPC calls to Tendermint required. -* No ABCI alterations required. - -### Negative - -* Duplicates `n` headers data in Tendermint & the application (additional disk usage) - in the long term, an approach such as [this](https://github.com/tendermint/tendermint/issues/4210) might be preferable. - -### Neutral - -(none known) - -## References - -* [ICS 2: "Consensus state introspection"](https://github.com/cosmos/ibc/tree/master/spec/01-tx-lifecycle.mdics-002-client-semantics#consensus-state-introspection) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-018-extendable-voting-period.md b/versioned_docs/version-0.46/integrate/architecture/adr-018-extendable-voting-period.md deleted file mode 100644 index ee238fc35..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-018-extendable-voting-period.md +++ /dev/null @@ -1,66 +0,0 @@ -# ADR 18: Extendable Voting Periods - -## Changelog - -* 1 January 2020: Start of first version - -## Context - -Currently the voting period for all governance proposals is the same. However, this is suboptimal as all governance proposals do not require the same time period. For more non-contentious proposals, they can be dealt with more efficently with a faster period, while more contentious or complex proposals may need a longer period for extended discussion/consideration. - -## Decision - -We would like to design a mechanism for making the voting period of a governance proposal variable based on the demand of voters. We would like it to be based on the view of the governance participants, rather than just the proposer of a governance proposal (thus, allowing the proposer to select the voting period length is not sufficient). - -However, we would like to avoid the creation of an entire second voting process to determine the length of the voting period, as it just pushed the problem to determining the length of that first voting period. - -Thus, we propose the following mechanism: - -### Params - -* The current gov param `VotingPeriod` is to be replaced by a `MinVotingPeriod` param. This is the default voting period that all governance proposal voting periods start with. -* There is a new gov param called `MaxVotingPeriodExtension`. - -### Mechanism - -There is a new `Msg` type called `MsgExtendVotingPeriod`, which can be sent by any staked account during a proposal's voting period. It allows the sender to unilaterally extend the length of the voting period by `MaxVotingPeriodExtension * sender's share of voting power`. Every address can only call `MsgExtendVotingPeriod` once per proposal. - -So for example, if the `MaxVotingPeriodExtension` is set to 100 Days, then anyone with 1% of voting power can extend the voting power by 1 day. If 33% of voting power has sent the message, the voting period will be extended by 33 days. Thus, if absolutely everyone chooses to extend the voting period, the absolute maximum voting period will be `MinVotingPeriod + MaxVotingPeriodExtension`. - -This system acts as a sort of distributed coordination, where individual stakers choosing to extend or not, allows the system the guage the conentiousness/complexity of the proposal. It is extremely unlikely that many stakers will choose to extend at the exact same time, it allows stakers to view how long others have already extended thus far, to decide whether or not to extend further. - -### Dealing with Unbonding/Redelegation - -There is one thing that needs to be addressed. How to deal with redelegation/unbonding during the voting period. If a staker of 5% calls `MsgExtendVotingPeriod` and then unbonds, does the voting period then decrease by 5 days again? This is not good as it can give people a false sense of how long they have to make their decision. For this reason, we want to design it such that the voting period length can only be extended, not shortened. To do this, the current extension amount is based on the highest percent that voted extension at any time. This is best explained by example: - -1. Let's say 2 stakers of voting power 4% and 3% respectively vote to extend. The voting period will be extended by 7 days. -2. Now the staker of 3% decides to unbond before the end of the voting period. The voting period extension remains 7 days. -3. Now, let's say another staker of 2% voting power decides to extend voting period. There is now 6% of active voting power choosing the extend. The voting power remains 7 days. -4. If a fourth staker of 10% chooses to extend now, there is a total of 16% of active voting power wishing to extend. The voting period will be extended to 16 days. - -### Delegators - -Just like votes in the actual voting period, delegators automatically inherit the extension of their validators. If their validator chooses to extend, their voting power will be used in the validator's extension. However, the delegator is unable to override their validator and "unextend" as that would contradict the "voting power length can only be ratcheted up" principle described in the previous section. However, a delegator may choose the extend using their personal voting power, if their validator has not done so. - -## Status - -Proposed - -## Consequences - -### Positive - -* More complex/contentious governance proposals will have more time to properly digest and deliberate - -### Negative - -* Governance process becomes more complex and requires more understanding to interact with effectively -* Can no longer predict when a governance proposal will end. Can't assume order in which governance proposals will end. - -### Neutral - -* The minimum voting period can be made shorter - -## References - -* [Cosmos Forum post where idea first originated](https://forum.cosmos.network/t/proposal-draft-reduce-governance-voting-period-to-7-days/3032/9) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-019-protobuf-state-encoding.md b/versioned_docs/version-0.46/integrate/architecture/adr-019-protobuf-state-encoding.md deleted file mode 100644 index 1d20145ae..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-019-protobuf-state-encoding.md +++ /dev/null @@ -1,379 +0,0 @@ -# ADR 019: Protocol Buffer State Encoding - -## Changelog - -* 2020 Feb 15: Initial Draft -* 2020 Feb 24: Updates to handle messages with interface fields -* 2020 Apr 27: Convert usages of `oneof` for interfaces to `Any` -* 2020 May 15: Describe `cosmos_proto` extensions and amino compatibility -* 2020 Dec 4: Move and rename `MarshalAny` and `UnmarshalAny` into the `codec.Codec` interface. -* 2021 Feb 24: Remove mentions of `HybridCodec`, which has been abandoned in [#6843](https://github.com/cosmos/cosmos-sdk/pull/6843). - -## Status - -Accepted - -## Context - -Currently, the Cosmos SDK utilizes [go-amino](https://github.com/tendermint/go-amino/) for binary -and JSON object encoding over the wire bringing parity between logical objects and persistence objects. - -From the Amino docs: - -> Amino is an object encoding specification. It is a subset of Proto3 with an extension for interface -> support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) for more -> information on Proto3, which Amino is largely compatible with (but not with Proto2). -> -> The goal of the Amino encoding protocol is to bring parity into logic objects and persistence objects. - -Amino also aims to have the following goals (not a complete list): - -* Binary bytes must be decode-able with a schema. -* Schema must be upgradeable. -* The encoder and decoder logic must be reasonably simple. - -However, we believe that Amino does not fulfill these goals completely and does not fully meet the -needs of a truly flexible cross-language and multi-client compatible encoding protocol in the Cosmos SDK. -Namely, Amino has proven to be a big pain-point in regards to supporting object serialization across -clients written in various languages while providing virtually little in the way of true backwards -compatibility and upgradeability. Furthermore, through profiling and various benchmarks, Amino has -been shown to be an extremely large performance bottleneck in the Cosmos SDK 1. This is -largely reflected in the performance of simulations and application transaction throughput. - -Thus, we need to adopt an encoding protocol that meets the following criteria for state serialization: - -* Language agnostic -* Platform agnostic -* Rich client support and thriving ecosystem -* High performance -* Minimal encoded message size -* Codegen-based over reflection-based -* Supports backward and forward compatibility - -Note, migrating away from Amino should be viewed as a two-pronged approach, state and client encoding. -This ADR focuses on state serialization in the Cosmos SDK state machine. A corresponding ADR will be -made to address client-side encoding. - -## Decision - -We will adopt [Protocol Buffers](https://developers.google.com/protocol-buffers) for serializing -persisted structured data in the Cosmos SDK while providing a clean mechanism and developer UX for -applications wishing to continue to use Amino. We will provide this mechanism by updating modules to -accept a codec interface, `Marshaler`, instead of a concrete Amino codec. Furthermore, the Cosmos SDK -will provide two concrete implementations of the `Marshaler` interface: `AminoCodec` and `ProtoCodec`. - -* `AminoCodec`: Uses Amino for both binary and JSON encoding. -* `ProtoCodec`: Uses Protobuf for both binary and JSON encoding. - -Modules will use whichever codec that is instantiated in the app. By default, the Cosmos SDK's `simapp` -instantiates a `ProtoCodec` as the concrete implementation of `Marshaler`, inside the `MakeTestEncodingConfig` -function. This can be easily overwritten by app developers if they so desire. - -The ultimate goal will be to replace Amino JSON encoding with Protobuf encoding and thus have -modules accept and/or extend `ProtoCodec`. Until then, Amino JSON is still provided for legacy use-cases. -A handful of places in the Cosmos SDK still have Amino JSON hardcoded, such as the Legacy API REST endpoints -and the `x/params` store. They are planned to be converted to Protobuf in a gradual manner. - -### Module Codecs - -Modules that do not require the ability to work with and serialize interfaces, the path to Protobuf -migration is pretty straightforward. These modules are to simply migrate any existing types that -are encoded and persisted via their concrete Amino codec to Protobuf and have their keeper accept a -`Marshaler` that will be a `ProtoCodec`. This migration is simple as things will just work as-is. - -Note, any business logic that needs to encode primitive types like `bool` or `int64` should use -[gogoprotobuf](https://github.com/gogo/protobuf) Value types. - -Example: - -```go - ts, err := gogotypes.TimestampProto(completionTime) - if err != nil { - // ... - } - - bz := cdc.MustMarshal(ts) -``` - -However, modules can vary greatly in purpose and design and so we must support the ability for modules -to be able to encode and work with interfaces (e.g. `Account` or `Content`). For these modules, they -must define their own codec interface that extends `Marshaler`. These specific interfaces are unique -to the module and will contain method contracts that know how to serialize the needed interfaces. - -Example: - -```go -// x/auth/types/codec.go - -type Codec interface { - codec.Codec - - MarshalAccount(acc exported.Account) ([]byte, error) - UnmarshalAccount(bz []byte) (exported.Account, error) - - MarshalAccountJSON(acc exported.Account) ([]byte, error) - UnmarshalAccountJSON(bz []byte) (exported.Account, error) -} -``` - -### Usage of `Any` to encode interfaces - -In general, module-level .proto files should define messages which encode interfaces -using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). -After [extension discussion](https://github.com/cosmos/cosmos-sdk/issues/6030), -this was chosen as the preferred alternative to application-level `oneof`s -as in our original protobuf design. The arguments in favor of `Any` can be -summarized as follows: - -* `Any` provides a simpler, more consistent client UX for dealing with -interfaces than app-level `oneof`s that will need to be coordinated more -carefully across applications. Creating a generic transaction -signing library using `oneof`s may be cumbersome and critical logic may need -to be reimplemented for each chain -* `Any` provides more resistance against human error than `oneof` -* `Any` is generally simpler to implement for both modules and apps - -The main counter-argument to using `Any` centers around its additional space -and possibly performance overhead. The space overhead could be dealt with using -compression at the persistence layer in the future and the performance impact -is likely to be small. Thus, not using `Any` is seem as a pre-mature optimization, -with user experience as the higher order concern. - -Note, that given the Cosmos SDK's decision to adopt the `Codec` interfaces described -above, apps can still choose to use `oneof` to encode state and transactions -but it is not the recommended approach. If apps do choose to use `oneof`s -instead of `Any` they will likely lose compatibility with client apps that -support multiple chains. Thus developers should think carefully about whether -they care more about what is possibly a pre-mature optimization or end-user -and client developer UX. - -### Safe usage of `Any` - -By default, the [gogo protobuf implementation of `Any`](https://pkg.go.dev/github.com/gogo/protobuf/types) -uses [global type registration]( https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) -to decode values packed in `Any` into concrete -go types. This introduces a vulnerability where any malicious module -in the dependency tree could register a type with the global protobuf registry -and cause it to be loaded and unmarshaled by a transaction that referenced -it in the `type_url` field. - -To prevent this, we introduce a type registration mechanism for decoding `Any` -values into concrete types through the `InterfaceRegistry` interface which -bears some similarity to type registration with Amino: - -```go -type InterfaceRegistry interface { - // RegisterInterface associates protoName as the public name for the - // interface passed in as iface - // Ex: - // registry.RegisterInterface("cosmos_sdk.Msg", (*sdk.Msg)(nil)) - RegisterInterface(protoName string, iface interface{}) - - // RegisterImplementations registers impls as a concrete implementations of - // the interface iface - // Ex: - // registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{}) - RegisterImplementations(iface interface{}, impls ...proto.Message) - -} -``` - -In addition to serving as a whitelist, `InterfaceRegistry` can also serve -to communicate the list of concrete types that satisfy an interface to clients. - -In .proto files: - -* fields which accept interfaces should be annotated with `cosmos_proto.accepts_interface` -using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface` -* interface implementations should be annotated with `cosmos_proto.implements_interface` -using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface` - -In the future, `protoName`, `cosmos_proto.accepts_interface`, `cosmos_proto.implements_interface` -may be used via code generation, reflection &/or static linting. - -The same struct that implements `InterfaceRegistry` will also implement an -interface `InterfaceUnpacker` to be used for unpacking `Any`s: - -```go -type InterfaceUnpacker interface { - // UnpackAny unpacks the value in any to the interface pointer passed in as - // iface. Note that the type in any must have been registered with - // RegisterImplementations as a concrete type for that interface - // Ex: - // var msg sdk.Msg - // err := ctx.UnpackAny(any, &msg) - // ... - UnpackAny(any *Any, iface interface{}) error -} -``` - -Note that `InterfaceRegistry` usage does not deviate from standard protobuf -usage of `Any`, it just introduces a security and introspection layer for -golang usage. - -`InterfaceRegistry` will be a member of `ProtoCodec` -described above. In order for modules to register interface types, app modules -can optionally implement the following interface: - -```go -type InterfaceModule interface { - RegisterInterfaceTypes(InterfaceRegistry) -} -``` - -The module manager will include a method to call `RegisterInterfaceTypes` on -every module that implements it in order to populate the `InterfaceRegistry`. - -### Using `Any` to encode state - -The Cosmos SDK will provide support methods `MarshalInterface` and `UnmarshalInterface` to hide a complexity of wrapping interface types into `Any` and allow easy serialization. - -```go -import "github.com/cosmos/cosmos-sdk/codec" - -// note: eviexported.Evidence is an interface type -func MarshalEvidence(cdc codec.BinaryCodec, e eviexported.Evidence) ([]byte, error) { - return cdc.MarshalInterface(e) -} - -func UnmarshalEvidence(cdc codec.BinaryCodec, bz []byte) (eviexported.Evidence, error) { - var evi eviexported.Evidence - err := cdc.UnmarshalInterface(&evi, bz) - return err, nil -} -``` - -### Using `Any` in `sdk.Msg`s - -A similar concept is to be applied for messages that contain interfaces fields. -For example, we can define `MsgSubmitEvidence` as follows where `Evidence` is -an interface: - -```protobuf -// x/evidence/types/types.proto - -message MsgSubmitEvidence { - bytes submitter = 1 - [ - (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress" - ]; - google.protobuf.Any evidence = 2; -} -``` - -Note that in order to unpack the evidence from `Any` we do need a reference to -`InterfaceRegistry`. In order to reference evidence in methods like -`ValidateBasic` which shouldn't have to know about the `InterfaceRegistry`, we -introduce an `UnpackInterfaces` phase to deserialization which unpacks -interfaces before they're needed. - -### Unpacking Interfaces - -To implement the `UnpackInterfaces` phase of deserialization which unpacks -interfaces wrapped in `Any` before they're needed, we create an interface -that `sdk.Msg`s and other types can implement: - -```go -type UnpackInterfacesMessage interface { - UnpackInterfaces(InterfaceUnpacker) error -} -``` - -We also introduce a private `cachedValue interface{}` field onto the `Any` -struct itself with a public getter `GetCachedValue() interface{}`. - -The `UnpackInterfaces` method is to be invoked during message deserialization right -after `Unmarshal` and any interface values packed in `Any`s will be decoded -and stored in `cachedValue` for reference later. - -Then unpacked interface values can safely be used in any code afterwards -without knowledge of the `InterfaceRegistry` -and messages can introduce a simple getter to cast the cached value to the -correct interface type. - -This has the added benefit that unmarshaling of `Any` values only happens once -during initial deserialization rather than every time the value is read. Also, -when `Any` values are first packed (for instance in a call to -`NewMsgSubmitEvidence`), the original interface value is cached so that -unmarshaling isn't needed to read it again. - -`MsgSubmitEvidence` could implement `UnpackInterfaces`, plus a convenience getter -`GetEvidence` as follows: - -```go -func (msg MsgSubmitEvidence) UnpackInterfaces(ctx sdk.InterfaceRegistry) error { - var evi eviexported.Evidence - return ctx.UnpackAny(msg.Evidence, *evi) -} - -func (msg MsgSubmitEvidence) GetEvidence() eviexported.Evidence { - return msg.Evidence.GetCachedValue().(eviexported.Evidence) -} -``` - -### Amino Compatibility - -Our custom implementation of `Any` can be used transparently with Amino if used -with the proper codec instance. What this means is that interfaces packed within -`Any`s will be amino marshaled like regular Amino interfaces (assuming they -have been registered properly with Amino). - -In order for this functionality to work: - -* **all legacy code must use `*codec.LegacyAmino` instead of `*amino.Codec` which is - now a wrapper which properly handles `Any`** -* **all new code should use `Marshaler` which is compatible with both amino and - protobuf** -* Also, before v0.39, `codec.LegacyAmino` will be renamed to `codec.LegacyAmino`. - -### Why Wasn't X Chosen Instead - -For a more complete comparison to alternative protocols, see [here](https://codeburst.io/json-vs-protocol-buffers-vs-flatbuffers-a4247f8bda6f). - -### Cap'n Proto - -While [Cap’n Proto](https://capnproto.org/) does seem like an advantageous alternative to Protobuf -due to it's native support for interfaces/generics and built in canonicalization, it does lack the -rich client ecosystem compared to Protobuf and is a bit less mature. - -### FlatBuffers - -[FlatBuffers](https://google.github.io/flatbuffers/) is also a potentially viable alternative, with the -primary difference being that FlatBuffers does not need a parsing/unpacking step to a secondary -representation before you can access data, often coupled with per-object memory allocation. - -However, it would require great efforts into research and full understanding the scope of the migration -and path forward -- which isn't immediately clear. In addition, FlatBuffers aren't designed for -untrusted inputs. - -## Future Improvements & Roadmap - -In the future we may consider a compression layer right above the persistence -layer which doesn't change tx or merkle tree hashes, but reduces the storage -overhead of `Any`. In addition, we may adopt protobuf naming conventions which -make type URLs a bit more concise while remaining descriptive. - -Additional code generation support around the usage of `Any` is something that -could also be explored in the future to make the UX for go developers more -seamless. - -## Consequences - -### Positive - -* Significant performance gains. -* Supports backward and forward type compatibility. -* Better support for cross-language clients. - -### Negative - -* Learning curve required to understand and implement Protobuf messages. -* Slightly larger message size due to use of `Any`, although this could be offset - by a compression layer in the future - -### Neutral - -## References - -1. https://github.com/cosmos/cosmos-sdk/issues/4977 -2. https://github.com/cosmos/cosmos-sdk/issues/5444 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-020-protobuf-transaction-encoding.md b/versioned_docs/version-0.46/integrate/architecture/adr-020-protobuf-transaction-encoding.md deleted file mode 100644 index 71bd1f9fd..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-020-protobuf-transaction-encoding.md +++ /dev/null @@ -1,464 +0,0 @@ -# ADR 020: Protocol Buffer Transaction Encoding - -## Changelog - -* 2020 March 06: Initial Draft -* 2020 March 12: API Updates -* 2020 April 13: Added details on interface `oneof` handling -* 2020 April 30: Switch to `Any` -* 2020 May 14: Describe public key encoding -* 2020 June 08: Store `TxBody` and `AuthInfo` as bytes in `SignDoc`; Document `TxRaw` as broadcast and storage type. -* 2020 August 07: Use ADR 027 for serializing `SignDoc`. -* 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`, as discussed in [#6966](https://github.com/cosmos/cosmos-sdk/issues/6966). -* 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`. -* 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface. -* 2021 Feb 24: The Cosmos SDK does not use Tendermint's `PubKey` interface anymore, but its own `cryptotypes.PubKey`. Updates to reflect this. -* 2021 May 3: Rename `clientCtx.JSONMarshaler` to `clientCtx.JSONCodec`. -* 2021 June 10: Add `clientCtx.Codec: codec.Codec`. - -## Status - -Accepted - -## Context - -This ADR is a continuation of the motivation, design, and context established in -[ADR 019](./adr-019-protobuf-state-encoding.md), namely, we aim to design the -Protocol Buffer migration path for the client-side of the Cosmos SDK. - -Specifically, the client-side migration path primarily includes tx generation and -signing, message construction and routing, in addition to CLI & REST handlers and -business logic (i.e. queriers). - -With this in mind, we will tackle the migration path via two main areas, txs and -querying. However, this ADR solely focuses on transactions. Querying should be -addressed in a future ADR, but it should build off of these proposals. - -Based on detailed discussions ([\#6030](https://github.com/cosmos/cosmos-sdk/issues/6030) -and [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078)), the original -design for transactions was changed substantially from an `oneof` /JSON-signing -approach to the approach described below. - -## Decision - -### Transactions - -Since interface values are encoded with `google.protobuf.Any` in state (see [ADR 019](adr-019-protobuf-state-encoding.md)), -`sdk.Msg`s are encoding with `Any` in transactions. - -One of the main goals of using `Any` to encode interface values is to have a -core set of types which is reused by apps so that -clients can safely be compatible with as many chains as possible. - -It is one of the goals of this specification to provide a flexible cross-chain transaction -format that can serve a wide variety of use cases without breaking client -compatibility. - -In order to facilitate signing, transactions are separated into `TxBody`, -which will be re-used by `SignDoc` below, and `signatures`: - -```proto -// types/types.proto -package cosmos_sdk.v1; - -message Tx { - TxBody body = 1; - AuthInfo auth_info = 2; - // A list of signatures that matches the length and order of AuthInfo's signer_infos to - // allow connecting signature meta information like public key and signing mode by position. - repeated bytes signatures = 3; -} - -// A variant of Tx that pins the signer's exact binary represenation of body and -// auth_info. This is used for signing, broadcasting and verification. The binary -// `serialize(tx: TxRaw)` is stored in Tendermint and the hash `sha256(serialize(tx: TxRaw))` -// becomes the "txhash", commonly used as the transaction ID. -message TxRaw { - // A protobuf serialization of a TxBody that matches the representation in SignDoc. - bytes body = 1; - // A protobuf serialization of an AuthInfo that matches the representation in SignDoc. - bytes auth_info = 2; - // A list of signatures that matches the length and order of AuthInfo's signer_infos to - // allow connecting signature meta information like public key and signing mode by position. - repeated bytes signatures = 3; -} - -message TxBody { - // A list of messages to be executed. The required signers of those messages define - // the number and order of elements in AuthInfo's signer_infos and Tx's signatures. - // Each required signer address is added to the list only the first time it occurs. - // - // By convention, the first required signer (usually from the first message) is referred - // to as the primary signer and pays the fee for the whole transaction. - repeated google.protobuf.Any messages = 1; - string memo = 2; - int64 timeout_height = 3; - repeated google.protobuf.Any extension_options = 1023; -} - -message AuthInfo { - // This list defines the signing modes for the required signers. The number - // and order of elements must match the required signers from TxBody's messages. - // The first element is the primary signer and the one which pays the fee. - repeated SignerInfo signer_infos = 1; - // The fee can be calculated based on the cost of evaluating the body and doing signature verification of the signers. This can be estimated via simulation. - Fee fee = 2; -} - -message SignerInfo { - // The public key is optional for accounts that already exist in state. If unset, the - // verifier can use the required signer address for this position and lookup the public key. - google.protobuf.Any public_key = 1; - // ModeInfo describes the signing mode of the signer and is a nested - // structure to support nested multisig pubkey's - ModeInfo mode_info = 2; - // sequence is the sequence of the account, which describes the - // number of committed transactions signed by a given address. It is used to prevent - // replay attacks. - uint64 sequence = 3; -} - -message ModeInfo { - oneof sum { - Single single = 1; - Multi multi = 2; - } - - // Single is the mode info for a single signer. It is structured as a message - // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the future - message Single { - SignMode mode = 1; - } - - // Multi is the mode info for a multisig public key - message Multi { - // bitarray specifies which keys within the multisig are signing - CompactBitArray bitarray = 1; - // mode_infos is the corresponding modes of the signers of the multisig - // which could include nested multisig public keys - repeated ModeInfo mode_infos = 2; - } -} - -enum SignMode { - SIGN_MODE_UNSPECIFIED = 0; - - SIGN_MODE_DIRECT = 1; - - SIGN_MODE_TEXTUAL = 2; - - SIGN_MODE_LEGACY_AMINO_JSON = 127; -} -``` - -As will be discussed below, in order to include as much of the `Tx` as possible -in the `SignDoc`, `SignerInfo` is separated from signatures so that only the -raw signatures themselves live outside of what is signed over. - -Because we are aiming for a flexible, extensible cross-chain transaction -format, new transaction processing options should be added to `TxBody` as soon -those use cases are discovered, even if they can't be implemented yet. - -Because there is coordination overhead in this, `TxBody` includes an -`extension_options` field which can be used for any transaction processing -options that are not already covered. App developers should, nevertheless, -attempt to upstream important improvements to `Tx`. - -### Signing - -All of the signing modes below aim to provide the following guarantees: - -* **No Malleability**: `TxBody` and `AuthInfo` cannot change once the transaction - is signed -* **Predictable Gas**: if I am signing a transaction where I am paying a fee, - the final gas is fully dependent on what I am signing - -These guarantees give the maximum amount confidence to message signers that -manipulation of `Tx`s by intermediaries can't result in any meaningful changes. - -#### `SIGN_MODE_DIRECT` - -The "direct" signing behavior is to sign the raw `TxBody` bytes as broadcast over -the wire. This has the advantages of: - -* requiring the minimum additional client capabilities beyond a standard protocol - buffers implementation -* leaving effectively zero holes for transaction malleability (i.e. there are no - subtle differences between the signing and encoding formats which could - potentially be exploited by an attacker) - -Signatures are structured using the `SignDoc` below which reuses the serialization of -`TxBody` and `AuthInfo` and only adds the fields which are needed for signatures: - -```proto -// types/types.proto -message SignDoc { - // A protobuf serialization of a TxBody that matches the representation in TxRaw. - bytes body = 1; - // A protobuf serialization of an AuthInfo that matches the representation in TxRaw. - bytes auth_info = 2; - string chain_id = 3; - uint64 account_number = 4; -} -``` - -In order to sign in the default mode, clients take the following steps: - -1. Serialize `TxBody` and `AuthInfo` using any valid protobuf implementation. -2. Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). -3. Sign the encoded `SignDoc` bytes. -4. Build a `TxRaw` and serialize it for broadcasting. - -Signature verification is based on comparing the raw `TxBody` and `AuthInfo` -bytes encoded in `TxRaw` not based on any ["canonicalization"](https://github.com/regen-network/canonical-proto3) -algorithm which creates added complexity for clients in addition to preventing -some forms of upgradeability (to be addressed later in this document). - -Signature verifiers do: - -1. Deserialize a `TxRaw` and pull out `body` and `auth_info`. -2. Create a list of required signer addresses from the messages. -3. For each required signer: - * Pull account number and sequence from the state. - * Obtain the public key either from state or `AuthInfo`'s `signer_infos`. - * Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). - * Verify the signature at the same list position against the serialized `SignDoc`. - -#### `SIGN_MODE_LEGACY_AMINO` - -In order to support legacy wallets and exchanges, Amino JSON will be temporarily -supported transaction signing. Once wallets and exchanges have had a -chance to upgrade to protobuf based signing, this option will be disabled. In -the meantime, it is foreseen that disabling the current Amino signing would cause -too much breakage to be feasible. Note that this is mainly a requirement of the -Cosmos Hub and other chains may choose to disable Amino signing immediately. - -Legacy clients will be able to sign a transaction using the current Amino -JSON format and have it encoded to protobuf using the REST `/tx/encode` -endpoint before broadcasting. - -#### `SIGN_MODE_TEXTUAL` - -As was discussed extensively in [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078), -there is a desire for a human-readable signing encoding, especially for hardware -wallets like the [Ledger](https://www.ledger.com) which display -transaction contents to users before signing. JSON was an attempt at this but -falls short of the ideal. - -`SIGN_MODE_TEXTUAL` is intended as a placeholder for a human-readable -encoding which will replace Amino JSON. This new encoding should be even more -focused on readability than JSON, possibly based on formatting strings like -[MessageFormat](http://userguide.icu-project.org/formatparse/messages). - -In order to ensure that the new human-readable format does not suffer from -transaction malleability issues, `SIGN_MODE_TEXTUAL` -requires that the _human-readable bytes are concatenated with the raw `SignDoc`_ -to generate sign bytes. - -Multiple human-readable formats (maybe even localized messages) may be supported -by `SIGN_MODE_TEXTUAL` when it is implemented. - -### Unknown Field Filtering - -Unknown fields in protobuf messages should generally be rejected by transaction -processors because: - -* important data may be present in the unknown fields, that if ignored, will - cause unexpected behavior for clients -* they present a malleability vulnerability where attackers can bloat tx size - by adding random uninterpreted data to unsigned content (i.e. the master `Tx`, - not `TxBody`) - -There are also scenarios where we may choose to safely ignore unknown fields -(https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-624400188) to -provide graceful forwards compatibility with newer clients. - -We propose that field numbers with bit 11 set (for most use cases this is -the range of 1024-2047) be considered non-critical fields that can safely be -ignored if unknown. - -To handle this we will need a unknown field filter that: - -* always rejects unknown fields in unsigned content (i.e. top-level `Tx` and - unsigned parts of `AuthInfo` if present based on the signing mode) -* rejects unknown fields in all messages (including nested `Any`s) other than - fields with bit 11 set - -This will likely need to be a custom protobuf parser pass that takes message bytes -and `FileDescriptor`s and returns a boolean result. - -### Public Key Encoding - -Public keys in the Cosmos SDK implement the `cryptotypes.PubKey` interface. -We propose to use `Any` for protobuf encoding as we are doing with other interfaces (for example, in `BaseAccount.PubKey` and `SignerInfo.PublicKey`). -The following public keys are implemented: secp256k1, secp256r1, ed25519 and legacy-multisignature. - -Ex: - -```proto -message PubKey { - bytes key = 1; -} -``` - -`multisig.LegacyAminoPubKey` has an array of `Any`'s member to support any -protobuf public key type. - -Apps should only attempt to handle a registered set of public keys that they -have tested. The provided signature verification ante handler decorators will -enforce this. - -### CLI & REST - -Currently, the REST and CLI handlers encode and decode types and txs via Amino -JSON encoding using a concrete Amino codec. Being that some of the types dealt with -in the client can be interfaces, similar to how we described in [ADR 019](./adr-019-protobuf-state-encoding.md), -the client logic will now need to take a codec interface that knows not only how -to handle all the types, but also knows how to generate transactions, signatures, -and messages. - -```go -type AccountRetriever interface { - GetAccount(clientCtx Context, addr sdk.AccAddress) (client.Account, error) - GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (client.Account, int64, error) - EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error - GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) -} - -type Generator interface { - NewTx() TxBuilder - NewFee() ClientFee - NewSignature() ClientSignature - MarshalTx(tx types.Tx) ([]byte, error) -} - -type TxBuilder interface { - GetTx() sdk.Tx - - SetMsgs(...sdk.Msg) error - GetSignatures() []sdk.Signature - SetSignatures(...sdk.Signature) - GetFee() sdk.Fee - SetFee(sdk.Fee) - GetMemo() string - SetMemo(string) -} -``` - -We then update `Context` to have new fields: `Codec`, `TxGenerator`, -and `AccountRetriever`, and we update `AppModuleBasic.GetTxCmd` to take -a `Context` which should have all of these fields pre-populated. - -Each client method should then use one of the `Init` methods to re-initialize -the pre-populated `Context`. `tx.GenerateOrBroadcastTx` can be used to -generate or broadcast a transaction. For example: - -```go -import "github.com/spf13/cobra" -import "github.com/cosmos/cosmos-sdk/client" -import "github.com/cosmos/cosmos-sdk/client/tx" - -func NewCmdDoSomething(clientCtx client.Context) *cobra.Command { - return &cobra.Command{ - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := ctx.InitWithInput(cmd.InOrStdin()) - msg := NewSomeMsg{...} - tx.GenerateOrBroadcastTx(clientCtx, msg) - }, - } -} -``` - -## Future Improvements - -### `SIGN_MODE_TEXTUAL` specification - -A concrete specification and implementation of `SIGN_MODE_TEXTUAL` is intended -as a near-term future improvement so that the ledger app and other wallets -can gracefully transition away from Amino JSON. - -### `SIGN_MODE_DIRECT_AUX` - -(\*Documented as option (3) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933) - -We could add a mode `SIGN_MODE_DIRECT_AUX` -to support scenarios where multiple signatures -are being gathered into a single transaction but the message composer does not -yet know which signatures will be included in the final transaction. For instance, -I may have a 3/5 multisig wallet and want to send a `TxBody` to all 5 -signers to see who signs first. As soon as I have 3 signatures then I will go -ahead and build the full transaction. - -With `SIGN_MODE_DIRECT`, each signer needs -to sign the full `AuthInfo` which includes the full list of all signers and -their signing modes, making the above scenario very hard. - -`SIGN_MODE_DIRECT_AUX` would allow "auxiliary" signers to create their signature -using only `TxBody` and their own `PublicKey`. This allows the full list of -signers in `AuthInfo` to be delayed until signatures have been collected. - -An "auxiliary" signer is any signer besides the primary signer who is paying -the fee. For the primary signer, the full `AuthInfo` is actually needed to calculate gas and fees -because that is dependent on how many signers and which key types and signing -modes they are using. Auxiliary signers, however, do not need to worry about -fees or gas and thus can just sign `TxBody`. - -To generate a signature in `SIGN_MODE_DIRECT_AUX` these steps would be followed: - -1. Encode `SignDocAux` (with the same requirement that fields must be serialized - in order): - - ```proto - // types/types.proto - message SignDocAux { - bytes body_bytes = 1; - // PublicKey is included in SignDocAux : - // 1. as a special case for multisig public keys. For multisig public keys, - // the signer should use the top-level multisig public key they are signing - // against, not their own public key. This is to prevent against a form - // of malleability where a signature could be taken out of context of the - // multisig key that was intended to be signed for - // 2. to guard against scenario where configuration information is encoded - // in public keys (it has been proposed) such that two keys can generate - // the same signature but have different security properties - // - // By including it here, the composer of AuthInfo cannot reference the - // a public key variant the signer did not intend to use - PublicKey public_key = 2; - string chain_id = 3; - uint64 account_number = 4; - } - ``` - -2. Sign the encoded `SignDocAux` bytes -3. Send their signature and `SignerInfo` to primary signer who will then - sign and broadcast the final transaction (with `SIGN_MODE_DIRECT` and `AuthInfo` - added) once enough signatures have been collected - -### `SIGN_MODE_DIRECT_RELAXED` - -(_Documented as option (1)(a) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933_) - -This is a variation of `SIGN_MODE_DIRECT` where multiple signers wouldn't need to -coordinate public keys and signing modes in advance. It would involve an alternate -`SignDoc` similar to `SignDocAux` above with fee. This could be added in the future -if client developers found the burden of collecting public keys and modes in advance -too burdensome. - -## Consequences - -### Positive - -* Significant performance gains. -* Supports backward and forward type compatibility. -* Better support for cross-language clients. -* Multiple signing modes allow for greater protocol evolution - -### Negative - -* `google.protobuf.Any` type URLs increase transaction size although the effect - may be negligible or compression may be able to mitigate it. - -### Neutral - -## References diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-021-protobuf-query-encoding.md b/versioned_docs/version-0.46/integrate/architecture/adr-021-protobuf-query-encoding.md deleted file mode 100644 index 9037d14a8..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-021-protobuf-query-encoding.md +++ /dev/null @@ -1,256 +0,0 @@ -# ADR 021: Protocol Buffer Query Encoding - -## Changelog - -* 2020 March 27: Initial Draft - -## Status - -Accepted - -## Context - -This ADR is a continuation of the motivation, design, and context established in -[ADR 019](./adr-019-protobuf-state-encoding.md) and -[ARD 020](./adr-019-protobuf-transaction-encoding.md), namely, we aim to design the -Protocol Buffer migration path for the client-side of the Cosmos SDK. - -This ADR continues from [ARD 020](./adr-020-protobuf-transaction-encoding.md) -to specify the encoding of queries. - -## Decision - -### Custom Query Definition - -Modules define custom queries through a protocol buffers `service` definition. -These `service` definitions are generally associated with and used by the -GRPC protocol. However, the protocol buffers specification indicates that -they can be used more generically by any request/response protocol that uses -protocol buffer encoding. Thus, we can use `service` definitions for specifying -custom ABCI queries and even reuse a substantial amount of the GRPC infrastructure. - -Each module with custom queries should define a service canonically named `Query`: - -```proto -// x/bank/types/types.proto - -service Query { - rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { } - rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { } -} -``` - -#### Handling of Interface Types - -Modules that use interface types and need true polymorphism generally force a -`oneof` up to the app-level that provides the set of concrete implementations of -that interface that the app supports. While app's are welcome to do the same for -queries and implement an app-level query service, it is recommended that modules -provide query methods that expose these interfaces via `google.protobuf.Any`. -There is a concern on the transaction level that the overhead of `Any` is too -high to justify its usage. However for queries this is not a concern, and -providing generic module-level queries that use `Any` does not preclude apps -from also providing app-level queries that return use the app-level `oneof`s. - -A hypothetical example for the `gov` module would look something like: - -```proto -// x/gov/types/types.proto - -import "google/protobuf/any.proto"; - -service Query { - rpc GetProposal(GetProposalParams) returns (AnyProposal) { } -} - -message AnyProposal { - ProposalBase base = 1; - google.protobuf.Any content = 2; -} -``` - -### Custom Query Implementation - -In order to implement the query service, we can reuse the existing [gogo protobuf](https://github.com/gogo/protobuf) -grpc plugin, which for a service named `Query` generates an interface named -`QueryServer` as below: - -```go -type QueryServer interface { - QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) - QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) -} -``` - -The custom queries for our module are implemented by implementing this interface. - -The first parameter in this generated interface is a generic `context.Context`, -whereas querier methods generally need an instance of `sdk.Context` to read -from the store. Since arbitrary values can be attached to `context.Context` -using the `WithValue` and `Value` methods, the Cosmos SDK should provide a function -`sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided -`context.Context`. - -An example implementation of `QueryBalance` for the bank module as above would -look something like: - -```go -type Querier struct { - Keeper -} - -func (q Querier) QueryBalance(ctx context.Context, params *types.QueryBalanceParams) (*sdk.Coin, error) { - balance := q.GetBalance(sdk.UnwrapSDKContext(ctx), params.Address, params.Denom) - return &balance, nil -} -``` - -### Custom Query Registration and Routing - -Query server implementations as above would be registered with `AppModule`s using -a new method `RegisterQueryService(grpc.Server)` which could be implemented simply -as below: - -```go -// x/bank/module.go -func (am AppModule) RegisterQueryService(server grpc.Server) { - types.RegisterQueryServer(server, keeper.Querier{am.keeper}) -} -``` - -Underneath the hood, a new method `RegisterService(sd *grpc.ServiceDesc, handler interface{})` -will be added to the existing `baseapp.QueryRouter` to add the queries to the custom -query routing table (with the routing method being described below). -The signature for this method matches the existing -`RegisterServer` method on the GRPC `Server` type where `handler` is the custom -query server implementation described above. - -GRPC-like requests are routed by the service name (ex. `cosmos_sdk.x.bank.v1.Query`) -and method name (ex. `QueryBalance`) combined with `/`s to form a full -method name (ex. `/cosmos_sdk.x.bank.v1.Query/QueryBalance`). This gets translated -into an ABCI query as `custom/cosmos_sdk.x.bank.v1.Query/QueryBalance`. Service handlers -registered with `QueryRouter.RegisterService` will be routed this way. - -Beyond the method name, GRPC requests carry a protobuf encoded payload, which maps naturally -to `RequestQuery.Data`, and receive a protobuf encoded response or error. Thus -there is a quite natural mapping of GRPC-like rpc methods to the existing -`sdk.Query` and `QueryRouter` infrastructure. - -This basic specification allows us to reuse protocol buffer `service` definitions -for ABCI custom queries substantially reducing the need for manual decoding and -encoding in query methods. - -### GRPC Protocol Support - -In addition to providing an ABCI query pathway, we can easily provide a GRPC -proxy server that routes requests in the GRPC protocol to ABCI query requests -under the hood. In this way, clients could use their host languages' existing -GRPC implementations to make direct queries against Cosmos SDK app's using -these `service` definitions. In order for this server to work, the `QueryRouter` -on `BaseApp` will need to expose the service handlers registered with -`QueryRouter.RegisterService` to the proxy server implementation. Nodes could -launch the proxy server on a separate port in the same process as the ABCI app -with a command-line flag. - -### REST Queries and Swagger Generation - -[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) is a project that -translates REST calls into GRPC calls using special annotations on service -methods. Modules that want to expose REST queries should add `google.api.http` -annotations to their `rpc` methods as in this example below. - -```proto -// x/bank/types/types.proto - -service Query { - rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { - option (google.api.http) = { - get: "/x/bank/v1/balance/{address}/{denom}" - }; - } - rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { - option (google.api.http) = { - get: "/x/bank/v1/balances/{address}" - }; - } -} -``` - -grpc-gateway will work direcly against the GRPC proxy described above which will -translate requests to ABCI queries under the hood. grpc-gateway can also -generate Swagger definitions automatically. - -In the current implementation of REST queries, each module needs to implement -REST queries manually in addition to ABCI querier methods. Using the grpc-gateway -approach, there will be no need to generate separate REST query handlers, just -query servers as described above as grpc-gateway handles the translation of protobuf -to REST as well as Swagger definitions. - -The Cosmos SDK should provide CLI commands for apps to start GRPC gateway either in -a separate process or the same process as the ABCI app, as well as provide a -command for generating grpc-gateway proxy `.proto` files and the `swagger.json` -file. - -### Client Usage - -The gogo protobuf grpc plugin generates client interfaces in addition to server -interfaces. For the `Query` service defined above we would get a `QueryClient` -interface like: - -```go -type QueryClient interface { - QueryBalance(ctx context.Context, in *QueryBalanceParams, opts ...grpc.CallOption) (*types.Coin, error) - QueryAllBalances(ctx context.Context, in *QueryAllBalancesParams, opts ...grpc.CallOption) (*QueryAllBalancesResponse, error) -} -``` - -Via a small patch to gogo protobuf ([gogo/protobuf#675](https://github.com/gogo/protobuf/pull/675)) -we have tweaked the grpc codegen to use an interface rather than concrete type -for the generated client struct. This allows us to also reuse the GRPC infrastructure -for ABCI client queries. - -1Context`will receive a new method`QueryConn`that returns a`ClientConn` -that routes calls to ABCI queries - -Clients (such as CLI methods) will then be able to call query methods like this: - -```go -clientCtx := client.NewContext() -queryClient := types.NewQueryClient(clientCtx.QueryConn()) -params := &types.QueryBalanceParams{addr, denom} -result, err := queryClient.QueryBalance(gocontext.Background(), params) -``` - -### Testing - -Tests would be able to create a query client directly from keeper and `sdk.Context` -references using a `QueryServerTestHelper` as below: - -```go -queryHelper := baseapp.NewQueryServerTestHelper(ctx) -types.RegisterQueryServer(queryHelper, keeper.Querier{app.BankKeeper}) -queryClient := types.NewQueryClient(queryHelper) -``` - -## Future Improvements - -## Consequences - -### Positive - -* greatly simplified querier implementation (no manual encoding/decoding) -* easy query client generation (can use existing grpc and swagger tools) -* no need for REST query implementations -* type safe query methods (generated via grpc plugin) -* going forward, there will be less breakage of query methods because of the -backwards compatibility guarantees provided by buf - -### Negative - -* all clients using the existing ABCI/REST queries will need to be refactored -for both the new GRPC/REST query paths as well as protobuf/proto-json encoded -data, but this is more or less unavoidable in the protobuf refactoring - -### Neutral - -## References diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-022-custom-panic-handling.md b/versioned_docs/version-0.46/integrate/architecture/adr-022-custom-panic-handling.md deleted file mode 100644 index 6ed7b6246..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-022-custom-panic-handling.md +++ /dev/null @@ -1,218 +0,0 @@ -# ADR 022: Custom BaseApp panic handling - -## Changelog - -* 2020 Apr 24: Initial Draft -* 2021 Sep 14: Superseded by ADR-045 - -## Status - -SUPERSEDED by ADR-045 - -## Context - -The current implementation of BaseApp does not allow developers to write custom error handlers during panic recovery -[runTx()](https://github.com/cosmos/cosmos-sdk/blob/bad4ca75f58b182f600396ca350ad844c18fc80b/baseapp/baseapp.go#L539) -method. We think that this method can be more flexible and can give Cosmos SDK users more options for customizations without -the need to rewrite whole BaseApp. Also there's one special case for `sdk.ErrorOutOfGas` error handling, that case -might be handled in a "standard" way (middleware) alongside the others. - -We propose middleware-solution, which could help developers implement the following cases: - -* add external logging (let's say sending reports to external services like [Sentry](https://sentry.io)); -* call panic for specific error cases; - -It will also make `OutOfGas` case and `default` case one of the middlewares. -`Default` case wraps recovery object to an error and logs it ([example middleware implementation](#Recovery-middleware)). - -Our project has a sidecar service running alongside the blockchain node (smart contracts virtual machine). It is -essential that node <-> sidecar connectivity stays stable for TXs processing. So when the communication breaks we need -to crash the node and reboot it once the problem is solved. That behaviour makes node's state machine execution -deterministic. As all keeper panics are caught by runTx's `defer()` handler, we have to adjust the BaseApp code -in order to customize it. - -## Decision - -### Design - -#### Overview - -Instead of hardcoding custom error handling into BaseApp we suggest using set of middlewares which can be customized -externally and will allow developers use as many custom error handlers as they want. Implementation with tests -can be found [here](https://github.com/cosmos/cosmos-sdk/pull/6053). - -#### Implementation details - -##### Recovery handler - -New `RecoveryHandler` type added. `recoveryObj` input argument is an object returned by the standard Go function -`recover()` from the `builtin` package. - -```go -type RecoveryHandler func(recoveryObj interface{}) error -``` - -Handler should type assert (or other methods) an object to define if object should be handled. -`nil` should be returned if input object can't be handled by that `RecoveryHandler` (not a handler's target type). -Not `nil` error should be returned if input object was handled and middleware chain execution should be stopped. - -An example: - -```go -func exampleErrHandler(recoveryObj interface{}) error { - err, ok := recoveryObj.(error) - if !ok { return nil } - - if someSpecificError.Is(err) { - panic(customPanicMsg) - } else { - return nil - } -} -``` - -This example breaks the application execution, but it also might enrich the error's context like the `OutOfGas` handler. - -##### Recovery middleware - -We also add a middleware type (decorator). That function type wraps `RecoveryHandler` and returns the next middleware in -execution chain and handler's `error`. Type is used to separate actual `recovery()` object handling from middleware -chain processing. - -```go -type recoveryMiddleware func(recoveryObj interface{}) (recoveryMiddleware, error) - -func newRecoveryMiddleware(handler RecoveryHandler, next recoveryMiddleware) recoveryMiddleware { - return func(recoveryObj interface{}) (recoveryMiddleware, error) { - if err := handler(recoveryObj); err != nil { - return nil, err - } - return next, nil - } -} -``` - -Function receives a `recoveryObj` object and returns: - -* (next `recoveryMiddleware`, `nil`) if object wasn't handled (not a target type) by `RecoveryHandler`; -* (`nil`, not nil `error`) if input object was handled and other middlewares in the chain should not be executed; -* (`nil`, `nil`) in case of invalid behavior. Panic recovery might not have been properly handled; -this can be avoided by always using a `default` as a rightmost middleware in the chain (always returns an `error`'); - -`OutOfGas` middleware example: - -```go -func newOutOfGasRecoveryMiddleware(gasWanted uint64, ctx sdk.Context, next recoveryMiddleware) recoveryMiddleware { - handler := func(recoveryObj interface{}) error { - err, ok := recoveryObj.(sdk.ErrorOutOfGas) - if !ok { return nil } - - return sdkerrors.Wrap( - sdkerrors.ErrOutOfGas, fmt.Sprintf( - "out of gas in location: %v; gasWanted: %d, gasUsed: %d", err.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(), - ), - ) - } - - return newRecoveryMiddleware(handler, next) -} -``` - -`Default` middleware example: - -```go -func newDefaultRecoveryMiddleware() recoveryMiddleware { - handler := func(recoveryObj interface{}) error { - return sdkerrors.Wrap( - sdkerrors.ErrPanic, fmt.Sprintf("recovered: %v\nstack:\n%v", recoveryObj, string(debug.Stack())), - ) - } - - return newRecoveryMiddleware(handler, nil) -} -``` - -##### Recovery processing - -Basic chain of middlewares processing would look like: - -```go -func processRecovery(recoveryObj interface{}, middleware recoveryMiddleware) error { - if middleware == nil { return nil } - - next, err := middleware(recoveryObj) - if err != nil { return err } - if next == nil { return nil } - - return processRecovery(recoveryObj, next) -} -``` - -That way we can create a middleware chain which is executed from left to right, the rightmost middleware is a -`default` handler which must return an `error`. - -##### BaseApp changes - -The `default` middleware chain must exist in a `BaseApp` object. `Baseapp` modifications: - -```go -type BaseApp struct { - // ... - runTxRecoveryMiddleware recoveryMiddleware -} - -func NewBaseApp(...) { - // ... - app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware() -} - -func (app *BaseApp) runTx(...) { - // ... - defer func() { - if r := recover(); r != nil { - recoveryMW := newOutOfGasRecoveryMiddleware(gasWanted, ctx, app.runTxRecoveryMiddleware) - err, result = processRecovery(r, recoveryMW), nil - } - - gInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed()} - }() - // ... -} -``` - -Developers can add their custom `RecoveryHandler`s by providing `AddRunTxRecoveryHandler` as a BaseApp option parameter to the `NewBaseapp` constructor: - -```go -func (app *BaseApp) AddRunTxRecoveryHandler(handlers ...RecoveryHandler) { - for _, h := range handlers { - app.runTxRecoveryMiddleware = newRecoveryMiddleware(h, app.runTxRecoveryMiddleware) - } -} -``` - -This method would prepend handlers to an existing chain. - -## Consequences - -### Positive - -* Developers of Cosmos SDK based projects can add custom panic handlers to: - * add error context for custom panic sources (panic inside of custom keepers); - * emit `panic()`: passthrough recovery object to the Tendermint core; - * other necessary handling; -* Developers can use standard Cosmos SDK `BaseApp` implementation, rather that rewriting it in their projects; -* Proposed solution doesn't break the current "standard" `runTx()` flow; - -### Negative - -* Introduces changes to the execution model design. - -### Neutral - -* `OutOfGas` error handler becomes one of the middlewares; -* Default panic handler becomes one of the middlewares; - -## References - -* [PR-6053 with proposed solution](https://github.com/cosmos/cosmos-sdk/pull/6053) -* [Similar solution. ADR-010 Modular AnteHandler](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-010-modular-antehandler.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-023-protobuf-naming.md b/versioned_docs/version-0.46/integrate/architecture/adr-023-protobuf-naming.md deleted file mode 100644 index 4360befde..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-023-protobuf-naming.md +++ /dev/null @@ -1,263 +0,0 @@ -# ADR 023: Protocol Buffer Naming and Versioning Conventions - -## Changelog - -* 2020 April 27: Initial Draft -* 2020 August 5: Update guidelines - -## Status - -Accepted - -## Context - -Protocol Buffers provide a basic [style guide](https://developers.google.com/protocol-buffers/docs/style) -and [Buf](https://buf.build/docs/style-guide) builds upon that. To the -extent possible, we want to follow industry accepted guidelines and wisdom for -the effective usage of protobuf, deviating from those only when there is clear -rationale for our use case. - -### Adoption of `Any` - -The adoption of `google.protobuf.Any` as the recommended approach for encoding -interface types (as opposed to `oneof`) makes package naming a central part -of the encoding as fully-qualified message names now appear in encoded -messages. - -### Current Directory Organization - -Thus far we have mostly followed [Buf's](https://buf.build) [DEFAULT](https://buf.build/docs/lint-checkers#default) -recommendations, with the minor deviation of disabling [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout) -which although being convenient for developing code comes with the warning -from Buf that: - -> you will have a very bad time with many Protobuf plugins across various languages if you do not do this - -### Adoption of gRPC Queries - -In [ADR 021](adr-021-protobuf-query-encoding.md), gRPC was adopted for Protobuf -native queries. The full gRPC service path thus becomes a key part of ABCI query -path. In the future, gRPC queries may be allowed from within persistent scripts -by technologies such as CosmWasm and these query routes would be stored within -script binaries. - -## Decision - -The goal of this ADR is to provide thoughtful naming conventions that: - -* encourage a good user experience for when users interact directly with -.proto files and fully-qualified protobuf names -* balance conciseness against the possibility of either over-optimizing (making -names too short and cryptic) or under-optimizing (just accepting bloated names -with lots of redundant information) - -These guidelines are meant to act as a style guide for both the Cosmos SDK and -third-party modules. - -As a starting point, we should adopt all of the [DEFAULT](https://buf.build/docs/lint-checkers#default) -checkers in [Buf's](https://buf.build) including [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout), -except: - -* [PACKAGE_VERSION_SUFFIX](https://buf.build/docs/lint-checkers#package_version_suffix) -* [SERVICE_SUFFIX](https://buf.build/docs/lint-checkers#service_suffix) - -Further guidelines to be described below. - -### Principles - -#### Concise and Descriptive Names - -Names should be descriptive enough to convey their meaning and distinguish -them from other names. - -Given that we are using fully-qualifed names within -`google.protobuf.Any` as well as within gRPC query routes, we should aim to -keep names concise, without going overboard. The general rule of thumb should -be if a shorter name would convey more or else the same thing, pick the shorter -name. - -For instance, `cosmos.bank.MsgSend` (19 bytes) conveys roughly the same information -as `cosmos_sdk.x.bank.v1.MsgSend` (28 bytes) but is more concise. - -Such conciseness makes names both more pleasant to work with and take up less -space within transactions and on the wire. - -We should also resist the temptation to over-optimize, by making names -cryptically short with abbreviations. For instance, we shouldn't try to -reduce `cosmos.bank.MsgSend` to `csm.bk.MSnd` just to save a few bytes. - -The goal is to make names **_concise but not cryptic_**. - -#### Names are for Clients First - -Package and type names should be chosen for the benefit of users, not -necessarily because of legacy concerns related to the go code-base. - -#### Plan for Longevity - -In the interests of long-term support, we should plan on the names we do -choose to be in usage for a long time, so now is the opportunity to make -the best choices for the future. - -### Versioning - -#### Guidelines on Stable Package Versions - -In general, schema evolution is the way to update protobuf schemas. That means that new fields, -messages, and RPC methods are _added_ to existing schemas and old fields, messages and RPC methods -are maintained as long as possible. - -Breaking things is often unacceptable in a blockchain scenario. For instance, immutable smart contracts -may depend on certain data schemas on the host chain. If the host chain breaks those schemas, the smart -contract may be irreparably broken. Even when things can be fixed (for instance in client software), -this often comes at a high cost. - -Instead of breaking things, we should make every effort to evolve schemas rather than just breaking them. -[Buf](https://buf.build) breaking change detection should be used on all stable (non-alpha or beta) packages -to prevent such breakage. - -With that in mind, different stable versions (i.e. `v1` or `v2`) of a package should more or less be considered -different packages and this should be last resort approach for upgrading protobuf schemas. Scenarios where creating -a `v2` may make sense are: - -* we want to create a new module with similar functionality to an existing module and adding `v2` is the most natural -way to do this. In that case, there are really just two different, but similar modules with different APIs. -* we want to add a new revamped API for an existing module and it's just too cumbersome to add it to the existing package, -so putting it in `v2` is cleaner for users. In this case, care should be made to not deprecate support for -`v1` if it is actively used in immutable smart contracts. - -#### Guidelines on unstable (alpha and beta) package versions - -The following guidelines are recommended for marking packages as alpha or beta: - -* marking something as `alpha` or `beta` should be a last resort and just putting something in the -stable package (i.e. `v1` or `v2`) should be preferred -* a package _should_ be marked as `alpha` _if and only if_ there are active discussions to remove -or significantly alter the package in the near future -* a package _should_ be marked as `beta` _if and only if_ there is an active discussion to -significantly refactor/rework the functionality in the near future but not remove it -* modules _can and should_ have types in both stable (i.e. `v1` or `v2`) and unstable (`alpha` or `beta`) packages. - -_`alpha` and `beta` should not be used to avoid responsibility for maintaining compatibility._ -Whenever code is released into the wild, especially on a blockchain, there is a high cost to changing things. In some -cases, for instance with immutable smart contracts, a breaking change may be impossible to fix. - -When marking something as `alpha` or `beta`, maintainers should ask the questions: - -* what is the cost of asking others to change their code vs the benefit of us maintaining the optionality to change it? -* what is the plan for moving this to `v1` and how will that affect users? - -`alpha` or `beta` should really be used to communicate "changes are planned". - -As a case study, gRPC reflection is in the package `grpc.reflection.v1alpha`. It hasn't been changed since -2017 and it is now used in other widely used software like gRPCurl. Some folks probably use it in production services -and so if they actually went and changed the package to `grpc.reflection.v1`, some software would break and -they probably don't want to do that... So now the `v1alpha` package is more or less the de-facto `v1`. Let's not do that. - -The following are guidelines for working with non-stable packages: - -* [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix) -(ex. `v1alpha1`) _should_ be used for non-stable packages -* non-stable packages should generally be excluded from breaking change detection -* immutable smart contract modules (i.e. CosmWasm) _should_ block smart contracts/persistent -scripts from interacting with `alpha`/`beta` packages - -#### Omit v1 suffix - -Instead of using [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix), -we can omit `v1` for packages that don't actually have a second version. This -allows for more concise names for common use cases like `cosmos.bank.Send`. -Packages that do have a second or third version can indicate that with `.v2` -or `.v3`. - -### Package Naming - -#### Adopt a short, unique top-level package name - -Top-level packages should adopt a short name that is known to not collide with -other names in common usage within the Cosmos ecosystem. In the near future, a -registry should be created to reserve and index top-level package names used -within the Cosmos ecosystem. Because the Cosmos SDK is intended to provide -the top-level types for the Cosmos project, the top-level package name `cosmos` -is recommended for usage within the Cosmos SDK instead of the longer `cosmos_sdk`. -[ICS](https://github.com/cosmos/ics) specifications could consider a -short top-level package like `ics23` based upon the standard number. - -#### Limit sub-package depth - -Sub-package depth should be increased with caution. Generally a single -sub-package is needed for a module or a library. Even though `x` or `modules` -is used in source code to denote modules, this is often unnecessary for .proto -files as modules are the primary thing sub-packages are used for. Only items which -are known to be used infrequently should have deep sub-package depths. - -For the Cosmos SDK, it is recommended that that we simply write `cosmos.bank`, -`cosmos.gov`, etc. rather than `cosmos.x.bank`. In practice, most non-module -types can go straight in the `cosmos` package or we can introduce a -`cosmos.base` package if needed. Note that this naming _will not_ change -go package names, i.e. the `cosmos.bank` protobuf package will still live in -`x/bank`. - -### Message Naming - -Message type names should be as concise possible without losing clarity. `sdk.Msg` -types which are used in transactions will retain the `Msg` prefix as that provides -helpful context. - -### Service and RPC Naming - -[ADR 021](adr-021-protobuf-query-encoding.md) specifies that modules should -implement a gRPC query service. We should consider the principle of conciseness -for query service and RPC names as these may be called from persistent script -modules such as CosmWasm. Also, users may use these query paths from tools like -[gRPCurl](https://github.com/fullstorydev/grpcurl). As an example, we can shorten -`/cosmos_sdk.x.bank.v1.QueryService/QueryBalance` to -`/cosmos.bank.Query/Balance` without losing much useful information. - -RPC request and response types _should_ follow the `ServiceNameMethodNameRequest`/ -`ServiceNameMethodNameResponse` naming convention. i.e. for an RPC method named `Balance` -on the `Query` service, the request and response types would be `QueryBalanceRequest` -and `QueryBalanceResponse`. This will be more self-explanatory than `BalanceRequest` -and `BalanceResponse`. - -#### Use just `Query` for the query service - -Instead of [Buf's default service suffix recommendation](https://github.com/cosmos/cosmos-sdk/pull/6033), -we should simply use the shorter `Query` for query services. - -For other types of gRPC services, we should consider sticking with Buf's -default recommendation. - -#### Omit `Get` and `Query` from query service RPC names - -`Get` and `Query` should be omitted from `Query` service names because they are -redundant in the fully-qualified name. For instance, `/cosmos.bank.Query/QueryBalance` -just says `Query` twice without any new information. - -## Future Improvements - -A registry of top-level package names should be created to coordinate naming -across the ecosystem, prevent collisions, and also help developers discover -useful schemas. A simple starting point would be a git repository with -community-based governance. - -## Consequences - -### Positive - -* names will be more concise and easier to read and type -* all transactions using `Any` will be at shorter (`_sdk.x` and `.v1` will be removed) -* `.proto` file imports will be more standard (without `"third_party/proto"` in -the path) -* code generation will be easier for clients because .proto files will be -in a single `proto/` directory which can be copied rather than scattered -throughout the Cosmos SDK - -### Negative - -### Neutral - -* `.proto` files will need to be reorganized and refactored -* some modules may need to be marked as alpha or beta - -## References diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-024-coin-metadata.md b/versioned_docs/version-0.46/integrate/architecture/adr-024-coin-metadata.md deleted file mode 100644 index 3c55f80f0..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-024-coin-metadata.md +++ /dev/null @@ -1,139 +0,0 @@ -# ADR 024: Coin Metadata - -## Changelog - -* 05/19/2020: Initial draft - -## Status - -Proposed - -## Context - -Assets in the Cosmos SDK are represented via a `Coins` type that consists of an `amount` and a `denom`, -where the `amount` can be any arbitrarily large or small value. In addition, the Cosmos SDK uses an -account-based model where there are two types of primary accounts -- basic accounts and module accounts. -All account types have a set of balances that are composed of `Coins`. The `x/bank` module keeps -track of all balances for all accounts and also keeps track of the total supply of balances in an -application. - -With regards to a balance `amount`, the Cosmos SDK assumes a static and fixed unit of denomination, -regardless of the denomination itself. In other words, clients and apps built atop a Cosmos-SDK-based -chain may choose to define and use arbitrary units of denomination to provide a richer UX, however, by -the time a tx or operation reaches the Cosmos SDK state machine, the `amount` is treated as a single -unit. For example, for the Cosmos Hub (Gaia), clients assume 1 ATOM = 10^6 uatom, and so all txs and -operations in the Cosmos SDK work off of units of 10^6. - -This clearly provides a poor and limited UX especially as interoperability of networks increases and -as a result the total amount of asset types increases. We propose to have `x/bank` additionally keep -track of metadata per `denom` in order to help clients, wallet providers, and explorers improve their -UX and remove the requirement for making any assumptions on the unit of denomination. - -## Decision - -The `x/bank` module will be updated to store and index metadata by `denom`, specifically the "base" or -smallest unit -- the unit the Cosmos SDK state-machine works with. - -Metadata may also include a non-zero length list of denominations. Each entry contains the name of -the denomination `denom`, the exponent to the base and a list of aliases. An entry is to be -interpreted as `1 denom = 10^exponent base_denom` (e.g. `1 ETH = 10^18 wei` and `1 uatom = 10^0 uatom`). - -There are two denominations that are of high importance for clients: the `base`, which is the smallest -possible unit and the `display`, which is the unit that is commonly referred to in human communication -and on exchanges. The values in those fields link to an entry in the list of denominations. - -The list in `denom_units` and the `display` entry may be changed via governance. - -As a result, we can define the type as follows: - -```protobuf -message DenomUnit { - string denom = 1; - uint32 exponent = 2; - repeated string aliases = 3; -} - -message Metadata { - string description = 1; - repeated DenomUnit denom_units = 2; - string base = 3; - string display = 4; -} -``` - -As an example, the ATOM's metadata can be defined as follows: - -```json -{ - "description": "The native staking token of the Cosmos Hub.", - "denom_units": [ - { - "denom": "uatom", - "exponent": 0, - "aliases": [ - "microatom" - ], - }, - { - "denom": "matom", - "exponent": 3, - "aliases": [ - "milliatom" - ] - }, - { - "denom": "atom", - "exponent": 6, - } - ], - "base": "uatom", - "display": "atom", -} -``` - -Given the above metadata, a client may infer the following things: - -* 4.3atom = 4.3 * (10^6) = 4,300,000uatom -* The string "atom" can be used as a display name in a list of tokens. -* The balance 4300000 can be displayed as 4,300,000uatom or 4,300matom or 4.3atom. - The `display` denomination 4.3atom is a good default if the authors of the client don't make - an explicit decision to choose a different representation. - -A client should be able to query for metadata by denom both via the CLI and REST interfaces. In -addition, we will add handlers to these interfaces to convert from any unit to another given unit, -as the base framework for this already exists in the Cosmos SDK. - -Finally, we need to ensure metadata exists in the `GenesisState` of the `x/bank` module which is also -indexed by the base `denom`. - -```go -type GenesisState struct { - SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` - Balances []Balance `json:"balances" yaml:"balances"` - Supply sdk.Coins `json:"supply" yaml:"supply"` - DenomMetadata []Metadata `json:"denom_metadata" yaml:"denom_metadata"` -} -``` - -## Future Work - -In order for clients to avoid having to convert assets to the base denomination -- either manually or -via an endpoint, we may consider supporting automatic conversion of a given unit input. - -## Consequences - -### Positive - -* Provides clients, wallet providers and block explorers with additional data on - asset denomination to improve UX and remove any need to make assumptions on - denomination units. - -### Negative - -* A small amount of required additional storage in the `x/bank` module. The amount - of additional storage should be minimal as the amount of total assets should not - be large. - -### Neutral - -## References diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-027-deterministic-protobuf-serialization.md b/versioned_docs/version-0.46/integrate/architecture/adr-027-deterministic-protobuf-serialization.md deleted file mode 100644 index a4602c264..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-027-deterministic-protobuf-serialization.md +++ /dev/null @@ -1,314 +0,0 @@ -# ADR 027: Deterministic Protobuf Serialization - -## Changelog - -* 2020-08-07: Initial Draft -* 2020-09-01: Further clarify rules - -## Status - -Proposed - -## Abstract - -Fully deterministic structure serialization, which works across many languages and clients, -is needed when signing messages. We need to be sure that whenever we serialize -a data structure, no matter in which supported language, the raw bytes -will stay the same. -[Protobuf](https://developers.google.com/protocol-buffers/docs/proto3) -serialization is not bijective (i.e. there exist a practically unlimited number of -valid binary representations for a given protobuf document)1. - -This document describes a deterministic serialization scheme for -a subset of protobuf documents, that covers this use case but can be reused in -other cases as well. - -### Context - -For signature verification in Cosmos SDK, the signer and verifier need to agree on -the same serialization of a `SignDoc` as defined in -[ADR-020](./adr-020-protobuf-transaction-encoding.md) without transmitting the -serialization. - -Currently, for block signatures we are using a workaround: we create a new [TxRaw](https://github.com/cosmos/cosmos-sdk/blob/9e85e81e0e8140067dd893421290c191529c148c/proto/cosmos/tx/v1beta1/tx.proto#L30) -instance (as defined in [adr-020-protobuf-transaction-encoding](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-020-protobuf-transaction-encoding.md#transactions)) -by converting all [Tx](https://github.com/cosmos/cosmos-sdk/blob/9e85e81e0e8140067dd893421290c191529c148c/proto/cosmos/tx/v1beta1/tx.proto#L13) -fields to bytes on the client side. This adds an additional manual -step when sending and signing transactions. - -### Decision - -The following encoding scheme is to be used by other ADRs, -and in particular for `SignDoc` serialization. - -## Specification - -### Scope - -This ADR defines a protobuf3 serializer. The output is a valid protobuf -serialization, such that every protobuf parser can parse it. - -No maps are supported in version 1 due to the complexity of defining a -deterministic serialization. This might change in future. Implementations must -reject documents containing maps as invalid input. - -### Background - Protobuf3 Encoding - -Most numeric types in protobuf3 are encoded as -[varints](https://developers.google.com/protocol-buffers/docs/encoding#varints). -Varints are at most 10 bytes, and since each varint byte has 7 bits of data, -varints are a representation of `uint70` (70-bit unsigned integer). When -encoding, numeric values are casted from their base type to `uint70`, and when -decoding, the parsed `uint70` is casted to the appropriate numeric type. - -The maximum valid value for a varint that complies with protobuf3 is -`FF FF FF FF FF FF FF FF FF 7F` (i.e. `2**70 -1`). If the field type is -`{,u,s}int64`, the highest 6 bits of the 70 are dropped during decoding, -introducing 6 bits of malleability. If the field type is `{,u,s}int32`, the -highest 38 bits of the 70 are dropped during decoding, introducing 38 bits of -malleability. - -Among other sources of non-determinism, this ADR eliminates the possibility of -encoding malleability. - -### Serialization rules - -The serialization is based on the -[protobuf3 encoding](https://developers.google.com/protocol-buffers/docs/encoding) -with the following additions: - -1. Fields must be serialized only once in ascending order -2. Extra fields or any extra data must not be added -3. [Default values](https://developers.google.com/protocol-buffers/docs/proto3#default) - must be omitted -4. `repeated` fields of scalar numeric types must use - [packed encoding](https://developers.google.com/protocol-buffers/docs/encoding#packed) -5. Varint encoding must not be longer than needed: - * No trailing zero bytes (in little endian, i.e. no leading zeroes in big - endian). Per rule 3 above, the default value of `0` must be omitted, so - this rule does not apply in such cases. - * The maximum value for a varint must be `FF FF FF FF FF FF FF FF FF 01`. - In other words, when decoded, the highest 6 bits of the 70-bit unsigned - integer must be `0`. (10-byte varints are 10 groups of 7 bits, i.e. - 70 bits, of which only the lowest 70-6=64 are useful.) - * The maximum value for 32-bit values in varint encoding must be `FF FF FF FF 0F` - with one exception (below). In other words, when decoded, the highest 38 - bits of the 70-bit unsigned integer must be `0`. - * The one exception to the above is _negative_ `int32`, which must be - encoded using the full 10 bytes for sign extension2. - * The maximum value for Boolean values in varint encoding must be `01` (i.e. - it must be `0` or `1`). Per rule 3 above, the default value of `0` must - be omitted, so if a Boolean is included it must have a value of `1`. - -While rule number 1. and 2. should be pretty straight forward and describe the -default behavior of all protobuf encoders the author is aware of, the 3rd rule -is more interesting. After a protobuf3 deserialization you cannot differentiate -between unset fields and fields set to the default value3. At -serialization level however, it is possible to set the fields with an empty -value or omitting them entirely. This is a significant difference to e.g. JSON -where a property can be empty (`""`, `0`), `null` or undefined, leading to 3 -different documents. - -Omitting fields set to default values is valid because the parser must assign -the default value to fields missing in the serialization4. For scalar -types, omitting defaults is required by the spec5. For `repeated` -fields, not serializing them is the only way to express empty lists. Enums must -have a first element of numeric value 0, which is the default6. And -message fields default to unset7. - -Omitting defaults allows for some amount of forward compatibility: users of -newer versions of a protobuf schema produce the same serialization as users of -older versions as long as newly added fields are not used (i.e. set to their -default value). - -### Implementation - -There are three main implementation strategies, ordered from the least to the -most custom development: - -* **Use a protobuf serializer that follows the above rules by default.** E.g. - [gogoproto](https://pkg.go.dev/github.com/gogo/protobuf/gogoproto) is known to - be compliant by in most cases, but not when certain annotations such as - `nullable = false` are used. It might also be an option to configure an - existing serializer accordingly. -* **Normalize default values before encoding them.** If your serializer follows - rule 1. and 2. and allows you to explicitly unset fields for serialization, - you can normalize default values to unset. This can be done when working with - [protobuf.js](https://www.npmjs.com/package/protobufjs): - - ```js - const bytes = SignDoc.encode({ - bodyBytes: body.length > 0 ? body : null, // normalize empty bytes to unset - authInfoBytes: authInfo.length > 0 ? authInfo : null, // normalize empty bytes to unset - chainId: chainId || null, // normalize "" to unset - accountNumber: accountNumber || null, // normalize 0 to unset - accountSequence: accountSequence || null, // normalize 0 to unset - }).finish(); - ``` - -* **Use a hand-written serializer for the types you need.** If none of the above - ways works for you, you can write a serializer yourself. For SignDoc this - would look something like this in Go, building on existing protobuf utilities: - - ```go - if !signDoc.body_bytes.empty() { - buf.WriteUVarInt64(0xA) // wire type and field number for body_bytes - buf.WriteUVarInt64(signDoc.body_bytes.length()) - buf.WriteBytes(signDoc.body_bytes) - } - - if !signDoc.auth_info.empty() { - buf.WriteUVarInt64(0x12) // wire type and field number for auth_info - buf.WriteUVarInt64(signDoc.auth_info.length()) - buf.WriteBytes(signDoc.auth_info) - } - - if !signDoc.chain_id.empty() { - buf.WriteUVarInt64(0x1a) // wire type and field number for chain_id - buf.WriteUVarInt64(signDoc.chain_id.length()) - buf.WriteBytes(signDoc.chain_id) - } - - if signDoc.account_number != 0 { - buf.WriteUVarInt64(0x20) // wire type and field number for account_number - buf.WriteUVarInt(signDoc.account_number) - } - - if signDoc.account_sequence != 0 { - buf.WriteUVarInt64(0x28) // wire type and field number for account_sequence - buf.WriteUVarInt(signDoc.account_sequence) - } - ``` - -### Test vectors - -Given the protobuf definition `Article.proto` - -```protobuf -package blog; -syntax = "proto3"; - -enum Type { - UNSPECIFIED = 0; - IMAGES = 1; - NEWS = 2; -}; - -enum Review { - UNSPECIFIED = 0; - ACCEPTED = 1; - REJECTED = 2; -}; - -message Article { - string title = 1; - string description = 2; - uint64 created = 3; - uint64 updated = 4; - bool public = 5; - bool promoted = 6; - Type type = 7; - Review review = 8; - repeated string comments = 9; - repeated string backlinks = 10; -}; -``` - -serializing the values - -```yaml -title: "The world needs change 🌳" -description: "" -created: 1596806111080 -updated: 0 -public: true -promoted: false -type: Type.NEWS -review: Review.UNSPECIFIED -comments: ["Nice one", "Thank you"] -backlinks: [] -``` - -must result in the serialization - -```text -0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75 -``` - -When inspecting the serialized document, you see that every second field is -omitted: - -```sh -$ echo 0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75 | xxd -r -p | protoc --decode_raw -1: "The world needs change \360\237\214\263" -3: 1596806111080 -5: 1 -7: 2 -9: "Nice one" -9: "Thank you" -``` - -## Consequences - -Having such an encoding available allows us to get deterministic serialization -for all protobuf documents we need in the context of Cosmos SDK signing. - -### Positive - -* Well defined rules that can be verified independent of a reference - implementation -* Simple enough to keep the barrier to implement transaction signing low -* It allows us to continue to use 0 and other empty values in SignDoc, avoiding - the need to work around 0 sequences. This does not imply the change from - https://github.com/cosmos/cosmos-sdk/pull/6949 should not be merged, but not - too important anymore. - -### Negative - -* When implementing transaction signing, the encoding rules above must be - understood and implemented. -* The need for rule number 3. adds some complexity to implementations. -* Some data structures may require custom code for serialization. Thus - the code is not very portable - it will require additional work for each - client implementing serialization to properly handle custom data structures. - -### Neutral - -### Usage in Cosmos SDK - -For the reasons mentioned above ("Negative" section) we prefer to keep workarounds -for shared data structure. Example: the aforementioned `TxRaw` is using raw bytes -as a workaround. This allows them to use any valid Protobuf library without -the need of implementing a custom serializer that adheres to this standard (and related risks of bugs). - -## References - -* 1 _When a message is serialized, there is no guaranteed order for - how its known or unknown fields should be written. Serialization order is an - implementation detail and the details of any particular implementation may - change in the future. Therefore, protocol buffer parsers must be able to parse - fields in any order._ from - https://developers.google.com/protocol-buffers/docs/encoding#order -* 2 https://developers.google.com/protocol-buffers/docs/encoding#signed_integers -* 3 _Note that for scalar message fields, once a message is parsed - there's no way of telling whether a field was explicitly set to the default - value (for example whether a boolean was set to false) or just not set at all: - you should bear this in mind when defining your message types. For example, - don't have a boolean that switches on some behavior when set to false if you - don't want that behavior to also happen by default._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -* 4 _When a message is parsed, if the encoded message does not - contain a particular singular element, the corresponding field in the parsed - object is set to the default value for that field._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -* 5 _Also note that if a scalar message field is set to its default, - the value will not be serialized on the wire._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -* 6 _For enums, the default value is the first defined enum value, - which must be 0._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -* 7 _For message fields, the field is not set. Its exact value is - language-dependent._ from - https://developers.google.com/protocol-buffers/docs/proto3#default -* Encoding rules and parts of the reasoning taken from - [canonical-proto3 Aaron Craelius](https://github.com/regen-network/canonical-proto3) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-028-public-key-addresses.md b/versioned_docs/version-0.46/integrate/architecture/adr-028-public-key-addresses.md deleted file mode 100644 index 8da5b70d0..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-028-public-key-addresses.md +++ /dev/null @@ -1,329 +0,0 @@ -# ADR 028: Public Key Addresses - -## Changelog - -* 2020/08/18: Initial version -* 2021/01/15: Analysis and algorithm update - -## Status - -Proposed - -## Abstract - -This ADR defines an address format for all addressable Cosmos SDK accounts. That includes: new public key algorithms, multisig public keys, and module accounts. - -## Context - -Issue [\#3685](https://github.com/cosmos/cosmos-sdk/issues/3685) identified that public key -address spaces are currently overlapping. We confirmed that it significantly decreases security of Cosmos SDK. - -### Problem - -An attacker can control an input for an address generation function. This leads to a birthday attack, which significantly decreases the security space. -To overcome this, we need to separate the inputs for different kind of account types: -a security break of one account type shouldn't impact the security of other account types. - -### Initial proposals - -One initial proposal was extending the address length and -adding prefixes for different types of addresses. - -@ethanfrey explained an alternate approach originally used in https://github.com/iov-one/weave: - -> I spent quite a bit of time thinking about this issue while building weave... The other cosmos Sdk. -> Basically I define a condition to be a type and format as human readable string with some binary data appended. This condition is hashed into an Address (again at 20 bytes). The use of this prefix makes it impossible to find a preimage for a given address with a different condition (eg ed25519 vs secp256k1). -> This is explained in depth here https://weave.readthedocs.io/en/latest/design/permissions.html -> And the code is here, look mainly at the top where we process conditions. https://github.com/iov-one/weave/blob/master/conditions.go - -And explained how this approach should be sufficiently collision resistant: - -> Yeah, AFAIK, 20 bytes should be collision resistance when the preimages are unique and not malleable. A space of 2^160 would expect some collision to be likely around 2^80 elements (birthday paradox). And if you want to find a collision for some existing element in the database, it is still 2^160. 2^80 only is if all these elements are written to state. -> The good example you brought up was eg. a public key bytes being a valid public key on two algorithms supported by the codec. Meaning if either was broken, you would break accounts even if they were secured with the safer variant. This is only as the issue when no differentiating type info is present in the preimage (before hashing into an address). -> I would like to hear an argument if the 20 bytes space is an actual issue for security, as I would be happy to increase my address sizes in weave. I just figured cosmos and ethereum and bitcoin all use 20 bytes, it should be good enough. And the arguments above which made me feel it was secure. But I have not done a deeper analysis. - -This led to the first proposal (which we proved to be not good enough): -we concatenate a key type with a public key, hash it and take the first 20 bytes of that hash, summarized as `sha256(keyTypePrefix || keybytes)[:20]`. - -### Review and Discussions - -In [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694) we discussed various solutions. -We agreed that 20 bytes it's not future proof, and extending the address length is the only way to allow addresses of different types, various signature types, etc. -This disqualifies the initial proposal. - -In the issue we discussed various modifications: - -* Choice of the hash function. -* Move the prefix out of the hash function: `keyTypePrefix + sha256(keybytes)[:20]` [post-hash-prefix-proposal]. -* Use double hashing: `sha256(keyTypePrefix + sha256(keybytes)[:20])`. -* Increase to keybytes hash slice from 20 byte to 32 or 40 bytes. We concluded that 32 bytes, produced by a good hash functions is future secure. - -### Requirements - -* Support currently used tools - we don't want to break an ecosystem, or add a long adaptation period. Ref: https://github.com/cosmos/cosmos-sdk/issues/8041 -* Try to keep the address length small - addresses are widely used in state, both as part of a key and object value. - -### Scope - -This ADR only defines a process for the generation of address bytes. For end-user interactions with addresses (through the API, or CLI, etc.), we still use bech32 to format these addresses as strings. This ADR doesn't change that. -Using Bech32 for string encoding gives us support for checksum error codes and handling of user typos. - -## Decision - -We define the following account types, for which we define the address function: - -1. simple accounts: represented by a regular public key (ie: secp256k1, sr25519) -2. naive multisig: accounts composed by other addressable objects (ie: naive multisig) -3. composed accounts with a native address key (ie: bls, group module accounts) -4. module accounts: basically any accounts which cannot sign transactions and which are managed internally by modules - -### Legacy Public Key Addresses Don't Change - -Currently (Jan 2021), the only officially supported Cosmos SDK user accounts are `secp256k1` basic accounts and legacy amino multisig. -They are used in existing Cosmos SDK zones. They use the following address formats: - -* secp256k1: `ripemd160(sha256(pk_bytes))[:20]` -* legacy amino multisig: `sha256(aminoCdc.Marshal(pk))[:20]` - -We don't want to change existing addresses. So the addresses for these two key types will remain the same. - -The current multisig public keys use amino serialization to generate the address. We will retain -those public keys and their address formatting, and call them "legacy amino" multisig public keys -in protobuf. We will also create multisig public keys without amino addresses to be described below. - -### Hash Function Choice - -As in other parts of the Cosmos SDK, we will use `sha256`. - -### Basic Address - -We start with defining a base hash algorithm for generating addresses. Notably, it's used for accounts represented by a single key pair. For each public key schema we have to have an associated `typ` string, which we discuss in a section below. `hash` is the cryptographic hash function defined in the previous section. - -```go -const A_LEN = 32 - -func Hash(typ string, key []byte) []byte { - return hash(hash(typ) + key)[:A_LEN] -} -``` - -The `+` is bytes concatenation, which doesn't use any separator. - -This algorithm is the outcome of a consultation session with a professional cryptographer. -Motivation: this algorithm keeps the address relatively small (length of the `typ` doesn't impact the length of the final address) -and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 bytes of a pubkey hash, significantly reducing the address space). -Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack. - -We use the `address.Hash` function for generating addresses for all accounts represented by a single key: - -* simple public keys: `address.Hash(keyType, pubkey)` - -* aggregated keys (eg: BLS): `address.Hash(keyType, aggregatedPubKey)` -* modules: `address.Hash("module", moduleName)` - -### Composed Addresses - -For simple composed accounts (like new naive multisig), we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address. - -```go -// We don't need a PubKey interface - we need anything which is addressable. -type Addressable interface { - Address() []byte -} - -func Composed(typ string, subaccounts []Addressable) []byte { - addresses = map(subaccounts, \a -> LengthPrefix(a.Address())) - addresses = sort(addresses) - return address.Hash(typ, addresses[0] + ... + addresses[n]) -} -``` - -The `typ` parameter should be a schema descriptor, containing all significant attributes with deterministic serialization (eg: utf8 string). -`LengthPrefix` is a function which prepends 1 byte to the address. The value of that byte is the length of the address bits before prepending. The address must be at most 255 bits long. -We are using `LengthPrefix` to eliminate conflicts - it assures, that for 2 lists of addresses: `as = {a1, a2, ..., an}` and `bs = {b1, b2, ..., bm}` such that every `bi` and `ai` is at most 255 long, `concatenate(map(as, \a -> LengthPrefix(a))) = map(bs, \b -> LengthPrefix(b))` iff `as = bs`. - -Implementation Tip: account implementations should cache addresses. - -#### Multisig Addresses - -For new multisig public keys, we define the `typ` parameter not based on any encoding scheme (amino or protobuf). This avoids issues with non-determinism in the encoding scheme. - -Example: - -```proto -package cosmos.crypto.multisig; - -message PubKey { - uint32 threshold = 1; - repeated google.protobuf.Any pubkeys = 2; -} -``` - -```go -func (multisig PubKey) Address() { - // first gather all nested pub keys - var keys []address.Addressable // cryptotypes.PubKey implements Addressable - for _, _key := range multisig.Pubkeys { - keys = append(keys, key.GetCachedValue().(cryptotypes.PubKey)) - } - - // form the type from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together - prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold) - - // use the Composed function defined above - return address.Composed(prefix, keys) -} -``` - -#### Module Account Addresses - -NOTE: this section is not finalize and it's in active discussion. - -In Basic Address section we defined a module account address as: - -```go -address.Hash("module", moduleName) -``` - -We use `"module"` as a schema type for all module derived addresses. Module accounts can have sub accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key. -Module account addresses are heavily used in the Cosmos SDK so it makes sense to optimize the derivation process: instead of using of using `LengthPrefix` for the module name, we use a null byte (`'\x00'`) as a separator. This works, because null byte is not a part of a valid module name. - -```go -func Module(moduleName string, key []byte) []byte{ - return Hash("module", []byte(moduleName) + 0 + key) -} -``` - -**Example** A lending BTC pool address would be: - -```go -btcPool := address.Module("lending", btc.Addrress()}) -``` - -If we want to create an address for a module account depending on more than one key, we can concatenate them: - -```go -btcAtomAMM := address.Module("amm", btc.Addrress() + atom.Address()}) -``` - -#### Derived Addresses - -We must be able to cryptographically derive one address from another one. The derivation process must guarantee hash properties, hence we use the already defined `Hash` function: - -```go -func Derive(address []byte, derivationKey []byte) []byte { - return Hash(addres, derivationKey) -} -``` - -Note: `Module` is a special case of the more general _derived_ address, where we set the `"module"` string for the _from address_. - -**Example** For a cosmwasm smart-contract address we could use the following construction: - -```go -smartContractAddr := Derived(Module("cosmwasm", smartContractsNamespace), []{smartContractKey}) -``` - -### Schema Types - -A `typ` parameter used in `Hash` function SHOULD be unique for each account type. -Since all Cosmos SDK account types are serialized in the state, we propose to use the protobuf message name string. - -Example: all public key types have a unique protobuf message type similar to: - -```proto -package cosmos.crypto.sr25519; - -message PubKey { - bytes key = 1; -} -``` - -All protobuf messages have unique fully qualified names, in this example `cosmos.crypto.sr25519.PubKey`. -These names are derived directly from .proto files in a standardized way and used -in other places such as the type URL in `Any`s. We can easily obtain the name using -`proto.MessageName(msg)`. - -## Consequences - -### Backwards Compatibility - -This ADR is compatible with what was committed and directly supported in the Cosmos SDK repository. - -### Positive - -* a simple algorithm for generating addresses for new public keys, complex accounts and modules -* the algorithm generalizes _native composed keys_ -* increased security and collision resistance of addresses -* the approach is extensible for future use-cases - one can use other address types, as long as they don't conflict with the address length specified here (20 or 32 bytes). -* support new account types. - -### Negative - -* addresses do not communicate key type, a prefixed approach would have done this -* addresses are 60% longer and will consume more storage space -* requires a refactor of KVStore store keys to handle variable length addresses - -### Neutral - -* protobuf message names are used as key type prefixes - -## Further Discussions - -Some accounts can have a fixed name or may be constructed in other way (eg: modules). We were discussing an idea of an account with a predefined name (eg: `me.regen`), which could be used by institutions. -Without going into details, these kinds of addresses are compatible with the hash based addresses described here as long as they don't have the same length. -More specifically, any special account address must not have a length equal to 20 or 32 bytes. - -## Appendix: Consulting session - -End of Dec 2020 we had a session with [Alan Szepieniec](https://scholar.google.be/citations?user=4LyZn8oAAAAJ&hl=en) to consult the approach presented above. - -Alan general observations: - -* we don’t need 2-preimage resistance -* we need 32bytes address space for collision resistance -* when an attacker can control an input for object with an address then we have a problem with birthday attack -* there is an issue with smart-contracts for hashing -* sha2 mining can be use to breaking address pre-image - -Hashing algorithm - -* any attack breaking blake3 will break blake2 -* Alan is pretty confident about the current security analysis of the blake hash algorithm. It was a finalist, and the author is well known in security analysis. - -Algorithm: - -* Alan recommends to hash the prefix: `address(pub_key) = hash(hash(key_type) + pub_key)[:32]`, main benefits: - * we are free to user arbitrary long prefix names - * we still don’t risk collisions - * switch tables -* discussion about penalization -> about adding prefix post hash -* Aaron asked about post hash prefixes (`address(pub_key) = key_type + hash(pub_key)`) and differences. Alan noted that this approach has longer address space and it’s stronger. - -Algorithm for complex / composed keys: - -* merging tree like addresses with same algorithm are fine - -Module addresses: Should module addresses have different size to differentiate it? - -* we will need to set a pre-image prefix for module addresse to keept them in 32-byte space: `hash(hash('module') + module_key)` -* Aaron observation: we already need to deal with variable length (to not break secp256k1 keys). - -Discssion about arithmetic hash function for ZKP - -* Posseidon / Rescue -* Problem: much bigger risk because we don’t know much techniques and history of crypto-analysis of arithmetic constructions. It’s still a new ground and area of active research. - -Post quantum signature size - -* Alan suggestion: Falcon: speed / size ration - very good. -* Aaron - should we think about it? - Alan: based on early extrapolation this thing will get able to break EC cryptography in 2050 . But that’s a lot of uncertainty. But there is magic happening with recurions / linking / simulation and that can speedup the progress. - -Other ideas - -* Let’s say we use same key and two different address algorithms for 2 different use cases. Is it still safe to use it? Alan: if we want to hide the public key (which is not our use case), then it’s less secure but there are fixes. - -### References - -* [Notes](https://hackmd.io/_NGWI4xZSbKzj1BkCqyZMw) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-029-fee-grant-module.md b/versioned_docs/version-0.46/integrate/architecture/adr-029-fee-grant-module.md deleted file mode 100644 index 5e026d604..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-029-fee-grant-module.md +++ /dev/null @@ -1,153 +0,0 @@ -# ADR 029: Fee Grant Module - -## Changelog - -* 2020/08/18: Initial Draft -* 2021/05/05: Removed height based expiration support and simplified naming. - -## Status - -Accepted - -## Context - -In order to make blockchain transactions, the signing account must possess a sufficient balance of the right denomination -in order to pay fees. There are classes of transactions where needing to maintain a wallet with sufficient fees is a -barrier to adoption. - -For instance, when proper permissions are setup, someone may temporarily delegate the ability to vote on proposals to -a "burner" account that is stored on a mobile phone with only minimal security. - -Other use cases include workers tracking items in a supply chain or farmers submitting field data for analytics -or compliance purposes. - -For all of these use cases, UX would be significantly enhanced by obviating the need for these accounts to always -maintain the appropriate fee balance. This is especially true if we wanted to achieve enterprise adoption for something -like supply chain tracking. - -While one solution would be to have a service that fills up these accounts automatically with the appropriate fees, a better UX -would be provided by allowing these accounts to pull from a common fee pool account with proper spending limits. -A single pool would reduce the churn of making lots of small "fill up" transactions and also more effectively leverages -the resources of the organization setting up the pool. - -## Decision - -As a solution we propose a module, `x/feegrant` which allows one account, the "granter" to grant another account, the "grantee" -an allowance to spend the granter's account balance for fees within certain well-defined limits. - -Fee allowances are defined by the extensible `FeeAllowanceI` interface: - -```go -type FeeAllowanceI { - // Accept can use fee payment requested as well as timestamp of the current block - // to determine whether or not to process this. This is checked in - // Keeper.UseGrantedFees and the return values should match how it is handled there. - // - // If it returns an error, the fee payment is rejected, otherwise it is accepted. - // The FeeAllowance implementation is expected to update it's internal state - // and will be saved again after an acceptance. - // - // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage - // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) - Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (remove bool, err error) - - // ValidateBasic should evaluate this FeeAllowance for internal consistency. - // Don't allow negative amounts, or negative periods for example. - ValidateBasic() error -} -``` - -Two basic fee allowance types, `BasicAllowance` and `PeriodicAllowance` are defined to support known use cases: - -```proto -// BasicAllowance implements FeeAllowanceI with a one-time grant of tokens -// that optionally expires. The delegatee can use up to SpendLimit to cover fees. -message BasicAllowance { - // spend_limit specifies the maximum amount of tokens that can be spent - // by this allowance and will be updated as tokens are spent. If it is - // empty, there is no spend limit and any amount of coins can be spent. - repeated cosmos_sdk.v1.Coin spend_limit = 1; - - // expiration specifies an optional time when this allowance expires - google.protobuf.Timestamp expiration = 2; -} - -// PeriodicAllowance extends FeeAllowanceI to allow for both a maximum cap, -// as well as a limit per time period. -message PeriodicAllowance { - BasicAllowance basic = 1; - - // period specifies the time duration in which period_spend_limit coins can - // be spent before that allowance is reset - google.protobuf.Duration period = 2; - - // period_spend_limit specifies the maximum number of coins that can be spent - // in the period - repeated cosmos_sdk.v1.Coin period_spend_limit = 3; - - // period_can_spend is the number of coins left to be spent before the period_reset time - repeated cosmos_sdk.v1.Coin period_can_spend = 4; - - // period_reset is the time at which this period resets and a new one begins, - // it is calculated from the start time of the first transaction after the - // last period ended - google.protobuf.Timestamp period_reset = 5; -} - -``` - -Allowances can be granted and revoked using `MsgGrantAllowance` and `MsgRevokeAllowance`: - -```proto -// MsgGrantAllowance adds permission for Grantee to spend up to Allowance -// of fees from the account of Granter. -message MsgGrantAllowance { - string granter = 1; - string grantee = 2; - google.protobuf.Any allowance = 3; - } - - // MsgRevokeAllowance removes any existing FeeAllowance from Granter to Grantee. - message MsgRevokeAllowance { - string granter = 1; - string grantee = 2; - } -``` - -In order to use allowances in transactions, we add a new field `granter` to the transaction `Fee` type: - -```proto -package cosmos.tx.v1beta1; - -message Fee { - repeated cosmos.base.v1beta1.Coin amount = 1; - uint64 gas_limit = 2; - string payer = 3; - string granter = 4; -} -``` - -`granter` must either be left empty or must correspond to an account which has granted -a fee allowance to fee payer (either the first signer or the value of the `payer` field). - -A new `AnteDecorator` named `DeductGrantedFeeDecorator` will be created in order to process transactions with `fee_payer` -set and correctly deduct fees based on fee allowances. - -## Consequences - -### Positive - -* improved UX for use cases where it is cumbersome to maintain an account balance just for fees - -### Negative - -### Neutral - -* a new field must be added to the transaction `Fee` message and a new `AnteDecorator` must be -created to use it - -## References - -* Blog article describing initial work: https://medium.com/regen-network/hacking-the-cosmos-cosmwasm-and-key-management-a08b9f561d1b -* Initial public specification: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56 -* Original subkeys proposal from B-harvest which influenced this design: https://github.com/cosmos/cosmos-sdk/issues/4480 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-030-authz-module.md b/versioned_docs/version-0.46/integrate/architecture/adr-030-authz-module.md deleted file mode 100644 index 1390d0f6b..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-030-authz-module.md +++ /dev/null @@ -1,259 +0,0 @@ -# ADR 030: Authorization Module - -## Changelog - -* 2019-11-06: Initial Draft -* 2020-10-12: Updated Draft -* 2020-11-13: Accepted -* 2020-05-06: proto API updates, use `sdk.Msg` instead of `sdk.ServiceMsg` (the latter concept was removed from Cosmos SDK) -* 2022-04-20: Updated the `SendAuthorization` proto docs to clarify the `SpendLimit` is a required field. (Generic authorization can be used with bank msg type url to create limit less bank authorization) - -## Status - -Accepted - -## Abstract - -This ADR defines the `x/authz` module which allows accounts to grant authorizations to perform actions -on behalf of that account to other accounts. - -## Context - -The concrete use cases which motivated this module include: - -* the desire to delegate the ability to vote on proposals to other accounts besides the account which one has -delegated stake -* "sub-keys" functionality, as originally proposed in [\#4480](https://github.com/cosmos/cosmos-sdk/issues/4480) which -is a term used to describe the functionality provided by this module together with -the `fee_grant` module from [ADR 029](./adr-029-fee-grant-module.md) and the [group module](https://github.com/regen-network/cosmos-modules/tree/master/incubator/group). - -The "sub-keys" functionality roughly refers to the ability for one account to grant some subset of its capabilities to -other accounts with possibly less robust, but easier to use security measures. For instance, a master account representing -an organization could grant the ability to spend small amounts of the organization's funds to individual employee accounts. -Or an individual (or group) with a multisig wallet could grant the ability to vote on proposals to any one of the member -keys. - -The current -implementation is based on work done by the [Gaian's team at Hackatom Berlin 2019](https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation). - -## Decision - -We will create a module named `authz` which provides functionality for -granting arbitrary privileges from one account (the _granter_) to another account (the _grantee_). Authorizations -must be granted for a particular `Msg` service methods one by one using an implementation -of `Authorization` interface. - -### Types - -Authorizations determine exactly what privileges are granted. They are extensible -and can be defined for any `Msg` service method even outside of the module where -the `Msg` method is defined. `Authorization`s reference `Msg`s using their TypeURL. - -#### Authorization - -```go -type Authorization interface { - proto.Message - - // MsgTypeURL returns the fully-qualified Msg TypeURL (as described in ADR 020), - // which will process and accept or reject a request. - MsgTypeURL() string - - // Accept determines whether this grant permits the provided sdk.Msg to be performed, and if - // so provides an upgraded authorization instance. - Accept(ctx sdk.Context, msg sdk.Msg) (AcceptResponse, error) - - // ValidateBasic does a simple validation check that - // doesn't require access to any other information. - ValidateBasic() error -} - -// AcceptResponse instruments the controller of an authz message if the request is accepted -// and if it should be updated or deleted. -type AcceptResponse struct { - // If Accept=true, the controller can accept and authorization and handle the update. - Accept bool - // If Delete=true, the controller must delete the authorization object and release - // storage resources. - Delete bool - // Controller, who is calling Authorization.Accept must check if `Updated != nil`. If yes, - // it must use the updated version and handle the update on the storage level. - Updated Authorization -} -``` - -For example a `SendAuthorization` like this is defined for `MsgSend` that takes -a `SpendLimit` and updates it down to zero: - -```go -type SendAuthorization struct { - // SpendLimit specifies the maximum amount of tokens that can be spent - // by this authorization and will be updated as tokens are spent. This field is required. (Generic authorization - // can be used with bank msg type url to create limit less bank authorization). - SpendLimit sdk.Coins -} - -func (a SendAuthorization) MsgTypeURL() string { - return sdk.MsgTypeURL(&MsgSend{}) -} - -func (a SendAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { - mSend, ok := msg.(*MsgSend) - if !ok { - return authz.AcceptResponse{}, sdkerrors.ErrInvalidType.Wrap("type mismatch") - } - limitLeft, isNegative := a.SpendLimit.SafeSub(mSend.Amount) - if isNegative { - return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit") - } - if limitLeft.IsZero() { - return authz.AcceptResponse{Accept: true, Delete: true}, nil - } - - return authz.AcceptResponse{Accept: true, Delete: false, Updated: &SendAuthorization{SpendLimit: limitLeft}}, nil -} -``` - -A different type of capability for `MsgSend` could be implemented -using the `Authorization` interface with no need to change the underlying -`bank` module. - -##### Small notes on `AcceptResponse` - -- The `AcceptResponse.Accept` field will be set to `true` if the authorization is accepted. -However, if it is rejected, the function `Accept` will raise an error (without setting `AcceptResponse.Accept` to `false`). - -- The `AcceptResponse.Updated` field will be set to a non-nil value only if there is a real change to the authorization. -If authorization remains the same (as is, for instance, always the case for a [`GenericAuthorization`](#genericauthorization)), -the field will be `nil`. - -### `Msg` Service - -```proto -service Msg { - // Grant grants the provided authorization to the grantee on the granter's - // account with the provided expiration time. - rpc Grant(MsgGrant) returns (MsgGrantResponse); - - // Exec attempts to execute the provided messages using - // authorizations granted to the grantee. Each message should have only - // one signer corresponding to the granter of the authorization. - rpc Exec(MsgExec) returns (MsgExecResponse); - - // Revoke revokes any authorization corresponding to the provided method name on the - // granter's account that has been granted to the grantee. - rpc Revoke(MsgRevoke) returns (MsgRevokeResponse); -} - -// Grant gives permissions to execute -// the provided method with expiration time. -message Grant { - google.protobuf.Any authorization = 1 [(cosmos_proto.accepts_interface) = "Authorization"]; - google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; -} - -message MsgGrant { - string granter = 1; - string grantee = 2; - - Grant grant = 3 [(gogoproto.nullable) = false]; -} - -message MsgExecResponse { - cosmos.base.abci.v1beta1.Result result = 1; -} - -message MsgExec { - string grantee = 1; - // Authorization Msg requests to execute. Each msg must implement Authorization interface - repeated google.protobuf.Any msgs = 2 [(cosmos_proto.accepts_interface) = "sdk.Msg"];; -} -``` - -### Router Middleware - -The `authz` `Keeper` will expose a `DispatchActions` method which allows other modules to send `Msg`s -to the router based on `Authorization` grants: - -```go -type Keeper interface { - // DispatchActions routes the provided msgs to their respective handlers if the grantee was granted an authorization - // to send those messages by the first (and only) signer of each msg. - DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.Msg) sdk.Result` -} -``` - -### CLI - -#### `tx exec` Method - -When a CLI user wants to run a transaction on behalf of another account using `MsgExec`, they -can use the `exec` method. For instance `gaiacli tx gov vote 1 yes --from --generate-only | gaiacli tx authz exec --send-as --from ` -would send a transaction like this: - -```go -MsgExec { - Grantee: mykey, - Msgs: []sdk.Msg{ - MsgVote { - ProposalID: 1, - Voter: cosmos3thsdgh983egh823 - Option: Yes - } - } -} -``` - -#### `tx grant --from ` - -This CLI command will send a `MsgGrant` transaction. `authorization` should be encoded as -JSON on the CLI. - -#### `tx revoke --from ` - -This CLI command will send a `MsgRevoke` transaction. - -### Built-in Authorizations - -#### `SendAuthorization` - -```proto -// SendAuthorization allows the grantee to spend up to spend_limit coins from -// the granter's account. -message SendAuthorization { - repeated cosmos.base.v1beta1.Coin spend_limit = 1; -} -``` - -#### `GenericAuthorization` - -```proto -// GenericAuthorization gives the grantee unrestricted permissions to execute -// the provided method on behalf of the granter's account. -message GenericAuthorization { - option (cosmos_proto.implements_interface) = "Authorization"; - - // Msg, identified by it's type URL, to grant unrestricted permissions to execute - string msg = 1; -} -``` - -## Consequences - -### Positive - -* Users will be able to authorize arbitrary actions on behalf of their accounts to other -users, improving key management for many use cases -* The solution is more generic than previously considered approaches and the -`Authorization` interface approach can be extended to cover other use cases by -SDK users - -### Negative - -### Neutral - -## References - -* Initial Hackatom implementation: https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation -* Post-Hackatom spec: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#delegation-module -* B-Harvest subkeys spec: https://github.com/cosmos/cosmos-sdk/issues/4480 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-031-msg-service.md b/versioned_docs/version-0.46/integrate/architecture/adr-031-msg-service.md deleted file mode 100644 index 0fcc6b4df..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-031-msg-service.md +++ /dev/null @@ -1,202 +0,0 @@ -# ADR 031: Protobuf Msg Services - -## Changelog - -* 2020-10-05: Initial Draft -* 2021-04-21: Remove `ServiceMsg`s to follow Protobuf `Any`'s spec, see [#9063](https://github.com/cosmos/cosmos-sdk/issues/9063). - -## Status - -Accepted - -## Abstract - -We want to leverage protobuf `service` definitions for defining `Msg`s which will give us significant developer UX -improvements in terms of the code that is generated and the fact that return types will now be well defined. - -## Context - -Currently `Msg` handlers in the Cosmos SDK do have return values that are placed in the `data` field of the response. -These return values, however, are not specified anywhere except in the golang handler code. - -In early conversations [it was proposed](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc/edit) -that `Msg` return types be captured using a protobuf extension field, ex: - -```protobuf -package cosmos.gov; - -message MsgSubmitProposal - option (cosmos_proto.msg_return) = “uint64”; - string delegator_address = 1; - string validator_address = 2; - repeated sdk.Coin amount = 3; -} -``` - -This was never adopted, however. - -Having a well-specified return value for `Msg`s would improve client UX. For instance, -in `x/gov`, `MsgSubmitProposal` returns the proposal ID as a big-endian `uint64`. -This isn’t really documented anywhere and clients would need to know the internals -of the Cosmos SDK to parse that value and return it to users. - -Also, there may be cases where we want to use these return values programatically. -For instance, https://github.com/cosmos/cosmos-sdk/issues/7093 proposes a method for -doing inter-module Ocaps using the `Msg` router. A well-defined return type would -improve the developer UX for this approach. - -In addition, handler registration of `Msg` types tends to add a bit of -boilerplate on top of keepers and is usually done through manual type switches. -This isn't necessarily bad, but it does add overhead to creating modules. - -## Decision - -We decide to use protobuf `service` definitions for defining `Msg`s as well as -the code generated by them as a replacement for `Msg` handlers. - -Below we define how this will look for the `SubmitProposal` message from `x/gov` module. -We start with a `Msg` `service` definition: - -```proto -package cosmos.gov; - -service Msg { - rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse); -} - -// Note that for backwards compatibility this uses MsgSubmitProposal as the request -// type instead of the more canonical MsgSubmitProposalRequest -message MsgSubmitProposal { - google.protobuf.Any content = 1; - string proposer = 2; -} - -message MsgSubmitProposalResponse { - uint64 proposal_id; -} -``` - -While this is most commonly used for gRPC, overloading protobuf `service` definitions like this does not violate -the intent of the [protobuf spec](https://developers.google.com/protocol-buffers/docs/proto3#services) which says: -> If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation. -With this approach, we would get an auto-generated `MsgServer` interface: - -In addition to clearly specifying return types, this has the benefit of generating client and server code. On the server -side, this is almost like an automatically generated keeper method and could maybe be used intead of keepers eventually -(see [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093)): - -```go -package gov - -type MsgServer interface { - SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) -} -``` - -On the client side, developers could take advantage of this by creating RPC implementations that encapsulate transaction -logic. Protobuf libraries that use asynchronous callbacks, like [protobuf.js](https://github.com/protobufjs/protobuf.js#using-services) -could use this to register callbacks for specific messages even for transactions that include multiple `Msg`s. - -Each `Msg` service method should have exactly one request parameter: its corresponding `Msg` type. For example, the `Msg` service method `/cosmos.gov.v1beta1.Msg/SubmitProposal` above has exactly one request parameter, namely the `Msg` type `/cosmos.gov.v1beta1.MsgSubmitProposal`. It is important the reader understands clearly the nomenclature difference between a `Msg` service (a Protobuf service) and a `Msg` type (a Protobuf message), and the differences in their fully-qualified name. - -This convention has been decided over the more canonical `Msg...Request` names mainly for backwards compatibility, but also for better readability in `TxBody.messages` (see [Encoding section](#encoding) below): transactions containing `/cosmos.gov.MsgSubmitProposal` read better than those containing `/cosmos.gov.v1beta1.MsgSubmitProposalRequest`. - -One consequence of this convention is that each `Msg` type can be the request parameter of only one `Msg` service method. However, we consider this limitation a good practice in explicitness. - -### Encoding - -Encoding of transactions generated with `Msg` services do not differ from current Protobuf transaction encoding as defined in [ADR-020](./adr-020-protobuf-transaction-encoding.md). We are encoding `Msg` types (which are exactly `Msg` service methods' request parameters) as `Any` in `Tx`s which involves packing the -binary-encoded `Msg` with its type URL. - -### Decoding - -Since `Msg` types are packed into `Any`, decoding transactions messages are done by unpacking `Any`s into `Msg` types. For more information, please refer to [ADR-020](./adr-020-protobuf-transaction-encoding.md#transactions). - -### Routing - -We propose to add a `msg_service_router` in BaseApp. This router is a key/value map which maps `Msg` types' `type_url`s to their corresponding `Msg` service method handler. Since there is a 1-to-1 mapping between `Msg` types and `Msg` service method, the `msg_service_router` has exactly one entry per `Msg` service method. - -When a transaction is processed by BaseApp (in CheckTx or in DeliverTx), its `TxBody.messages` are decoded as `Msg`s. Each `Msg`'s `type_url` is matched against an entry in the `msg_service_router`, and the respective `Msg` service method handler is called. - -For backward compatability, the old handlers are not removed yet. If BaseApp receives a legacy `Msg` with no correspoding entry in the `msg_service_router`, it will be routed via its legacy `Route()` method into the legacy handler. - -### Module Configuration - -In [ADR 021](./adr-021-protobuf-query-encoding.md), we introduced a method `RegisterQueryService` -to `AppModule` which allows for modules to register gRPC queriers. - -To register `Msg` services, we attempt a more extensible approach by converting `RegisterQueryService` -to a more generic `RegisterServices` method: - -```go -type AppModule interface { - RegisterServices(Configurator) - ... -} - -type Configurator interface { - QueryServer() grpc.Server - MsgServer() grpc.Server -} - -// example module: -func (am AppModule) RegisterServices(cfg Configurator) { - types.RegisterQueryServer(cfg.QueryServer(), keeper) - types.RegisterMsgServer(cfg.MsgServer(), keeper) -} -``` - -The `RegisterServices` method and the `Configurator` interface are intended to -evolve to satisfy the use cases discussed in [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) -and [\#7122](https://github.com/cosmos/cosmos-sdk/issues/7421). - -When `Msg` services are registered, the framework _should_ verify that all `Msg` types -implement the `sdk.Msg` interface and throw an error during initialization rather -than later when transactions are processed. - -### `Msg` Service Implementation - -Just like query services, `Msg` service methods can retrieve the `sdk.Context` -from the `context.Context` parameter method using the `sdk.UnwrapSDKContext` -method: - -```go -package gov - -func (k Keeper) SubmitProposal(goCtx context.Context, params *types.MsgSubmitProposal) (*MsgSubmitProposalResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - ... -} -``` - -The `sdk.Context` should have an `EventManager` already attached by BaseApp's `msg_service_router`. - -Separate handler definition is no longer needed with this approach. - -## Consequences - -This design changes how a module functionality is exposed and accessed. It deprecates the existing `Handler` interface and `AppModule.Route` in favor of [Protocol Buffer Services](https://developers.google.com/protocol-buffers/docs/proto3#services) and Service Routing described above. This dramatically simplifies the code. We don't need to create handlers and keepers any more. Use of Protocol Buffer auto-generated clients clearly separates the communication interfaces between the module and a modules user. The control logic (aka handlers and keepers) is not exposed any more. A module interface can be seen as a black box accessible through a client API. It's worth to note that the client interfaces are also generated by Protocol Buffers. - -This also allows us to change how we perform functional tests. Instead of mocking AppModules and Router, we will mock a client (server will stay hidden). More specifically: we will never mock `moduleA.MsgServer` in `moduleB`, but rather `moduleA.MsgClient`. One can think about it as working with external services (eg DBs, or online servers...). We assume that the transmission between clients and servers is correctly handled by generated Protocol Buffers. - -Finally, closing a module to client API opens desirable OCAP patterns discussed in ADR-033. Since server implementation and interface is hidden, nobody can hold "keepers"/servers and will be forced to relay on the client interface, which will drive developers for correct encapsulation and software engineering patterns. - -### Pros - -* communicates return type clearly -* manual handler registration and return type marshaling is no longer needed, just implement the interface and register it -* communication interface is automatically generated, the developer can now focus only on the state transition methods - this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that -* generated client code could be useful for clients and tests -* dramatically reduces and simplifies the code - -### Cons - -* using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec) - -## References - -* [Initial Github Issue \#7122](https://github.com/cosmos/cosmos-sdk/issues/7122) -* [proto 3 Language Guide: Defining Services](https://developers.google.com/protocol-buffers/docs/proto3#services) -* [Initial pre-`Any` `Msg` designs](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc) -* [ADR 020](./adr-020-protobuf-transaction-encoding.md) -* [ADR 021](./adr-021-protobuf-query-encoding.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-032-typed-events.md b/versioned_docs/version-0.46/integrate/architecture/adr-032-typed-events.md deleted file mode 100644 index 1e769f450..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-032-typed-events.md +++ /dev/null @@ -1,319 +0,0 @@ -# ADR 032: Typed Events - -## Changelog - -* 28-Sept-2020: Initial Draft - -## Authors - -* Anil Kumar (@anilcse) -* Jack Zampolin (@jackzampolin) -* Adam Bozanich (@boz) - -## Status - -Proposed - -## Abstract - -Currently in the Cosmos SDK, events are defined in the handlers for each message as well as `BeginBlock` and `EndBlock`. Each module doesn't have types defined for each event, they are implemented as `map[string]string`. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team. - -## Context - -Currently in the Cosmos SDK, events are defined in the handlers for each message, meaning each module doesn't have a cannonical set of types for each event. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team. - -[Our platform](http://github.com/ovrclk/akash) requires a number of programatic on chain interactions both on the provider (datacenter - to bid on new orders and listen for leases created) and user (application developer - to send the app manifest to the provider) side. In addition the Akash team is now maintaining the IBC [`relayer`](https://github.com/ovrclk/relayer), another very event driven process. In working on these core pieces of infrastructure, and integrating lessons learned from Kubernetes developement, our team has developed a standard method for defining and consuming typed events in Cosmos SDK modules. We have found that it is extremely useful in building this type of event driven application. - -As the Cosmos SDK gets used more extensively for apps like `peggy`, other peg zones, IBC, DeFi, etc... there will be an exploding demand for event driven applications to support new features desired by users. We propose upstreaming our findings into the Cosmos SDK to enable all Cosmos SDK applications to quickly and easily build event driven apps to aid their core application. Wallets, exchanges, explorers, and defi protocols all stand to benefit from this work. - -If this proposal is accepted, users will be able to build event driven Cosmos SDK apps in go by just writing `EventHandler`s for their specific event types and passing them to `EventEmitters` that are defined in the Cosmos SDK. - -The end of this proposal contains a detailed example of how to consume events after this refactor. - -This proposal is specifically about how to consume these events as a client of the blockchain, not for intermodule communication. - -## Decision - -**Step-1**: Implement additional functionality in the `types` package: `EmitTypedEvent` and `ParseTypedEvent` functions - -```go -// types/events.go - -// EmitTypedEvent takes typed event and emits converting it into sdk.Event -func (em *EventManager) EmitTypedEvent(event proto.Message) error { - evtType := proto.MessageName(event) - evtJSON, err := codec.ProtoMarshalJSON(event) - if err != nil { - return err - } - - var attrMap map[string]json.RawMessage - err = json.Unmarshal(evtJSON, &attrMap) - if err != nil { - return err - } - - var attrs []abci.EventAttribute - for k, v := range attrMap { - attrs = append(attrs, abci.EventAttribute{ - Key: []byte(k), - Value: v, - }) - } - - em.EmitEvent(Event{ - Type: evtType, - Attributes: attrs, - }) - - return nil -} - -// ParseTypedEvent converts abci.Event back to typed event -func ParseTypedEvent(event abci.Event) (proto.Message, error) { - concreteGoType := proto.MessageType(event.Type) - if concreteGoType == nil { - return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type) - } - - var value reflect.Value - if concreteGoType.Kind() == reflect.Ptr { - value = reflect.New(concreteGoType.Elem()) - } else { - value = reflect.Zero(concreteGoType) - } - - protoMsg, ok := value.Interface().(proto.Message) - if !ok { - return nil, fmt.Errorf("%q does not implement proto.Message", event.Type) - } - - attrMap := make(map[string]json.RawMessage) - for _, attr := range event.Attributes { - attrMap[string(attr.Key)] = attr.Value - } - - attrBytes, err := json.Marshal(attrMap) - if err != nil { - return nil, err - } - - err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg) - if err != nil { - return nil, err - } - - return protoMsg, nil -} -``` - -Here, the `EmitTypedEvent` is a method on `EventManager` which takes typed event as input and apply json serialization on it. Then it maps the JSON key/value pairs to `event.Attributes` and emits it in form of `sdk.Event`. `Event.Type` will be the type URL of the proto message. - -When we subscribe to emitted events on the tendermint websocket, they are emitted in the form of an `abci.Event`. `ParseTypedEvent` parses the event back to it's original proto message. - -**Step-2**: Add proto definitions for typed events for msgs in each module: - -For example, let's take `MsgSubmitProposal` of `gov` module and implement this event's type. - -```protobuf -// proto/cosmos/gov/v1beta1/gov.proto -// Add typed event definition - -package cosmos.gov.v1beta1; - -message EventSubmitProposal { - string from_address = 1; - uint64 proposal_id = 2; - TextProposal proposal = 3; -} -``` - -**Step-3**: Refactor event emission to use the typed event created and emit using `sdk.EmitTypedEvent`: - -```go -// x/gov/handler.go -func handleMsgSubmitProposal(ctx sdk.Context, keeper keeper.Keeper, msg types.MsgSubmitProposalI) (*sdk.Result, error) { - ... - types.Context.EventManager().EmitTypedEvent( - &EventSubmitProposal{ - FromAddress: fromAddress, - ProposalId: id, - Proposal: proposal, - }, - ) - ... -} -``` - -### How to subscribe to these typed events in `Client` - -> NOTE: Full code example below - -Users will be able to subscribe using `client.Context.Client.Subscribe` and consume events which are emitted using `EventHandler`s. - -Akash Network has built a simple [`pubsub`](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/pubsub/bus.go#L20). This can be used to subscribe to `abci.Events` and [publish](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L21) them as typed events. - -Please see the below code sample for more detail on this flow looks for clients. - -## Consequences - -### Positive - -* Improves consistency of implementation for the events currently in the Cosmos SDK -* Provides a much more ergonomic way to handle events and facilitates writing event driven applications -* This implementation will support a middleware ecosystem of `EventHandler`s - -### Negative - -## Detailed code example of publishing events - -This ADR also proposes adding affordances to emit and consume these events. This way developers will only need to write -`EventHandler`s which define the actions they desire to take. - -```go -// EventEmitter is a type that describes event emitter functions -// This should be defined in `types/events.go` -type EventEmitter func(context.Context, client.Context, ...EventHandler) error - -// EventHandler is a type of function that handles events coming out of the event bus -// This should be defined in `types/events.go` -type EventHandler func(proto.Message) error - -// Sample use of the functions below -func main() { - ctx, cancel := context.WithCancel(context.Background()) - - if err := TxEmitter(ctx, client.Context{}.WithNodeURI("tcp://localhost:26657"), SubmitProposalEventHandler); err != nil { - cancel() - panic(err) - } - - return -} - -// SubmitProposalEventHandler is an example of an event handler that prints proposal details -// when any EventSubmitProposal is emitted. -func SubmitProposalEventHandler(ev proto.Message) (err error) { - switch event := ev.(type) { - // Handle governance proposal events creation events - case govtypes.EventSubmitProposal: - // Users define business logic here e.g. - fmt.Println(ev.FromAddress, ev.ProposalId, ev.Proposal) - return nil - default: - return nil - } -} - -// TxEmitter is an example of an event emitter that emits just transaction events. This can and -// should be implemented somewhere in the Cosmos SDK. The Cosmos SDK can include an EventEmitters for tm.event='Tx' -// and/or tm.event='NewBlock' (the new block events may contain typed events) -func TxEmitter(ctx context.Context, cliCtx client.Context, ehs ...EventHandler) (err error) { - // Instantiate and start tendermint RPC client - client, err := cliCtx.GetNode() - if err != nil { - return err - } - - if err = client.Start(); err != nil { - return err - } - - // Start the pubsub bus - bus := pubsub.NewBus() - defer bus.Close() - - // Initialize a new error group - eg, ctx := errgroup.WithContext(ctx) - - // Publish chain events to the pubsub bus - eg.Go(func() error { - return PublishChainTxEvents(ctx, client, bus, simapp.ModuleBasics) - }) - - // Subscribe to the bus events - subscriber, err := bus.Subscribe() - if err != nil { - return err - } - - // Handle all the events coming out of the bus - eg.Go(func() error { - var err error - for { - select { - case <-ctx.Done(): - return nil - case <-subscriber.Done(): - return nil - case ev := <-subscriber.Events(): - for _, eh := range ehs { - if err = eh(ev); err != nil { - break - } - } - } - } - return nil - }) - - return group.Wait() -} - -// PublishChainTxEvents events using tmclient. Waits on context shutdown signals to exit. -func PublishChainTxEvents(ctx context.Context, client tmclient.EventsClient, bus pubsub.Bus, mb module.BasicManager) (err error) { - // Subscribe to transaction events - txch, err := client.Subscribe(ctx, "txevents", "tm.event='Tx'", 100) - if err != nil { - return err - } - - // Unsubscribe from transaction events on function exit - defer func() { - err = client.UnsubscribeAll(ctx, "txevents") - }() - - // Use errgroup to manage concurrency - g, ctx := errgroup.WithContext(ctx) - - // Publish transaction events in a goroutine - g.Go(func() error { - var err error - for { - select { - case <-ctx.Done(): - break - case ed := <-ch: - switch evt := ed.Data.(type) { - case tmtypes.EventDataTx: - if !evt.Result.IsOK() { - continue - } - // range over events, parse them using the basic manager and - // send them to the pubsub bus - for _, abciEv := range events { - typedEvent, err := sdk.ParseTypedEvent(abciEv) - if err != nil { - return er - } - if err := bus.Publish(typedEvent); err != nil { - bus.Close() - return - } - continue - } - } - } - } - return err - }) - - // Exit on error or context cancelation - return g.Wait() -} -``` - -## References - -* [Publish Custom Events via a bus](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L19-L58) -* [Consuming the events in `Client`](https://github.com/ovrclk/deploy/blob/bf6c633ab6c68f3026df59efd9982d6ca1bf0561/cmd/event-handlers.go#L57) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-033-protobuf-inter-module-comm.md b/versioned_docs/version-0.46/integrate/architecture/adr-033-protobuf-inter-module-comm.md deleted file mode 100644 index 1bf7f8b04..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-033-protobuf-inter-module-comm.md +++ /dev/null @@ -1,400 +0,0 @@ -# ADR 033: Protobuf-based Inter-Module Communication - -## Changelog - -* 2020-10-05: Initial Draft - -## Status - -Proposed - -## Abstract - -This ADR introduces a system for permissioned inter-module communication leveraging the protobuf `Query` and `Msg` -service definitions defined in [ADR 021](./adr-021-protobuf-query-encoding.md) and -[ADR 031](./adr-031-msg-service.md) which provides: - -* stable protobuf based module interfaces to potentially later replace the keeper paradigm -* stronger inter-module object capabilities (OCAPs) guarantees -* module accounts and sub-account authorization - -## Context - -In the current Cosmos SDK documentation on the [Object-Capability Model](../01-tx-lifecycle.mdocap.md), it is stated that: - -> We assume that a thriving ecosystem of Cosmos SDK modules that are easy to compose into a blockchain application will contain faulty or malicious modules. - -There is currently not a thriving ecosystem of Cosmos SDK modules. We hypothesize that this is in part due to: - -1. lack of a stable v1.0 Cosmos SDK to build modules off of. Module interfaces are changing, sometimes dramatically, from -point release to point release, often for good reasons, but this does not create a stable foundation to build on. -2. lack of a properly implemented object capability or even object-oriented encapsulation system which makes refactors -of module keeper interfaces inevitable because the current interfaces are poorly constrained. - -### `x/bank` Case Study - -Currently the `x/bank` keeper gives pretty much unrestricted access to any module which references it. For instance, the -`SetBalance` method allows the caller to set the balance of any account to anything, bypassing even proper tracking of supply. - -There appears to have been some later attempts to implement some semblance of OCAPs using module-level minting, staking -and burning permissions. These permissions allow a module to mint, burn or delegate tokens with reference to the module’s -own account. These permissions are actually stored as a `[]string` array on the `ModuleAccount` type in state. - -However, these permissions don’t really do much. They control what modules can be referenced in the `MintCoins`, -`BurnCoins` and `DelegateCoins***` methods, but for one there is no unique object capability token that controls access — -just a simple string. So the `x/upgrade` module could mint tokens for the `x/staking` module simple by calling -`MintCoins(“staking”)`. Furthermore, all modules which have access to these keeper methods, also have access to -`SetBalance` negating any other attempt at OCAPs and breaking even basic object-oriented encapsulation. - -## Decision - -Based on [ADR-021](./adr-021-protobuf-query-encoding.md) and [ADR-031](./adr-031-msg-service.md), we introduce the -Inter-Module Communication framework for secure module authorization and OCAPs. -When implemented, this could also serve as an alternative to the existing paradigm of passing keepers between -modules. The approach outlined here-in is intended to form the basis of a Cosmos SDK v1.0 that provides the necessary -stability and encapsulation guarantees that allow a thriving module ecosystem to emerge. - -Of particular note — the decision is to _enable_ this functionality for modules to adopt at their own discretion. -Proposals to migrate existing modules to this new paradigm will have to be a separate conversation, potentially -addressed as amendments to this ADR. - -### New "Keeper" Paradigm - -In [ADR 021](./adr-021-protobuf-query-encoding.md), a mechanism for using protobuf service definitions to define queriers -was introduced and in [ADR 31](./adr-031-msg-service.md), a mechanism for using protobuf service to define `Msg`s was added. -Protobuf service definitions generate two golang interfaces representing the client and server sides of a service plus -some helper code. Here is a minimal example for the bank `cosmos.bank.Msg/Send` message type: - -```go -package bank - -type MsgClient interface { - Send(context.Context, *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error) -} - -type MsgServer interface { - Send(context.Context, *MsgSend) (*MsgSendResponse, error) -} -``` - -[ADR 021](./adr-021-protobuf-query-encoding.md) and [ADR 31](./adr-031-msg-service.md) specifies how modules can implement the generated `QueryServer` -and `MsgServer` interfaces as replacements for the legacy queriers and `Msg` handlers respectively. - -In this ADR we explain how modules can make queries and send `Msg`s to other modules using the generated `QueryClient` -and `MsgClient` interfaces and propose this mechanism as a replacement for the existing `Keeper` paradigm. To be clear, -this ADR does not necessitate the creation of new protobuf definitions or services. Rather, it leverages the same proto -based service interfaces already used by clients for inter-module communication. - -Using this `QueryClient`/`MsgClient` approach has the following key benefits over exposing keepers to external modules: - -1. Protobuf types are checked for breaking changes using [buf](https://buf.build/docs/breaking-overview) and because of -the way protobuf is designed this will give us strong backwards compatibility guarantees while allowing for forward -evolution. -2. The separation between the client and server interfaces will allow us to insert permission checking code in between -the two which checks if one module is authorized to send the specified `Msg` to the other module providing a proper -object capability system (see below). -3. The router for inter-module communication gives us a convenient place to handle rollback of transactions, -enabling atomicy of operations ([currently a problem](https://github.com/cosmos/cosmos-sdk/issues/8030)). Any failure within a module-to-module call would result in a failure of the entire -transaction - -This mechanism has the added benefits of: - -* reducing boilerplate through code generation, and -* allowing for modules in other languages either via a VM like CosmWasm or sub-processes using gRPC - -### Inter-module Communication - -To use the `Client` generated by the protobuf compiler we need a `grpc.ClientConn` [interface](https://github.com/regen-network/protobuf/blob/cosmos/grpc/types.go#L12) -implementation. For this we introduce -a new type, `ModuleKey`, which implements the `grpc.ClientConn` interface. `ModuleKey` can be thought of as the "private -key" corresponding to a module account, where authentication is provided through use of a special `Invoker()` function, -described in more detail below. - -Blockchain users (external clients) use their account's private key to sign transactions containing `Msg`s where they are listed as signers (each -message specifies required signers with `Msg.GetSigner`). The authentication checks is performed by `AnteHandler`. - -Here, we extend this process, by allowing modules to be identified in `Msg.GetSigners`. When a module wants to trigger the execution a `Msg` in another module, -its `ModuleKey` acts as the sender (through the `ClientConn` interface we describe below) and is set as a sole "signer". It's worth to note -that we don't use any cryptographic signature in this case. -For example, module `A` could use its `A.ModuleKey` to create `MsgSend` object for `/cosmos.bank.Msg/Send` transaction. `MsgSend` validation -will assure that the `from` account (`A.ModuleKey` in this case) is the signer. - -Here's an example of a hypothetical module `foo` interacting with `x/bank`: - -```go -package foo - - -type FooMsgServer { - // ... - - bankQuery bank.QueryClient - bankMsg bank.MsgClient -} - -func NewFooMsgServer(moduleKey RootModuleKey, ...) FooMsgServer { - // ... - - return FooMsgServer { - // ... - modouleKey: moduleKey, - bankQuery: bank.NewQueryClient(moduleKey), - bankMsg: bank.NewMsgClient(moduleKey), - } -} - -func (foo *FooMsgServer) Bar(ctx context.Context, req *MsgBarRequest) (*MsgBarResponse, error) { - balance, err := foo.bankQuery.Balance(&bank.QueryBalanceRequest{Address: fooMsgServer.moduleKey.Address(), Denom: "foo"}) - - ... - - res, err := foo.bankMsg.Send(ctx, &bank.MsgSendRequest{FromAddress: fooMsgServer.moduleKey.Address(), ...}) - - ... -} -``` - -This design is also intended to be extensible to cover use cases of more fine grained permissioning like minting by -denom prefix being restricted to certain modules (as discussed in -[#7459](https://github.com/cosmos/cosmos-sdk/pull/7459#discussion_r529545528)). - -### `ModuleKey`s and `ModuleID`s - -A `ModuleKey` can be thought of as a "private key" for a module account and a `ModuleID` can be thought of as the -corresponding "public key". From the [ADR 028](./adr-028-public-key-addresses.md), modules can have both a root module account and any number of sub-accounts -or derived accounts that can be used for different pools (ex. staking pools) or managed accounts (ex. group -accounts). We can also think of module sub-accounts as similar to derived keys - there is a root key and then some -derivation path. `ModuleID` is a simple struct which contains the module name and optional "derivation" path, -and forms its address based on the `AddressHash` method from [the ADR-028](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-028-public-key-addresses.md): - -```go -type ModuleID struct { - ModuleName string - Path []byte -} - -func (key ModuleID) Address() []byte { - return AddressHash(key.ModuleName, key.Path) -} -``` - -In addition to being able to generate a `ModuleID` and address, a `ModuleKey` contains a special function called -`Invoker` which is the key to safe inter-module access. The `Invoker` creates an `InvokeFn` closure which is used as an `Invoke` method in -the `grpc.ClientConn` interface and under the hood is able to route messages to the appropriate `Msg` and `Query` handlers -performing appropriate security checks on `Msg`s. This allows for even safer inter-module access than keeper's whose -private member variables could be manipulated through reflection. Golang does not support reflection on a function -closure's captured variables and direct manipulation of memory would be needed for a truly malicious module to bypass -the `ModuleKey` security. - -The two `ModuleKey` types are `RootModuleKey` and `DerivedModuleKey`: - -```go -type Invoker func(callInfo CallInfo) func(ctx context.Context, request, response interface{}, opts ...interface{}) error - -type CallInfo { - Method string - Caller ModuleID -} - -type RootModuleKey struct { - moduleName string - invoker Invoker -} - -func (rm RootModuleKey) Derive(path []byte) DerivedModuleKey { /* ... */} - -type DerivedModuleKey struct { - moduleName string - path []byte - invoker Invoker -} -``` - -A module can get access to a `DerivedModuleKey`, using the `Derive(path []byte)` method on `RootModuleKey` and then -would use this key to authenticate `Msg`s from a sub-account. Ex: - -```go -package foo - -func (fooMsgServer *MsgServer) Bar(ctx context.Context, req *MsgBar) (*MsgBarResponse, error) { - derivedKey := fooMsgServer.moduleKey.Derive(req.SomePath) - bankMsgClient := bank.NewMsgClient(derivedKey) - res, err := bankMsgClient.Balance(ctx, &bank.MsgSend{FromAddress: derivedKey.Address(), ...}) - ... -} -``` - -In this way, a module can gain permissioned access to a root account and any number of sub-accounts and send -authenticated `Msg`s from these accounts. The `Invoker` `callInfo.Caller` parameter is used under the hood to -distinguish between different module accounts, but either way the function returned by `Invoker` only allows `Msg`s -from either the root or a derived module account to pass through. - -Note that `Invoker` itself returns a function closure based on the `CallInfo` passed in. This will allow client implementations -in the future that cache the invoke function for each method type avoiding the overhead of hash table lookup. -This would reduce the performance overhead of this inter-module communication method to the bare minimum required for -checking permissions. - -To re-iterate, the closure only allows access to authorized calls. There is no access to anything else regardless of any -name impersonation. - -Below is a rough sketch of the implementation of `grpc.ClientConn.Invoke` for `RootModuleKey`: - -```go -func (key RootModuleKey) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...grpc.CallOption) error { - f := key.invoker(CallInfo {Method: method, Caller: ModuleID {ModuleName: key.moduleName}}) - return f(ctx, args, reply) -} -``` - -### `AppModule` Wiring and Requirements - -In [ADR 031](./adr-031-msg-service.md), the `AppModule.RegisterService(Configurator)` method was introduced. To support -inter-module communication, we extend the `Configurator` interface to pass in the `ModuleKey` and to allow modules to -specify their dependencies on other modules using `RequireServer()`: - -```go -type Configurator interface { - MsgServer() grpc.Server - QueryServer() grpc.Server - - ModuleKey() ModuleKey - RequireServer(msgServer interface{}) -} -``` - -The `ModuleKey` is passed to modules in the `RegisterService` method itself so that `RegisterServices` serves as a single -entry point for configuring module services. This is intended to also have the side-effect of greatly reducing boilerplate in -`app.go`. For now, `ModuleKey`s will be created based on `AppModuleBasic.Name()`, but a more flexible system may be -introduced in the future. The `ModuleManager` will handle creation of module accounts behind the scenes. - -Because modules do not get direct access to each other anymore, modules may have unfulfilled dependencies. To make sure -that module dependencies are resolved at startup, the `Configurator.RequireServer` method should be added. The `ModuleManager` -will make sure that all dependencies declared with `RequireServer` can be resolved before the app starts. An example -module `foo` could declare it's dependency on `x/bank` like this: - -```go -package foo - -func (am AppModule) RegisterServices(cfg Configurator) { - cfg.RequireServer((*bank.QueryServer)(nil)) - cfg.RequireServer((*bank.MsgServer)(nil)) -} -``` - -### Security Considerations - -In addition to checking for `ModuleKey` permissions, a few additional security precautions will need to be taken by -the underlying router infrastructure. - -#### Recursion and Re-entry - -Recursive or re-entrant method invocations pose a potential security threat. This can be a problem if Module A -calls Module B and Module B calls module A again in the same call. - -One basic way for the router system to deal with this is to maintain a call stack which prevents a module from -being referenced more than once in the call stack so that there is no re-entry. A `map[string]interface{}` table -in the router could be used to perform this security check. - -#### Queries - -Queries in Cosmos SDK are generally un-permissioned so allowing one module to query another module should not pose -any major security threats assuming basic precautions are taken. The basic precaution that the router system will -need to take is making sure that the `sdk.Context` passed to query methods does not allow writing to the store. This -can be done for now with a `CacheMultiStore` as is currently done for `BaseApp` queries. - -### Internal Methods - -In many cases, we may wish for modules to call methods on other modules which are not exposed to clients at all. For this -purpose, we add the `InternalServer` method to `Configurator`: - -```go -type Configurator interface { - MsgServer() grpc.Server - QueryServer() grpc.Server - InternalServer() grpc.Server -} -``` - -As an example, x/slashing's Slash must call x/staking's Slash, but we don't want to expose x/staking's Slash to end users -and clients. - -Internal protobuf services will be defined in a corresponding `internal.proto` file in the given module's -proto package. - -Services registered against `InternalServer` will be callable from other modules but not by external clients. - -An alternative solution to internal-only methods could involve hooks / plugins as discussed [here](https://github.com/cosmos/cosmos-sdk/pull/7459#issuecomment-733807753). -A more detailed evaluation of a hooks / plugin system will be addressed later in follow-ups to this ADR or as a separate -ADR. - -### Authorization - -By default, the inter-module router requires that messages are sent by the first signer returned by `GetSigners`. The -inter-module router should also accept authorization middleware such as that provided by [ADR 030](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-030-authz-module.md). -This middleware will allow accounts to otherwise specific module accounts to perform actions on their behalf. -Authorization middleware should take into account the need to grant certain modules effectively "admin" privileges to -other modules. This will be addressed in separate ADRs or updates to this ADR. - -### Future Work - -Other future improvements may include: - -* custom code generation that: - * simplifies interfaces (ex. generates code with `sdk.Context` instead of `context.Context`) - * optimizes inter-module calls - for instance caching resolved methods after first invocation -* combining `StoreKey`s and `ModuleKey`s into a single interface so that modules have a single OCAPs handle -* code generation which makes inter-module communication more performant -* decoupling `ModuleKey` creation from `AppModuleBasic.Name()` so that app's can override root module account names -* inter-module hooks and plugins - -## Alternatives - -### MsgServices vs `x/capability` - -The `x/capability` module does provide a proper object-capability implementation that can be used by any module in the -Cosmos SDK and could even be used for inter-module OCAPs as described in [\#5931](https://github.com/cosmos/cosmos-sdk/issues/5931). - -The advantages of the approach described in this ADR are mostly around how it integrates with other parts of the Cosmos SDK, -specifically: - -* protobuf so that: - * code generation of interfaces can be leveraged for a better dev UX - * module interfaces are versioned and checked for breakage using [buf](https://docs.buf.build/breaking-overview) -* sub-module accounts as per ADR 028 -* the general `Msg` passing paradigm and the way signers are specified by `GetSigners` - -Also, this is a complete replacement for keepers and could be applied to _all_ inter-module communication whereas the -`x/capability` approach in #5931 would need to be applied method by method. - -## Consequences - -### Backwards Compatibility - -This ADR is intended to provide a pathway to a scenario where there is greater long term compatibility between modules. -In the short-term, this will likely result in breaking certain `Keeper` interfaces which are too permissive and/or -replacing `Keeper` interfaces altogether. - -### Positive - -* an alternative to keepers which can more easily lead to stable inter-module interfaces -* proper inter-module OCAPs -* improved module developer DevX, as commented on by several particpants on - [Architecture Review Call, Dec 3](https://hackmd.io/E0wxxOvRQ5qVmTf6N_k84Q) -* lays the groundwork for what can be a greatly simplified `app.go` -* router can be setup to enforce atomic transactions for module-to-module calls - -### Negative - -* modules which adopt this will need significant refactoring - -### Neutral - -## Test Cases [optional] - -## References - -* [ADR 021](./adr-021-protobuf-query-encoding.md) -* [ADR 031](./adr-031-msg-service.md) -* [ADR 028](./adr-028-public-key-addresses.md) -* [ADR 030 draft](https://github.com/cosmos/cosmos-sdk/pull/7105) -* [Object-Capability Model](../docs/01-tx-lifecycle.mdocap.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-034-account-rekeying.md b/versioned_docs/version-0.46/integrate/architecture/adr-034-account-rekeying.md deleted file mode 100644 index cd9b91469..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-034-account-rekeying.md +++ /dev/null @@ -1,76 +0,0 @@ -# ADR 034: Account Rekeying - -## Changelog - -* 30-09-2020: Initial Draft - -## Status - -PROPOSED - -## Abstract - -Account rekeying is a process hat allows an account to replace its authentication pubkey with a new one. - -## Context - -Currently, in the Cosmos SDK, the address of an auth `BaseAccount` is based on the hash of the public key. Once an account is created, the public key for the account is set in stone, and cannot be changed. This can be a problem for users, as key rotation is a useful security practice, but is not possible currently. Furthermore, as multisigs are a type of pubkey, once a multisig for an account is set, it can not be updated. This is problematic, as multisigs are often used by organizations or companies, who may need to change their set of multisig signers for internal reasons. - -Transferring all the assets of an account to a new account with the updated pubkey is not sufficient, because some "engagements" of an account are not easily transferable. For example, in staking, to transfer bonded Atoms, an account would have to unbond all delegations and wait the three week unbonding period. Even more significantly, for validator operators, ownership over a validator is not transferrable at all, meaning that the operator key for a validator can never be updated, leading to poor operational security for validators. - -## Decision - -We propose the addition of a new feature to `x/auth` that allows accounts to update the public key associated with their account, while keeping the address the same. - -This is possible because the Cosmos SDK `BaseAccount` stores the public key for an account in state, instead of making the assumption that the public key is included in the transaction (whether explicitly or implicitly through the signature) as in other blockchains such as Bitcoin and Ethereum. Because the public key is stored on chain, it is okay for the public key to not hash to the address of an account, as the address is not pertinent to the signature checking process. - -To build this system, we design a new Msg type as follows: - -```protobuf -service Msg { - rpc ChangePubKey(MsgChangePubKey) returns (MsgChangePubKeyResponse); -} - -message MsgChangePubKey { - string address = 1; - google.protobuf.Any pub_key = 2; -} - -message MsgChangePubKeyResponse {} -``` - -The MsgChangePubKey transaction needs to be signed by the existing pubkey in state. - -Once, approved, the handler for this message type, which takes in the AccountKeeper, will update the in-state pubkey for the account and replace it with the pubkey from the Msg. - -An account that has had its pubkey changed cannot be automatically pruned from state. This is because if pruned, the original pubkey of the account would be needed to recreate the same address, but the owner of the address may not have the original pubkey anymore. Currently, we do not automatically prune any accounts anyways, but we would like to keep this option open the road (this is the purpose of account numbers). To resolve this, we charge an additional gas fee for this operation to compensate for this this externality (this bound gas amount is configured as parameter `PubKeyChangeCost`). The bonus gas is charged inside the handler, using the `ConsumeGas` function. Furthermore, in the future, we can allow accounts that have rekeyed manually prune themselves using a new Msg type such as `MsgDeleteAccount`. Manually pruning accounts can give a gas refund as an incentive for performing the action. - -```go - amount := ak.GetParams(ctx).PubKeyChangeCost - ctx.GasMeter().ConsumeGas(amount, "pubkey change fee") -``` - -Everytime a key for an address is changed, we will store a log of this change in the state of the chain, thus creating a stack of all previous keys for an address and the time intervals for which they were active. This allows dapps and clients to easily query past keys for an account which may be useful for features such as verifying timestamped off-chain signed messages. - -## Consequences - -### Positive - -* Will allow users and validator operators to employ better operational security practices with key rotation. -* Will allow organizations or groups to easily change and add/remove multisig signers. - -### Negative - -Breaks the current assumed relationship between address and pubkeys as H(pubkey) = address. This has a couple of consequences. - -* This makes wallets that support this feature more complicated. For example, if an address on chain was updated, the corresponding key in the CLI wallet also needs to be updated. -* Cannot automatically prune accounts with 0 balance that have had their pubkey changed. - -### Neutral - -* While the purpose of this is intended to allow the owner of an account to update to a new pubkey they own, this could technically also be used to transfer ownership of an account to a new owner. For example, this could be use used to sell a staked position without unbonding or an account that has vesting tokens. However, the friction of this is very high as this would essentially have to be done as a very specific OTC trade. Furthermore, additional constraints could be added to prevent accouns with Vesting tokens to use this feature. -* Will require that PubKeys for an account are included in the genesis exports. - -## References - -* https://www.algorand.com/resources/blog/announcing-rekeying diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-035-rosetta-api-support.md b/versioned_docs/version-0.46/integrate/architecture/adr-035-rosetta-api-support.md deleted file mode 100644 index 01a81048b..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-035-rosetta-api-support.md +++ /dev/null @@ -1,211 +0,0 @@ -# ADR 035: Rosetta API Support - -## Authors - -* Jonathan Gimeno (@jgimeno) -* David Grierson (@senormonito) -* Alessio Treglia (@alessio) -* Frojdy Dymylja (@fdymylja) - -## Changelog - -* 2021-05-12: the external library [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) has been moved within the Cosmos SDK. - -## Context - -[Rosetta API](https://www.rosetta-api.org/) is an open-source specification and set of tools developed by Coinbase to -standardise blockchain interactions. - -Through the use of a standard API for integrating blockchain applications it will - -* Be easier for a user to interact with a given blockchain -* Allow exchanges to integrate new blockchains quickly and easily -* Enable application developers to build cross-blockchain applications such as block explorers, wallets and dApps at - considerably lower cost and effort. - -## Decision - -It is clear that adding Rosetta API support to the Cosmos SDK will bring value to all the developers and -Cosmos SDK based chains in the ecosystem. How it is implemented is key. - -The driving principles of the proposed design are: - -1. **Extensibility:** it must be as riskless and painless as possible for application developers to set-up network - configurations to expose Rosetta API-compliant services. -2. **Long term support:** This proposal aims to provide support for all the supported Cosmos SDK release series. -3. **Cost-efficiency:** Backporting changes to Rosetta API specifications from `master` to the various stable - branches of Cosmos SDK is a cost that needs to be reduced. - -We will achieve these delivering on these principles by the following: - -1. There will be a package `rosetta/lib` - for the implementation of the core Rosetta API features, particularly: - a. The types and interfaces (`Client`, `OfflineClient`...), this separates design from implementation detail. - b. The `Server` functionality as this is independent of the Cosmos SDK version. - c. The `Online/OfflineNetwork`, which is not exported, and implements the rosetta API using the `Client` interface to query the node, build tx and so on. - d. The `errors` package to extend rosetta errors. -2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface. -3. There will be two options for starting an API service in applications: - a. API shares the application process - b. API-specific process. - -## Architecture - -### The External Repo - -As section will describe the proposed external library, including the service implementation, plus the defined types and interfaces. - -#### Server - -`Server` is a simple `struct` that is started and listens to the port specified in the settings. This is meant to be used across all the Cosmos SDK versions that are actively supported. - -The constructor follows: - -`func NewServer(settings Settings) (Server, error)` - -`Settings`, which are used to construct a new server, are the following: - -```go -// Settings define the rosetta server settings -type Settings struct { - // Network contains the information regarding the network - Network *types.NetworkIdentifier - // Client is the online API handler - Client crgtypes.Client - // Listen is the address the handler will listen at - Listen string - // Offline defines if the rosetta service should be exposed in offline mode - Offline bool - // Retries is the number of readiness checks that will be attempted when instantiating the handler - // valid only for online API - Retries int - // RetryWait is the time that will be waited between retries - RetryWait time.Duration -} -``` - -#### Types - -Package types uses a mixture of rosetta types and custom defined type wrappers, that the client must parse and return while executing operations. - -##### Interfaces - -Every SDK version uses a different format to connect (rpc, gRPC, etc), query and build transactions, we have abstracted this in what is the `Client` interface. -The client uses rosetta types, whilst the `Online/OfflineNetwork` takes care of returning correctly parsed rosetta responses and errors. - -Each Cosmos SDK release series will have their own `Client` implementations. -Developers can implement their own custom `Client`s as required. - -```go -// Client defines the API the client implementation should provide. -type Client interface { - // Needed if the client needs to perform some action before connecting. - Bootstrap() error - // Ready checks if the servicer constraints for queries are satisfied - // for example the node might still not be ready, it's useful in process - // when the rosetta instance might come up before the node itself - // the servicer must return nil if the node is ready - Ready() error - - // Data API - - // Balances fetches the balance of the given address - // if height is not nil, then the balance will be displayed - // at the provided height, otherwise last block balance will be returned - Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error) - // BlockByHashAlt gets a block and its transaction at the provided height - BlockByHash(ctx context.Context, hash string) (BlockResponse, error) - // BlockByHeightAlt gets a block given its height, if height is nil then last block is returned - BlockByHeight(ctx context.Context, height *int64) (BlockResponse, error) - // BlockTransactionsByHash gets the block, parent block and transactions - // given the block hash. - BlockTransactionsByHash(ctx context.Context, hash string) (BlockTransactionsResponse, error) - // BlockTransactionsByHash gets the block, parent block and transactions - // given the block hash. - BlockTransactionsByHeight(ctx context.Context, height *int64) (BlockTransactionsResponse, error) - // GetTx gets a transaction given its hash - GetTx(ctx context.Context, hash string) (*types.Transaction, error) - // GetUnconfirmedTx gets an unconfirmed Tx given its hash - // NOTE(fdymylja): NOT IMPLEMENTED YET! - GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error) - // Mempool returns the list of the current non confirmed transactions - Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error) - // Peers gets the peers currently connected to the node - Peers(ctx context.Context) ([]*types.Peer, error) - // Status returns the node status, such as sync data, version etc - Status(ctx context.Context) (*types.SyncStatus, error) - - // Construction API - - // PostTx posts txBytes to the node and returns the transaction identifier plus metadata related - // to the transaction itself. - PostTx(txBytes []byte) (res *types.TransactionIdentifier, meta map[string]interface{}, err error) - // ConstructionMetadataFromOptions - ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error) - OfflineClient -} - -// OfflineClient defines the functionalities supported without having access to the node -type OfflineClient interface { - NetworkInformationProvider - // SignedTx returns the signed transaction given the tx bytes (msgs) plus the signatures - SignedTx(ctx context.Context, txBytes []byte, sigs []*types.Signature) (signedTxBytes []byte, err error) - // TxOperationsAndSignersAccountIdentifiers returns the operations related to a transaction and the account - // identifiers if the transaction is signed - TxOperationsAndSignersAccountIdentifiers(signed bool, hexBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) - // ConstructionPayload returns the construction payload given the request - ConstructionPayload(ctx context.Context, req *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) - // PreprocessOperationsToOptions returns the options given the preprocess operations - PreprocessOperationsToOptions(ctx context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error) - // AccountIdentifierFromPublicKey returns the account identifier given the public key - AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) -} -``` - -### 2. Cosmos SDK Implementation - -The Cosmos SDK implementation, based on version, takes care of satisfying the `Client` interface. -In Stargate, Launchpad and 0.37, we have introduced the concept of rosetta.Msg, this message is not in the shared repository as the sdk.Msg type differs between Cosmos SDK versions. - -The rosetta.Msg interface follows: - -```go -// Msg represents a cosmos-sdk message that can be converted from and to a rosetta operation. -type Msg interface { - sdk.Msg - ToOperations(withStatus, hasError bool) []*types.Operation - FromOperations(ops []*types.Operation) (sdk.Msg, error) -} -``` - -Hence developers who want to extend the rosetta set of supported operations just need to extend their module's sdk.Msgs with the `ToOperations` and `FromOperations` methods. - -### 3. API service invocation - -As stated at the start, application developers will have two methods for invocation of the Rosetta API service: - -1. Shared process for both application and API -2. Standalone API service - -#### Shared Process (Only Stargate) - -Rosetta API service could run within the same execution process as the application. This would be enabled via app.toml settings, and if gRPC is not enabled the rosetta instance would be spinned in offline mode (tx building capabilities only). - -#### Separate API service - -Client application developers can write a new command to launch a Rosetta API server as a separate process too, using the rosetta command contained in the `/server/rosetta` package. Construction of the command depends on Cosmos SDK version. Examples can be found inside `simd` for stargate, and `contrib/rosetta/simapp` for other release series. - -## Status - -Proposed - -## Consequences - -### Positive - -* Out-of-the-box Rosetta API support within Cosmos SDK. -* Blockchain interface standardisation - -## References - -* https://www.rosetta-api.org/ diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-036-arbitrary-signature.md b/versioned_docs/version-0.46/integrate/architecture/adr-036-arbitrary-signature.md deleted file mode 100644 index d40929452..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-036-arbitrary-signature.md +++ /dev/null @@ -1,132 +0,0 @@ -# ADR 036: Arbitrary Message Signature Specification - -## Changelog - -* 28/10/2020 - Initial draft - -## Authors - -* Antoine Herzog (@antoineherzog) -* Zaki Manian (@zmanian) -* Aleksandr Bezobchuk (alexanderbez) [1] -* Frojdi Dymylja (@fdymylja) - -## Status - -Draft - -## Abstract - -Currently, in the Cosmos SDK, there is no convention to sign arbitrary message like on Ethereum. We propose with this specification, for Cosmos SDK ecosystem, a way to sign and validate off-chain arbitrary messages. - -This specification serves the purpose of covering every use case, this means that cosmos-sdk applications developers decide how to serialize and represent `Data` to users. - -## Context - -Having the ability to sign messages off-chain has proven to be a fundamental aspect of nearly any blockchain. The notion of signing messages off-chain has many added benefits such as saving on computational costs and reducing transaction throughput and overhead. Within the context of the Cosmos, some of the major applications of signing such data includes, but is not limited to, providing a cryptographic secure and verifiable means of proving validator identity and possibly associating it with some other framework or organization. In addition, having the ability to sign Cosmos messages with a Ledger or similar HSM device. - -Further context and use cases can be found in the references links. - -## Decision - -The aim is being able to sign arbitrary messages, even using Ledger or similar HSM devices. - -As a result signed messages should look roughly like Cosmos SDK messages but **must not** be a valid on-chain transaction. `chain-id`, `account_number` and `sequence` can all be assigned invalid values. - -Cosmos SDK 0.40 also introduces a concept of “auth_info” this can specify SIGN_MODES. - -A spec should include an `auth_info` that supports SIGN_MODE_DIRECT and SIGN_MODE_LEGACY_AMINO. - -Create the `offchain` proto definitions, we extend the auth module with `offchain` package to offer functionalities to verify and sign offline messages. - -An offchain transaction follows these rules: - -* the memo must be empty -* nonce, sequence number must be equal to 0 -* chain-id must be equal to “” -* fee gas must be equal to 0 -* fee amount must be an empty array - -Verification of an offchain transaction follows the same rules as an onchain one, except for the spec differences highlighted above. - -The first message added to the `offchain` package is `MsgSignData`. - -`MsgSignData` allows developers to sign arbitrary bytes valid offchain only. Where `Signer` is the account address of the signer. `Data` is arbitrary bytes which can represent `text`, `files`, `object`s. It's applications developers decision how `Data` should be deserialized, serialized and the object it can represent in their context. - -It's applications developers decision how `Data` should be treated, by treated we mean the serialization and deserialization process and the Object `Data` should represent. - -Proto definition: - -```proto -// MsgSignData defines an arbitrary, general-purpose, off-chain message -message MsgSignData { - // Signer is the sdk.AccAddress of the message signer - bytes Signer = 1 [(gogoproto.jsontag) = "signer", (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - // Data represents the raw bytes of the content that is signed (text, json, etc) - bytes Data = 2 [(gogoproto.jsontag) = "data"]; -} -``` - -Signed MsgSignData json example: - -```json -{ - "type": "cosmos-sdk/StdTx", - "value": { - "msg": [ - { - "type": "sign/MsgSignData", - "value": { - "signer": "cosmos1hftz5ugqmpg9243xeegsqqav62f8hnywsjr4xr", - "data": "cmFuZG9t" - } - } - ], - "fee": { - "amount": [], - "gas": "0" - }, - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AqnDSiRoFmTPfq97xxEb2VkQ/Hm28cPsqsZm9jEVsYK9" - }, - "signature": "8y8i34qJakkjse9pOD2De+dnlc4KvFgh0wQpes4eydN66D9kv7cmCEouRrkka9tlW9cAkIL52ErB+6ye7X5aEg==" - } - ], - "memo": "" - } -} -``` - -## Consequences - -There is a specification on how messages, that are not meant to be broadcast to a live chain, should be formed. - -### Backwards Compatibility - -Backwards compatibility is maintained as this is a new message spec definition. - -### Positive - -* A common format that can be used by multiple applications to sign and verify off-chain messages. -* The specification is primitive which means it can cover every use case without limiting what is possible to fit inside it. -* It gives room for other off-chain messages specifications that aim to target more specific and common use cases such as off-chain-based authN/authZ layers [2]. - -### Negative - -* Current proposal requires a fixed relationship between an account address and a public key. -* Doesn't work with multisig accounts. - -## Further discussion - -* Regarding security in `MsgSignData`, the developer using `MsgSignData` is in charge of making the content laying in `Data` non-replayable when, and if, needed. -* the offchain package will be further extended with extra messages that target specific use cases such as, but not limited to, authentication in applications, payment channels, L2 solutions in general. - -## References - -1. https://github.com/cosmos/ics/pull/33 -2. https://github.com/cosmos/cosmos-sdk/pull/7727#discussion_r515668204 -3. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-722478477 -4. https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-721062923 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-037-gov-split-vote.md b/versioned_docs/version-0.46/integrate/architecture/adr-037-gov-split-vote.md deleted file mode 100644 index a2f457c33..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-037-gov-split-vote.md +++ /dev/null @@ -1,111 +0,0 @@ -# ADR 037: Governance split votes - -## Changelog - -* 2020/10/28: Intial draft - -## Status - -Accepted - -## Abstract - -This ADR defines a modification to the governance module that would allow a staker to split their votes into several voting options. For example, it could use 70% of its voting power to vote Yes and 30% of its voting power to vote No. - -## Context - -Currently, an address can cast a vote with only one options (Yes/No/Abstain/NoWithVeto) and use their full voting power behind that choice. - -However, often times the entity owning that address might not be a single individual. For example, a company might have different stakeholders who want to vote differently, and so it makes sense to allow them to split their voting power. Another example use case is exchanges. Many centralized exchanges often stake a portion of their users' tokens in their custody. Currently, it is not possible for them to do "passthrough voting" and giving their users voting rights over their tokens. However, with this system, exchanges can poll their users for voting preferences, and then vote on-chain proportionally to the results of the poll. - -## Decision - -We modify the vote structs to be - -```go -type WeightedVoteOption struct { - Option string - Weight sdk.Dec -} - -type Vote struct { - ProposalID int64 - Voter sdk.Address - Options []WeightedVoteOption -} -``` - -And for backwards compatibility, we introduce `MsgVoteWeighted` while keeping `MsgVote`. - -```go -type MsgVote struct { - ProposalID int64 - Voter sdk.Address - Option Option -} - -type MsgVoteWeighted struct { - ProposalID int64 - Voter sdk.Address - Options []WeightedVoteOption -} -``` - -The `ValidateBasic` of a `MsgVoteWeighted` struct would require that - -1. The sum of all the Rates is equal to 1.0 -2. No Option is repeated - -The governance tally function will iterate over all the options in a vote and add to the tally the result of the voter's voting power * the rate for that option. - -```go -tally() { - results := map[types.VoteOption]sdk.Dec - - for _, vote := range votes { - for i, weightedOption := range vote.Options { - results[weightedOption.Option] += getVotingPower(vote.voter) * weightedOption.Weight - } - } -} -``` - -The CLI command for creating a multi-option vote would be as such: - -```sh -simd tx gov vote 1 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05" --from mykey -``` - -To create a single-option vote a user can do either - -```sh -simd tx gov vote 1 "yes=1" --from mykey -``` - -or - -```sh -simd tx gov vote 1 yes --from mykey -``` - -to maintain backwards compatibility. - -## Consequences - -### Backwards Compatibility - -* Previous VoteMsg types will remain the same and so clients will not have to update their procedure unless they want to support the WeightedVoteMsg feature. -* When querying a Vote struct from state, its structure will be different, and so clients wanting to display all voters and their respective votes will have to handle the new format and the fact that a single voter can have split votes. -* The result of querying the tally function should have the same API for clients. - -### Positive - -* Can make the voting process more accurate for addresses representing multiple stakeholders, often some of the largest addresses. - -### Negative - -* Is more complex than simple voting, and so may be harder to explain to users. However, this is mostly mitigated because the feature is opt-in. - -### Neutral - -* Relatively minor change to governance tally function. diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-038-state-listening.md b/versioned_docs/version-0.46/integrate/architecture/adr-038-state-listening.md deleted file mode 100644 index 12d0bdb8b..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-038-state-listening.md +++ /dev/null @@ -1,569 +0,0 @@ -# ADR 038: KVStore state listening - -## Changelog - -* 11/23/2020: Initial draft -* 10/14/2022: - * Add `ListenCommit`, flatten the state writes in a block to a single batch. - * Remove listeners from cache stores, should only listen to `rootmulti.Store`. - * Remove `HaltAppOnDeliveryError()`, the errors are propogated by default, the implementations should return nil if don't want to propogate errors. - - -## Status - -Proposed - -## Abstract - -This ADR defines a set of changes to enable listening to state changes of individual KVStores and exposing these data to consumers. - -## Context - -Currently, KVStore data can be remotely accessed through [Queries](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/02-messages-and-queries.md#queries) -which proceed either through Tendermint and the ABCI, or through the gRPC server. -In addition to these request/response queries, it would be beneficial to have a means of listening to state changes as they occur in real time. - -## Decision - -We will modify the `CommitMultiStore` interface and its concrete (`rootmulti`) implementations and introduce a new `listenkv.Store` to allow listening to state changes in underlying KVStores. We don't need to listen to cache stores, because we can't be sure that the writes will be committed eventually, and the writes are duplicated in `rootmulti.Store` eventually, so we should only listen to `rootmulti.Store`. -We will introduce a plugin system for configuring and running streaming services that write these state changes and their surrounding ABCI message context to different destinations. - -### Listening interface - -In a new file, `store/types/listening.go`, we will create a `WriteListener` interface for streaming out state changes from a KVStore. - -```go -// WriteListener interface for streaming data out from a listenkv.Store -type WriteListener interface { - // if value is nil then it was deleted - // storeKey indicates the source KVStore, to facilitate using the same WriteListener across separate KVStores - // delete bool indicates if it was a delete; true: delete, false: set - OnWrite(storeKey StoreKey, key []byte, value []byte, delete bool) error -} -``` - -### Listener type - -We will create two concrete implementations of the `WriteListener` interface in `store/types/listening.go`, that writes out protobuf -encoded KV pairs to an underlying `io.Writer`, and simply accumulate them in memory. - -This will include defining a simple protobuf type for the KV pairs. In addition to the key and value fields this message -will include the StoreKey for the originating KVStore so that we can write out from separate KVStores to the same stream/file -and determine the source of each KV pair. - -```protobuf -message StoreKVPair { - optional string store_key = 1; // the store key for the KVStore this pair originates from - required bool set = 2; // true indicates a set operation, false indicates a delete operation - required bytes key = 3; - required bytes value = 4; -} -``` - -```go -// StoreKVPairWriteListener is used to configure listening to a KVStore by writing out length-prefixed -// protobuf encoded StoreKVPairs to an underlying io.Writer -type StoreKVPairWriteListener struct { - writer io.Writer - marshaller codec.BinaryCodec -} - -// NewStoreKVPairWriteListener wraps creates a StoreKVPairWriteListener with a provdied io.Writer and codec.BinaryCodec -func NewStoreKVPairWriteListener(w io.Writer, m codec.BinaryCodec) *StoreKVPairWriteListener { - return &StoreKVPairWriteListener{ - writer: w, - marshaller: m, - } -} - -// OnWrite satisfies the WriteListener interface by writing length-prefixed protobuf encoded StoreKVPairs -func (wl *StoreKVPairWriteListener) OnWrite(storeKey types.StoreKey, key []byte, value []byte, delete bool) error error { - kvPair := new(types.StoreKVPair) - kvPair.StoreKey = storeKey.Name() - kvPair.Delete = Delete - kvPair.Key = key - kvPair.Value = value - by, err := wl.marshaller.MarshalBinaryLengthPrefixed(kvPair) - if err != nil { - return err - } - if _, err := wl.writer.Write(by); err != nil { - return err - } - return nil -} -``` - -```golang -// MemoryListener listens to the state writes and accumulate the records in memory. -type MemoryListener struct { - key StoreKey - stateCache []StoreKVPair -} - -// NewMemoryListener creates a listener that accumulate the state writes in memory. -func NewMemoryListener(key StoreKey) *MemoryListener { - return &MemoryListener{key: key} -} - -// OnWrite implements WriteListener interface -func (fl *MemoryListener) OnWrite(storeKey StoreKey, key []byte, value []byte, delete bool) error { - fl.stateCache = append(fl.stateCache, StoreKVPair{ - StoreKey: storeKey.Name(), - Delete: delete, - Key: key, - Value: value, - }) - return nil -} - -// PopStateCache returns the current state caches and set to nil -func (fl *MemoryListener) PopStateCache() []StoreKVPair { - res := fl.stateCache - fl.stateCache = nil - return res -} - -// StoreKey returns the storeKey it listens to -func (fl *MemoryListener) StoreKey() StoreKey { - return fl.key -} -``` - -### ListenKVStore - -We will create a new `Store` type `listenkv.Store` that the `MultiStore` wraps around a `KVStore` to enable state listening. -We can configure the `Store` with a set of `WriteListener`s which stream the output to specific destinations. - -```go -// Store implements the KVStore interface with listening enabled. -// Operations are traced on each core KVStore call and written to any of the -// underlying listeners with the proper key and operation permissions -type Store struct { - parent types.KVStore - listeners []types.WriteListener - parentStoreKey types.StoreKey -} - -// NewStore returns a reference to a new traceKVStore given a parent -// KVStore implementation and a buffered writer. -func NewStore(parent types.KVStore, psk types.StoreKey, listeners []types.WriteListener) *Store { - return &Store{parent: parent, listeners: listeners, parentStoreKey: psk} -} - -// Set implements the KVStore interface. It traces a write operation and -// delegates the Set call to the parent KVStore. -func (s *Store) Set(key []byte, value []byte) { - types.AssertValidKey(key) - s.parent.Set(key, value) - s.onWrite(false, key, value) -} - -// Delete implements the KVStore interface. It traces a write operation and -// delegates the Delete call to the parent KVStore. -func (s *Store) Delete(key []byte) { - s.parent.Delete(key) - s.onWrite(true, key, nil) -} - -// onWrite writes a KVStore operation to all the WriteListeners -func (s *Store) onWrite(delete bool, key, value []byte) { - for _, l := range s.listeners { - if err := l.OnWrite(s.parentStoreKey, key, value, delete); err != nil { - // log error - } - } -} -``` - -### MultiStore interface updates - -We will update the `CommitMultiStore` interface to allow us to wrap a set of listeners around a specific `KVStore`. - -```go -type CommitMultiStore interface { - ... - - // ListeningEnabled returns if listening is enabled for the KVStore belonging the provided StoreKey - ListeningEnabled(key StoreKey) bool - - // AddListeners adds WriteListeners for the KVStore belonging to the provided StoreKey - // It appends the listeners to a current set, if one already exists - AddListeners(key StoreKey, listeners []WriteListener) -} -``` - -### MultiStore implementation updates - -We will modify all of the `CommitMultiStore` implementations to satisfy these new interfaces, and adjust the `rootmulti` `GetKVStore` method -to wrap the returned `KVStore` with a `listenkv.Store` if listening is turned on for that `Store`. - -```go -func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { - store := rs.stores[key].(types.KVStore) - - if rs.TracingEnabled() { - store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) - } - if rs.ListeningEnabled(key) { - store = listenkv.NewStore(key, store, rs.listeners[key]) - } - - return store -} -``` - -We will also adjust the `rootmulti` `CacheMultiStore` method to wrap the stores with `listenkv.Store` to enable listening when the cache layer writes. - -```go -func (rs *Store) CacheMultiStore() types.CacheMultiStore { - stores := make(map[types.StoreKey]types.CacheWrapper) - for k, v := range rs.stores { - store := v.(types.KVStore) - // Wire the listenkv.Store to allow listeners to observe the writes from the cache store, - // set same listeners on cache store will observe duplicated writes. - if rs.ListeningEnabled(k) { - store = listenkv.NewStore(store, k, rs.listeners[k]) - } - stores[k] = store - } - return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.getTracingContext()) -} -``` - -### Exposing the data - -#### Streaming service - -We will introduce a new `StreamingService` interface for exposing `WriteListener` data streams to external consumers. -In addition to streaming state changes as `StoreKVPair`s, the interface satisfies an `ABCIListener` interface that plugs -into the BaseApp and relays ABCI requests and responses so that the service can observe those block metadatas as well. - -The `WriteListener`s of `StreamingService` listens to the `rootmulti.Store`, which is only written into at commit event by the cache store of `deliverState`. - -```go -// ABCIListener interface used to hook into the ABCI message processing of the BaseApp -type ABCIListener interface { - // ListenBeginBlock updates the streaming service with the latest BeginBlock messages - ListenBeginBlock(ctx types.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) error - // ListenEndBlock updates the steaming service with the latest EndBlock messages - ListenEndBlock(ctx types.Context, req abci.RequestEndBlock, res abci.ResponseEndBlock) error - // ListenDeliverTx updates the steaming service with the latest DeliverTx messages - ListenDeliverTx(ctx types.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) error - // ListenCommit updates the steaming service with the latest Commit message, - // All the state writes of current block should have notified before this message. - ListenCommit(ctx types.Context, res abci.ResponseCommit) error -} - -// StreamingService interface for registering WriteListeners with the BaseApp and updating the service with the ABCI messages using the hooks -type StreamingService interface { - // Stream is the streaming service loop, awaits kv pairs and writes them to a destination stream or file - Stream(wg *sync.WaitGroup) error - // Listeners returns the streaming service's listeners for the BaseApp to register - Listeners() map[types.StoreKey][]store.WriteListener - // ABCIListener interface for hooking into the ABCI messages from inside the BaseApp - ABCIListener - // Closer interface - io.Closer -} -``` - -#### BaseApp registration - -We will add a new method to the `BaseApp` to enable the registration of `StreamingService`s: - -```go -// SetStreamingService is used to set a streaming service into the BaseApp hooks and load the listeners into the multistore -func (app *BaseApp) SetStreamingService(s StreamingService) { - // add the listeners for each StoreKey - for key, lis := range s.Listeners() { - app.cms.AddListeners(key, lis) - } - // register the StreamingService within the BaseApp - // BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context - app.abciListeners = append(app.abciListeners, s) -} -``` - -We will also modify the `BeginBlock`, `EndBlock`, and `DeliverTx` methods to pass ABCI requests and responses to any streaming service hooks registered -with the `BaseApp`. - -```go -func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { - - ... - - defer func() { - // call the hooks with the BeginBlock messages - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenBeginBlock(app.deliverState.ctx, req, res); err != nil { - panic(sdkerrors.Wrapf(err, "BeginBlock listening hook failed, height: %d", req.Header.Height)) - } - } - }() - - return res -} -``` - -```go -func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { - - ... - - defer func() { - // Call the streaming service hooks with the EndBlock messages - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenEndBlock(app.deliverState.ctx, req, res); err != nil { - panic(sdkerrors.Wrapf(err, "EndBlock listening hook failed, height: %d", req.Height)) - } - } - }() - - return res -} -``` - -```go -func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) { - - defer func() { - // call the hooks with the DeliverTx messages - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenDeliverTx(app.deliverState.ctx, req, res); err != nil { - panic(sdkerrors.Wrap(err, "DeliverTx listening hook failed")) - } - } - }() - - ... - - return res -} -``` - -```golang -func (app *BaseApp) Commit() abci.ResponseCommit { - header := app.deliverState.ctx.BlockHeader() - retainHeight := app.GetBlockRetentionHeight(header.Height) - - // Write the DeliverTx state into branched storage and commit the MultiStore. - // The write to the DeliverTx state writes all state transitions to the root - // MultiStore (app.cms) so when Commit() is called is persists those values. - app.deliverState.ms.Write() - commitID := app.cms.Commit() - - res := abci.ResponseCommit{ - Data: commitID.Hash, - RetainHeight: retainHeight, - } - - // call the hooks with the Commit message - for _, streamingListener := range app.abciListeners { - if err := streamingListener.ListenCommit(app.deliverState.ctx, res); err != nil { - panic(sdkerrors.Wrapf(err, "Commit listening hook failed, height: %d", header.Height)) - } - } - - app.logger.Info("commit synced", "commit", fmt.Sprintf("%X", commitID)) - ... -} -``` - -#### Error Handling And Async Consumers - -`ABCIListener`s are called synchronously inside the consensus state machine, the returned error causes panic which in turn halt the consensus state machine. The implementer should be careful not to break consensus unexpectedly or slow down it too much. - -For some async use cases, one can spawn a go-routine internanlly to avoid slow down consensus state machine, and handle the errors internally and always returns `nil` to avoid halting consensus state machine on error. - -Furthermore, for most of the cases, we only need to use the builtin file streamer to listen to state changes directly inside cosmos-sdk, the other consumers should subscribe to the file streamer output externally. - -#### File Streamer - -We provide a minimal filesystem based implementation inside cosmos-sdk, and provides options to write output files reliably, the output files can be further consumed by external consumers, so most of the state listeners actually don't need to live inside the sdk and node, which improves the node robustness and simplify sdk internals. - -The file streamer can be wired in app like this: -```golang -exposeStoreKeys := ... // decide the key list to listen -service, err := file.NewStreamingService(streamingDir, "", exposeStoreKeys, appCodec, logger) -bApp.SetStreamingService(service) -``` - -#### Plugin system - -We propose a plugin architecture to load and run `StreamingService` implementations. We will introduce a plugin -loading/preloading system that is used to load, initialize, inject, run, and stop Cosmos-SDK plugins. Each plugin -must implement the following interface: - -```go -// Plugin is the base interface for all kinds of cosmos-sdk plugins -// It will be included in interfaces of different Plugins -type Plugin interface { - // Name should return unique name of the plugin - Name() string - - // Version returns current version of the plugin - Version() string - - // Init is called once when the Plugin is being loaded - // The plugin is passed the AppOptions for configuration - // A plugin will not necessarily have a functional Init - Init(env serverTypes.AppOptions) error - - // Closer interface for shutting down the plugin process - io.Closer -} -``` - -The `Name` method returns a plugin's name. -The `Version` method returns a plugin's version. -The `Init` method initializes a plugin with the provided `AppOptions`. -The io.Closer is used to shut down the plugin service. - -For the purposes of this ADR we introduce a single kind of plugin- a state streaming plugin. -We will define a `StateStreamingPlugin` interface which extends the above `Plugin` interface to support a state streaming service. - -```go -// StateStreamingPlugin interface for plugins that load a baseapp.StreamingService onto a baseapp.BaseApp -type StateStreamingPlugin interface { - // Register configures and registers the plugin streaming service with the BaseApp - Register(bApp *baseapp.BaseApp, marshaller codec.BinaryCodec, keys map[string]*types.KVStoreKey) error - - // Start starts the background streaming process of the plugin streaming service - Start(wg *sync.WaitGroup) error - - // Plugin is the base Plugin interface - Plugin -} -``` - -The `Register` method is used during App construction to register the plugin's streaming service with an App's BaseApp using the BaseApp's `SetStreamingService` method. -The `Start` method is used during App construction to start the registered plugin streaming services and maintain synchronization with them. - -e.g. in `NewSimApp`: - -```go -func NewSimApp( - logger log.Logger, - db dbm.DB, - traceStore io.Writer, - loadLatest bool, - appOpts servertypes.AppOptions, - baseAppOptions ...func(*baseapp.BaseApp), -) *SimApp { - - ... - - keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, - minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, - evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, - ) - - pluginsOnKey := fmt.Sprintf("%s.%s", plugin.PLUGINS_TOML_KEY, plugin.PLUGINS_ON_TOML_KEY) - if cast.ToBool(appOpts.Get(pluginsOnKey)) { - // this loads the preloaded and any plugins found in `plugins.dir` - pluginLoader, err := loader.NewPluginLoader(appOpts, logger) - if err != nil { - // handle error - } - - // initialize the loaded plugins - if err := pluginLoader.Initialize(); err != nil { - // handle error - } - - // register the plugin(s) with the BaseApp - if err := pluginLoader.Inject(bApp, appCodec, keys); err != nil { - // handle error - } - - // start the plugin services, optionally use wg to synchronize shutdown using io.Closer - wg := new(sync.WaitGroup) - if err := pluginLoader.Start(wg); err != nil { - // handler error - } - } - - ... - - return app -} -``` - - -#### Configuration - -The plugin system will be configured within an app's app.toml file. - -```toml -[plugins] - on = false # turn the plugin system, as a whole, on or off - enabled = ["list", "of", "plugin", "names", "to", "enable"] - dir = "the directory to load non-preloaded plugins from; defaults to cosmos-sdk/plugin/plugins" -``` - -There will be three parameters for configuring the plugin system: `plugins.on`, `plugins.enabled` and `plugins.dir`. -`plugins.on` is a bool that turns on or off the plugin system at large, `plugins.dir` directs the system to a directory -to load plugins from, and `plugins.enabled` provides `opt-in` semantics to plugin names to enable (including preloaded plugins). - -Configuration of a given plugin is ultimately specific to the plugin, but we will introduce some standards here: - -Plugin TOML configuration should be split into separate sub-tables for each kind of plugin (e.g. `plugins.streaming`). - -Within these sub-tables, the parameters for a specific plugin of that kind are included in another sub-table (e.g. `plugins.streaming.file`). -It is generally expected, but not required, that a streaming service plugin can be configured with a set of store keys -(e.g. `plugins.streaming.file.keys`) for the stores it listens to and a flag (e.g. `plugins.streaming.file.halt_app_on_delivery_error`) -that signifies whether the service operates in a fire-and-forget capacity, or stop the BaseApp when an error occurs in -any of `ListenBeginBlock`, `ListenEndBlock` and `ListenDeliverTx`. - -e.g. - -```toml -[plugins] - on = false # turn the plugin system, as a whole, on or off - enabled = ["list", "of", "plugin", "names", "to", "enable"] - dir = "the directory to load non-preloaded plugins from; defaults to " - [plugins.streaming] # a mapping of plugin-specific streaming service parameters, mapped to their plugin name - [plugins.streaming.file] # the specific parameters for the file streaming service plugin - keys = ["list", "of", "store", "keys", "we", "want", "to", "expose", "for", "this", "streaming", "service"] - write_dir = "path to the write directory" - prefix = "optional prefix to prepend to the generated file names" - halt_app_on_delivery_error = "false" # false == fire-and-forget; true == stop the application - [plugins.streaming.kafka] - keys = [] - topic_prefix = "block" # Optional prefix for topic names where data will be stored. - flush_timeout_ms = 5000 # Flush and wait for outstanding messages and requests to complete delivery when calling `StreamingService.Close(). (milliseconds) - halt_app_on_delivery_error = true # Whether or not to halt the application when plugin fails to deliver message(s). - ... -``` - -#### Encoding and decoding streams - -ADR-038 introduces the interfaces and types for streaming state changes out from KVStores, associating this -data with their related ABCI requests and responses, and registering a service for consuming this data and streaming it to some destination in a final format. -Instead of prescribing a final data format in this ADR, it is left to a specific plugin implementation to define and document this format. -We take this approach because flexibility in the final format is necessary to support a wide range of streaming service plugins. For example, -the data format for a streaming service that writes the data out to a set of files will differ from the data format that is written to a Kafka topic. - -## Consequences - -These changes will provide a means of subscribing to KVStore state changes in real time. - -### Backwards Compatibility - -* This ADR changes the `CommitMultiStore` interface, implementations supporting the previous version of these interfaces will not support the new ones - -### Positive - -* Ability to listen to KVStore state changes in real time and expose these events to external consumers - -### Negative - -* Changes `CommitMultiStore`interface - -### Neutral - -* Introduces additional- but optional- complexity to configuring and running a cosmos application -* If an application developer opts to use these features to expose data, they need to be aware of the ramifications/risks of that data exposure as it pertains to the specifics of their application diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-039-epoched-staking.md b/versioned_docs/version-0.46/integrate/architecture/adr-039-epoched-staking.md deleted file mode 100644 index 29418fc89..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-039-epoched-staking.md +++ /dev/null @@ -1,122 +0,0 @@ -# ADR 039: Epoched Staking - -## Changelog - -* 10-Feb-2021: Initial Draft - -## Authors - -* Dev Ojha (@valardragon) -* Sunny Aggarwal (@sunnya97) - -## Status - -Proposed - -## Abstract - -This ADR updates the proof of stake module to buffer the staking weight updates for a number of blocks before updating the consensus' staking weights. The length of the buffer is dubbed an epoch. The prior functionality of the staking module is then a special case of the abstracted module, with the epoch being set to 1 block. - -## Context - -The current proof of stake module takes the design decision to apply staking weight changes to the consensus engine immediately. This means that delegations and unbonds get applied immediately to the validator set. This decision was primarily done as it was implementationally simplest, and because we at the time believed that this would lead to better UX for clients. - -An alternative design choice is to allow buffering staking updates (delegations, unbonds, validators joining) for a number of blocks. This 'epoch'd proof of stake consensus provides the guarantee that the consensus weights for validators will not change mid-epoch, except in the event of a slash condition. - -Additionally, the UX hurdle may not be as significant as was previously thought. This is because it is possible to provide users immediate acknowledgement that their bond was recorded and will be executed. - -Furthermore, it has become clearer over time that immediate execution of staking events comes with limitations, such as: - -* Threshold based cryptography. One of the main limitations is that because the validator set can change so regularly, it makes the running of multiparty computation by a fixed validator set difficult. Many threshold-based cryptographic features for blockchains such as randomness beacons and threshold decryption require a computationally-expensive DKG process (will take much longer than 1 block to create). To productively use these, we need to guarantee that the result of the DKG will be used for a reasonably long time. It wouldn't be feasible to rerun the DKG every block. By epoching staking, it guarantees we'll only need to run a new DKG once every epoch. - -* Light client efficiency. This would lessen the overhead for IBC when there is high churn in the validator set. In the Tendermint light client bisection algorithm, the number of headers you need to verify is related to bounding the difference in validator sets between a trusted header and the latest header. If the difference is too great, you verify more header in between the two. By limiting the frequency of validator set changes, we can reduce the worst case size of IBC lite client proofs, which occurs when a validator set has high churn. - -* Fairness of deterministic leader election. Currently we have no ways of reasoning of fairness of deterministic leader election in the presence of staking changes without epochs (tendermint/spec#217). Breaking fairness of leader election is profitable for validators, as they earn additional rewards from being the proposer. Adding epochs at least makes it easier for our deterministic leader election to match something we can prove secure. (Albeit, we still haven’t proven if our current algorithm is fair with > 2 validators in the presence of stake changes) - -* Staking derivative design. Currently, reward distribution is done lazily using the F1 fee distribution. While saving computational complexity, lazy accounting requires a more stateful staking implementation. Right now, each delegation entry has to track the time of last withdrawal. Handling this can be a challenge for some staking derivatives designs that seek to provide fungibility for all tokens staked to a single validator. Force-withdrawing rewards to users can help solve this, however it is infeasible to force-withdraw rewards to users on a per block basis. With epochs, a chain could more easily alter the design to have rewards be forcefully withdrawn (iterating over delegator accounts only once per-epoch), and can thus remove delegation timing from state. This may be useful for certain staking derivative designs. - -## Design considerations - -### Slashing - -There is a design consideration for whether to apply a slash immediately or at the end of an epoch. A slash event should apply to only members who are actually staked during the time of the infraction, namely during the epoch the slash event occured. - -Applying it immediately can be viewed as offering greater consensus layer security, at potential costs to the aforementioned usecases. The benefits of immediate slashing for consensus layer security can be all be obtained by executing the validator jailing immediately (thus removing it from the validator set), and delaying the actual slash change to the validator's weight until the epoch boundary. For the use cases mentioned above, workarounds can be integrated to avoid problems, as follows: - -* For threshold based cryptography, this setting will have the threshold cryptography use the original epoch weights, while consensus has an update that lets it more rapidly benefit from additional security. If the threshold based cryptography blocks liveness of the chain, then we have effectively raised the liveness threshold of the remaining validators for the rest of the epoch. (Alternatively, jailed nodes could still contribute shares) This plan will fail in the extreme case that more than 1/3rd of the validators have been jailed within a single epoch. For such an extreme scenario, the chain already have its own custom incident response plan, and defining how to handle the threshold cryptography should be a part of that. -* For light client efficiency, there can be a bit included in the header indicating an intra-epoch slash (ala https://github.com/tendermint/spec/issues/199). -* For fairness of deterministic leader election, applying a slash or jailing within an epoch would break the guarantee we were seeking to provide. This then re-introduces a new (but significantly simpler) problem for trying to provide fairness guarantees. Namely, that validators can adversarially elect to remove themself from the set of proposers. From a security perspective, this could potentially be handled by two different mechanisms (or prove to still be too difficult to achieve). One is making a security statement acknowledging the ability for an adversary to force an ahead-of-time fixed threshold of users to drop out of the proposer set within an epoch. The second method would be to parameterize such that the cost of a slash within the epoch far outweights benefits due to being a proposer. However, this latter criterion is quite dubious, since being a proposer can have many advantageous side-effects in chains with complex state machines. (Namely, DeFi games such as Fomo3D) -* For staking derivative design, there is no issue introduced. This does not increase the state size of staking records, since whether a slash has occured is fully queryable given the validator address. - -### Token lockup - -When someone makes a transaction to delegate, even though they are not immediately staked, their tokens should be moved into a pool managed by the staking module which will then be used at the end of an epoch. This prevents concerns where they stake, and then spend those tokens not realizing they were already allocated for staking, and thus having their staking tx fail. - -### Pipelining the epochs - -For threshold based cryptography in particular, we need a pipeline for epoch changes. This is because when we are in epoch N, we want the epoch N+1 weights to be fixed so that the validator set can do the DKG accordingly. So if we are currently in epoch N, the stake weights for epoch N+1 should already be fixed, and new stake changes should be getting applied to epoch N + 2. - -This can be handled by making a parameter for the epoch pipeline length. This parameter should not be alterable except during hard forks, to mitigate implementation complexity of switching the pipeline length. - -With pipeline length 1, if I redelegate during epoch N, then my redelegation is applied prior to the beginning of epoch N+1. -With pipeline length 2, if I redelegate during epoch N, then my redelegation is applied prior to the beginning of epoch N+2. - -### Rewards - -Even though all staking updates are applied at epoch boundaries, rewards can still be distributed immediately when they are claimed. This is because they do not affect the current stake weights, as we do not implement auto-bonding of rewards. If such a feature were to be implemented, it would have to be setup so that rewards are auto-bonded at the epoch boundary. - -### Parameterizing the epoch length - -When choosing the epoch length, there is a trade-off queued state/computation buildup, and countering the previously discussed limitations of immediate execution if they apply to a given chain. - -Until an ABCI mechanism for variable block times is introduced, it is ill-advised to be using high epoch lengths due to the computation buildup. This is because when a block's execution time is greater than the expected block time from Tendermint, rounds may increment. - -## Decision - -**Step-1**: Implement buffering of all staking and slashing messages. - -First we create a pool for storing tokens that are being bonded, but should be applied at the epoch boundary called the `EpochDelegationPool`. Then, we have two separate queues, one for staking, one for slashing. We describe what happens on each message being delivered below: - -### Staking messages - -* **MsgCreateValidator**: Move user's self-bond to `EpochDelegationPool` immediately. Queue a message for the epoch boundary to handle the self-bond, taking the funds from the `EpochDelegationPool`. If Epoch execution fail, return back funds from `EpochDelegationPool` to user's account. -* **MsgEditValidator**: Validate message and if valid queue the message for execution at the end of the Epoch. -* **MsgDelegate**: Move user's funds to `EpochDelegationPool` immediately. Queue a message for the epoch boundary to handle the delegation, taking the funds from the `EpochDelegationPool`. If Epoch execution fail, return back funds from `EpochDelegationPool` to user's account. -* **MsgBeginRedelegate**: Validate message and if valid queue the message for execution at the end of the Epoch. -* **MsgUndelegate**: Validate message and if valid queue the message for execution at the end of the Epoch. - -### Slashing messages - -* **MsgUnjail**: Validate message and if valid queue the message for execution at the end of the Epoch. -* **Slash Event**: Whenever a slash event is created, it gets queued in the slashing module to apply at the end of the epoch. The queues should be setup such that this slash applies immediately. - -### Evidence Messages - -* **MsgSubmitEvidence**: This gets executed immediately, and the validator gets jailed immediately. However in slashing, the actual slash event gets queued. - -Then we add methods to the end blockers, to ensure that at the epoch boundary the queues are cleared and delegation updates are applied. - -**Step-2**: Implement querying of queued staking txs. - -When querying the staking activity of a given address, the status should return not only the amount of tokens staked, but also if there are any queued stake events for that address. This will require more work to be done in the querying logic, to trace the queued upcoming staking events. - -As an initial implementation, this can be implemented as a linear search over all queued staking events. However, for chains that need long epochs, they should eventually build additional support for nodes that support querying to be able to produce results in constant time. (This is do-able by maintaining an auxilliary hashmap for indexing upcoming staking events by address) - -**Step-3**: Adjust gas - -Currently gas represents the cost of executing a transaction when its done immediately. (Merging together costs of p2p overhead, state access overhead, and computational overhead) However, now a transaction can cause computation in a future block, namely at the epoch boundary. - -To handle this, we should initially include parameters for estimating the amount of future computation (denominated in gas), and add that as a flat charge needed for the message. -We leave it as out of scope for how to weight future computation versus current computation in gas pricing, and have it set such that the are weighted equally for now. - -## Consequences - -### Positive - -* Abstracts the proof of stake module that allows retaining the existing functionality -* Enables new features such as validator-set based threshold cryptography - -### Negative - -* Increases complexity of integrating more complex gas pricing mechanisms, as they now have to consider future execution costs as well. -* When epoch > 1, validators can no longer leave the network immediately, and must wait until an epoch boundary. diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-040-storage-and-smt-state-commitments.md b/versioned_docs/version-0.46/integrate/architecture/adr-040-storage-and-smt-state-commitments.md deleted file mode 100644 index 5d7f6c89d..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-040-storage-and-smt-state-commitments.md +++ /dev/null @@ -1,289 +0,0 @@ -# ADR 040: Storage and SMT State Commitments - -## Changelog - -* 2020-01-15: Draft - -## Status - -DRAFT Not Implemented - -## Abstract - -Sparse Merkle Tree ([SMT](https://osf.io/8mcnh/)) is a version of a Merkle Tree with various storage and performance optimizations. This ADR defines a separation of state commitments from data storage and the Cosmos SDK transition from IAVL to SMT. - -## Context - -Currently, Cosmos SDK uses IAVL for both state [commitments](https://cryptography.fandom.com/wiki/Commitment_scheme) and data storage. - -IAVL has effectively become an orphaned project within the Cosmos ecosystem and it's proven to be an inefficient state commitment data structure. -In the current design, IAVL is used for both data storage and as a Merkle Tree for state commitments. IAVL is meant to be a standalone Merkelized key/value database, however it's using a KV DB engine to store all tree nodes. So, each node is stored in a separate record in the KV DB. This causes many inefficiencies and problems: - -* Each object query requires a tree traversal from the root. Subsequent queries for the same object are cached on the Cosmos SDK level. -* Each edge traversal requires a DB query. -* Creating snapshots is [expensive](https://github.com/cosmos/cosmos-sdk/issues/7215#issuecomment-684804950). It takes about 30 seconds to export less than 100 MB of state (as of March 2020). -* Updates in IAVL may trigger tree reorganization and possible O(log(n)) hashes re-computation, which can become a CPU bottleneck. -* The node structure is pretty expensive - it contains a standard tree node elements (key, value, left and right element) and additional metadata such as height, version (which is not required by the Cosmos SDK). The entire node is hashed, and that hash is used as the key in the underlying database, [ref](https://github.com/cosmos/iavl/blob/master/docs/node/03-node.md -). - -Moreover, the IAVL project lacks support and a maintainer and we already see better and well-established alternatives. Instead of optimizing the IAVL, we are looking into other solutions for both storage and state commitments. - -## Decision - -We propose to separate the concerns of state commitment (**SC**), needed for consensus, and state storage (**SS**), needed for state machine. Finally we replace IAVL with [Celestia's SMT](https://github.com/lazyledger/smt). Celestia SMT is based on Diem (called jellyfish) design [*] - it uses a compute-optimised SMT by replacing subtrees with only default values with a single node (same approach is used by Ethereum2) and implements compact proofs. - -The storage model presented here doesn't deal with data structure nor serialization. It's a Key-Value database, where both key and value are binaries. The storage user is responsible for data serialization. - -### Decouple state commitment from storage - -Separation of storage and commitment (by the SMT) will allow the optimization of different components according to their usage and access patterns. - -`SC` (SMT) is used to commit to a data and compute Merkle proofs. `SS` is used to directly access data. To avoid collisions, both `SS` and `SC` will use a separate storage namespace (they could use the same database underneath). `SS` will store each record directly (mapping `(key, value)` as `key → value`). - -SMT is a merkle tree structure: we don't store keys directly. For every `(key, value)` pair, `hash(key)` is used as leaf path (we hash a key to uniformly distribute leaves in the tree) and `hash(value)` as the leaf contents. The tree structure is specified in more depth [below](#smt-for-state-commitment). - -For data access we propose 2 additional KV buckets (implemented as namespaces for the key-value pairs, sometimes called [column family](https://github.com/facebook/rocksdb/wiki/Terminology)): - -1. B1: `key → value`: the principal object storage, used by a state machine, behind the Cosmos SDK `KVStore` interface: provides direct access by key and allows prefix iteration (KV DB backend must support it). -2. B2: `hash(key) → key`: a reverse index to get a key from an SMT path. Internally the SMT will store `(key, value)` as `prefix || hash(key) || hash(value)`. So, we can get an object value by composing `hash(key) → B2 → B1`. -3. We could use more buckets to optimize the app usage if needed. - -We propose to use a KV database for both `SS` and `SC`. The store interface will allow to use the same physical DB backend for both `SS` and `SC` as well two separate DBs. The latter option allows for the separation of `SS` and `SC` into different hardware units, providing support for more complex setup scenarios and improving overall performance: one can use different backends (eg RocksDB and Badger) as well as independently tuning the underlying DB configuration. - -### Requirements - -State Storage requirements: - -* range queries -* quick (key, value) access -* creating a snapshot -* historical versioning -* pruning (garbage collection) - -State Commitment requirements: - -* fast updates -* tree path should be short -* query historical commitment proofs using ICS-23 standard -* pruning (garbage collection) - -### SMT for State Commitment - -A Sparse Merkle tree is based on the idea of a complete Merkle tree of an intractable size. The assumption here is that as the size of the tree is intractable, there would only be a few leaf nodes with valid data blocks relative to the tree size, rendering a sparse tree. - -The full specification can be found at [Celestia](https://github.com/celestiaorg/celestia-specs/blob/ec98170398dfc6394423ee79b00b71038879e211/src/specs/data_structures.md#sparse-merkle-tree). In summary: - -* The SMT consists of a binary Merkle tree, constructed in the same fashion as described in [Certificate Transparency (RFC-6962)](https://tools.ietf.org/html/rfc6962), but using as the hashing function SHA-2-256 as defined in [FIPS 180-4](https://doi.org/10.6028/NIST.FIPS.180-4). -* Leaves and internal nodes are hashed differently: the one-byte `0x00` is prepended for leaf nodes while `0x01` is prepended for internal nodes. -* Default values are given to leaf nodes with empty leaves. -* While the above rule is sufficient to pre-compute the values of intermediate nodes that are roots of empty subtrees, a further simplification is to extend this default value to all nodes that are roots of empty subtrees. The 32-byte zero is used as the default value. This rule takes precedence over the above one. -* An internal node that is the root of a subtree that contains exactly one non-empty leaf is replaced by that leaf's leaf node. - -### Snapshots for storage sync and state versioning - -Below, with simple _snapshot_ we refer to a database snapshot mechanism, not to a _ABCI snapshot sync_. The latter will be referred as _snapshot sync_ (which will directly use DB snapshot as described below). - -Database snapshot is a view of DB state at a certain time or transaction. It's not a full copy of a database (it would be too big). Usually a snapshot mechanism is based on a _copy on write_ and it allows DB state to be efficiently delivered at a certain stage. -Some DB engines support snapshotting. Hence, we propose to reuse that functionality for the state sync and versioning (described below). We limit the supported DB engines to ones which efficiently implement snapshots. In a final section we discuss the evaluated DBs. - -One of the Stargate core features is a _snapshot sync_ delivered in the `/snapshot` package. It provides a way to trustlessly sync a blockchain without repeating all transactions from the genesis. This feature is implemented in Cosmos SDK and requires storage support. Currently IAVL is the only supported backend. It works by streaming to a client a snapshot of a `SS` at a certain version together with a header chain. - -A new database snapshot will be created in every `EndBlocker` and identified by a block height. The `root` store keeps track of the available snapshots to offer `SS` at a certain version. The `root` store implements the `RootStore` interface described below. In essence, `RootStore` encapsulates a `Committer` interface. `Committer` has a `Commit`, `SetPruning`, `GetPruning` functions which will be used for creating and removing snapshots. The `rootStore.Commit` function creates a new snapshot and increments the version on each call, and checks if it needs to remove old versions. We will need to update the SMT interface to implement the `Committer` interface. -NOTE: `Commit` must be called exactly once per block. Otherwise we risk going out of sync for the version number and block height. -NOTE: For the Cosmos SDK storage, we may consider splitting that interface into `Committer` and `PruningCommitter` - only the multiroot should implement `PruningCommitter` (cache and prefix store don't need pruning). - -Number of historical versions for `abci.RequestQuery` and state sync snapshots is part of a node configuration, not a chain configuration (configuration implied by the blockchain consensus). A configuration should allow to specify number of past blocks and number of past blocks modulo some number (eg: 100 past blocks and one snapshot every 100 blocks for past 2000 blocks). Archival nodes can keep all past versions. - -Pruning old snapshots is effectively done by a database. Whenever we update a record in `SC`, SMT won't update nodes - instead it creates new nodes on the update path, without removing the old one. Since we are snapshotting each block, we need to change that mechanism to immediately remove orphaned nodes from the database. This is a safe operation - snapshots will keep track of the records and make it available when accessing past versions. - -To manage the active snapshots we will either use a DB _max number of snapshots_ option (if available), or we will remove DB snapshots in the `EndBlocker`. The latter option can be done efficiently by identifying snapshots with block height and calling a store function to remove past versions. - -#### Accessing old state versions - -One of the functional requirements is to access old state. This is done through `abci.RequestQuery` structure. The version is specified by a block height (so we query for an object by a key `K` at block height `H`). The number of old versions supported for `abci.RequestQuery` is configurable. Accessing an old state is done by using available snapshots. -`abci.RequestQuery` doesn't need old state of `SC` unless the `prove=true` parameter is set. The SMT merkle proof must be included in the `abci.ResponseQuery` only if both `SC` and `SS` have a snapshot for requested version. - -Moreover, Cosmos SDK could provide a way to directly access a historical state. However, a state machine shouldn't do that - since the number of snapshots is configurable, it would lead to nondeterministic execution. - -We positively [validated](https://github.com/cosmos/cosmos-sdk/discussions/8297) a versioning and snapshot mechanism for querying old state with regards to the database we evaluated. - -### State Proofs - -For any object stored in State Store (SS), we have corresponding object in `SC`. A proof for object `V` identified by a key `K` is a branch of `SC`, where the path corresponds to the key `hash(K)`, and the leaf is `hash(K, V)`. - -### Rollbacks - -We need to be able to process transactions and roll-back state updates if a transaction fails. This can be done in the following way: during transaction processing, we keep all state change requests (writes) in a `CacheWrapper` abstraction (as it's done today). Once we finish the block processing, in the `Endblocker`, we commit a root store - at that time, all changes are written to the SMT and to the `SS` and a snapshot is created. - -### Committing to an object without saving it - -We identified use-cases, where modules will need to save an object commitment without storing an object itself. Sometimes clients are receiving complex objects, and they have no way to prove a correctness of that object without knowing the storage layout. For those use cases it would be easier to commit to the object without storing it directly. - -### Refactor MultiStore - -The Stargate `/store` implementation (store/v1) adds an additional layer in the SDK store construction - the `MultiStore` structure. The multistore exists to support the modularity of the Cosmos SDK - each module is using its own instance of IAVL, but in the current implementation, all instances share the same database. The latter indicates, however, that the implementation doesn't provide true modularity. Instead it causes problems related to race condition and atomic DB commits (see: [\#6370](https://github.com/cosmos/cosmos-sdk/issues/6370) and [discussion](https://github.com/cosmos/cosmos-sdk/discussions/8297#discussioncomment-757043)). - -We propose to reduce the multistore concept from the SDK, and to use a single instance of `SC` and `SS` in a `RootStore` object. To avoid confusion, we should rename the `MultiStore` interface to `RootStore`. The `RootStore` will have the following interface; the methods for configuring tracing and listeners are omitted for brevity. - -```go -// Used where read-only access to versions is needed. -type BasicRootStore interface { - Store - GetKVStore(StoreKey) KVStore - CacheRootStore() CacheRootStore -} - -// Used as the main app state, replacing CommitMultiStore. -type CommitRootStore interface { - BasicRootStore - Committer - Snapshotter - - GetVersion(uint64) (BasicRootStore, error) - SetInitialVersion(uint64) error - - ... // Trace and Listen methods -} - -// Replaces CacheMultiStore for branched state. -type CacheRootStore interface { - BasicRootStore - Write() - - ... // Trace and Listen methods -} - -// Example of constructor parameters for the concrete type. -type RootStoreConfig struct { - Upgrades *StoreUpgrades - InitialVersion uint64 - - ReservePrefix(StoreKey, StoreType) -} -``` - - - - -In contrast to `MultiStore`, `RootStore` doesn't allow to dynamically mount sub-stores or provide an arbitrary backing DB for individual sub-stores. - -NOTE: modules will be able to use a special commitment and their own DBs. For example: a module which will use ZK proofs for state can store and commit this proof in the `RootStore` (usually as a single record) and manage the specialized store privately or using the `SC` low level interface. - -#### Compatibility support - -To ease the transition to this new interface for users, we can create a shim which wraps a `CommitMultiStore` but provides a `CommitRootStore` interface, and expose functions to safely create and access the underlying `CommitMultiStore`. - -The new `RootStore` and supporting types can be implemented in a `store/v2alpha1` package to avoid breaking existing code. - -#### Merkle Proofs and IBC - -Currently, an IBC (v1.0) Merkle proof path consists of two elements (`["", ""]`), with each key corresponding to a separate proof. These are each verified according to individual [ICS-23 specs](https://github.com/cosmos/ibc-go/blob/f7051429e1cf833a6f65d51e6c3df1609290a549/modules/01-tx-lifecycle.md23-commitment/types/merkle.go#L17), and the result hash of each step is used as the committed value of the next step, until a root commitment hash is obtained. -The root hash of the proof for `""` is hashed with the `""` to validate against the App Hash. - -This is not compatible with the `RootStore`, which stores all records in a single Merkle tree structure, and won't produce separate proofs for the store- and record-key. Ideally, the store-key component of the proof could just be omitted, and updated to use a "no-op" spec, so only the record-key is used. However, because the IBC verification code hardcodes the `"ibc"` prefix and applies it to the SDK proof as a separate element of the proof path, this isn't possible without a breaking change. Breaking this behavior would severely impact the Cosmos ecosystem which already widely adopts the IBC module. Requesting an update of the IBC module across the chains is a time consuming effort and not easily feasible. - -As a workaround, the `RootStore` will have to use two separate SMTs (they could use the same underlying DB): one for IBC state and one for everything else. A simple Merkle map that reference these SMTs will act as a Merkle Tree to create a final App hash. The Merkle map is not stored in a DBs - it's constructed in the runtime. The IBC substore key must be `"ibc"`. - -The workaround can still guarantee atomic syncs: the [proposed DB backends](#evaluated-kv-databases) support atomic transactions and efficient rollbacks, which will be used in the commit phase. - -The presented workaround can be used until the IBC module is fully upgraded to supports single-element commitment proofs. - -### Optimization: compress module key prefixes - -We consider a compression of prefix keys by creating a mapping from module key to an integer, and serializing the integer using varint coding. Varint coding assures that different values don't have common byte prefix. For Merkle Proofs we can't use prefix compression - so it should only apply for the `SS` keys. Moreover, the prefix compression should be only applied for the module namespace. More precisely: - -* each module has it's own namespace; -* when accessing a module namespace we create a KVStore with embedded prefix; -* that prefix will be compressed only when accessing and managing `SS`. - -We need to assure that the codes won't change. We can fix the mapping in a static variable (provided by an app) or SS state under a special key. - -TODO: need to make decision about the key compression. - -## Optimization: SS key compression - -Some objects may be saved with key, which contains a Protobuf message type. Such keys are long. We could save a lot of space if we can map Protobuf message types in varints. - -TODO: finalize this or move to another ADR. - -## Migration - -Using the new store will require a migration. 2 Migrations are proposed: - -1. Genesis export -- it will reset the blockchain history. -2. In place migration: we can reuse `UpgradeKeeper.SetUpgradeHandler` to provide the migration logic: - -```go -app.UpgradeKeeper.SetUpgradeHandler("adr-40", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - - storev2.Migrate(iavlstore, v2.store) - - // RunMigrations returns the VersionMap - // with the updated module ConsensusVersions - return app.mm.RunMigrations(ctx, vm) -}) -``` - -The `Migrate` function will read all entries from a store/v1 DB and save them to the AD-40 combined KV store. -Cache layer should not be used and the operation must finish with a single Commit call. - -Inserting records to the `SC` (SMT) component is the bottleneck. Unfortunately SMT doesn't support batch transactions. -Adding batch transactions to `SC` layer is considered as a feature after the main release. - -## Consequences - -### Backwards Compatibility - -This ADR doesn't introduce any Cosmos SDK level API changes. - -We change the storage layout of the state machine, a storage hard fork and network upgrade is required to incorporate these changes. SMT provides a merkle proof functionality, however it is not compatible with ICS23. Updating the proofs for ICS23 compatibility is required. - -### Positive - -* Decoupling state from state commitment introduce better engineering opportunities for further optimizations and better storage patterns. -* Performance improvements. -* Joining SMT based camp which has wider and proven adoption than IAVL. Example projects which decided on SMT: Ethereum2, Diem (Libra), Trillan, Tezos, Celestia. -* Multistore removal fixes a longstanding issue with the current MultiStore design. -* Simplifies merkle proofs - all modules, except IBC, have only one pass for merkle proof. - -### Negative - -* Storage migration -* LL SMT doesn't support pruning - we will need to add and test that functionality. -* `SS` keys will have an overhead of a key prefix. This doesn't impact `SC` because all keys in `SC` have same size (they are hashed). - -### Neutral - -* Deprecating IAVL, which is one of the core proposals of Cosmos Whitepaper. - -## Alternative designs - -Most of the alternative designs were evaluated in [state commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h). - -Ethereum research published [Verkle Trie](https://dankradfeist.de/ethereum/2021/06/18/verkle-trie-for-eth1.html) - an idea of combining polynomial commitments with merkle tree in order to reduce the tree height. This concept has a very good potential, but we think it's too early to implement it. The current, SMT based design could be easily updated to the Verkle Trie once other research implement all necessary libraries. The main advantage of the design described in this ADR is the separation of state commitments from the data storage and designing a more powerful interface. - -## Further Discussions - -### Evaluated KV Databases - -We verified existing databases KV databases for evaluating snapshot support. The following databases provide efficient snapshot mechanism: Badger, RocksDB, [Pebble](https://github.com/cockroachdb/pebble). Databases which don't provide such support or are not production ready: boltdb, leveldb, goleveldb, membdb, lmdb. - -### RDBMS - -Use of RDBMS instead of simple KV store for state. Use of RDBMS will require a Cosmos SDK API breaking change (`KVStore` interface) and will allow better data extraction and indexing solutions. Instead of saving an object as a single blob of bytes, we could save it as record in a table in the state storage layer, and as a `hash(key, protobuf(object))` in the SMT as outlined above. To verify that an object registered in RDBMS is same as the one committed to SMT, one will need to load it from RDBMS, marshal using protobuf, hash and do SMT search. - -### Off Chain Store - -We were discussing use case where modules can use a support database, which is not automatically committed. Module will responsible for having a sound storage model and can optionally use the feature discussed in __Committing to an object without saving it_ section. - -## References - -* [IAVL What's Next?](https://github.com/cosmos/cosmos-sdk/issues/7100) -* [IAVL overview](https://docs.google.com/document/d/16Z_hW2rSAmoyMENO-RlAhQjAG3mSNKsQueMnKpmcBv0/edit#heading=h.yd2th7x3o1iv) of it's state v0.15 -* [State commitments and storage report](https://paper.dropbox.com/published/State-commitments-and-storage-review--BDvA1MLwRtOx55KRihJ5xxLbBw-KeEB7eOd11pNrZvVtqUgL3h) -* [Celestia (LazyLedger) SMT](https://github.com/lazyledger/smt) -* Facebook Diem (Libra) SMT [design](https://developers.diem.com/papers/jellyfish-merkle-tree/2021-01-14.pdf) -* [Trillian Revocation Transparency](https://github.com/google/trillian/blob/master/docs/papers/RevocationTransparency.pdf), [Trillian Verifiable Data Structures](https://github.com/google/trillian/blob/master/docs/papers/VerifiableDataStructures.pdf). -* Design and implementation [discussion](https://github.com/cosmos/cosmos-sdk/discussions/8297). -* [How to Upgrade IBC Chains and their Clients](https://github.com/cosmos/ibc-go/blob/main/docs/ibc/upgrades/quick-guide.md) -* [ADR-40 Effect on IBC](https://github.com/cosmos/ibc-go/discussions/256) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-041-in-place-store-migrations.md b/versioned_docs/version-0.46/integrate/architecture/adr-041-in-place-store-migrations.md deleted file mode 100644 index b0ff8e4e7..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-041-in-place-store-migrations.md +++ /dev/null @@ -1,167 +0,0 @@ -# ADR 041: In-Place Store Migrations - -## Changelog - -* 17.02.2021: Initial Draft - -## Status - -Accepted - -## Abstract - -This ADR introduces a mechanism to perform in-place state store migrations during chain software upgrades. - -## Context - -When a chain upgrade introduces state-breaking changes inside modules, the current procedure consists of exporting the whole state into a JSON file (via the `simd export` command), running migration scripts on the JSON file (`simd migrate` command), clearing the stores (`simd unsafe-reset-all` command), and starting a new chain with the migrated JSON file as new genesis (optionally with a custom initial block height). An example of such a procedure can be seen [in the Cosmos Hub 3->4 migration guide](https://github.com/cosmos/gaia/blob/v4.0.3/docs/migration/cosmoshub-3.md#upgrade-procedure). - -This procedure is cumbersome for multiple reasons: - -* The procedure takes time. It can take hours to run the `export` command, plus some additional hours to run `InitChain` on the fresh chain using the migrated JSON. -* The exported JSON file can be heavy (~100MB-1GB), making it difficult to view, edit and transfer, which in turn introduces additional work to solve these problems (such as [streaming genesis](https://github.com/cosmos/cosmos-sdk/issues/6936)). - -## Decision - -We propose a migration procedure based on modifying the KV store in-place without involving the JSON export-process-import flow described above. - -### Module `ConsensusVersion` - -We introduce a new method on the `AppModule` interface: - -```go -type AppModule interface { - // --snip-- - ConsensusVersion() uint64 -} -``` - -This methods returns an `uint64` which serves as state-breaking version of the module. It MUST be incremented on each consensus-breaking change introduced by the module. To avoid potential errors with default values, the initial version of a module MUST be set to 1. In the Cosmos SDK, version 1 corresponds to the modules in the v0.41 series. - -### Module-Specific Migration Functions - -For each consensus-breaking change introduced by the module, a migration script from ConsensusVersion `N` to version `N+1` MUST be registered in the `Configurator` using its newly-added `RegisterMigration` method. All modules receive a reference to the configurator in their `RegisterServices` method on `AppModule`, and this is where the migration functions should be registered. The migration functions should be registered in increasing order. - -```go -func (am AppModule) RegisterServices(cfg module.Configurator) { - // --snip-- - cfg.RegisterMigration(types.ModuleName, 1, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 1 to 2. - }) - cfg.RegisterMigration(types.ModuleName, 2, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 2 to 3. - }) - // etc. -} -``` - -For example, if the new ConsensusVersion of a module is `N` , then `N-1` migration functions MUST be registered in the configurator. - -In the Cosmos SDK, the migration functions are handled by each module's keeper, because the keeper holds the `sdk.StoreKey` used to perform in-place store migrations. To not overload the keeper, a `Migrator` wrapper is used by each module to handle the migration functions: - -```go -// Migrator is a struct for handling in-place store migrations. -type Migrator struct { - BaseKeeper -} -``` - -Migration functions should live inside the `migrations/` folder of each module, and be called by the Migrator's methods. We propose the format `Migrate{M}to{N}` for method names. - -```go -// Migrate1to2 migrates from version 1 to 2. -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v043bank.MigrateStore(ctx, m.keeper.storeKey) // v043bank is package `x/bank/migrations/v043`. -} -``` - -Each module's migration functions are specific to the module's store evolutions, and are not described in this ADR. An example of x/bank store key migrations after the introduction of ADR-028 length-prefixed addresses can be seen in this [store.go code](https://github.com/cosmos/cosmos-sdk/blob/36f68eb9e041e20a5bb47e216ac5eb8b91f95471/x/bank/legacy/v043/store.go#L41-L62). - -### Tracking Module Versions in `x/upgrade` - -We introduce a new prefix store in `x/upgrade`'s store. This store will track each module's current version, it can be modelized as a `map[string]uint64` of module name to module ConsensusVersion, and will be used when running the migrations (see next section for details). The key prefix used is `0x1`, and the key/value format is: - -```text -0x2 | {bytes(module_name)} => BigEndian(module_consensus_version) -``` - -The initial state of the store is set from `app.go`'s `InitChainer` method. - -The UpgradeHandler signature needs to be updated to take a `VersionMap`, as well as return an upgraded `VersionMap` and an error: - -```diff -- type UpgradeHandler func(ctx sdk.Context, plan Plan) -+ type UpgradeHandler func(ctx sdk.Context, plan Plan, versionMap VersionMap) (VersionMap, error) -``` - -To apply an upgrade, we query the `VersionMap` from the `x/upgrade` store and pass it into the handler. The handler runs the actual migration functions (see next section), and if successful, returns an updated `VersionMap` to be stored in state. - -```diff -func (k UpgradeKeeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) { - // --snip-- -- handler(ctx, plan) -+ updatedVM, err := handler(ctx, plan, k.GetModuleVersionMap(ctx)) // k.GetModuleVersionMap() fetches the VersionMap stored in state. -+ if err != nil { -+ return err -+ } -+ -+ // Set the updated consensus versions to state -+ k.SetModuleVersionMap(ctx, updatedVM) -} -``` - -A gRPC query endpoint to query the `VersionMap` stored in `x/upgrade`'s state will also be added, so that app developers can double-check the `VersionMap` before the upgrade handler runs. - -### Running Migrations - -Once all the migration handlers are registered inside the configurator (which happens at startup), running migrations can happen by calling the `RunMigrations` method on `module.Manager`. This function will loop through all modules, and for each module: - -* Get the old ConsensusVersion of the module from its `VersionMap` argument (let's call it `M`). -* Fetch the new ConsensusVersion of the module from the `ConsensusVersion()` method on `AppModule` (call it `N`). -* If `N>M`, run all registered migrations for the module sequentially `M -> M+1 -> M+2...` until `N`. - * There is a special case where there is no ConsensusVersion for the module, as this means that the module has been newly added during the upgrade. In this case, no migration function is run, and the module's current ConsensusVersion is saved to `x/upgrade`'s store. - -If a required migration is missing (e.g. if it has not been registered in the `Configurator`), then the `RunMigrations` function will error. - -In practice, the `RunMigrations` method should be called from inside an `UpgradeHandler`. - -```go -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - return app.mm.RunMigrations(ctx, vm) -}) -``` - -Assuming a chain upgrades at block `n`, the procedure should run as follows: - -* the old binary will halt in `BeginBlock` when starting block `N`. In its store, the ConsensusVersions of the old binary's modules are stored. -* the new binary will start at block `N`. The UpgradeHandler is set in the new binary, so will run at `BeginBlock` of the new binary. Inside `x/upgrade`'s `ApplyUpgrade`, the `VersionMap` will be retrieved from the (old binary's) store, and passed into the `RunMigrations` functon, migrating all module stores in-place before the modules' own `BeginBlock`s. - -## Consequences - -### Backwards Compatibility - -This ADR introduces a new method `ConsensusVersion()` on `AppModule`, which all modules need to implement. It also alters the UpgradeHandler function signature. As such, it is not backwards-compatible. - -While modules MUST register their migration functions when bumping ConsensusVersions, running those scripts using an upgrade handler is optional. An application may perfectly well decide to not call the `RunMigrations` inside its upgrade handler, and continue using the legacy JSON migration path. - -### Positive - -* Perform chain upgrades without manipulating JSON files. -* While no benchmark has been made yet, it is probable that in-place store migrations will take less time than JSON migrations. The main reason supporting this claim is that both the `simd export` command on the old binary and the `InitChain` function on the new binary will be skipped. - -### Negative - -* Module developers MUST correctly track consensus-breaking changes in their modules. If a consensus-breaking change is introduced in a module without its corresponding `ConsensusVersion()` bump, then the `RunMigrations` function won't detect the migration, and the chain upgrade might be unsuccessful. Documentation should clearly reflect this. - -### Neutral - -* The Cosmos SDK will continue to support JSON migrations via the existing `simd export` and `simd migrate` commands. -* The current ADR does not allow creating, renaming or deleting stores, only modifying existing store keys and values. The Cosmos SDK already has the `StoreLoader` for those operations. - -## Further Discussions - -## References - -* Initial discussion: https://github.com/cosmos/cosmos-sdk/discussions/8429 -* Implementation of `ConsensusVersion` and `RunMigrations`: https://github.com/cosmos/cosmos-sdk/pull/8485 -* Issue discussing `x/upgrade` design: https://github.com/cosmos/cosmos-sdk/issues/8514 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-042-group-module.md b/versioned_docs/version-0.46/integrate/architecture/adr-042-group-module.md deleted file mode 100644 index 74ba17f43..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-042-group-module.md +++ /dev/null @@ -1,279 +0,0 @@ -# ADR 042: Group Module - -## Changelog - -* 2020/04/09: Initial Draft - -## Status - -Draft - -## Abstract - -This ADR defines the `x/group` module which allows the creation and management of on-chain multi-signature accounts and enables voting for message execution based on configurable decision policies. - -## Context - -The legacy amino multi-signature mechanism of the Cosmos SDK has certain limitations: - -* Key rotation is not possible, although this can be solved with [account rekeying](adr-034-account-rekeying.md). -* Thresholds can't be changed. -* UX is cumbersome for non-technical users ([#5661](https://github.com/cosmos/cosmos-sdk/issues/5661)). -* It requires `legacy_amino` sign mode ([#8141](https://github.com/cosmos/cosmos-sdk/issues/8141)). - -While the group module is not meant to be a total replacement for the current multi-signature accounts, it provides a solution to the limitations described above, with a more flexible key management system where keys can be added, updated or removed, as well as configurable thresholds. -It's meant to be used with other access control modules such as [`x/feegrant`](./adr-029-fee-grant-module.md) ans [`x/authz`](adr-030-authz-module.md) to simplify key management for individuals and organizations. - -The proof of concept of the group module can be found in https://github.com/regen-network/regen-ledger/tree/master/proto/regen/group/v1alpha1 and https://github.com/regen-network/regen-ledger/tree/master/x/group. - -## Decision - -We propose merging the `x/group` module with its supporting [ORM/Table Store package](https://github.com/regen-network/regen-ledger/tree/master/orm) ([#7098](https://github.com/cosmos/cosmos-sdk/issues/7098)) into the Cosmos SDK and continuing development here. There will be a dedicated ADR for the ORM package. - -### Group - -A group is a composition of accounts with associated weights. It is not -an account and doesn't have a balance. It doesn't in and of itself have any -sort of voting or decision weight. -Group members can create proposals and vote on them through group accounts using different decision policies. - -It has an `admin` account which can manage members in the group, update the group -metadata and set a new admin. - -```proto -message GroupInfo { - - // group_id is the unique ID of this group. - uint64 group_id = 1; - - // admin is the account address of the group's admin. - string admin = 2; - - // metadata is any arbitrary metadata to attached to the group. - bytes metadata = 3; - - // version is used to track changes to a group's membership structure that - // would break existing proposals. Whenever a member weight has changed, - // or any member is added or removed, the version is incremented and will - // invalidate all proposals from older versions. - uint64 version = 4; - - // total_weight is the sum of the group members' weights. - string total_weight = 5; -} -``` - -```proto -message GroupMember { - - // group_id is the unique ID of the group. - uint64 group_id = 1; - - // member is the member data. - Member member = 2; -} - -// Member represents a group member with an account address, -// non-zero weight and metadata. -message Member { - - // address is the member's account address. - string address = 1; - - // weight is the member's voting weight that should be greater than 0. - string weight = 2; - - // metadata is any arbitrary metadata to attached to the member. - bytes metadata = 3; -} -``` - -### Group Account - -A group account is an account associated with a group and a decision policy. -A group account does have a balance. - -Group accounts are abstracted from groups because a single group may have -multiple decision policies for different types of actions. Managing group -membership separately from decision policies results in the least overhead -and keeps membership consistent across different policies. The pattern that -is recommended is to have a single master group account for a given group, -and then to create separate group accounts with different decision policies -and delegate the desired permissions from the master account to -those "sub-accounts" using the [`x/authz` module](adr-030-authz-module.md). - -```proto -message GroupAccountInfo { - - // address is the group account address. - string address = 1; - - // group_id is the ID of the Group the GroupAccount belongs to. - uint64 group_id = 2; - - // admin is the account address of the group admin. - string admin = 3; - - // metadata is any arbitrary metadata of this group account. - bytes metadata = 4; - - // version is used to track changes to a group's GroupAccountInfo structure that - // invalidates active proposal from old versions. - uint64 version = 5; - - // decision_policy specifies the group account's decision policy. - google.protobuf.Any decision_policy = 6 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; -} -``` - -Similarly to a group admin, a group account admin can update its metadata, decision policy or set a new group account admin. - -A group account can also be an admin or a member of a group. -For instance, a group admin could be another group account which could "elects" the members or it could be the same group that elects itself. - -### Decision Policy - -A decision policy is the mechanism by which members of a group can vote on -proposals. - -All decision policies should have a minimum and maximum voting window. -The minimum voting window is the minimum duration that must pass in order -for a proposal to potentially pass, and it may be set to 0. The maximum voting -window is the maximum time that a proposal may be voted on and executed if -it reached enough support before it is closed. -Both of these values must be less than a chain-wide max voting window parameter. - -We define the `DecisionPolicy` interface that all decision policies must implement: - -```go -type DecisionPolicy interface { - codec.ProtoMarshaler - - ValidateBasic() error - GetTimeout() types.Duration - Allow(tally Tally, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error) - Validate(g GroupInfo) error -} - -type DecisionPolicyResult struct { - Allow bool - Final bool -} -``` - -#### Threshold decision policy - -A threshold decision policy defines a minimum support votes (_yes_), based on a tally -of voter weights, for a proposal to pass. For -this decision policy, abstain and veto are treated as no support (_no_). - -```proto -message ThresholdDecisionPolicy { - - // threshold is the minimum weighted sum of support votes for a proposal to succeed. - string threshold = 1; - - // voting_period is the duration from submission of a proposal to the end of voting period - // Within this period, votes and exec messages can be submitted. - google.protobuf.Duration voting_period = 2 [(gogoproto.nullable) = false]; -} -``` - -### Proposal - -Any member of a group can submit a proposal for a group account to decide upon. -A proposal consists of a set of `sdk.Msg`s that will be executed if the proposal -passes as well as any metadata associated with the proposal. These `sdk.Msg`s get validated as part of the `Msg/CreateProposal` request validation. They should also have their signer set as the group account. - -Internally, a proposal also tracks: - -* its current `Status`: submitted, closed or aborted -* its `Result`: unfinalized, accepted or rejected -* its `VoteState` in the form of a `Tally`, which is calculated on new votes and when executing the proposal. - -```proto -// Tally represents the sum of weighted votes. -message Tally { - option (gogoproto.goproto_getters) = false; - - // yes_count is the weighted sum of yes votes. - string yes_count = 1; - - // no_count is the weighted sum of no votes. - string no_count = 2; - - // abstain_count is the weighted sum of abstainers. - string abstain_count = 3; - - // veto_count is the weighted sum of vetoes. - string veto_count = 4; -} -``` - -### Voting - -Members of a group can vote on proposals. There are four choices to choose while voting - yes, no, abstain and veto. Not -all decision policies will support them. Votes can contain some optional metadata. -In the current implementation, the voting window begins as soon as a proposal -is submitted. - -Voting internally updates the proposal `VoteState` as well as `Status` and `Result` if needed. - -### Executing Proposals - -Proposals will not be automatically executed by the chain in this current design, -but rather a user must submit a `Msg/Exec` transaction to attempt to execute the -proposal based on the current votes and decision policy. A future upgrade could -automate this and have the group account (or a fee granter) pay. - -#### Changing Group Membership - -In the current implementation, updating a group or a group account after submitting a proposal will make it invalid. It will simply fail if someone calls `Msg/Exec` and will eventually be garbage collected. - -### Notes on current implementation - -This section outlines the current implementation used in the proof of concept of the group module but this could be subject to changes and iterated on. - -#### ORM - -The [ORM package](https://github.com/cosmos/cosmos-sdk/discussions/9156) defines tables, sequences and secondary indexes which are used in the group module. - -Groups are stored in state as part of a `groupTable`, the `group_id` being an auto-increment integer. Group members are stored in a `groupMemberTable`. - -Group accounts are stored in a `groupAccountTable`. The group account address is generated based on an auto-increment integer which is used to derive the group module `RootModuleKey` into a `DerivedModuleKey`, as stated in [ADR-033](adr-033-protobuf-inter-module-comm.md#modulekeys-and-moduleids). The group account is added as a new `ModuleAccount` through `x/auth`. - -Proposals are stored as part of the `proposalTable` using the `Proposal` type. The `proposal_id` is an auto-increment integer. - -Votes are stored in the `voteTable`. The primary key is based on the vote's `proposal_id` and `voter` account address. - -#### ADR-033 to route proposal messages - -Inter-module communication introduced by [ADR-033](adr-033-protobuf-inter-module-comm.md) can be used to route a proposal's messages using the `DerivedModuleKey` corresponding to the proposal's group account. - -## Consequences - -### Positive - -* Improved UX for multi-signature accounts allowing key rotation and custom decision policies. - -### Negative - -### Neutral - -* It uses ADR 033 so it will need to be implemented within the Cosmos SDK, but this doesn't imply necessarily any large refactoring of existing Cosmos SDK modules. -* The current implementation of the group module uses the ORM package. - -## Further Discussions - -* Convergence of `/group` and `x/gov` as both support proposals and voting: https://github.com/cosmos/cosmos-sdk/discussions/9066 -* `x/group` possible future improvements: - * Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288) - * Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41) - * Make `Tally` more flexible and support non-binary choices - -## References - -* Initial specification: - * https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module - * [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236) -* Proposal to add `x/group` into the Cosmos SDK: [#7633](https://github.com/cosmos/cosmos-sdk/issues/7633) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-043-nft-module.md b/versioned_docs/version-0.46/integrate/architecture/adr-043-nft-module.md deleted file mode 100644 index 7d4498bf8..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-043-nft-module.md +++ /dev/null @@ -1,340 +0,0 @@ -# ADR 43: NFT Module - -## Changelog - -* 2021-05-01: Initial Draft -* 2021-07-02: Review updates - -## Status - -PROPOSED - -## Abstract - -This ADR defines the `x/nft` module which is a generic implementation of NFTs, roughly "compatible" with ERC721. **Applications using the `x/nft` module must implement the following functions**: - -* `MsgNewClass` - Receive the user's request to create a class, and call the `NewClass` of the `x/nft` module. -* `MsgUpdateClass` - Receive the user's request to update a class, and call the `UpdateClass` of the `x/nft` module. -* `MsgMintNFT` - Receive the user's request to mint a nft, and call the `MintNFT` of the `x/nft` module. -* `BurnNFT` - Receive the user's request to burn a nft, and call the `BurnNFT` of the `x/nft` module. -* `UpdateNFT` - Receive the user's request to update a nft, and call the `UpdateNFT` of the `x/nft` module. - -## Context - -NFTs are more than just crypto art, which is very helpful for accruing value to the Cosmos ecosystem. As a result, Cosmos Hub should implement NFT functions and enable a unified mechanism for storing and sending the ownership representative of NFTs as discussed in https://github.com/cosmos/cosmos-sdk/discussions/9065. - -As discussed in [#9065](https://github.com/cosmos/cosmos-sdk/discussions/9065), several potential solutions can be considered: - -* irismod/nft and modules/incubator/nft -* CW721 -* DID NFTs -* interNFT - -Since functions/use cases of NFTs are tightly connected with their logic, it is almost impossible to support all the NFTs' use cases in one Cosmos SDK module by defining and implementing different transaction types. - -Considering generic usage and compatibility of interchain protocols including IBC and Gravity Bridge, it is preferred to have a generic NFT module design which handles the generic NFTs logic. -This design idea can enable composability that application-specific functions should be managed by other modules on Cosmos Hub or on other Zones by importing the NFT module. - -The current design is based on the work done by [IRISnet team](https://github.com/irisnet/irismod/tree/master/modules/nft) and an older implementation in the [Cosmos repository](https://github.com/cosmos/modules/tree/master/incubator/nft). - -## Decision - -We create a `x/nft` module, which contains the following functionality: - -* Store NFTs and track their ownership. -* Expose `Keeper` interface for composing modules to transfer, mint and burn NFTs. -* Expose external `Message` interface for users to transfer ownership of their NFTs. -* Query NFTs and their supply information. - -The proposed module is a base module for NFT app logic. It's goal it to provide a common layer for storage, basic transfer functionality and IBC. The module should not be used as a standalone. -Instead an app should create a specialized module to handle app specific logic (eg: NFT ID construction, royalty), user level minting and burning. Moreover an app specialized module should handle auxiliary data to support the app logic (eg indexes, ORM, business data). - -All data carried over IBC must be part of the `NFT` or `Class` type described below. The app specific NFT data should be encoded in `NFT.data` for cross-chain integrity. Other objects related to NFT, which are not important for integrity can be part of the app specific module. - -### Types - -We propose two main types: - -* `Class` -- describes NFT class. We can think about it as a smart contract address. -* `NFT` -- object representing unique, non fungible asset. Each NFT is associated with a Class. - -#### Class - -NFT **Class** is comparable to an ERC-721 smart contract (provides description of a smart contract), under which a collection of NFTs can be created and managed. - -```protobuf -message Class { - string id = 1; - string name = 2; - string symbol = 3; - string description = 4; - string uri = 5; - string uri_hash = 6; - google.protobuf.Any data = 7; -} -``` - -* `id` is an alphanumeric identifier of the NFT class; it is used as the primary index for storing the class; _required_ -* `name` is a descriptive name of the NFT class; _optional_ -* `symbol` is the symbol usually shown on exchanges for the NFT class; _optional_ -* `description` is a detailed description of the NFT class; _optional_ -* `uri` is a URI for the class metadata stored off chain. It should be a JSON file that contains metadata about the NFT class and NFT data schema ([OpenSea example](https://docs.opensea.io/docs/contract-level-metadata)); _optional_ -* `uri_hash` is a hash of the document pointed by uri; _optional_ -* `data` is app specific metadata of the class; _optional_ - -#### NFT - -We define a general model for `NFT` as follows. - -```protobuf -message NFT { - string class_id = 1; - string id = 2; - string uri = 3; - string uri_hash = 4; - google.protobuf.Any data = 10; -} -``` - -* `class_id` is the identifier of the NFT class where the NFT belongs; _required_,`[a-zA-Z][a-zA-Z0-9/:-]{2,100}` -* `id` is an alphanumeric identifier of the NFT, unique within the scope of its class. It is specified by the creator of the NFT and may be expanded to use DID in the future. `class_id` combined with `id` uniquely identifies an NFT and is used as the primary index for storing the NFT; _required_,`[a-zA-Z][a-zA-Z0-9/:-]{2,100}` - - ```text - {class_id}/{id} --> NFT (bytes) - ``` - -* `uri` is a URI for the NFT metadata stored off chain. Should point to a JSON file that contains metadata about this NFT (Ref: [ERC721 standard and OpenSea extension](https://docs.opensea.io/docs/metadata-standards)); _required_ -* `uri_hash` is a hash of the document pointed by uri; _optional_ -* `data` is an app specific data of the NFT. CAN be used by composing modules to specify additional properties of the NFT; _optional_ - -This ADR doesn't specify values that `data` can take; however, best practices recommend upper-level NFT modules clearly specify their contents. Although the value of this field doesn't provide the additional context required to manage NFT records, which means that the field can technically be removed from the specification, the field's existence allows basic informational/UI functionality. - -### `Keeper` Interface - -```go -type Keeper interface { - NewClass(class Class) - UpdateClass(class Class) - - Mint(nft NFT,receiver sdk.AccAddress) // updates totalSupply - Burn(classId string, nftId string) // updates totalSupply - Update(nft NFT) - Transfer(classId string, nftId string, receiver sdk.AccAddress) - - GetClass(classId string) Class - GetClasses() []Class - - GetNFT(classId string, nftId string) NFT - GetNFTsOfClassByOwner(classId string, owner sdk.AccAddress) []NFT - GetNFTsOfClass(classId string) []NFT - - GetOwner(classId string, nftId string) sdk.AccAddress - GetBalance(classId string, owner sdk.AccAddress) uint64 - GetTotalSupply(classId string) uint64 -} -``` - -Other business logic implementations should be defined in composing modules that import `x/nft` and use its `Keeper`. - -### `Msg` Service - -```protobuf -service Msg { - rpc Send(MsgSend) returns (MsgSendResponse); -} - -message MsgSend { - string class_id = 1; - string id = 2; - string sender = 3; - string reveiver = 4; -} -message MsgSendResponse {} -``` - -`MsgSend` can be used to transfer the ownership of an NFT to another address. - -The implementation outline of the server is as follows: - -```go -type msgServer struct{ - k Keeper -} - -func (m msgServer) Send(ctx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error) { - // check current ownership - assertEqual(msg.Sender, m.k.GetOwner(msg.ClassId, msg.Id)) - - // transfer ownership - m.k.Transfer(msg.ClassId, msg.Id, msg.Receiver) - - return &types.MsgSendResponse{}, nil -} -``` - -The query service methods for the `x/nft` module are: - -```proto -service Query { - // Balance queries the number of NFTs of a given class owned by the owner, same as balanceOf in ERC721 - rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/balance/{owner}/{class_id}"; - } - - // Owner queries the owner of the NFT based on its class and id, same as ownerOf in ERC721 - rpc Owner(QueryOwnerRequest) returns (QueryOwnerResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/owner/{class_id}/{id}"; - } - - // Supply queries the number of NFTs from the given class, same as totalSupply of ERC721. - rpc Supply(QuerySupplyRequest) returns (QuerySupplyResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/supply/{class_id}"; - } - - // NFTs queries all NFTs of a given class or owner,choose at least one of the two, similar to tokenByIndex in ERC721Enumerable - rpc NFTs(QueryNFTsRequest) returns (QueryNFTsResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/nfts"; - } - - // NFT queries an NFT based on its class and id. - rpc NFT(QueryNFTRequest) returns (QueryNFTResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/nfts/{class_id}/{id}"; - } - - // Class queries an NFT class based on its id - rpc Class(QueryClassRequest) returns (QueryClassResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/classes/{class_id}"; - } - - // Classes queries all NFT classes - rpc Classes(QueryClassesRequest) returns (QueryClassesResponse) { - option (google.api.http).get = "/cosmos/nft/v1beta1/classes"; - } -} - -// QueryBalanceRequest is the request type for the Query/Balance RPC method -message QueryBalanceRequest { - string class_id = 1; - string owner = 2; -} - -// QueryBalanceResponse is the response type for the Query/Balance RPC method -message QueryBalanceResponse { - uint64 amount = 1; -} - -// QueryOwnerRequest is the request type for the Query/Owner RPC method -message QueryOwnerRequest { - string class_id = 1; - string id = 2; -} - -// QueryOwnerResponse is the response type for the Query/Owner RPC method -message QueryOwnerResponse { - string owner = 1; -} - -// QuerySupplyRequest is the request type for the Query/Supply RPC method -message QuerySupplyRequest { - string class_id = 1; -} - -// QuerySupplyResponse is the response type for the Query/Supply RPC method -message QuerySupplyResponse { - uint64 amount = 1; -} - -// QueryNFTstRequest is the request type for the Query/NFTs RPC method -message QueryNFTsRequest { - string class_id = 1; - string owner = 2; - cosmos.base.query.v1beta1.PageRequest pagination = 3; -} - -// QueryNFTsResponse is the response type for the Query/NFTs RPC methods -message QueryNFTsResponse { - repeated cosmos.nft.v1beta1.NFT nfts = 1; - cosmos.base.query.v1beta1.PageResponse pagination = 2; -} - -// QueryNFTRequest is the request type for the Query/NFT RPC method -message QueryNFTRequest { - string class_id = 1; - string id = 2; -} - -// QueryNFTResponse is the response type for the Query/NFT RPC method -message QueryNFTResponse { - cosmos.nft.v1beta1.NFT nft = 1; -} - -// QueryClassRequest is the request type for the Query/Class RPC method -message QueryClassRequest { - string class_id = 1; -} - -// QueryClassResponse is the response type for the Query/Class RPC method -message QueryClassResponse { - cosmos.nft.v1beta1.Class class = 1; -} - -// QueryClassesRequest is the request type for the Query/Classes RPC method -message QueryClassesRequest { - // pagination defines an optional pagination for the request. - cosmos.base.query.v1beta1.PageRequest pagination = 1; -} - -// QueryClassesResponse is the response type for the Query/Classes RPC method -message QueryClassesResponse { - repeated cosmos.nft.v1beta1.Class classes = 1; - cosmos.base.query.v1beta1.PageResponse pagination = 2; -} -``` - -### Interoperability - -Interoperability is all about reusing assets between modules and chains. The former one is achieved by ADR-33: Protobuf client - server communication. At the time of writing ADR-33 is not finalized. The latter is achieved by IBC. Here we will focus on the IBC side. -IBC is implemented per module. Here, we aligned that NFTs will be recorded and managed in the x/nft. This requires creation of a new IBC standard and implementation of it. - -For IBC interoperability, NFT custom modules MUST use the NFT object type understood by the IBC client. So, for x/nft interoperability, custom NFT implementations (example: x/cryptokitty) should use the canonical x/nft module and proxy all NFT balance keeping functionality to x/nft or else re-implement all functionality using the NFT object type understood by the IBC client. In other words: x/nft becomes the standard NFT registry for all Cosmos NFTs (example: x/cryptokitty will register a kitty NFT in x/nft and use x/nft for book keeping). This was [discussed](https://github.com/cosmos/cosmos-sdk/discussions/9065#discussioncomment-873206) in the context of using x/bank as a general asset balance book. Not using x/nft will require implementing another module for IBC. - -## Consequences - -### Backward Compatibility - -No backward incompatibilities. - -### Forward Compatibility - -This specification conforms to the ERC-721 smart contract specification for NFT identifiers. Note that ERC-721 defines uniqueness based on (contract address, uint256 tokenId), and we conform to this implicitly because a single module is currently aimed to track NFT identifiers. Note: use of the (mutable) data field to determine uniqueness is not safe.s - -### Positive - -* NFT identifiers available on Cosmos Hub. -* Ability to build different NFT modules for the Cosmos Hub, e.g., ERC-721. -* NFT module which supports interoperability with IBC and other cross-chain infrastructures like Gravity Bridge - -### Negative - -* New IBC app is required for x/nft -* CW721 adapter is required - -### Neutral - -* Other functions need more modules. For example, a custody module is needed for NFT trading function, a collectible module is needed for defining NFT properties. - -## Further Discussions - -For other kinds of applications on the Hub, more app-specific modules can be developed in the future: - -* `x/nft/custody`: custody of NFTs to support trading functionality. -* `x/nft/marketplace`: selling and buying NFTs using sdk.Coins. -* `x/fractional`: a module to split an ownership of an asset (NFT or other assets) for multiple stakeholder. `x/group` should work for most of the cases. - -Other networks in the Cosmos ecosystem could design and implement their own NFT modules for specific NFT applications and use cases. - -## References - -* Initial discussion: https://github.com/cosmos/cosmos-sdk/discussions/9065 -* x/nft: initialize module: https://github.com/cosmos/cosmos-sdk/pull/9174 -* [ADR 033](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-033-protobuf-inter-module-comm.md) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-044-protobuf-updates-guidelines.md b/versioned_docs/version-0.46/integrate/architecture/adr-044-protobuf-updates-guidelines.md deleted file mode 100644 index 6c0b33bc8..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-044-protobuf-updates-guidelines.md +++ /dev/null @@ -1,138 +0,0 @@ -# ADR 044: Guidelines for Updating Protobuf Definitions - -## Changelog - -* 28.06.2021: Initial Draft -* 02.12.2021: Add `Since:` comment for new fields - -## Status - -Draft - -## Abstract - -This ADR provides guidelines and recommended practices when updating Protobuf definitions. These guidelines are targeting module developers. - -## Context - -The Cosmos SDK maintains a set of [Protobuf definitions](https://github.com/cosmos/cosmos-sdk/tree/main/proto/cosmos). It is important to correctly design Protobuf definitions to avoid any breaking changes within the same version. The reasons are to not break tooling (including indexers and explorers), wallets and other third-party integrations. - -When making changes to these Protobuf definitions, the Cosmos SDK currently only follows [Buf's](https://docs.buf.build/) recommendations. We noticed however that Buf's recommendations might still result in breaking changes in the SDK in some cases. For example: - -* Adding fields to `Msg`s. Adding fields is a not a Protobuf spec-breaking operation. However, when adding new fields to `Msg`s, the unknown field rejection will throw an error when sending the new `Msg` to an older node. -* Marking fields as `reserved`. Protobuf proposes the `reserved` keyword for removing fields without the need to bump the package version. However, by doing so, client backwards compatibility is broken as Protobuf doesn't generate anything for `reserved` fields. See [#9446](https://github.com/cosmos/cosmos-sdk/issues/9446) for more details on this issue. - -Moreover, module developers often face other questions around Protobuf definitions such as "Can I rename a field?" or "Can I deprecate a field?" This ADR aims to answer all these questions by providing clear guidelines about allowed updates for Protobuf definitions. - -## Decision - -We decide to keep [Buf's](https://docs.buf.build/) recommendations with the following exceptions: - -* `UNARY_RPC`: the Cosmos SDK currently does not support streaming RPCs. -* `COMMENT_FIELD`: the Cosmos SDK allows fields with no comments. -* `SERVICE_SUFFIX`: we use the `Query` and `Msg` service naming convention, which doesn't use the `-Service` suffix. -* `PACKAGE_VERSION_SUFFIX`: some packages, such as `cosmos.crypto.ed25519`, don't use a version suffix. -* `RPC_REQUEST_STANDARD_NAME`: Requests for the `Msg` service don't have the `-Request` suffix to keep backwards compatibility. - -On top of Buf's recommendations we add the following guidelines that are specific to the Cosmos SDK. - -### Updating Protobuf Definition Without Bumping Version - -#### 1. `Msg`s MUST NOT have new fields - -When processing `Msg`s, the Cosmos SDK's antehandlers are strict and don't allow unknown fields in `Msg`s. This is checked by the unknown field rejection in the [`codec/unknownproto` package](https://github.com/cosmos/cosmos-sdk/blob/master/codec/unknownproto). - -Now imagine a v0.43 node accepting a `MsgExample` transaction, and in v0.44 the chain developer decides to add a field to `MsgExample`. A client developer, which only manipulates Protobuf definitions, would see that `MsgExample` has a new field, and will populate it. However, sending the new `MsgExample` to an old v0.43 node would cause the v0.43 node to reject the `MsgExample` because of the unknown field. The expectation that the same Protobuf version can be used across multiple node versions MUST be guaranteed. - -For this reason, module developers MUST NOT add new fields to existing `Msg`s. - -It is worth mentioning that this does not limit adding fields to a `Msg`, but also to all nested structs and `Any`s inside a `Msg`. - -#### 2. Non-`Msg`-related Protobuf definitions MAY have new fields, but MUST add a `Since:` comment - -On the other hand, module developers MAY add new fields to Protobuf definitions related to the `Query` service or to objects which are saved in the store. This recommendation follows the Protobuf specification, but is added in this document for clarity. - -The SDK requires the Protobuf comment of the new field to contain one line with the following format: - -```protobuf -// Since: cosmos-sdk {, ...} -``` - -Where each `version` denotes a minor ("0.45") or patch ("0.44.5") version from which the field is available. This will greatly help client libraries, who can optionally use reflection or custom code generation to show/hide these fields depending on the targetted node version. - -As examples, the following comments are valid: - -```protobuf -// Since: cosmos-sdk 0.44 - -// Since: cosmos-sdk 0.42.11, 0.44.5 -``` - -and the following ones are NOT valid: - -```protobuf -// Since cosmos-sdk v0.44 - -// since: cosmos-sdk 0.44 - -// Since: cosmos-sdk 0.42.11 0.44.5 - -// Since: Cosmos SDK 0.42.11, 0.44.5 -``` - -#### 3. Fields MAY be marked as `deprecated`, and nodes MAY implement a protocol-breaking change for handling these fields - -Protobuf supports the [`deprecated` field option](https://developers.google.com/protocol-buffers/docs/proto#options), and this option MAY be used on any field, including `Msg` fields. If a node handles a Protobuf message with a non-empty deprecated field, the node MAY change its behavior upon processing it, even in a protocol-breaking way. When possible, the node MUST handle backwards compatibility without breaking the consensus (unless we increment the proto version). - -As an example, the Cosmos SDK v0.42 to v0.43 update contained two Protobuf-breaking changes, listed below. Instead of bumping the package versions from `v1beta1` to `v1`, the SDK team decided to follow this guideline, by reverting the breaking changes, marking those changes as deprecated, and modifying the node implementation when processing messages with deprecated fields. More specifically: - -* The Cosmos SDK recently removed support for [time-based software upgrades](https://github.com/cosmos/cosmos-sdk/pull/8849). As such, the `time` field has been marked as deprecated in `cosmos.upgrade.v1beta1.Plan`. Moreover, the node will reject any proposal containing an upgrade Plan whose `time` field is non-empty. -* The Cosmos SDK now supports [governance split votes](./adr-037-gov-split-vote.md). When querying for votes, the returned `cosmos.gov.v1beta1.Vote` message has its `option` field (used for 1 vote option) deprecated in favor of its `options` field (allowing multiple vote options). Whenever possible, the SDK still populates the deprecated `option` field, that is, if and only if the `len(options) == 1` and `options[0].Weight == 1.0`. - -#### 4. Fields MUST NOT be renamed - -Whereas the official Protobuf recommendations do not prohibit renaming fields, as it does not break the Protobuf binary representation, the SDK explicitly forbids renaming fields in Protobuf structs. The main reason for this choice is to avoid introducing breaking changes for clients, which often rely on hard-coded fields from generated types. Moreover, renaming fields will lead to client-breaking JSON representations of Protobuf definitions, used in REST endpoints and in the CLI. - -### Incrementing Protobuf Package Version - -TODO, needs architecture review. Some topics: - -* Bumping versions frequency -* When bumping versions, should the Cosmos SDK support both versions? - * i.e. v1beta1 -> v1, should we have two folders in the Cosmos SDK, and handlers for both versions? -* mention ADR-023 Protobuf naming - -## Consequences - -> This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future. - -### Backwards Compatibility - -> All ADRs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The ADR must explain how the author proposes to deal with these incompatibilities. ADR submissions without a sufficient backwards compatibility treatise may be rejected outright. - -### Positive - -* less pain to tool developers -* more compatibility in the ecosystem -* ... - -### Negative - -{negative consequences} - -### Neutral - -* more rigor in Protobuf review - -## Further Discussions - -This ADR is still in the DRAFT stage, and the "Incrementing Protobuf Package Version" will be filled in once we make a decision on how to correctly do it. - -## Test Cases [optional] - -Test cases for an implementation are mandatory for ADRs that are affecting consensus changes. Other ADRs can choose to include links to test cases if applicable. - -## References - -* [#9445](https://github.com/cosmos/cosmos-sdk/issues/9445) Release proto definitions v1 -* [#9446](https://github.com/cosmos/cosmos-sdk/issues/9446) Address v1beta1 proto breaking changes diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-045-check-delivertx-middlewares.md b/versioned_docs/version-0.46/integrate/architecture/adr-045-check-delivertx-middlewares.md deleted file mode 100644 index 60172977c..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-045-check-delivertx-middlewares.md +++ /dev/null @@ -1,312 +0,0 @@ -# ADR 045: BaseApp `{Check,Deliver}Tx` as Middlewares - -## Changelog - -* 20.08.2021: Initial draft. -* 07.12.2021: Update `tx.Handler` interface ([\#10693](https://github.com/cosmos/cosmos-sdk/pull/10693)). -* 17.05.2022: ADR is abandoned, as middlewares are deemed too hard to reason about. - -## Status - -ABANDONED. Replacement is being discussed in [#11955](https://github.com/cosmos/cosmos-sdk/issues/11955). - -## Abstract - -This ADR replaces the current BaseApp `runTx` and antehandlers design with a middleware-based design. - -## Context - -BaseApp's implementation of ABCI `{Check,Deliver}Tx()` and its own `Simulate()` method call the `runTx` method under the hood, which first runs antehandlers, then executes `Msg`s. However, the [transaction Tips](https://github.com/cosmos/cosmos-sdk/issues/9406) and [refunding unused gas](https://github.com/cosmos/cosmos-sdk/issues/2150) use cases require custom logic to be run after the `Msg`s execution. There is currently no way to achieve this. - -An naive solution would be to add post-`Msg` hooks to BaseApp. However, the Cosmos SDK team thinks in parallel about the bigger picture of making app wiring simpler ([#9181](https://github.com/cosmos/cosmos-sdk/discussions/9182)), which includes making BaseApp more lightweight and modular. - -## Decision - -We decide to transform Baseapp's implementation of ABCI `{Check,Deliver}Tx` and its own `Simulate` methods to use a middleware-based design. - -The two following interfaces are the base of the middleware design, and are defined in `types/tx`: - -```go -type Handler interface { - CheckTx(ctx context.Context, req Request, checkReq RequestCheckTx) (Response, ResponseCheckTx, error) - DeliverTx(ctx context.Context, req Request) (Response, error) - SimulateTx(ctx context.Context, req Request (Response, error) -} - -type Middleware func(Handler) Handler -``` - -where we define the following arguments and return types: - -```go -type Request struct { - Tx sdk.Tx - TxBytes []byte -} - -type Response struct { - GasWanted uint64 - GasUsed uint64 - // MsgResponses is an array containing each Msg service handler's response - // type, packed in an Any. This will get proto-serialized into the `Data` field - // in the ABCI Check/DeliverTx responses. - MsgResponses []*codectypes.Any - Log string - Events []abci.Event -} - -type RequestCheckTx struct { - Type abci.CheckTxType -} - -type ResponseCheckTx struct { - Priority int64 -} -``` - -Please note that because CheckTx handles separate logic related to mempool priotization, its signature is different than DeliverTx and SimulateTx. - -BaseApp holds a reference to a `tx.Handler`: - -```go -type BaseApp struct { - // other fields - txHandler tx.Handler -} -``` - -Baseapp's ABCI `{Check,Deliver}Tx()` and `Simulate()` methods simply call `app.txHandler.{Check,Deliver,Simulate}Tx()` with the relevant arguments. For example, for `DeliverTx`: - -```go -func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { - var abciRes abci.ResponseDeliverTx - ctx := app.getContextForTx(runTxModeDeliver, req.Tx) - res, err := app.txHandler.DeliverTx(ctx, tx.Request{TxBytes: req.Tx}) - if err != nil { - abciRes = sdkerrors.ResponseDeliverTx(err, uint64(res.GasUsed), uint64(res.GasWanted), app.trace) - return abciRes - } - - abciRes, err = convertTxResponseToDeliverTx(res) - if err != nil { - return sdkerrors.ResponseDeliverTx(err, uint64(res.GasUsed), uint64(res.GasWanted), app.trace) - } - - return abciRes -} - -// convertTxResponseToDeliverTx converts a tx.Response into a abci.ResponseDeliverTx. -func convertTxResponseToDeliverTx(txRes tx.Response) (abci.ResponseDeliverTx, error) { - data, err := makeABCIData(txRes) - if err != nil { - return abci.ResponseDeliverTx{}, nil - } - - return abci.ResponseDeliverTx{ - Data: data, - Log: txRes.Log, - Events: txRes.Events, - }, nil -} - -// makeABCIData generates the Data field to be sent to ABCI Check/DeliverTx. -func makeABCIData(txRes tx.Response) ([]byte, error) { - return proto.Marshal(&sdk.TxMsgData{MsgResponses: txRes.MsgResponses}) -} -``` - -The implementations are similar for `BaseApp.CheckTx` and `BaseApp.Simulate`. - -`baseapp.txHandler`'s three methods' implementations can obviously be monolithic functions, but for modularity we propose a middleware composition design, where a middleware is simply a function that takes a `tx.Handler`, and returns another `tx.Handler` wrapped around the previous one. - -### Implementing a Middleware - -In practice, middlewares are created by Go function that takes as arguments some parameters needed for the middleware, and returns a `tx.Middleware`. - -For example, for creating an arbitrary `MyMiddleware`, we can implement: - -```go -// myTxHandler is the tx.Handler of this middleware. Note that it holds a -// reference to the next tx.Handler in the stack. -type myTxHandler struct { - // next is the next tx.Handler in the middleware stack. - next tx.Handler - // some other fields that are relevant to the middleware can be added here -} - -// NewMyMiddleware returns a middleware that does this and that. -func NewMyMiddleware(arg1, arg2) tx.Middleware { - return func (txh tx.Handler) tx.Handler { - return myTxHandler{ - next: txh, - // optionally, set arg1, arg2... if they are needed in the middleware - } - } -} - -// Assert myTxHandler is a tx.Handler. -var _ tx.Handler = myTxHandler{} - -func (h myTxHandler) CheckTx(ctx context.Context, req Request, checkReq RequestcheckTx) (Response, ResponseCheckTx, error) { - // CheckTx specific pre-processing logic - - // run the next middleware - res, checkRes, err := txh.next.CheckTx(ctx, req, checkReq) - - // CheckTx specific post-processing logic - - return res, checkRes, err -} - -func (h myTxHandler) DeliverTx(ctx context.Context, req Request) (Response, error) { - // DeliverTx specific pre-processing logic - - // run the next middleware - res, err := txh.next.DeliverTx(ctx, tx, req) - - // DeliverTx specific post-processing logic - - return res, err -} - -func (h myTxHandler) SimulateTx(ctx context.Context, req Request) (Response, error) { - // SimulateTx specific pre-processing logic - - // run the next middleware - res, err := txh.next.SimulateTx(ctx, tx, req) - - // SimulateTx specific post-processing logic - - return res, err -} -``` - -### Composing Middlewares - -While BaseApp simply holds a reference to a `tx.Handler`, this `tx.Handler` itself is defined using a middleware stack. The Cosmos SDK exposes a base (i.e. innermost) `tx.Handler` called `RunMsgsTxHandler`, which executes messages. - -Then, the app developer can compose multiple middlewares on top on the base `tx.Handler`. Each middleware can run pre-and-post-processing logic around its next middleware, as described in the section above. Conceptually, as an example, given the middlewares `A`, `B`, and `C` and the base `tx.Handler` `H` the stack looks like: - -```text -A.pre - B.pre - C.pre - H # The base tx.handler, for example `RunMsgsTxHandler` - C.post - B.post -A.post -``` - -We define a `ComposeMiddlewares` function for composing middlewares. It takes the base handler as first argument, and middlewares in the "outer to inner" order. For the above stack, the final `tx.Handler` is: - -```go -txHandler := middleware.ComposeMiddlewares(H, A, B, C) -``` - -The middleware is set in BaseApp via its `SetTxHandler` setter: - -```go -// simapp/app.go - -txHandler := middleware.ComposeMiddlewares(...) -app.SetTxHandler(txHandler) -``` - -The app developer can define their own middlewares, or use the Cosmos SDK's pre-defined middlewares from `middleware.NewDefaultTxHandler()`. - -### Middlewares Maintained by the Cosmos SDK - -While the app developer can define and compose the middlewares of their choice, the Cosmos SDK provides a set of middlewares that caters for the ecosystem's most common use cases. These middlewares are: - -| Middleware | Description | -| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| RunMsgsTxHandler | This is the base `tx.Handler`. It replaces the old baseapp's `runMsgs`, and executes a transaction's `Msg`s. | -| TxDecoderMiddleware | This middleware takes in transaction raw bytes, and decodes them into a `sdk.Tx`. It replaces the `baseapp.txDecoder` field, so that BaseApp stays as thin as possible. Since most middlewares read the contents of the `sdk.Tx`, the TxDecoderMiddleware should be run first in the middleware stack. | -| {Antehandlers} | Each antehandler is converted to its own middleware. These middlewares perform signature verification, fee deductions and other validations on the incoming transaction. | -| IndexEventsTxMiddleware | This is a simple middleware that chooses which events to index in Tendermint. Replaces `baseapp.indexEvents` (which unfortunately still exists in baseapp too, because it's used to index Begin/EndBlock events) | -| RecoveryTxMiddleware | This index recovers from panics. It replaces baseapp.runTx's panic recovery described in [ADR-022](./adr-022-custom-panic-handling.md). | -| GasTxMiddleware | This replaces the [`Setup`](https://github.com/cosmos/cosmos-sdk/blob/v0.43.0/x/auth/ante/setup.go) Antehandler. It sets a GasMeter on sdk.Context. Note that before, GasMeter was set on sdk.Context inside the antehandlers, and there was some mess around the fact that antehandlers had their own panic recovery system so that the GasMeter could be read by baseapp's recovery system. Now, this mess is all removed: one middleware sets GasMeter, another one handles recovery. | - -### Similarities and Differences between Antehandlers and Middlewares - -The middleware-based design builds upon the existing antehandlers design described in [ADR-010](./adr-010-modular-antehandler.md). Even though the final decision of ADR-010 was to go with the "Simple Decorators" approach, the middleware design is actually very similar to the other [Decorator Pattern](./adr-010-modular-antehandler.md#decorator-pattern) proposal, also used in [weave](https://github.com/iov-one/weave). - -#### Similarities with Antehandlers - -* Designed as chaining/composing small modular pieces. -* Allow code reuse for `{Check,Deliver}Tx` and for `Simulate`. -* Set up in `app.go`, and easily customizable by app developers. -* Order is important. - -#### Differences with Antehandlers - -* The Antehandlers are run before `Msg` execution, whereas middlewares can run before and after. -* The middleware approach uses separate methods for `{Check,Deliver,Simulate}Tx`, whereas the antehandlers pass a `simulate bool` flag and uses the `sdkCtx.Is{Check,Recheck}Tx()` flags to determine in which transaction mode we are. -* The middleware design lets each middleware hold a reference to the next middleware, whereas the antehandlers pass a `next` argument in the `AnteHandle` method. -* The middleware design use Go's standard `context.Context`, whereas the antehandlers use `sdk.Context`. - -## Consequences - -### Backwards Compatibility - -Since this refactor removes some logic away from BaseApp and into middlewares, it introduces API-breaking changes for app developers. Most notably, instead of creating an antehandler chain in `app.go`, app developers need to create a middleware stack: - -```diff -- anteHandler, err := ante.NewAnteHandler( -- ante.HandlerOptions{ -- AccountKeeper: app.AccountKeeper, -- BankKeeper: app.BankKeeper, -- SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), -- FeegrantKeeper: app.FeeGrantKeeper, -- SigGasConsumer: ante.DefaultSigVerificationGasConsumer, -- }, --) -+txHandler, err := authmiddleware.NewDefaultTxHandler(authmiddleware.TxHandlerOptions{ -+ Debug: app.Trace(), -+ IndexEvents: indexEvents, -+ LegacyRouter: app.legacyRouter, -+ MsgServiceRouter: app.msgSvcRouter, -+ LegacyAnteHandler: anteHandler, -+ TxDecoder: encodingConfig.TxConfig.TxDecoder, -+}) -if err != nil { - panic(err) -} -- app.SetAnteHandler(anteHandler) -+ app.SetTxHandler(txHandler) -``` - -Other more minor API breaking changes will also be provided in the CHANGELOG. As usual, the Cosmos SDK will provide a release migration document for app developers. - -This ADR does not introduce any state-machine-, client- or CLI-breaking changes. - -### Positive - -* Allow custom logic to be run before an after `Msg` execution. This enables the [tips](https://github.com/cosmos/cosmos-sdk/issues/9406) and [gas refund](https://github.com/cosmos/cosmos-sdk/issues/2150) uses cases, and possibly other ones. -* Make BaseApp more lightweight, and defer complex logic to small modular components. -* Separate paths for `{Check,Deliver,Simulate}Tx` with different returns types. This allows for improved readability (replace `if sdkCtx.IsRecheckTx() && !simulate {...}` with separate methods) and more flexibility (e.g. returning a `priority` in `ResponseCheckTx`). - -### Negative - -* It is hard to understand at first glance the state updates that would occur after a middleware runs given the `sdk.Context` and `tx`. A middleware can have an arbitrary number of nested middleware being called within its function body, each possibly doing some pre- and post-processing before calling the next middleware on the chain. Thus to understand what a middleware is doing, one must also understand what every other middleware further along the chain is also doing, and the order of middlewares matters. This can get quite complicated to understand. -* API-breaking changes for app developers. - -### Neutral - -No neutral consequences. - -## Further Discussions - -* [#9934](https://github.com/cosmos/cosmos-sdk/discussions/9934) Decomposing BaseApp's other ABCI methods into middlewares. -* Replace `sdk.Tx` interface with the concrete protobuf Tx type in the `tx.Handler` methods signature. - -## Test Cases - -We update the existing baseapp and antehandlers tests to use the new middleware API, but keep the same test cases and logic, to avoid introducing regressions. Existing CLI tests will also be left untouched. - -For new middlewares, we introduce unit tests. Since middlewares are purposefully small, unit tests suit well. - -## References - -* Initial discussion: https://github.com/cosmos/cosmos-sdk/issues/9585 -* Implementation: [#9920 BaseApp refactor](https://github.com/cosmos/cosmos-sdk/pull/9920) and [#10028 Antehandlers migration](https://github.com/cosmos/cosmos-sdk/pull/10028) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-046-module-params.md b/versioned_docs/version-0.46/integrate/architecture/adr-046-module-params.md deleted file mode 100644 index 6c068e4e0..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-046-module-params.md +++ /dev/null @@ -1,184 +0,0 @@ -# ADR 046: Module Params - -## Changelog - -* Sep 22, 2021: Initial Draft - -## Status - -Proposed - -## Abstract - -This ADR describes an alternative approach to how Cosmos SDK modules use, interact, -and store their respective parameters. - -## Context - -Currently, in the Cosmos SDK, modules that require the use of parameters use the -`x/params` module. The `x/params` works by having modules define parameters, -typically via a simple `Params` structure, and registering that structure in -the `x/params` module via a unique `Subspace` that belongs to the respective -registering module. The registering module then has unique access to its respective -`Subspace`. Through this `Subspace`, the module can get and set its `Params` -structure. - -In addition, the Cosmos SDK's `x/gov` module has direct support for changing -parameters on-chain via a `ParamChangeProposal` governance proposal type, where -stakeholders can vote on suggested parameter changes. - -There are various tradeoffs to using the `x/params` module to manage individual -module parameters. Namely, managing parameters essentially comes for "free" in -that developers only need to define the `Params` struct, the `Subspace`, and the -various auxiliary functions, e.g. `ParamSetPairs`, on the `Params` type. However, -there are some notable drawbacks. These drawbacks include the fact that parameters -are serialized in state via JSON which is extremely slow. In addition, parameter -changes via `ParamChangeProposal` governance proposals have no way of reading from -or writing to state. In other words, it is currently not possible to have any -state transitions in the application during an attempt to change param(s). - -## Decision - -We will build off of the alignment of `x/gov` and `x/authz` work per -[#9810](https://github.com/cosmos/cosmos-sdk/pull/9810). Namely, module developers -will create one or more unique parameter data structures that must be serialized -to state. The Param data structures must implement `sdk.Msg` interface with respective -Protobuf Msg service method which will validate and update the parameters with all -necessary changes. The `x/gov` module via the work done in -[#9810](https://github.com/cosmos/cosmos-sdk/pull/9810), will dispatch Param -messages, which will be handled by Protobuf Msg services. - -Note, it is up to developers to decide how to structure their parameters and -the respective `sdk.Msg` messages. Consider the parameters currently defined in -`x/auth` using the `x/params` module for parameter management: - -```protobuf -message Params { - uint64 max_memo_characters = 1; - uint64 tx_sig_limit = 2; - uint64 tx_size_cost_per_byte = 3; - uint64 sig_verify_cost_ed25519 = 4; - uint64 sig_verify_cost_secp256k1 = 5; -} -``` - -Developers can choose to either create a unique data structure for every field in -`Params` or they can create a single `Params` structure as outlined above in the -case of `x/auth`. - -In the former, `x/params`, approach, a `sdk.Msg` would need to be created for every single -field along with a handler. This can become burdensome if there are a lot of -parameter fields. In the latter case, there is only a single data structure and -thus only a single message handler, however, the message handler might have to be -more sophisticated in that it might need to understand what parameters are being -changed vs what parameters are untouched. - -Params change proposals are made using the `x/gov` module. Execution is done through -`x/authz` authorization to the root `x/gov` module's account. - -Continuing to use `x/auth`, we demonstrate a more complete example: - -```go -type Params struct { - MaxMemoCharacters uint64 - TxSigLimit uint64 - TxSizeCostPerByte uint64 - SigVerifyCostED25519 uint64 - SigVerifyCostSecp256k1 uint64 -} - -type MsgUpdateParams struct { - MaxMemoCharacters uint64 - TxSigLimit uint64 - TxSizeCostPerByte uint64 - SigVerifyCostED25519 uint64 - SigVerifyCostSecp256k1 uint64 -} - -type MsgUpdateParamsResponse struct {} - -func (ms msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - // verification logic... - - // persist params - params := ParamsFromMsg(msg) - ms.SaveParams(ctx, params) - - return &types.MsgUpdateParamsResponse{}, nil -} - -func ParamsFromMsg(msg *types.MsgUpdateParams) Params { - // ... -} -``` - -A gRPC `Service` query should also be provided, for example: - -```protobuf -service Query { - // ... - - rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { - option (google.api.http).get = "/cosmos//v1beta1/params"; - } -} - -message QueryParamsResponse { - Params params = 1 [(gogoproto.nullable) = false]; -} -``` - -## Consequences - -As a result of implementing the module parameter methodology, we gain the ability -for module parameter changes to be stateful and extensible to fit nearly every -application's use case. We will be able to emit events (and trigger hooks registered -to that events using the work proposed in [even hooks](https://github.com/cosmos/cosmos-sdk/discussions/9656)), -call other Msg service methods or perform migration. -In addition, there will be significant gains in performance when it comes to reading -and writing parameters from and to state, especially if a specific set of parameters -are read on a consistent basis. - -However, this methodology will require developers to implement more types and -Msg service metohds which can become burdensome if many parameters exist. In addition, -developers are required to implement persistance logics of module parameters. -However, this should be trivial. - -### Backwards Compatibility - -The new method for working with module parameters is naturally not backwards -compatible with the existing `x/params` module. However, the `x/params` will -remain in the Cosmos SDK and will be marked as deprecated with no additional -functionality being added apart from potential bug fixes. Note, the `x/params` -module may be removed entirely in a future release. - -### Positive - -* Module parameters are serialized more efficiently -* Modules are able to react on parameters changes and perform additional actions. -* Special events can be emitted, allowing hooks to be triggered. - -### Negative - -* Module parameters becomes slightly more burdensome for module developers: - * Modules are now responsible for persisting and retrieving parameter state - * Modules are now required to have unique message handlers to handle parameter - changes per unique parameter data structure. - -### Neutral - -* Requires [#9810](https://github.com/cosmos/cosmos-sdk/pull/9810) to be reviewed - and merged. - - - -## References - -* https://github.com/cosmos/cosmos-sdk/pull/9810 -* https://github.com/cosmos/cosmos-sdk/issues/9438 -* https://github.com/cosmos/cosmos-sdk/discussions/9913 diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-047-extend-upgrade-plan.md b/versioned_docs/version-0.46/integrate/architecture/adr-047-extend-upgrade-plan.md deleted file mode 100644 index 0df030062..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-047-extend-upgrade-plan.md +++ /dev/null @@ -1,245 +0,0 @@ -# ADR 047: Extend Upgrade Plan - -## Changelog - -- Nov, 23, 2021: Initial Draft - -## Status - -PROPOSED Not Implemented - -## Abstract - -This ADR expands the existing x/upgrade `Plan` proto message to include new fields for defining pre-run and post-run processes within upgrade tooling. -It also defines a structure for providing downloadable artifacts involved in an upgrade. - -## Context - -The `upgrade` module in conjunction with Cosmovisor are designed to facilitate and automate a blockchain's transition from one version to another. - -Users submit a software upgrade governance proposal containing an upgrade `Plan`. -The [Plan](https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/proto/cosmos/upgrade/v1beta1/upgrade.proto#L12) currently contains the following fields: -- `name`: A short string identifying the new version. -- `height`: The chain height at which the upgrade is to be performed. -- `info`: A string containing information about the upgrade. - -The `info` string can be anything. -However, Cosmovisor will try to use the `info` field to automatically download a new version of the blockchain executable. -For the auto-download to work, Cosmovisor expects it to be either a stringified JSON object (with a specific structure defined through documentation), or a URL that will return such JSON. -The JSON object identifies URLs used to download the new blockchain executable for different platforms (OS and Architecture, e.g. "linux/amd64"). -Such a URL can either return the executable file directly or can return an archive containing the executable and possibly other assets. - -If the URL returns an archive, it is decompressed into `{DAEMON_HOME}/cosmovisor/{upgrade name}`. -Then, if `{DAEMON_HOME}/cosmovisor/{upgrade name}/bin/{DAEMON_NAME}` does not exist, but `{DAEMON_HOME}/cosmovisor/{upgrade name}/{DAEMON_NAME}` does, the latter is copied to the former. -If the URL returns something other than an archive, it is downloaded to `{DAEMON_HOME}/cosmovisor/{upgrade name}/bin/{DAEMON_NAME}`. - -If an upgrade height is reached and the new version of the executable version isn't available, Cosmovisor will stop running. - -Both `DAEMON_HOME` and `DAEMON_NAME` are [environment variables used to configure Cosmovisor](https://github.com/cosmos/cosmos-sdk/blob/cosmovisor/v1.0.0/cosmovisor/README.md#command-line-arguments-and-environment-variables). - -Currently, there is no mechanism that makes Cosmovisor run a command after the upgraded chain has been restarted. - -The current upgrade process has this timeline: -1. An upgrade governance proposal is submitted and approved. -1. The upgrade height is reached. -1. The `x/upgrade` module writes the `upgrade_info.json` file. -1. The chain halts. -1. Cosmovisor backs up the data directory (if set up to do so). -1. Cosmovisor downloads the new executable (if not already in place). -1. Cosmovisor executes the `${DAEMON_NAME} pre-upgrade`. -1. Cosmovisor restarts the app using the new version and same args originally provided. - -## Decision - -### Protobuf Updates - -We will update the `x/upgrade.Plan` message for providing upgrade instructions. -The upgrade instructions will contain a list of artifacts available for each platform. -It allows for the definition of a pre-run and post-run commands. -These commands are not consensus guaranteed; they will be executed by Cosmosvisor (or other) during its upgrade handling. - -```protobuf -message Plan { - // ... (existing fields) - - UpgradeInstructions instructions = 6; -} -``` - -The new `UpgradeInstructions instructions` field MUST be optional. - -```protobuf -message UpgradeInstructions { - string pre_run = 1; - string post_run = 2; - repeated Artifact artifacts = 3; - string description = 4; -} -``` - -All fields in the `UpgradeInstructions` are optional. -- `pre_run` is a command to run prior to the upgraded chain restarting. - If defined, it will be executed after halting and downloading the new artifact but before restarting the upgraded chain. - The working directory this command runs from MUST be `{DAEMON_HOME}/cosmovisor/{upgrade name}`. - This command MUST behave the same as the current [pre-upgrade](https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/docs/migrations/pre-upgrade.md) command. - It does not take in any command-line arguments and is expected to terminate with the following exit codes: - - | Exit status code | How it is handled in Cosmosvisor | - |------------------|---------------------------------------------------------------------------------------------------------------------| - | `0` | Assumes `pre-upgrade` command executed successfully and continues the upgrade. | - | `1` | Default exit code when `pre-upgrade` command has not been implemented. | - | `30` | `pre-upgrade` command was executed but failed. This fails the entire upgrade. | - | `31` | `pre-upgrade` command was executed but failed. But the command is retried until exit code `1` or `30` are returned. | - If defined, then the app supervisors (e.g. Cosmovisor) MUST NOT run `app pre-run`. -- `post_run` is a command to run after the upgraded chain has been started. If defined, this command MUST be only executed at most once by an upgrading node. - The output and exit code SHOULD be logged but SHOULD NOT affect the running of the upgraded chain. - The working directory this command runs from MUST be `{DAEMON_HOME}/cosmovisor/{upgrade name}`. -- `artifacts` define items to be downloaded. - It SHOULD have only one entry per platform. -- `description` contains human-readable information about the upgrade and might contain references to external resources. - It SHOULD NOT be used for structured processing information. - -```protobuf -message Artifact { - string platform = 1; - string url = 2; - string checksum = 3; - string checksum_algo = 4; -} -``` - -- `platform` is a required string that SHOULD be in the format `{OS}/{CPU}`, e.g. `"linux/amd64"`. - The string `"any"` SHOULD also be allowed. - An `Artifact` with a `platform` of `"any"` SHOULD be used as a fallback when a specific `{OS}/{CPU}` entry is not found. - That is, if an `Artifact` exists with a `platform` that matches the system's OS and CPU, that should be used; - otherwise, if an `Artifact` exists with a `platform` of `any`, that should be used; - otherwise no artifact should be downloaded. -- `url` is a required URL string that MUST conform to [RFC 1738: Uniform Resource Locators](https://www.ietf.org/rfc/rfc1738.txt). - A request to this `url` MUST return either an executable file or an archive containing either `bin/{DAEMON_NAME}` or `{DAEMON_NAME}`. - The URL should not contain checksum - it should be specified by the `checksum` attribute. -- `checksum` is a checksum of the expected result of a request to the `url`. - It is not required, but is recommended. - If provided, it MUST be a hex encoded checksum string. - Tools utilizing these `UpgradeInstructions` MUST fail if a `checksum` is provided but is different from the checksum of the result returned by the `url`. -- `checksum_algo` is a string identify the algorithm used to generate the `checksum`. - Recommended algorithms: `sha256`, `sha512`. - Algorithms also supported (but not recommended): `sha1`, `md5`. - If a `checksum` is provided, a `checksum_algo` MUST also be provided. - -A `url` is not required to contain a `checksum` query parameter. -If the `url` does contain a `checksum` query parameter, the `checksum` and `checksum_algo` fields MUST also be populated, and their values MUST match the value of the query parameter. -For example, if the `url` is `"https://example.com?checksum=md5:d41d8cd98f00b204e9800998ecf8427e"`, then the `checksum` field must be `"d41d8cd98f00b204e9800998ecf8427e"` and the `checksum_algo` field must be `"md5"`. - -### Upgrade Module Updates - -If an upgrade `Plan` does not use the new `UpgradeInstructions` field, existing functionality will be maintained. -The parsing of the `info` field as either a URL or `binaries` JSON will be deprecated. -During validation, if the `info` field is used as such, a warning will be issued, but not an error. - -We will update the creation of the `upgrade-info.json` file to include the `UpgradeInstructions`. - -We will update the optional validation available via CLI to account for the new `Plan` structure. -We will add the following validation: -1. If `UpgradeInstructions` are provided: - 1. There MUST be at least one entry in `artifacts`. - 1. All of the `artifacts` MUST have a unique `platform`. - 1. For each `Artifact`, if the `url` contains a `checksum` query parameter: - 1. The `checksum` query parameter value MUST be in the format of `{checksum_algo}:{checksum}`. - 1. The `{checksum}` from the query parameter MUST equal the `checksum` provided in the `Artifact`. - 1. The `{checksum_algo}` from the query parameter MUST equal the `checksum_algo` provided in the `Artifact`. -1. The following validation is currently done using the `info` field. We will apply similar validation to the `UpgradeInstructions`. - For each `Artifact`: - 1. The `platform` MUST have the format `{OS}/{CPU}` or be `"any"`. - 1. The `url` field MUST NOT be empty. - 1. The `url` field MUST be a proper URL. - 1. A `checksum` MUST be provided either in the `checksum` field or as a query parameter in the `url`. - 1. If the `checksum` field has a value and the `url` also has a `checksum` query parameter, the two values MUST be equal. - 1. The `url` MUST return either a file or an archive containing either `bin/{DAEMON_NAME}` or `{DAEMON_NAME}`. - 1. If a `checksum` is provided (in the field or as a query param), the checksum of the result of the `url` MUST equal the provided checksum. - -Downloading of an `Artifact` will happen the same way that URLs from `info` are currently downloaded. - -### Cosmovisor Updates - -If the `upgrade-info.json` file does not contain any `UpgradeInstructions`, existing functionality will be maintained. - -We will update Cosmovisor to look for and handle the new `UpgradeInstructions` in `upgrade-info.json`. -If the `UpgradeInstructions` are provided, we will do the following: -1. The `info` field will be ignored. -1. The `artifacts` field will be used to identify the artifact to download based on the `platform` that Cosmovisor is running in. -1. If a `checksum` is provided (either in the field or as a query param in the `url`), and the downloaded artifact has a different checksum, the upgrade process will be interrupted and Cosmovisor will exit with an error. -1. If a `pre_run` command is defined, it will be executed at the same point in the process where the `app pre-upgrade` command would have been executed. - It will be executed using the same environment as other commands run by Cosmovisor. -1. If a `post_run` command is defined, it will be executed after executing the command that restarts the chain. - It will be executed in a background process using the same environment as the other commands. - Any output generated by the command will be logged. - Once complete, the exit code will be logged. - -We will deprecate the use of the `info` field for anything other than human readable information. -A warning will be logged if the `info` field is used to define the assets (either by URL or JSON). - -The new upgrade timeline is very similar to the current one. Changes are in bold: -1. An upgrade governance proposal is submitted and approved. -1. The upgrade height is reached. -1. The `x/upgrade` module writes the `upgrade_info.json` file **(now possibly with `UpgradeInstructions`)**. -1. The chain halts. -1. Cosmovisor backs up the data directory (if set up to do so). -1. Cosmovisor downloads the new executable (if not already in place). -1. Cosmovisor executes **the `pre_run` command if provided**, or else the `${DAEMON_NAME} pre-upgrade` command. -1. Cosmovisor restarts the app using the new version and same args originally provided. -1. **Cosmovisor immediately runs the `post_run` command in a detached process.** - -## Consequences - -### Backwards Compatibility - -Since the only change to existing definitions is the addition of the `instructions` field to the `Plan` message, and that field is optional, there are no backwards incompatibilities with respects to the proto messages. -Additionally, current behavior will be maintained when no `UpgradeInstructions` are provided, so there are no backwards incompatibilities with respects to either the upgrade module or Cosmovisor. - -### Forwards Compatibility - -In order to utilize the `UpgradeInstructions` as part of a software upgrade, both of the following must be true: -1. The chain must already be using a sufficiently advanced version of the Cosmos SDK. -1. The chain's nodes must be using a sufficiently advanced version of Cosmovisor. - -### Positive - -1. The structure for defining artifacts is clearer since it is now defined in the proto instead of in documentation. -1. Availability of a pre-run command becomes more obvious. -1. A post-run command becomes possible. - -### Negative - -1. The `Plan` message becomes larger. This is negligible because A) the `x/upgrades` module only stores at most one upgrade plan, and B) upgrades are rare enough that the increased gas cost isn't a concern. -1. There is no option for providing a URL that will return the `UpgradeInstructions`. -1. The only way to provide multiple assets (executables and other files) for a platform is to use an archive as the platform's artifact. - -### Neutral - -1. Existing functionality of the `info` field is maintained when the `UpgradeInstructions` aren't provided. - -## Further Discussions - -1. [Draft PR #10032 Comment](https://github.com/cosmos/cosmos-sdk/pull/10032/files?authenticity_token=pLtzpnXJJB%2Fif2UWiTp9Td3MvRrBF04DvjSuEjf1azoWdLF%2BSNymVYw9Ic7VkqHgNLhNj6iq9bHQYnVLzMXd4g%3D%3D&file-filters%5B%5D=.go&file-filters%5B%5D=.proto#r698708349): - Consider different names for `UpgradeInstructions instructions` (either the message type or field name). -1. [Draft PR #10032 Comment](https://github.com/cosmos/cosmos-sdk/pull/10032/files?authenticity_token=pLtzpnXJJB%2Fif2UWiTp9Td3MvRrBF04DvjSuEjf1azoWdLF%2BSNymVYw9Ic7VkqHgNLhNj6iq9bHQYnVLzMXd4g%3D%3D&file-filters%5B%5D=.go&file-filters%5B%5D=.proto#r754655072): - 1. Consider putting the `string platform` field inside `UpgradeInstructions` and make `UpgradeInstructions` a repeated field in `Plan`. - 1. Consider using a `oneof` field in the `Plan` which could either be `UpgradeInstructions` or else a URL that should return the `UpgradeInstructions`. - 1. Consider allowing `info` to either be a JSON serialized version of `UpgradeInstructions` or else a URL that returns that. -1. [Draft PR #10032 Comment](https://github.com/cosmos/cosmos-sdk/pull/10032/files?authenticity_token=pLtzpnXJJB%2Fif2UWiTp9Td3MvRrBF04DvjSuEjf1azoWdLF%2BSNymVYw9Ic7VkqHgNLhNj6iq9bHQYnVLzMXd4g%3D%3D&file-filters%5B%5D=.go&file-filters%5B%5D=.proto#r755462876): - Consider not including the `UpgradeInstructions.description` field, using the `info` field for that purpose instead. -1. [Draft PR #10032 Comment](https://github.com/cosmos/cosmos-sdk/pull/10032/files?authenticity_token=pLtzpnXJJB%2Fif2UWiTp9Td3MvRrBF04DvjSuEjf1azoWdLF%2BSNymVYw9Ic7VkqHgNLhNj6iq9bHQYnVLzMXd4g%3D%3D&file-filters%5B%5D=.go&file-filters%5B%5D=.proto#r754643691): - Consider allowing multiple artifacts to be downloaded for any given `platform` by adding a `name` field to the `Artifact` message. -1. [PR #10502 Comment](https://github.com/cosmos/cosmos-sdk/pull/10602#discussion_r781438288) - Allow the new `UpgradeInstructions` to be provided via URL. -1. [PR #10502 Comment](https://github.com/cosmos/cosmos-sdk/pull/10602#discussion_r781438288) - Allow definition of a `signer` for assets (as an alternative to using a `checksum`). - -## References - -- [Current upgrade.proto](https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/proto/cosmos/upgrade/v1beta1/upgrade.proto) -- [Upgrade Module README](https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/x/upgrade/spec/README.md) -- [Cosmovisor README](https://github.com/cosmos/cosmos-sdk/blob/cosmovisor/v1.0.0/cosmovisor/README.md) -- [Pre-upgrade README](https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/docs/migrations/pre-upgrade.md) -- [Draft/POC PR #10032](https://github.com/cosmos/cosmos-sdk/pull/10032) -- [RFC 1738: Uniform Resource Locators](https://www.ietf.org/rfc/rfc1738.txt) diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-048-consensus-fees.md b/versioned_docs/version-0.46/integrate/architecture/adr-048-consensus-fees.md deleted file mode 100644 index 4ce4c8137..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-048-consensus-fees.md +++ /dev/null @@ -1,203 +0,0 @@ -# ADR 048: Multi Tire Gas Price System - -## Changelog - -- Dec 1, 2021: Initial Draft - -## Status - -Rejected - -## Abstract - -This ADR describes a flexible mechanism to maintain a consensus level gas prices, in which one can choose a multi-tier gas price system or EIP-1559 like one through configuration. - -## Context - -Currently, each validator configures it's own `minimal-gas-prices` in `app.yaml`. But setting a proper minimal gas price is critical to protect network from dos attack, and it's hard for all the validators to pick a sensible value, so we propose to maintain a gas price in consensus level. - -Since tendermint 0.35 has supported mempool prioritization, we can take advantage of that to implement more sophisticated gas fee system. - -## Multi-Tier Price System - -We propose a multi-tier price system on consensus to provide maximum flexibility: - -- Tier 1: a constant gas price, which could only be modified occasionally through governance proposal. -- Tier 2: a dynamic gas price which is adjusted according to previous block load. -- Tier 3: a dynamic gas price which is adjusted according to previous block load at a higher speed. - -The gas price of higher tier should bigger than the lower tier. - -The transaction fees are charged with the exact gas price calculated on consensus. - -The parameter schema is like this: - -```protobuf -message TierParams { - uint32 priority = 1 // priority in tendermint mempool - Coin initial_gas_price = 2 // - uint32 parent_gas_target = 3 // the target saturation of block - uint32 change_denominator = 4 // decides the change speed - Coin min_gas_price = 5 // optional lower bound of the price adjustment - Coin max_gas_price = 6 // optional upper bound of the price adjustment -} - -message Params { - repeated TierParams tiers = 1; -} -``` - -### Extension Options - -We need to allow user to specify the tier of service for the transaction, to support it in an extensible way, we add an extension option in `AuthInfo`: - -```protobuf -message ExtensionOptionsTieredTx { - uint32 fee_tier = 1 -} -``` - -The value of `fee_tier` is just the index to the `tiers` parameter list. - -We also change the semantic of existing `fee` field of `Tx`, instead of charging user the exact `fee` amount, we treat it as a fee cap, while the actual amount of fee charged is decided dynamically. If the `fee` is smaller than dynamic one, the transaction won't be included in current block and ideally should stay in the mempool until the consensus gas price drop. The mempool can eventually prune old transactions. - -### Tx Prioritization - -Transactions are prioritized based on the tier, the higher the tier, the higher the priority. - -Within the same tier, follow the default Tendermint order (currently FIFO). Be aware of that the mempool tx ordering logic is not part of consensus and can be modified by malicious validator. - -This mechanism can be easily composed with prioritization mechanisms: -* we can add extra tiers out of a user control: - * Example 1: user can set tier 0, 10 or 20, but the protocol will create tiers 0, 1, 2 ... 29. For example IBC transactions will go to tier `user_tier + 5`: if user selected tier 1, then the transaction will go to tier 15. - * Example 2: we can reserve tier 4, 5, ... only for special transaction types. For example, tier 5 is reserved for evidence tx. So if submits a bank.Send transaction and set tier 5, it will be delegated to tier 3 (the max tier level available for any transaction). - * Example 3: we can enforce that all transactions of a sepecific type will go to specific tier. For example, tier 100 will be reserved for evidence transactions and all evidence transactions will always go to that tier. - -### `min-gas-prices` - -Deprecate the current per-validator `min-gas-prices` configuration, since it would confusing for it to work together with the consensus gas price. - -### Adjust For Block Load - -For tier 2 and tier 3 transactions, the gas price is adjusted according to previous block load, the logic could be similar to EIP-1559: - -```python -def adjust_gas_price(gas_price, parent_gas_used, tier): - if parent_gas_used == tier.parent_gas_target: - return gas_price - elif parent_gas_used > tier.parent_gas_target: - gas_used_delta = parent_gas_used - tier.parent_gas_target - gas_price_delta = max(gas_price * gas_used_delta // tier.parent_gas_target // tier.change_speed, 1) - return gas_price + gas_price_delta - else: - gas_used_delta = parent_gas_target - parent_gas_used - gas_price_delta = gas_price * gas_used_delta // parent_gas_target // tier.change_speed - return gas_price - gas_price_delta -``` - -### Block Segment Reservation - -Ideally we should reserve block segments for each tier, so the lower tiered transactions won't be completely squeezed out by higher tier transactions, which will force user to use higher tier, and the system degraded to a single tier. - -We need help from tendermint to implement this. - -## Implementation - -We can make each tier's gas price strategy fully configurable in protocol parameters, while providing a sensible default one. - -Pseudocode in python-like syntax: - -```python -interface TieredTx: - def tier(self) -> int: - pass - -def tx_tier(tx): - if isinstance(tx, TieredTx): - return tx.tier() - else: - # default tier for custom transactions - return 0 - # NOTE: we can add more rules here per "Tx Prioritization" section - -class TierParams: - 'gas price strategy parameters of one tier' - priority: int # priority in tendermint mempool - initial_gas_price: Coin - parent_gas_target: int - change_speed: Decimal # 0 means don't adjust for block load. - -class Params: - 'protocol parameters' - tiers: List[TierParams] - -class State: - 'consensus state' - # total gas used in last block, None when it's the first block - parent_gas_used: Optional[int] - # gas prices of last block for all tiers - gas_prices: List[Coin] - -def begin_block(): - 'Adjust gas prices' - for i, tier in enumerate(Params.tiers): - if State.parent_gas_used is None: - # initialized gas price for the first block - State.gas_prices[i] = tier.initial_gas_price - else: - # adjust gas price according to gas used in previous block - State.gas_prices[i] = adjust_gas_price(State.gas_prices[i], State.parent_gas_used, tier) - -def mempoolFeeTxHandler_checkTx(ctx, tx): - # the minimal-gas-price configured by validator, zero in deliver_tx context - validator_price = ctx.MinGasPrice() - consensus_price = State.gas_prices[tx_tier(tx)] - min_price = max(validator_price, consensus_price) - - # zero means infinity for gas price cap - if tx.gas_price() > 0 and tx.gas_price() < min_price: - return 'insufficient fees' - return next_CheckTx(ctx, tx) - -def txPriorityHandler_checkTx(ctx, tx): - res, err := next_CheckTx(ctx, tx) - # pass priority to tendermint - res.Priority = Params.tiers[tx_tier(tx)].priority - return res, err - -def end_block(): - 'Update block gas used' - State.parent_gas_used = block_gas_meter.consumed() -``` - -### Dos attack protection - -To fully saturate the blocks and prevent other transactions from executing, attacker need to use transactions of highest tier, the cost would be significantly higher than the default tier. - -If attacker spam with lower tier transactions, user can mitigate by sending higher tier transactions. - -## Consequences - -### Backwards Compatibility - -- New protocol parameters. -- New consensus states. -- New/changed fields in transaction body. - -### Positive - -- The default tier keeps the same predictable gas price experience for client. -- The higher tier's gas price can adapt to block load. -- No priority conflict with custom priority based on transaction types, since this proposal only occupy three priority levels. -- Possibility to compose different priority rules with tiers - -### Negative - -- Wallets & tools need to update to support the new `tier` parameter, and semantic of `fee` field is changed. - -### Neutral - -## References - -- https://eips.ethereum.org/EIPS/eip-1559 -- https://iohk.io/en/blog/posts/2021/11/26/network-traffic-and-tiered-pricing/ diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-049-state-sync-hooks.md b/versioned_docs/version-0.46/integrate/architecture/adr-049-state-sync-hooks.md deleted file mode 100644 index e1616c226..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-049-state-sync-hooks.md +++ /dev/null @@ -1,160 +0,0 @@ -# ADR 049: State Sync Hooks - -## Changelog - -- Jan 19, 2022: Initial Draft - -## Status - -Draft, Under Implementation - -## Abstract - -This ADR outlines a hooks-based mechanism for application modules to provide additional state (outside of the IAVL tree) to be used -during state sync. - -## Context - -New clients use state-sync to download snapshots of module state from peers. Currently, the snapshot consists of a -stream of `SnapshotStoreItem` and `SnapshotIAVLItem`, which means that application modules that define their state outside of the IAVL -tree cannot include their state as part of the state-sync process. - -Note, Even though the module state data is outside of the tree, for determinism we require that the hash of the external data should -be posted in the IAVL tree. - -## Decision - -A simple proposal based on our existing implementation is that, we can add two new message types: `SnapshotExtensionMeta` -and `SnapshotExtensionPayload`, and they are appended to the existing multi-store stream with `SnapshotExtensionMeta` -acting as a delimiter between extensions. As the chunk hashes should be able to ensure data integrity, we don't need -a delimiter to mark the end of the snapshot stream. - -Besides, we provide `Snapshotter` and `ExtensionSnapshotter` interface for modules to implement snapshotters, which will handle both taking -snapshot and the restoration. Each module could have mutiple snapshotters, and for modules with additional state, they should -implement `ExtensionSnapshotter` as extension snapshotters. When setting up the application, the snapshot `Manager` should call -`RegisterExtensions([]ExtensionSnapshotter…)` to register all the extension snapshotters. - -```proto -// SnapshotItem is an item contained in a rootmulti.Store snapshot. -// On top of the exsiting SnapshotStoreItem and SnapshotIAVLItem, we add two new options for the item. -message SnapshotItem { - // item is the specific type of snapshot item. - oneof item { - SnapshotStoreItem store = 1; - SnapshotIAVLItem iavl = 2 [(gogoproto.customname) = "IAVL"]; - SnapshotExtensionMeta extension = 3; - SnapshotExtensionPayload extension_payload = 4; - } -} - -// SnapshotExtensionMeta contains metadata about an external snapshotter. -// One module may need multiple snapshotters, so each module may have multiple SnapshotExtensionMeta. -message SnapshotExtensionMeta { - // the name of the ExtensionSnapshotter, and it is registered to snapshotter manager when setting up the application - // name should be unique for each ExtensionSnapshotter as we need to alphabetically order their snapshots to get - // deterministic snapshot stream. - string name = 1; - // this is used by each ExtensionSnapshotter to decide the format of payloads included in SnapshotExtensionPayload message - // it is used within the snapshotter/namespace, not global one for all modules - uint32 format = 2; -} - -// SnapshotExtensionPayload contains payloads of an external snapshotter. -message SnapshotExtensionPayload { - bytes payload = 1; -} -``` - -When we create a snapshot stream, the `multistore` snapshot is always placed at the beginning of the binary stream, and other extension snapshots are alphabetically ordered by the name of the corresponding `ExtensionSnapshotter`. - -The snapshot stream would look like as follows: - -```go -// multi-store snapshot -{SnapshotStoreItem | SnapshotIAVLItem, ...} -// extension1 snapshot -SnapshotExtensionMeta -{SnapshotExtensionPayload, ...} -// extension2 snapshot -SnapshotExtensionMeta -{SnapshotExtensionPayload, ...} -``` - -We add an `extensions` field to snapshot `Manager` for extension snapshotters. The `multistore` snapshotter is a special one and it doesn't need a name because it is always placed at the beginning of the binary stream. - -```go -type Manager struct { - store *Store - multistore types.Snapshotter - extensions map[string]types.ExtensionSnapshotter - mtx sync.Mutex - operation operation - chRestore chan<- io.ReadCloser - chRestoreDone <-chan restoreDone - restoreChunkHashes [][]byte - restoreChunkIndex uint32 -} -``` - -For extension snapshotters that implement the `ExtensionSnapshotter` interface, their names should be registered to the snapshot `Manager` by -calling `RegisterExtensions` when setting up the application. The snapshotters will handle both taking snapshot and restoration. - -```go -// RegisterExtensions register extension snapshotters to manager -func (m *Manager) RegisterExtensions(extensions ...types.ExtensionSnapshotter) error -``` - -On top of the existing `Snapshotter` interface for the `multistore`, we add `ExtensionSnapshotter` interface for the extension snapshotters. Three more function signatures: `SnapshotFormat()`, `SupportedFormats()` and `SnapshotName()` are added to `ExtensionSnapshotter`. - -```go -// ExtensionSnapshotter is an extension Snapshotter that is appended to the snapshot stream. -// ExtensionSnapshotter has an unique name and manages it's own internal formats. -type ExtensionSnapshotter interface { - Snapshotter - - // SnapshotName returns the name of snapshotter, it should be unique in the manager. - SnapshotName() string - - // SnapshotFormat returns the default format used to take a snapshot. - SnapshotFormat() uint32 - - // SupportedFormats returns a list of formats it can restore from. - SupportedFormats() []uint32 -} -``` - -## Consequences - -As a result of this implementation, we are able to create snapshots of binary chunk stream for the state that we maintain outside of the IAVL Tree, CosmWasm blobs for example. And new clients are able to fetch sanpshots of state for all modules that have implemented the corresponding interface from peer nodes. - - -### Backwards Compatibility - -This ADR introduces new proto message types, add an `extensions` field in snapshot `Manager`, and add new `ExtensionSnapshotter` interface, so this is not backwards compatible if we have extensions. - -But for applications that does not have the state data outside of the IAVL tree for any module, the snapshot stream is backwards-compatible. - -### Positive - -- State maintained outside of IAVL tree like CosmWasm blobs can create snapshots by implementing extension snapshotters, and being fetched by new clients via state-sync. - -### Negative - -### Neutral - -- All modules that maintain state outside of IAVL tree need to implement `ExtensionSnapshotter` and the snapshot `Manager` need to call `RegisterExtensions` when setting up the application. - -## Further Discussions - -While an ADR is in the DRAFT or PROPOSED stage, this section should contain a summary of issues to be solved in future iterations (usually referencing comments from a pull-request discussion). -Later, this section can optionally list ideas or improvements the author or reviewers found during the analysis of this ADR. - -## Test Cases [optional] - -Test cases for an implementation are mandatory for ADRs that are affecting consensus changes. Other ADRs can choose to include links to test cases if applicable. - -## References - -- https://github.com/cosmos/cosmos-sdk/pull/10961 -- https://github.com/cosmos/cosmos-sdk/issues/7340 -- https://hackmd.io/gJoyev6DSmqqkO667WQlGw diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-053-go-module-refactoring.md b/versioned_docs/version-0.46/integrate/architecture/adr-053-go-module-refactoring.md deleted file mode 100644 index 9093ae9d9..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-053-go-module-refactoring.md +++ /dev/null @@ -1,109 +0,0 @@ -# ADR 053: Go Module Refactoring - -## Changelog - -* 2022-04-27: First Draft - -## Status - -PROPOSED - -## Abstract - -The current SDK is built as a single monolithic go module. This ADR describes -how we refactor the SDK into smaller independently versioned go modules -for ease of maintenance. - -## Context - -Go modules impose certain requirements on software projects with respect to -stable version numbers (anything above 0.x) in that [any API breaking changes -necessitate a major version](https://go.dev/doc/modules/release-workflow#breaking) -increase which technically creates a new go module -(with a v2, v3, etc. suffix). - -[Keeping modules API compatible](https://go.dev/blog/module-compatibility) in -this way requires a fair amount of fair thought and discipline. - -The Cosmos SDK is a fairly large project which originated before go modules -came into existence and has always been under a v0.x release even though -it has been used in production for years now, not because it isn't production -quality software, but rather because the API compatibility guarantees required -by go modules are fairly complex to adhere to with such a large project. -Up to now, it has generally been deemed more important to be able to break the -API if needed rather than require all users update all package import paths -to accommodate breaking changes causing v2, v3, etc. releases. This is in -addition to the other complexities related to protobuf generated code that will -be addressed in a separate ADR. - -Nevertheless, the desire for semantic versioning has been [strong in the -community](https://github.com/cosmos/cosmos-sdk/discussions/10162) and the -single go module release process has made it very hard to -release small changes to isolated features in a timely manner. Release cycles -often exceed six months which means small improvements done in a day or -two get bottle-necked by everything else in the monolithic release cycle. - -## Decision - -To improve the current situation, the SDK is being refactored into multiple -go modules within the current repository. There has been a [fair amount of -debate](https://github.com/cosmos/cosmos-sdk/discussions/10582#discussioncomment-1813377) -as to how to do this, with some developers arguing for larger vs smaller -module scopes. There are pros and cons to both approaches (which will be -discussed below in the [Consequences](#consequences) section), but the -approach being adopted is the following: -* a go module should generally be scoped to a specific coherent set of -functionality (such as math, errors, store, etc.) -* when code is removed from the core SDK and moved to a new module path, every -effort should be made to avoid API breaking changes in the existing code using -aliases and wrapper types (as done in https://github.com/cosmos/cosmos-sdk/pull/10779 -and https://github.com/cosmos/cosmos-sdk/pull/11788) -* new go modules should be moved to a standalone domain (`cosmossdk.io`) before -being tagged as `v1.0.0` to accommodate the possibility that they may be -better served by a standalone repository in the future -* all go modules should follow the guidelines in https://go.dev/blog/module-compatibility -before `v1.0.0` is tagged and should make use of `internal` packages to limit -the exposed API surface -* the new go module's API may deviate from the existing code where there are -clear improvements to be made or to remove legacy dependencies (for instance on -amino or gogo proto), as long the old package attempts -to avoid API breakage with aliases and wrappers -* care should be taken when simply trying to turn an existing package into a -new go module: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository. -In general, it seems safer to just create a new module path (appending v2, v3, etc. -if necessary), rather than trying to make an old package a new module. - -## Consequences - -### Backwards Compatibility - -If the above guidelines are followed to use aliases or wrapper types pointing -in existing APIs that point back to the new go modules, there should be no or -very limited breaking changes to existing APIs. - -### Positive - -* standalone pieces of software will reach `v1.0.0` sooner -* new features to specific functionality will be released sooner - -### Negative - -* there will be more go module versions to update in the SDK itself and -per-project, although most of these will hopefully be indirect - -### Neutral - -## Further Discussions - -Further discussions are occurring in primarily in -https://github.com/cosmos/cosmos-sdk/discussions/10582 and within -the Cosmos SDK Framework Working Group. - -## References - -* https://go.dev/doc/modules/release-workflow -* https://go.dev/blog/module-compatibility -* https://github.com/cosmos/cosmos-sdk/discussions/10162 -* https://github.com/cosmos/cosmos-sdk/discussions/10582 -* https://github.com/cosmos/cosmos-sdk/pull/10779 -* https://github.com/cosmos/cosmos-sdk/pull/11788 \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-055-orm.md b/versioned_docs/version-0.46/integrate/architecture/adr-055-orm.md deleted file mode 100644 index 71a759526..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-055-orm.md +++ /dev/null @@ -1,111 +0,0 @@ -# ADR 055: ORM - -## Changelog - -* 2022-04-27: First draft - -## Status - -ACCEPTED Implemented - -## Abstract - -In order to make it easier for developers to build Cosmos SDK modules and for clients to query, index and verify proofs -against state data, we have implemented an ORM (object-relational mapping) layer for the Cosmos SDK. - -## Context - -Historically modules in the Cosmos SDK have always used the key-value store directly and created various handwritten -functions for managing key format as well as constructing secondary indexes. This consumes a significant amount of -time when building a module and is error-prone. Because key formats are non-standard, sometimes poorly documented, -and subject to change, it is hard for clients to generically index, query and verify merkle proofs against state data. - -The known first instance of an "ORM" in the Cosmos ecosystem was in [weave](https://github.com/iov-one/weave/tree/master/orm). -A later version was built for [regen-ledger](https://github.com/regen-network/regen-ledger/tree/157181f955823149e1825263a317ad8e16096da4/orm) for -use in the group module and later [ported to the SDK](https://github.com/cosmos/cosmos-sdk/tree/35d3312c3be306591fcba39892223f1244c8d108/x/group/internal/orm) -just for that purpose. - -While these earlier designs made it significantly easier to write state machines, they still required a lot of manual -configuration, didn't expose state format directly to clients, and were limited in their support of different types -of index keys, composite keys, and range queries. - -Discussions about the design continued in https://github.com/cosmos/cosmos-sdk/discussions/9156 and more -sophisticated proofs of concept were created in https://github.com/allinbits/cosmos-sdk-poc/tree/master/runtime/orm -and https://github.com/cosmos/cosmos-sdk/pull/10454. - -## Decision - -These prior efforts culminated in the creation of the Cosmos SDK `orm` go module which uses protobuf annotations -for specifying ORM table definitions. This ORM is based on the new `google.golang.org/protobuf/reflect/protoreflect` -API and supports: -* sorted indexes for all simple protobuf types (except `bytes`, `enum`, `float`, `double`) as well as `Timestamp` and `Duration` -* unsorted `bytes` and `enum` indexes -* composite primary and secondary keys -* unique indexes -* auto-incrementing `uint64` primary keys -* complex prefix and range queries -* paginated queries -* complete logical decoding of KV-store data - -Almost all the information needed to decode state directly is specified in .proto files. Each table definition specifies -an ID which is unique per .proto file and each index within a table is unique within that table. Clients then only need -to know the name of a module and the prefix ORM data for a specific .proto file within that module in order to decode -state data directly. This additional information will be exposed directly through app configs which will be explained -in a future ADR related to app wiring. - -The ORM makes optimizations around storage space by not repeating values in the primary key in the key value -when storing primary key records. For example, if the object `{"a":0,"b":1}` has the primary key `a`, it will -be stored in the key value store as `Key: '0', Value: {"b":1}` (with more efficient protobuf binary encoding). -Also, the generated code from https://github.com/cosmos/cosmos-proto does optimizations around the -`google.golang.org/protobuf/reflect/protoreflect` API to improve performance. - -A code generator is included with the ORM which creates type safe wrappers around the ORM's dynamic `Table` -implementation and is the recommended way for modules to use the ORM. - -The ORM tests provide a simplified bank module demonstration which illustrates: -- [ORM proto options](https://github.com/cosmos/cosmos-sdk/blob/0d846ae2f0424b2eb640f6679a703b52d407813d/orm/internal/testpb/bank.proto) -- [Generated Code](https://github.com/cosmos/cosmos-sdk/blob/0d846ae2f0424b2eb640f6679a703b52d407813d/orm/internal/testpb/bank.cosmos_orm.go) -- [Example Usage in a Module Keeper](https://github.com/cosmos/cosmos-sdk/blob/0d846ae2f0424b2eb640f6679a703b52d407813d/orm/model/ormdb/module_test.go) - -## Consequences - -### Backwards Compatibility - -State machine code that adopts the ORM will need migrations as the state layout is generally backwards incompatible. -These state machines will also need to migrate to https://github.com/cosmos/cosmos-proto at least for state data. - -### Positive - -* easier to build modules -* easier to add secondary indexes to state -* possible to write a generic indexer for ORM state -* easier to write clients that do state proofs -* possible to automatically write query layers rather than needing to manually implement gRPC queries - -### Negative - -* worse performance than handwritten keys (for now). See [Further Discussions](#further-discussions) -for potential improvements - -### Neutral - -## Further Discussions - -Further discussions will happen within the Cosmos SDK Framework Working Group. Current planned and ongoing work includes: -* automatically generate client-facing query layer -* client-side query libraries that transparently verify light client proofs -* index ORM data to SQL databases -* improve performance by: - * optimizing existing reflection based code to avoid unnecessary gets when doing deletes & updates of simple tables - * more sophisticated code generation such as making fast path reflection even faster (avoiding `switch` statements), - or even fully generating code that equals handwritten performance - - -## References - -* https://github.com/iov-one/weave/tree/master/orm). -* https://github.com/regen-network/regen-ledger/tree/157181f955823149e1825263a317ad8e16096da4/orm -* https://github.com/cosmos/cosmos-sdk/tree/35d3312c3be306591fcba39892223f1244c8d108/x/group/internal/orm -* https://github.com/cosmos/cosmos-sdk/discussions/9156 -* https://github.com/allinbits/cosmos-sdk-poc/tree/master/runtime/orm -* https://github.com/cosmos/cosmos-sdk/pull/10454 \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/architecture/adr-template.md b/versioned_docs/version-0.46/integrate/architecture/adr-template.md deleted file mode 100644 index dae4dfd44..000000000 --- a/versioned_docs/version-0.46/integrate/architecture/adr-template.md +++ /dev/null @@ -1,60 +0,0 @@ -# ADR {ADR-NUMBER}: {TITLE} - -## Changelog - -* {date}: {changelog} - -## Status - -{DRAFT | PROPOSED} Not Implemented - -> Please have a look at the [PROCESS](./PROCESS.md#adr-status) page. -> Use DRAFT if the ADR is in a draft stage (draft PR) or PROPOSED if it's in review. - -## Abstract - -> "If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the ADR. -> A short (~200 word) description of the issue being addressed. - -## Context - -> This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. It should clearly explain the problem and motivation that the proposal aims to resolve. -> {context body} - -## Decision - -> This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..." -> {decision body} - -## Consequences - -> This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future. - -### Backwards Compatibility - -> All ADRs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The ADR must explain how the author proposes to deal with these incompatibilities. ADR submissions without a sufficient backwards compatibility treatise may be rejected outright. - -### Positive - -{positive consequences} - -### Negative - -{negative consequences} - -### Neutral - -{neutral consequences} - -## Further Discussions - -While an ADR is in the DRAFT or PROPOSED stage, this section should contain a summary of issues to be solved in future iterations (usually referencing comments from a pull-request discussion). -Later, this section can optionally list ideas or improvements the author or reviewers found during the analysis of this ADR. - -## Test Cases [optional] - -Test cases for an implementation are mandatory for ADRs that are affecting consensus changes. Other ADRs can choose to include links to test cases if applicable. - -## References - -* {reference link} diff --git a/versioned_docs/version-0.46/integrate/building-modules/00-intro.md b/versioned_docs/version-0.46/integrate/building-modules/00-intro.md deleted file mode 100644 index 590289b88..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/00-intro.md +++ /dev/null @@ -1,88 +0,0 @@ -# Introduction to Cosmos SDK Modules - -Modules define most of the logic of Cosmos SDK applications. Developers compose modules together using the Cosmos SDK to build their custom application-specific blockchains. This document outlines the basic concepts behind SDK modules and how to approach module management. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK application](../high-level-concepts/app-anatomy.md) {prereq} -* [Lifecycle of a Cosmos SDK transaction](../high-level-concepts/01-tx-lifecycle.md) {prereq} - -## Role of Modules in a Cosmos SDK Application - -The Cosmos SDK can be thought of as the Ruby-on-Rails of blockchain development. It comes with a core that provides the basic functionalities every blockchain application needs, like a [boilerplate implementation of the ABCI](../../develop/advanced-concepts/00-baseapp.md) to communicate with the underlying consensus engine, a [`multistore`](../advanced-concepts/04-store.md#multistore) to persist state, a [server](../advanced-concepts/03-node.md) to form a full-node and [interfaces](./09-module-interfaces.md) to handle queries. - -On top of this core, the Cosmos SDK enables developers to build modules that implement the business logic of their application. In other words, SDK modules implement the bulk of the logic of applications, while the core does the wiring and enables modules to be composed together. The end goal is to build a robust ecosystem of open-source Cosmos SDK modules, making it increasingly easier to build complex blockchain applications. - -Cosmos SDK modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one or more `KVStore`s in the [main multistore](../advanced-concepts/04-store.md), as well as a subset of [message types](./02-messages-and-queries.md#messages). These messages are routed by one of the main components of Cosmos SDK core, [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md), to a module Protobuf [`Msg` service](./03-msg-services.md) that defines them. - -```text - + - | - | Transaction relayed from the full-node's consensus engine - | to the node's application via DeliverTx - | - | - | - +---------------------v--------------------------+ - | APPLICATION | - | | - | Using baseapp's methods: Decode the Tx, | - | extract and route the message(s) | - | | - +---------------------+--------------------------+ - | - | - | - +---------------------------+ - | - | - | - | Message routed to the correct - | module to be processed - | - | -+----------------+ +---------------+ +----------------+ +------v----------+ -| | | | | | | | -| AUTH MODULE | | BANK MODULE | | STAKING MODULE | | GOV MODULE | -| | | | | | | | -| | | | | | | Handles message,| -| | | | | | | Updates state | -| | | | | | | | -+----------------+ +---------------+ +----------------+ +------+----------+ - | - | - | - | - +--------------------------+ - | - | Return result to the underlying consensus engine (e.g. Tendermint) - | (0=Ok, 1=Err) - v -``` - -As a result of this architecture, building a Cosmos SDK application usually revolves around writing modules to implement the specialized logic of the application and composing them with existing modules to complete the application. Developers will generally work on modules that implement logic needed for their specific use case that do not exist yet, and will use existing modules for more generic functionalities like staking, accounts, or token management. - -## How to Approach Building Modules as a Developer - -While there are no definitive guidelines for writing modules, here are some important design principles developers should keep in mind when building them: - -* **Composability**: Cosmos SDK applications are almost always composed of multiple modules. This means developers need to carefully consider the integration of their module not only with the core of the Cosmos SDK, but also with other modules. The former is achieved by following standard design patterns outlined [here](#main-components-of-sdk-modules), while the latter is achieved by properly exposing the store(s) of the module via the [`keeper`](./06-keeper.md). -* **Specialization**: A direct consequence of the **composability** feature is that modules should be **specialized**. Developers should carefully establish the scope of their module and not batch multiple functionalities into the same module. This separation of concerns enables modules to be re-used in other projects and improves the upgradability of the application. **Specialization** also plays an important role in the [object-capabilities model](../advanced-concepts/ocap.md) of the Cosmos SDK. -* **Capabilities**: Most modules need to read and/or write to the store(s) of other modules. However, in an open-source environment, it is possible for some modules to be malicious. That is why module developers need to carefully think not only about how their module interacts with other modules, but also about how to give access to the module's store(s). The Cosmos SDK takes a capabilities-oriented approach to inter-module security. This means that each store defined by a module is accessed by a `key`, which is held by the module's [`keeper`](./06-keeper.md). This `keeper` defines how to access the store(s) and under what conditions. Access to the module's store(s) is done by passing a reference to the module's `keeper`. - -## Main Components of Cosmos SDK Modules - -Modules are by convention defined in the `./x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components: - -* A [`keeper`](./06-keeper.md), used to access the module's store(s) and update the state. -* A [`Msg` service](./02-messages-and-queries.md#messages), used to process messages when they are routed to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#message-routing) and trigger state-transitions. -* A [query service](./04-query-services.md), used to process user queries when they are routed to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#query-routing). -* Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module. - -In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./01-module-manager.md). - -Please refer to the [structure document](./11-structure.md) to learn about the recommended structure of a module's directory. - -## Next {hide} - -Read more on the [`AppModule` interface and the `module manager`](./01-module-manager.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/01-module-manager.md b/versioned_docs/version-0.46/integrate/building-modules/01-module-manager.md deleted file mode 100644 index 96144e688..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/01-module-manager.md +++ /dev/null @@ -1,147 +0,0 @@ -# Module Manager - -Cosmos SDK modules need to implement the [`AppModule` interfaces](#application-module-interfaces), in order to be managed by the application's [module manager](#module-manager). The module manager plays an important role in [`message` and `query` routing](../../develop/advanced-concepts/00-baseapp.md#routing), and allows application developers to set the order of execution of a variety of functions like [`BeginBlocker` and `EndBlocker`](../high-level-concepts/app-anatomy.md#begingblocker-and-endblocker). {synopsis} - -## Pre-requisite Readings - -* [Introduction to Cosmos SDK Modules](./intro.md) - -## Application Module Interfaces - -Application module interfaces exist to facilitate the composition of modules together to form a functional Cosmos SDK application. There are 3 main application module interfaces: - -* [`AppModuleBasic`](#appmodulebasic) for independent module functionalities. -* [`AppModule`](#appmodule) for inter-dependent module functionalities (except genesis-related functionalities). -* [`AppModuleGenesis`](#appmodulegenesis) for inter-dependent genesis-related module functionalities. - -The `AppModuleBasic` interface exists to define independent methods of the module, i.e. those that do not depend on other modules in the application. This allows for the construction of the basic application structure early in the application definition, generally in the `init()` function of the [main application file](../high-level-concepts/app-anatomy.md#core-application-file). - -The `AppModule` interface exists to define inter-dependent module methods. Many modules need to interact with other modules, typically through [`keeper`s](./06-keeper.md), which means there is a need for an interface where modules list their `keeper`s and other methods that require a reference to another module's object. `AppModule` interface also enables the module manager to set the order of execution between module's methods like `BeginBlock` and `EndBlock`, which is important in cases where the order of execution between modules matters in the context of the application. - -Lastly the interface for genesis functionality `AppModuleGenesis` is separated out from full module functionality `AppModule` so that modules which -are only used for genesis can take advantage of the `Module` patterns without having to define many placeholder functions. - -### `AppModuleBasic` - -The `AppModuleBasic` interface defines the independent methods modules need to implement. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/module/module.go#L47-L60 - -Let us go through the methods: - -* `Name()`: Returns the name of the module as a `string`. -* `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marshal and unmarshal structs to/from `[]byte` in order to persist them in the module's `KVStore`. -* `RegisterInterfaces(codectypes.InterfaceRegistry)`: Registers a module's interface types and their concrete implementations as `proto.Message`. -* `DefaultGenesis(codec.JSONCodec)`: Returns a default [`GenesisState`](./08-genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing. -* `ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./08-genesis.md#validategenesis) function defined by the module developer. -* `RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux)`: Registers gRPC routes for the module. -* `GetTxCmd()`: Returns the root [`Tx` command](./09-module-interfaces.md#tx) for the module. The subcommands of this root command are used by end-users to generate new transactions containing [`message`s](./02-messages-and-queries.md#queries) defined in the module. -* `GetQueryCmd()`: Return the root [`query` command](./09-module-interfaces.md#query) for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module. - -All the `AppModuleBasic` of an application are managed by the [`BasicManager`](#basicmanager). - -### `AppModuleGenesis` - -The `AppModuleGenesis` interface is a simple embedding of the `AppModuleBasic` interface with two added methods. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/module/module.go#L140-L146 - -Let us go through the two added methods: - -* `InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started). -* `ExportGenesis(sdk.Context, codec.JSONCodec)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain. - -It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)` and `ExportGenesis(sdk.Context, codec.JSONCodec)` will generally be defined as methods of the concrete type implementing the `AppModule` interface. - -### `AppModule` - -The `AppModule` interface defines the inter-dependent methods that modules need to implement. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/module/module.go#L148-L176 - -`AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter. - -Let us go through the methods of `AppModule`: - -* `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./07-invariants.md) of the module. If an invariant deviates from its predicted value, the [`InvariantRegistry`](./07-invariants.md#registry) triggers appropriate logic (most often the chain will be halted). -* `Route()` (deprecated): Returns the route for [`message`s](./02-messages-and-queries.md#messages) to be routed to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#message-routing). -* `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./02-messages-and-queries.md#queries) to be routes to the module by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#query-routing). -* `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./04-query-services.md#legacy-queriers) given the query `path`, in order to process the `query`. -* `RegisterServices(Configurator)`: Allows a module to register services. -* `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module. -* `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the end of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the end of each block for this module. - -### Implementing the Application Module Interfaces - -Typically, the various application module interfaces are implemented in a file called `module.go`, located in the module's folder (e.g. `./x/module/module.go`). - -Almost every module needs to implement the `AppModuleBasic` and `AppModule` interfaces. If the module is only used for genesis, it will implement `AppModuleGenesis` instead of `AppModule`. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the `Route()` function often calls a `NewMsgServerImpl(k keeper)` function defined in `keeper/msg_server.go` and therefore needs to pass the module's [`keeper`](./06-keeper.md) as a parameter. - -```go -// example -type AppModule struct { - AppModuleBasic - keeper Keeper -} -``` - -In the example above, you can see that the `AppModule` concrete type references an `AppModuleBasic`, and not an `AppModuleGenesis`. That is because `AppModuleGenesis` only needs to be implemented in modules that focus on genesis-related functionalities. In most modules, the concrete `AppModule` type will have a reference to an `AppModuleBasic` and implement the two added methods of `AppModuleGenesis` directly in the `AppModule` type. - -If no parameter is required (which is often the case for `AppModuleBasic`), just declare an empty concrete type like so: - -```go -type AppModuleBasic struct{} -``` - -## Module Managers - -Module managers are used to manage collections of `AppModuleBasic` and `AppModule`. - -### `BasicManager` - -The `BasicManager` is a structure that lists all the `AppModuleBasic` of an application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/module/module.go#L62-L72 - -It implements the following methods: - -* `NewBasicManager(modules ...AppModuleBasic)`: Constructor function. It takes a list of the application's `AppModuleBasic` and builds a new `BasicManager`. This function is generally called in the `init()` function of [`app.go`](../high-level-concepts/app-anatomy.md#core-application-file) to quickly initialize the independent elements of the application's modules (click [here](https://github.com/cosmos/gaia/blob/main/app/app.go#L59-L74) to see an example). -* `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec.LegacyAmino`s](../01-tx-lifecycle.md05-encoding.md#amino) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../high-level-concepts/app-anatomy.md#constructor). -* `RegisterInterfaces(registry codectypes.InterfaceRegistry)`: Registers interface types and implementations of each of the application's `AppModuleBasic`. -* `DefaultGenesis(cdc codec.JSONCodec)`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis(cdc codec.JSONCodec)`](./08-genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application. -* `ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage)`](./08-genesis.md#validategenesis) function of each module. -* `RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux)`: Registers gRPC routes for modules. -* `AddTxCommands(rootTxCmd *cobra.Command)`: Adds modules' transaction commands to the application's [`rootTxCommand`](../01-tx-lifecycle.md06-cli.md#transaction-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../01-tx-lifecycle.md06-cli.md). -* `AddQueryCommands(rootQueryCmd *cobra.Command)`: Adds modules' query commands to the application's [`rootQueryCommand`](../01-tx-lifecycle.md06-cli.md#query-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../01-tx-lifecycle.md06-cli.md). - -### `Manager` - -The `Manager` is a structure that holds all the `AppModule` of an application, and defines the order of execution between several key components of these modules: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/module/module.go#L216-L225 - -The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods: - -* `NewManager(modules ...AppModule)`: Constructor function. It takes a list of the application's `AppModule`s and builds a new `Manager`. It is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -* `SetOrderInitGenesis(moduleNames ...string)`: Sets the order in which the [`InitGenesis`](./08-genesis.md#initgenesis) function of each module will be called when the application is first started. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). - - To initialize modules successfully, module dependencies should be considered. For example, the `genutil` module must occur after `staking` module so that the pools are properly initialized with tokens from genesis accounts, the `genutils` module must also occur after `auth` so that it can access the params from auth, `capability` module should be initialized before all other modules so that it can initialize any capabilities. -* `SetOrderExportGenesis(moduleNames ...string)`: Sets the order in which the [`ExportGenesis`](./08-genesis.md#exportgenesis) function of each module will be called in case of an export. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -* `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -* `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the end of each block. This function is generally called from the application's main [constructor function](../high-level-concepts/app-anatomy.md#constructor-function). -* `SetOrderMigrations(moduleNames ...string)`: Sets the order of migrations to be run. If not set then migrations will be run with an order defined in `DefaultMigrationsOrder`. -* `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./07-invariants.md) of each module. -* `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers legacy [`Msg`](./02-messages-and-queries.md#messages) and [`querier`](./04-query-services.md#legacy-queriers) routes. -* `RegisterServices(cfg Configurator)`: Registers all module services. -* `InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./08-genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates. -* `ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec)`: Calls the [`ExportGenesis`](./08-genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required. -* `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../01-tx-lifecycle.md02-context.md) with an event manager to aggregate [events](../01-tx-lifecycle.md07-events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events. -* `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../01-tx-lifecycle.md02-context.md) with an event manager to aggregate [events](../01-tx-lifecycle.md07-events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any). - -Here's an example of a concrete integration within an application: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/simapp/app.go#L342-L409 - -## Next {hide} - -Learn more about [`message`s and `queries`](./02-messages-and-queries.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/02-messages-and-queries.md b/versioned_docs/version-0.46/integrate/building-modules/02-messages-and-queries.md deleted file mode 100644 index 7b1124359..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/02-messages-and-queries.md +++ /dev/null @@ -1,111 +0,0 @@ -# Messages and Queries - -`Msg`s and `Queries` are the two primary objects handled by modules. Most of the core components defined in a module, like `Msg` services, `keeper`s and `Query` services, exist to process `message`s and `queries`. {synopsis} - -## Pre-requisite Readings - -* [Introduction to Cosmos SDK Modules](./intro.md) {prereq} - -## Messages - -`Msg`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../advanced-concepts/01-transactions.md), which may contain one or more of them. - -When a transaction is relayed from the underlying consensus engine to the Cosmos SDK application, it is first decoded by [`BaseApp`](../../develop/advanced-concepts/00-baseapp.md). Then, each message contained in the transaction is extracted and routed to the appropriate module via `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](./03-msg-services.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../high-level-concepts/01-tx-lifecycle.md). - -### `Msg` Services - -Defining Protobuf `Msg` services is the recommended way to handle messages. A Protobuf `Msg` service should be created for each module, typically in `tx.proto` (see more info about [conventions and naming](../advanced-concepts/05-encoding.md#faq)). It must have an RPC service method defined for each message in the module. - -See an example of a `Msg` service definition from `x/bank` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L12-L19 - -Each `Msg` service method must have exactly one argument, which must implement the `sdk.Msg` interface, and a Protobuf response. The naming convention is to call the RPC argument `Msg` and the RPC response `MsgResponse`. For example: - -```protobuf - rpc Send(MsgSend) returns (MsgSendResponse); -``` - -`sdk.Msg` interface is a simplified version of the Amino `LegacyMsg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods. For backwards compatibility with [Amino `LegacyMsg`s](#legacy-amino-msgs), existing `LegacyMsg` types should be used as the request parameter for `service` RPC definitions. Newer `sdk.Msg` types, which only support `service` definitions, should use canonical `Msg...` name. - -The Cosmos SDK uses Protobuf definitions to generate client and server code: - -* `MsgServer` interface defines the server API for the `Msg` service and its implementation is described as part of the [`Msg` services](./03-msg-services.md) documentation. -* Structures are generated for all RPC request and response types. - -A `RegisterMsgServer` method is also generated and should be used to register the module's `MsgServer` implementation in `RegisterServices` method from the [`AppModule` interface](./01-module-manager.md#appmodule). - -In order for clients (CLI and grpc-gateway) to have these URLs registered, the Cosmos SDK provides the function `RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc)` that should be called inside module's [`RegisterInterfaces`](module-manager.md#appmodulebasic) method, using the proto-generated `&_Msg_serviceDesc` as `*grpc.ServiceDesc` argument. - -### Legacy Amino `LegacyMsg`s - -The following way of defining messages is deprecated and using [`Msg` services](#msg-services) is preferred. - -Amino `LegacyMsg`s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message. - -A `LegacyMsg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./09-module-interfaces.md). `message`s also need to implement the `sdk.Msg` interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/tx_msg.go#L10-L22 - -It extends `proto.Message` and contains the following methods: - -* `Route() string`: Name of the route for this message. Typically all `message`s in a module have the same route, which is most often the module's name. -* `Type() string`: Type of the message, used primarily in [events](../advanced-concepts/07-events.md). This should return a message-specific `string`, typically the denomination of the message itself. -* [`ValidateBasic() error`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic). -* `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature. -* `GetSigners() []AccAddress`: Return the list of signers. The Cosmos SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/migrations/legacytx/stdsign.go#L20-L36 - -See an example implementation of a `message` from the `gov` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/gov/types/v1/msgs.go#L106-L138 - -## Queries - -A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `BaseApp`'s `QueryRouter` so that it can be processed by the module's query service (./04-query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../high-level-concepts/query-lifecycle.md). - -### gRPC Queries - -Queries should be defined using [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services). A `Query` service should be created per module in `query.proto`. This service lists endpoints starting with `rpc`. - -Here's an example of such a `Query` service definition: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/auth/v1beta1/query.proto#L13-L59 - -As `proto.Message`s, generated `Response` types implement by default `String()` method of [`fmt.Stringer`](https://pkg.go.dev/fmt#Stringer). - -A `RegisterQueryServer` method is also generated and should be used to register the module's query server in the `RegisterServices` method from the [`AppModule` interface](./01-module-manager.md#appmodule). - -### Legacy Queries - -Before the introduction of Protobuf and gRPC in the Cosmos SDK, there was usually no specific `query` object defined by module developers, contrary to `message`s. Instead, the Cosmos SDK took the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed to process it. For most module queries, the `path` should look like the following: - -```text -queryCategory/queryRoute/queryType/arg1/arg2/... -``` - -where: - -* `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `BaseApp`'s [`Query` method](../../develop/advanced-concepts/00-baseapp.md#query). -* `queryRoute` is used by `BaseApp`'s [`queryRouter`](../../develop/advanced-concepts/00-baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module. -* `queryType` is used by the module's [`querier`](./04-query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module. -* `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`. - -The `path` for each `query` must be defined by the module developer in the module's [command-line interface file](./09-module-interfaces.md#query-commands).Overall, there are 3 mains components module developers need to implement in order to make the subset of the state defined by their module queryable: - -* A [`querier`](./04-query-services.md#legacy-queriers), to process the `query` once it has been [routed to the module](../../develop/advanced-concepts/00-baseapp.md#query-routing). -* [Query commands](./09-module-interfaces.md#query-commands) in the module's CLI file, where the `path` for each `query` is specified. -* `query` return types. Typically defined in a file `types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://pkg.go.dev/fmt#Stringer). - -### Store Queries - -Store queries query directly for store keys. They use `clientCtx.QueryABCI(req abci.RequestQuery)` to return the full `abci.ResponseQuery` with inclusion Merkle proofs. - -See following examples: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/abci.go#L756-L777 - -## Next {hide} - -Learn about [`Msg` services](./03-msg-services.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/03-msg-services.md b/versioned_docs/version-0.46/integrate/building-modules/03-msg-services.md deleted file mode 100644 index 6db59fa4a..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/03-msg-services.md +++ /dev/null @@ -1,96 +0,0 @@ -# `Msg` Services - -A Protobuf `Msg` service processes [messages](./02-messages-and-queries.md#messages). Protobuf `Msg` services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from `BaseApp` during [`DeliverTx`](../../develop/advanced-concepts/00-baseapp.md#delivertx). {synopsis} - -## Pre-requisite Readings - -* [Module Manager](./01-module-manager.md) {prereq} -* [Messages and Queries](./02-messages-and-queries.md) {prereq} - -## Implementation of a module `Msg` service - -Each module should define a Protobuf `Msg` service, which will be responsible for processing requests (implementing `sdk.Msg`) and returning responses. - -As further described in [ADR 031](../architecture/adr-031-msg-service.md), this approach has the advantage of clearly specifying return types and generating server and client code. - -Protobuf generates a `MsgServer` interface based on a definition of `Msg` service. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each `sdk.Msg`. As an example, here is the generated `MsgServer` interface for `x/bank`, which exposes two `sdk.Msg`s: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/types/tx.pb.go#L288-L294 - -When possible, the existing module's [`Keeper`](keeper.md) should implement `MsgServer`, otherwise a `msgServer` struct that embeds the `Keeper` can be created, typically in `./keeper/msg_server.go`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/keeper/msg_server.go#L14-L16 - -`msgServer` methods can retrieve the `sdk.Context` from the `context.Context` parameter method using the `sdk.UnwrapSDKContext`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/keeper/msg_server.go#L27-L27 - -`sdk.Msg` processing usually follows these 3 steps: - -### Validation - -Before a `msgServer` method is executed, the message's [`ValidateBasic()`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic) method has already been called. Since `msg.ValidateBasic()` performs only the most basic checks, this stage must perform all other validation (both *stateful* and *stateless*) to make sure the `message` is valid. Checks performed in the `msgServer` method can be more expensive and the signer is charged gas for these operations. -For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. - -It is recommended to implement all validation checks in a separate function that passes state values as arguments. This implementation simplifies testing. As expected, expensive validation functions charge additional gas. Example: - -```go -ValidateMsgA(msg MsgA, now Time, gm GasMeter) error { - if now.Before(msg.Expire) { - return sdkerrrors.ErrInvalidRequest.Wrap("msg expired") - } - gm.ConsumeGas(1000, "signature verification") - return signatureVerificaton(msg.Prover, msg.Data) -} -``` - -### State Transition - -After the validation is successful, the `msgServer` method uses the [`keeper`](./06-keeper.md) functions to access the state and perform a state transition. - -### Events - -Before returning, `msgServer` methods generally emit one or more [events](../advanced-concepts/07-events.md) by using the `EventManager` held in the `ctx`. Use the new `EmitTypedEvent` function that uses protobuf-based event types: - -```go -ctx.EventManager().EmitTypedEvent( - &group.EventABC{Key1: Value1, Key2, Value2}) -``` - -or the older `EmitEvent` function: - -```go -ctx.EventManager().EmitEvent( - sdk.NewEvent( - eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module - sdk.NewAttribute(key1, value1), - sdk.NewAttribute(key2, value2), - ), -) -``` - -These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../advanced-concepts/07-events.md) to learn more about events. - -The invoked `msgServer` method returns a `proto.Message` response and an `error`. These return values are then wrapped into an `*sdk.Result` or an `error` using `sdk.WrapServiceResult(ctx sdk.Context, res proto.Message, err error)`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/baseapp/msg_service_router.go#L127 - -This method takes care of marshaling the `res` parameter to protobuf and attaching any events on the `ctx.EventManager()` to the `sdk.Result`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/base/abci/v1beta1/abci.proto#L88-L109 - -This diagram shows a typical structure of a Protobuf `Msg` service, and how the message propagates through the module. - -![Transaction flow](./transaction_flow.svg) - -## Telemetry - -New [telemetry metrics](../advanced-concepts/11-telemetry.md) can be created from `msgServer` methods when handling messages. - -This is an example from the `x/auth/vesting` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/vesting/msg_server.go#L73-L85 - -## Next {hide} - -Learn about [query services](./04-query-services.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/04-query-services.md b/versioned_docs/version-0.46/integrate/building-modules/04-query-services.md deleted file mode 100644 index c8b794a28..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/04-query-services.md +++ /dev/null @@ -1,73 +0,0 @@ -# Query Services - -A Protobuf Query service processes [`queries`](./02-messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `BaseApp`'s [`Query` method](../../develop/advanced-concepts/00-baseapp.md#query). {synopsis} - -## Pre-requisite Readings - -* [Module Manager](./01-module-manager.md) {prereq} -* [Messages and Queries](./02-messages-and-queries.md) {prereq} - -## `Querier` type - -The `querier` type defined in the Cosmos SDK will be deprecated in favor of [gRPC Services](#grpc-service). It specifies the typical structure of a `querier` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/queryable.go#L9 - -Let us break it down: - -* The [`Context`](../advanced-concepts/02-context.md) contains all the necessary information needed to process the `query`, as well as a branch of the latest state. It is primarily used by the [`keeper`](./06-keeper.md) to access the state. -* The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./02-messages-and-queries.md#queries) for more information. -* The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. -* The result in `[]byte` returned to `BaseApp`, marshalled using the application's [`codec`](../advanced-concepts/05-encoding.md). - -## Implementation of a module query service - -### gRPC Service - -When defining a Protobuf `Query` service, a `QueryServer` interface is generated for each module with all the service methods: - -```go -type QueryServer interface { - QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) - QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) -} -``` - -These custom queries methods should be implemented by a module's keeper, typically in `./keeper/grpc_query.go`. The first parameter of these methods is a generic `context.Context`, whereas querier methods generally need an instance of `sdk.Context` to read -from the store. Therefore, the Cosmos SDK provides a function `sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided -`context.Context`. - -Here's an example implementation for the bank module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/keeper/grpc_query.go - -### Legacy Queriers - -Module legacy `querier`s are typically implemented in a `./keeper/querier.go` file inside the module's folder. The [module manager](./01-module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../../develop/advanced-concepts/00-baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following: - -```go -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - case QueryType1: - return queryType1(ctx, path[1:], req, keeper) - - case QueryType2: - return queryType2(ctx, path[1:], req, keeper) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} -``` - -This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../high-level-concepts/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query. - -The `querier` functions themselves are pretty straightforward. They generally fetch a value or values from the state using the [`keeper`](./06-keeper.md). Then, they marshall the value(s) using the [`codec`](../01-tx-lifecycle.md05-encoding.md) and return the `[]byte` obtained as result. - -For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/gov/keeper/querier.go) from the bank module. - -## Next {hide} - -Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/05-beginblock-endblock.md b/versioned_docs/version-0.46/integrate/building-modules/05-beginblock-endblock.md deleted file mode 100644 index 541a3dfb1..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/05-beginblock-endblock.md +++ /dev/null @@ -1,35 +0,0 @@ -# BeginBlocker and EndBlocker - -`BeginBlocker` and `EndBlocker` are optional methods module developers can implement in their module. They will be triggered at the beginning and at the end of each block respectively, when the [`BeginBlock`](../../develop/advanced-concepts/00-baseapp.md#beginblock) and [`EndBlock`](../../develop/advanced-concepts/00-baseapp.md#endblock) ABCI messages are received from the underlying consensus engine. {synopsis} - -## Pre-requisite Readings - -* [Module Manager](./01-module-manager.md) {prereq} - -## BeginBlocker and EndBlocker - -`BeginBlocker` and `EndBlocker` are a way for module developers to add automatic execution of logic to their module. This is a powerful tool that should be used carefully, as complex automatic functions can slow down or even halt the chain. - -When needed, `BeginBlocker` and `EndBlocker` are implemented as part of the [`AppModule` interface](./01-module-manager.md#appmodule). The `BeginBlock` and `EndBlock` methods of the interface implemented in `module.go` generally defer to `BeginBlocker` and `EndBlocker` methods respectively, which are usually implemented in `abci.go`. - -The actual implementation of `BeginBlocker` and `EndBlocker` in `abci.go` are very similar to that of a [`Msg` service](./03-msg-services.md): - -* They generally use the [`keeper`](./06-keeper.md) and [`ctx`](../advanced-concepts/02-context.md) to retrieve information about the latest state. -* If needed, they use the `keeper` and `ctx` to trigger state-transitions. -* If needed, they can emit [`events`](../advanced-concepts/07-events.md) via the `ctx`'s `EventManager`. - -A specificity of the `EndBlocker` is that it can return validator updates to the underlying consensus engine in the form of an [`[]abci.ValidatorUpdates`](https://docs.tendermint.com/master/spec/abci/abci.html#validatorupdate). This is the preferred way to implement custom validator changes. - -It is possible for developers to define the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./01-module-manager.md#manager). - -See an example implementation of `BeginBlocker` from the `distribution` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/distribution/abci.go#L14-L38 - -and an example implementation of `EndBlocker` from the `staking` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/abci.go#L22-L27 - -## Next {hide} - -Learn about [`keeper`s](./06-keeper.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/06-keeper.md b/versioned_docs/version-0.46/integrate/building-modules/06-keeper.md deleted file mode 100644 index 68892af3e..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/06-keeper.md +++ /dev/null @@ -1,81 +0,0 @@ -# Keepers - -`Keeper`s refer to a Cosmos SDK abstraction whose role is to manage access to the subset of the state defined by various modules. `Keeper`s are module-specific, i.e. the subset of state defined by a module can only be accessed by a `keeper` defined in said module. If a module needs to access the subset of state defined by another module, a reference to the second module's internal `keeper` needs to be passed to the first one. This is done in `app.go` during the instantiation of module keepers. {synopsis} - -## Pre-requisite Readings - -* [Introduction to Cosmos SDK Modules](./intro.md) {prereq} - -## Motivation - -The Cosmos SDK is a framework that makes it easy for developers to build complex decentralized applications from scratch, mainly by composing modules together. As the ecosystem of open-source modules for the Cosmos SDK expands, it will become increasingly likely that some of these modules contain vulnerabilities, as a result of the negligence or malice of their developer. - -The Cosmos SDK adopts an [object-capabilities-based approach](../advanced-concepts/ocap.md) to help developers better protect their application from unwanted inter-module interactions, and `keeper`s are at the core of this approach. A `keeper` can be considered quite literally to be the gatekeeper of a module's store(s). Each store (typically an [`IAVL` Store](../advanced-concepts/04-store.md#iavl-store)) defined within a module comes with a `storeKey`, which grants unlimited access to it. The module's `keeper` holds this `storeKey` (which should otherwise remain unexposed), and defines [methods](#implementing-methods) for reading and writing to the store(s). - -The core idea behind the object-capabilities approach is to only reveal what is necessary to get the work done. In practice, this means that instead of handling permissions of modules through access-control lists, module `keeper`s are passed a reference to the specific instance of the other modules' `keeper`s that they need to access (this is done in the [application's constructor function](../high-level-concepts/app-anatomy.md#constructor-function)). As a consequence, a module can only interact with the subset of state defined in another module via the methods exposed by the instance of the other module's `keeper`. This is a great way for developers to control the interactions that their own module can have with modules developed by external developers. - -## Type Definition - -`keeper`s are generally implemented in a `/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure: - -```go -type Keeper struct { - // External keepers, if any - - // Store key(s) - - // codec -} -``` - -For example, here is the type definition of the `keeper` from the `staking` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/keeper/keeper.go#L21-L29 - -Let us go through the different parameters: - -* An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in an `expected_keepers.go` file in the root of the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself. -* `storeKey`s grant access to the store(s) of the [multistore](../advanced-concepts/04-store.md) managed by the module. They should always remain unexposed to external modules. -* `cdc` is the [codec](../advanced-concepts/05-encoding.md) used to marshall and unmarshall structs to/from `[]byte`. The `cdc` can be any of `codec.BinaryCodec`, `codec.JSONCodec` or `codec.Codec` based on your requirements. It can be either a proto or amino codec as long as they implement these interfaces. - -Of course, it is possible to define different types of internal `keeper`s for the same module (e.g. a read-only `keeper`). Each type of `keeper` comes with its own constructor function, which is called from the [application's constructor function](../high-level-concepts/app-anatomy.md). This is where `keeper`s are instantiated, and where developers make sure to pass correct instances of modules' `keeper`s to other modules that require them. - -## Implementing Methods - -`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./02-messages-and-queries.md#messages) and the [`Msg` server](./03-msg-services.md) when `keeper`s' methods are called. - -Typically, a *getter* method will have the following signature - -```go -func (k Keeper) Get(ctx sdk.Context, key string) returnType -``` - -and the method will go through the following steps: - -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's preferred to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. -2. If it exists, get the `[]byte` value stored at location `[]byte(key)` using the `Get(key []byte)` method of the store. -3. Unmarshall the retrieved value from `[]byte` to `returnType` using the codec `cdc`. Return the value. - -Similarly, a *setter* method will have the following signature - -```go -func (k Keeper) Set(ctx sdk.Context, key string, value valueType) -``` - -and the method will go through the following steps: - -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. It's preferred to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. -2. Marshal `value` to `[]byte` using the codec `cdc`. -3. Set the encoded value in the store at location `key` using the `Set(key []byte, value []byte)` method of the store. - -For more, see an example of `keeper`'s [methods implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/keeper/keeper.go). - -The [module `KVStore`](../advanced-concepts/04-store.md#kvstore-and-commitkvstore-interfaces) also provides an `Iterator()` method which returns an `Iterator` object to iterate over a domain of keys. - -This is an example from the `auth` module to iterate accounts: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/keeper/account.go#L76-L90 - -## Next {hide} - -Learn about [invariants](./07-invariants.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/07-invariants.md b/versioned_docs/version-0.46/integrate/building-modules/07-invariants.md deleted file mode 100644 index 359039dee..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/07-invariants.md +++ /dev/null @@ -1,76 +0,0 @@ -# Invariants - -An invariant is a property of the application that should always be true. In the context of the Cosmos SDK, an `Invariant` is a function that checks for a particular invariant. These functions are useful to detect bugs early on and act upon them to limit their potential consequences (e.g. by halting the chain). They are also useful in the development process of the application to detect bugs via simulations. {synopsis} - -## Pre-requisite Readings - -* [Keepers](./06-keeper.md) {prereq} - -## Implementing `Invariant`s - -An `Invariant` is a function that checks for a particular invariant within a module. Module `Invariant`s must follow the `Invariant` type: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/invariant.go#L9 - -The `string` return value is the invariant message, which can be used when printing logs, and the `bool` return value is the actual result of the invariant check. - -In practice, each module implements `Invariant`s in a `keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: - -```go -// Example for an Invariant that checks balance-related invariants - -func BalanceInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - // Implement checks for balance-related invariants - } -} -``` - -Additionally, module developers should generally implement an `AllInvariants` function that runs all the `Invariant`s functions of the module: - -```go -// AllInvariants runs all invariants of the module. -// In this example, the module implements two Invariants: BalanceInvariants and DepositsInvariants - -func AllInvariants(k Keeper) sdk.Invariant { - - return func(ctx sdk.Context) (string, bool) { - res, stop := BalanceInvariants(k)(ctx) - if stop { - return res, stop - } - - return DepositsInvariant(k)(ctx) - } -} -``` - -Finally, module developers need to implement the `RegisterInvariants` method as part of the [`AppModule` interface](./01-module-manager.md#appmodule). Indeed, the `RegisterInvariants` method of the module, implemented in the `module/module.go` file, typically only defers the call to a `RegisterInvariants` method implemented in the `keeper/invariants.go` file. The `RegisterInvariants` method registers a route for each `Invariant` function in the [`InvariantRegistry`](#invariant-registry): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/keeper/invariants.go#L12-L21 - -For more, see an example of [`Invariant`s implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/keeper/invariants.go). - -## Invariant Registry - -The `InvariantRegistry` is a registry where the `Invariant`s of all the modules of an application are registered. There is only one `InvariantRegistry` per **application**, meaning module developers need not implement their own `InvariantRegistry` when building a module. **All module developers need to do is to register their modules' invariants in the `InvariantRegistry`, as explained in the section above**. The rest of this section gives more information on the `InvariantRegistry` itself, and does not contain anything directly relevant to module developers. - -At its core, the `InvariantRegistry` is defined in the Cosmos SDK as an interface: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/types/invariant.go#L14-L17 - -Typically, this interface is implemented in the `keeper` of a specific module. The most used implementation of an `InvariantRegistry` can be found in the `crisis` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/crisis/keeper/keeper.go#L49-L53 - - The `InvariantRegistry` is therefore typically instantiated by instantiating the `keeper` of the `crisis` module in the [application's constructor function](../high-level-concepts/app-anatomy.md#constructor-function). - -`Invariant`s can be checked manually via [`message`s](./02-messages-and-queries.md), but most often they are checked automatically at the end of each block. Here is an example from the `crisis` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/crisis/abci.go#L12-L21 - -In both cases, if one of the `Invariant`s returns false, the `InvariantRegistry` can trigger special logic (e.g. have the application panic and print the `Invariant`s message in the log). - -## Next {hide} - -Learn about [genesis functionalities](./08-genesis.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/08-genesis.md b/versioned_docs/version-0.46/integrate/building-modules/08-genesis.md deleted file mode 100644 index 2406e10b9..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/08-genesis.md +++ /dev/null @@ -1,56 +0,0 @@ -# Module Genesis - -Modules generally handle a subset of the state and, as such, they need to define the related subset of the genesis file as well as methods to initialize, verify and export it. {synopsis} - -## Pre-requisite Readings - -* [Module Manager](./01-module-manager.md) {prereq} -* [Keepers](./06-keeper.md) {prereq} - -## Type Definition - -The subset of the genesis state defined from a given module is generally defined in a `genesis.proto` file ([more info](../advanced-concepts/05-encoding.md#gogoproto) on how to define protobuf messages). The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. - -See an example of `GenesisState` protobuf message definition from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/auth/v1beta1/genesis.proto - -Next we present the main genesis-related methods that need to be implemented by module developers in order for their module to be used in Cosmos SDK applications. - -### `DefaultGenesis` - -The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/module.go#L45-L49 - -### `ValidateGenesis` - -The `ValidateGenesis(data GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameters listed in `GenesisState`. See an example from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/types/genesis.go#L61-L74 - -## Other Genesis Methods - -Other than the methods related directly to `GenesisState`, module developers are expected to implement two other methods as part of the [`AppModuleGenesis` interface](./01-module-manager.md#appmodulegenesis) (only if the module needs to initialize a subset of state in genesis). These methods are [`InitGenesis`](#initgenesis) and [`ExportGenesis`](#exportgenesis). - -### `InitGenesis` - -The `InitGenesis` method is executed during [`InitChain`](../../develop/advanced-concepts/00-baseapp.md#initchain) when the application is first started. Given a `GenesisState`, it initializes the subset of the state managed by the module by using the module's [`keeper`](./06-keeper.md) setter function on each parameter within the `GenesisState`. - -The [module manager](./01-module-manager.md#manager) of the application is responsible for calling the `InitGenesis` method of each of the application's modules in order. This order is set by the application developer via the manager's `SetOrderGenesisMethod`, which is called in the [application's constructor function](../high-level-concepts/app-anatomy.md#constructor-function). - -See an example of `InitGenesis` from the `auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/keeper/genesis.go#L8-L27 - -### `ExportGenesis` - -The `ExportGenesis` method is executed whenever an export of the state is made. It takes the latest known version of the subset of the state managed by the module and creates a new `GenesisState` out of it. This is mainly used when the chain needs to be upgraded via a hard fork. - -See an example of `ExportGenesis` from the `auth` module. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/keeper/genesis.go#L29-L41 - -## Next {hide} - -Learn about [modules interfaces](module-interfaces.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/09-module-interfaces.md b/versioned_docs/version-0.46/integrate/building-modules/09-module-interfaces.md deleted file mode 100644 index d189941e3..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/09-module-interfaces.md +++ /dev/null @@ -1,135 +0,0 @@ -# Module Interfaces - -This document details how to build CLI and REST interfaces for a module. Examples from various Cosmos SDK modules are included. {synopsis} - -## Prerequisite Readings - -* [Building Modules Intro](./intro.md) {prereq} - -## CLI - -One of the main interfaces for an application is the [command-line interface](../advanced-concepts/06-cli.md). This entrypoint adds commands from the application's modules enabling end-users to create [**messages**](./02-messages-and-queries.md#messages) wrapped in transactions and [**queries**](./02-messages-and-queries.md#queries). The CLI files are typically found in the module's `./client/cli` folder. - -### Transaction Commands - -In order to create messages that trigger state changes, end-users must create [transactions](../advanced-concepts/01-transactions.md) that wrap and deliver the messages. A transaction command creates a transaction that includes one or more messages. - -Transaction commands typically have their own `tx.go` file that lives within the module's `./client/cli` folder. The commands are specified in getter functions and the name of the function should include the name of the command. - -Here is an example from the `x/bank` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/client/cli/tx.go#L35-L71 - -In the example, `NewSendTxCmd()` creates and returns the transaction command for a transaction that wraps and delivers `MsgSend`. `MsgSend` is the message used to send tokens from one account to another. - -In general, the getter function does the following: - -* **Constructs the command:** Read the [Cobra Documentation](https://pkg.go.dev/github.com/spf13/cobra) for more detailed information on how to create commands. - * **Use:** Specifies the format of the user input required to invoke the command. In the example above, `send` is the name of the transaction command and `[from_key_or_address]`, `[to_address]`, and `[amount]` are the arguments. - * **Args:** The number of arguments the user provides. In this case, there are exactly three: `[from_key_or_address]`, `[to_address]`, and `[amount]`. - * **Short and Long:** Descriptions for the command. A `Short` description is expected. A `Long` description can be used to provide additional information that is displayed when a user adds the `--help` flag. - * **RunE:** Defines a function that can return an error. This is the function that is called when the command is executed. This function encapsulates all of the logic to create a new transaction. - * The function typically starts by getting the `clientCtx`, which can be done with `client.GetClientTxContext(cmd)`. The `clientCtx` contains information relevant to transaction handling, including information about the user. In this example, the `clientCtx` is used to retrieve the address of the sender by calling `clientCtx.GetFromAddress()`. - * If applicable, the command's arguments are parsed. In this example, the arguments `[to_address]` and `[amount]` are both parsed. - * A [message](./02-messages-and-queries.md) is created using the parsed arguments and information from the `clientCtx`. The constructor function of the message type is called directly. In this case, `types.NewMsgSend(fromAddr, toAddr, amount)`. Its good practice to call [`msg.ValidateBasic()`](../high-level-concepts/01-tx-lifecycle.md#ValidateBasic) and other validation methods before broadcasting the message. - * Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `tx.GenerateOrBroadcastTxCLI(clientCtx, flags, msg)`. -* **Adds transaction flags:** All transaction commands must add a set of transaction [flags](#flags). The transaction flags are used to collect additional information from the user (e.g. the amount of fees the user is willing to pay). The transaction flags are added to the constructed command using `AddTxFlagsToCmd(cmd)`. -* **Returns the command:** Finally, the transaction command is returned. - -Each module must implement `NewTxCmd()`, which aggregates all of the transaction commands of the module. Here is an example from the `x/bank` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/client/cli/tx.go#L17-L33 - -Each module must also implement the `GetTxCmd()` method for `AppModuleBasic` that simply returns `NewTxCmd()`. This allows the root command to easily aggregate all of the transaction commands for each module. Here is an example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/module.go#L70-L73 - -### Query Commands - -[Queries](./02-messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module's `./client/cli` folder. Like transaction commands, they are specified in getter functions. Here is an example of a query command from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/client/cli/query.go#L83-L125 - -In the example, `GetAccountCmd()` creates and returns a query command that returns the state of an account based on the provided account address. - -In general, the getter function does the following: - -* **Constructs the command:** Read the [Cobra Documentation](https://pkg.go.dev/github.com/spf13/cobra) for more detailed information on how to create commands. - * **Use:** Specifies the format of the user input required to invoke the command. In the example above, `account` is the name of the query command and `[address]` is the argument. - * **Args:** The number of arguments the user provides. In this case, there is exactly one: `[address]`. - * **Short and Long:** Descriptions for the command. A `Short` description is expected. A `Long` description can be used to provide additional information that is displayed when a user adds the `--help` flag. - * **RunE:** Defines a function that can return an error. This is the function that is called when the command is executed. This function encapsulates all of the logic to create a new query. - * The function typically starts by getting the `clientCtx`, which can be done with `client.GetClientQueryContext(cmd)`. The `clientCtx` contains information relevant to query handling. - * If applicable, the command's arguments are parsed. In this example, the argument `[address]` is parsed. - * A new `queryClient` is initialized using `NewQueryClient(clientCtx)`. The `queryClient` is then used to call the appropriate [query](./02-messages-and-queries.md#grpc-queries). - * The `clientCtx.PrintProto` method is used to format the `proto.Message` object so that the results can be printed back to the user. -* **Adds query flags:** All query commands must add a set of query [flags](#flags). The query flags are added to the constructed command using `AddQueryFlagsToCmd(cmd)`. -* **Returns the command:** Finally, the query command is returned. - -Each module must implement `GetQueryCmd()`, which aggregates all of the query commands of the module. Here is an example from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/client/cli/query.go#L25-L42 - -Each module must also implement the `GetQueryCmd()` method for `AppModuleBasic` that returns the `GetQueryCmd()` function. This allows for the root command to easily aggregate all of the query commands for each module. Here is an example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/client/cli/query.go#L32-L50 - -### Flags - -[Flags](../01-tx-lifecycle.md06-cli.md#flags) allow users to customize commands. `--fees` and `--gas-prices` are examples of flags that allow users to set the [fees](../high-level-concepts/gas-fees.md) and gas prices for their transactions. - -Flags that are specific to a module are typically created in a `flags.go` file in the module's `./client/cli` folder. When creating a flag, developers set the value type, the name of the flag, the default value, and a description about the flag. Developers also have the option to mark flags as _required_ so that an error is thrown if the user does not include a value for the flag. - -Here is an example that adds the `--from` flag to a command: - -```go -cmd.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") -``` - -In this example, the value of the flag is a `String`, the name of the flag is `from` (the value of the `FlagFrom` constant), the default value of the flag is `""`, and there is a description that will be displayed when a user adds `--help` to the command. - -Here is an example that marks the `--from` flag as _required_: - -```go -cmd.MarkFlagRequired(FlagFrom) -``` - -For more detailed information on creating flags, visit the [Cobra Documentation](https://github.com/spf13/cobra). - -As mentioned in [transaction commands](#transaction-commands), there is a set of flags that all transaction commands must add. This is done with the `AddTxFlagsToCmd` method defined in the Cosmos SDK's `./client/flags` package. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/flags/flags.go#L103-L131 - -Since `AddTxFlagsToCmd(cmd *cobra.Command)` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). - -Similarly, there is a `AddQueryFlagsToCmd(cmd *cobra.Command)` to add common flags to a module query command. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/flags/flags.go#L92-L101 - -## gRPC - -[gRPC](https://grpc.io/) is a Remote Procedure Call (RPC) framework. RPC is the preferred way for external clients like wallets and exchanges to interact with a blockchain. - -In addition to providing an ABCI query pathway, the Cosmos SDK provides a gRPC proxy server that routes gRPC query requests to ABCI query requests. - -In order to do that, modules must implement `RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)` on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. - -Here's an example from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/module.go#L61-L66 - -## gRPC-gateway REST - -Applications need to support web services that use HTTP requests (e.g. a web wallet like [Keplr](https://keplr.app)). [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) translates REST calls into gRPC calls, which might be useful for clients that do not use gRPC. - -Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods, such as in the example below from the `x/auth` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/auth/v1beta1/query.proto#L13-L59 - -gRPC gateway is started in-process along with the application and Tendermint. It can be enabled or disabled by setting gRPC Configuration `enable` in [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). - -The Cosmos SDK provides a command for generating [Swagger](https://swagger.io/) documentation (`protoc-gen-swagger`). Setting `swagger` in [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) defines if swagger documentation should be automatically registered. - -## Next {hide} - -Read about the recommended [module structure](./11-structure.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/11-structure.md b/versioned_docs/version-0.46/integrate/building-modules/11-structure.md deleted file mode 100644 index 043bcfaa6..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/11-structure.md +++ /dev/null @@ -1,95 +0,0 @@ -# Recommended Folder Structure - -This document outlines the recommended structure of Cosmos SDK modules. These ideas are meant to be applied as suggestions. Application developers are encouraged to improve upon and contribute to module structure and development design. {synopsis} - -## Structure - -A typical Cosmos SDK module can be structured as follows: - -```shell -proto -└── {project_name} -    └── {module_name} -    └── {proto_version} -       ├── {module_name}.proto -       ├── event.proto -       ├── genesis.proto -       ├── query.proto -       └── tx.proto -``` - -* `{module_name}.proto`: The module's common message type definitions. -* `event.proto`: The module's message type definitions related to events. -* `genesis.proto`: The module's message type definitions related to genesis state. -* `query.proto`: The module's Query service and related message type definitions. -* `tx.proto`: The module's Msg service and related message type definitions. - -```shell -x/{module_name} -├── client -│   ├── cli -│   │ ├── query.go -│   │   └── tx.go -│   └── testutil -│   ├── cli_test.go -│   └── suite.go -├── exported -│   └── exported.go -├── keeper -│   ├── genesis.go -│   ├── grpc_query.go -│   ├── hooks.go -│   ├── invariants.go -│   ├── keeper.go -│   ├── keys.go -│   ├── msg_server.go -│   └── querier.go -├── module -│   └── module.go -├── simulation -│   ├── decoder.go -│   ├── genesis.go -│   ├── operations.go -│   └── params.go -├── spec -│   ├── 01_concepts.md -│   ├── 02_state.md -│   ├── 03_messages.md -│   └── 04_events.md -├── {module_name}.pb.go -├── abci.go -├── codec.go -├── errors.go -├── events.go -├── events.pb.go -├── expected_keepers.go -├── genesis.go -├── genesis.pb.go -├── keys.go -├── msgs.go -├── params.go -├── query.pb.go -└── tx.pb.go -``` - -* `client/`: The module's CLI client functionality implementation and the module's integration testing suite. -* `exported/`: The module's exported types - typically interface types. If a module relies on keepers from another module, it is expected to receive the keepers as interface contracts through the `expected_keepers.go` file (see below) in order to avoid a direct dependency on the module implementing the keepers. However, these interface contracts can define methods that operate on and/or return types that are specific to the module that is implementing the keepers and this is where `exported/` comes into play. The interface types that are defined in `exported/` use canonical types, allowing for the module to receive the keepers as interface contracts through the `expected_keepers.go` file. This pattern allows for code to remain DRY and also alleviates import cycle chaos. -* `keeper/`: The module's `Keeper` and `MsgServer` implementation. -* `module/`: The module's `AppModule` and `AppModuleBasic` implementation. -* `simulation/`: The module's [simulation](./simulator.html) package defines functions used by the blockchain simulator application (`simapp`). -* `spec/`: The module's specification documents outlining important concepts, state storage structure, and message and event type definitions. -* The root directory includes type definitions for messages, events, and genesis state, including the type definitions generated by Protocol Buffers. - * `abci.go`: The module's `BeginBlocker` and `EndBlocker` implementations (this file is only required if `BeginBlocker` and/or `EndBlocker` need to be defined). - * `codec.go`: The module's registry methods for interface types. - * `errors.go`: The module's sentinel errors. - * `events.go`: The module's event types and constructors. - * `expected_keepers.go`: The module's [expected keeper](./keeper.html#type-definition) interfaces. - * `genesis.go`: The module's genesis state methods and helper functions. - * `keys.go`: The module's store keys and associated helper functions. - * `msgs.go`: The module's message type definitions and associated methods. - * `params.go`: The module's parameter type definitions and associated methods. - * `*.pb.go`: The module's type definitions generated by Protocol Buffers (as defined in the respective `*.proto` files above). - -## Next {hide} - -Learn about [Errors](./12-errors.md) {hide} diff --git a/versioned_docs/version-0.46/integrate/building-modules/12-errors.md b/versioned_docs/version-0.46/integrate/building-modules/12-errors.md deleted file mode 100644 index 62d7a572c..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/12-errors.md +++ /dev/null @@ -1,46 +0,0 @@ -# Errors - -This document outlines the recommended usage and APIs for error handling in Cosmos SDK modules. {synopsis} - -Modules are encouraged to define and register their own errors to provide better -context on failed message or handler execution. Typically, these errors should be -common or general errors which can be further wrapped to provide additional specific -execution context. - -## Registration - -Modules should define and register their custom errors in `x/{module}/errors.go`. -Registration of errors is handled via the [`errors` package](https://github.com/cosmos/cosmos-sdk/blob/main/errors/errors.go). - -Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/distribution/types/errors.go#L1-L21 - -Each custom module error must provide the codespace, which is typically the module name -(e.g. "distribution") and is unique per module, and a uint32 code. Together, the codespace and code -provide a globally unique Cosmos SDK error. Typically, the code is monotonically increasing but does not -necessarily have to be. The only restrictions on error codes are the following: - -* Must be greater than one, as a code value of one is reserved for internal errors. -* Must be unique within the module. - -Note, the Cosmos SDK provides a core set of *common* errors. These errors are defined in [`types/errors/errors.go`](https://github.com/cosmos/cosmos-sdk/blob/main/types/errors/errors.go). - -## Wrapping - -The custom module errors can be returned as their concrete type as they already fulfill the `error` -interface. However, module errors can be wrapped to provide further context and meaning to failed -execution. - -Example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/keeper/keeper.go#L143-L184 - -Regardless if an error is wrapped or not, the Cosmos SDK's `errors` package provides a function to determine if -an error is of a particular kind via `Is`. - -## ABCI - -If a module error is registered, the Cosmos SDK `errors` package allows ABCI information to be extracted -through the `ABCIInfo` function. The package also provides `ResponseCheckTx` and `ResponseDeliverTx` as -auxiliary functions to automatically get `CheckTx` and `DeliverTx` responses from an error. diff --git a/versioned_docs/version-0.46/integrate/building-modules/13-upgrade.md b/versioned_docs/version-0.46/integrate/building-modules/13-upgrade.md deleted file mode 100644 index 4f2ef3e0e..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/13-upgrade.md +++ /dev/null @@ -1,53 +0,0 @@ -# Upgrading Modules - -[In-Place Store Migrations](../advanced-concepts/13-upgrade.md) allow your modules to upgrade to new versions that include breaking changes. This document outlines how to build modules to take advantage of this functionality. {synopsis} - -## Prerequisite Readings - -* [In-Place Store Migration](../advanced-concepts/13-upgrade.md) {prereq} - -## Consensus Version - -Successful upgrades of existing modules require each `AppModule` to implement the function `ConsensusVersion() uint64`. - -* The versions must be hard-coded by the module developer. -* The initial version **must** be set to 1. - -Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes. - -## Registering Migrations - -To register the functionality that takes place during a module upgrade, you must register which migrations you want to take place. - -Migration registration takes place in the `Configurator` using the `RegisterMigration` method. The `AppModule` reference to the configurator is in the `RegisterServices` method. - -You can register one or more migrations. If you register more than one migration script, list the migrations in increasing order and ensure there are enough migrations that lead to the desired consensus version. For example, to migrate to version 3 of a module, register separate migrations for version 1 and version 2 as shown in the following example: - -```golang -func (am AppModule) RegisterServices(cfg module.Configurator) { - // --snip-- - cfg.RegisterMigration(types.ModuleName, 1, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 1 to 2. - }) - cfg.RegisterMigration(types.ModuleName, 2, func(ctx sdk.Context) error { - // Perform in-place store migrations from ConsensusVersion 2 to 3. - }) -} -``` - -Since these migrations are functions that need access to a Keeper's store, use a wrapper around the keepers called `Migrator` as shown in this example: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/keeper/migrations.go#L9-L27 - -## Writing Migration Scripts - -To define the functionality that takes place during an upgrade, write a migration script and place the functions in a `migrations/` directory. For example, to write migration scripts for the bank module, place the functions in `x/bank/migrations/`. Use the recommended naming convention for these functions. For example, `v043bank` is the script that migrates the package `x/bank/migrations/v043`: - -```golang -// Migrating bank module from version 1 to 2 -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v043bank.MigrateStore(ctx, m.keeper.storeKey) // v043bank is package `x/bank/migrations/v043`. -} -``` - -To see example code of changes that were implemented in a migration of balance keys, check out [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/migrations/v043/store.go#L50-L71). For context, this code introduced migrations of the bank store that updated addresses to be prefixed by their length in bytes as outlined in [ADR-028](../architecture/adr-028-public-key-addresses.md). diff --git a/versioned_docs/version-0.46/integrate/building-modules/14-simulator.md b/versioned_docs/version-0.46/integrate/building-modules/14-simulator.md deleted file mode 100644 index 0ed80a148..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/14-simulator.md +++ /dev/null @@ -1,122 +0,0 @@ -# Module Simulation - -## Prerequisites - -* [Cosmos Blockchain Simulator](./../using-the-sdk/simulation.md) - -## Synopsis - -This document details how to define each module simulation functions to be -integrated with the application `SimulationManager`. - -* [Simulation package](#simulation-package) - * [Store decoders](#store-decoders) - * [Randomized genesis](#randomized-genesis) - * [Randomized parameter changes](#randomized-parameter-changes) - * [Random weighted operations](#random-weighted-operations) - * [Random proposal contents](#random-proposal-contents) -* [Registering simulation functions](#registering-simulation-functions) -* [App Simulator manager](#app-simulator-manager) - -## Simulation package - -Every module that implements the Cosmos SDK simulator needs to have a `x//simulation` -package which contains the primary functions required by the fuzz tests: store -decoders, randomized genesis state and parameters, weighted operations and proposal -contents. - -### Store decoders - -Registering the store decoders is required for the `AppImportExport`. This allows -for the key-value pairs from the stores to be decoded (_i.e_ unmarshalled) -to their corresponding types. In particular, it matches the key to a concrete type -and then unmarshals the value from the `KVPair` to the type provided. - -You can use the example [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/distribution/simulation/decoder.go) from the distribution module to implement your store decoders. - -### Randomized genesis - -The simulator tests different scenarios and values for genesis parameters -in order to fully test the edge cases of specific modules. The `simulator` package from each module must expose a `RandomizedGenState` function to generate the initial random `GenesisState` from a given seed. - -Once the module genesis parameter are generated randomly (or with the key and -values defined in a `params` file), they are marshaled to JSON format and added -to the app genesis JSON to use it on the simulations. - -You can check an example on how to create the randomized genesis [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/simulation/genesis.go). - -### Randomized parameter changes - -The simulator is able to test parameter changes at random. The simulator package from each module must contain a `RandomizedParams` func that will simulate parameter changes of the module throughout the simulations lifespan. - -You can see how an example of what is needed to fully test parameter changes [here](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/simulation/params.go) - -### Random weighted operations - -Operations are one of the crucial parts of the Cosmos SDK simulation. They are the transactions -(`Msg`) that are simulated with random field values. The sender of the operation -is also assigned randomly. - -Operations on the simulation are simulated using the full [transaction cycle](../advanced-concepts/01-transactions.md) of a -`ABCI` application that exposes the `BaseApp`. - -Shown below is how weights are set: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/simulation/operations.go#L17-L75 - -As you can see, the weights are predefined in this case. Options exist to override this behavior with different weights. One option is to use `*rand.Rand` to define a random weight for the operation, or you can inject your own predefined weights. - -Here is how one can override the above package `simappparams`. - -+++ https://github.com/cosmos/gaia/blob/main/sims.mk#L9-L22 - -For the last test a tool called runsim is used, this is used to parallelize go test instances, provide info to Github and slack integrations to provide information to your team on how the simulations are running. - -### Random proposal contents - -Randomized governance proposals are also supported on the Cosmos SDK simulator. Each -module must define the governance proposal `Content`s that they expose and register -them to be used on the parameters. - -## Registering simulation functions - -Now that all the required functions are defined, we need to integrate them into the module pattern within the `module.go`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/distribution/module.go - -## App Simulator manager - -The following step is setting up the `SimulatorManager` at the app level. This -is required for the simulation test files on the next step. - -```go -type CustomApp struct { - ... - sm *module.SimulationManager -} -``` - -Then at the instantiation of the application, we create the `SimulationManager` -instance in the same way we create the `ModuleManager` but this time we only pass -the modules that implement the simulation functions from the `AppModuleSimulation` -interface described above. - -```go -func NewCustomApp(...) { - // create the simulation manager and define the order of the modules for deterministic simulations - app.sm = module.NewSimulationManager( - auth.NewAppModule(app.accountKeeper), - bank.NewAppModule(app.bankKeeper, app.accountKeeper), - supply.NewAppModule(app.supplyKeeper, app.accountKeeper), - ov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper), - mint.NewAppModule(app.mintKeeper), - distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper), - staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper), - slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper), - ) - - // register the store decoders for simulation tests - app.sm.RegisterStoreDecoders() - ... -} -``` diff --git a/versioned_docs/version-0.46/integrate/building-modules/_category_.json b/versioned_docs/version-0.46/integrate/building-modules/_category_.json deleted file mode 100644 index 17bc3fc57..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Building Modules", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/building-modules/transaction_flow.svg b/versioned_docs/version-0.46/integrate/building-modules/transaction_flow.svg deleted file mode 100644 index 1ae962de3..000000000 --- a/versioned_docs/version-0.46/integrate/building-modules/transaction_flow.svg +++ /dev/null @@ -1,48 +0,0 @@ -UserUserbaseAppbaseApprouterrouterhandlerhandlermsgServermsgServerkeeperkeeperContext.EventManagerContext.EventManagerTransaction Type<Tx>Route(ctx, msgRoute)handlerMsg<Tx>(Context, Msg(...))<Tx>(Context, Msg)alt[addresses invalid, denominations wrong, etc.]errorperform action, update contextresults, error codeEmit relevant eventsmaybe wrap results in more structureresult, error coderesults, error code \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/ibc/README.md b/versioned_docs/version-0.46/integrate/ibc/README.md deleted file mode 100644 index 872e5c0dc..000000000 --- a/versioned_docs/version-0.46/integrate/ibc/README.md +++ /dev/null @@ -1,9 +0,0 @@ - - -# IBC - -See the official [`ibc-go` documentation](https://ibc.cosmos.network). diff --git a/versioned_docs/version-0.46/integrate/ibc/_category_.json b/versioned_docs/version-0.46/integrate/ibc/_category_.json deleted file mode 100644 index 5241a91e8..000000000 --- a/versioned_docs/version-0.46/integrate/ibc/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "IBC", - "position": 3, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/migrations/00-pre-upgrade.md b/versioned_docs/version-0.46/integrate/migrations/00-pre-upgrade.md deleted file mode 100644 index 00b4c54e2..000000000 --- a/versioned_docs/version-0.46/integrate/migrations/00-pre-upgrade.md +++ /dev/null @@ -1,55 +0,0 @@ -# Pre-Upgrade Handling - -Cosmovisor supports custom pre-upgrade handling. Use pre-upgrade handling when you need to implement application config changes that are required in the newer version before you perform the upgrade. - -Using Cosmovisor pre-upgrade handling is optional. If pre-upgrade handling is not implemented, the upgrade continues. - -For example, make the required new-version changes to `app.toml` settings during the pre-upgrade handling. The pre-upgrade handling process means that the file does not have to be manually updated after the upgrade. - -Before the application binary is upgraded, Cosmovisor calls a `pre-upgrade` command that can be implemented by the application. - -The `pre-upgrade` command does not take in any command-line arguments and is expected to terminate with the following exit codes: - -| Exit status code | How it is handled in Cosmosvisor | -|------------------|---------------------------------------------------------------------------------------------------------------------| -| `0` | Assumes `pre-upgrade` command executed successfully and continues the upgrade. | -| `1` | Default exit code when `pre-upgrade` command has not been implemented. | -| `30` | `pre-upgrade` command was executed but failed. This fails the entire upgrade. | -| `31` | `pre-upgrade` command was executed but failed. But the command is retried until exit code `1` or `30` are returned. | - -## Sample - -Here is a sample structure of the `pre-upgrade` command: - -```go -func preUpgradeCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "pre-upgrade", - Short: "Pre-upgrade command", - Long: "Pre-upgrade command to implement custom pre-upgrade handling", - Run: func(cmd *cobra.Command, args []string) { - - err := HandlePreUpgrade() - - if err != nil { - os.Exit(30) - } - - os.Exit(0) - - }, - } - - return cmd -} -``` - -Ensure that the pre-upgrade command has been registered in the application: - -```go -rootCmd.AddCommand( - // .. - preUpgradeCommand(), - // .. - ) -``` diff --git a/versioned_docs/version-0.46/integrate/migrations/_category_.json b/versioned_docs/version-0.46/integrate/migrations/_category_.json deleted file mode 100644 index e28fa8a0c..000000000 --- a/versioned_docs/version-0.46/integrate/migrations/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Migrations", - "position": 5, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/_category_.json b/versioned_docs/version-0.46/integrate/modules/_category_.json deleted file mode 100644 index 136eee1a4..000000000 --- a/versioned_docs/version-0.46/integrate/modules/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Modules", - "position": 1, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/auth/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/auth/01_concepts.md deleted file mode 100644 index a0475f058..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/01_concepts.md +++ /dev/null @@ -1,39 +0,0 @@ -# Concepts - -**Note:** The auth module is different from the [authz module](../modules/authz/). - -The differences are: - -* `auth` - authentication of accounts and transactions for Cosmos SDK applications and is responsible for specifying the base transaction and account types. -* `authz` - authorization for accounts to perform actions on behalf of other accounts and enables a granter to grant authorizations to a grantee that allows the grantee to execute messages on behalf of the granter. - -## Gas & Fees - -Fees serve two purposes for an operator of the network. - -Fees limit the growth of the state stored by every full node and allow for -general purpose censorship of transactions of little economic value. Fees -are best suited as an anti-spam mechanism where validators are disinterested in -the use of the network and identities of users. - -Fees are determined by the gas limits and gas prices transactions provide, where -`fees = ceil(gasLimit * gasPrices)`. Txs incur gas costs for all state reads/writes, -signature verification, as well as costs proportional to the tx size. Operators -should set minimum gas prices when starting their nodes. They must set the unit -costs of gas in each token denomination they wish to support: - -`simd start ... --minimum-gas-prices=0.00001stake;0.05photinos` - -When adding transactions to mempool or gossipping transactions, validators check -if the transaction's gas prices, which are determined by the provided fees, meet -any of the validator's minimum gas prices. In other words, a transaction must -provide a fee of at least one denomination that matches a validator's minimum -gas price. - -Tendermint does not currently provide fee based mempool prioritization, and fee -based mempool filtering is local to node and not part of consensus. But with -minimum gas prices set, such a mechanism could be implemented by node operators. - -Because the market value for tokens will fluctuate, validators are expected to -dynamically adjust their minimum gas prices to a level that would encourage the -use of the network. diff --git a/versioned_docs/version-0.46/integrate/modules/auth/02_state.md b/versioned_docs/version-0.46/integrate/modules/auth/02_state.md deleted file mode 100644 index 8853e9ba9..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/02_state.md +++ /dev/null @@ -1,69 +0,0 @@ -# State - -## Accounts - -Accounts contain authentication information for a uniquely identified external user of an SDK blockchain, -including public key, address, and account number / sequence number for replay protection. For efficiency, -since account balances must also be fetched to pay fees, account structs also store the balance of a user -as `sdk.Coins`. - -Accounts are exposed externally as an interface, and stored internally as -either a base account or vesting account. Module clients wishing to add more -account types may do so. - -* `0x01 | Address -> ProtocolBuffer(account)` - -### Account Interface - -The account interface exposes methods to read and write standard account information. -Note that all of these methods operate on an account struct confirming to the -interface - in order to write the account to the store, the account keeper will -need to be used. - -```go -// AccountI is an interface used to store coins at a given address within state. -// It presumes a notion of sequence numbers for replay protection, -// a notion of account numbers for replay protection for previously pruned accounts, -// and a pubkey for authentication purposes. -// -// Many complex conditions can be used in the concrete struct which implements AccountI. -type AccountI interface { - proto.Message - - GetAddress() sdk.AccAddress - SetAddress(sdk.AccAddress) error // errors if already set. - - GetPubKey() crypto.PubKey // can return nil. - SetPubKey(crypto.PubKey) error - - GetAccountNumber() uint64 - SetAccountNumber(uint64) error - - GetSequence() uint64 - SetSequence(uint64) error - - // Ensure that account implements stringer - String() string -} -``` - -#### Base Account - -A base account is the simplest and most common account type, which just stores all requisite -fields directly in a struct. - -```protobuf -// BaseAccount defines a base account type. It contains all the necessary fields -// for basic account functionality. Any custom account type should extend this -// type for additional functionality (e.g. vesting). -message BaseAccount { - string address = 1; - google.protobuf.Any pub_key = 2; - uint64 account_number = 3; - uint64 sequence = 4; -} -``` - -### Vesting Account - -See [Vesting](05_vesting.md). diff --git a/versioned_docs/version-0.46/integrate/modules/auth/03_antehandlers.md b/versioned_docs/version-0.46/integrate/modules/auth/03_antehandlers.md deleted file mode 100644 index 7e10af878..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/03_antehandlers.md +++ /dev/null @@ -1,36 +0,0 @@ -# AnteHandlers - -The `x/auth` module presently has no transaction handlers of its own, but does expose the special `AnteHandler`, used for performing basic validity checks on a transaction, such that it could be thrown out of the mempool. -The `AnteHandler` can be seen as a set of decorators that check transactions within the current context, per [ADR 010](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-010-modular-antehandler.md). - -Note that the `AnteHandler` is called on both `CheckTx` and `DeliverTx`, as Tendermint proposers presently have the ability to include in their proposed block transactions which fail `CheckTx`. - -## Decorators - -The auth module provides `AnteDecorator`s that are recursively chained together into a single `AnteHandler` in the following order: - -* `SetUpContextDecorator`: Sets the `GasMeter` in the `Context` and wraps the next `AnteHandler` with a defer clause to recover from any downstream `OutOfGas` panics in the `AnteHandler` chain to return an error with information on gas provided and gas used. - -* `RejectExtensionOptionsDecorator`: Rejects all extension options which can optionally be included in protobuf transactions. - -* `MempoolFeeDecorator`: Checks if the `tx` fee is above local mempool `minFee` parameter during `CheckTx`. - -* `ValidateBasicDecorator`: Calls `tx.ValidateBasic` and returns any non-nil error. - -* `TxTimeoutHeightDecorator`: Check for a `tx` height timeout. - -* `ValidateMemoDecorator`: Validates `tx` memo with application parameters and returns any non-nil error. - -* `ConsumeGasTxSizeDecorator`: Consumes gas proportional to the `tx` size based on application parameters. - -* `DeductFeeDecorator`: Deducts the `FeeAmount` from first signer of the `tx`. If the `x/feegrant` module is enabled and a fee granter is set, it deducts fees from the fee granter account. - -* `SetPubKeyDecorator`: Sets the pubkey from a `tx`'s signers that does not already have its corresponding pubkey saved in the state machine and in the current context. - -* `ValidateSigCountDecorator`: Validates the number of signatures in `tx` based on app-parameters. - -* `SigGasConsumeDecorator`: Consumes parameter-defined amount of gas for each signature. This requires pubkeys to be set in context for all signers as part of `SetPubKeyDecorator`. - -* `SigVerificationDecorator`: Verifies all signatures are valid. This requires pubkeys to be set in context for all signers as part of `SetPubKeyDecorator`. - -* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks. diff --git a/versioned_docs/version-0.46/integrate/modules/auth/04_keepers.md b/versioned_docs/version-0.46/integrate/modules/auth/04_keepers.md deleted file mode 100644 index 130d3a6ea..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/04_keepers.md +++ /dev/null @@ -1,43 +0,0 @@ -# Keepers - -The auth module only exposes one keeper, the account keeper, which can be used to read and write accounts. - -## Account Keeper - -Presently only one fully-permissioned account keeper is exposed, which has the ability to both read and write -all fields of all accounts, and to iterate over all stored accounts. - -```go -// AccountKeeperI is the interface contract that x/auth's keeper implements. -type AccountKeeperI interface { - // Return a new account with the next account number and the specified address. Does not save the new account to the store. - NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI - - // Return a new account with the next account number. Does not save the new account to the store. - NewAccount(sdk.Context, types.AccountI) types.AccountI - - // Check if an account exists in the store. - HasAccount(sdk.Context, sdk.AccAddress) bool - - // Retrieve an account from the store. - GetAccount(sdk.Context, sdk.AccAddress) types.AccountI - - // Set an account in the store. - SetAccount(sdk.Context, types.AccountI) - - // Remove an account from the store. - RemoveAccount(sdk.Context, types.AccountI) - - // Iterate over all accounts, calling the provided function. Stop iteration when it returns true. - IterateAccounts(sdk.Context, func(types.AccountI) bool) - - // Fetch the public key of an account at a specified address - GetPubKey(sdk.Context, sdk.AccAddress) (crypto.PubKey, error) - - // Fetch the sequence of an account at a specified address. - GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) - - // Fetch the next account number, and increment the internal counter. - GetNextAccountNumber(sdk.Context) uint64 -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/auth/05_vesting.md b/versioned_docs/version-0.46/integrate/modules/auth/05_vesting.md deleted file mode 100644 index 38c294577..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/05_vesting.md +++ /dev/null @@ -1,626 +0,0 @@ -# Vesting - -* [Vesting](#vesting) - * [Intro and Requirements](#intro-and-requirements) - * [Note](#note) - * [Vesting Account Types](#vesting-account-types) - * [BaseVestingAccount](#basevestingaccount) - * [ContinuousVestingAccount](#continuousvestingaccount) - * [DelayedVestingAccount](#delayedvestingaccount) - * [Period](#period) - * [PeriodicVestingAccount](#periodicvestingaccount) - * [PermanentLockedAccount](#permanentlockedaccount) - * [Vesting Account Specification](#vesting-account-specification) - * [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts) - * [Continuously Vesting Accounts](#continuously-vesting-accounts) - * [Periodic Vesting Accounts](#periodic-vesting-accounts) - * [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts) - * [Transferring/Sending](#transferringsending) - * [Keepers/Handlers](#keepershandlers) - * [Delegating](#delegating) - * [Keepers/Handlers](#keepershandlers-1) - * [Undelegating](#undelegating) - * [Keepers/Handlers](#keepershandlers-2) - * [Keepers & Handlers](#keepers--handlers) - * [Genesis Initialization](#genesis-initialization) - * [Examples](#examples) - * [Simple](#simple) - * [Slashing](#slashing) - * [Periodic Vesting](#periodic-vesting) - * [Glossary](#glossary) - -## Intro and Requirements - -This specification defines the vesting account implementation that is used by -the Cosmos Hub. The requirements for this vesting account is that it should be -initialized during genesis with a starting balance `X` and a vesting end -time `ET`. A vesting account may be initialized with a vesting start time `ST` -and a number of vesting periods `P`. If a vesting start time is included, the -vesting period does not begin until start time is reached. If vesting periods -are included, the vesting occurs over the specified number of periods. - -For all vesting accounts, the owner of the vesting account is able to delegate -and undelegate from validators, however they cannot transfer coins to another -account until those coins are vested. This specification allows for four -different kinds of vesting: - -* Delayed vesting, where all coins are vested once `ET` is reached. -* Continous vesting, where coins begin to vest at `ST` and vest linearly with -respect to time until `ET` is reached -* Periodic vesting, where coins begin to vest at `ST` and vest periodically -according to number of periods and the vesting amount per period. -The number of periods, length per period, and amount per period are -configurable. A periodic vesting account is distinguished from a continuous -vesting account in that coins can be released in staggered tranches. For -example, a periodic vesting account could be used for vesting arrangements -where coins are relased quarterly, yearly, or over any other function of -tokens over time. -* Permanent locked vesting, where coins are locked forever. Coins in this account can -still be used for delegating and for governance votes even while locked. - -## Note - -Vesting accounts can be initialized with some vesting and non-vesting coins. -The non-vesting coins would be immediately transferable. DelayedVesting and -ContinuousVesting accounts can be created with normal messages after genesis. -Other types of vesting accounts must be created at genesis, or as -part of a manual network upgrade. The current specification only allows -for _unconditional_ vesting (ie. there is no possibility of reaching `ET` and -having coins fail to vest). - -## Vesting Account Types - -```go -// VestingAccount defines an interface that any vesting account type must -// implement. -type VestingAccount interface { - Account - - GetVestedCoins(Time) Coins - GetVestingCoins(Time) Coins - - // TrackDelegation performs internal vesting accounting necessary when - // delegating from a vesting account. It accepts the current block time, the - // delegation amount and balance of all coins whose denomination exists in - // the account's original vesting balance. - TrackDelegation(Time, Coins, Coins) - - // TrackUndelegation performs internal vesting accounting necessary when a - // vesting account performs an undelegation. - TrackUndelegation(Coins) - - GetStartTime() int64 - GetEndTime() int64 -} -``` - -### BaseVestingAccount - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L10-L24 - -### ContinuousVestingAccount - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L26-L34 - -### DelayedVestingAccount - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L36-L44 - -### Period - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L46-L53 - -```go -// Stores all vesting periods passed as part of a PeriodicVestingAccount -type Periods []Period - -``` - -### PeriodicVestingAccount - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L55-L64 - -In order to facilitate less ad-hoc type checking and assertions and to support -flexibility in account balance usage, the existing `x/bank` `ViewKeeper` interface -is updated to contain the following: - -```go -type ViewKeeper interface { - // ... - - // Calculates the total locked account balance. - LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - - // Calculates the total spendable balance that can be sent to other accounts. - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins -} -``` - -### PermanentLockedAccount - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L55-L64 - -## Vesting Account Specification - -Given a vesting account, we define the following in the proceeding operations: - -* `OV`: The original vesting coin amount. It is a constant value. -* `V`: The number of `OV` coins that are still _vesting_. It is derived by -`OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a -per-block basis. -* `V'`: The number of `OV` coins that are _vested_ (unlocked). This value is -computed on demand and not a per-block basis. -* `DV`: The number of delegated _vesting_ coins. It is a variable value. It is -stored and modified directly in the vesting account. -* `DF`: The number of delegated _vested_ (unlocked) coins. It is a variable -value. It is stored and modified directly in the vesting account. -* `BC`: The number of `OV` coins less any coins that are transferred -(which can be negative or delegated). It is considered to be balance of the -embedded base account. It is stored and modified directly in the vesting account. - -### Determining Vesting & Vested Amounts - -It is important to note that these values are computed on demand and not on a -mandatory per-block basis (e.g. `BeginBlocker` or `EndBlocker`). - -#### Continuously Vesting Accounts - -To determine the amount of coins that are vested for a given block time `T`, the -following is performed: - -1. Compute `X := T - StartTime` -2. Compute `Y := EndTime - StartTime` -3. Compute `V' := OV * (X / Y)` -4. Compute `V := OV - V'` - -Thus, the total amount of _vested_ coins is `V'` and the remaining amount, `V`, -is _vesting_. - -```go -func (cva ContinuousVestingAccount) GetVestedCoins(t Time) Coins { - if t <= cva.StartTime { - // We must handle the case where the start time for a vesting account has - // been set into the future or when the start of the chain is not exactly - // known. - return ZeroCoins - } else if t >= cva.EndTime { - return cva.OriginalVesting - } - - x := t - cva.StartTime - y := cva.EndTime - cva.StartTime - - return cva.OriginalVesting * (x / y) -} - -func (cva ContinuousVestingAccount) GetVestingCoins(t Time) Coins { - return cva.OriginalVesting - cva.GetVestedCoins(t) -} -``` - -### Periodic Vesting Accounts - -Periodic vesting accounts require calculating the coins released during each -period for a given block time `T`. Note that multiple periods could have passed -when calling `GetVestedCoins`, so we must iterate over each period until the -end of that period is after `T`. - -1. Set `CT := StartTime` -2. Set `V' := 0` - -For each Period P: - - 1. Compute `X := T - CT` - 2. IF `X >= P.Length` - 1. Compute `V' += P.Amount` - 2. Compute `CT += P.Length` - 3. ELSE break - 3. Compute `V := OV - V'` - -```go -func (pva PeriodicVestingAccount) GetVestedCoins(t Time) Coins { - if t < pva.StartTime { - return ZeroCoins - } - ct := pva.StartTime // The start of the vesting schedule - vested := 0 - periods = pva.GetPeriods() - for _, period := range periods { - if t - ct < period.Length { - break - } - vested += period.Amount - ct += period.Length // increment ct to the start of the next vesting period - } - return vested -} - -func (pva PeriodicVestingAccount) GetVestingCoins(t Time) Coins { - return pva.OriginalVesting - cva.GetVestedCoins(t) -} -``` - -#### Delayed/Discrete Vesting Accounts - -Delayed vesting accounts are easier to reason about as they only have the full -amount vesting up until a certain time, then all the coins become vested (unlocked). -This does not include any unlocked coins the account may have initially. - -```go -func (dva DelayedVestingAccount) GetVestedCoins(t Time) Coins { - if t >= dva.EndTime { - return dva.OriginalVesting - } - - return ZeroCoins -} - -func (dva DelayedVestingAccount) GetVestingCoins(t Time) Coins { - return dva.OriginalVesting - dva.GetVestedCoins(t) -} -``` - -### Transferring/Sending - -At any given time, a vesting account may transfer: `min((BC + DV) - V, BC)`. - -In other words, a vesting account may transfer the minimum of the base account -balance and the base account balance plus the number of currently delegated -vesting coins less the number of coins vested so far. - -However, given that account balances are tracked via the `x/bank` module and that -we want to avoid loading the entire account balance, we can instead determine -the locked balance, which can be defined as `max(V - DV, 0)`, and infer the -spendable balance from that. - -```go -func (va VestingAccount) LockedCoins(t Time) Coins { - return max(va.GetVestingCoins(t) - va.DelegatedVesting, 0) -} -``` - -The `x/bank` `ViewKeeper` can then provide APIs to determine locked and spendable -coins for any account: - -```go -func (k Keeper) LockedCoins(ctx Context, addr AccAddress) Coins { - acc := k.GetAccount(ctx, addr) - if acc != nil { - if acc.IsVesting() { - return acc.LockedCoins(ctx.BlockTime()) - } - } - - // non-vesting accounts do not have any locked coins - return NewCoins() -} -``` - -#### Keepers/Handlers - -The corresponding `x/bank` keeper should appropriately handle sending coins -based on if the account is a vesting account or not. - -```go -func (k Keeper) SendCoins(ctx Context, from Account, to Account, amount Coins) { - bc := k.GetBalances(ctx, from) - v := k.LockedCoins(ctx, from) - - spendable := bc - v - newCoins := spendable - amount - assert(newCoins >= 0) - - from.SetBalance(newCoins) - to.AddBalance(amount) - - // save balances... -} -``` - -### Delegating - -For a vesting account attempting to delegate `D` coins, the following is performed: - -1. Verify `BC >= D > 0` -2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting) -3. Compute `Y := D - X` (portion of `D` that is free) -4. Set `DV += X` -5. Set `DF += Y` - -```go -func (va VestingAccount) TrackDelegation(t Time, balance Coins, amount Coins) { - assert(balance <= amount) - x := min(max(va.GetVestingCoins(t) - va.DelegatedVesting, 0), amount) - y := amount - x - - va.DelegatedVesting += x - va.DelegatedFree += y -} -``` - -**Note** `TrackDelegation` only modifies the `DelegatedVesting` and `DelegatedFree` -fields, so upstream callers MUST modify the `Coins` field by subtracting `amount`. - -#### Keepers/Handlers - -```go -func DelegateCoins(t Time, from Account, amount Coins) { - if isVesting(from) { - from.TrackDelegation(t, amount) - } else { - from.SetBalance(sc - amount) - } - - // save account... -} -``` - -### Undelegating - -For a vesting account attempting to undelegate `D` coins, the following is performed: -NOTE: `DV < D` and `(DV + DF) < D` may be possible due to quirks in the rounding of -delegation/undelegation logic. - -1. Verify `D > 0` -2. Compute `X := min(DF, D)` (portion of `D` that should become free, prioritizing free coins) -3. Compute `Y := min(DV, D - X)` (portion of `D` that should remain vesting) -4. Set `DF -= X` -5. Set `DV -= Y` - -```go -func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) { - x := min(cva.DelegatedFree, amount) - y := amount - x - - cva.DelegatedFree -= x - cva.DelegatedVesting -= y -} -``` - -**Note** `TrackUnDelegation` only modifies the `DelegatedVesting` and `DelegatedFree` -fields, so upstream callers MUST modify the `Coins` field by adding `amount`. - -**Note**: If a delegation is slashed, the continuous vesting account ends up -with an excess `DV` amount, even after all its coins have vested. This is because -undelegating free coins are prioritized. - -**Note**: The undelegation (bond refund) amount may exceed the delegated -vesting (bond) amount due to the way undelegation truncates the bond refund, -which can increase the validator's exchange rate (tokens/shares) slightly if the -undelegated tokens are non-integral. - -#### Keepers/Handlers - -```go -func UndelegateCoins(to Account, amount Coins) { - if isVesting(to) { - if to.DelegatedFree + to.DelegatedVesting >= amount { - to.TrackUndelegation(amount) - // save account ... - } - } else { - AddBalance(to, amount) - // save account... - } -} -``` - -## Keepers & Handlers - -The `VestingAccount` implementations reside in `x/auth`. However, any keeper in -a module (e.g. staking in `x/staking`) wishing to potentially utilize any vesting -coins, must call explicit methods on the `x/bank` keeper (e.g. `DelegateCoins`) -opposed to `SendCoins` and `SubtractCoins`. - -In addition, the vesting account should also be able to spend any coins it -receives from other users. Thus, the bank module's `MsgSend` handler should -error if a vesting account is trying to send an amount that exceeds their -unlocked coin amount. - -See the above specification for full implementation details. - -## Genesis Initialization - -To initialize both vesting and non-vesting accounts, the `GenesisAccount` struct -includes new fields: `Vesting`, `StartTime`, and `EndTime`. Accounts meant to be -of type `BaseAccount` or any non-vesting type have `Vesting = false`. The -genesis initialization logic (e.g. `initFromGenesisState`) must parse -and return the correct accounts accordingly based off of these fields. - -```go -type GenesisAccount struct { - // ... - - // vesting account fields - OriginalVesting sdk.Coins `json:"original_vesting"` - DelegatedFree sdk.Coins `json:"delegated_free"` - DelegatedVesting sdk.Coins `json:"delegated_vesting"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` -} - -func ToAccount(gacc GenesisAccount) Account { - bacc := NewBaseAccount(gacc) - - if gacc.OriginalVesting > 0 { - if ga.StartTime != 0 && ga.EndTime != 0 { - // return a continuous vesting account - } else if ga.EndTime != 0 { - // return a delayed vesting account - } else { - // invalid genesis vesting account provided - panic() - } - } - - return bacc -} -``` - -## Examples - -### Simple - -Given a continuous vesting account with 10 vesting coins. - -```text -OV = 10 -DF = 0 -DV = 0 -BC = 10 -V = 10 -V' = 0 -``` - -1. Immediately receives 1 coin - - ```text - BC = 11 - ``` - -2. Time passes, 2 coins vest - - ```text - V = 8 - V' = 2 - ``` - -3. Delegates 4 coins to validator A - - ```text - DV = 4 - BC = 7 - ``` - -4. Sends 3 coins - - ```text - BC = 4 - ``` - -5. More time passes, 2 more coins vest - - ```text - V = 6 - V' = 4 - ``` - -6. Sends 2 coins. At this point the account cannot send anymore until further -coins vest or it receives additional coins. It can still however, delegate. - - ```text - BC = 2 - ``` - -### Slashing - -Same initial starting conditions as the simple example. - -1. Time passes, 5 coins vest - - ```text - V = 5 - V' = 5 - ``` - -2. Delegate 5 coins to validator A - - ```text - DV = 5 - BC = 5 - ``` - -3. Delegate 5 coins to validator B - - ```text - DF = 5 - BC = 0 - ``` - -4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins -5. Undelegate from validator A (2.5 coins) - - ```text - DF = 5 - 2.5 = 2.5 - BC = 0 + 2.5 = 2.5 - ``` - -6. Undelegate from validator B (5 coins). The account at this point can only -send 2.5 coins unless it receives more coins or until more coins vest. -It can still however, delegate. - - ```text - DV = 5 - 2.5 = 2.5 - DF = 2.5 - 2.5 = 0 - BC = 2.5 + 5 = 7.5 - ``` - - Notice how we have an excess amount of `DV`. - -### Periodic Vesting - -A vesting account is created where 100 tokens will be released over 1 year, with -1/4 of tokens vesting each quarter. The vesting schedule would be as follows: - -```yaml -Periods: -- amount: 25stake, length: 7884000 -- amount: 25stake, length: 7884000 -- amount: 25stake, length: 7884000 -- amount: 25stake, length: 7884000 -``` - -```text -OV = 100 -DF = 0 -DV = 0 -BC = 100 -V = 100 -V' = 0 -``` - -1. Immediately receives 1 coin - - ```text - BC = 101 - ``` - -2. Vesting period 1 passes, 25 coins vest - - ```text - V = 75 - V' = 25 - ``` - -3. During vesting period 2, 5 coins are transfered and 5 coins are delegated - - ```text - DV = 5 - BC = 91 - ``` - -4. Vesting period 2 passes, 25 coins vest - - ```text - V = 50 - V' = 50 - ``` - -## Glossary - -* OriginalVesting: The amount of coins (per denomination) that are initially -part of a vesting account. These coins are set at genesis. -* StartTime: The BFT time at which a vesting account starts to vest. -* EndTime: The BFT time at which a vesting account is fully vested. -* DelegatedFree: The tracked amount of coins (per denomination) that are -delegated from a vesting account that have been fully vested at time of delegation. -* DelegatedVesting: The tracked amount of coins (per denomination) that are -delegated from a vesting account that were vesting at time of delegation. -* ContinuousVestingAccount: A vesting account implementation that vests coins -linearly over time. -* DelayedVestingAccount: A vesting account implementation that only fully vests -all coins at a given time. -* PeriodicVestingAccount: A vesting account implementation that vests coins -according to a custom vesting schedule. -* PermanentLockedAccount: It does not ever release coins, locking them indefinitely. -Coins in this account can still be used for delegating and for governance votes even while locked. diff --git a/versioned_docs/version-0.46/integrate/modules/auth/06_params.md b/versioned_docs/version-0.46/integrate/modules/auth/06_params.md deleted file mode 100644 index 1250aaa31..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/06_params.md +++ /dev/null @@ -1,11 +0,0 @@ -# Parameters - -The auth module contains the following parameters: - -| Key | Type | Example | -| ---------------------- | --------------- | ------- | -| MaxMemoCharacters | uint64 | 256 | -| TxSigLimit | uint64 | 7 | -| TxSizeCostPerByte | uint64 | 10 | -| SigVerifyCostED25519 | uint64 | 590 | -| SigVerifyCostSecp256k1 | uint64 | 1000 | diff --git a/versioned_docs/version-0.46/integrate/modules/auth/07_client.md b/versioned_docs/version-0.46/integrate/modules/auth/07_client.md deleted file mode 100644 index 0e0129be5..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/07_client.md +++ /dev/null @@ -1,417 +0,0 @@ -# Client - -# Auth - -## CLI - -A user can query and interact with the `auth` module using the CLI. - -### Query - -The `query` commands allow users to query `auth` state. - -```bash -simd query auth --help -``` - -#### account - -The `account` command allow users to query for an account by it's address. - -```bash -simd query auth account [address] [flags] -``` - -Example: - -```bash -simd query auth account cosmos1... -``` - -Example Output: - -```bash -'@type': /cosmos.auth.v1beta1.BaseAccount -account_number: "0" -address: cosmos1zwg6tpl8aw4rawv8sgag9086lpw5hv33u5ctr2 -pub_key: - '@type': /cosmos.crypto.secp256k1.PubKey - key: ApDrE38zZdd7wLmFS9YmqO684y5DG6fjZ4rVeihF/AQD -sequence: "1" -``` - -#### accounts - -The `accounts` command allow users to query all the available accounts. - -```bash -simd query auth accounts [flags] -``` - -Example: - -```bash -simd query auth accounts -``` - -Example Output: - -```bash -accounts: -- '@type': /cosmos.auth.v1beta1.BaseAccount - account_number: "0" - address: cosmos1zwg6tpl8aw4rawv8sgag9086lpw5hv33u5ctr2 - pub_key: - '@type': /cosmos.crypto.secp256k1.PubKey - key: ApDrE38zZdd7wLmFS9YmqO684y5DG6fjZ4rVeihF/AQD - sequence: "1" -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "8" - address: cosmos1yl6hdjhmkf37639730gffanpzndzdpmhwlkfhr - pub_key: null - sequence: "0" - name: transfer - permissions: - - minter - - burner -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "4" - address: cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh - pub_key: null - sequence: "0" - name: bonded_tokens_pool - permissions: - - burner - - staking -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "5" - address: cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r - pub_key: null - sequence: "0" - name: not_bonded_tokens_pool - permissions: - - burner - - staking -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "6" - address: cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn - pub_key: null - sequence: "0" - name: gov - permissions: - - burner -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "3" - address: cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl - pub_key: null - sequence: "0" - name: distribution - permissions: [] -- '@type': /cosmos.auth.v1beta1.BaseAccount - account_number: "1" - address: cosmos147k3r7v2tvwqhcmaxcfql7j8rmkrlsemxshd3j - pub_key: null - sequence: "0" -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "7" - address: cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q - pub_key: null - sequence: "0" - name: mint - permissions: - - minter -- '@type': /cosmos.auth.v1beta1.ModuleAccount - base_account: - account_number: "2" - address: cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta - pub_key: null - sequence: "0" - name: fee_collector - permissions: [] -pagination: - next_key: null - total: "0" -``` - -#### params - -The `params` command allow users to query the current auth parameters. - -```bash -simd query auth params [flags] -``` - -Example: - -```bash -simd query auth params -``` - -Example Output: - -```bash -max_memo_characters: "256" -sig_verify_cost_ed25519: "590" -sig_verify_cost_secp256k1: "1000" -tx_sig_limit: "7" -tx_size_cost_per_byte: "10" -``` - -## gRPC - -A user can query the `auth` module using gRPC endpoints. - -### Account - -The `account` endpoint allow users to query for an account by it's address. - -```bash -cosmos.auth.v1beta1.Query/Account -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"address":"cosmos1.."}' \ - localhost:9090 \ - cosmos.auth.v1beta1.Query/Account -``` - -Example Output: - -```bash -{ - "account":{ - "@type":"/cosmos.auth.v1beta1.BaseAccount", - "address":"cosmos1zwg6tpl8aw4rawv8sgag9086lpw5hv33u5ctr2", - "pubKey":{ - "@type":"/cosmos.crypto.secp256k1.PubKey", - "key":"ApDrE38zZdd7wLmFS9YmqO684y5DG6fjZ4rVeihF/AQD" - }, - "sequence":"1" - } -} -``` - -### Accounts - -The `accounts` endpoint allow users to query all the available accounts. - -```bash -cosmos.auth.v1beta1.Query/Accounts -``` - -Example: - -```bash -grpcurl -plaintext \ - localhost:9090 \ - cosmos.auth.v1beta1.Query/Accounts -``` - -Example Output: - -```bash -{ - "accounts":[ - { - "@type":"/cosmos.auth.v1beta1.BaseAccount", - "address":"cosmos1zwg6tpl8aw4rawv8sgag9086lpw5hv33u5ctr2", - "pubKey":{ - "@type":"/cosmos.crypto.secp256k1.PubKey", - "key":"ApDrE38zZdd7wLmFS9YmqO684y5DG6fjZ4rVeihF/AQD" - }, - "sequence":"1" - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos1yl6hdjhmkf37639730gffanpzndzdpmhwlkfhr", - "accountNumber":"8" - }, - "name":"transfer", - "permissions":[ - "minter", - "burner" - ] - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", - "accountNumber":"4" - }, - "name":"bonded_tokens_pool", - "permissions":[ - "burner", - "staking" - ] - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r", - "accountNumber":"5" - }, - "name":"not_bonded_tokens_pool", - "permissions":[ - "burner", - "staking" - ] - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", - "accountNumber":"6" - }, - "name":"gov", - "permissions":[ - "burner" - ] - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl", - "accountNumber":"3" - }, - "name":"distribution" - }, - { - "@type":"/cosmos.auth.v1beta1.BaseAccount", - "accountNumber":"1", - "address":"cosmos147k3r7v2tvwqhcmaxcfql7j8rmkrlsemxshd3j" - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", - "accountNumber":"7" - }, - "name":"mint", - "permissions":[ - "minter" - ] - }, - { - "@type":"/cosmos.auth.v1beta1.ModuleAccount", - "baseAccount":{ - "address":"cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta", - "accountNumber":"2" - }, - "name":"fee_collector" - } - ], - "pagination":{ - "total":"9" - } -} -``` - -### Params - -The `params` endpoint allow users to query the current auth parameters. - -```bash -cosmos.auth.v1beta1.Query/Params -``` - -Example: - -```bash -grpcurl -plaintext \ - localhost:9090 \ - cosmos.auth.v1beta1.Query/Params -``` - -Example Output: - -```bash -{ - "params": { - "maxMemoCharacters": "256", - "txSigLimit": "7", - "txSizeCostPerByte": "10", - "sigVerifyCostEd25519": "590", - "sigVerifyCostSecp256k1": "1000" - } -} -``` - -## REST - -A user can query the `auth` module using REST endpoints. - -### Account - -The `account` endpoint allow users to query for an account by it's address. - -```bash -/cosmos/auth/v1beta1/account?address={address} -``` - -### Accounts - -The `accounts` endpoint allow users to query all the available accounts. - -```bash -/cosmos/auth/v1beta1/accounts -``` - -### Params - -The `params` endpoint allow users to query the current auth parameters. - -```bash -/cosmos/auth/v1beta1/params -``` - -# Vesting - -## CLI - -A user can query and interact with the `vesting` module using the CLI. - -### Transactions - -The `tx` commands allow users to interact with the `vesting` module. - -```bash -simd tx vesting --help -``` - -#### create-periodic-vesting-account - -The `create-periodic-vesting-account` command creates a new vesting account funded with an allocation of tokens, where a sequence of coins and period length in seconds. Periods are sequential, in that the duration of of a period only starts at the end of the previous period. The duration of the first period starts upon account creation. - -```bash -simd tx vesting create-periodic-vesting-account [to_address] [periods_json_file] [flags] -``` - -Example: - -```bash -simd tx vesting create-periodic-vesting-account cosmos1.. periods.json -``` - -#### create-vesting-account - -The `create-vesting-account` command creates a new vesting account funded with an allocation of tokens. The account can either be a delayed or continuous vesting account, which is determined by the '--delayed' flag. All vesting accouts created will have their start time set by the committed block's time. The end_time must be provided as a UNIX epoch timestamp. - -```bash -simd tx vesting create-vesting-account [to_address] [amount] [end_time] [flags] -``` - -Example: - -```bash -simd tx vesting create-vesting-account cosmos1.. 100stake 2592000 -``` diff --git a/versioned_docs/version-0.46/integrate/modules/auth/README.md b/versioned_docs/version-0.46/integrate/modules/auth/README.md deleted file mode 100644 index 1090bb6ba..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/README.md +++ /dev/null @@ -1,45 +0,0 @@ - - -# `auth` - -## Abstract - -This document specifies the auth module of the Cosmos SDK. - -The auth module is responsible for specifying the base transaction and account types -for an application, since the SDK itself is agnostic to these particulars. It contains -the middlewares, where all basic transaction validity checks (signatures, nonces, auxiliary fields) -are performed, and exposes the account keeper, which allows other modules to read, write, and modify accounts. - -This module is used in the Cosmos Hub. - -## Contents - -1. **[Concepts](01_concepts.md)** - * [Gas & Fees](01_concepts.md#gas-&-fees) -2. **[State](02_state.md)** - * [Accounts](02_state.md#accounts) -3. **[AnteHandlers](03_antehandlers.md)** -4. **[Keepers](04_keepers.md)** - * [Account Keeper](04_keepers.md#account-keeper) -5. **[Vesting](05_vesting.md)** - * [Intro and Requirements](05_vesting.md#intro-and-requirements) - * [Vesting Account Types](05_vesting.md#vesting-account-types) - * [Vesting Account Specification](05_vesting.md#vesting-account-specification) - * [Keepers & Handlers](05_vesting.md#keepers-&-handlers) - * [Genesis Initialization](05_vesting.md#genesis-initialization) - * [Examples](05_vesting.md#examples) - * [Glossary](05_vesting.md#glossary) -6. **[Parameters](06_params.md)** -7. **[Client](07_client.md)** - * **[Auth](07_client.md#auth)** - * [CLI](07_client.md#cli) - * [gRPC](07_client.md#grpc) - * [REST](07_client.md#rest) - * **[Vesting](07_client.md#vesting)** - * [CLI](07_client.md#vesting#cli) diff --git a/versioned_docs/version-0.46/integrate/modules/auth/_category_.json b/versioned_docs/version-0.46/integrate/modules/auth/_category_.json deleted file mode 100644 index f8162812b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/auth/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Auth", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/authz/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/authz/01_concepts.md deleted file mode 100644 index 9259c3b17..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/01_concepts.md +++ /dev/null @@ -1,51 +0,0 @@ -# Concepts - -## Authorization and Grant - -The `x/authz` module defines interfaces and messages grant authorizations to perform actions -on behalf of one account to other accounts. The design is defined in the [ADR 030](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-030-authz-module.md). - -A *grant* is an allowance to execute a Msg by the grantee on behalf of the granter. -Authorization is an interface that must be implemented by a concrete authorization logic to validate and execute grants. Authorizations are extensible and can be defined for any Msg service method even outside of the module where the Msg method is defined. See the `SendAuthorization` example in the next section for more details. - -**Note:** The authz module is different from the [auth (authentication)](../modules/auth/) module that is responsible for specifying the base transaction and account types. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/authz/authorizations.go#L11-L25 - -## Built-in Authorizations - -The Cosmos SDK `x/authz` module comes with following authorization types: - -### GenericAuthorization - -`GenericAuthorization` implements the `Authorization` interface that gives unrestricted permission to execute the provided Msg on behalf of granter's account. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/authz/v1beta1/authz.proto#L13-L20 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/authz/generic_authorization.go#L16-L29 - -* `msg` stores Msg type URL. - -### SendAuthorization - -`SendAuthorization` implements the `Authorization` interface for the `cosmos.bank.v1beta1.MsgSend` Msg. It takes a (positive) `SpendLimit` that specifies the maximum amount of tokens the grantee can spend. The `SpendLimit` is updated as the tokens are spent. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/bank/v1beta1/authz.proto#L10-L19 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/bank/types/send_authorization.go#L23-L38 - -* `spend_limit` keeps track of how many coins are left in the authorization. - -### StakeAuthorization - -`StakeAuthorization` implements the `Authorization` interface for messages in the [staking module](https://docs.cosmos.network/v0.44/modules/staking/). It takes an `AuthorizationType` to specify whether you want to authorise delegating, undelegating or redelegating (i.e. these have to be authorised seperately). It also takes a required `MaxTokens` that keeps track of a limit to the amount of tokens that can be delegated/undelegated/redelegated. If left empty, the amount is unlimited. Additionally, this Msg takes an `AllowList` or a `DenyList`, which allows you to select which validators you allow or deny grantees to stake with. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/authz.proto#L10-L33 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/staking/types/authz.go#L15-L35 - -## Gas - -In order to prevent DoS attacks, granting `StakeAuthorization`s with `x/authz` incurs gas. `StakeAuthorization` allows you to authorize another account to delegate, undelegate, or redelegate to validators. The authorizer can define a list of validators they allow or deny delegations to. The Cosmos SDK iterates over these lists and charge 10 gas for each validator in both of the lists. - -Since the state maintaining a list for granter, grantee pair with same expiration, we are iterating over the list to remove the grant (incase of any revoke of paritcular `msgType`) from the list and we are charging 20 gas per iteration. diff --git a/versioned_docs/version-0.46/integrate/modules/authz/02_state.md b/versioned_docs/version-0.46/integrate/modules/authz/02_state.md deleted file mode 100644 index 24552b626..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/02_state.md +++ /dev/null @@ -1,19 +0,0 @@ -# State - -## Grant - -Grants are identified by combining granter address (the address bytes of the granter), grantee address (the address bytes of the grantee) and Authorization type (its type URL). Hence we only allow one grant for the (granter, grantee, Authorization) triple. - -* Grant: `0x01 | granter_address_len (1 byte) | granter_address_bytes | grantee_address_len (1 byte) | grantee_address_bytes | msgType_bytes-> ProtocolBuffer(AuthorizationGrant)` - -The grant object encapsulates an `Authorization` type and an expiration timestamp: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/authz/v1beta1/authz.proto#L22-L30 - -## GrantQueue - -We are maintaining a queue for authz pruning, whenever a grant created an item will be added to `GrantQueue` with a key of granter, grantee, expiration and value added as array of msg type urls. - -* GrantQueue: `0x02 | granter_address_len (1 byte) | granter_address_bytes | grantee_address_len (1 byte) | grantee_address_bytes | expiration_bytes -> ProtocalBuffer([]string{msgTypeUrls})` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/authz/keeper/keys.go#L78-L93 diff --git a/versioned_docs/version-0.46/integrate/modules/authz/03_messages.md b/versioned_docs/version-0.46/integrate/modules/authz/03_messages.md deleted file mode 100644 index e9203168f..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/03_messages.md +++ /dev/null @@ -1,42 +0,0 @@ -# Messages - -In this section we describe the processing of messages for the authz module. - -## MsgGrant - -An authorization grant is created using the `MsgGrant` message. -If there is already a grant for the `(granter, grantee, Authorization)` triple, then the new grant overwrites the previous one. To update or extend an existing grant, a new grant with the same `(granter, grantee, Authorization)` triple should be created. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/authz/v1beta1/tx.proto#L32-L41 - -The message handling should fail if: - -* both granter and grantee have the same address. -* provided `Expiration` time is less than current unix timestamp (but a grant will be created if no `expiration` time is provided since `expiration` is optional). -* provided `Grant.Authorization` is not implemented. -* `Authorization.MsgTypeURL()` is not defined in the router (there is no defined handler in the app router to handle that Msg types). - -## MsgRevoke - -A grant can be removed with the `MsgRevoke` message. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/authz/v1beta1/tx.proto#L66-L72 - -The message handling should fail if: - -* both granter and grantee have the same address. -* provided `MsgTypeUrl` is empty. - -NOTE: The `MsgExec` message removes a grant if the grant has expired. - -## MsgExec - -When a grantee wants to execute a transaction on behalf of a granter, they must send `MsgExec`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/authz/v1beta1/tx.proto#L51-L59 - -The message handling should fail if: - -* provided `Authorization` is not implemented. -* grantee doesn't have permission to run the transaction. -* if granted authorization is expired. diff --git a/versioned_docs/version-0.46/integrate/modules/authz/04_events.md b/versioned_docs/version-0.46/integrate/modules/authz/04_events.md deleted file mode 100644 index b5fd09752..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/04_events.md +++ /dev/null @@ -1,3 +0,0 @@ -# Events - -The authz module emits proto events defined in [the Protobuf reference](https://buf.build/cosmos/cosmos-sdk/docs/main/cosmos.authz.v1beta1#cosmos.authz.v1beta1.EventGrant). diff --git a/versioned_docs/version-0.46/integrate/modules/authz/05_client.md b/versioned_docs/version-0.46/integrate/modules/authz/05_client.md deleted file mode 100644 index 5b27e58e8..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/05_client.md +++ /dev/null @@ -1,168 +0,0 @@ -# Client - -## CLI - -A user can query and interact with the `authz` module using the CLI. - -### Query - -The `query` commands allow users to query `authz` state. - -```bash -simd query authz --help -``` - -#### grants - -The `grants` command allows users to query grants for a granter-grantee pair. If the message type URL is set, it selects grants only for that message type. - -```bash -simd query authz grants [granter-addr] [grantee-addr] [msg-type-url]? [flags] -``` - -Example: - -```bash -simd query authz grants cosmos1.. cosmos1.. /cosmos.bank.v1beta1.MsgSend -``` - -Example Output: - -```bash -grants: -- authorization: - '@type': /cosmos.bank.v1beta1.SendAuthorization - spend_limit: - - amount: "100" - denom: stake - expiration: "2022-01-01T00:00:00Z" -pagination: null -``` - -### Transactions - -The `tx` commands allow users to interact with the `authz` module. - -```bash -simd tx authz --help -``` - -#### exec - -The `exec` command allows a grantee to execute a transaction on behalf of granter. - -```bash - simd tx authz exec [tx-json-file] --from [grantee] [flags] -``` - -Example: - -```bash -simd tx authz exec tx.json --from=cosmos1.. -``` - -#### grant - -The `grant` command allows a granter to grant an authorization to a grantee. - -```bash -simd tx authz grant --from [flags] -``` - -Example: - -```bash -simd tx authz grant cosmos1.. send --spend-limit=100stake --from=cosmos1.. -``` - -#### revoke - -The `revoke` command allows a granter to revoke an authorization from a grantee. - -```bash -simd tx authz revoke [grantee] [msg-type-url] --from=[granter] [flags] -``` - -Example: - -```bash -simd tx authz revoke cosmos1.. /cosmos.bank.v1beta1.MsgSend --from=cosmos1.. -``` - -## gRPC - -A user can query the `authz` module using gRPC endpoints. - -### Grants - -The `Grants` endpoint allows users to query grants for a granter-grantee pair. If the message type URL is set, it selects grants only for that message type. - -```bash -cosmos.authz.v1beta1.Query/Grants -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"granter":"cosmos1..","grantee":"cosmos1..","msg_type_url":"/cosmos.bank.v1beta1.MsgSend"}' \ - localhost:9090 \ - cosmos.authz.v1beta1.Query/Grants -``` - -Example Output: - -```bash -{ - "grants": [ - { - "authorization": { - "@type": "/cosmos.bank.v1beta1.SendAuthorization", - "spendLimit": [ - { - "denom":"stake", - "amount":"100" - } - ] - }, - "expiration": "2022-01-01T00:00:00Z" - } - ] -} -``` - -## REST - -A user can query the `authz` module using REST endpoints. - -```bash -/cosmos/authz/v1beta1/grants -``` - -Example: - -```bash -curl "localhost:1317/cosmos/authz/v1beta1/grants?granter=cosmos1..&grantee=cosmos1..&msg_type_url=/cosmos.bank.v1beta1.MsgSend" -``` - -Example Output: - -```bash -{ - "grants": [ - { - "authorization": { - "@type": "/cosmos.bank.v1beta1.SendAuthorization", - "spend_limit": [ - { - "denom": "stake", - "amount": "100" - } - ] - }, - "expiration": "2022-01-01T00:00:00Z" - } - ], - "pagination": null -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/authz/README.md b/versioned_docs/version-0.46/integrate/modules/authz/README.md deleted file mode 100644 index ac73d76db..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/README.md +++ /dev/null @@ -1,30 +0,0 @@ - - -# `authz` - -## Contents - -## Abstract - -`x/authz` is an implementation of a Cosmos SDK module, per [ADR 30](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-030-authz-module.md), that allows -granting arbitrary privileges from one account (the granter) to another account (the grantee). Authorizations must be granted for a particular Msg service method one by one using an implementation of the `Authorization` interface. - -1. **[Concept](01_concepts.md)** - * [Authorization and Grant](01_concepts.md#Authorization-and-Grant) - * [Built-in Authorizations](01_concepts.md#Built-in-Authorizations) - * [Gas](01_concepts.md#gas) -2. **[State](02_state.md)** -3. **[Messages](03_messages.md)** - * [MsgGrant](03_messages.md#MsgGrant) - * [MsgRevoke](03_messages.md#MsgRevoke) - * [MsgExec](03_messages.md#MsgExec) -4. **[Events](04_events.md)** -5. **[Client](05_client.md)** - * [CLI](05_client.md#cli) - * [gRPC](05_client.md#grpc) - * [REST](05_client.md#rest) diff --git a/versioned_docs/version-0.46/integrate/modules/authz/_category_.json b/versioned_docs/version-0.46/integrate/modules/authz/_category_.json deleted file mode 100644 index d77e44faf..000000000 --- a/versioned_docs/version-0.46/integrate/modules/authz/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Authz", - "position": 1, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/bank/01_state.md b/versioned_docs/version-0.46/integrate/modules/bank/01_state.md deleted file mode 100644 index f24725242..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/01_state.md +++ /dev/null @@ -1,19 +0,0 @@ - - -# State - -The `x/bank` module keeps state of three primary objects: - -1. Account balances -2. Denomination metadata -3. The total supply of all balances - -In addition, the `x/bank` module keeps the following indexes to manage the -aforementioned state: - -* Supply Index: `0x0 | byte(denom) -> byte(amount)` -* Denom Metadata Index: `0x1 | byte(denom) -> ProtocolBuffer(Metadata)` -* Balances Index: `0x2 | byte(address length) | []byte(address) | []byte(balance.Denom) -> ProtocolBuffer(balance)` -* Reverse Denomination to Address Index: `0x03 | byte(denom) | 0x00 | []byte(address) -> 0` diff --git a/versioned_docs/version-0.46/integrate/modules/bank/02_keepers.md b/versioned_docs/version-0.46/integrate/modules/bank/02_keepers.md deleted file mode 100644 index 71d35942b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/02_keepers.md +++ /dev/null @@ -1,135 +0,0 @@ - - -# Keepers - -The bank module provides these exported keeper interfaces that can be -passed to other modules that read or update account balances. Modules -should use the least-permissive interface that provides the functionality they -require. - -Best practices dictate careful review of `bank` module code to ensure that -permissions are limited in the way that you expect. - -## Blocklisting Addresses - -The `x/bank` module accepts a map of addresses that are considered blocklisted -from directly and explicitly receiving funds through means such as `MsgSend` and -`MsgMultiSend` and direct API calls like `SendCoinsFromModuleToAccount`. - -Typically, these addresses are module accounts. If these addresses receive funds -outside the expected rules of the state machine, invariants are likely to be -broken and could result in a halted network. - -By providing the `x/bank` module with a blocklisted set of addresses, an error occurs for the operation if a user or client attempts to directly or indirectly send funds to a blocklisted account, for example, by using [IBC](https://ibc.cosmos.network). - -## Common Types - -### Input - -An input of a multiparty transfer - -```protobuf -// Input models transaction input. -message Input { - string address = 1; - repeated cosmos.base.v1beta1.Coin coins = 2; -} -``` - -### Output - -An output of a multiparty transfer. - -```protobuf -// Output models transaction outputs. -message Output { - string address = 1; - repeated cosmos.base.v1beta1.Coin coins = 2; -} -``` - -## BaseKeeper - -The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins. - -Restricted permission to mint per module could be achieved by using baseKeeper with `WithMintCoinsRestriction` to give specific restrictions to mint (e.g. only minting certain denom). - -```go -// Keeper defines a module interface that facilitates the transfer of coins -// between accounts. -type Keeper interface { - SendKeeper - WithMintCoinsRestriction(NewRestrictionFn BankMintingRestrictionFn) BaseKeeper - - InitGenesis(sdk.Context, *types.GenesisState) - ExportGenesis(sdk.Context) *types.GenesisState - - GetSupply(ctx sdk.Context, denom string) sdk.Coin - GetPaginatedTotalSupply(ctx sdk.Context, pagination *query.PageRequest) (sdk.Coins, *query.PageResponse, error) - IterateTotalSupply(ctx sdk.Context, cb func(sdk.Coin) bool) - GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) - SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) - IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) - - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - - DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error - UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error - - types.QueryServer -} -``` - -## SendKeeper - -The send keeper provides access to account balances and the ability to transfer coins between -accounts. The send keeper does not alter the total supply (mint or burn coins). - -```go -// SendKeeper defines a module interface that facilitates the transfer of coins -// between accounts without the possibility of creating coins. -type SendKeeper interface { - ViewKeeper - - InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - - GetParams(ctx sdk.Context) types.Params - SetParams(ctx sdk.Context, params types.Params) - - IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool - IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error - - BlockedAddr(addr sdk.AccAddress) bool -} -``` - -## ViewKeeper - -The view keeper provides read-only access to account balances. The view keeper does not have balance alteration functionality. All balance lookups are `O(1)`. - -```go -// ViewKeeper defines a module interface that facilitates read only access to -// account balances. -type ViewKeeper interface { - ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error - HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool - - GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - GetAccountsBalances(ctx sdk.Context) []types.Balance - GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - - IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) - IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/bank/03_messages.md b/versioned_docs/version-0.46/integrate/modules/bank/03_messages.md deleted file mode 100644 index a6d237945..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/03_messages.md +++ /dev/null @@ -1,28 +0,0 @@ - - -# Messages - -## MsgSend - -Send coins from one address to another. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L21-L32 - -The message will fail under the following conditions: - -* The coins do not have sending enabled -* The `to` address is restricted - -## MsgMultiSend - -Send coins from and to a series of different address. If any of the receiving addresses do not correspond to an existing account, a new account is created. -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L37-L45 - -The message will fail under the following conditions: - -* Any of the coins do not have sending enabled -* Any of the `to` addresses are restricted -* Any of the coins are locked -* The inputs and outputs do not correctly correspond to one another diff --git a/versioned_docs/version-0.46/integrate/modules/bank/04_events.md b/versioned_docs/version-0.46/integrate/modules/bank/04_events.md deleted file mode 100644 index e71b82057..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/04_events.md +++ /dev/null @@ -1,149 +0,0 @@ - - -# Events - -The bank module emits the following events: - -## Handlers - -### MsgSend - -| Type | Attribute Key | Attribute Value | -| -------- | ------------- | ------------------ | -| transfer | recipient | {recipientAddress} | -| transfer | amount | {amount} | -| message | module | bank | -| message | action | send | -| message | sender | {senderAddress} | - -### MsgMultiSend - -| Type | Attribute Key | Attribute Value | -| -------- | ------------- | ------------------ | -| transfer | recipient | {recipientAddress} | -| transfer | amount | {amount} | -| message | module | bank | -| message | action | multisend | -| message | sender | {senderAddress} | - -## Keeper events - -In addition to handlers events, the bank keeper will produce events when the following methods are called (or any method which ends up calling them) - -### MintCoins - -```json -{ - "type": "coinbase", - "attributes": [ - { - "key": "minter", - "value": "{{sdk.AccAddress of the module minting coins}}", - "index": true - }, - { - "key": "amount", - "value": "{{sdk.Coins being minted}}", - "index": true - } - ] -} -``` - -```json -{ - "type": "coin_received", - "attributes": [ - { - "key": "receiver", - "value": "{{sdk.AccAddress of the module minting coins}}", - "index": true - }, - { - "key": "amount", - "value": "{{sdk.Coins being received}}", - "index": true - } - ] -} -``` - -### BurnCoins - -```json -{ - "type": "burn", - "attributes": [ - { - "key": "burner", - "value": "{{sdk.AccAddress of the module burning coins}}", - "index": true - }, - { - "key": "amount", - "value": "{{sdk.Coins being burned}}", - "index": true - } - ] -} -``` - -```json -{ - "type": "coin_spent", - "attributes": [ - { - "key": "spender", - "value": "{{sdk.AccAddress of the module burning coins}}", - "index": true - }, - { - "key": "amount", - "value": "{{sdk.Coins being burned}}", - "index": true - } - ] -} -``` - -### addCoins - -```json -{ - "type": "coin_received", - "attributes": [ - { - "key": "receiver", - "value": "{{sdk.AccAddress of the address beneficiary of the coins}}", - "index": true - }, - { - "key": "amount", - "value": "{{sdk.Coins being received}}", - "index": true - } - ] -} -``` - -### subUnlockedCoins/DelegateCoins - -```json -{ - "type": "coin_spent", - "attributes": [ - { - "key": "spender", - "value": "{{sdk.AccAddress of the address which is spending coins}}", - "index": true - }, - { - "key": "amount", - "value": "{{sdk.Coins being spent}}", - "index": true - } - ] -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/bank/05_params.md b/versioned_docs/version-0.46/integrate/modules/bank/05_params.md deleted file mode 100644 index 8d254243c..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/05_params.md +++ /dev/null @@ -1,24 +0,0 @@ - - -# Parameters - -The bank module contains the following parameters: - -| Key | Type | Example | -| ------------------ | ------------- | ---------------------------------- | -| SendEnabled | []SendEnabled | [{denom: "stake", enabled: true }] | -| DefaultSendEnabled | bool | true | - -## SendEnabled - -The send enabled parameter is an array of SendEnabled entries mapping coin -denominations to their send_enabled status. Entries in this list take -precedence over the `DefaultSendEnabled` setting. - -## DefaultSendEnabled - -The default send enabled value controls send transfer capability for all -coin denominations unless specifically included in the array of `SendEnabled` -parameters. diff --git a/versioned_docs/version-0.46/integrate/modules/bank/06_client.md b/versioned_docs/version-0.46/integrate/modules/bank/06_client.md deleted file mode 100644 index 2377b0d92..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/06_client.md +++ /dev/null @@ -1,390 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `bank` module using the CLI. - -### Query - -The `query` commands allow users to query `bank` state. - -```sh -simd query bank --help -``` - -#### balances - -The `balances` command allows users to query account balances by address. - -```sh -simd query bank balances [address] [flags] -``` - -Example: - -```sh -simd query bank balances cosmos1.. -``` - -Example Output: - -```yml -balances: -- amount: "1000000000" - denom: stake -pagination: - next_key: null - total: "0" -``` - -#### denom-metadata - -The `denom-metadata` command allows users to query metadata for coin denominations. A user can query metadata for a single denomination using the `--denom` flag or all denominations without it. - -```sh -simd query bank denom-metadata [flags] -``` - -Example: - -```sh -simd query bank denom-metadata --denom stake -``` - -Example Output: - -```yml -metadata: - base: stake - denom_units: - - aliases: - - STAKE - denom: stake - description: native staking token of simulation app - display: stake - name: SimApp Token - symbol: STK -``` - -#### total - -The `total` command allows users to query the total supply of coins. A user can query the total supply for a single coin using the `--denom` flag or all coins without it. - -```sh -simd query bank total [flags] -``` - -Example: - -```sh -simd query bank total --denom stake -``` - -Example Output: - -```yml -amount: "10000000000" -denom: stake -``` - -### Transactions - -The `tx` commands allow users to interact with the `bank` module. - -```sh -simd tx bank --help -``` - -#### send - -The `send` command allows users to send funds from one account to another. - -```sh -simd tx bank send [from_key_or_address] [to_address] [amount] [flags] -``` - -Example: - -```sh -simd tx bank send cosmos1.. cosmos1.. 100stake -``` - -## gRPC - -A user can query the `bank` module using gRPC endpoints. - -### Balance - -The `Balance` endpoint allows users to query account balance by address for a given denomination. - -```sh -cosmos.bank.v1beta1.Query/Balance -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"address":"cosmos1..","denom":"stake"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/Balance -``` - -Example Output: - -```json -{ - "balance": { - "denom": "stake", - "amount": "1000000000" - } -} -``` - -### AllBalances - -The `AllBalances` endpoint allows users to query account balance by address for all denominations. - -```sh -cosmos.bank.v1beta1.Query/AllBalances -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"address":"cosmos1.."}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/AllBalances -``` - -Example Output: - -```json -{ - "balances": [ - { - "denom": "stake", - "amount": "1000000000" - } - ], - "pagination": { - "total": "1" - } -} -``` - -### DenomMetadata - -The `DenomMetadata` endpoint allows users to query metadata for a single coin denomination. - -```sh -cosmos.bank.v1beta1.Query/DenomMetadata -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"denom":"stake"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/DenomMetadata -``` - -Example Output: - -```json -{ - "metadata": { - "description": "native staking token of simulation app", - "denomUnits": [ - { - "denom": "stake", - "aliases": [ - "STAKE" - ] - } - ], - "base": "stake", - "display": "stake", - "name": "SimApp Token", - "symbol": "STK" - } -} -``` - -### DenomsMetadata - -The `DenomsMetadata` endpoint allows users to query metadata for all coin denominations. - -```sh -cosmos.bank.v1beta1.Query/DenomsMetadata -``` - -Example: - -```sh -grpcurl -plaintext \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/DenomsMetadata -``` - -Example Output: - -```json -{ - "metadatas": [ - { - "description": "native staking token of simulation app", - "denomUnits": [ - { - "denom": "stake", - "aliases": [ - "STAKE" - ] - } - ], - "base": "stake", - "display": "stake", - "name": "SimApp Token", - "symbol": "STK" - } - ], - "pagination": { - "total": "1" - } -} -``` - -### DenomOwners - -The `DenomOwners` endpoint allows users to query metadata for a single coin denomination. - -```sh -cosmos.bank.v1beta1.Query/DenomOwners -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"denom":"stake"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/DenomOwners -``` - -Example Output: - -```json -{ - "denomOwners": [ - { - "address": "cosmos1..", - "balance": { - "denom": "stake", - "amount": "5000000000" - } - }, - { - "address": "cosmos1..", - "balance": { - "denom": "stake", - "amount": "5000000000" - } - }, - ], - "pagination": { - "total": "2" - } -} -``` - -### TotalSupply - -The `TotalSupply` endpoint allows users to query the total supply of all coins. - -```sh -cosmos.bank.v1beta1.Query/TotalSupply -``` - -Example: - -```sh -grpcurl -plaintext \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/TotalSupply -``` - -Example Output: - -```json -{ - "supply": [ - { - "denom": "stake", - "amount": "10000000000" - } - ], - "pagination": { - "total": "1" - } -} -``` - -### SupplyOf - -The `SupplyOf` endpoint allows users to query the total supply of a single coin. - -```sh -cosmos.bank.v1beta1.Query/SupplyOf -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"denom":"stake"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/SupplyOf -``` - -Example Output: - -```json -{ - "amount": { - "denom": "stake", - "amount": "10000000000" - } -} -``` - -### Params - -The `Params` endpoint allows users to query the parameters of the `bank` module. - -```sh -cosmos.bank.v1beta1.Query/Params -``` - -Example: - -```sh -grpcurl -plaintext \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/Params -``` - -Example Output: - -```json -{ - "params": { - "defaultSendEnabled": true - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/bank/README.md b/versioned_docs/version-0.46/integrate/modules/bank/README.md deleted file mode 100644 index ec5d1df2f..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/README.md +++ /dev/null @@ -1,106 +0,0 @@ - - -# `x/bank` - -## Abstract - -This document specifies the bank module of the Cosmos SDK. - -The bank module is responsible for handling multi-asset coin transfers between -accounts and tracking special-case pseudo-transfers which must work differently -with particular kinds of accounts (notably delegating/undelegating for vesting -accounts). It exposes several interfaces with varying capabilities for secure -interaction with other modules which must alter user balances. - -In addition, the bank module tracks and provides query support for the total -supply of all assets used in the application. - -This module will be used in the Cosmos Hub. - -## Supply - -The `supply` functionality: - -* passively tracks the total supply of coins within a chain, -* provides a pattern for modules to hold/interact with `Coins`, and -* introduces the invariant check to verify a chain's total supply. - -### Total Supply - -The total `Supply` of the network is equal to the sum of all coins from the -account. The total supply is updated every time a `Coin` is minted (eg: as part -of the inflation mechanism) or burned (eg: due to slashing or if a governance -proposal is vetoed). - -## Module Accounts - -The supply functionality introduces a new type of `auth.Account` which can be used by -modules to allocate tokens and in special cases mint or burn tokens. At a base -level these module accounts are capable of sending/receiving tokens to and from -`auth.Account`s and other module accounts. This design replaces previous -alternative designs where, to hold tokens, modules would burn the incoming -tokens from the sender account, and then track those tokens internally. Later, -in order to send tokens, the module would need to effectively mint tokens -within a destination account. The new design removes duplicate logic between -modules to perform this accounting. - -The `ModuleAccount` interface is defined as follows: - -```go -type ModuleAccount interface { - auth.Account // same methods as the Account interface - - GetName() string // name of the module; used to obtain the address - GetPermissions() []string // permissions of module account - HasPermission(string) bool -} -``` - -> **WARNING!** -> Any module or message handler that allows either direct or indirect sending of funds must explicitly guarantee those funds cannot be sent to module accounts (unless allowed). - -The supply `Keeper` also introduces new wrapper functions for the auth `Keeper` -and the bank `Keeper` that are related to `ModuleAccount`s in order to be able -to: - -* Get and set `ModuleAccount`s by providing the `Name`. -* Send coins from and to other `ModuleAccount`s or standard `Account`s - (`BaseAccount` or `VestingAccount`) by passing only the `Name`. -* `Mint` or `Burn` coins for a `ModuleAccount` (restricted to its permissions). - -### Permissions - -Each `ModuleAccount` has a different set of permissions that provide different -object capabilities to perform certain actions. Permissions need to be -registered upon the creation of the supply `Keeper` so that every time a -`ModuleAccount` calls the allowed functions, the `Keeper` can lookup the -permissions to that specific account and perform or not the action. - -The available permissions are: - -* `Minter`: allows for a module to mint a specific amount of coins. -* `Burner`: allows for a module to burn a specific amount of coins. -* `Staking`: allows for a module to delegate and undelegate a specific amount of coins. - -## Contents - -1. **[State](01_state.md)** -2. **[Keepers](02_keepers.md)** - * [Common Types](02_keepers.md#common-types) - * [BaseKeeper](02_keepers.md#basekeeper) - * [SendKeeper](02_keepers.md#sendkeeper) - * [ViewKeeper](02_keepers.md#viewkeeper) -3. **[Messages](03_messages.md)** - * [MsgSend](03_messages.md#msgsend) - * [MsgMultiSend](03_messages.md#msgmultisend) -4. **[Events](04_events.md)** - * [Handlers](04_events.md#handlers) -5. **[Parameters](05_params.md)** -6. **[Client](06_client.md)** - * [CLI](06_client.md#cli) - * [gRPC](06_client.md#grpc) diff --git a/versioned_docs/version-0.46/integrate/modules/bank/_category_.json b/versioned_docs/version-0.46/integrate/modules/bank/_category_.json deleted file mode 100644 index 26ef5343c..000000000 --- a/versioned_docs/version-0.46/integrate/modules/bank/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Bank", - "position": 2, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/capability/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/capability/01_concepts.md deleted file mode 100644 index 28a19e06e..000000000 --- a/versioned_docs/version-0.46/integrate/modules/capability/01_concepts.md +++ /dev/null @@ -1,31 +0,0 @@ -# Concepts - -## Capabilities - -Capabilities are multi-owner. A scoped keeper can create a capability via `NewCapability` -which creates a new unique, unforgeable object-capability reference. The newly -created capability is automatically persisted; the calling module need not call -`ClaimCapability`. Calling `NewCapability` will create the capability with the -calling module and name as a tuple to be treated the capabilities first owner. - -Capabilities can be claimed by other modules which add them as owners. `ClaimCapability` -allows a module to claim a capability key which it has received from another -module so that future `GetCapability` calls will succeed. `ClaimCapability` MUST -be called if a module which receives a capability wishes to access it by name in -the future. Again, capabilities are multi-owner, so if multiple modules have a -single Capability reference, they will all own it. If a module receives a capability -from another module but does not call `ClaimCapability`, it may use it in the executing -transaction but will not be able to access it afterwards. - -`AuthenticateCapability` can be called by any module to check that a capability -does in fact correspond to a particular name (the name can be un-trusted user input) -with which the calling module previously associated it. - -`GetCapability` allows a module to fetch a capability which it has previously -claimed by name. The module is not allowed to retrieve capabilities which it does -not own. - -## Stores - -* MemStore -* KeyStore diff --git a/versioned_docs/version-0.46/integrate/modules/capability/02_state.md b/versioned_docs/version-0.46/integrate/modules/capability/02_state.md deleted file mode 100644 index 4d77a034e..000000000 --- a/versioned_docs/version-0.46/integrate/modules/capability/02_state.md +++ /dev/null @@ -1,22 +0,0 @@ -# State -## In persisted KV store - -1. Global unique capability index -2. Capability owners - -Indexes: - -* Unique index: `[]byte("index") -> []byte(currentGlobalIndex)` -* Capability Index: `[]byte("capability_index") | []byte(index) -> ProtocolBuffer(CapabilityOwners)` - -## In-memory KV store - -1. Initialized flag -2. Mapping between the module and capability tuple and the capability name -3. Mapping between the module and capability name and its index - -Indexes: - -* Initialized flag: `[]byte("mem_initialized")` -* RevCapabilityKey: `[]byte(moduleName + "/rev/" + capabilityName) -> []byte(index)` -* FwdCapabilityKey: `[]byte(moduleName + "/fwd/" + capabilityPointerAddress) -> []byte(capabilityName)` diff --git a/versioned_docs/version-0.46/integrate/modules/capability/README.md b/versioned_docs/version-0.46/integrate/modules/capability/README.md deleted file mode 100644 index 96a2dcbd9..000000000 --- a/versioned_docs/version-0.46/integrate/modules/capability/README.md +++ /dev/null @@ -1,77 +0,0 @@ - - -# `capability` - -## Overview - -`x/capability` is an implementation of a Cosmos SDK module, per [ADR 003](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-003-dynamic-capability-store.md), -that allows for provisioning, tracking, and authenticating multi-owner capabilities -at runtime. - -The keeper maintains two states: persistent and ephemeral in-memory. The persistent -store maintains a globally unique auto-incrementing index and a mapping from -capability index to a set of capability owners that are defined as a module and -capability name tuple. The in-memory ephemeral state keeps track of the actual -capabilities, represented as addresses in local memory, with both forward and reverse indexes. -The forward index maps module name and capability tuples to the capability name. The -reverse index maps between the module and capability name and the capability itself. - -The keeper allows the creation of "scoped" sub-keepers which are tied to a particular -module by name. Scoped keepers must be created at application initialization and -passed to modules, which can then use them to claim capabilities they receive and -retrieve capabilities which they own by name, in addition to creating new capabilities -& authenticating capabilities passed by other modules. A scoped keeper cannot escape its scope, -so a module cannot interfere with or inspect capabilities owned by other modules. - -The keeper provides no other core functionality that can be found in other modules -like queriers, REST and CLI handlers, and genesis state. - -## Initialization - -During application initialization, the keeper must be instantiated with a persistent -store key and an in-memory store key. - -```go -type App struct { - // ... - - capabilityKeeper *capability.Keeper -} - -func NewApp(...) *App { - // ... - - app.capabilityKeeper = capability.NewKeeper(codec, persistentStoreKey, memStoreKey) -} -``` - -After the keeper is created, it can be used to create scoped sub-keepers which -are passed to other modules that can create, authenticate, and claim capabilities. -After all the necessary scoped keepers are created and the state is loaded, the -main capability keeper must be sealed to prevent further scoped keepers from -being created. - -```go -func NewApp(...) *App { - // ... - - // Creating a scoped keeper - scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) - - // Seal the capability keeper to prevent any further modules from creating scoped - // sub-keepers. - app.capabilityKeeper.Seal() - - return app -} -``` - -## Contents - -1. **[Concepts](01_concepts.md)** -1. **[State](02_state.md)** diff --git a/versioned_docs/version-0.46/integrate/modules/capability/_category_.json b/versioned_docs/version-0.46/integrate/modules/capability/_category_.json deleted file mode 100644 index 76516372b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/capability/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Capability", - "position": 3, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/01_state.md b/versioned_docs/version-0.46/integrate/modules/crisis/01_state.md deleted file mode 100644 index dea835dae..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/01_state.md +++ /dev/null @@ -1,13 +0,0 @@ -# State - -## ConstantFee - -Due to the anticipated large gas cost requirement to verify an invariant (and -potential to exceed the maximum allowable block gas limit) a constant fee is -used instead of the standard gas consumption method. The constant fee is -intended to be larger than the anticipated gas cost of running the invariant -with the standard gas consumption method. - -The ConstantFee param is held in the global params store. - -* Params: `mint/params -> legacy_amino(sdk.Coin)` diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/02_messages.md b/versioned_docs/version-0.46/integrate/modules/crisis/02_messages.md deleted file mode 100644 index e712f5eae..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/02_messages.md +++ /dev/null @@ -1,21 +0,0 @@ -# Messages - -In this section we describe the processing of the crisis messages and the -corresponding updates to the state. - -## MsgVerifyInvariant - -Blockchain invariants can be checked using the `MsgVerifyInvariant` message. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/crisis/v1beta1/tx.proto#L16-L26 - -This message is expected to fail if: - -* the sender does not have enough coins for the constant fee -* the invariant route is not registered - -This message checks the invariant provided, and if the invariant is broken it -panics, halting the blockchain. If the invariant is broken, the constant fee is -never deducted as the transaction is never committed to a block (equivalent to -being refunded). However, if the invariant is not broken, the constant fee will -not be refunded. diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/03_events.md b/versioned_docs/version-0.46/integrate/modules/crisis/03_events.md deleted file mode 100644 index f645e823b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/03_events.md +++ /dev/null @@ -1,14 +0,0 @@ -# Events - -The crisis module emits the following events: - -## Handlers - -### MsgVerifyInvariance - -| Type | Attribute Key | Attribute Value | -|-----------|---------------|------------------| -| invariant | route | {invariantRoute} | -| message | module | crisis | -| message | action | verify_invariant | -| message | sender | {senderAddress} | diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/04_params.md b/versioned_docs/version-0.46/integrate/modules/crisis/04_params.md deleted file mode 100644 index 19ccc3648..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/04_params.md +++ /dev/null @@ -1,7 +0,0 @@ -# Parameters - -The crisis module contains the following parameters: - -| Key | Type | Example | -|-------------|---------------|-----------------------------------| -| ConstantFee | object (coin) | {"denom":"uatom","amount":"1000"} | diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/05_client.md b/versioned_docs/version-0.46/integrate/modules/crisis/05_client.md deleted file mode 100644 index 09ad513e8..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/05_client.md +++ /dev/null @@ -1,27 +0,0 @@ -# Client - -## CLI - -A user can query and interact with the `crisis` module using the CLI. - -### Transactions - -The `tx` commands allow users to interact with the `crisis` module. - -```bash -simd tx crisis --help -``` - -#### invariant-broken - -The `invariant-broken` command submits proof when an invariant was broken to halt the chain - -```bash -simd tx crisis invariant-broken [module-name] [invariant-route] [flags] -``` - -Example: - -```bash -simd tx crisis invariant-broken bank total-supply --from=[keyname or address] -``` diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/README.md b/versioned_docs/version-0.46/integrate/modules/crisis/README.md deleted file mode 100644 index 79d688b41..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/README.md +++ /dev/null @@ -1,26 +0,0 @@ - - -# `crisis` - -## Overview - -The crisis module halts the blockchain under the circumstance that a blockchain -invariant is broken. Invariants can be registered with the application during the -application initialization process. - -## Contents - -1. **[State](01_state.md)** - * [ConstantFee](01_state.md#constantfee) -2. **[Messages](02_messages.md)** - * [MsgVerifyInvariant](02_messages.md#msgverifyinvariant) -3. **[Events](03_events.md)** - * [Handlers](03_events.md#handlers) -4. **[Parameters](04_params.md)** -5. **[Client](05_client.md)** - * [CLI](05_client.md#cli) diff --git a/versioned_docs/version-0.46/integrate/modules/crisis/_category_.json b/versioned_docs/version-0.46/integrate/modules/crisis/_category_.json deleted file mode 100644 index 1e851ca4b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/crisis/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Crisis", - "position": 4, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/distribution/01_concepts.md deleted file mode 100644 index 6bef259f1..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/01_concepts.md +++ /dev/null @@ -1,34 +0,0 @@ - - -# Concepts - -In Proof of Stake (PoS) blockchains, rewards gained from transaction fees are paid to validators. The fee distribution module fairly distributes the rewards to the validators' constituent delegators. - -Rewards are calculated per period. The period is updated each time a validator's delegation changes, for example, when the validator receives a new delegation. -The rewards for a single validator can then be calculated by taking the total rewards for the period before the delegation started, minus the current total rewards. -To learn more, see the [F1 Fee Distribution paper](/docs/spec/fee_distribution/f1_fee_distr.pdf). - -The commission to the validator is paid when the validator is removed or when the validator requests a withdrawal. -The commission is calculated and incremented at every `BeginBlock` operation to update accumulated fee amounts. - -The rewards to a delegator are distributed when the delegation is changed or removed, or a withdrawal is requested. -Before rewards are distributed, all slashes to the validator that occurred during the current delegation are applied. - -## Reference Counting in F1 Fee Distribution - -In F1 fee distribution, the rewards a delegator receives are calculated when their delegation is withdrawn. This calculation must read the terms of the summation of rewards divided by the share of tokens from the period which they ended when they delegated, and the final period that was created for the withdrawal. - -Additionally, as slashes change the amount of tokens a delegation will have (but we calculate this lazily, -only when a delegator un-delegates), we must calculate rewards in separate periods before / after any slashes -which occurred in between when a delegator delegated and when they withdrew their rewards. Thus slashes, like -delegations, reference the period which was ended by the slash event. - -All stored historical rewards records for periods which are no longer referenced by any delegations -or any slashes can thus be safely removed, as they will never be read (future delegations and future -slashes will always reference future periods). This is implemented by tracking a `ReferenceCount` -along with each historical reward storage entry. Each time a new object (delegation or slash) -is created which might need to reference the historical record, the reference count is incremented. -Each time one object which previously needed to reference the historical record is deleted, the reference -count is decremented. If the reference count hits zero, the historical record is deleted. diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/02_state.md b/versioned_docs/version-0.46/integrate/modules/distribution/02_state.md deleted file mode 100644 index e914ce253..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/02_state.md +++ /dev/null @@ -1,65 +0,0 @@ - - -# State - -## FeePool - -All globally tracked parameters for distribution are stored within -`FeePool`. Rewards are collected and added to the reward pool and -distributed to validators/delegators from here. - -Note that the reward pool holds decimal coins (`DecCoins`) to allow -for fractions of coins to be received from operations like inflation. -When coins are distributed from the pool they are truncated back to -`sdk.Coins` which are non-decimal. - -* FeePool: `0x00 -> ProtocolBuffer(FeePool)` - -```go -// coins with decimal -type DecCoins []DecCoin - -type DecCoin struct { - Amount sdk.Dec - Denom string -} -``` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/distribution/v1beta1/distribution.proto#L92-L96 - -## Validator Distribution - -Validator distribution information for the relevant validator is updated each time: - -1. delegation amount to a validator is updated, -2. a validator successfully proposes a block and receives a reward, -3. any delegator withdraws from a validator, or -4. the validator withdraws its commission. - -* ValidatorDistInfo: `0x02 | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)` - -```go -type ValidatorDistInfo struct { - OperatorAddress sdk.AccAddress - SelfBondRewards sdk.DecCoins - ValidatorCommission types.ValidatorAccumulatedCommission -} -``` - -## Delegation Distribution - -Each delegation distribution only needs to record the height at which it last -withdrew fees. Because a delegation must withdraw fees each time it's -properties change (aka bonded tokens etc.) its properties will remain constant -and the delegator's _accumulation_ factor can be calculated passively knowing -only the height of the last withdrawal and its current properties. - -* DelegationDistInfo: `0x02 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(delegatorDist)` - -```go -type DelegationDistInfo struct { - WithdrawalHeight int64 // last time this delegation withdrew rewards -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/03_begin_block.md b/versioned_docs/version-0.46/integrate/modules/distribution/03_begin_block.md deleted file mode 100644 index 0b68aa796..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/03_begin_block.md +++ /dev/null @@ -1,87 +0,0 @@ - - -# Begin Block - -At each `BeginBlock`, all fees received in the previous block are transferred to -the distribution `ModuleAccount` account. When a delegator or validator -withdraws their rewards, they are taken out of the `ModuleAccount`. During begin -block, the different claims on the fees collected are updated as follows: - -* The block proposer of the previous height and its delegators receive between 1% and 5% of fee rewards. -* The reserve community tax is charged. -* The remainder is distributed proportionally by voting power to all bonded validators - -To incentivize validators to wait and include additional pre-commits in the block, the block proposer reward is calculated from Tendermint pre-commit messages. - -## The Distribution Scheme - -See [params](07_params.md) for description of parameters. - -Let `fees` be the total fees collected in the previous block, including -inflationary rewards to the stake. All fees are collected in a specific module -account during the block. During `BeginBlock`, they are sent to the -`"distribution"` `ModuleAccount`. No other sending of tokens occurs. Instead, the -rewards each account is entitled to are stored, and withdrawals can be triggered -through the messages `FundCommunityPool`, `WithdrawValidatorCommission` and -`WithdrawDelegatorReward`. - -### Reward to the Community Pool - -The community pool gets `community_tax * fees`, plus any remaining dust after -validators get their rewards that are always rounded down to the nearest -integer value. - -### Reward To the Validators - -The proposer receives a base reward of `fees * baseproposerreward` and a bonus -of `fees * bonusproposerreward * P`, where `P = (total power of validators with -included precommits / total bonded validator power)`. The more precommits the -proposer includes, the larger `P` is. `P` can never be larger than `1.00` (since -only bonded validators can supply valid precommits) and is always larger than -`2/3`. - -Any remaining fees are distributed among all the bonded validators, including -the proposer, in proportion to their consensus power. - -```text -powFrac = validator power / total bonded validator power -proposerMul = baseproposerreward + bonusproposerreward * P -voteMul = 1 - communitytax - proposerMul -``` - -In total, the proposer receives `fees * (voteMul * powFrac + proposerMul)`. -All other validators receive `fees * voteMul * powFrac`. - -### Rewards to Delegators - -Each validator's rewards are distributed to its delegators. The validator also -has a self-delegation that is treated like a regular delegation in -distribution calculations. - -The validator sets a commission rate. The commission rate is flexible, but each -validator sets a maximum rate and a maximum daily increase. These maximums cannot be exceeded and protect delegators from sudden increases of validator commission rates to prevent validators from taking all of the rewards. - -The outstanding rewards that the operator is entitled to are stored in -`ValidatorAccumulatedCommission`, while the rewards the delegators are entitled -to are stored in `ValidatorCurrentRewards`. The [F1 fee distribution -scheme](01_concepts.md) is used to calculate the rewards per delegator as they -withdraw or update their delegation, and is thus not handled in `BeginBlock`. - -### Example Distribution - -For this example distribution, the underlying consensus engine selects block proposers in -proportion to their power relative to the entire bonded power. - -All validators are equally performant at including pre-commits in their proposed -blocks. Then hold `(precommits included) / (total bonded validator power)` -constant so that the amortized block reward for the validator is `( validator power / total bonded power) * (1 - community tax rate)` of -the total rewards. Consequently, the reward for a single delegator is: - -```text -(delegator proportion of the validator power / validator power) * (validator power / total bonded power) - * (1 - community tax rate) * (1 - validator commision rate) -= (delegator proportion of the validator power / total bonded power) * (1 - -community tax rate) * (1 - validator commision rate) -``` diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/04_messages.md b/versioned_docs/version-0.46/integrate/modules/distribution/04_messages.md deleted file mode 100644 index 87d608842..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/04_messages.md +++ /dev/null @@ -1,122 +0,0 @@ - - -# Messages - -## MsgSetWithdrawAddress - -By default, the withdraw address is the delegator address. To change its withdraw address, a delegator must send a `MsgSetWithdrawAddress` message. -Changing the withdraw address is possible only if the parameter `WithdrawAddrEnabled` is set to `true`. - -The withdraw address cannot be any of the module accounts. These accounts are blocked from being withdraw addresses by being added to the distribution keeper's `blockedAddrs` array at initialization. - -Response: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L31-L41 - -```go -func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error - if k.blockedAddrs[withdrawAddr.String()] { - fail with "`{withdrawAddr}` is not allowed to receive external funds" - } - - if !k.GetWithdrawAddrEnabled(ctx) { - fail with `ErrSetWithdrawAddrDisabled` - } - - k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr) -``` - -## MsgWithdrawDelegatorReward - -A delegator can withdraw its rewards. -Internally in the distribution module, this transaction simultaneously removes the previous delegation with associated rewards, the same as if the delegator simply started a new delegation of the same value. -The rewards are sent immediately from the distribution `ModuleAccount` to the withdraw address. -Any remainder (truncated decimals) are sent to the community pool. -The starting height of the delegation is set to the current validator period, and the reference count for the previous period is decremented. -The amount withdrawn is deducted from the `ValidatorOutstandingRewards` variable for the validator. - -In the F1 distribution, the total rewards are calculated per validator period, and a delegator receives a piece of those rewards in proportion to their stake in the validator. -In basic F1, the total rewards that all the delegators are entitled to between to periods is calculated the following way. -Let `R(X)` be the total accumulated rewards up to period `X` divided by the tokens staked at that time. The delegator allocation is `R(X) * delegator_stake`. -Then the rewards for all the delegators for staking between periods `A` and `B` are `(R(B) - R(A)) * total stake`. -However, these calculated rewards don't account for slashing. - -Taking the slashes into account requires iteration. -Let `F(X)` be the fraction a validator is to be slashed for a slashing event that happened at period `X`. -If the validator was slashed at periods `P1, ..., PN`, where `A < P1`, `PN < B`, the distribution module calculates the individual delegator's rewards, `T(A, B)`, as follows: - -```go -stake := initial stake -rewards := 0 -previous := A -for P in P1, ..., PN`: - rewards = (R(P) - previous) * stake - stake = stake * F(P) - previous = P -rewards = rewards + (R(B) - R(PN)) * stake -``` - -The historical rewards are calculated retroactively by playing back all the slashes and then attenuating the delegator's stake at each step. -The final calculated stake is equivalent to the actual staked coins in the delegation with a margin of error due to rounding errors. - -Response: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/distribution/v1beta1/tx.proto#L46-L56 - -## WithdrawValidatorCommission - -The validator can send the WithdrawValidatorCommission message to withdraw their accumulated commission. -The commission is calculated in every block during `BeginBlock`, so no iteration is required to withdraw. -The amount withdrawn is deducted from the `ValidatorOutstandingRewards` variable for the validator. -Only integer amounts can be sent. If the accumulated awards have decimals, the amount is truncated before the withdrawal is sent, and the remainder is left to be withdrawn later. - -## FundCommunityPool - -This message sends coins directly from the sender to the community pool. - -The transaction fails if the amount cannot be transferred from the sender to the distribution module account. - -```go -func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error { - if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil { - return err - } - - feePool := k.GetFeePool(ctx) - feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...) - k.SetFeePool(ctx, feePool) - - return nil -} -``` - -## Common distribution operations - -These operations take place during many different messages. - -### Initialize delegation - -Each time a delegation is changed, the rewards are withdrawn and the delegation is reinitialized. -Initializing a delegation increments the validator period and keeps track of the starting period of the delegation. - -```go -// initialize starting info for a new delegation -func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) { - // period has already been incremented - we want to store the period ended by this delegation action - previousPeriod := k.GetValidatorCurrentRewards(ctx, val).Period - 1 - - // increment reference count for the period we're going to track - k.incrementReferenceCount(ctx, val, previousPeriod) - - validator := k.stakingKeeper.Validator(ctx, val) - delegation := k.stakingKeeper.Delegation(ctx, del, val) - - // calculate delegation stake in tokens - // we don't store directly, so multiply delegation shares * (tokens per share) - // note: necessary to truncate so we don't allow withdrawing more rewards than owed - stake := validator.TokensFromSharesTruncated(delegation.GetShares()) - k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight()))) -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/05_hooks.md b/versioned_docs/version-0.46/integrate/modules/distribution/05_hooks.md deleted file mode 100644 index a1702ef73..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/05_hooks.md +++ /dev/null @@ -1,59 +0,0 @@ - - -# Hooks - -Available hooks that can be called by and from this module. - -## Create or modify delegation distribution - -* triggered-by: `staking.MsgDelegate`, `staking.MsgBeginRedelegate`, `staking.MsgUndelegate` - -### Before - -* The delegation rewards are withdrawn to the withdraw address of the delegator. - The rewards include the current period and exclude the starting period. -* The validator period is incremented. - The validator period is incremented because the validator's power and share distribution might have changed. -* The reference count for the delegator's starting period is decremented. - -### After - -The starting height of the delegation is set to the previous period. -Because of the `Before`-hook, this period is the last period for which the delegator was rewarded. - -## Validator created - -* triggered-by: `staking.MsgCreateValidator` - -When a validator is created, the following validator variables are initialized: - -* Historical rewards -* Current accumulated rewards -* Accumulated commission -* Total outstanding rewards -* Period - -By default, all values are set to a `0`, except period, which is set to `1`. - -## Validator removed - -* triggered-by: `staking.RemoveValidator` - -Outstanding commission is sent to the validator's self-delegation withdrawal address. -Remaining delegator rewards get sent to the community fee pool. - -Note: The validator gets removed only when it has no remaining delegations. -At that time, all outstanding delegator rewards will have been withdrawn. -Any remaining rewards are dust amounts. - -## Validator is slashed - -* triggered-by: `staking.Slash` - -* The current validator period reference count is incremented. - The reference count is incremented because the slash event has created a reference to it. -* The validator period is incremented. -* The slash event is stored for later use. - The slash event will be referenced when calculating delegator rewards. diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/06_events.md b/versioned_docs/version-0.46/integrate/modules/distribution/06_events.md deleted file mode 100644 index 7e70f0beb..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/06_events.md +++ /dev/null @@ -1,48 +0,0 @@ - - -# Events - -The distribution module emits the following events: - -## BeginBlocker - -| Type | Attribute Key | Attribute Value | -|-----------------|---------------|--------------------| -| proposer_reward | validator | {validatorAddress} | -| proposer_reward | reward | {proposerReward} | -| commission | amount | {commissionAmount} | -| commission | validator | {validatorAddress} | -| rewards | amount | {rewardAmount} | -| rewards | validator | {validatorAddress} | - -## Handlers - -### MsgSetWithdrawAddress - -| Type | Attribute Key | Attribute Value | -|----------------------|------------------|----------------------| -| set_withdraw_address | withdraw_address | {withdrawAddress} | -| message | module | distribution | -| message | action | set_withdraw_address | -| message | sender | {senderAddress} | - -### MsgWithdrawDelegatorReward - -| Type | Attribute Key | Attribute Value | -|---------|---------------|---------------------------| -| withdraw_rewards | amount | {rewardAmount} | -| withdraw_rewards | validator | {validatorAddress} | -| message | module | distribution | -| message | action | withdraw_delegator_reward | -| message | sender | {senderAddress} | - -### MsgWithdrawValidatorCommission - -| Type | Attribute Key | Attribute Value | -|------------|---------------|-------------------------------| -| withdraw_commission | amount | {commissionAmount} | -| message | module | distribution | -| message | action | withdraw_validator_commission | -| message | sender | {senderAddress} | diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/07_params.md b/versioned_docs/version-0.46/integrate/modules/distribution/07_params.md deleted file mode 100644 index 4cee94c9d..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/07_params.md +++ /dev/null @@ -1,17 +0,0 @@ - - -# Parameters - -The distribution module contains the following parameters: - -| Key | Type | Example | -| ------------------- | ------------ | -------------------------- | -| communitytax | string (dec) | "0.020000000000000000" [0] | -| baseproposerreward | string (dec) | "0.010000000000000000" [0] | -| bonusproposerreward | string (dec) | "0.040000000000000000" [0] | -| withdrawaddrenabled | bool | true | - -* [0] `communitytax`, `baseproposerreward` and `bonusproposerreward` must be - positive and their sum cannot exceed 1.00. diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/08_client.md b/versioned_docs/version-0.46/integrate/modules/distribution/08_client.md deleted file mode 100644 index 8e547ba46..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/08_client.md +++ /dev/null @@ -1,469 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `distribution` module using the CLI. - -### Query - -The `query` commands allow users to query `distribution` state. - -```sh -simd query distribution --help -``` - -#### commission - -The `commission` command allows users to query validator commission rewards by address. - -```sh -simd query distribution commission [address] [flags] -``` - -Example: - -```sh -simd query distribution commission cosmosvaloper1.. -``` - -Example Output: - -```yml -commission: -- amount: "1000000.000000000000000000" - denom: stake -``` - -#### community-pool - -The `community-pool` command allows users to query all coin balances within the community pool. - -```sh -simd query distribution community-pool [flags] -``` - -Example: - -```sh -simd query distribution community-pool -``` - -Example Output: - -```yml -pool: -- amount: "1000000.000000000000000000" - denom: stake -``` - -#### params - -The `params` command allows users to query the parameters of the `distribution` module. - -```sh -simd query distribution params [flags] -``` - -Example: - -```sh -simd query distribution params -``` - -Example Output: - -```yml -base_proposer_reward: "0.010000000000000000" -bonus_proposer_reward: "0.040000000000000000" -community_tax: "0.020000000000000000" -withdraw_addr_enabled: true -``` - -#### rewards - -The `rewards` command allows users to query delegator rewards. Users can optionally include the validator address to query rewards earned from a specific validator. - -```sh -simd query distribution rewards [delegator-addr] [validator-addr] [flags] -``` - -Example: - -```sh -simd query distribution rewards cosmos1.. -``` - -Example Output: - -```yml -rewards: -- reward: - - amount: "1000000.000000000000000000" - denom: stake - validator_address: cosmosvaloper1.. -total: -- amount: "1000000.000000000000000000" - denom: stake -``` - -#### slashes - -The `slashes` command allows users to query all slashes for a given block range. - -```sh -simd query distribution slashes [validator] [start-height] [end-height] [flags] -``` - -Example: - -```sh -simd query distribution slashes cosmosvaloper1.. 1 1000 -``` - -Example Output: - -```yml -pagination: - next_key: null - total: "0" -slashes: -- validator_period: 20, - fraction: "0.009999999999999999" -``` - -#### validator-outstanding-rewards - -The `validator-outstanding-rewards` command allows users to query all outstanding (un-withdrawn) rewards for a validator and all their delegations. - -```sh -simd query distribution validator-outstanding-rewards [validator] [flags] -``` - -Example: - -```sh -simd query distribution validator-outstanding-rewards cosmosvaloper1.. -``` - -Example Output: - -```yml -rewards: -- amount: "1000000.000000000000000000" - denom: stake -``` - -### Transactions - -The `tx` commands allow users to interact with the `distribution` module. - -```sh -simd tx distribution --help -``` - -#### fund-community-pool - -The `fund-community-pool` command allows users to send funds to the community pool. - -```sh -simd tx distribution fund-community-pool [amount] [flags] -``` - -Example: - -```sh -simd tx distribution fund-community-pool 100stake --from cosmos1.. -``` - -#### set-withdraw-addr - -The `set-withdraw-addr` command allows users to set the withdraw address for rewards associated with a delegator address. - -```sh -simd tx distribution set-withdraw-addr [withdraw-addr] [flags] -``` - -Example: - -```sh -simd tx distribution set-withdraw-addr cosmos1.. --from cosmos1.. -``` - -#### withdraw-all-rewards - -The `withdraw-all-rewards` command allows users to withdraw all rewards for a delegator. - -```sh -simd tx distribution withdraw-all-rewards [flags] -``` - -Example: - -```sh -simd tx distribution withdraw-all-rewards --from cosmos1.. -``` - -#### withdraw-rewards - -The `withdraw-rewards` command allows users to withdraw all rewards from a given delegation address, -and optionally withdraw validator commission if the delegation address given is a validator operator and the user proves the `--commision` flag. - -```sh -simd tx distribution withdraw-rewards [validator-addr] [flags] -``` - -Example: - -```sh -simd tx distribution withdraw-rewards cosmosvaloper1.. --from cosmos1.. --commision -``` - -## gRPC - -A user can query the `distribution` module using gRPC endpoints. - -### Params - -The `Params` endpoint allows users to query parameters of the `distribution` module. - -Example: - -```sh -grpcurl -plaintext \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/Params -``` - -Example Output: - -```json -{ - "params": { - "communityTax": "20000000000000000", - "baseProposerReward": "10000000000000000", - "bonusProposerReward": "40000000000000000", - "withdrawAddrEnabled": true - } -} -``` - -### ValidatorOutstandingRewards - -The `ValidatorOutstandingRewards` endpoint allows users to query rewards of a validator address. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"validator_address":"cosmosvalop1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards -``` - -Example Output: - -```json -{ - "rewards": { - "rewards": [ - { - "denom": "stake", - "amount": "1000000000000000" - } - ] - } -} -``` - -### ValidatorCommission - -The `ValidatorCommission` endpoint allows users to query accumulated commission for a validator. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"validator_address":"cosmosvalop1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/ValidatorCommission -``` - -Example Output: - -```json -{ - "commission": { - "commission": [ - { - "denom": "stake", - "amount": "1000000000000000" - } - ] - } -} -``` - -### ValidatorSlashes - -The `ValidatorSlashes` endpoint allows users to query slash events of a validator. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"validator_address":"cosmosvalop1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/ValidatorSlashes -``` - -Example Output: - -```json -{ - "slashes": [ - { - "validator_period": "20", - "fraction": "0.009999999999999999" - } - ], - "pagination": { - "total": "1" - } -} -``` - -### DelegationRewards - -The `DelegationRewards` endpoint allows users to query the total rewards accrued by a delegation. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"delegator_address":"cosmos1..","validator_address":"cosmosvalop1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/DelegationRewards -``` - -Example Output: - -```json -{ - "rewards": [ - { - "denom": "stake", - "amount": "1000000000000000" - } - ] -} -``` - -### DelegationTotalRewards - -The `DelegationTotalRewards` endpoint allows users to query the total rewards accrued by each validator. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"delegator_address":"cosmos1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/DelegationTotalRewards -``` - -Example Output: - -```json -{ - "rewards": [ - { - "validatorAddress": "cosmosvaloper1..", - "reward": [ - { - "denom": "stake", - "amount": "1000000000000000" - } - ] - } - ], - "total": [ - { - "denom": "stake", - "amount": "1000000000000000" - } - ] -} -``` - -### DelegatorValidators - -The `DelegatorValidators` endpoint allows users to query all validators for given delegator. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"delegator_address":"cosmos1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/DelegatorValidators -``` - -Example Output: - -```json -{ - "validators": [ - "cosmosvaloper1.." - ] -} -``` - -### DelegatorWithdrawAddress - -The `DelegatorWithdrawAddress` endpoint allows users to query the withdraw address of a delegator. - -Example: - -```sh -grpcurl -plaintext \ - -d '{"delegator_address":"cosmos1.."}' \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/DelegatorWithdrawAddress -``` - -Example Output: - -```json -{ - "withdrawAddress": "cosmos1.." -} -``` - -### CommunityPool - -The `CommunityPool` endpoint allows users to query the community pool coins. - -Example: - -```sh -grpcurl -plaintext \ - localhost:9090 \ - cosmos.distribution.v1beta1.Query/CommunityPool -``` - -Example Output: - -```json -{ - "pool": [ - { - "denom": "stake", - "amount": "1000000000000000000" - } - ] -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/README.md b/versioned_docs/version-0.46/integrate/modules/distribution/README.md deleted file mode 100644 index c5a841ceb..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/README.md +++ /dev/null @@ -1,106 +0,0 @@ - - -# `distribution` - -## Overview - -This _simple_ distribution mechanism describes a functional way to passively -distribute rewards between validators and delegators. Note that this mechanism does -not distribute funds in as precisely as active reward distribution mechanisms and -will therefore be upgraded in the future. - -The mechanism operates as follows. Collected rewards are pooled globally and -divided out passively to validators and delegators. Each validator has the -opportunity to charge commission to the delegators on the rewards collected on -behalf of the delegators. Fees are collected directly into a global reward pool -and validator proposer-reward pool. Due to the nature of passive accounting, -whenever changes to parameters which affect the rate of reward distribution -occurs, withdrawal of rewards must also occur. - -* Whenever withdrawing, one must withdraw the maximum amount they are entitled - to, leaving nothing in the pool. -* Whenever bonding, unbonding, or re-delegating tokens to an existing account, a - full withdrawal of the rewards must occur (as the rules for lazy accounting - change). -* Whenever a validator chooses to change the commission on rewards, all accumulated - commission rewards must be simultaneously withdrawn. - -The above scenarios are covered in `hooks.md`. - -The distribution mechanism outlined herein is used to lazily distribute the -following rewards between validators and associated delegators: - -* multi-token fees to be socially distributed -* proposer reward pool -* inflated atom provisions -* validator commission on all rewards earned by their delegators stake - -Fees are pooled within a global pool, as well as validator specific -proposer-reward pools. The mechanisms used allow for validators and delegators -to independently and lazily withdraw their rewards. - -## Shortcomings - -As a part of the lazy computations, each delegator holds an accumulation term -specific to each validator which is used to estimate what their approximate -fair portion of tokens held in the global fee pool is owed to them. - -```text -entitlement = delegator-accumulation / all-delegators-accumulation -``` - -Under the circumstance that there was constant and equal flow of incoming -reward tokens every block, this distribution mechanism would be equal to the -active distribution (distribute individually to all delegators each block). -However, this is unrealistic so deviations from the active distribution will -occur based on fluctuations of incoming reward tokens as well as timing of -reward withdrawal by other delegators. - -If you happen to know that incoming rewards are about to significantly increase, -you are incentivized to not withdraw until after this event, increasing the -worth of your existing _accum_. See [#2764](https://github.com/cosmos/cosmos-sdk/issues/2764) -for further details. - -## Effect on Staking - -Charging commission on Atom provisions while also allowing for Atom-provisions -to be auto-bonded (distributed directly to the validators bonded stake) is -problematic within BPoS. Fundamentally, these two mechanisms are mutually -exclusive. If both commission and auto-bonding mechanisms are simultaneously -applied to the staking-token then the distribution of staking-tokens between -any validator and its delegators will change with each block. This then -necessitates a calculation for each delegation records for each block - -which is considered computationally expensive. - -In conclusion, we can only have Atom commission and unbonded atoms -provisions or bonded atom provisions with no Atom commission, and we elect to -implement the former. Stakeholders wishing to rebond their provisions may elect -to set up a script to periodically withdraw and rebond rewards. - -## Contents - -1. **[Concepts](01_concepts.md)** - * [Reference Counting in F1 Fee Distribution](01_concepts.md#reference-counting-in-f1-fee-distribution) -2. **[State](02_state.md)** -3. **[Begin Block](03_begin_block.md)** -4. **[Messages](04_messages.md)** - * [MsgSetWithdrawAddress](04_messages.md#msgsetwithdrawaddress) - * [MsgWithdrawDelegatorReward](04_messages.md#msgwithdrawdelegatorreward) - * [Withdraw Validator Rewards All](04_messages.md#withdraw-validator-rewards-all) - * [Common calculations](04_messages.md#common-calculations-) -5. **[Hooks](05_hooks.md)** - * [Create or modify delegation distribution](05_hooks.md#create-or-modify-delegation-distribution) - * [Commission rate change](05_hooks.md#commission-rate-change) - * [Change in Validator State](05_hooks.md#change-in-validator-state) -6. **[Events](06_events.md)** - * [BeginBlocker](06_events.md#beginblocker) - * [Handlers](06_events.md#handlers) -7. **[Parameters](07_params.md)** -8. **[Parameters](07_params.md)** - * [CLI](08_client.md#cli) - * [gRPC](08_client.md#grpc) diff --git a/versioned_docs/version-0.46/integrate/modules/distribution/_category_.json b/versioned_docs/version-0.46/integrate/modules/distribution/_category_.json deleted file mode 100644 index e83c5672d..000000000 --- a/versioned_docs/version-0.46/integrate/modules/distribution/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Distribution", - "position": 5, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/evidence/01_concepts.md deleted file mode 100644 index 926a573bb..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/01_concepts.md +++ /dev/null @@ -1,78 +0,0 @@ - - -# Concepts - -## Evidence - -Any concrete type of evidence submitted to the `x/evidence` module must fulfill the -`Evidence` contract outlined below. Not all concrete types of evidence will fulfill -this contract in the same way and some data may be entirely irrelevant to certain -types of evidence. An additional `ValidatorEvidence`, which extends `Evidence`, -has also been created to define a contract for evidence against malicious validators. - -```go -// Evidence defines the contract which concrete evidence types of misbehavior -// must implement. -type Evidence interface { - proto.Message - - Route() string - Type() string - String() string - Hash() tmbytes.HexBytes - ValidateBasic() error - - // Height at which the infraction occurred - GetHeight() int64 -} - -// ValidatorEvidence extends Evidence interface to define contract -// for evidence against malicious validators -type ValidatorEvidence interface { - Evidence - - // The consensus address of the malicious validator at time of infraction - GetConsensusAddress() sdk.ConsAddress - - // The total power of the malicious validator at time of infraction - GetValidatorPower() int64 - - // The total validator set power at time of infraction - GetTotalPower() int64 -} -``` - -## Registration & Handling - -The `x/evidence` module must first know about all types of evidence it is expected -to handle. This is accomplished by registering the `Route` method in the `Evidence` -contract with what is known as a `Router` (defined below). The `Router` accepts -`Evidence` and attempts to find the corresponding `Handler` for the `Evidence` -via the `Route` method. - -```go -type Router interface { - AddRoute(r string, h Handler) Router - HasRoute(r string) bool - GetRoute(path string) Handler - Seal() - Sealed() bool -} -``` - -The `Handler` (defined below) is responsible for executing the entirety of the -business logic for handling `Evidence`. This typically includes validating the -evidence, both stateless checks via `ValidateBasic` and stateful checks via any -keepers provided to the `Handler`. In addition, the `Handler` may also perform -capabilities such as slashing and jailing a validator. All `Evidence` handled -by the `Handler` should be persisted. - -```go -// Handler defines an agnostic Evidence handler. The handler is responsible -// for executing all corresponding business logic necessary for verifying the -// evidence as valid. In addition, the Handler may execute any necessary -// slashing and potential jailing. -type Handler func(sdk.Context, Evidence) error -``` diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/02_state.md b/versioned_docs/version-0.46/integrate/modules/evidence/02_state.md deleted file mode 100644 index 00d8d05be..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/02_state.md +++ /dev/null @@ -1,19 +0,0 @@ - - -# State - -Currently the `x/evidence` module only stores valid submitted `Evidence` in state. -The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`. - -```protobuf -// GenesisState defines the evidence module's genesis state. -message GenesisState { - // evidence defines all the evidence at genesis. - repeated google.protobuf.Any evidence = 1; -} - -``` - -All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`). diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/03_messages.md b/versioned_docs/version-0.46/integrate/modules/evidence/03_messages.md deleted file mode 100644 index cd902ec99..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/03_messages.md +++ /dev/null @@ -1,55 +0,0 @@ - - -# Messages - -## MsgSubmitEvidence - -Evidence is submitted through a `MsgSubmitEvidence` message: - -```protobuf -// MsgSubmitEvidence represents a message that supports submitting arbitrary -// Evidence of misbehavior such as equivocation or counterfactual signing. -message MsgSubmitEvidence { - string submitter = 1; - google.protobuf.Any evidence = 2; -} -``` - -Note, the `Evidence` of a `MsgSubmitEvidence` message must have a corresponding -`Handler` registered with the `x/evidence` module's `Router` in order to be processed -and routed correctly. - -Given the `Evidence` is registered with a corresponding `Handler`, it is processed -as follows: - -```go -func SubmitEvidence(ctx Context, evidence Evidence) error { - if _, ok := GetEvidence(ctx, evidence.Hash()); ok { - return sdkerrors.Wrap(types.ErrEvidenceExists, evidence.Hash().String()) - } - if !router.HasRoute(evidence.Route()) { - return sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidence.Route()) - } - - handler := router.GetRoute(evidence.Route()) - if err := handler(ctx, evidence); err != nil { - return sdkerrors.Wrap(types.ErrInvalidEvidence, err.Error()) - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeSubmitEvidence, - sdk.NewAttribute(types.AttributeKeyEvidenceHash, evidence.Hash().String()), - ), - ) - - SetEvidence(ctx, evidence) - return nil -} -``` - -First, there must not already exist valid submitted `Evidence` of the exact same -type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally, -if there is no error in handling the `Evidence`, an event is emitted and it is persisted to state. diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/04_events.md b/versioned_docs/version-0.46/integrate/modules/evidence/04_events.md deleted file mode 100644 index 35fd77b3f..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/04_events.md +++ /dev/null @@ -1,18 +0,0 @@ - - -# Events - -The `x/evidence` module emits the following events: - -## Handlers - -### MsgSubmitEvidence - -| Type | Attribute Key | Attribute Value | -| --------------- | ------------- | --------------- | -| submit_evidence | evidence_hash | {evidenceHash} | -| message | module | evidence | -| message | sender | {senderAddress} | -| message | action | submit_evidence | diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/05_params.md b/versioned_docs/version-0.46/integrate/modules/evidence/05_params.md deleted file mode 100644 index 4c48b540a..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/05_params.md +++ /dev/null @@ -1,7 +0,0 @@ - - -# Parameters - -The evidence module does not contain any parameters. diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/06_begin_block.md b/versioned_docs/version-0.46/integrate/modules/evidence/06_begin_block.md deleted file mode 100644 index 1c335d1e4..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/06_begin_block.md +++ /dev/null @@ -1,154 +0,0 @@ - - -# BeginBlock - -## Evidence Handling - -Tendermint blocks can include -[Evidence](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#evidence) that indicates if a validator committed malicious behavior. The relevant information is forwarded to the application as ABCI Evidence in `abci.RequestBeginBlock` so that the validator can be punished accordingly. - -### Equivocation - -The Cosmos SDK handles two types of evidence inside the ABCI `BeginBlock`: - -* `DuplicateVoteEvidence`, -* `LightClientAttackEvidence`. - -The evidence module handles these two evidence types the same way. First, the Cosmos SDK converts the Tendermint concrete evidence type to an SDK `Evidence` interface using `Equivocation` as the concrete type. - -```proto -// Equivocation implements the Evidence interface. -message Equivocation { - int64 height = 1; - google.protobuf.Timestamp time = 2; - int64 power = 3; - string consensus_address = 4; -} -``` - -For some `Equivocation` submitted in `block` to be valid, it must satisfy: - -`Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge` - -Where: - -* `Evidence.Timestamp` is the timestamp in the block at height `Evidence.Height` -* `block.Timestamp` is the current block timestamp. - -If valid `Equivocation` evidence is included in a block, the validator's stake is -reduced (slashed) by `SlashFractionDoubleSign` as defined by the `x/slashing` module -of what their stake was when the infraction occurred, rather than when the evidence was discovered. -We want to "follow the stake", i.e., the stake that contributed to the infraction -should be slashed, even if it has since been redelegated or started unbonding. - -In addition, the validator is permanently jailed and tombstoned to make it impossible for that -validator to ever re-enter the validator set. - -The `Equivocation` evidence is handled as follows: - -```go -func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) { - logger := k.Logger(ctx) - consAddr := evidence.GetConsensusAddress() - - if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { - // Ignore evidence that cannot be handled. - // - // NOTE: We used to panic with: - // `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`, - // but this couples the expectations of the app to both Tendermint and - // the simulator. Both are expected to provide the full range of - // allowable but none of the disallowed evidence types. Instead of - // getting this coordination right, it is easier to relax the - // constraints and ignore evidence that cannot be handled. - return - } - - // calculate the age of the evidence - infractionHeight := evidence.GetHeight() - infractionTime := evidence.GetTime() - ageDuration := ctx.BlockHeader().Time.Sub(infractionTime) - ageBlocks := ctx.BlockHeader().Height - infractionHeight - - // Reject evidence if the double-sign is too old. Evidence is considered stale - // if the difference in time and number of blocks is greater than the allowed - // parameters defined. - cp := ctx.ConsensusParams() - if cp != nil && cp.Evidence != nil { - if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks { - logger.Info( - "ignored equivocation; evidence too old", - "validator", consAddr, - "infraction_height", infractionHeight, - "max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks, - "infraction_time", infractionTime, - "max_age_duration", cp.Evidence.MaxAgeDuration, - ) - return - } - } - - validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) - if validator == nil || validator.IsUnbonded() { - // Defensive: Simulation doesn't take unbonding periods into account, and - // Tendermint might break this assumption at some point. - return - } - - if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { - panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr)) - } - - // ignore if the validator is already tombstoned - if k.slashingKeeper.IsTombstoned(ctx, consAddr) { - logger.Info( - "ignored equivocation; validator already tombstoned", - "validator", consAddr, - "infraction_height", infractionHeight, - "infraction_time", infractionTime, - ) - return - } - - logger.Info( - "confirmed equivocation", - "validator", consAddr, - "infraction_height", infractionHeight, - "infraction_time", infractionTime, - ) - - // We need to retrieve the stake distribution which signed the block, so we - // subtract ValidatorUpdateDelay from the evidence height. - // Note, that this *can* result in a negative "distributionHeight", up to - // -ValidatorUpdateDelay, i.e. at the end of the - // pre-genesis block (none) = at the beginning of the genesis block. - // That's fine since this is just used to filter unbonding delegations & redelegations. - distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay - - // Slash validator. The `power` is the int64 power of the validator as provided - // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via - // ABCI, and now received as evidence. The fraction is passed in to separately - // to slash unbonding and rebonding delegations. - k.slashingKeeper.Slash( - ctx, - consAddr, - k.slashingKeeper.SlashFractionDoubleSign(ctx), - evidence.GetValidatorPower(), distributionHeight, - ) - - // Jail the validator if not already jailed. This will begin unbonding the - // validator if not already unbonding (tombstoned). - if !validator.IsJailed() { - k.slashingKeeper.Jail(ctx, consAddr) - } - - k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) - k.slashingKeeper.Tombstone(ctx, consAddr) -} -``` - -**Note:** The slashing, jailing, and tombstoning calls are delegated through the `x/slashing` module -that emits informative events and finally delegates calls to the `x/staking` module. See documentation -on slashing and jailing in [State Transitions](/.././cosmos-sdk/x/staking/spec/02_state_transitions.md). diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/07_client.md b/versioned_docs/version-0.46/integrate/modules/evidence/07_client.md deleted file mode 100644 index 52a4b34f7..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/07_client.md +++ /dev/null @@ -1,188 +0,0 @@ -# Client - -## CLI - -A user can query and interact with the `evidence` module using the CLI. - -### Query - -The `query` commands allows users to query `evidence` state. - -```bash -simd query evidence --help -``` - -### evidence - -The `evidence` command allows users to list all evidence or evidence by hash. - -Usage: - -```bash -simd query evidence [flags] -``` - -To query evidence by hash - -Example: - -```bash -simd query evidence "DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660" -``` - -Example Output: - -```bash -evidence: - consensus_address: cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h - height: 11 - power: 100 - time: "2021-10-20T16:08:38.194017624Z" -``` - -To get all evidence - -Example: - -```bash -simd query evidence -``` - -Example Output: - -```bash -evidence: - consensus_address: cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h - height: 11 - power: 100 - time: "2021-10-20T16:08:38.194017624Z" -pagination: - next_key: null - total: "1" -``` - -## REST - -A user can query the `evidence` module using REST endpoints. - -### Evidence - -Get evidence by hash - -```bash -/cosmos/evidence/v1beta1/evidence/{evidence_hash} -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/evidence/v1beta1/evidence/DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660" -``` - -Example Output: - -```bash -{ - "evidence": { - "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h", - "height": "11", - "power": "100", - "time": "2021-10-20T16:08:38.194017624Z" - } -} -``` - -### All evidence - -Get all evidence - -```bash -/cosmos/evidence/v1beta1/evidence -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/evidence/v1beta1/evidence" -``` - -Example Output: - -```bash -{ - "evidence": [ - { - "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h", - "height": "11", - "power": "100", - "time": "2021-10-20T16:08:38.194017624Z" - } - ], - "pagination": { - "total": "1" - } -} -``` - -## gRPC - -A user can query the `evidence` module using gRPC endpoints. - -### Evidence - -Get evidence by hash - -```bash -cosmos.evidence.v1beta1.Query/Evidence -``` - -Example: - -```bash -grpcurl -plaintext -d '{"evidence_hash":"DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"}' localhost:9090 cosmos.evidence.v1beta1.Query/Evidence -``` - -Example Output: - -```bash -{ - "evidence": { - "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h", - "height": "11", - "power": "100", - "time": "2021-10-20T16:08:38.194017624Z" - } -} -``` - -### All evidence - -Get all evidence - -```bash -cosmos.evidence.v1beta1.Query/AllEvidence -``` - -Example: - -```bash -grpcurl -plaintext localhost:9090 cosmos.evidence.v1beta1.Query/AllEvidence -``` - -Example Output: - -```bash -{ - "evidence": [ - { - "consensus_address": "cosmosvalcons1ntk8eualewuprz0gamh8hnvcem2nrcdsgz563h", - "height": "11", - "power": "100", - "time": "2021-10-20T16:08:38.194017624Z" - } - ], - "pagination": { - "total": "1" - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/README.md b/versioned_docs/version-0.46/integrate/modules/evidence/README.md deleted file mode 100644 index a6b3d9205..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/README.md +++ /dev/null @@ -1,40 +0,0 @@ - - -# `x/evidence` - -## Table of Contents - - - -1. **[Concepts](01_concepts.md)** -2. **[State](02_state.md)** -3. **[Messages](03_messages.md)** -4. **[Events](04_events.md)** -5. **[Params](05_params.md)** -6. **[BeginBlock](06_begin_block.md)** - -## Abstract - -`x/evidence` is an implementation of a Cosmos SDK module, per [ADR 009](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-009-evidence-module.md), -that allows for the submission and handling of arbitrary evidence of misbehavior such -as equivocation and counterfactual signing. - -The evidence module differs from standard evidence handling which typically expects the -underlying consensus engine, e.g. Tendermint, to automatically submit evidence when -it is discovered by allowing clients and foreign chains to submit more complex evidence -directly. - -All concrete evidence types must implement the `Evidence` interface contract. Submitted -`Evidence` is first routed through the evidence module's `Router` in which it attempts -to find a corresponding registered `Handler` for that specific `Evidence` type. -Each `Evidence` type must have a `Handler` registered with the evidence module's -keeper in order for it to be successfully routed and executed. - -Each corresponding handler must also fulfill the `Handler` interface contract. The -`Handler` for a given `Evidence` type can perform any arbitrary state transitions -such as slashing, jailing, and tombstoning. diff --git a/versioned_docs/version-0.46/integrate/modules/evidence/_category_.json b/versioned_docs/version-0.46/integrate/modules/evidence/_category_.json deleted file mode 100644 index 49492c8d5..000000000 --- a/versioned_docs/version-0.46/integrate/modules/evidence/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Evidence", - "position": 6, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/feegrant/01_concepts.md deleted file mode 100644 index 2d9ceda89..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/01_concepts.md +++ /dev/null @@ -1,93 +0,0 @@ - - -# Concepts - -## Grant - -`Grant` is stored in the KVStore to record a grant with full context. Every grant will contain `granter`, `grantee` and what kind of `allowance` is granted. `granter` is an account address who is giving permission to `grantee` (the beneficiary account address) to pay for some or all of `grantee`'s transaction fees. `allowance` defines what kind of fee allowance (`BasicAllowance` or `PeriodicAllowance`, see below) is granted to `grantee`. `allowance` accepts an interface which implements `FeeAllowanceI`, encoded as `Any` type. There can be only one existing fee grant allowed for a `grantee` and `granter`, self grants are not allowed. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/feegrant/v1beta1/feegrant.proto#L76-L77 - -`FeeAllowanceI` looks like: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/feegrant/fees.go#L9-L32 - -## Fee Allowance types - -There are two types of fee allowances present at the moment: - -* `BasicAllowance` -* `PeriodicAllowance` -* `AllowedMsgAllowance` - -## BasicAllowance - -`BasicAllowance` is permission for `grantee` to use fee from a `granter`'s account. If any of the `spend_limit` or `expiration` reaches its limit, the grant will be removed from the state. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/feegrant/v1beta1/feegrant.proto#L13-L26 - -* `spend_limit` is the limit of coins that are allowed to be used from the `granter` account. If it is empty, it assumes there's no spend limit, `grantee` can use any number of available coins from `granter` account address before the expiration. - -* `expiration` specifies an optional time when this allowance expires. If the value is left empty, there is no expiry for the grant. - -* When a grant is created with empty values for `spend_limit` and `expiration`, it is still a valid grant. It won't restrict the `grantee` to use any number of coins from `granter` and it won't have any expiration. The only way to restrict the `grantee` is by revoking the grant. - -## PeriodicAllowance - -`PeriodicAllowance` is a repeating fee allowance for the mentioned period, we can mention when the grant can expire as well as when a period can reset. We can also define the maximum number of coins that can be used in a mentioned period of time. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/feegrant/v1beta1/feegrant.proto#L29-L54 - -* `basic` is the instance of `BasicAllowance` which is optional for periodic fee allowance. If empty, the grant will have no `expiration` and no `spend_limit`. - -* `period` is the specific period of time, after each period passes, `period_spend_limit` will be reset. - -* `period_spend_limit` specifies the maximum number of coins that can be spent in the period. - -* `period_can_spend` is the number of coins left to be spent before the period_reset time. - -* `period_reset` keeps track of when a next period reset should happen. - -## AllowedMsgAllowance - -`AllowedMsgAllowance` is a fee allowance, it can be any of `BasicFeeAllowance`, `PeriodicAllowance` but restricted only to the allowed messages mentioned by the granter. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/feegrant/v1beta1/feegrant.proto#L56-L66 - -* `allowance` is either `BasicAllowance` or `PeriodicAllowance`. - -* `allowed_messages` is array of messages allowed to execute the given allowance. - -## FeeGranter flag - -`feegrant` module introduces a `FeeGranter` flag for CLI for the sake of executing transactions with fee granter. When this flag is set, `clientCtx` will append the granter account address for transactions generated through CLI. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/cmd.go#L236-L246 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/tx/tx.go#L109-L109 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/auth/tx/builder.go#L270-L279 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/tx/v1beta1/tx.proto#L196-L217 - -Example cmd: - -```go -./simd tx gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --from validator-key --fee-granter=cosmos1xh44hxt7spr67hqaa7nyx5gnutrz5fraw6grxn --chain-id=testnet --fees="10stake" -``` - -## Granted Fee Deductions - -Fees are deducted from grants in the `x/auth` ante handler. To learn more about how ante handlers work, read the [Auth Module AnteHandlers Guide](../../auth/spec/03_antehandlers.md). - -## Gas - -In order to prevent DoS attacks, using a filtered `x/feegrant` incurs gas. The SDK must assure that the `grantee`'s transactions all conform to the filter set by the `granter`. The SDK does this by iterating over the allowed messages in the filter and charging 10 gas per filtered message. The SDK will then iterate over the messages being sent by the `grantee` to ensure the messages adhere to the filter, also charging 10 gas per message. The SDK will stop iterating and fail the transaction if it finds a message that does not conform to the filter. - -**WARNING**: The gas is charged against the granted allowance. Ensure your messages conform to the filter, if any, before sending transactions using your allowance. - -## Pruning - -A queue in the state maintained with the prefix of expiration of the grants and checks them on EndBlock with the current block time for every block to prune. diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/02_state.md b/versioned_docs/version-0.46/integrate/modules/feegrant/02_state.md deleted file mode 100644 index 3ff4d8c49..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/02_state.md +++ /dev/null @@ -1,23 +0,0 @@ - - -# State - -## FeeAllowance - -Fee Allowances are identified by combining `Grantee` (the account address of fee allowance grantee) with the `Granter` (the account address of fee allowance granter). - -Fee allowance grants are stored in the state as follows: - -* Grant: `0x00 | grantee_addr_len (1 byte) | grantee_addr_bytes | granter_addr_len (1 byte) | granter_addr_bytes -> ProtocolBuffer(Grant)` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/x/feegrant/feegrant.pb.go#L221-L229 - -## FeeAllowanceQueue - -Fee Allowances queue items are identified by combining the `FeeAllowancePrefixQueue` (i.e., 0x01), `expiration`, `grantee` (the account address of fee allowance grantee), `granter` (the account address of fee allowance granter). Endblocker checks `FeeAllowanceQueue` state for the expired grants and prunes them from `FeeAllowance` if there are any found. - -Fee allowance queue keys are stored in the state as follows: - -* Grant: `0x01 | expiration_bytes | grantee_addr_len (1 byte) | grantee_addr_bytes | granter_addr_len (1 byte) | granter_addr_bytes -> EmptyBytes` diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/03_messages.md b/versioned_docs/version-0.46/integrate/modules/feegrant/03_messages.md deleted file mode 100644 index a4d09619a..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/03_messages.md +++ /dev/null @@ -1,17 +0,0 @@ - - -# Messages - -## Msg/GrantAllowance - -A fee allowance grant will be created with the `MsgGrantAllowance` message. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/feegrant/v1beta1/tx.proto#L23-L36 - -## Msg/RevokeAllowance - -An allowed grant fee allowance can be removed with the `MsgRevokeAllowance` message. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/feegrant/v1beta1/tx.proto#L41-L50 diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/04_events.md b/versioned_docs/version-0.46/integrate/modules/feegrant/04_events.md deleted file mode 100644 index e443558f7..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/04_events.md +++ /dev/null @@ -1,33 +0,0 @@ - - -# Events - -The feegrant module emits the following events: - -# Msg Server - -## MsgGrantAllowance - -| Type | Attribute Key | Attribute Value | -| ------- | ------------- | ---------------- | -| message | action | set_feegrant | -| message | granter | {granterAddress} | -| message | grantee | {granteeAddress} | - -## MsgRevokeAllowance - -| Type | Attribute Key | Attribute Value | -| ------- | ------------- | ---------------- | -| message | action | revoke_feegrant | -| message | granter | {granterAddress} | -| message | grantee | {granteeAddress} | - -## Exec fee allowance - -| Type | Attribute Key | Attribute Value | -| ------- | ------------- | ---------------- | -| message | action | use_feegrant | -| message | granter | {granterAddress} | -| message | grantee | {granteeAddress} | diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/05_client.md b/versioned_docs/version-0.46/integrate/modules/feegrant/05_client.md deleted file mode 100644 index 52c1d9726..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/05_client.md +++ /dev/null @@ -1,184 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `feegrant` module using the CLI. - -### Query - -The `query` commands allow users to query `feegrant` state. - -```sh -simd query feegrant --help -``` - -#### grant - -The `grant` command allows users to query a grant for a given granter-grantee pair. - -```sh -simd query feegrant grant [granter] [grantee] [flags] -``` - -Example: - -```sh -simd query feegrant grant cosmos1.. cosmos1.. -``` - -Example Output: - -```yml -allowance: - '@type': /cosmos.feegrant.v1beta1.BasicAllowance - expiration: null - spend_limit: - - amount: "100" - denom: stake -grantee: cosmos1.. -granter: cosmos1.. -``` - -#### grants - -The `grants` command allows users to query all grants for a given grantee. - -```sh -simd query feegrant grants [grantee] [flags] -``` - -Example: - -```sh -simd query feegrant grants cosmos1.. -``` - -Example Output: - -```yml -allowances: -- allowance: - '@type': /cosmos.feegrant.v1beta1.BasicAllowance - expiration: null - spend_limit: - - amount: "100" - denom: stake - grantee: cosmos1.. - granter: cosmos1.. -pagination: - next_key: null - total: "0" -``` - -### Transactions - -The `tx` commands allow users to interact with the `feegrant` module. - -```sh -simd tx feegrant --help -``` - -#### grant - -The `grant` command allows users to grant fee allowances to another account. The fee allowance can have an expiration date, a total spend limit, and/or a periodic spend limit. - -```sh -simd tx feegrant grant [granter] [grantee] [flags] -``` - -Example (one-time spend limit): - -```sh -simd tx feegrant grant cosmos1.. cosmos1.. --spend-limit 100stake -``` - -Example (periodic spend limit): - -```sh -simd tx feegrant grant cosmos1.. cosmos1.. --period 3600 --period-limit 10stake -``` - -#### revoke - -The `revoke` command allows users to revoke a granted fee allowance. - -```sh -simd tx feegrant revoke [granter] [grantee] [flags] -``` - -Example: - -```sh -simd tx feegrant revoke cosmos1.. cosmos1.. -``` - -## gRPC - -A user can query the `feegrant` module using gRPC endpoints. - -### Allowance - -The `Allowance` endpoint allows users to query a granted fee allowance. - -```sh -cosmos.feegrant.v1beta1.Query/Allowance -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"grantee":"cosmos1..","granter":"cosmos1.."}' \ - localhost:9090 \ - cosmos.feegrant.v1beta1.Query/Allowance -``` - -Example Output: - -```json -{ - "allowance": { - "granter": "cosmos1..", - "grantee": "cosmos1..", - "allowance": {"@type":"/cosmos.feegrant.v1beta1.BasicAllowance","spendLimit":[{"denom":"stake","amount":"100"}]} - } -} -``` - -### Allowances - -The `Allowances` endpoint allows users to query all granted fee allowances for a given grantee. - -```sh -cosmos.feegrant.v1beta1.Query/Allowances -``` - -Example: - -```sh -grpcurl -plaintext \ - -d '{"address":"cosmos1.."}' \ - localhost:9090 \ - cosmos.feegrant.v1beta1.Query/Allowances -``` - -Example Output: - -```json -{ - "allowances": [ - { - "granter": "cosmos1..", - "grantee": "cosmos1..", - "allowance": {"@type":"/cosmos.feegrant.v1beta1.BasicAllowance","spendLimit":[{"denom":"stake","amount":"100"}]} - } - ], - "pagination": { - "total": "1" - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/README.md b/versioned_docs/version-0.46/integrate/modules/feegrant/README.md deleted file mode 100644 index 8f08a51ae..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/README.md +++ /dev/null @@ -1,37 +0,0 @@ - - -# Fee grant - -## Abstract - -This document specifies the fee grant module. For the full ADR, please see [Fee Grant ADR-029](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-029-fee-grant-module.md). - -This module allows accounts to grant fee allowances and to use fees from their accounts. Grantees can execute any transaction without the need to maintain sufficient fees. - -## Contents - -1. **[Concepts](01_concepts.md)** - * [Grant](01_concepts.md#grant) - * [Fee Allowance types](01_concepts.md#fee-allowance-types) - * [BasicAllowance](01_concepts.md#basicallowance) - * [PeriodicAllowance](01_concepts.md#periodicallowance) - * [FeeAccount flag](01_concepts.md#feeaccount-flag) - * [Granted Fee Deductions](01_concepts.md#granted-fee-deductions) - * [Gas](01_concepts.md#gas) -2. **[State](02_state.md)** - * [FeeAllowance](02_state.md#feeallowance) -3. **[Messages](03_messages.md)** - * [Msg/GrantAllowance](03_messages.md#msggrantallowance) - * [Msg/RevokeAllowance](03_messages.md#msgrevokeallowance) -4. **[Events](04_events.md)** - * [MsgGrantAllowance](04_events.md#msggrantallowance) - * [MsgRevokeAllowance](04_events.md#msgrevokeallowance) - * [Exec fee allowance](04_events.md#exec-fee-allowance) -5. **[Client](05_client.md)** - * [CLI](05_client.md#cli) - * [gRPC](05_client.md#grpc) diff --git a/versioned_docs/version-0.46/integrate/modules/feegrant/_category_.json b/versioned_docs/version-0.46/integrate/modules/feegrant/_category_.json deleted file mode 100644 index 4616ef3b7..000000000 --- a/versioned_docs/version-0.46/integrate/modules/feegrant/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Feegrant", - "position": 7, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/gov/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/gov/01_concepts.md deleted file mode 100644 index 39b39d13a..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/01_concepts.md +++ /dev/null @@ -1,203 +0,0 @@ - - -# Concepts - -_Disclaimer: This is work in progress. Mechanisms are susceptible to change._ - -The governance process is divided in a few steps that are outlined below: - -* **Proposal submission:** Proposal is submitted to the blockchain with a - deposit. -* **Vote:** Once deposit reaches a certain value (`MinDeposit`), proposal is - confirmed and vote opens. Bonded Atom holders can then send `TxGovVote` - transactions to vote on the proposal. -* **Execution** After a period of time, the votes are tallied and depending - on the result, the messages in the proposal will be executed. - -## Proposal submission - -### Right to submit a proposal - -Every account can submit proposals by sending a `MsgSubmitProposal` transaction. -Once a proposal is submitted, it is identified by its unique `proposalID`. - -### Proposal Messages - -A proposal includes an array of `sdk.Msg`s which are executed automatically if the -proposal passes. The messages are executed by the governance `ModuleAccount` itself. Modules -such as `x/upgrade`, that want to allow certain messages to be executed by governance -only should add a whitelist within the respective msg server, granting the governance -module the right to execute the message once a quorum has been reached. The governance -module uses the `MsgServiceRouter` to check that these messages are correctly constructed -and have a respective path to execute on but do not perform a full validity check. - -## Deposit - -To prevent spam, proposals must be submitted with a deposit in the coins defined by -the `MinDeposit` param. - -When a proposal is submitted, it has to be accompanied with a deposit that must be -strictly positive, but can be inferior to `MinDeposit`. The submitter doesn't need -to pay for the entire deposit on their own. The newly created proposal is stored in -an _inactive proposal queue_ and stays there until its deposit passes the `MinDeposit`. -Other token holders can increase the proposal's deposit by sending a `Deposit` -transaction. If a proposal doesn't pass the `MinDeposit` before the deposit end time -(the time when deposits are no longer accepted), the proposal will be destroyed: the -proposal will be removed from state and the deposit will be burned (see x/gov `EndBlocker`). -When a proposal deposit passes the `MinDeposit` threshold (even during the proposal -submission) before the deposit end time, the proposal will be moved into the -_active proposal queue_ and the voting period will begin. - -The deposit is kept in escrow and held by the governance `ModuleAccount` until the -proposal is finalized (passed or rejected). - -### Deposit refund and burn - -When a proposal is finalized, the coins from the deposit are either refunded or burned -according to the final tally of the proposal: - -* If the proposal is approved or rejected but _not_ vetoed, each deposit will be - automatically refunded to its respective depositor (transferred from the governance - `ModuleAccount`). -* When the proposal is vetoed with greater than 1/3, deposits will be burned from the - governance `ModuleAccount` and the proposal information along with its deposit - information will be removed from state. -* All refunded or burned deposits are removed from the state. Events are issued when - burning or refunding a deposit. - -## Vote - -### Participants - -_Participants_ are users that have the right to vote on proposals. On the -Cosmos Hub, participants are bonded Atom holders. Unbonded Atom holders and -other users do not get the right to participate in governance. However, they -can submit and deposit on proposals. - -Note that some _participants_ can be forbidden to vote on a proposal under a -certain validator if: - -* _participant_ bonded or unbonded Atoms to said validator after proposal - entered voting period. -* _participant_ became validator after proposal entered voting period. - -This does not prevent _participant_ to vote with Atoms bonded to other -validators. For example, if a _participant_ bonded some Atoms to validator A -before a proposal entered voting period and other Atoms to validator B after -proposal entered voting period, only the vote under validator B will be -forbidden. - -### Voting period - -Once a proposal reaches `MinDeposit`, it immediately enters `Voting period`. We -define `Voting period` as the interval between the moment the vote opens and -the moment the vote closes. `Voting period` should always be shorter than -`Unbonding period` to prevent double voting. The initial value of -`Voting period` is 2 weeks. - -### Option set - -The option set of a proposal refers to the set of choices a participant can -choose from when casting its vote. - -The initial option set includes the following options: - -* `Yes` -* `No` -* `NoWithVeto` -* `Abstain` - -`NoWithVeto` counts as `No` but also adds a `Veto` vote. `Abstain` option -allows voters to signal that they do not intend to vote in favor or against the -proposal but accept the result of the vote. - -_Note: from the UI, for urgent proposals we should maybe add a ‘Not Urgent’ -option that casts a `NoWithVeto` vote._ - -### Weighted Votes - -[ADR-037](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-037-gov-split-vote.md) introduces the weighted vote feature which allows a staker to split their votes into several voting options. For example, it could use 70% of its voting power to vote Yes and 30% of its voting power to vote No. - -Often times the entity owning that address might not be a single individual. For example, a company might have different stakeholders who want to vote differently, and so it makes sense to allow them to split their voting power. Currently, it is not possible for them to do "passthrough voting" and giving their users voting rights over their tokens. However, with this system, exchanges can poll their users for voting preferences, and then vote on-chain proportionally to the results of the poll. - -To represent weighted vote on chain, we use the following Protobuf message. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1beta1/gov.proto#L33-L43 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1beta1/gov.proto#L136-L150 - -For a weighted vote to be valid, the `options` field must not contain duplicate vote options, and the sum of weights of all options must be equal to 1. - -### Quorum - -Quorum is defined as the minimum percentage of voting power that needs to be -casted on a proposal for the result to be valid. - -### Threshold - -Threshold is defined as the minimum proportion of `Yes` votes (excluding -`Abstain` votes) for the proposal to be accepted. - -Initially, the threshold is set at 50% of `Yes` votes, excluding `Abstain` -votes. A possibility to veto exists if more than 1/3rd of all votes are -`NoWithVeto` votes. Note, both of these values are derived from the `TallyParams` -on-chain parameter, which is modifiable by governance. -This means that proposals are accepted iff: - -* There exist bonded tokens. -* Quorum has been achieved. -* The proportion of `Abstain` votes is inferior to 1/1. -* The proportion of `NoWithVeto` votes is inferior to 1/3, including - `Abstain` votes. -* The proportion of `Yes` votes, excluding `Abstain` votes, at the end of - the voting period is superior to 1/2. - -### Inheritance - -If a delegator does not vote, it will inherit its validator vote. - -* If the delegator votes before its validator, it will not inherit from the - validator's vote. -* If the delegator votes after its validator, it will override its validator - vote with its own. If the proposal is urgent, it is possible - that the vote will close before delegators have a chance to react and - override their validator's vote. This is not a problem, as proposals require more than 2/3rd of the total voting power to pass before the end of the voting period. Because as little as 1/3 + 1 validation power could collude to censor transactions, non-collusion is already assumed for ranges exceeding this threshold. - -### Validator’s punishment for non-voting - -At present, validators are not punished for failing to vote. - -### Governance address - -Later, we may add permissioned keys that could only sign txs from certain modules. For the MVP, the `Governance address` will be the main validator address generated at account creation. This address corresponds to a different PrivKey than the Tendermint PrivKey which is responsible for signing consensus messages. Validators thus do not have to sign governance transactions with the sensitive Tendermint PrivKey. - -## Software Upgrade - -If proposals are of type `SoftwareUpgradeProposal`, then nodes need to upgrade -their software to the new version that was voted. This process is divided into -two steps: - -### Signal - -After a `SoftwareUpgradeProposal` is accepted, validators are expected to -download and install the new version of the software while continuing to run -the previous version. Once a validator has downloaded and installed the -upgrade, it will start signaling to the network that it is ready to switch by -including the proposal's `proposalID` in its _precommits_.(_Note: Confirmation -that we want it in the precommit?_) - -Note: There is only one signal slot per _precommit_. If several -`SoftwareUpgradeProposals` are accepted in a short timeframe, a pipeline will -form and they will be implemented one after the other in the order that they -were accepted. - -### Switch - -Once a block contains more than 2/3rd _precommits_ where a common -`SoftwareUpgradeProposal` is signaled, all the nodes (including validator -nodes, non-validating full nodes and light-nodes) are expected to switch to the -new version of the software. - -Validators and full nodes can use an automation tool, such as [Cosmovisor](https://github.com/cosmos/cosmos-sdk/blob/main/cosmovisor/README.md), for automatically switching version of the chain. diff --git a/versioned_docs/version-0.46/integrate/modules/gov/02_state.md b/versioned_docs/version-0.46/integrate/modules/gov/02_state.md deleted file mode 100644 index 66514ec2c..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/02_state.md +++ /dev/null @@ -1,217 +0,0 @@ - - -# State - -## Proposals - -`Proposal` objects are used to tally votes and generally track the proposal's state. -They contain an array of arbitrary `sdk.Msg`'s which the governance module will attempt -to resolve and then execute if the proposal passes. `Proposal`'s are identified by a -unique id and contains a series of timestamps: `submit_time`, `deposit_end_time`, -`voting_start_time`, `voting_end_time` which track the lifecycle of a proposal - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/gov.proto#L42-L59 - -A proposal will generally require more than just a set of messages to explain its -purpose but need some greater justification and allow a means for interested participants -to discuss and debate the proposal. -In most cases, **it is encouraged to have an off-chain system that supports the on-chain governance process**. -To accommodate for this, a proposal contains a special **`metadata`** field, an array of bytes, -which can be used to add context to the proposal. The `metadata` field allows custom use for networks, -however, it is expected that the field contains a URL or some form of CID using a system such as -[IPFS](https://docs.ipfs.io/concepts/content-addressing/). To support the case of -interoperability across networks, the SDK recommends that the `metadata` represents -the following `JSON` template: - -```json -{ - "title": "...", - "description": "...", - "forum": "...", // a link to the discussion platform (i.e. Discord) - "other": "..." // any extra data that doesn't correspond to the other fields -} -``` - -This makes it far easier for clients to support multiple networks. - -The metadata has a maximum length that is chosen by the app developer, and -passed into the gov keeper as a config. The default maximum length in the SDK is 255 characters. - -### Writing a module that uses governance - -There are many aspects of a chain, or of the individual modules that you may want to -use governance to perform such as changing various parameters. This is very simple -to do. First, write out your message types and `MsgServer` implementation. Add an -`authority` field to the keeper which will be populated in the constructor with the -governance module account: `govKeeper.GetGovernanceAccount().GetAddress()`. Then for -the methods in the `msg_server.go`, perform a check on the message that the signer -matches `authority`. This will prevent any user from executing that message. - -## Parameters and base types - -`Parameters` define the rules according to which votes are run. There can only -be one active parameter set at any given time. If governance wants to change a -parameter set, either to modify a value or add/remove a parameter field, a new -parameter set has to be created and the previous one rendered inactive. - -### DepositParams - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/gov.proto#L102-L112 - -### VotingParams - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/gov.proto#L114-L118 - -### TallyParams - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/gov.proto#L120-L132 - -Parameters are stored in a global `GlobalParams` KVStore. - -Additionally, we introduce some basic types: - -```go -type Vote byte - -const ( - VoteYes = 0x1 - VoteNo = 0x2 - VoteNoWithVeto = 0x3 - VoteAbstain = 0x4 -) - -type ProposalType string - -const ( - ProposalTypePlainText = "Text" - ProposalTypeSoftwareUpgrade = "SoftwareUpgrade" -) - -type ProposalStatus byte - - -const ( - StatusNil ProposalStatus = 0x00 - StatusDepositPeriod ProposalStatus = 0x01 // Proposal is submitted. Participants can deposit on it but not vote - StatusVotingPeriod ProposalStatus = 0x02 // MinDeposit is reached, participants can vote - StatusPassed ProposalStatus = 0x03 // Proposal passed and successfully executed - StatusRejected ProposalStatus = 0x04 // Proposal has been rejected - StatusFailed ProposalStatus = 0x05 // Proposal passed but failed execution -) -``` - -## Deposit - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/gov.proto#L34-L40 - -## ValidatorGovInfo - -This type is used in a temp map when tallying - -```go - type ValidatorGovInfo struct { - Minus sdk.Dec - Vote Vote - } -``` - -## Stores - -_Note: Stores are KVStores in the multi-store. The key to find the store is the first parameter in the list_ - -We will use one KVStore `Governance` to store two mappings: - -* A mapping from `proposalID|'proposal'` to `Proposal`. -* A mapping from `proposalID|'addresses'|address` to `Vote`. This mapping allows - us to query all addresses that voted on the proposal along with their vote by - doing a range query on `proposalID:addresses`. - -For pseudocode purposes, here are the two function we will use to read or write in stores: - -* `load(StoreKey, Key)`: Retrieve item stored at key `Key` in store found at key `StoreKey` in the multistore -* `store(StoreKey, Key, value)`: Write value `Value` at key `Key` in store found at key `StoreKey` in the multistore - -## Proposal Processing Queue - -**Store:** - -* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the - `ProposalIDs` of proposals that reached `MinDeposit`. During each `EndBlock`, - all the proposals that have reached the end of their voting period are processed. - To process a finished proposal, the application tallies the votes, computes the - votes of each validator and checks if every validator in the validator set has - voted. If the proposal is accepted, deposits are refunded. Finally, the proposal - content `Handler` is executed. - -And the pseudocode for the `ProposalProcessingQueue`: - -```go - in EndBlock do - - for finishedProposalID in GetAllFinishedProposalIDs(block.Time) - proposal = load(Governance, ) // proposal is a const key - - validators = Keeper.getAllValidators() - tmpValMap := map(sdk.AccAddress)ValidatorGovInfo - - // Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes - for each validator in validators - tmpValMap(validator.OperatorAddr).Minus = 0 - - // Tally - voterIterator = rangeQuery(Governance, ) //return all the addresses that voted on the proposal - for each (voterAddress, vote) in voterIterator - delegations = stakingKeeper.getDelegations(voterAddress) // get all delegations for current voter - - for each delegation in delegations - // make sure delegation.Shares does NOT include shares being unbonded - tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares - proposal.updateTally(vote, delegation.Shares) - - _, isVal = stakingKeeper.getValidator(voterAddress) - if (isVal) - tmpValMap(voterAddress).Vote = vote - - tallyingParam = load(GlobalParams, 'TallyingParam') - - // Update tally if validator voted - for each validator in validators - if tmpValMap(validator).HasVoted - proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) - - - - // Check if proposal is accepted or rejected - totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes - if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingParam.Veto) - // proposal was accepted at the end of the voting period - // refund deposits (non-voters already punished) - for each (amount, depositor) in proposal.Deposits - depositor.AtomBalance += amount - - stateWriter, err := proposal.Handler() - if err != nil - // proposal passed but failed during state execution - proposal.CurrentStatus = ProposalStatusFailed - else - // proposal pass and state is persisted - proposal.CurrentStatus = ProposalStatusAccepted - stateWriter.save() - else - // proposal was rejected - proposal.CurrentStatus = ProposalStatusRejected - - store(Governance, , proposal) -``` - -## Legacy Proposal - -A legacy proposal is the old implementation of governance proposal. -Contrary to proposal that can contain any messages, a legacy proposal allows to submit a set of pre-defined proposals. -These proposal are defined by their types. - -While proposals should use the new implementation of the governance proposal, we need still to use legacy proposal in order to submit a `software-upgrade` and a `cancel-software-upgrade` proposal. - -More information on how to submit proposals in the [client section](07_client.md). diff --git a/versioned_docs/version-0.46/integrate/modules/gov/03_messages.md b/versioned_docs/version-0.46/integrate/modules/gov/03_messages.md deleted file mode 100644 index 405931da5..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/03_messages.md +++ /dev/null @@ -1,183 +0,0 @@ - - -# Messages - -## Proposal Submission - -Proposals can be submitted by any account via a `MsgSubmitProposal` -transaction. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/tx.proto#L33-L43 - -All `sdk.Msgs` passed into the `messages` field of a `MsgSubmitProposal` message -must be registered in the app's `MsgServiceRouter`. Each of these messages must -have one signer, namely the gov module account. And finally, the metadata length -must not be larger than the `maxMetadataLen` config passed into the gov keeper. - -**State modifications:** - -* Generate new `proposalID` -* Create new `Proposal` -* Initialise `Proposal`'s attributes -* Decrease balance of sender by `InitialDeposit` -* If `MinDeposit` is reached: - * Push `proposalID` in `ProposalProcessingQueue` -* Transfer `InitialDeposit` from the `Proposer` to the governance `ModuleAccount` - -A `MsgSubmitProposal` transaction can be handled according to the following -pseudocode. - -```go -// PSEUDOCODE // -// Check if MsgSubmitProposal is valid. If it is, create proposal // - -upon receiving txGovSubmitProposal from sender do - - if !correctlyFormatted(txGovSubmitProposal) - // check if proposal is correctly formatted and the messages have routes to other modules. Includes fee payment. - // check if all messages' unique Signer is the gov acct. - // check if the metadata is not too long. - throw - - initialDeposit = txGovSubmitProposal.InitialDeposit - if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms) - // InitialDeposit is negative or null OR sender has insufficient funds - throw - - if (txGovSubmitProposal.Type != ProposalTypePlainText) OR (txGovSubmitProposal.Type != ProposalTypeSoftwareUpgrade) - - sender.AtomBalance -= initialDeposit.Atoms - - depositParam = load(GlobalParams, 'DepositParam') - - proposalID = generate new proposalID - proposal = NewProposal() - - proposal.Messages = txGovSubmitProposal.Messages - proposal.Metadata = txGovSubmitProposal.Metadata - proposal.TotalDeposit = initialDeposit - proposal.SubmitTime = - proposal.DepositEndTime = .Add(depositParam.MaxDepositPeriod) - proposal.Deposits.append({initialDeposit, sender}) - proposal.Submitter = sender - proposal.YesVotes = 0 - proposal.NoVotes = 0 - proposal.NoWithVetoVotes = 0 - proposal.AbstainVotes = 0 - proposal.CurrentStatus = ProposalStatusOpen - - store(Proposals, , proposal) // Store proposal in Proposals mapping - return proposalID -``` - -## Deposit - -Once a proposal is submitted, if -`Proposal.TotalDeposit < ActiveParam.MinDeposit`, Atom holders can send -`MsgDeposit` transactions to increase the proposal's deposit. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/tx.proto#L90-L97 - -**State modifications:** - -* Decrease balance of sender by `deposit` -* Add `deposit` of sender in `proposal.Deposits` -* Increase `proposal.TotalDeposit` by sender's `deposit` -* If `MinDeposit` is reached: - * Push `proposalID` in `ProposalProcessingQueueEnd` -* Transfer `Deposit` from the `proposer` to the governance `ModuleAccount` - -A `MsgDeposit` transaction has to go through a number of checks to be valid. -These checks are outlined in the following pseudocode. - -```go -// PSEUDOCODE // -// Check if MsgDeposit is valid. If it is, increase deposit and check if MinDeposit is reached - -upon receiving txGovDeposit from sender do - // check if proposal is correctly formatted. Includes fee payment. - - if !correctlyFormatted(txGovDeposit) - throw - - proposal = load(Proposals, ) // proposal is a const key, proposalID is variable - - if (proposal == nil) - // There is no proposal for this proposalID - throw - - if (txGovDeposit.Deposit.Atoms <= 0) OR (sender.AtomBalance < txGovDeposit.Deposit.Atoms) OR (proposal.CurrentStatus != ProposalStatusOpen) - - // deposit is negative or null - // OR sender has insufficient funds - // OR proposal is not open for deposit anymore - - throw - - depositParam = load(GlobalParams, 'DepositParam') - - if (CurrentBlock >= proposal.SubmitBlock + depositParam.MaxDepositPeriod) - proposal.CurrentStatus = ProposalStatusClosed - - else - // sender can deposit - sender.AtomBalance -= txGovDeposit.Deposit.Atoms - - proposal.Deposits.append({txGovVote.Deposit, sender}) - proposal.TotalDeposit.Plus(txGovDeposit.Deposit) - - if (proposal.TotalDeposit >= depositParam.MinDeposit) - // MinDeposit is reached, vote opens - - proposal.VotingStartBlock = CurrentBlock - proposal.CurrentStatus = ProposalStatusActive - ProposalProcessingQueue.push(txGovDeposit.ProposalID) - - store(Proposals, , proposal) -``` - -## Vote - -Once `ActiveParam.MinDeposit` is reached, voting period starts. From there, -bonded Atom holders are able to send `MsgVote` transactions to cast their -vote on the proposal. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/gov/v1/tx.proto#L64-L72 - -**State modifications:** - -* Record `Vote` of sender - -_Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker._ - -Next is a pseudocode outline of the way `MsgVote` transactions are -handled: - -```go - // PSEUDOCODE // - // Check if MsgVote is valid. If it is, count vote// - - upon receiving txGovVote from sender do - // check if proposal is correctly formatted. Includes fee payment. - - if !correctlyFormatted(txGovDeposit) - throw - - proposal = load(Proposals, ) - - if (proposal == nil) - // There is no proposal for this proposalID - throw - - - if (proposal.CurrentStatus == ProposalStatusActive) - - - // Sender can vote if - // Proposal is active - // Sender has some bonds - - store(Governance, , txGovVote.Vote) // Voters can vote multiple times. Re-voting overrides previous vote. This is ok because tallying is done once at the end. -``` diff --git a/versioned_docs/version-0.46/integrate/modules/gov/04_events.md b/versioned_docs/version-0.46/integrate/modules/gov/04_events.md deleted file mode 100644 index 3c0d72ce9..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/04_events.md +++ /dev/null @@ -1,65 +0,0 @@ - - -# Events - -The governance module emits the following events: - -## EndBlocker - -| Type | Attribute Key | Attribute Value | -| ----------------- | --------------- | ---------------- | -| inactive_proposal | proposal_id | {proposalID} | -| inactive_proposal | proposal_result | {proposalResult} | -| active_proposal | proposal_id | {proposalID} | -| active_proposal | proposal_result | {proposalResult} | - -## Handlers - -### MsgSubmitProposal - -| Type | Attribute Key | Attribute Value | -| ------------------- | ------------------- | --------------- | -| submit_proposal | proposal_id | {proposalID} | -| submit_proposal [0] | voting_period_start | {proposalID} | -| proposal_deposit | amount | {depositAmount} | -| proposal_deposit | proposal_id | {proposalID} | -| message | module | governance | -| message | action | submit_proposal | -| message | sender | {senderAddress} | - -* [0] Event only emitted if the voting period starts during the submission. - -### MsgVote - -| Type | Attribute Key | Attribute Value | -| ------------- | ------------- | --------------- | -| proposal_vote | option | {voteOption} | -| proposal_vote | proposal_id | {proposalID} | -| message | module | governance | -| message | action | vote | -| message | sender | {senderAddress} | - -### MsgVoteWeighted - -| Type | Attribute Key | Attribute Value | -| ------------- | ------------- | ------------------------ | -| proposal_vote | option | {weightedVoteOptions} | -| proposal_vote | proposal_id | {proposalID} | -| message | module | governance | -| message | action | vote | -| message | sender | {senderAddress} | - -### MsgDeposit - -| Type | Attribute Key | Attribute Value | -| -------------------- | ------------------- | --------------- | -| proposal_deposit | amount | {depositAmount} | -| proposal_deposit | proposal_id | {proposalID} | -| proposal_deposit [0] | voting_period_start | {proposalID} | -| message | module | governance | -| message | action | deposit | -| message | sender | {senderAddress} | - -* [0] Event only emitted if the voting period starts during the submission. diff --git a/versioned_docs/version-0.46/integrate/modules/gov/05_future_improvements.md b/versioned_docs/version-0.46/integrate/modules/gov/05_future_improvements.md deleted file mode 100644 index 12f2e9e5c..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/05_future_improvements.md +++ /dev/null @@ -1,30 +0,0 @@ - - -# Future Improvements - -The current documentation only describes the minimum viable product for the -governance module. Future improvements may include: - -* **`BountyProposals`:** If accepted, a `BountyProposal` creates an open - bounty. The `BountyProposal` specifies how many Atoms will be given upon - completion. These Atoms will be taken from the `reserve pool`. After a - `BountyProposal` is accepted by governance, anybody can submit a - `SoftwareUpgradeProposal` with the code to claim the bounty. Note that once a - `BountyProposal` is accepted, the corresponding funds in the `reserve pool` - are locked so that payment can always be honored. In order to link a - `SoftwareUpgradeProposal` to an open bounty, the submitter of the - `SoftwareUpgradeProposal` will use the `Proposal.LinkedProposal` attribute. - If a `SoftwareUpgradeProposal` linked to an open bounty is accepted by - governance, the funds that were reserved are automatically transferred to the - submitter. -* **Complex delegation:** Delegators could choose other representatives than - their validators. Ultimately, the chain of representatives would always end - up to a validator, but delegators could inherit the vote of their chosen - representative before they inherit the vote of their validator. In other - words, they would only inherit the vote of their validator if their other - appointed representative did not vote. -* **Better process for proposal review:** There would be two parts to - `proposal.Deposit`, one for anti-spam (same as in MVP) and an other one to - reward third party auditors. diff --git a/versioned_docs/version-0.46/integrate/modules/gov/06_params.md b/versioned_docs/version-0.46/integrate/modules/gov/06_params.md deleted file mode 100644 index 2cc017669..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/06_params.md +++ /dev/null @@ -1,28 +0,0 @@ - - -# Parameters - -The governance module contains the following parameters: - -| Key | Type | Example | -|---------------|--------|----------------------------------------------------------------------------------------------------| -| depositparams | object | {"min_deposit":[{"denom":"uatom","amount":"10000000"}],"max_deposit_period":"172800000000000"} | -| votingparams | object | {"voting_period":"172800000000000"} | -| tallyparams | object | {"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto":"0.334000000000000000"} | - -## SubKeys - -| Key | Type | Example | -|--------------------|------------------|-----------------------------------------| -| min_deposit | array (coins) | [{"denom":"uatom","amount":"10000000"}] | -| max_deposit_period | string (time ns) | "172800000000000" | -| voting_period | string (time ns) | "172800000000000" | -| quorum | string (dec) | "0.334000000000000000" | -| threshold | string (dec) | "0.500000000000000000" | -| veto | string (dec) | "0.334000000000000000" | - -__NOTE__: The governance module contains parameters that are objects unlike other -modules. If only a subset of parameters are desired to be changed, only they need -to be included and not the entire parameter object structure. diff --git a/versioned_docs/version-0.46/integrate/modules/gov/07_client.md b/versioned_docs/version-0.46/integrate/modules/gov/07_client.md deleted file mode 100644 index 3450a052e..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/07_client.md +++ /dev/null @@ -1,1804 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `gov` module using the CLI. - -### Query - -The `query` commands allow users to query `gov` state. - -```bash -simd query gov --help -``` - -#### deposit - -The `deposit` command allows users to query a deposit for a given proposal from a given depositor. - -```bash -simd query gov deposit [proposal-id] [depositer-addr] [flags] -``` - -Example: - -```bash -simd query gov deposit 1 cosmos1.. -``` - -Example Output: - -```bash -amount: -- amount: "100" - denom: stake -depositor: cosmos1.. -proposal_id: "1" -``` - -#### deposits - -The `deposits` command allows users to query all deposits for a given proposal. - -```bash -simd query gov deposits [proposal-id] [flags] -``` - -Example: - -```bash -simd query gov deposits 1 -``` - -Example Output: - -```bash -deposits: -- amount: - - amount: "100" - denom: stake - depositor: cosmos1.. - proposal_id: "1" -pagination: - next_key: null - total: "0" -``` - -#### param - -The `param` command allows users to query a given parameter for the `gov` module. - -```bash -simd query gov param [param-type] [flags] -``` - -Example: - -```bash -simd query gov param voting -``` - -Example Output: - -```bash -voting_period: "172800000000000" -``` - -#### params - -The `params` command allows users to query all parameters for the `gov` module. - -```bash -simd query gov params [flags] -``` - -Example: - -```bash -simd query gov params -``` - -Example Output: - -```bash -deposit_params: - max_deposit_period: "172800000000000" - min_deposit: - - amount: "10000000" - denom: stake -tally_params: - quorum: "0.334000000000000000" - threshold: "0.500000000000000000" - veto_threshold: "0.334000000000000000" -voting_params: - voting_period: "172800000000000" -``` - -#### proposal - -The `proposal` command allows users to query a given proposal. - -```bash -simd query gov proposal [proposal-id] [flags] -``` - -Example: - -```bash -simd query gov proposal 1 -``` - -Example Output: - -```bash -deposit_end_time: "2022-03-30T11:50:20.819676256Z" -final_tally_result: - abstain_count: "0" - no_count: "0" - no_with_veto_count: "0" - yes_count: "0" -id: "1" -messages: -- '@type': /cosmos.bank.v1beta1.MsgSend - amount: - - amount: "10" - denom: stake - from_address: cosmos1.. - to_address: cosmos1.. -metadata: AQ== -status: PROPOSAL_STATUS_DEPOSIT_PERIOD -submit_time: "2022-03-28T11:50:20.819676256Z" -total_deposit: -- amount: "10" - denom: stake -voting_end_time: null -voting_start_time: null -``` - -#### proposals - -The `proposals` command allows users to query all proposals with optional filters. - -```bash -simd query gov proposals [flags] -``` - -Example: - -```bash -simd query gov proposals -``` - -Example Output: - -```bash -pagination: - next_key: null - total: "0" -proposals: -- deposit_end_time: "2022-03-30T11:50:20.819676256Z" - final_tally_result: - abstain_count: "0" - no_count: "0" - no_with_veto_count: "0" - yes_count: "0" - id: "1" - messages: - - '@type': /cosmos.bank.v1beta1.MsgSend - amount: - - amount: "10" - denom: stake - from_address: cosmos1.. - to_address: cosmos1.. - metadata: AQ== - status: PROPOSAL_STATUS_DEPOSIT_PERIOD - submit_time: "2022-03-28T11:50:20.819676256Z" - total_deposit: - - amount: "10" - denom: stake - voting_end_time: null - voting_start_time: null -- deposit_end_time: "2022-03-30T14:02:41.165025015Z" - final_tally_result: - abstain_count: "0" - no_count: "0" - no_with_veto_count: "0" - yes_count: "0" - id: "2" - messages: - - '@type': /cosmos.bank.v1beta1.MsgSend - amount: - - amount: "10" - denom: stake - from_address: cosmos1.. - to_address: cosmos1.. - metadata: AQ== - status: PROPOSAL_STATUS_DEPOSIT_PERIOD - submit_time: "2022-03-28T14:02:41.165025015Z" - total_deposit: - - amount: "10" - denom: stake - voting_end_time: null - voting_start_time: null -``` - -#### proposer - -The `proposer` command allows users to query the proposer for a given proposal. - -```bash -simd query gov proposer [proposal-id] [flags] -``` - -Example: - -```bash -simd query gov proposer 1 -``` - -Example Output: - -```bash -proposal_id: "1" -proposer: cosmos1.. -``` - -#### tally - -The `tally` command allows users to query the tally of a given proposal vote. - -```bash -simd query gov tally [proposal-id] [flags] -``` - -Example: - -```bash -simd query gov tally 1 -``` - -Example Output: - -```bash -abstain: "0" -"no": "0" -no_with_veto: "0" -"yes": "1" -``` - -#### vote - -The `vote` command allows users to query a vote for a given proposal. - -```bash -simd query gov vote [proposal-id] [voter-addr] [flags] -``` - -Example: - -```bash -simd query gov vote 1 cosmos1.. -``` - -Example Output: - -```bash -option: VOTE_OPTION_YES -options: -- option: VOTE_OPTION_YES - weight: "1.000000000000000000" -proposal_id: "1" -voter: cosmos1.. -``` - -#### votes - -The `votes` command allows users to query all votes for a given proposal. - -```bash -simd query gov votes [proposal-id] [flags] -``` - -Example: - -```bash -simd query gov votes 1 -``` - -Example Output: - -```bash -pagination: - next_key: null - total: "0" -votes: -- option: VOTE_OPTION_YES - options: - - option: VOTE_OPTION_YES - weight: "1.000000000000000000" - proposal_id: "1" - voter: cosmos1.. -``` - -### Transactions - -The `tx` commands allow users to interact with the `gov` module. - -```bash -simd tx gov --help -``` - -#### deposit - -The `deposit` command allows users to deposit tokens for a given proposal. - -```bash -simd tx gov deposit [proposal-id] [deposit] [flags] -``` - -Example: - -```bash -simd tx gov deposit 1 10000000stake --from cosmos1.. -``` - -#### draft-proposal - -The `draft-proposal` command allows users to draft any type of proposal. -The command returns a `draft_proposal.json`, to be used by `submit-proposal` after being completed. -The `draft_metadata.json` is meant to be uploaded to [IPFS](./08_metadata.md). - -```bash -simd tx gov draft-proposal -``` - -#### submit-proposal - -The `submit-proposal` command allows users to submit a governance proposal along with some messages and metadata. -Messages, metadata and deposit are defined in a JSON file. - -```bash -simd tx gov submit-proposal [path-to-proposal-json] [flags] -``` - -Example: - -```bash -simd tx gov submit-proposal /path/to/proposal.json --from cosmos1.. -``` - -where `proposal.json` contains: - -```json -{ - "messages": [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - "from_address": "cosmos1...", // The gov module module address - "to_address": "cosmos1...", - "amount":[{"denom": "stake","amount": "10"}] - } - ], - "metadata": "AQ==", - "deposit": "10stake" -} -``` - -#### submit-legacy-proposal - -The `submit-legacy-proposal` command allows users to submit a governance legacy proposal along with an initial deposit. - -```bash -simd tx gov submit-legacy-proposal [command] [flags] -``` - -Example: - -```bash -simd tx gov submit-legacy-proposal --title="Test Proposal" --description="testing" --type="Text" --deposit="100000000stake" --from cosmos1.. -``` - -Example (`cancel-software-upgrade`): - -```bash -simd tx gov submit-legacy-proposal cancel-software-upgrade --title="Test Proposal" --description="testing" --deposit="100000000stake" --from cosmos1.. -``` - -Example (`community-pool-spend`): - -```bash -simd tx gov submit-legacy-proposal community-pool-spend proposal.json --from cosmos1.. -``` - -```json -{ - "title": "Test Proposal", - "description": "testing, 1, 2, 3", - "recipient": "cosmos1..", - "amount": "10000000stake", - "deposit": "10000000stake" -} -``` - -Example (`param-change`): - -```bash -simd tx gov submit-legacy-proposal param-change proposal.json --from cosmos1.. -``` - -```json -{ - "title": "Test Proposal", - "description": "testing, testing, 1, 2, 3", - "changes": [ - { - "subspace": "staking", - "key": "MaxValidators", - "value": 100 - } - ], - "deposit": "10000000stake" -} -``` - -Example (`software-upgrade`): - -```bash -simd tx gov submit-legacy-proposal software-upgrade v2 --title="Test Proposal" --description="testing, testing, 1, 2, 3" --upgrade-height 1000000 --from cosmos1.. -``` - -#### vote - -The `vote` command allows users to submit a vote for a given governance proposal. - -```bash -simd tx gov vote [command] [flags] -``` - -Example: - -```bash -simd tx gov vote 1 yes --from cosmos1.. -``` - -#### weighted-vote - -The `weighted-vote` command allows users to submit a weighted vote for a given governance proposal. - -```bash -simd tx gov weighted-vote [proposal-id] [weighted-options] [flags] -``` - -Example: - -```bash -simd tx gov weighted-vote 1 yes=0.5,no=0.5 --from cosmos1.. -``` - -## gRPC - -A user can query the `gov` module using gRPC endpoints. - -### Proposal - -The `Proposal` endpoint allows users to query a given proposal. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Proposal -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Proposal -``` - -Example Output: - -```bash -{ - "proposal": { - "proposalId": "1", - "content": {"@type":"/cosmos.gov.v1beta1.TextProposal","description":"testing, testing, 1, 2, 3","title":"Test Proposal"}, - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "finalTallyResult": { - "yes": "0", - "abstain": "0", - "no": "0", - "noWithVeto": "0" - }, - "submitTime": "2021-09-16T19:40:08.712440474Z", - "depositEndTime": "2021-09-18T19:40:08.712440474Z", - "totalDeposit": [ - { - "denom": "stake", - "amount": "10000000" - } - ], - "votingStartTime": "2021-09-16T19:40:08.712440474Z", - "votingEndTime": "2021-09-18T19:40:08.712440474Z" - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Proposal -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1.Query/Proposal -``` - -Example Output: - -```bash -{ - "proposal": { - "id": "1", - "messages": [ - {"@type":"/cosmos.bank.v1beta1.MsgSend","amount":[{"denom":"stake","amount":"10"}],"fromAddress":"cosmos1..","toAddress":"cosmos1.."} - ], - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "finalTallyResult": { - "yesCount": "0", - "abstainCount": "0", - "noCount": "0", - "noWithVetoCount": "0" - }, - "submitTime": "2022-03-28T11:50:20.819676256Z", - "depositEndTime": "2022-03-30T11:50:20.819676256Z", - "totalDeposit": [ - { - "denom": "stake", - "amount": "10000000" - } - ], - "votingStartTime": "2022-03-28T14:25:26.644857113Z", - "votingEndTime": "2022-03-30T14:25:26.644857113Z", - "metadata": "AQ==" - } -} -``` - - -### Proposals - -The `Proposals` endpoint allows users to query all proposals with optional filters. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Proposals -``` - -Example: - -```bash -grpcurl -plaintext \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Proposals -``` - -Example Output: - -```bash -{ - "proposals": [ - { - "proposalId": "1", - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "finalTallyResult": { - "yes": "0", - "abstain": "0", - "no": "0", - "noWithVeto": "0" - }, - "submitTime": "2022-03-28T11:50:20.819676256Z", - "depositEndTime": "2022-03-30T11:50:20.819676256Z", - "totalDeposit": [ - { - "denom": "stake", - "amount": "10000000010" - } - ], - "votingStartTime": "2022-03-28T14:25:26.644857113Z", - "votingEndTime": "2022-03-30T14:25:26.644857113Z" - }, - { - "proposalId": "2", - "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", - "finalTallyResult": { - "yes": "0", - "abstain": "0", - "no": "0", - "noWithVeto": "0" - }, - "submitTime": "2022-03-28T14:02:41.165025015Z", - "depositEndTime": "2022-03-30T14:02:41.165025015Z", - "totalDeposit": [ - { - "denom": "stake", - "amount": "10" - } - ], - "votingStartTime": "0001-01-01T00:00:00Z", - "votingEndTime": "0001-01-01T00:00:00Z" - } - ], - "pagination": { - "total": "2" - } -} - -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Proposals -``` - -Example: - -```bash -grpcurl -plaintext \ - localhost:9090 \ - cosmos.gov.v1.Query/Proposals -``` - -Example Output: - -```bash -{ - "proposals": [ - { - "id": "1", - "messages": [ - {"@type":"/cosmos.bank.v1beta1.MsgSend","amount":[{"denom":"stake","amount":"10"}],"fromAddress":"cosmos1..","toAddress":"cosmos1.."} - ], - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "finalTallyResult": { - "yesCount": "0", - "abstainCount": "0", - "noCount": "0", - "noWithVetoCount": "0" - }, - "submitTime": "2022-03-28T11:50:20.819676256Z", - "depositEndTime": "2022-03-30T11:50:20.819676256Z", - "totalDeposit": [ - { - "denom": "stake", - "amount": "10000000010" - } - ], - "votingStartTime": "2022-03-28T14:25:26.644857113Z", - "votingEndTime": "2022-03-30T14:25:26.644857113Z", - "metadata": "AQ==" - }, - { - "id": "2", - "messages": [ - {"@type":"/cosmos.bank.v1beta1.MsgSend","amount":[{"denom":"stake","amount":"10"}],"fromAddress":"cosmos1..","toAddress":"cosmos1.."} - ], - "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", - "finalTallyResult": { - "yesCount": "0", - "abstainCount": "0", - "noCount": "0", - "noWithVetoCount": "0" - }, - "submitTime": "2022-03-28T14:02:41.165025015Z", - "depositEndTime": "2022-03-30T14:02:41.165025015Z", - "totalDeposit": [ - { - "denom": "stake", - "amount": "10" - } - ], - "metadata": "AQ==" - } - ], - "pagination": { - "total": "2" - } -} -``` - -### Vote - -The `Vote` endpoint allows users to query a vote for a given proposal. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Vote -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1","voter":"cosmos1.."}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Vote -``` - -Example Output: - -```bash -{ - "vote": { - "proposalId": "1", - "voter": "cosmos1..", - "option": "VOTE_OPTION_YES", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1000000000000000000" - } - ] - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Vote -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1","voter":"cosmos1.."}' \ - localhost:9090 \ - cosmos.gov.v1.Query/Vote -``` - -Example Output: - -```bash -{ - "vote": { - "proposalId": "1", - "voter": "cosmos1..", - "option": "VOTE_OPTION_YES", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ] - } -} -``` - -### Votes - -The `Votes` endpoint allows users to query all votes for a given proposal. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Votes -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Votes -``` - -Example Output: - -```bash -{ - "votes": [ - { - "proposalId": "1", - "voter": "cosmos1..", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1000000000000000000" - } - ] - } - ], - "pagination": { - "total": "1" - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Votes -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1.Query/Votes -``` - -Example Output: - -```bash -{ - "votes": [ - { - "proposalId": "1", - "voter": "cosmos1..", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ] - } - ], - "pagination": { - "total": "1" - } -} -``` - -### Params - -The `Params` endpoint allows users to query all parameters for the `gov` module. - - - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Params -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"params_type":"voting"}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Params -``` - -Example Output: - -```bash -{ - "votingParams": { - "votingPeriod": "172800s" - }, - "depositParams": { - "maxDepositPeriod": "0s" - }, - "tallyParams": { - "quorum": "MA==", - "threshold": "MA==", - "vetoThreshold": "MA==" - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Params -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"params_type":"voting"}' \ - localhost:9090 \ - cosmos.gov.v1.Query/Params -``` - -Example Output: - -```bash -{ - "votingParams": { - "votingPeriod": "172800s" - } -} -``` - -### Deposit - -The `Deposit` endpoint allows users to query a deposit for a given proposal from a given depositor. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Deposit -``` - -Example: - -```bash -grpcurl -plaintext \ - '{"proposal_id":"1","depositor":"cosmos1.."}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Deposit -``` - -Example Output: - -```bash -{ - "deposit": { - "proposalId": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Deposit -``` - -Example: - -```bash -grpcurl -plaintext \ - '{"proposal_id":"1","depositor":"cosmos1.."}' \ - localhost:9090 \ - cosmos.gov.v1.Query/Deposit -``` - -Example Output: - -```bash -{ - "deposit": { - "proposalId": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } -} -``` - -### deposits - -The `Deposits` endpoint allows users to query all deposits for a given proposal. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/Deposits -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/Deposits -``` - -Example Output: - -```bash -{ - "deposits": [ - { - "proposalId": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } - ], - "pagination": { - "total": "1" - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/Deposits -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1.Query/Deposits -``` - -Example Output: - -```bash -{ - "deposits": [ - { - "proposalId": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } - ], - "pagination": { - "total": "1" - } -} -``` - -### TallyResult - -The `TallyResult` endpoint allows users to query the tally of a given proposal. - -Using legacy v1beta1: - -```bash -cosmos.gov.v1beta1.Query/TallyResult -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1beta1.Query/TallyResult -``` - -Example Output: - -```bash -{ - "tally": { - "yes": "1000000", - "abstain": "0", - "no": "0", - "noWithVeto": "0" - } -} -``` - -Using v1: - -```bash -cosmos.gov.v1.Query/TallyResult -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"proposal_id":"1"}' \ - localhost:9090 \ - cosmos.gov.v1.Query/TallyResult -``` - -Example Output: - -```bash -{ - "tally": { - "yes": "1000000", - "abstain": "0", - "no": "0", - "noWithVeto": "0" - } -} -``` - -## REST - -A user can query the `gov` module using REST endpoints. - -### proposal - -The `proposals` endpoint allows users to query a given proposal. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals/{proposal_id} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals/1 -``` - -Example Output: - -```bash -{ - "proposal": { - "proposal_id": "1", - "content": null, - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "final_tally_result": { - "yes": "0", - "abstain": "0", - "no": "0", - "no_with_veto": "0" - }, - "submit_time": "2022-03-28T11:50:20.819676256Z", - "deposit_end_time": "2022-03-30T11:50:20.819676256Z", - "total_deposit": [ - { - "denom": "stake", - "amount": "10000000010" - } - ], - "voting_start_time": "2022-03-28T14:25:26.644857113Z", - "voting_end_time": "2022-03-30T14:25:26.644857113Z" - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals/{proposal_id} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals/1 -``` - -Example Output: - -```bash -{ - "proposal": { - "id": "1", - "messages": [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - "from_address": "cosmos1..", - "to_address": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10" - } - ] - } - ], - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "final_tally_result": { - "yes_count": "0", - "abstain_count": "0", - "no_count": "0", - "no_with_veto_count": "0" - }, - "submit_time": "2022-03-28T11:50:20.819676256Z", - "deposit_end_time": "2022-03-30T11:50:20.819676256Z", - "total_deposit": [ - { - "denom": "stake", - "amount": "10000000" - } - ], - "voting_start_time": "2022-03-28T14:25:26.644857113Z", - "voting_end_time": "2022-03-30T14:25:26.644857113Z", - "metadata": "AQ==" - } -} -``` - -### proposals - -The `proposals` endpoint also allows users to query all proposals with optional filters. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals -``` - -Example Output: - -```bash -{ - "proposals": [ - { - "proposal_id": "1", - "content": null, - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "final_tally_result": { - "yes": "0", - "abstain": "0", - "no": "0", - "no_with_veto": "0" - }, - "submit_time": "2022-03-28T11:50:20.819676256Z", - "deposit_end_time": "2022-03-30T11:50:20.819676256Z", - "total_deposit": [ - { - "denom": "stake", - "amount": "10000000" - } - ], - "voting_start_time": "2022-03-28T14:25:26.644857113Z", - "voting_end_time": "2022-03-30T14:25:26.644857113Z" - }, - { - "proposal_id": "2", - "content": null, - "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", - "final_tally_result": { - "yes": "0", - "abstain": "0", - "no": "0", - "no_with_veto": "0" - }, - "submit_time": "2022-03-28T14:02:41.165025015Z", - "deposit_end_time": "2022-03-30T14:02:41.165025015Z", - "total_deposit": [ - { - "denom": "stake", - "amount": "10" - } - ], - "voting_start_time": "0001-01-01T00:00:00Z", - "voting_end_time": "0001-01-01T00:00:00Z" - } - ], - "pagination": { - "next_key": null, - "total": "2" - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals -``` - -Example Output: - -```bash -{ - "proposals": [ - { - "id": "1", - "messages": [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - "from_address": "cosmos1..", - "to_address": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10" - } - ] - } - ], - "status": "PROPOSAL_STATUS_VOTING_PERIOD", - "final_tally_result": { - "yes_count": "0", - "abstain_count": "0", - "no_count": "0", - "no_with_veto_count": "0" - }, - "submit_time": "2022-03-28T11:50:20.819676256Z", - "deposit_end_time": "2022-03-30T11:50:20.819676256Z", - "total_deposit": [ - { - "denom": "stake", - "amount": "10000000010" - } - ], - "voting_start_time": "2022-03-28T14:25:26.644857113Z", - "voting_end_time": "2022-03-30T14:25:26.644857113Z", - "metadata": "AQ==" - }, - { - "id": "2", - "messages": [ - { - "@type": "/cosmos.bank.v1beta1.MsgSend", - "from_address": "cosmos1..", - "to_address": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10" - } - ] - } - ], - "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", - "final_tally_result": { - "yes_count": "0", - "abstain_count": "0", - "no_count": "0", - "no_with_veto_count": "0" - }, - "submit_time": "2022-03-28T14:02:41.165025015Z", - "deposit_end_time": "2022-03-30T14:02:41.165025015Z", - "total_deposit": [ - { - "denom": "stake", - "amount": "10" - } - ], - "voting_start_time": null, - "voting_end_time": null, - "metadata": "AQ==" - } - ], - "pagination": { - "next_key": null, - "total": "2" - } -} -``` - -### voter vote - -The `votes` endpoint allows users to query a vote for a given proposal. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals/1/votes/cosmos1.. -``` - -Example Output: - -```bash -{ - "vote": { - "proposal_id": "1", - "voter": "cosmos1..", - "option": "VOTE_OPTION_YES", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ] - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals/1/votes/cosmos1.. -``` - -Example Output: - -```bash -{ - "vote": { - "proposal_id": "1", - "voter": "cosmos1..", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ], - "metadata": "" - } -} -``` - -### votes - -The `votes` endpoint allows users to query all votes for a given proposal. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals/{proposal_id}/votes -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals/1/votes -``` - -Example Output: - -```bash -{ - "votes": [ - { - "proposal_id": "1", - "voter": "cosmos1..", - "option": "VOTE_OPTION_YES", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ] - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals/{proposal_id}/votes -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals/1/votes -``` - -Example Output: - -```bash -{ - "votes": [ - { - "proposal_id": "1", - "voter": "cosmos1..", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ], - "metadata": "" - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### params - -The `params` endpoint allows users to query all parameters for the `gov` module. - - - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/params/{params_type} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/params/voting -``` - -Example Output: - -```bash -{ - "voting_params": { - "voting_period": "172800s" - }, - "deposit_params": { - "min_deposit": [ - ], - "max_deposit_period": "0s" - }, - "tally_params": { - "quorum": "0.000000000000000000", - "threshold": "0.000000000000000000", - "veto_threshold": "0.000000000000000000" - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/params/{params_type} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/params/voting -``` - -Example Output: - -```bash -{ - "voting_params": { - "voting_period": "172800s" - }, - "deposit_params": { - "min_deposit": [ - ], - "max_deposit_period": "0s" - }, - "tally_params": { - "quorum": "0.000000000000000000", - "threshold": "0.000000000000000000", - "veto_threshold": "0.000000000000000000" - } -} -``` - -### deposits - -The `deposits` endpoint allows users to query a deposit for a given proposal from a given depositor. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals/1/deposits/cosmos1.. -``` - -Example Output: - -```bash -{ - "deposit": { - "proposal_id": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals/{proposal_id}/deposits/{depositor} -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals/1/deposits/cosmos1.. -``` - -Example Output: - -```bash -{ - "deposit": { - "proposal_id": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } -} -``` - -### proposal deposits - -The `deposits` endpoint allows users to query all deposits for a given proposal. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals/1/deposits -``` - -Example Output: - -```bash -{ - "deposits": [ - { - "proposal_id": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals/{proposal_id}/deposits -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals/1/deposits -``` - -Example Output: - -```bash -{ - "deposits": [ - { - "proposal_id": "1", - "depositor": "cosmos1..", - "amount": [ - { - "denom": "stake", - "amount": "10000000" - } - ] - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### tally - -The `tally` endpoint allows users to query the tally of a given proposal. - -Using legacy v1beta1: - -```bash -/cosmos/gov/v1beta1/proposals/{proposal_id}/tally -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1beta1/proposals/1/tally -``` - -Example Output: - -```bash -{ - "tally": { - "yes": "1000000", - "abstain": "0", - "no": "0", - "no_with_veto": "0" - } -} -``` - -Using v1: - -```bash -/cosmos/gov/v1/proposals/{proposal_id}/tally -``` - -Example: - -```bash -curl localhost:1317/cosmos/gov/v1/proposals/1/tally -``` - -Example Output: - -```bash -{ - "tally": { - "yes": "1000000", - "abstain": "0", - "no": "0", - "no_with_veto": "0" - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/gov/08_metadata.md b/versioned_docs/version-0.46/integrate/modules/gov/08_metadata.md deleted file mode 100644 index f5a0b3943..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/08_metadata.md +++ /dev/null @@ -1,32 +0,0 @@ - - -# Metadata - -The gov module has two locations for metadata where users can provide further context about the on-chain actions they are taking. By default all metadata fields have a 255 character length field where metadata can be stored in json format, either on-chain or off-chain depending on the amount of data required. Here we provide a recommendation for the json structure and where the data should be stored. There are two important factors in making these recommendations. First, that the gov and group modules are consistent with one another, note the number of proposals made by all groups may be quite large. Second, that client applications such as block explorers and governance interfaces have confidence in the consistency of metadata structure accross chains. - -## Proposal - -Location: off-chain as json object stored on IPFS (mirrors [group proposal](../../group/spec/06_metadata.md#proposal)) - -```json -{ - "title": "", - "authors": "", - "summary": "", - "details": "", - "proposal_forum_url": "", - "vote_option_context": "", -} -``` - -## Vote - -Location: on-chain as json within 255 character limit (mirrors [group vote](../../group/spec/06_metadata.md#vote)) - -```json -{ - "justification": "", -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/gov/README.md b/versioned_docs/version-0.46/integrate/modules/gov/README.md deleted file mode 100644 index 3a5a2decf..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/README.md +++ /dev/null @@ -1,65 +0,0 @@ - - -# `gov` - -## Abstract - -This paper specifies the Governance module of the Cosmos-SDK, which was first -described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in -June 2016. - -The module enables Cosmos-SDK based blockchain to support an on-chain governance -system. In this system, holders of the native staking token of the chain can vote -on proposals on a 1 token 1 vote basis. Next is a list of features the module -currently supports: - -* **Proposal submission:** Users can submit proposals with a deposit. Once the -minimum deposit is reached, proposal enters voting period -* **Vote:** Participants can vote on proposals that reached MinDeposit -* **Inheritance and penalties:** Delegators inherit their validator's vote if -they don't vote themselves. -* **Claiming deposit:** Users that deposited on proposals can recover their -deposits if the proposal was accepted OR if the proposal never entered voting period. - -This module will be used in the Cosmos Hub, the first Hub in the Cosmos network. -Features that may be added in the future are described in [Future Improvements](05_future_improvements.md). - -## Contents - -The following specification uses *ATOM* as the native staking token. The module -can be adapted to any Proof-Of-Stake blockchain by replacing *ATOM* with the native -staking token of the chain. - -1. **[Concepts](01_concepts.md)** - * [Proposal submission](01_concepts.md#proposal-submission) - * [Deposit](01_concepts.md#Deposit) - * [Vote](01_concepts.md#vote) - * [Software Upgrade](01_concepts.md#software-upgrade) -2. **[State](02_state.md)** - * [Parameters and base types](02_state.md#parameters-and-base-types) - * [Deposit](02_state.md#deposit) - * [ValidatorGovInfo](02_state.md#validatorgovinfo) - * [Proposals](02_state.md#proposals) - * [Stores](02_state.md#stores) - * [Proposal Processing Queue](02_state.md#proposal-processing-queue) -3. **[Messages](03_messages.md)** - * [Proposal Submission](03_messages.md#proposal-submission) - * [Deposit](03_messages.md#deposit) - * [Vote](03_messages.md#vote) -4. **[Events](04_events.md)** - * [EndBlocker](04_events.md#endblocker) - * [Handlers](04_events.md#handlers) -5. **[Future Improvements](05_future_improvements.md)** -6. **[Parameters](06_params.md)** -7. **[Client](07_client.md)** - * [CLI](07_client.md#cli) - * [gRPC](07_client.md#grpc) - * [REST](07_client.md#rest) -8. **[Metadata](08_metadata.md)** - * [Proposal](08_metadata.md#proposal) - * [Vote](08_metadata.md#vote) diff --git a/versioned_docs/version-0.46/integrate/modules/gov/_category_.json b/versioned_docs/version-0.46/integrate/modules/gov/_category_.json deleted file mode 100644 index 03024ef30..000000000 --- a/versioned_docs/version-0.46/integrate/modules/gov/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Gov", - "position": 8, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/mint/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/mint/01_concepts.md deleted file mode 100644 index 11669404d..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/01_concepts.md +++ /dev/null @@ -1,28 +0,0 @@ - - -# Concepts - -## The Minting Mechanism - -The minting mechanism was designed to: - -* allow for a flexible inflation rate determined by market demand targeting a particular bonded-stake ratio -* effect a balance between market liquidity and staked supply - -In order to best determine the appropriate market rate for inflation rewards, a -moving change rate is used. The moving change rate mechanism ensures that if -the % bonded is either over or under the goal %-bonded, the inflation rate will -adjust to further incentivize or disincentivize being bonded, respectively. Setting the goal -%-bonded at less than 100% encourages the network to maintain some non-staked tokens -which should help provide some liquidity. - -It can be broken down in the following way: - -* If the inflation rate is below the goal %-bonded the inflation rate will - increase until a maximum value is reached -* If the goal % bonded (67% in Cosmos-Hub) is maintained, then the inflation - rate will stay constant -* If the inflation rate is above the goal %-bonded the inflation rate will - decrease until a minimum value is reached diff --git a/versioned_docs/version-0.46/integrate/modules/mint/02_state.md b/versioned_docs/version-0.46/integrate/modules/mint/02_state.md deleted file mode 100644 index 2fa04b1ef..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/02_state.md +++ /dev/null @@ -1,21 +0,0 @@ - - -# State - -## Minter - -The minter is a space for holding current inflation information. - -* Minter: `0x00 -> ProtocolBuffer(minter)` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/mint/v1beta1/mint.proto#L9-L23 - -## Params - -Minting params are held in the global params store. - -* Params: `mint/params -> legacy_amino(params)` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/mint/v1beta1/mint.proto#L25-L57 diff --git a/versioned_docs/version-0.46/integrate/modules/mint/03_begin_block.md b/versioned_docs/version-0.46/integrate/modules/mint/03_begin_block.md deleted file mode 100644 index 67bd7d8a8..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/03_begin_block.md +++ /dev/null @@ -1,66 +0,0 @@ - - -# Begin-Block - -Minting parameters are recalculated and inflation -paid at the beginning of each block. - -## Inflation rate calculation - -Inflation rate is calculated using an "inflation calculation function" that's -passed to the `NewAppModule` function. If no function is passed, then the SDK's -default inflation function will be used (`NextInflationRate`). In case a custom -inflation calculation logic is needed, this can be achieved by defining and -passing a function that matches `InflationCalculationFn`'s signature. - -```go -type InflationCalculationFn func(ctx sdk.Context, minter Minter, params Params, bondedRatio sdk.Dec) sdk.Dec -``` - -### NextInflationRate - -The target annual inflation rate is recalculated each block. -The inflation is also subject to a rate change (positive or negative) -depending on the distance from the desired ratio (67%). The maximum rate change -possible is defined to be 13% per year, however the annual inflation is capped -as between 7% and 20%. - -```go -NextInflationRate(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) { - inflationRateChangePerYear = (1 - bondedRatio/params.GoalBonded) * params.InflationRateChange - inflationRateChange = inflationRateChangePerYear/blocksPerYr - - // increase the new annual inflation for this next cycle - inflation += inflationRateChange - if inflation > params.InflationMax { - inflation = params.InflationMax - } - if inflation < params.InflationMin { - inflation = params.InflationMin - } - - return inflation -} -``` - -## NextAnnualProvisions - -Calculate the annual provisions based on current total supply and inflation -rate. This parameter is calculated once per block. - -```go -NextAnnualProvisions(params Params, totalSupply sdk.Dec) (provisions sdk.Dec) { - return Inflation * totalSupply -``` - -## BlockProvision - -Calculate the provisions generated for each block based on current annual provisions. The provisions are then minted by the `mint` module's `ModuleMinterAccount` and then transferred to the `auth`'s `FeeCollector` `ModuleAccount`. - -```go -BlockProvision(params Params) sdk.Coin { - provisionAmt = AnnualProvisions/ params.BlocksPerYear - return sdk.NewCoin(params.MintDenom, provisionAmt.Truncate()) -``` diff --git a/versioned_docs/version-0.46/integrate/modules/mint/04_params.md b/versioned_docs/version-0.46/integrate/modules/mint/04_params.md deleted file mode 100644 index ed18e5557..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/04_params.md +++ /dev/null @@ -1,16 +0,0 @@ - - -# Parameters - -The minting module contains the following parameters: - -| Key | Type | Example | -|---------------------|-----------------|------------------------| -| MintDenom | string | "uatom" | -| InflationRateChange | string (dec) | "0.130000000000000000" | -| InflationMax | string (dec) | "0.200000000000000000" | -| InflationMin | string (dec) | "0.070000000000000000" | -| GoalBonded | string (dec) | "0.670000000000000000" | -| BlocksPerYear | string (uint64) | "6311520" | diff --git a/versioned_docs/version-0.46/integrate/modules/mint/05_events.md b/versioned_docs/version-0.46/integrate/modules/mint/05_events.md deleted file mode 100644 index f6130a3b9..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/05_events.md +++ /dev/null @@ -1,16 +0,0 @@ - - -# Events - -The minting module emits the following events: - -## BeginBlocker - -| Type | Attribute Key | Attribute Value | -|------|-------------------|--------------------| -| mint | bonded_ratio | {bondedRatio} | -| mint | inflation | {inflation} | -| mint | annual_provisions | {annualProvisions} | -| mint | amount | {amount} | diff --git a/versioned_docs/version-0.46/integrate/modules/mint/06_client.md b/versioned_docs/version-0.46/integrate/modules/mint/06_client.md deleted file mode 100644 index a79063a99..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/06_client.md +++ /dev/null @@ -1,224 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `mint` module using the CLI. - -### Query - -The `query` commands allow users to query `mint` state. - -```sh -simd query mint --help -``` - -#### annual-provisions - -The `annual-provisions` command allow users to query the current minting annual provisions value - -```sh -simd query mint annual-provisions [flags] -``` - -Example: - -```sh -simd query mint annual-provisions -``` - -Example Output: - -```sh -22268504368893.612100895088410693 -``` - -#### inflation - -The `inflation` command allow users to query the current minting inflation value - -```sh -simd query mint inflation [flags] -``` - -Example: - -```sh -simd query mint inflation -``` - -Example Output: - -```sh -0.199200302563256955 -``` - -#### params - -The `params` command allow users to query the current minting parameters - -```sh -simd query mint params [flags] -``` - -Example: - -```yml -blocks_per_year: "4360000" -goal_bonded: "0.670000000000000000" -inflation_max: "0.200000000000000000" -inflation_min: "0.070000000000000000" -inflation_rate_change: "0.130000000000000000" -mint_denom: stake -``` - -## gRPC - -A user can query the `mint` module using gRPC endpoints. - -### AnnualProvisions - -The `AnnualProvisions` endpoint allow users to query the current minting annual provisions value - -```sh -/cosmos.mint.v1beta1.Query/AnnualProvisions -``` - -Example: - -```sh -grpcurl -plaintext localhost:9090 cosmos.mint.v1beta1.Query/AnnualProvisions -``` - -Example Output: - -```json -{ - "annualProvisions": "1432452520532626265712995618" -} -``` - -### Inflation - -The `Inflation` endpoint allow users to query the current minting inflation value - -```sh -/cosmos.mint.v1beta1.Query/Inflation -``` - -Example: - -```sh -grpcurl -plaintext localhost:9090 cosmos.mint.v1beta1.Query/Inflation -``` - -Example Output: - -```json -{ - "inflation": "130197115720711261" -} -``` - -### Params - -The `Params` endpoint allow users to query the current minting parameters - -```sh -/cosmos.mint.v1beta1.Query/Params -``` - -Example: - -```sh -grpcurl -plaintext localhost:9090 cosmos.mint.v1beta1.Query/Params -``` - -Example Output: - -```json -{ - "params": { - "mintDenom": "stake", - "inflationRateChange": "130000000000000000", - "inflationMax": "200000000000000000", - "inflationMin": "70000000000000000", - "goalBonded": "670000000000000000", - "blocksPerYear": "6311520" - } -} -``` - -## REST - -A user can query the `mint` module using REST endpoints. - -### annual-provisions - -```sh -/cosmos/mint/v1beta1/annual_provisions -``` - -Example: - -```sh -curl "localhost:1317/cosmos/mint/v1beta1/annual_provisions" -``` - -Example Output: - -```json -{ - "annualProvisions": "1432452520532626265712995618" -} -``` - -### inflation - -```sh -/cosmos/mint/v1beta1/inflation -``` - -Example: - -```sh -curl "localhost:1317/cosmos/mint/v1beta1/inflation" -``` - -Example Output: - -```json -{ - "inflation": "130197115720711261" -} -``` - -### params - -```sh -/cosmos/mint/v1beta1/params -``` - -Example: - -```sh -curl "localhost:1317/cosmos/mint/v1beta1/params" -``` - -Example Output: - -```json -{ - "params": { - "mintDenom": "stake", - "inflationRateChange": "130000000000000000", - "inflationMax": "200000000000000000", - "inflationMin": "70000000000000000", - "goalBonded": "670000000000000000", - "blocksPerYear": "6311520" - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/mint/README.md b/versioned_docs/version-0.46/integrate/modules/mint/README.md deleted file mode 100644 index 400d723b2..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/README.md +++ /dev/null @@ -1,26 +0,0 @@ - - -# `mint` - -## Contents - -1. **[Concept](01_concepts.md)** -2. **[State](02_state.md)** - * [Minter](02_state.md#minter) - * [Params](02_state.md#params) -3. **[Begin-Block](03_begin_block.md)** - * [NextInflationRate](03_begin_block.md#nextinflationrate) - * [NextAnnualProvisions](03_begin_block.md#nextannualprovisions) - * [BlockProvision](03_begin_block.md#blockprovision) -4. **[Parameters](04_params.md)** -5. **[Events](05_events.md)** - * [BeginBlocker](05_events.md#beginblocker) -6. **[Client](06_client.md)** - * [CLI](06_client.md#cli) - * [gRPC](06_client.md#grpc) - * [REST](06_client.md#rest) diff --git a/versioned_docs/version-0.46/integrate/modules/mint/_category_.json b/versioned_docs/version-0.46/integrate/modules/mint/_category_.json deleted file mode 100644 index a2d53fc97..000000000 --- a/versioned_docs/version-0.46/integrate/modules/mint/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Mint", - "position": 9, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/params/01_keeper.md b/versioned_docs/version-0.46/integrate/modules/params/01_keeper.md deleted file mode 100644 index fa97ec5b3..000000000 --- a/versioned_docs/version-0.46/integrate/modules/params/01_keeper.md +++ /dev/null @@ -1,19 +0,0 @@ - - -# Keeper - -In the app initialization stage, [subspaces](02_subspace.md) can be allocated for other modules' keeper using `Keeper.Subspace` and are stored in `Keeper.spaces`. Then, those modules can have a reference to their specific parameter store through `Keeper.GetSubspace`. - -Example: - -```go -type ExampleKeeper struct { - paramSpace paramtypes.Subspace -} - -func (k ExampleKeeper) SetParams(ctx sdk.Context, params types.Params) { - k.paramSpace.SetParamSet(ctx, ¶ms) -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/params/02_subspace.md b/versioned_docs/version-0.46/integrate/modules/params/02_subspace.md deleted file mode 100644 index 2eaa391a2..000000000 --- a/versioned_docs/version-0.46/integrate/modules/params/02_subspace.md +++ /dev/null @@ -1,38 +0,0 @@ - - -# Subspace - -`Subspace` is a prefixed subspace of the parameter store. Each module which uses the -parameter store will take a `Subspace` to isolate permission to access. - -## Key - -Parameter keys are human readable alphanumeric strings. A parameter for the key -`"ExampleParameter"` is stored under `[]byte("SubspaceName" + "/" + "ExampleParameter")`, - where `"SubspaceName"` is the name of the subspace. - -Subkeys are secondary parameter keys those are used along with a primary parameter key. -Subkeys can be used for grouping or dynamic parameter key generation during runtime. - -## KeyTable - -All of the parameter keys that will be used should be registered at the compile -time. `KeyTable` is essentially a `map[string]attribute`, where the `string` is a parameter key. - -Currently, `attribute` consists of a `reflect.Type`, which indicates the parameter -type to check that provided key and value are compatible and registered, as well as a function `ValueValidatorFn` to validate values. - -Only primary keys have to be registered on the `KeyTable`. Subkeys inherit the -attribute of the primary key. - -## ParamSet - -Modules often define parameters as a proto message. The generated struct can implement -`ParamSet` interface to be used with the following methods: - -* `KeyTable.RegisterParamSet()`: registers all parameters in the struct -* `Subspace.{Get, Set}ParamSet()`: Get to & Set from the struct - -The implementor should be a pointer in order to use `GetParamSet()`. diff --git a/versioned_docs/version-0.46/integrate/modules/params/README.md b/versioned_docs/version-0.46/integrate/modules/params/README.md deleted file mode 100644 index 6f0387a01..000000000 --- a/versioned_docs/version-0.46/integrate/modules/params/README.md +++ /dev/null @@ -1,29 +0,0 @@ - - -# `params` - -## Abstract - -Package params provides a globally available parameter store. - -There are two main types, Keeper and Subspace. Subspace is an isolated namespace for a -paramstore, where keys are prefixed by preconfigured spacename. Keeper has a -permission to access all existing spaces. - -Subspace can be used by the individual keepers, which need a private parameter store -that the other keepers cannot modify. The params Keeper can be used to add a route to `x/gov` router in order to modify any parameter in case a proposal passes. - -The following contents explains how to use params module for master and user modules. - -## Contents - -1. **[Keeper](01_keeper.md)** -2. **[Subspace](02_subspace.md)** - * [Key](02_subspace.md#key) - * [KeyTable](02_subspace.md#keytable) - * [ParamSet](02_subspace.md#paramset) diff --git a/versioned_docs/version-0.46/integrate/modules/params/_category_.json b/versioned_docs/version-0.46/integrate/modules/params/_category_.json deleted file mode 100644 index e79e238db..000000000 --- a/versioned_docs/version-0.46/integrate/modules/params/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Params", - "position": 10, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/slashing/01_concepts.md deleted file mode 100644 index ea7c6b319..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/01_concepts.md +++ /dev/null @@ -1,57 +0,0 @@ - - -# Concepts - -## States - -At any given time, there are any number of validators registered in the state -machine. Each block, the top `MaxValidators` (defined by `x/staking`) validators -who are not jailed become _bonded_, meaning that they may propose and vote on -blocks. Validators who are _bonded_ are _at stake_, meaning that part or all of -their stake and their delegators' stake is at risk if they commit a protocol fault. - -For each of these validators we keep a `ValidatorSigningInfo` record that contains -information partaining to validator's liveness and other infraction related -attributes. - -## Tombstone Caps - -In order to mitigate the impact of initially likely categories of non-malicious -protocol faults, the Cosmos Hub implements for each validator -a _tombstone_ cap, which only allows a validator to be slashed once for a double -sign fault. For example, if you misconfigure your HSM and double-sign a bunch of -old blocks, you'll only be punished for the first double-sign (and then immediately tombstombed). This will still be quite expensive and desirable to avoid, but tombstone caps -somewhat blunt the economic impact of unintentional misconfiguration. - -Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between. - -## Infraction Timelines - -To illustrate how the `x/slashing` module handles submitted evidence through -Tendermint consensus, consider the following examples: - -**Definitions**: - -_[_ : timeline start -_]_ : timeline end -_Cn_ : infraction `n` committed -_Dn_ : infraction `n` discovered -_Vb_ : validator bonded -_Vu_ : validator unbonded - -### Single Double Sign Infraction - -\[----------C1----D1,Vu-----\] - -A single infraction is committed then later discovered, at which point the -validator is unbonded and slashed at the full amount for the infraction. - -### Multiple Double Sign Infractions - -\[----------C1--C2---C3---D1,D2,D3Vu-----\] - -Multiple infractions are committed and then later discovered, at which point the -validator is jailed and slashed for only one infraction. Because the validator -is also tombstoned, they can not rejoin the validator set. diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/02_state.md b/versioned_docs/version-0.46/integrate/modules/slashing/02_state.md deleted file mode 100644 index 50aa0e141..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/02_state.md +++ /dev/null @@ -1,51 +0,0 @@ - - -# State - -## Signing Info (Liveness) - -Every block includes a set of precommits by the validators for the previous block, -known as the `LastCommitInfo` provided by Tendermint. A `LastCommitInfo` is valid so -long as it contains precommits from +2/3 of total voting power. - -Proposers are incentivized to include precommits from all validators in the Tendermint `LastCommitInfo` -by receiving additional fees proportional to the difference between the voting -power included in the `LastCommitInfo` and +2/3 (see [fee distribution](x/distribution/spec/03_begin_block.md)). - -```go -type LastCommitInfo struct { - Round int32 - Votes []VoteInfo -} -``` - -Validators are penalized for failing to be included in the `LastCommitInfo` for some -number of blocks by being automatically jailed, potentially slashed, and unbonded. - -Information about validator's liveness activity is tracked through `ValidatorSigningInfo`. -It is indexed in the store as follows: - -* ValidatorSigningInfo: `0x01 | ConsAddrLen (1 byte) | ConsAddress -> ProtocolBuffer(ValSigningInfo)` -* MissedBlocksBitArray: `0x02 | ConsAddrLen (1 byte) | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format) - -The first mapping allows us to easily lookup the recent signing info for a -validator based on the validator's consensus address. - -The second mapping (`MissedBlocksBitArray`) acts -as a bit-array of size `SignedBlocksWindow` that tells us if the validator missed -the block for a given index in the bit-array. The index in the bit-array is given -as little endian uint64. -The result is a `varint` that takes on `0` or `1`, where `0` indicates the -validator did not miss (did sign) the corresponding block, and `1` indicates -they missed the block (did not sign). - -Note that the `MissedBlocksBitArray` is not explicitly initialized up-front. Keys -are added as we progress through the first `SignedBlocksWindow` blocks for a newly -bonded validator. The `SignedBlocksWindow` parameter defines the size -(number of blocks) of the sliding window used to track validator liveness. - -The information stored for tracking validator liveness is as follows: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/slashing/v1beta1/slashing.proto#L12-L33 diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/03_messages.md b/versioned_docs/version-0.46/integrate/modules/slashing/03_messages.md deleted file mode 100644 index 874931579..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/03_messages.md +++ /dev/null @@ -1,51 +0,0 @@ - - -# Messages - -In this section we describe the processing of messages for the `slashing` module. - -## Unjail - -If a validator was automatically unbonded due to downtime and wishes to come back online & -possibly rejoin the bonded set, it must send `MsgUnjail`: - -```protobuf -// MsgUnjail is an sdk.Msg used for unjailing a jailed validator, thus returning -// them into the bonded validator set, so they can begin receiving provisions -// and rewards again. -message MsgUnjail { - string validator_addr = 1; -} -``` - -Below is a pseudocode of the `MsgSrv/Unjail` RPC: - -```go -unjail(tx MsgUnjail) - validator = getValidator(tx.ValidatorAddr) - if validator == nil - fail with "No validator found" - - if getSelfDelegation(validator) == 0 - fail with "validator must self delegate before unjailing" - - if !validator.Jailed - fail with "Validator not jailed, cannot unjail" - - info = GetValidatorSigningInfo(operator) - if info.Tombstoned - fail with "Tombstoned validator cannot be unjailed" - if block time < info.JailedUntil - fail with "Validator still jailed, cannot unjail until period has expired" - - validator.Jailed = false - setValidator(validator) - - return -``` - -If the validator has enough stake to be in the top `n = MaximumBondedValidators`, it will be automatically rebonded, -and all delegators still delegated to the validator will be rebonded and begin to again collect -provisions and rewards. diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/04_begin_block.md b/versioned_docs/version-0.46/integrate/modules/slashing/04_begin_block.md deleted file mode 100644 index 99572c419..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/04_begin_block.md +++ /dev/null @@ -1,98 +0,0 @@ - - -# BeginBlock - -## Liveness Tracking - -At the beginning of each block, we update the `ValidatorSigningInfo` for each -validator and check if they've crossed below the liveness threshold over a -sliding window. This sliding window is defined by `SignedBlocksWindow` and the -index in this window is determined by `IndexOffset` found in the validator's -`ValidatorSigningInfo`. For each block processed, the `IndexOffset` is incremented -regardless if the validator signed or not. Once the index is determined, the -`MissedBlocksBitArray` and `MissedBlocksCounter` are updated accordingly. - -Finally, in order to determine if a validator crosses below the liveness threshold, -we fetch the maximum number of blocks missed, `maxMissed`, which is -`SignedBlocksWindow - (MinSignedPerWindow * SignedBlocksWindow)` and the minimum -height at which we can determine liveness, `minHeight`. If the current block is -greater than `minHeight` and the validator's `MissedBlocksCounter` is greater than -`maxMissed`, they will be slashed by `SlashFractionDowntime`, will be jailed -for `DowntimeJailDuration`, and have the following values reset: -`MissedBlocksBitArray`, `MissedBlocksCounter`, and `IndexOffset`. - -**Note**: Liveness slashes do **NOT** lead to a tombstombing. - -```go -height := block.Height - -for vote in block.LastCommitInfo.Votes { - signInfo := GetValidatorSigningInfo(vote.Validator.Address) - - // This is a relative index, so we counts blocks the validator SHOULD have - // signed. We use the 0-value default signing info if not present, except for - // start height. - index := signInfo.IndexOffset % SignedBlocksWindow() - signInfo.IndexOffset++ - - // Update MissedBlocksBitArray and MissedBlocksCounter. The MissedBlocksCounter - // just tracks the sum of MissedBlocksBitArray. That way we avoid needing to - // read/write the whole array each time. - missedPrevious := GetValidatorMissedBlockBitArray(vote.Validator.Address, index) - missed := !signed - - switch { - case !missedPrevious && missed: - // array index has changed from not missed to missed, increment counter - SetValidatorMissedBlockBitArray(vote.Validator.Address, index, true) - signInfo.MissedBlocksCounter++ - - case missedPrevious && !missed: - // array index has changed from missed to not missed, decrement counter - SetValidatorMissedBlockBitArray(vote.Validator.Address, index, false) - signInfo.MissedBlocksCounter-- - - default: - // array index at this index has not changed; no need to update counter - } - - if missed { - // emit events... - } - - minHeight := signInfo.StartHeight + SignedBlocksWindow() - maxMissed := SignedBlocksWindow() - MinSignedPerWindow() - - // If we are past the minimum height and the validator has missed too many - // jail and slash them. - if height > minHeight && signInfo.MissedBlocksCounter > maxMissed { - validator := ValidatorByConsAddr(vote.Validator.Address) - - // emit events... - - // We need to retrieve the stake distribution which signed the block, so we - // subtract ValidatorUpdateDelay from the block height, and subtract an - // additional 1 since this is the LastCommit. - // - // Note, that this CAN result in a negative "distributionHeight" up to - // -ValidatorUpdateDelay-1, i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block. - // That's fine since this is just used to filter unbonding delegations & redelegations. - distributionHeight := height - sdk.ValidatorUpdateDelay - 1 - - Slash(vote.Validator.Address, distributionHeight, vote.Validator.Power, SlashFractionDowntime()) - Jail(vote.Validator.Address) - - signInfo.JailedUntil = block.Time.Add(DowntimeJailDuration()) - - // We need to reset the counter & array so that the validator won't be - // immediately slashed for downtime upon rebonding. - signInfo.MissedBlocksCounter = 0 - signInfo.IndexOffset = 0 - ClearValidatorMissedBlockBitArray(vote.Validator.Address) - } - - SetValidatorSigningInfo(vote.Validator.Address, signInfo) -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/05_hooks.md b/versioned_docs/version-0.46/integrate/modules/slashing/05_hooks.md deleted file mode 100644 index a83968942..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/05_hooks.md +++ /dev/null @@ -1,45 +0,0 @@ - - -# Hooks - -This section contains a description of the module's `hooks`. Hooks are operations that are executed automatically when events are raised. - -## Staking hooks - -The slashing module implements the `StakingHooks` defined in `x/staking` and are used as record-keeping of validators information. During the app initialization, these hooks should be registered in the staking module struct. - -The following hooks impact the slashing state: - -* `AfterValidatorBonded` creates a `ValidatorSigningInfo` instance as described in the following section. -* `AfterValidatorCreated` stores a validator's consensus key. -* `AfterValidatorRemoved` removes a validator's consensus key. - -## Validator Bonded - -Upon successful first-time bonding of a new validator, we create a new `ValidatorSigningInfo` structure for the -now-bonded validator, which `StartHeight` of the current block. - -If the validator was out of the validator set and gets bonded again, its new bonded height is set. - -```go -onValidatorBonded(address sdk.ValAddress) - - signingInfo, found = GetValidatorSigningInfo(address) - if !found { - signingInfo = ValidatorSigningInfo { - StartHeight : CurrentHeight, - IndexOffset : 0, - JailedUntil : time.Unix(0, 0), - Tombstone : false, - MissedBloskCounter : 0 - } else { - signingInfo.StartHeight = CurrentHeight - } - - setValidatorSigningInfo(signingInfo) - } - - return -``` diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/06_events.md b/versioned_docs/version-0.46/integrate/modules/slashing/06_events.md deleted file mode 100644 index c7dbf5723..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/06_events.md +++ /dev/null @@ -1,46 +0,0 @@ - - -# Events - -The slashing module emits the following events: - -## MsgServer - -### MsgUnjail - -| Type | Attribute Key | Attribute Value | -| ------- | ------------- | ------------------ | -| message | module | slashing | -| message | sender | {validatorAddress} | - -## Keeper - -## BeginBlocker: HandleValidatorSignature - -| Type | Attribute Key | Attribute Value | -| ----- | ------------- | --------------------------- | -| slash | address | {validatorConsensusAddress} | -| slash | power | {validatorPower} | -| slash | reason | {slashReason} | -| slash | jailed [0] | {validatorConsensusAddress} | -| slash | burned coins | {sdk.Int} | - -* [0] Only included if the validator is jailed. - -| Type | Attribute Key | Attribute Value | -| -------- | ------------- | --------------------------- | -| liveness | address | {validatorConsensusAddress} | -| liveness | missed_blocks | {missedBlocksCounter} | -| liveness | height | {blockHeight} | - -### Slash - -* same as `"slash"` event from `HandleValidatorSignature`, but without the `jailed` attribute. - -### Jail - -| Type | Attribute Key | Attribute Value | -| ----- | ------------- | ------------------ | -| slash | jailed | {validatorAddress} | diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/07_tombstone.md b/versioned_docs/version-0.46/integrate/modules/slashing/07_tombstone.md deleted file mode 100644 index ab06c9456..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/07_tombstone.md +++ /dev/null @@ -1,127 +0,0 @@ - - -# Staking Tombstone - -## Abstract - -In the current implementation of the `slashing` module, when the consensus engine -informs the state machine of a validator's consensus fault, the validator is -partially slashed, and put into a "jail period", a period of time in which they -are not allowed to rejoin the validator set. However, because of the nature of -consensus faults and ABCI, there can be a delay between an infraction occurring, -and evidence of the infraction reaching the state machine (this is one of the -primary reasons for the existence of the unbonding period). - -> Note: The tombstone concept, only applies to faults that have a delay between -> the infraction occurring and evidence reaching the state machine. For example, -> evidence of a validator double signing may take a while to reach the state machine -> due to unpredictable evidence gossip layer delays and the ability of validators to -> selectively reveal double-signatures (e.g. to infrequently-online light clients). -> Liveness slashing, on the other hand, is detected immediately as soon as the -> infraction occurs, and therefore no slashing period is needed. A validator is -> immediately put into jail period, and they cannot commit another liveness fault -> until they unjail. In the future, there may be other types of byzantine faults -> that have delays (for example, submitting evidence of an invalid proposal as a transaction). -> When implemented, it will have to be decided whether these future types of -> byzantine faults will result in a tombstoning (and if not, the slash amounts -> will not be capped by a slashing period). - -In the current system design, once a validator is put in the jail for a consensus -fault, after the `JailPeriod` they are allowed to send a transaction to `unjail` -themselves, and thus rejoin the validator set. - -One of the "design desires" of the `slashing` module is that if multiple -infractions occur before evidence is executed (and a validator is put in jail), -they should only be punished for single worst infraction, but not cumulatively. -For example, if the sequence of events is: - -1. Validator A commits Infraction 1 (worth 30% slash) -2. Validator A commits Infraction 2 (worth 40% slash) -3. Validator A commits Infraction 3 (worth 35% slash) -4. Evidence for Infraction 1 reaches state machine (and validator is put in jail) -5. Evidence for Infraction 2 reaches state machine -6. Evidence for Infraction 3 reaches state machine - -Only Infraction 2 should have its slash take effect, as it is the highest. This -is done, so that in the case of the compromise of a validator's consensus key, -they will only be punished once, even if the hacker double-signs many blocks. -Because, the unjailing has to be done with the validator's operator key, they -have a chance to re-secure their consensus key, and then signal that they are -ready using their operator key. We call this period during which we track only -the max infraction, the "slashing period". - -Once, a validator rejoins by unjailing themselves, we begin a new slashing period; -if they commit a new infraction after unjailing, it gets slashed cumulatively on -top of the worst infraction from the previous slashing period. - -However, while infractions are grouped based off of the slashing periods, because -evidence can be submitted up to an `unbondingPeriod` after the infraction, we -still have to allow for evidence to be submitted for previous slashing periods. -For example, if the sequence of events is: - -1. Validator A commits Infraction 1 (worth 30% slash) -2. Validator A commits Infraction 2 (worth 40% slash) -3. Evidence for Infraction 1 reaches state machine (and Validator A is put in jail) -4. Validator A unjails - -We are now in a new slashing period, however we still have to keep the door open -for the previous infraction, as the evidence for Infraction 2 may still come in. -As the number of slashing periods increase, it creates more complexity as we have -to keep track of the highest infraction amount for every single slashing period. - -> Note: Currently, according to the `slashing` module spec, a new slashing period -> is created every time a validator is unbonded then rebonded. This should probably -> be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205) -> for further details. For the remainder of this, I will assume that we only start -> a new slashing period when a validator gets unjailed. - -The maximum number of slashing periods is the `len(UnbondingPeriod) / len(JailPeriod)`. -The current defaults in Gaia for the `UnbondingPeriod` and `JailPeriod` are 3 weeks -and 2 days, respectively. This means there could potentially be up to 11 slashing -periods concurrently being tracked per validator. If we set the `JailPeriod >= UnbondingPeriod`, -we only have to track 1 slashing period (i.e not have to track slashing periods). - -Currently, in the jail period implementation, once a validator unjails, all of -their delegators who are delegated to them (haven't unbonded / redelegated away), -stay with them. Given that consensus safety faults are so egregious -(way more so than liveness faults), it is probably prudent to have delegators not -"auto-rebond" to the validator. - -### Proposal: infinite jail - -We propose setting the "jail time" for a -validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state). -This essentially kicks the validator out of the validator set and does not allow -them to re-enter the validator set. All of their delegators (including the operator themselves) -have to either unbond or redelegate away. The validator operator can create a new -validator if they would like, with a new operator key and consensus key, but they -have to "re-earn" their delegations back. - -Implementing the tombstone system and getting rid of the slashing period tracking -will make the `slashing` module way simpler, especially because we can remove all -of the hooks defined in the `slashing` module consumed by the `staking` module -(the `slashing` module still consumes hooks defined in `staking`). - -### Single slashing amount - -Another optimization that can be made is that if we assume that all ABCI faults -for Tendermint consensus are slashed at the same level, we don't have to keep -track of "max slash". Once an ABCI fault happens, we don't have to worry about -comparing potential future ones to find the max. - -Currently the only Tendermint ABCI fault is: - -* Unjustified precommits (double signs) - -It is currently planned to include the following fault in the near future: - -* Signing a precommit when you're in unbonding phase (needed to make light client bisection safe) - -Given that these faults are both attributable byzantine faults, we will likely -want to slash them equally, and thus we can enact the above change. - -> Note: This change may make sense for current Tendermint consensus, but maybe -> not for a different consensus algorithm or future versions of Tendermint that -> may want to punish at different levels (for example, partial slashing). diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/08_params.md b/versioned_docs/version-0.46/integrate/modules/slashing/08_params.md deleted file mode 100644 index defed189a..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/08_params.md +++ /dev/null @@ -1,15 +0,0 @@ - - -# Parameters - -The slashing module contains the following parameters: - -| Key | Type | Example | -| ----------------------- | -------------- | ---------------------- | -| SignedBlocksWindow | string (int64) | "100" | -| MinSignedPerWindow | string (dec) | "0.500000000000000000" | -| DowntimeJailDuration | string (ns) | "600000000000" | -| SlashFractionDoubleSign | string (dec) | "0.050000000000000000" | -| SlashFractionDowntime | string (dec) | "0.010000000000000000" | diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/09_client.md b/versioned_docs/version-0.46/integrate/modules/slashing/09_client.md deleted file mode 100644 index 11d82961e..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/09_client.md +++ /dev/null @@ -1,294 +0,0 @@ - - -# CLI - -A user can query and interact with the `slashing` module using the CLI. - -## Query - -The `query` commands allow users to query `slashing` state. - -```sh -simd query slashing --help -``` - -### params - -The `params` command allows users to query genesis parameters for the slashing module. - -```sh -simd query slashing params [flags] -``` - -Example: - -```sh -simd query slashing params -``` - -Example Output: - -```yml -downtime_jail_duration: 600s -min_signed_per_window: "0.500000000000000000" -signed_blocks_window: "100" -slash_fraction_double_sign: "0.050000000000000000" -slash_fraction_downtime: "0.010000000000000000" -``` - -### signing-info - -The `signing-info` command allows users to query signing-info of the validator using consensus public key. - -```sh -simd query slashing signing-infos [flags] -``` - -Example: - -```sh -simd query slashing signing-info '{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Auxs3865HpB/EfssYOzfqNhEJjzys6jD5B6tPgC8="}' - -``` - -Example Output: - -```yml -address: cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c -index_offset: "2068" -jailed_until: "1970-01-01T00:00:00Z" -missed_blocks_counter: "0" -start_height: "0" -tombstoned: false -``` - -### signing-infos - -The `signing-infos` command allows users to query signing infos of all validators. - -```sh -simd query slashing signing-infos [flags] -``` - -Example: - -```sh -simd query slashing signing-infos -``` - -Example Output: - -```yml -info: -- address: cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c - index_offset: "2075" - jailed_until: "1970-01-01T00:00:00Z" - missed_blocks_counter: "0" - start_height: "0" - tombstoned: false -pagination: - next_key: null - total: "0" -``` - -## Transactions - -The `tx` commands allow users to interact with the `slashing` module. - -```bash -simd tx slashing --help -``` - -### unjail - -The `unjail` command allows users to unjail a validator previously jailed for downtime. - -```bash - simd tx slashing unjail --from mykey [flags] -``` - -Example: - -```bash -simd tx slashing unjail --from mykey -``` - -## gRPC - -A user can query the `slashing` module using gRPC endpoints. - -### Params - -The `Params` endpoint allows users to query the parameters of slashing module. - -```sh -cosmos.slashing.v1beta1.Query/Params -``` - -Example: - -```sh -grpcurl -plaintext localhost:9090 cosmos.slashing.v1beta1.Query/Params -``` - -Example Output: - -```json -{ - "params": { - "signedBlocksWindow": "100", - "minSignedPerWindow": "NTAwMDAwMDAwMDAwMDAwMDAw", - "downtimeJailDuration": "600s", - "slashFractionDoubleSign": "NTAwMDAwMDAwMDAwMDAwMDA=", - "slashFractionDowntime": "MTAwMDAwMDAwMDAwMDAwMDA=" - } -} -``` - -### SigningInfo - -The SigningInfo queries the signing info of given cons address. - -```sh -cosmos.slashing.v1beta1.Query/SigningInfo -``` - -Example: - -```sh -grpcurl -plaintext -d '{"cons_address":"cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c"}' localhost:9090 cosmos.slashing.v1beta1.Query/SigningInfo -``` - -Example Output: - -```json -{ - "valSigningInfo": { - "address": "cosmosvalcons1nrqsld3aw6lh6t082frdqc84uwxn0t958c", - "indexOffset": "3493", - "jailedUntil": "1970-01-01T00:00:00Z" - } -} -``` - -### SigningInfos - -The SigningInfos queries signing info of all validators. - -```sh -cosmos.slashing.v1beta1.Query/SigningInfos -``` - -Example: - -```sh -grpcurl -plaintext localhost:9090 cosmos.slashing.v1beta1.Query/SigningInfos -``` - -Example Output: - -```json -{ - "info": [ - { - "address": "cosmosvalcons1nrqslkwd3pz096lh6t082frdqc84uwxn0t958c", - "indexOffset": "2467", - "jailedUntil": "1970-01-01T00:00:00Z" - } - ], - "pagination": { - "total": "1" - } -} -``` - -## REST - -A user can query the `slashing` module using REST endpoints. - -### Params - -```sh -/cosmos/slashing/v1beta1/params -``` - -Example: - -```sh -curl "localhost:1317/cosmos/slashing/v1beta1/params" -``` - -Example Output: - -```json -{ - "params": { - "signed_blocks_window": "100", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "600s", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.010000000000000000" -} -``` - -### signing_info - -```sh -/cosmos/slashing/v1beta1/signing_infos/%s -``` - -Example: - -```sh -curl "localhost:1317/cosmos/slashing/v1beta1/signing_infos/cosmosvalcons1nrqslkwd3pz096lh6t082frdqc84uwxn0t958c" -``` - -Example Output: - -```json -{ - "val_signing_info": { - "address": "cosmosvalcons1nrqslkwd3pz096lh6t082frdqc84uwxn0t958c", - "start_height": "0", - "index_offset": "4184", - "jailed_until": "1970-01-01T00:00:00Z", - "tombstoned": false, - "missed_blocks_counter": "0" - } -} -``` - -### signing_infos - -```sh -/cosmos/slashing/v1beta1/signing_infos -``` - -Example: - -```sh -curl "localhost:1317/cosmos/slashing/v1beta1/signing_infos -``` - -Example Output: - -```json -{ - "info": [ - { - "address": "cosmosvalcons1nrqslkwd3pz096lh6t082frdqc84uwxn0t958c", - "start_height": "0", - "index_offset": "4169", - "jailed_until": "1970-01-01T00:00:00Z", - "tombstoned": false, - "missed_blocks_counter": "0" - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/README.md b/versioned_docs/version-0.46/integrate/modules/slashing/README.md deleted file mode 100644 index d1988cb96..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/README.md +++ /dev/null @@ -1,49 +0,0 @@ - - -# `x/slashing` - -## Abstract - -This section specifies the slashing module of the Cosmos SDK, which implements functionality -first outlined in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016. - -The slashing module enables Cosmos SDK-based blockchains to disincentivize any attributable action -by a protocol-recognized actor with value at stake by penalizing them ("slashing"). - -Penalties may include, but are not limited to: - -* Burning some amount of their stake -* Removing their ability to vote on future blocks for a period of time. - -This module will be used by the Cosmos Hub, the first hub in the Cosmos ecosystem. - -## Contents - -1. **[Concepts](01_concepts.md)** - * [States](01_concepts.md#states) - * [Tombstone Caps](01_concepts.md#tombstone-caps) - * [ASCII timelines](01_concepts.md#ascii-timelines) -2. **[State](02_state.md)** - * [Signing Info](02_state.md#signing-info) -3. **[Messages](03_messages.md)** - * [Unjail](03_messages.md#unjail) -4. **[Begin-Block](04_begin_block.md)** - * [Evidence handling](04_begin_block.md#evidence-handling) - * [Uptime tracking](04_begin_block.md#uptime-tracking) -5. **[05_hooks.md](05_hooks.md)** - * [Hooks](05_hooks.md#hooks) -6. **[Events](06_events.md)** - * [BeginBlocker](06_events.md#beginblocker) - * [Handlers](06_events.md#handlers) -7. **[Staking Tombstone](07_tombstone.md)** - * [Abstract](07_tombstone.md#abstract) -8. **[Parameters](08_params.md)** -9. **[Client](09_client.md)** - * [CLI](09_client.md#cli) - * [gRPC](09_client.md#grpc) - * [REST](09_client.md#rest) diff --git a/versioned_docs/version-0.46/integrate/modules/slashing/_category_.json b/versioned_docs/version-0.46/integrate/modules/slashing/_category_.json deleted file mode 100644 index 97d1fa939..000000000 --- a/versioned_docs/version-0.46/integrate/modules/slashing/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Slashing", - "position": 11, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/staking/01_state.md b/versioned_docs/version-0.46/integrate/modules/staking/01_state.md deleted file mode 100644 index e0ca517c0..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/01_state.md +++ /dev/null @@ -1,215 +0,0 @@ - - -# State - -## Pool - -Pool is used for tracking bonded and not-bonded token supply of the bond denomination. - -## LastTotalPower - -LastTotalPower tracks the total amounts of bonded tokens recorded during the previous end block. -Store entries prefixed with "Last" must remain unchanged until EndBlock. - -* LastTotalPower: `0x12 -> ProtocolBuffer(math.Int)` - -## Params - -Params is a module-wide configuration structure that stores system parameters -and defines overall functioning of the staking module. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L285-L306 - -## Validator - -Validators can have one of three statuses - -* `Unbonded`: The validator is not in the active set. They cannot sign blocks and do not earn - rewards. They can receive delegations. -* `Bonded`: Once the validator receives sufficient bonded tokens they automtically join the - active set during [`EndBlock`](./05_end_block.md#validator-set-changes) and their status is updated to `Bonded`. - They are signing blocks and receiving rewards. They can receive further delegations. - They can be slashed for misbehavior. Delegators to this validator who unbond their delegation - must wait the duration of the UnbondingTime, a chain-specific param, during which time - they are still slashable for offences of the source validator if those offences were committed - during the period of time that the tokens were bonded. -* `Unbonding`: When a validator leaves the active set, either by choice or due to slashing, jailing or - tombstoning, an unbonding of all their delegations begins. All delegations must then wait the UnbondingTime - before their tokens are moved to their accounts from the `BondedPool`. - -Validators objects should be primarily stored and accessed by the -`OperatorAddr`, an SDK validator address for the operator of the validator. Two -additional indices are maintained per validator object in order to fulfill -required lookups for slashing and validator-set updates. A third special index -(`LastValidatorPower`) is also maintained which however remains constant -throughout each block, unlike the first two indices which mirror the validator -records within a block. - -* Validators: `0x21 | OperatorAddrLen (1 byte) | OperatorAddr -> ProtocolBuffer(validator)` -* ValidatorsByConsAddr: `0x22 | ConsAddrLen (1 byte) | ConsAddr -> OperatorAddr` -* ValidatorsByPower: `0x23 | BigEndian(ConsensusPower) | OperatorAddrLen (1 byte) | OperatorAddr -> OperatorAddr` -* LastValidatorsPower: `0x11 | OperatorAddrLen (1 byte) | OperatorAddr -> ProtocolBuffer(ConsensusPower)` - -`Validators` is the primary index - it ensures that each operator can have only one -associated validator, where the public key of that validator can change in the -future. Delegators can refer to the immutable operator of the validator, without -concern for the changing public key. - -`ValidatorByConsAddr` is an additional index that enables lookups for slashing. -When Tendermint reports evidence, it provides the validator address, so this -map is needed to find the operator. Note that the `ConsAddr` corresponds to the -address which can be derived from the validator's `ConsPubKey`. - -`ValidatorsByPower` is an additional index that provides a sorted list of -potential validators to quickly determine the current active set. Here -ConsensusPower is validator.Tokens/10^6 by default. Note that all validators -where `Jailed` is true are not stored within this index. - -`LastValidatorsPower` is a special index that provides a historical list of the -last-block's bonded validators. This index remains constant during a block but -is updated during the validator set update process which takes place in [`EndBlock`](./05_end_block.md). - -Each validator's state is stored in a `Validator` struct: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L78-L127 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L24-L76 - -## Delegation - -Delegations are identified by combining `DelegatorAddr` (the address of the delegator) -with the `ValidatorAddr` Delegators are indexed in the store as follows: - -* Delegation: `0x31 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorAddr -> ProtocolBuffer(delegation)` - -Stake holders may delegate coins to validators; under this circumstance their -funds are held in a `Delegation` data structure. It is owned by one -delegator, and is associated with the shares for one validator. The sender of -the transaction is the owner of the bond. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L187-L205 - -### Delegator Shares - -When one Delegates tokens to a Validator they are issued a number of delegator shares based on a -dynamic exchange rate, calculated as follows from the total number of tokens delegated to the -validator and the number of shares issued so far: - -`Shares per Token = validator.TotalShares() / validator.Tokens()` - -Only the number of shares received is stored on the DelegationEntry. When a delegator then -Undelegates, the token amount they receive is calculated from the number of shares they currently -hold and the inverse exchange rate: - -`Tokens per Share = validator.Tokens() / validatorShares()` - -These `Shares` are simply an accounting mechanism. They are not a fungible asset. The reason for -this mechanism is to simplify the accounting around slashing. Rather than iteratively slashing the -tokens of every delegation entry, instead the Validators total bonded tokens can be slashed, -effectively reducing the value of each issued delegator share. - -## UnbondingDelegation - -Shares in a `Delegation` can be unbonded, but they must for some time exist as -an `UnbondingDelegation`, where shares can be reduced if Byzantine behavior is -detected. - -`UnbondingDelegation` are indexed in the store as: - -* UnbondingDelegation: `0x32 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorAddr -> ProtocolBuffer(unbondingDelegation)` -* UnbondingDelegationsFromValidator: `0x33 | ValidatorAddrLen (1 byte) | ValidatorAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil` - -The first map here is used in queries, to lookup all unbonding delegations for -a given delegator, while the second map is used in slashing, to lookup all -unbonding delegations associated with a given validator that need to be -slashed. - -A UnbondingDelegation object is created every time an unbonding is initiated. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L207-L220 - -## Redelegation - -The bonded tokens worth of a `Delegation` may be instantly redelegated from a -source validator to a different validator (destination validator). However when -this occurs they must be tracked in a `Redelegation` object, whereby their -shares can be slashed if their tokens have contributed to a Byzantine fault -committed by the source validator. - -`Redelegation` are indexed in the store as: - -* Redelegations: `0x34 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorSrcAddr | ValidatorDstAddr -> ProtocolBuffer(redelegation)` -* RedelegationsBySrc: `0x35 | ValidatorSrcAddrLen (1 byte) | ValidatorSrcAddr | ValidatorDstAddrLen (1 byte) | ValidatorDstAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil` -* RedelegationsByDst: `0x36 | ValidatorDstAddrLen (1 byte) | ValidatorDstAddr | ValidatorSrcAddrLen (1 byte) | ValidatorSrcAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil` - -The first map here is used for queries, to lookup all redelegations for a given -delegator. The second map is used for slashing based on the `ValidatorSrcAddr`, -while the third map is for slashing based on the `ValidatorDstAddr`. - -A redelegation object is created every time a redelegation occurs. To prevent -"redelegation hopping" redelegations may not occur under the situation that: - -* the (re)delegator already has another immature redelegation in progress - with a destination to a validator (let's call it `Validator X`) -* and, the (re)delegator is attempting to create a _new_ redelegation - where the source validator for this new redelegation is `Validator X`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L245-L283 - -## Queues - -All queues objects are sorted by timestamp. The time used within any queue is -first rounded to the nearest nanosecond then sorted. The sortable time format -used is a slight modification of the RFC3339Nano and uses the format string -`"2006-01-02T15:04:05.000000000"`. Notably this format: - -* right pads all zeros -* drops the time zone info (uses UTC) - -In all cases, the stored timestamp represents the maturation time of the queue -element. - -### UnbondingDelegationQueue - -For the purpose of tracking progress of unbonding delegations the unbonding -delegations queue is kept. - -* UnbondingDelegation: `0x41 | format(time) -> []DVPair` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L151-L161 - -### RedelegationQueue - -For the purpose of tracking progress of redelegations the redelegation queue is -kept. - -* RedelegationQueue: `0x42 | format(time) -> []DVVTriplet` - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L168-L179 - -### ValidatorQueue - -For the purpose of tracking progress of unbonding validators the validator -queue is kept. - -* ValidatorQueueTime: `0x43 | format(time) -> []sdk.ValAddress` - -The stored object as each key is an array of validator operator addresses from -which the validator object can be accessed. Typically it is expected that only -a single validator record will be associated with a given timestamp however it is possible -that multiple validators exist in the queue at the same location. - -## HistoricalInfo - -HistoricalInfo objects are stored and pruned at each block such that the staking keeper persists -the `n` most recent historical info defined by staking module parameter: `HistoricalEntries`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/staking.proto#L15-L22 - -At each BeginBlock, the staking keeper will persist the current Header and the Validators that committed -the current block in a `HistoricalInfo` object. The Validators are sorted on their address to ensure that -they are in a determisnistic order. -The oldest HistoricalEntries will be pruned to ensure that there only exist the parameter-defined number of -historical entries. diff --git a/versioned_docs/version-0.46/integrate/modules/staking/02_state_transitions.md b/versioned_docs/version-0.46/integrate/modules/staking/02_state_transitions.md deleted file mode 100644 index 9e9efffae..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/02_state_transitions.md +++ /dev/null @@ -1,180 +0,0 @@ - - -# State Transitions - -This document describes the state transition operations pertaining to: - -1. [Validators](./02_state_transitions.md#validators) -2. [Delegations](./02_state_transitions.md#delegations) -3. [Slashing](./02_state_transitions.md#slashing) - -## Validators - -State transitions in validators are performed on every [`EndBlock`](./05_end_block.md#validator-set-changes) -in order to check for changes in the active `ValidatorSet`. - -A validator can be `Unbonded`, `Unbonding` or `Bonded`. `Unbonded` -and `Unbonding` are collectively called `Not Bonded`. A validator can move -directly between all the states, except for from `Bonded` to `Unbonded`. - -### Not bonded to Bonded - -The following transition occurs when a validator's ranking in the `ValidatorPowerIndex` surpasses -that of the `LastValidator`. - -* set `validator.Status` to `Bonded` -* send the `validator.Tokens` from the `NotBondedTokens` to the `BondedPool` `ModuleAccount` -* delete the existing record from `ValidatorByPowerIndex` -* add a new updated record to the `ValidatorByPowerIndex` -* update the `Validator` object for this validator -* if it exists, delete any `ValidatorQueue` record for this validator - -### Bonded to Unbonding - -When a validator begins the unbonding process the following operations occur: - -* send the `validator.Tokens` from the `BondedPool` to the `NotBondedTokens` `ModuleAccount` -* set `validator.Status` to `Unbonding` -* delete the existing record from `ValidatorByPowerIndex` -* add a new updated record to the `ValidatorByPowerIndex` -* update the `Validator` object for this validator -* insert a new record into the `ValidatorQueue` for this validator - -### Unbonding to Unbonded - -A validator moves from unbonding to unbonded when the `ValidatorQueue` object -moves from bonded to unbonded - -* update the `Validator` object for this validator -* set `validator.Status` to `Unbonded` - -### Jail/Unjail - -when a validator is jailed it is effectively removed from the Tendermint set. -this process may be also be reversed. the following operations occur: - -* set `Validator.Jailed` and update object -* if jailed delete record from `ValidatorByPowerIndex` -* if unjailed add record to `ValidatorByPowerIndex` - -Jailed validators are not present in any of the following stores: - -* the power store (from consensus power to address) - -## Delegations - -### Delegate - -When a delegation occurs both the validator and the delegation objects are affected - -* determine the delegators shares based on tokens delegated and the validator's exchange rate -* remove tokens from the sending account -* add shares the delegation object or add them to a created validator object -* add new delegator shares and update the `Validator` object -* transfer the `delegation.Amount` from the delegator's account to the `BondedPool` or the `NotBondedPool` `ModuleAccount` depending if the `validator.Status` is `Bonded` or not -* delete the existing record from `ValidatorByPowerIndex` -* add an new updated record to the `ValidatorByPowerIndex` - -### Begin Unbonding - -As a part of the Undelegate and Complete Unbonding state transitions Unbond -Delegation may be called. - -* subtract the unbonded shares from delegator -* add the unbonded tokens to an `UnbondingDelegation` Entry -* update the delegation or remove the delegation if there are no more shares -* if the delegation is the operator of the validator and no more shares exist then trigger a jail validator -* update the validator with removed the delegator shares and associated coins -* if the validator state is `Bonded`, transfer the `Coins` worth of the unbonded - shares from the `BondedPool` to the `NotBondedPool` `ModuleAccount` -* remove the validator if it is unbonded and there are no more delegation shares. - -### Cancel an `UnbondingDelegation` Entry -When a `cancel unbond delegation` occurs both the `validator`, the `delegation` and an `UnbondingDelegationQueue` state will be updated. -* if cancel unbonding delegation amount equals to the `UnbondingDelegation` entry `balance`, then the `UnbondingDelegation` entry deleted from `UnbondingDelegationQueue`. -* if the `cancel unbonding delegation amount is less than the `UnbondingDelegation` entry balance, then the `UnbondingDelegation` entry will be updated with new balance in the `UnbondingDelegationQueue`. -* cancel `amount` is [Delegated](02_state_transitions.md#delegations) back to the original `validator`. - -### Complete Unbonding - -For undelegations which do not complete immediately, the following operations -occur when the unbonding delegation queue element matures: - -* remove the entry from the `UnbondingDelegation` object -* transfer the tokens from the `NotBondedPool` `ModuleAccount` to the delegator `Account` - -### Begin Redelegation - -Redelegations affect the delegation, source and destination validators. - -* perform an `unbond` delegation from the source validator to retrieve the tokens worth of the unbonded shares -* using the unbonded tokens, `Delegate` them to the destination validator -* if the `sourceValidator.Status` is `Bonded`, and the `destinationValidator` is not, - transfer the newly delegated tokens from the `BondedPool` to the `NotBondedPool` `ModuleAccount` -* otherwise, if the `sourceValidator.Status` is not `Bonded`, and the `destinationValidator` - is `Bonded`, transfer the newly delegated tokens from the `NotBondedPool` to the `BondedPool` `ModuleAccount` -* record the token amount in an new entry in the relevant `Redelegation` - -From when a redelegation begins until it completes, the delegator is in a state of "pseudo-unbonding", and can still be -slashed for infractions that occured before the redelegation began. - -### Complete Redelegation - -When a redelegations complete the following occurs: - -* remove the entry from the `Redelegation` object - -## Slashing - -### Slash Validator - -When a Validator is slashed, the following occurs: - -* The total `slashAmount` is calculated as the `slashFactor` (a chain parameter) \* `TokensFromConsensusPower`, - the total number of tokens bonded to the validator at the time of the infraction. -* Every unbonding delegation and pseudo-unbonding redelegation such that the infraction occured before the unbonding or - redelegation began from the validator are slashed by the `slashFactor` percentage of the initialBalance. -* Each amount slashed from redelegations and unbonding delegations is subtracted from the - total slash amount. -* The `remaingSlashAmount` is then slashed from the validator's tokens in the `BondedPool` or - `NonBondedPool` depending on the validator's status. This reduces the total supply of tokens. - -In the case of a slash due to any infraction that requires evidence to submitted (for example double-sign), the slash -occurs at the block where the evidence is included, not at the block where the infraction occured. -Put otherwise, validators are not slashed retroactively, only when they are caught. - -### Slash Unbonding Delegation - -When a validator is slashed, so are those unbonding delegations from the validator that began unbonding -after the time of the infraction. Every entry in every unbonding delegation from the validator -is slashed by `slashFactor`. The amount slashed is calculated from the `InitialBalance` of the -delegation and is capped to prevent a resulting negative balance. Completed (or mature) unbondings are not slashed. - -### Slash Redelegation - -When a validator is slashed, so are all redelegations from the validator that began after the -infraction. Redelegations are slashed by `slashFactor`. -Redelegations that began before the infraction are not slashed. -The amount slashed is calculated from the `InitialBalance` of the delegation and is capped to -prevent a resulting negative balance. -Mature redelegations (that have completed pseudo-unbonding) are not slashed. - -## How Shares are calculated - -At any given point in time, each validator has a number of tokens, `T`, and has a number of shares issued, `S`. -Each delegator, `i`, holds a number of shares, `S_i`. -The number of tokens is the sum of all tokens delegated to the validator, plus the rewards, minus the slashes. - -The delegator is entitled to a portion of the underlying tokens proportional to their proportion of shares. -So delegator `i` is entitled to `T * S_i / S` of the validator's tokens. - -When a delegator delegates new tokens to the validator, they receive a number of shares proportional to their contribution. -So when delegator `j` delegates `T_j` tokens, they receive `S_j = S * T_j / T` shares. -The total number of tokens is now `T + T_j`, and the total number of shares is `S + S_j`. -`j`s proportion of the shares is the same as their proportion of the total tokens contributed: `(S + S_j) / S = (T + T_j) / T`. - -A special case is the initial delegation, when `T = 0` and `S = 0`, so `T_j / T` is undefined. -For the initial delegation, delegator `j` who delegates `T_j` tokens receive `S_j = T_j` shares. -So a validator that hasn't received any rewards and has not been slashed will have `T = S`. diff --git a/versioned_docs/version-0.46/integrate/modules/staking/03_messages.md b/versioned_docs/version-0.46/integrate/modules/staking/03_messages.md deleted file mode 100644 index f63c2f07f..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/03_messages.md +++ /dev/null @@ -1,175 +0,0 @@ - - -# Messages - -In this section we describe the processing of the staking messages and the corresponding updates to the state. All created/modified state objects specified by each message are defined within the [state](./02_state_transitions.md) section. - -## MsgCreateValidator - -A validator is created using the `MsgCreateValidator` message. -The validator must be created with an initial delegation from the operator. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L18-L19 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L43-L65 - -This message is expected to fail if: - -* another validator with this operator address is already registered -* another validator with this pubkey is already registered -* the initial self-delegation tokens are of a denom not specified as the bonding denom -* the commission parameters are faulty, namely: - * `MaxRate` is either > 1 or < 0 - * the initial `Rate` is either negative or > `MaxRate` - * the initial `MaxChangeRate` is either negative or > `MaxRate` -* the description fields are too large - -This message creates and stores the `Validator` object at appropriate indexes. -Additionally a self-delegation is made with the initial tokens delegation -tokens `Delegation`. The validator always starts as unbonded but may be bonded -in the first end-block. - -## MsgEditValidator - -The `Description`, `CommissionRate` of a validator can be updated using the -`MsgEditValidator` message. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L21-L22 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L70-L88 - -This message is expected to fail if: - -* the initial `CommissionRate` is either negative or > `MaxRate` -* the `CommissionRate` has already been updated within the previous 24 hours -* the `CommissionRate` is > `MaxChangeRate` -* the description fields are too large - -This message stores the updated `Validator` object. - -## MsgDelegate - -Within this message the delegator provides coins, and in return receives -some amount of their validator's (newly created) delegator-shares that are -assigned to `Delegation.Shares`. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L24-L26 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L93-L104 - -This message is expected to fail if: - -* the validator does not exist -* the `Amount` `Coin` has a denomination different than one defined by `params.BondDenom` -* the exchange rate is invalid, meaning the validator has no tokens (due to slashing) but there are outstanding shares -* the amount delegated is less than the minimum allowed delegation - -If an existing `Delegation` object for provided addresses does not already -exist then it is created as part of this message otherwise the existing -`Delegation` is updated to include the newly received shares. - -The delegator receives newly minted shares at the current exchange rate. -The exchange rate is the number of existing shares in the validator divided by -the number of currently delegated tokens. - -The validator is updated in the `ValidatorByPower` index, and the delegation is -tracked in validator object in the `Validators` index. - -It is possible to delegate to a jailed validator, the only difference being it -will not be added to the power index until it is unjailed. - -![Delegation sequence](./delegation_sequence.svg) - -## MsgUndelegate - -The `MsgUndelegate` message allows delegators to undelegate their tokens from -validator. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L32-L34 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L128-L139 - -This message returns a response containing the completion time of the undelegation: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L128-L144 - -This message is expected to fail if: - -* the delegation doesn't exist -* the validator doesn't exist -* the delegation has less shares than the ones worth of `Amount` -* existing `UnbondingDelegation` has maximum entries as defined by `params.MaxEntries` -* the `Amount` has a denomination different than one defined by `params.BondDenom` - -When this message is processed the following actions occur: - -* validator's `DelegatorShares` and the delegation's `Shares` are both reduced by the message `SharesAmount` -* calculate the token worth of the shares remove that amount tokens held within the validator -* with those removed tokens, if the validator is: - * `Bonded` - add them to an entry in `UnbondingDelegation` (create `UnbondingDelegation` if it doesn't exist) with a completion time a full unbonding period from the current time. Update pool shares to reduce BondedTokens and increase NotBondedTokens by token worth of the shares. - * `Unbonding` - add them to an entry in `UnbondingDelegation` (create `UnbondingDelegation` if it doesn't exist) with the same completion time as the validator (`UnbondingMinTime`). - * `Unbonded` - then send the coins the message `DelegatorAddr` -* if there are no more `Shares` in the delegation, then the delegation object is removed from the store - * under this situation if the delegation is the validator's self-delegation then also jail the validator. - -![Unbond sequence](./unbond_sequence.svg) - -## MsgCancelUnbondingDelegation - -The `MsgCancelUnbondingDelegation` message allows delegators to cancel the `unbondingDelegation` entry and deleagate back to a previous validator. - -+++ hhttps://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L36-L40 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L146-L165 - -This message is expected to fail if: - -* the `unbondingDelegation` entry is already processed. -* the `cancel unbonding delegation` amount is greater than the `unbondingDelegation` entry balance. -* the `cancel unbonding delegation` height doesn't exists in the `unbondingDelegationQueue` of the delegator. - -When this message is processed the following actions occur: - -* if the `unbondingDelegation` Entry balance is zero - * in this condition `unbondingDelegation` entry will be removed from `unbondingDelegationQueue`. - * otherwise `unbondingDelegationQueue` will be updated with new `unbondingDelegation` entry balance and initial balance -* the validator's `DelegatorShares` and the delegation's `Shares` are both increased by the message `Amount`. - -## MsgBeginRedelegate - -The redelegation command allows delegators to instantly switch validators. Once -the unbonding period has passed, the redelegation is automatically completed in -the EndBlocker. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L28-L30 - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L109-L121 - -This message returns a response containing the completion time of the redelegation: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/staking/v1beta1/tx.proto#L123-L126 - -This message is expected to fail if: - -* the delegation doesn't exist -* the source or destination validators don't exist -* the delegation has less shares than the ones worth of `Amount` -* the source validator has a receiving redelegation which is not matured (aka. the redelegation may be transitive) -* existing `Redelegation` has maximum entries as defined by `params.MaxEntries` -* the `Amount` `Coin` has a denomination different than one defined by `params.BondDenom` - -When this message is processed the following actions occur: - -* the source validator's `DelegatorShares` and the delegations `Shares` are both reduced by the message `SharesAmount` -* calculate the token worth of the shares remove that amount tokens held within the source validator. -* if the source validator is: - * `Bonded` - add an entry to the `Redelegation` (create `Redelegation` if it doesn't exist) with a completion time a full unbonding period from the current time. Update pool shares to reduce BondedTokens and increase NotBondedTokens by token worth of the shares (this may be effectively reversed in the next step however). - * `Unbonding` - add an entry to the `Redelegation` (create `Redelegation` if it doesn't exist) with the same completion time as the validator (`UnbondingMinTime`). - * `Unbonded` - no action required in this step -* Delegate the token worth to the destination validator, possibly moving tokens back to the bonded state. -* if there are no more `Shares` in the source delegation, then the source delegation object is removed from the store - * under this situation if the delegation is the validator's self-delegation then also jail the validator. - -![Begin redelegation sequence](./begin_redelegation_sequence.svg) diff --git a/versioned_docs/version-0.46/integrate/modules/staking/04_begin_block.md b/versioned_docs/version-0.46/integrate/modules/staking/04_begin_block.md deleted file mode 100644 index 3ba615b5d..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/04_begin_block.md +++ /dev/null @@ -1,16 +0,0 @@ - - -# Begin-Block - -Each abci begin block call, the historical info will get stored and pruned -according to the `HistoricalEntries` parameter. - -## Historical Info Tracking - -If the `HistoricalEntries` parameter is 0, then the `BeginBlock` performs a no-op. - -Otherwise, the latest historical info is stored under the key `historicalInfoKey|height`, while any entries older than `height - HistoricalEntries` is deleted. -In most cases, this results in a single entry being pruned per block. -However, if the parameter `HistoricalEntries` has changed to a lower value there will be multiple entries in the store that must be pruned. diff --git a/versioned_docs/version-0.46/integrate/modules/staking/05_end_block.md b/versioned_docs/version-0.46/integrate/modules/staking/05_end_block.md deleted file mode 100644 index 7eb014211..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/05_end_block.md +++ /dev/null @@ -1,77 +0,0 @@ - - -# End-Block - -Each abci end block call, the operations to update queues and validator set -changes are specified to execute. - -## Validator Set Changes - -The staking validator set is updated during this process by state transitions -that run at the end of every block. As a part of this process any updated -validators are also returned back to Tendermint for inclusion in the Tendermint -validator set which is responsible for validating Tendermint messages at the -consensus layer. Operations are as following: - -* the new validator set is taken as the top `params.MaxValidators` number of - validators retrieved from the `ValidatorsByPower` index -* the previous validator set is compared with the new validator set: - * missing validators begin unbonding and their `Tokens` are transferred from the - `BondedPool` to the `NotBondedPool` `ModuleAccount` - * new validators are instantly bonded and their `Tokens` are transferred from the - `NotBondedPool` to the `BondedPool` `ModuleAccount` - -In all cases, any validators leaving or entering the bonded validator set or -changing balances and staying within the bonded validator set incur an update -message reporting their new consensus power which is passed back to Tendermint. - -The `LastTotalPower` and `LastValidatorsPower` hold the state of the total power -and validator power from the end of the last block, and are used to check for -changes that have occured in `ValidatorsByPower` and the total new power, which -is calculated during `EndBlock`. - -## Queues - -Within staking, certain state-transitions are not instantaneous but take place -over a duration of time (typically the unbonding period). When these -transitions are mature certain operations must take place in order to complete -the state operation. This is achieved through the use of queues which are -checked/processed at the end of each block. - -### Unbonding Validators - -When a validator is kicked out of the bonded validator set (either through -being jailed, or not having sufficient bonded tokens) it begins the unbonding -process along with all its delegations begin unbonding (while still being -delegated to this validator). At this point the validator is said to be an -"unbonding validator", whereby it will mature to become an "unbonded validator" -after the unbonding period has passed. - -Each block the validator queue is to be checked for mature unbonding validators -(namely with a completion time <= current time and completion height <= current -block height). At this point any mature validators which do not have any -delegations remaining are deleted from state. For all other mature unbonding -validators that still have remaining delegations, the `validator.Status` is -switched from `types.Unbonding` to -`types.Unbonded`. - -### Unbonding Delegations - -Complete the unbonding of all mature `UnbondingDelegations.Entries` within the -`UnbondingDelegations` queue with the following procedure: - -* transfer the balance coins to the delegator's wallet address -* remove the mature entry from `UnbondingDelegation.Entries` -* remove the `UnbondingDelegation` object from the store if there are no - remaining entries. - -### Redelegations - -Complete the unbonding of all mature `Redelegation.Entries` within the -`Redelegations` queue with the following procedure: - -* remove the mature entry from `Redelegation.Entries` -* remove the `Redelegation` object from the store if there are no - remaining entries. diff --git a/versioned_docs/version-0.46/integrate/modules/staking/06_hooks.md b/versioned_docs/version-0.46/integrate/modules/staking/06_hooks.md deleted file mode 100644 index 18af77238..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/06_hooks.md +++ /dev/null @@ -1,27 +0,0 @@ - - -# Hooks - -Other modules may register operations to execute when a certain event has -occurred within staking. These events can be registered to execute either -right `Before` or `After` the staking event (as per the hook name). The -following hooks can registered with staking: - -* `AfterValidatorCreated(Context, ValAddress) error` - * called when a validator is created -* `BeforeValidatorModified(Context, ValAddress) error` - * called when a validator's state is changed -* `AfterValidatorRemoved(Context, ConsAddress, ValAddress) error` - * called when a validator is deleted -* `AfterValidatorBonded(Context, ConsAddress, ValAddress) error` - * called when a validator is bonded -* `AfterValidatorBeginUnbonding(Context, ConsAddress, ValAddress) error` - * called when a validator begins unbonding -* `BeforeDelegationCreated(Context, AccAddress, ValAddress) error` - * called when a delegation is created -* `BeforeDelegationSharesModified(Context, AccAddress, ValAddress) error` - * called when a delegation's shares are modified -* `BeforeDelegationRemoved(Context, AccAddress, ValAddress) error` - * called when a delegation is removed diff --git a/versioned_docs/version-0.46/integrate/modules/staking/07_events.md b/versioned_docs/version-0.46/integrate/modules/staking/07_events.md deleted file mode 100644 index eeeb84c4b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/07_events.md +++ /dev/null @@ -1,90 +0,0 @@ - - -# Events - -The staking module emits the following events: - -## EndBlocker - -| Type | Attribute Key | Attribute Value | -| --------------------- | --------------------- | ------------------------- | -| complete_unbonding | amount | {totalUnbondingAmount} | -| complete_unbonding | validator | {validatorAddress} | -| complete_unbonding | delegator | {delegatorAddress} | -| complete_redelegation | amount | {totalRedelegationAmount} | -| complete_redelegation | source_validator | {srcValidatorAddress} | -| complete_redelegation | destination_validator | {dstValidatorAddress} | -| complete_redelegation | delegator | {delegatorAddress} | - -## Msg's - -### MsgCreateValidator - -| Type | Attribute Key | Attribute Value | -| ---------------- | ------------- | ------------------ | -| create_validator | validator | {validatorAddress} | -| create_validator | amount | {delegationAmount} | -| message | module | staking | -| message | action | create_validator | -| message | sender | {senderAddress} | - -### MsgEditValidator - -| Type | Attribute Key | Attribute Value | -| -------------- | ------------------- | ------------------- | -| edit_validator | commission_rate | {commissionRate} | -| edit_validator | min_self_delegation | {minSelfDelegation} | -| message | module | staking | -| message | action | edit_validator | -| message | sender | {senderAddress} | - -### MsgDelegate - -| Type | Attribute Key | Attribute Value | -| -------- | ------------- | ------------------ | -| delegate | validator | {validatorAddress} | -| delegate | amount | {delegationAmount} | -| message | module | staking | -| message | action | delegate | -| message | sender | {senderAddress} | - -### MsgUndelegate - -| Type | Attribute Key | Attribute Value | -| ------- | ------------------- | ------------------ | -| unbond | validator | {validatorAddress} | -| unbond | amount | {unbondAmount} | -| unbond | completion_time [0] | {completionTime} | -| message | module | staking | -| message | action | begin_unbonding | -| message | sender | {senderAddress} | - -* [0] Time is formatted in the RFC3339 standard - -### MsgCancelUnbondingDelegation - -| Type | Attribute Key | Attribute Value | -| ----------------------------- | ------------------ | ------------------------------------| -| cancel_unbonding_delegation | validator | {validatorAddress} | -| cancel_unbonding_delegation | delegator | {delegatorAddress} | -| cancel_unbonding_delegation | amount | {cancelUnbondingDelegationAmount} | -| cancel_unbonding_delegation | creation_height | {unbondingCreationHeight} | -| message | module | staking | -| message | action | cancel_unbond | -| message | sender | {senderAddress} | - -### MsgBeginRedelegate - -| Type | Attribute Key | Attribute Value | -| ---------- | --------------------- | --------------------- | -| redelegate | source_validator | {srcValidatorAddress} | -| redelegate | destination_validator | {dstValidatorAddress} | -| redelegate | amount | {unbondAmount} | -| redelegate | completion_time [0] | {completionTime} | -| message | module | staking | -| message | action | begin_redelegate | -| message | sender | {senderAddress} | - -* [0] Time is formatted in the RFC3339 standard diff --git a/versioned_docs/version-0.46/integrate/modules/staking/08_params.md b/versioned_docs/version-0.46/integrate/modules/staking/08_params.md deleted file mode 100644 index e4a56ab3f..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/08_params.md +++ /dev/null @@ -1,16 +0,0 @@ - - -# Parameters - -The staking module contains the following parameters: - -| Key | Type | Example | -|-------------------|------------------|------------------------| -| UnbondingTime | string (time ns) | "259200000000000" | -| MaxValidators | uint16 | 100 | -| KeyMaxEntries | uint16 | 7 | -| HistoricalEntries | uint16 | 3 | -| BondDenom | string | "stake" | -| MinCommissionRate | string | "0.000000000000000000" | diff --git a/versioned_docs/version-0.46/integrate/modules/staking/09_client.md b/versioned_docs/version-0.46/integrate/modules/staking/09_client.md deleted file mode 100644 index 0c3383d01..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/09_client.md +++ /dev/null @@ -1,2101 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `staking` module using the CLI. - -### Query - -The `query` commands allows users to query `staking` state. - -```bash -simd query staking --help -``` - -#### delegation - -The `delegation` command allows users to query delegations for an individual delegator on an individual validator. - -Usage: - -```bash -simd query staking delegation [delegator-addr] [validator-addr] [flags] -``` - -Example: - -```bash -simd query staking delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -Example Output: - -```bash -balance: - amount: "10000000000" - denom: stake -delegation: - delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - shares: "10000000000.000000000000000000" - validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -#### delegations - -The `delegations` command allows users to query delegations for an individual delegator on all validators. - -Usage: - -```bash -simd query staking delegations [delegator-addr] [flags] -``` - -Example: - -```bash -simd query staking delegations cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -``` - -Example Output: - -```bash -delegation_responses: -- balance: - amount: "10000000000" - denom: stake - delegation: - delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - shares: "10000000000.000000000000000000" - validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -- balance: - amount: "10000000000" - denom: stake - delegation: - delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - shares: "10000000000.000000000000000000" - validator_address: cosmosvaloper1x20lytyf6zkcrv5edpkfkn8sz578qg5sqfyqnp -pagination: - next_key: null - total: "0" -``` - -#### delegations-to - -The `delegations-to` command allows users to query delegations on an individual validator. - -Usage: - -```bash -simd query staking delegations-to [validator-addr] [flags] -``` - -Example: - -```bash -simd query staking delegations-to cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -Example Output: - -```bash -- balance: - amount: "504000000" - denom: stake - delegation: - delegator_address: cosmos1q2qwwynhv8kh3lu5fkeex4awau9x8fwt45f5cp - shares: "504000000.000000000000000000" - validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -- balance: - amount: "78125000000" - denom: uixo - delegation: - delegator_address: cosmos1qvppl3479hw4clahe0kwdlfvf8uvjtcd99m2ca - shares: "78125000000.000000000000000000" - validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -pagination: - next_key: null - total: "0" -``` - -#### historical-info - -The `historical-info` command allows users to query historical information at given height. - -Usage: - -```bash -simd query staking historical-info [height] [flags] -``` - -Example: - -```bash -simd query staking historical-info 10 -``` - -Example Output: - -```bash -header: - app_hash: Lbx8cXpI868wz8sgp4qPYVrlaKjevR5WP/IjUxwp3oo= - chain_id: testnet - consensus_hash: BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8= - data_hash: 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= - evidence_hash: 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= - height: "10" - last_block_id: - hash: RFbkpu6pWfSThXxKKl6EZVDnBSm16+U0l0xVjTX08Fk= - part_set_header: - hash: vpIvXD4rxD5GM4MXGz0Sad9I7//iVYLzZsEU4BVgWIU= - total: 1 - last_commit_hash: Ne4uXyx4QtNp4Zx89kf9UK7oG9QVbdB6e7ZwZkhy8K0= - last_results_hash: 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= - next_validators_hash: nGBgKeWBjoxeKFti00CxHsnULORgKY4LiuQwBuUrhCs= - proposer_address: mMEP2c2IRPLr99LedSRtBg9eONM= - time: "2021-10-01T06:00:49.785790894Z" - validators_hash: nGBgKeWBjoxeKFti00CxHsnULORgKY4LiuQwBuUrhCs= - version: - app: "0" - block: "11" -valset: -- commission: - commission_rates: - max_change_rate: "0.010000000000000000" - max_rate: "0.200000000000000000" - rate: "0.100000000000000000" - update_time: "2021-10-01T05:52:50.380144238Z" - consensus_pubkey: - '@type': /cosmos.crypto.ed25519.PubKey - key: Auxs3865HpB/EfssYOzfqNhEJjzys2Fo6jD5B8tPgC8= - delegator_shares: "10000000.000000000000000000" - description: - details: "" - identity: "" - moniker: myvalidator - security_contact: "" - website: "" - jailed: false - min_self_delegation: "1" - operator_address: cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc - status: BOND_STATUS_BONDED - tokens: "10000000" - unbonding_height: "0" - unbonding_time: "1970-01-01T00:00:00Z" -``` - -#### params - -The `params` command allows users to query values set as staking parameters. - -Usage: - -```bash -simd query staking params [flags] -``` - -Example: - -```bash -simd query staking params -``` - -Example Output: - -```bash -bond_denom: stake -historical_entries: 10000 -max_entries: 7 -max_validators: 50 -unbonding_time: 1814400s -``` - -#### pool - -The `pool` command allows users to query values for amounts stored in the staking pool. - -Usage: - -```bash -simd q staking pool [flags] -``` - -Example: - -```bash -simd q staking pool -``` - -Example Output: - -```bash -bonded_tokens: "10000000" -not_bonded_tokens: "0" -``` - -#### redelegation - -The `redelegation` command allows users to query a redelegation record based on delegator and a source and destination validator address. - -Usage: - -```bash -simd query staking redelegation [delegator-addr] [src-validator-addr] [dst-validator-addr] [flags] -``` - -Example: - -```bash -simd query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -Example Output: - -```bash -pagination: null -redelegation_responses: -- entries: - - balance: "50000000" - redelegation_entry: - completion_time: "2021-10-24T20:33:21.960084845Z" - creation_height: 2.382847e+06 - initial_balance: "50000000" - shares_dst: "50000000.000000000000000000" - - balance: "5000000000" - redelegation_entry: - completion_time: "2021-10-25T21:33:54.446846862Z" - creation_height: 2.397271e+06 - initial_balance: "5000000000" - shares_dst: "5000000000.000000000000000000" - redelegation: - delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - entries: null - validator_dst_address: cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm - validator_src_address: cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm -``` - -#### redelegations - -The `redelegations` command allows users to query all redelegation records for an individual delegator. - -Usage: - -```bash -simd query staking redelegations [delegator-addr] [flags] -``` - -Example: - -```bash -simd query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -``` - -Example Output: - -```bash -pagination: - next_key: null - total: "0" -redelegation_responses: -- entries: - - balance: "50000000" - redelegation_entry: - completion_time: "2021-10-24T20:33:21.960084845Z" - creation_height: 2.382847e+06 - initial_balance: "50000000" - shares_dst: "50000000.000000000000000000" - - balance: "5000000000" - redelegation_entry: - completion_time: "2021-10-25T21:33:54.446846862Z" - creation_height: 2.397271e+06 - initial_balance: "5000000000" - shares_dst: "5000000000.000000000000000000" - redelegation: - delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - entries: null - validator_dst_address: cosmosvaloper1uccl5ugxrm7vqlzwqr04pjd320d2fz0z3hc6vm - validator_src_address: cosmosvaloper1zppjyal5emta5cquje8ndkpz0rs046m7zqxrpp -- entries: - - balance: "562770000000" - redelegation_entry: - completion_time: "2021-10-25T21:42:07.336911677Z" - creation_height: 2.39735e+06 - initial_balance: "562770000000" - shares_dst: "562770000000.000000000000000000" - redelegation: - delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - entries: null - validator_dst_address: cosmosvaloper1uccl5ugxrm7vqlzwqr04pjd320d2fz0z3hc6vm - validator_src_address: cosmosvaloper1zppjyal5emta5cquje8ndkpz0rs046m7zqxrpp -``` - -#### redelegations-from - -The `redelegations-from` command allows users to query delegations that are redelegating _from_ a validator. - -Usage: - -```bash -simd query staking redelegations-from [validator-addr] [flags] -``` - -Example: - -```bash -simd query staking redelegations-from cosmosvaloper1y4rzzrgl66eyhzt6gse2k7ej3zgwmngeleucjy -``` - -Example Output: - -```bash -pagination: - next_key: null - total: "0" -redelegation_responses: -- entries: - - balance: "50000000" - redelegation_entry: - completion_time: "2021-10-24T20:33:21.960084845Z" - creation_height: 2.382847e+06 - initial_balance: "50000000" - shares_dst: "50000000.000000000000000000" - - balance: "5000000000" - redelegation_entry: - completion_time: "2021-10-25T21:33:54.446846862Z" - creation_height: 2.397271e+06 - initial_balance: "5000000000" - shares_dst: "5000000000.000000000000000000" - redelegation: - delegator_address: cosmos1pm6e78p4pgn0da365plzl4t56pxy8hwtqp2mph - entries: null - validator_dst_address: cosmosvaloper1uccl5ugxrm7vqlzwqr04pjd320d2fz0z3hc6vm - validator_src_address: cosmosvaloper1y4rzzrgl66eyhzt6gse2k7ej3zgwmngeleucjy -- entries: - - balance: "221000000" - redelegation_entry: - completion_time: "2021-10-05T21:05:45.669420544Z" - creation_height: 2.120693e+06 - initial_balance: "221000000" - shares_dst: "221000000.000000000000000000" - redelegation: - delegator_address: cosmos1zqv8qxy2zgn4c58fz8jt8jmhs3d0attcussrf6 - entries: null - validator_dst_address: cosmosvaloper10mseqwnwtjaqfrwwp2nyrruwmjp6u5jhah4c3y - validator_src_address: cosmosvaloper1y4rzzrgl66eyhzt6gse2k7ej3zgwmngeleucjy -``` - -#### unbonding-delegation - -The `unbonding-delegation` command allows users to query unbonding delegations for an individual delegator on an individual validator. - -Usage: - -```bash -simd query staking unbonding-delegation [delegator-addr] [validator-addr] [flags] -``` - -Example: - -```bash -simd query staking unbonding-delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -Example Output: - -```bash -delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -entries: -- balance: "52000000" - completion_time: "2021-11-02T11:35:55.391594709Z" - creation_height: "55078" - initial_balance: "52000000" -validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -#### unbonding-delegations - -The `unbonding-delegations` command allows users to query all unbonding-delegations records for one delegator. - -Usage: - -```bash -simd query staking unbonding-delegations [delegator-addr] [flags] -``` - -Example: - -```bash -simd query staking unbonding-delegations cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p -``` - -Example Output: - -```bash -pagination: - next_key: null - total: "0" -unbonding_responses: -- delegator_address: cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p - entries: - - balance: "52000000" - completion_time: "2021-11-02T11:35:55.391594709Z" - creation_height: "55078" - initial_balance: "52000000" - validator_address: cosmosvaloper1t8ehvswxjfn3ejzkjtntcyrqwvmvuknzmvtaaa - -``` - -#### unbonding-delegations-from - -The `unbonding-delegations-from` command allows users to query delegations that are unbonding _from_ a validator. - -Usage: - -```bash -simd query staking unbonding-delegations-from [validator-addr] [flags] -``` - -Example: - -```bash -simd query staking unbonding-delegations-from cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -Example Output: - -```bash -pagination: - next_key: null - total: "0" -unbonding_responses: -- delegator_address: cosmos1qqq9txnw4c77sdvzx0tkedsafl5s3vk7hn53fn - entries: - - balance: "150000000" - completion_time: "2021-11-01T21:41:13.098141574Z" - creation_height: "46823" - initial_balance: "150000000" - validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -- delegator_address: cosmos1peteje73eklqau66mr7h7rmewmt2vt99y24f5z - entries: - - balance: "24000000" - completion_time: "2021-10-31T02:57:18.192280361Z" - creation_height: "21516" - initial_balance: "24000000" - validator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -#### validator - -The `validator` command allows users to query details about an individual validator. - -Usage: - -```bash -simd query staking validator [validator-addr] [flags] -``` - -Example: - -```bash -simd query staking validator cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -``` - -Example Output: - -```bash -commission: - commission_rates: - max_change_rate: "0.020000000000000000" - max_rate: "0.200000000000000000" - rate: "0.050000000000000000" - update_time: "2021-10-01T19:24:52.663191049Z" -consensus_pubkey: - '@type': /cosmos.crypto.ed25519.PubKey - key: sIiexdJdYWn27+7iUHQJDnkp63gq/rzUq1Y+fxoGjXc= -delegator_shares: "32948270000.000000000000000000" -description: - details: Witval is the validator arm from Vitwit. Vitwit is into software consulting - and services business since 2015. We are working closely with Cosmos ecosystem - since 2018. We are also building tools for the ecosystem, Aneka is our explorer - for the cosmos ecosystem. - identity: 51468B615127273A - moniker: Witval - security_contact: "" - website: "" -jailed: false -min_self_delegation: "1" -operator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj -status: BOND_STATUS_BONDED -tokens: "32948270000" -unbonding_height: "0" -unbonding_time: "1970-01-01T00:00:00Z" -``` - -#### validators - -The `validators` command allows users to query details about all validators on a network. - -Usage: - -```bash -simd query staking validators [flags] -``` - -Example: - -```bash -simd query staking validators -``` - -Example Output: - -```bash -pagination: - next_key: FPTi7TKAjN63QqZh+BaXn6gBmD5/ - total: "0" -validators: -commission: - commission_rates: - max_change_rate: "0.020000000000000000" - max_rate: "0.200000000000000000" - rate: "0.050000000000000000" - update_time: "2021-10-01T19:24:52.663191049Z" -consensus_pubkey: - '@type': /cosmos.crypto.ed25519.PubKey - key: sIiexdJdYWn27+7iUHQJDnkp63gq/rzUq1Y+fxoGjXc= -delegator_shares: "32948270000.000000000000000000" -description: - details: Witval is the validator arm from Vitwit. Vitwit is into software consulting - and services business since 2015. We are working closely with Cosmos ecosystem - since 2018. We are also building tools for the ecosystem, Aneka is our explorer - for the cosmos ecosystem. - identity: 51468B615127273A - moniker: Witval - security_contact: "" - website: "" - jailed: false - min_self_delegation: "1" - operator_address: cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj - status: BOND_STATUS_BONDED - tokens: "32948270000" - unbonding_height: "0" - unbonding_time: "1970-01-01T00:00:00Z" -- commission: - commission_rates: - max_change_rate: "0.100000000000000000" - max_rate: "0.200000000000000000" - rate: "0.050000000000000000" - update_time: "2021-10-04T18:02:21.446645619Z" - consensus_pubkey: - '@type': /cosmos.crypto.ed25519.PubKey - key: GDNpuKDmCg9GnhnsiU4fCWktuGUemjNfvpCZiqoRIYA= - delegator_shares: "559343421.000000000000000000" - description: - details: Noderunners is a professional validator in POS networks. We have a huge - node running experience, reliable soft and hardware. Our commissions are always - low, our support to delegators is always full. Stake with us and start receiving - your Cosmos rewards now! - identity: 812E82D12FEA3493 - moniker: Noderunners - security_contact: info@noderunners.biz - website: http://noderunners.biz - jailed: false - min_self_delegation: "1" - operator_address: cosmosvaloper1q5ku90atkhktze83j9xjaks2p7uruag5zp6wt7 - status: BOND_STATUS_BONDED - tokens: "559343421" - unbonding_height: "0" - unbonding_time: "1970-01-01T00:00:00Z" -``` - -### Transactions - -The `tx` commands allows users to interact with the `staking` module. - -```bash -simd tx staking --help -``` - -#### create-validator - -The command `create-validator` allows users to create new validator initialized with a self-delegation to it. - -Usage: - -```bash -simd tx staking create-validator [flags] -``` - -Example: - -```bash -simd tx staking create-validator \ - --amount=1000000stake \ - --pubkey=$(simd tendermint show-validator) \ - --moniker="my-moniker" \ - --website="https://myweb.site" \ - --details="description of your validator" \ - --chain-id="name_of_chain_id" \ - --commission-rate="0.10" \ - --commission-max-rate="0.20" \ - --commission-max-change-rate="0.01" \ - --min-self-delegation="1" \ - --gas="auto" \ - --gas-adjustment="1.2" \ - --gas-prices="0.025stake" \ - --from=mykey -``` - -#### delegate - -The command `delegate` allows users to delegate liquid tokens to a validator. - -Usage: - -```bash -simd tx staking delegate [validator-addr] [amount] [flags] -``` - -Example: - -```bash -simd tx staking delegate cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey -``` - -#### edit-validator - -The command `edit-validator` allows users to edit an existing validator account. - -Usage: - -```bash -simd tx staking edit-validator [flags] -``` - -Example: - -```bash -simd tx staking edit-validator --moniker "new_moniker_name" --website "new_webiste_url" --from mykey -``` - -#### redelegate - -The command `redelegate` allows users to redelegate illiquid tokens from one validator to another. - -Usage: - -```bash -simd tx staking redelegate [src-validator-addr] [dst-validator-addr] [amount] [flags] -``` - -Example: - -```bash -simd tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey -``` - -#### unbond - -The command `unbond` allows users to unbond shares from a validator. - -Usage: - -```bash -simd tx staking unbond [validator-addr] [amount] [flags] -``` - -Example: - -```bash -simd tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey -``` -#### cancel unbond -The command `cancel-unbond` allow users to cancel the unbonding delegation entry and delegate back to the original validator. - -Usage: -```bash -simd tx staking cancel-unbond [validator-addr] [amount] [creation-height] -``` - -Example: -```bash -simd tx staking cancel-unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake 123123 --from mykey -``` - - -## gRPC - -A user can query the `staking` module using gRPC endpoints. - -### Validators - -The `Validators` endpoint queries all validators that match the given status. - -```bash -cosmos.staking.v1beta1.Query/Validators -``` - -Example: - -```bash -grpcurl -plaintext localhost:9090 cosmos.staking.v1beta1.Query/Validators -``` - -Example Output: - -```bash -{ - "validators": [ - { - "operatorAddress": "cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "consensusPubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"Auxs3865HpB/EfssYOzfqNhEJjzys2Fo6jD5B8tPgC8="}, - "status": "BOND_STATUS_BONDED", - "tokens": "10000000", - "delegatorShares": "10000000000000000000000000", - "description": { - "moniker": "myvalidator" - }, - "unbondingTime": "1970-01-01T00:00:00Z", - "commission": { - "commissionRates": { - "rate": "100000000000000000", - "maxRate": "200000000000000000", - "maxChangeRate": "10000000000000000" - }, - "updateTime": "2021-10-01T05:52:50.380144238Z" - }, - "minSelfDelegation": "1" - } - ], - "pagination": { - "total": "1" - } -} -``` - -### Validator - -The `Validator` endpoint queries validator information for given validator address. - -```bash -cosmos.staking.v1beta1.Query/Validator -``` - -Example: - -```bash -grpcurl -plaintext -d '{"validator_addr":"cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/Validator -``` - -Example Output: - -```bash -{ - "validator": { - "operatorAddress": "cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "consensusPubkey": {"@type":"/cosmos.crypto.ed25519.PubKey","key":"Auxs3865HpB/EfssYOzfqNhEJjzys2Fo6jD5B8tPgC8="}, - "status": "BOND_STATUS_BONDED", - "tokens": "10000000", - "delegatorShares": "10000000000000000000000000", - "description": { - "moniker": "myvalidator" - }, - "unbondingTime": "1970-01-01T00:00:00Z", - "commission": { - "commissionRates": { - "rate": "100000000000000000", - "maxRate": "200000000000000000", - "maxChangeRate": "10000000000000000" - }, - "updateTime": "2021-10-01T05:52:50.380144238Z" - }, - "minSelfDelegation": "1" - } -} -``` - -### ValidatorDelegations - -The `ValidatorDelegations` endpoint queries delegate information for given validator. - -```bash -cosmos.staking.v1beta1.Query/ValidatorDelegations -``` - -Example: - -```bash -grpcurl -plaintext -d '{"validator_addr":"cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/ValidatorDelegations -``` - -Example Output: - -```bash -{ - "delegationResponses": [ - { - "delegation": { - "delegatorAddress": "cosmos1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgy3ua5t", - "validatorAddress": "cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "shares": "10000000000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "10000000" - } - } - ], - "pagination": { - "total": "1" - } -} -``` - -### ValidatorUnbondingDelegations - -The `ValidatorUnbondingDelegations` endpoint queries delegate information for given validator. - -```bash -cosmos.staking.v1beta1.Query/ValidatorUnbondingDelegations -``` - -Example: - -```bash -grpcurl -plaintext -d '{"validator_addr":"cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/ValidatorUnbondingDelegations -``` - -Example Output: - -```bash -{ - "unbonding_responses": [ - { - "delegator_address": "cosmos1z3pzzw84d6xn00pw9dy3yapqypfde7vg6965fy", - "validator_address": "cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "entries": [ - { - "creation_height": "25325", - "completion_time": "2021-10-31T09:24:36.797320636Z", - "initial_balance": "20000000", - "balance": "20000000" - } - ] - }, - { - "delegator_address": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77", - "validator_address": "cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "entries": [ - { - "creation_height": "13100", - "completion_time": "2021-10-30T12:53:02.272266791Z", - "initial_balance": "1000000", - "balance": "1000000" - } - ] - }, - ], - "pagination": { - "next_key": null, - "total": "8" - } -} -``` - -### Delegation - -The `Delegation` endpoint queries delegate information for given validator delegator pair. - -```bash -cosmos.staking.v1beta1.Query/Delegation -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77", validator_addr":"cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/Delegation -``` - -Example Output: - -```bash -{ - "delegation_response": - { - "delegation": - { - "delegator_address":"cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77", - "validator_address":"cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "shares":"25083119936.000000000000000000" - }, - "balance": - { - "denom":"stake", - "amount":"25083119936" - } - } -} -``` - -### UnbondingDelegation - -The `UnbondingDelegation` endpoint queries unbonding information for given validator delegator. - -```bash -cosmos.staking.v1beta1.Query/UnbondingDelegation -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77", validator_addr":"cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/UnbondingDelegation -``` - -Example Output: - -```bash -{ - "unbond": { - "delegator_address": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77", - "validator_address": "cosmosvaloper1rne8lgs98p0jqe82sgt0qr4rdn4hgvmgp9ggcc", - "entries": [ - { - "creation_height": "136984", - "completion_time": "2021-11-08T05:38:47.505593891Z", - "initial_balance": "400000000", - "balance": "400000000" - }, - { - "creation_height": "137005", - "completion_time": "2021-11-08T05:40:53.526196312Z", - "initial_balance": "385000000", - "balance": "385000000" - } - ] - } -} -``` - -### DelegatorDelegations - -The `DelegatorDelegations` endpoint queries all delegations of a given delegator address. - -```bash -cosmos.staking.v1beta1.Query/DelegatorDelegations -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/DelegatorDelegations -``` - -Example Output: - -```bash -{ - "delegation_responses": [ - {"delegation":{"delegator_address":"cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77","validator_address":"cosmosvaloper1eh5mwu044gd5ntkkc2xgfg8247mgc56fww3vc8","shares":"25083339023.000000000000000000"},"balance":{"denom":"stake","amount":"25083339023"}} - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### DelegatorUnbondingDelegations - -The `DelegatorUnbondingDelegations` endpoint queries all unbonding delegations of a given delegator address. - -```bash -cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations -``` - -Example Output: - -```bash -{ - "unbonding_responses": [ - { - "delegator_address": "cosmos1y8nyfvmqh50p6ldpzljk3yrglppdv3t8phju77", - "validator_address": "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9uxyejze", - "entries": [ - { - "creation_height": "136984", - "completion_time": "2021-11-08T05:38:47.505593891Z", - "initial_balance": "400000000", - "balance": "400000000" - }, - { - "creation_height": "137005", - "completion_time": "2021-11-08T05:40:53.526196312Z", - "initial_balance": "385000000", - "balance": "385000000" - } - ] - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### Redelegations - -The `Redelegations` endpoint queries redelegations of given address. - -```bash -cosmos.staking.v1beta1.Query/Redelegations -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1ld5p7hn43yuh8ht28gm9pfjgj2fctujp2tgwvf", "src_validator_addr" : "cosmosvaloper1j7euyj85fv2jugejrktj540emh9353ltgppc3g", "dst_validator_addr" : "cosmosvaloper1yy3tnegzmkdcm7czzcy3flw5z0zyr9vkkxrfse"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/Redelegations -``` - -Example Output: - -```bash -{ - "redelegation_responses": [ - { - "redelegation": { - "delegator_address": "cosmos1ld5p7hn43yuh8ht28gm9pfjgj2fctujp2tgwvf", - "validator_src_address": "cosmosvaloper1j7euyj85fv2jugejrktj540emh9353ltgppc3g", - "validator_dst_address": "cosmosvaloper1yy3tnegzmkdcm7czzcy3flw5z0zyr9vkkxrfse", - "entries": null - }, - "entries": [ - { - "redelegation_entry": { - "creation_height": 135932, - "completion_time": "2021-11-08T03:52:55.299147901Z", - "initial_balance": "2900000", - "shares_dst": "2900000.000000000000000000" - }, - "balance": "2900000" - } - ] - } - ], - "pagination": null -} -``` - -### DelegatorValidators - -The `DelegatorValidators` endpoint queries all validators information for given delegator. - -```bash -cosmos.staking.v1beta1.Query/DelegatorValidators -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1ld5p7hn43yuh8ht28gm9pfjgj2fctujp2tgwvf"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/DelegatorValidators -``` - -Example Output: - -```bash -{ - "validators": [ - { - "operator_address": "cosmosvaloper1eh5mwu044gd5ntkkc2xgfg8247mgc56fww3vc8", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "UPwHWxH1zHJWGOa/m6JB3f5YjHMvPQPkVbDqqi+U7Uw=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "347260647559", - "delegator_shares": "347260647559.000000000000000000", - "description": { - "moniker": "BouBouNode", - "identity": "", - "website": "https://boubounode.com", - "security_contact": "", - "details": "AI-based Validator. #1 AI Validator on Game of Stakes. Fairly priced. Don't trust (humans), verify. Made with BouBou love." - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.061000000000000000", - "max_rate": "0.300000000000000000", - "max_change_rate": "0.150000000000000000" - }, - "update_time": "2021-10-01T15:00:00Z" - }, - "min_self_delegation": "1" - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### DelegatorValidator - -The `DelegatorValidator` endpoint queries validator information for given delegator validator - -```bash -cosmos.staking.v1beta1.Query/DelegatorValidator -``` - -Example: - -```bash -grpcurl -plaintext \ --d '{"delegator_addr": "cosmos1eh5mwu044gd5ntkkc2xgfg8247mgc56f3n8rr7", "validator_addr": "cosmosvaloper1eh5mwu044gd5ntkkc2xgfg8247mgc56fww3vc8"}' \ -localhost:9090 cosmos.staking.v1beta1.Query/DelegatorValidator -``` - -Example Output: - -```bash -{ - "validator": { - "operator_address": "cosmosvaloper1eh5mwu044gd5ntkkc2xgfg8247mgc56fww3vc8", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "UPwHWxH1zHJWGOa/m6JB3f5YjHMvPQPkVbDqqi+U7Uw=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "347262754841", - "delegator_shares": "347262754841.000000000000000000", - "description": { - "moniker": "BouBouNode", - "identity": "", - "website": "https://boubounode.com", - "security_contact": "", - "details": "AI-based Validator. #1 AI Validator on Game of Stakes. Fairly priced. Don't trust (humans), verify. Made with BouBou love." - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.061000000000000000", - "max_rate": "0.300000000000000000", - "max_change_rate": "0.150000000000000000" - }, - "update_time": "2021-10-01T15:00:00Z" - }, - "min_self_delegation": "1" - } -} -``` - -### HistoricalInfo - -```bash -cosmos.staking.v1beta1.Query/HistoricalInfo -``` - -Example: - -```bash -grpcurl -plaintext -d '{"height" : 1}' localhost:9090 cosmos.staking.v1beta1.Query/HistoricalInfo -``` - -Example Output: - -```bash -{ - "hist": { - "header": { - "version": { - "block": "11", - "app": "0" - }, - "chain_id": "simd-1", - "height": "140142", - "time": "2021-10-11T10:56:29.720079569Z", - "last_block_id": { - "hash": "9gri/4LLJUBFqioQ3NzZIP9/7YHR9QqaM6B2aJNQA7o=", - "part_set_header": { - "total": 1, - "hash": "Hk1+C864uQkl9+I6Zn7IurBZBKUevqlVtU7VqaZl1tc=" - } - }, - "last_commit_hash": "VxrcS27GtvGruS3I9+AlpT7udxIT1F0OrRklrVFSSKc=", - "data_hash": "80BjOrqNYUOkTnmgWyz9AQ8n7SoEmPVi4QmAe8RbQBY=", - "validators_hash": "95W49n2hw8RWpr1GPTAO5MSPi6w6Wjr3JjjS7AjpBho=", - "next_validators_hash": "95W49n2hw8RWpr1GPTAO5MSPi6w6Wjr3JjjS7AjpBho=", - "consensus_hash": "BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8=", - "app_hash": "ZZaxnSY3E6Ex5Bvkm+RigYCK82g8SSUL53NymPITeOE=", - "last_results_hash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", - "evidence_hash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", - "proposer_address": "aH6dO428B+ItuoqPq70efFHrSMY=" - }, - "valset": [ - { - "operator_address": "cosmosvaloper196ax4vc0lwpxndu9dyhvca7jhxp70rmcqcnylw", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "/O7BtNW0pafwfvomgR4ZnfldwPXiFfJs9mHg3gwfv5Q=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "1426045203613", - "delegator_shares": "1426045203613.000000000000000000", - "description": { - "moniker": "SG-1", - "identity": "48608633F99D1B60", - "website": "https://sg-1.online", - "security_contact": "", - "details": "SG-1 - your favorite validator on Witval. We offer 100% Soft Slash protection." - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.037500000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.030000000000000000" - }, - "update_time": "2021-10-01T15:00:00Z" - }, - "min_self_delegation": "1" - } - ] - } -} - -``` - -### Pool - -The `Pool` endpoint queries the pool information. - -```bash -cosmos.staking.v1beta1.Query/Pool -``` - -Example: - -```bash -grpcurl -plaintext -d localhost:9090 cosmos.staking.v1beta1.Query/Pool -``` - -Example Output: - -```bash -{ - "pool": { - "not_bonded_tokens": "369054400189", - "bonded_tokens": "15657192425623" - } -} -``` - -### Params - -The `Params` endpoint queries the pool information. - -```bash -cosmos.staking.v1beta1.Query/Params -``` - -Example: - -```bash -grpcurl -plaintext localhost:9090 cosmos.staking.v1beta1.Query/Params -``` - -Example Output: - -```bash -{ - "params": { - "unbondingTime": "1814400s", - "maxValidators": 100, - "maxEntries": 7, - "historicalEntries": 10000, - "bondDenom": "stake" - } -} -``` - -## REST - -A user can query the `staking` module using REST endpoints. - -### DelegatorDelegations - -The `DelegtaorDelegations` REST endpoint queries all delegations of a given delegator address. - -```bash -/cosmos/staking/v1beta1/delegations/{delegatorAddr} -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/staking/v1beta1/delegations/cosmos1vcs68xf2tnqes5tg0khr0vyevm40ff6zdxatp5" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "delegation_responses": [ - { - "delegation": { - "delegator_address": "cosmos1vcs68xf2tnqes5tg0khr0vyevm40ff6zdxatp5", - "validator_address": "cosmosvaloper1quqxfrxkycr0uzt4yk0d57tcq3zk7srm7sm6r8", - "shares": "256250000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "256250000" - } - }, - { - "delegation": { - "delegator_address": "cosmos1vcs68xf2tnqes5tg0khr0vyevm40ff6zdxatp5", - "validator_address": "cosmosvaloper194v8uwee2fvs2s8fa5k7j03ktwc87h5ym39jfv", - "shares": "255150000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "255150000" - } - } - ], - "pagination": { - "next_key": null, - "total": "2" - } -} -``` - -### Redelegations - -The `Redelegations` REST endpoint queries redelegations of given address. - -```bash -/cosmos/staking/v1beta1/delegators/{delegatorAddr}/redelegations -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/delegators/cosmos1thfntksw0d35n2tkr0k8v54fr8wxtxwxl2c56e/redelegations?srcValidatorAddr=cosmosvaloper1lzhlnpahvznwfv4jmay2tgaha5kmz5qx4cuznf&dstValidatorAddr=cosmosvaloper1vq8tw77kp8lvxq9u3c8eeln9zymn68rng8pgt4" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "redelegation_responses": [ - { - "redelegation": { - "delegator_address": "cosmos1thfntksw0d35n2tkr0k8v54fr8wxtxwxl2c56e", - "validator_src_address": "cosmosvaloper1lzhlnpahvznwfv4jmay2tgaha5kmz5qx4cuznf", - "validator_dst_address": "cosmosvaloper1vq8tw77kp8lvxq9u3c8eeln9zymn68rng8pgt4", - "entries": null - }, - "entries": [ - { - "redelegation_entry": { - "creation_height": 151523, - "completion_time": "2021-11-09T06:03:25.640682116Z", - "initial_balance": "200000000", - "shares_dst": "200000000.000000000000000000" - }, - "balance": "200000000" - } - ] - } - ], - "pagination": null -} -``` - -### DelegatorUnbondingDelegations - -The `DelegatorUnbondingDelegations` REST endpoint queries all unbonding delegations of a given delegator address. - -```bash -/cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/delegators/cosmos1nxv42u3lv642q0fuzu2qmrku27zgut3n3z7lll/unbonding_delegations" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "unbonding_responses": [ - { - "delegator_address": "cosmos1nxv42u3lv642q0fuzu2qmrku27zgut3n3z7lll", - "validator_address": "cosmosvaloper1e7mvqlz50ch6gw4yjfemsc069wfre4qwmw53kq", - "entries": [ - { - "creation_height": "2442278", - "completion_time": "2021-10-12T10:59:03.797335857Z", - "initial_balance": "50000000000", - "balance": "50000000000" - } - ] - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### DelegatorValidators - -The `DelegatorValidators` REST endpoint queries all validators information for given delegator address. - -```bash -/cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/delegators/cosmos1xwazl8ftks4gn00y5x3c47auquc62ssune9ppv/validators" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "validators": [ - { - "operator_address": "cosmosvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvynw64", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "5v4n3px3PkfNnKflSgepDnsMQR1hiNXnqOC11Y72/PQ=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "21592843799", - "delegator_shares": "21592843799.000000000000000000", - "description": { - "moniker": "jabbey", - "identity": "", - "website": "https://twitter.com/JoeAbbey", - "security_contact": "", - "details": "just another dad in the cosmos" - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.100000000000000000" - }, - "update_time": "2021-10-09T19:03:54.984821705Z" - }, - "min_self_delegation": "1" - } - ], - "pagination": { - "next_key": null, - "total": "1" - } -} -``` - -### DelegatorValidator - -The `DelegatorValidator` REST endpoint queries validator information for given delegator validator pair. - -```bash -/cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators/{validatorAddr} -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/delegators/cosmos1xwazl8ftks4gn00y5x3c47auquc62ssune9ppv/validators/cosmosvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvynw64" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "validator": { - "operator_address": "cosmosvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvynw64", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "5v4n3px3PkfNnKflSgepDnsMQR1hiNXnqOC11Y72/PQ=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "21592843799", - "delegator_shares": "21592843799.000000000000000000", - "description": { - "moniker": "jabbey", - "identity": "", - "website": "https://twitter.com/JoeAbbey", - "security_contact": "", - "details": "just another dad in the cosmos" - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.100000000000000000" - }, - "update_time": "2021-10-09T19:03:54.984821705Z" - }, - "min_self_delegation": "1" - } -} -``` - -### HistoricalInfo - -The `HistoricalInfo` REST endpoint queries the historical information for given height. - -```bash -/cosmos/staking/v1beta1/historical_info/{height} -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/staking/v1beta1/historical_info/153332" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "hist": { - "header": { - "version": { - "block": "11", - "app": "0" - }, - "chain_id": "cosmos-1", - "height": "153332", - "time": "2021-10-12T09:05:35.062230221Z", - "last_block_id": { - "hash": "NX8HevR5khb7H6NGKva+jVz7cyf0skF1CrcY9A0s+d8=", - "part_set_header": { - "total": 1, - "hash": "zLQ2FiKM5tooL3BInt+VVfgzjlBXfq0Hc8Iux/xrhdg=" - } - }, - "last_commit_hash": "P6IJrK8vSqU3dGEyRHnAFocoDGja0bn9euLuy09s350=", - "data_hash": "eUd+6acHWrNXYju8Js449RJ99lOYOs16KpqQl4SMrEM=", - "validators_hash": "mB4pravvMsJKgi+g8aYdSeNlt0kPjnRFyvtAQtaxcfw=", - "next_validators_hash": "mB4pravvMsJKgi+g8aYdSeNlt0kPjnRFyvtAQtaxcfw=", - "consensus_hash": "BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8=", - "app_hash": "fuELArKRK+CptnZ8tu54h6xEleSWenHNmqC84W866fU=", - "last_results_hash": "p/BPexV4LxAzlVcPRvW+lomgXb6Yze8YLIQUo/4Kdgc=", - "evidence_hash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", - "proposer_address": "G0MeY8xQx7ooOsni8KE/3R/Ib3Q=" - }, - "valset": [ - { - "operator_address": "cosmosvaloper196ax4vc0lwpxndu9dyhvca7jhxp70rmcqcnylw", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "/O7BtNW0pafwfvomgR4ZnfldwPXiFfJs9mHg3gwfv5Q=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "1416521659632", - "delegator_shares": "1416521659632.000000000000000000", - "description": { - "moniker": "SG-1", - "identity": "48608633F99D1B60", - "website": "https://sg-1.online", - "security_contact": "", - "details": "SG-1 - your favorite validator on cosmos. We offer 100% Soft Slash protection." - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.037500000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.030000000000000000" - }, - "update_time": "2021-10-01T15:00:00Z" - }, - "min_self_delegation": "1" - }, - { - "operator_address": "cosmosvaloper1t8ehvswxjfn3ejzkjtntcyrqwvmvuknzmvtaaa", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "uExZyjNLtr2+FFIhNDAMcQ8+yTrqE7ygYTsI7khkA5Y=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "1348298958808", - "delegator_shares": "1348298958808.000000000000000000", - "description": { - "moniker": "Cosmostation", - "identity": "AE4C403A6E7AA1AC", - "website": "https://www.cosmostation.io", - "security_contact": "admin@stamper.network", - "details": "Cosmostation validator node. Delegate your tokens and Start Earning Staking Rewards" - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.050000000000000000", - "max_rate": "1.000000000000000000", - "max_change_rate": "0.200000000000000000" - }, - "update_time": "2021-10-01T15:06:38.821314287Z" - }, - "min_self_delegation": "1" - } - ] - } -} -``` - -### Parameters - -The `Parameters` REST endpoint queries the staking parameters. - -```bash -/cosmos/staking/v1beta1/params -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/staking/v1beta1/params" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "params": { - "unbonding_time": "2419200s", - "max_validators": 100, - "max_entries": 7, - "historical_entries": 10000, - "bond_denom": "stake" - } -} -``` - -### Pool - -The `Pool` REST endpoint queries the pool information. - -```bash -/cosmos/staking/v1beta1/pool -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/staking/v1beta1/pool" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "pool": { - "not_bonded_tokens": "432805737458", - "bonded_tokens": "15783637712645" - } -} -``` - -### Validators - -The `Validators` REST endpoint queries all validators that match the given status. - -```bash -/cosmos/staking/v1beta1/validators -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/staking/v1beta1/validators" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "validators": [ - { - "operator_address": "cosmosvaloper1q3jsx9dpfhtyqqgetwpe5tmk8f0ms5qywje8tw", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "N7BPyek2aKuNZ0N/8YsrqSDhGZmgVaYUBuddY8pwKaE=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "383301887799", - "delegator_shares": "383301887799.000000000000000000", - "description": { - "moniker": "SmartNodes", - "identity": "D372724899D1EDC8", - "website": "https://smartnodes.co", - "security_contact": "", - "details": "Earn Rewards with Crypto Staking & Node Deployment" - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.050000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.100000000000000000" - }, - "update_time": "2021-10-01T15:51:31.596618510Z" - }, - "min_self_delegation": "1" - }, - { - "operator_address": "cosmosvaloper1q5ku90atkhktze83j9xjaks2p7uruag5zp6wt7", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "GDNpuKDmCg9GnhnsiU4fCWktuGUemjNfvpCZiqoRIYA=" - }, - "jailed": false, - "status": "BOND_STATUS_UNBONDING", - "tokens": "1017819654", - "delegator_shares": "1017819654.000000000000000000", - "description": { - "moniker": "Noderunners", - "identity": "812E82D12FEA3493", - "website": "http://noderunners.biz", - "security_contact": "info@noderunners.biz", - "details": "Noderunners is a professional validator in POS networks. We have a huge node running experience, reliable soft and hardware. Our commissions are always low, our support to delegators is always full. Stake with us and start receiving your cosmos rewards now!" - }, - "unbonding_height": "147302", - "unbonding_time": "2021-11-08T22:58:53.718662452Z", - "commission": { - "commission_rates": { - "rate": "0.050000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.100000000000000000" - }, - "update_time": "2021-10-04T18:02:21.446645619Z" - }, - "min_self_delegation": "1" - } - ], - "pagination": { - "next_key": "FONDBFkE4tEEf7yxWWKOD49jC2NK", - "total": "2" - } -} -``` - -### Validator - -The `Validator` REST endpoint queries validator information for given validator address. - -```bash -/cosmos/staking/v1beta1/validators/{validatorAddr} -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/validators/cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "validator": { - "operator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "consensus_pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "sIiexdJdYWn27+7iUHQJDnkp63gq/rzUq1Y+fxoGjXc=" - }, - "jailed": false, - "status": "BOND_STATUS_BONDED", - "tokens": "33027900000", - "delegator_shares": "33027900000.000000000000000000", - "description": { - "moniker": "Witval", - "identity": "51468B615127273A", - "website": "", - "security_contact": "", - "details": "Witval is the validator arm from Vitwit. Vitwit is into software consulting and services business since 2015. We are working closely with Cosmos ecosystem since 2018. We are also building tools for the ecosystem, Aneka is our explorer for the cosmos ecosystem." - }, - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z", - "commission": { - "commission_rates": { - "rate": "0.050000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.020000000000000000" - }, - "update_time": "2021-10-01T19:24:52.663191049Z" - }, - "min_self_delegation": "1" - } -} -``` - -### ValidatorDelegations - -The `ValidatorDelegations` REST endpoint queries delegate information for given validator. - -```bash -/cosmos/staking/v1beta1/validators/{validatorAddr}/delegations -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/staking/v1beta1/validators/cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q/delegations" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "delegation_responses": [ - { - "delegation": { - "delegator_address": "cosmos190g5j8aszqhvtg7cprmev8xcxs6csra7xnk3n3", - "validator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "shares": "31000000000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "31000000000" - } - }, - { - "delegation": { - "delegator_address": "cosmos1ddle9tczl87gsvmeva3c48nenyng4n56qwq4ee", - "validator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "shares": "628470000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "628470000" - } - }, - { - "delegation": { - "delegator_address": "cosmos10fdvkczl76m040smd33lh9xn9j0cf26kk4s2nw", - "validator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "shares": "838120000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "838120000" - } - }, - { - "delegation": { - "delegator_address": "cosmos1n8f5fknsv2yt7a8u6nrx30zqy7lu9jfm0t5lq8", - "validator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "shares": "500000000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "500000000" - } - }, - { - "delegation": { - "delegator_address": "cosmos16msryt3fqlxtvsy8u5ay7wv2p8mglfg9hrek2e", - "validator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "shares": "61310000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "61310000" - } - } - ], - "pagination": { - "next_key": null, - "total": "5" - } -} -``` - -### Delegation - -The `Delegation` REST endpoint queries delegate information for given validator delegator pair. - -```bash -/cosmos/staking/v1beta1/validators/{validatorAddr}/delegations/{delegatorAddr} -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/validators/cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q/delegations/cosmos1n8f5fknsv2yt7a8u6nrx30zqy7lu9jfm0t5lq8" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "delegation_response": { - "delegation": { - "delegator_address": "cosmos1n8f5fknsv2yt7a8u6nrx30zqy7lu9jfm0t5lq8", - "validator_address": "cosmosvaloper16msryt3fqlxtvsy8u5ay7wv2p8mglfg9g70e3q", - "shares": "500000000.000000000000000000" - }, - "balance": { - "denom": "stake", - "amount": "500000000" - } - } -} -``` - -### UnbondingDelegation - -The `UnbondingDelegation` REST endpoint queries unbonding information for given validator delegator pair. - -```bash -/cosmos/staking/v1beta1/validators/{validatorAddr}/delegations/{delegatorAddr}/unbonding_delegation -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/validators/cosmosvaloper13v4spsah85ps4vtrw07vzea37gq5la5gktlkeu/delegations/cosmos1ze2ye5u5k3qdlexvt2e0nn0508p04094ya0qpm/unbonding_delegation" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "unbond": { - "delegator_address": "cosmos1ze2ye5u5k3qdlexvt2e0nn0508p04094ya0qpm", - "validator_address": "cosmosvaloper13v4spsah85ps4vtrw07vzea37gq5la5gktlkeu", - "entries": [ - { - "creation_height": "153687", - "completion_time": "2021-11-09T09:41:18.352401903Z", - "initial_balance": "525111", - "balance": "525111" - } - ] - } -} -``` - -### ValidatorUnbondingDelegations - -The `ValidatorUnbondingDelegations` REST endpoint queries unbonding delegations of a validator. - -```bash -/cosmos/staking/v1beta1/validators/{validatorAddr}/unbonding_delegations -``` - -Example: - -```bash -curl -X GET \ -"http://localhost:1317/cosmos/staking/v1beta1/validators/cosmosvaloper13v4spsah85ps4vtrw07vzea37gq5la5gktlkeu/unbonding_delegations" \ --H "accept: application/json" -``` - -Example Output: - -```bash -{ - "unbonding_responses": [ - { - "delegator_address": "cosmos1q9snn84jfrd9ge8t46kdcggpe58dua82vnj7uy", - "validator_address": "cosmosvaloper13v4spsah85ps4vtrw07vzea37gq5la5gktlkeu", - "entries": [ - { - "creation_height": "90998", - "completion_time": "2021-11-05T00:14:37.005841058Z", - "initial_balance": "24000000", - "balance": "24000000" - } - ] - }, - { - "delegator_address": "cosmos1qf36e6wmq9h4twhdvs6pyq9qcaeu7ye0s3dqq2", - "validator_address": "cosmosvaloper13v4spsah85ps4vtrw07vzea37gq5la5gktlkeu", - "entries": [ - { - "creation_height": "47478", - "completion_time": "2021-11-01T22:47:26.714116854Z", - "initial_balance": "8000000", - "balance": "8000000" - } - ] - } - ], - "pagination": { - "next_key": null, - "total": "2" - } -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/staking/README.md b/versioned_docs/version-0.46/integrate/modules/staking/README.md deleted file mode 100644 index 08c21234e..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/README.md +++ /dev/null @@ -1,56 +0,0 @@ - - -# `staking` - -## Abstract - -This paper specifies the Staking module of the Cosmos SDK that was first -described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) -in June 2016. - -The module enables Cosmos SDK-based blockchain to support an advanced -Proof-of-Stake (PoS) system. In this system, holders of the native staking token of -the chain can become validators and can delegate tokens to validators, -ultimately determining the effective validator set for the system. - -This module is used in the Cosmos Hub, the first Hub in the Cosmos -network. - -## Contents - -1. **[State](01_state.md)** - * [Pool](01_state.md#pool) - * [LastTotalPower](01_state.md#lasttotalpower) - * [Params](01_state.md#params) - * [Validator](01_state.md#validator) - * [Delegation](01_state.md#delegation) - * [UnbondingDelegation](01_state.md#unbondingdelegation) - * [Redelegation](01_state.md#redelegation) - * [Queues](01_state.md#queues) - * [HistoricalInfo](01_state.md#historicalinfo) -2. **[State Transitions](02_state_transitions.md)** - * [Validators](02_state_transitions.md#validators) - * [Delegations](02_state_transitions.md#delegations) - * [Slashing](02_state_transitions.md#slashing) -3. **[Messages](03_messages.md)** - * [MsgCreateValidator](03_messages.md#msgcreatevalidator) - * [MsgEditValidator](03_messages.md#msgeditvalidator) - * [MsgDelegate](03_messages.md#msgdelegate) - * [MsgUndelegate](03_messages.md#msgundelegate) - * [MsgCancelUnbondingDelegation](03_messages.md#msgcancelunbondingdelegation) - * [MsgBeginRedelegate](03_messages.md#msgbeginredelegate) -4. **[Begin-Block](04_begin_block.md)** - * [Historical Info Tracking](04_begin_block.md#historical-info-tracking) -5. **[End-Block](05_end_block.md)** - * [Validator Set Changes](05_end_block.md#validator-set-changes) - * [Queues](05_end_block.md#queues-) -6. **[Hooks](06_hooks.md)** -7. **[Events](07_events.md)** - * [EndBlocker](07_events.md#endblocker) - * [Msg's](07_events.md#msg's) -8. **[Parameters](08_params.md)** diff --git a/versioned_docs/version-0.46/integrate/modules/staking/_category_.json b/versioned_docs/version-0.46/integrate/modules/staking/_category_.json deleted file mode 100644 index 9ee26e8d5..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Staking", - "position": 12, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/staking/begin_redelegation_sequence.svg b/versioned_docs/version-0.46/integrate/modules/staking/begin_redelegation_sequence.svg deleted file mode 100644 index 5d17f311b..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/begin_redelegation_sequence.svg +++ /dev/null @@ -1,106 +0,0 @@ -RedelegationmsgServermsgServerkeeperkeeperstorestoreBeginRedelegation(delAddr, valSrcAddr, valDstAddr, sharesAmount)get number of sharewIf the delegator has more shares than the total shares in the validator(due to rounding errors), then just withdraw the max number of shares.check the redelegation uses correct denomalt[valSrcAddr == valDstAddr]erroralt[transitive redelegation]erroralt[already has max redelegations]errorthis is the number of redelegations for a specific (del, valSrc, valDst) tripledefault : 7Unbond(del, valSrc) returns returnAmountSee unbonding diagramalt[returnAmount is zero]errorDelegate(del, returnAmount, status := valSrc.status, valDst, subtractAccount := false)See delegation diagramalt[validator is unbonded]current timealt[unbonding not complete, or just started]create redelegation objectinsert redelegation in queue, to be processed at the appropriate timecompletion time of the redelegationemit event: delegator, valSrc, valSrc,sharesAmount, completionTime \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/staking/delegation_sequence.svg b/versioned_docs/version-0.46/integrate/modules/staking/delegation_sequence.svg deleted file mode 100644 index 47ad53789..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/delegation_sequence.svg +++ /dev/null @@ -1,192 +0,0 @@ -Delegating (currently undelegated funds delegator)msgServer (staking)msgServer (staking)keeper (staking)keeper (staking)validatorvalidatorkeeper.bankKeeperkeeper.bankKeepervestingAccountvestingAccountctx.EventManagerctx.EventManagerstorestoreDelegate(Context, DelegatorAddress, Amount, Validator, tokenSrc := Unbonded)alt[exchange rate is invalid (tokens in validator is 0)]erroralt[perform a new delegation]delegation := create delegation objectBeforeDelegationCreated hookCalls IncrementValidatorPeriod (Used to calculate distribution) in keeper/validator.go[delegation exists, more tokens being added]BeforeDelegationModified hookwithdraw current delegation rewards (and increment period)alt[delegating from an account (subtractTokens == true)]DelegateCoinsFromAccountToModuleDelegateCoinsFromAccountToModule functionDelegateCoinsFromAccountToModuleDelegateCoinsDelegateCoins functionCheck the delegator has enough balances of all tokens delegatedTrack delegation (register that it exists to keep track of it)alt[validator is currently bonded]Transfer tokens from delegator to BondedTokensPool.[validator is currently unbonded or unbonding]Transfer tokens from delegator to NotBondedTokensPool.trackDelegation functiontrackDelegationalt[delegator is a vesting account]keep track of this delegationnil (success)[moving tokens between pools (subtractTokens == false)]alt[delegator tokens are not bonded but validator is bonded]SendCoinsFromModuleToModule(notBondedPool, bondedPool, coins)[delegator tokens are bonded but validator is not bonded]SendCoinsFromModuleToModule(bondedPool, notBondedPool, coins)SendCoins functionSendCoinsEmit TransferEvent(to, from, amount)alt[amount of spendable (balance - locked) coins too low]errorsubtract balance from senderadd balance to recipientAddTokensFromDelcalculate number of shares to issueIf there are no shares (validator being created) then 1 token = 1 share.If there are already shares, thenadded shares = (added tokens amount) * (current validator shares) / (current validator tokens)add delegated tokens to validatorvalidator, addedSharesupdate validator statecalculate new validator's powerNumber of tokens divided by PowerReduction (default: 1,000,000,000,000,000,000 = 10^18)alt[validator is not jailed]update validator's power in power indexthe power index has entries shaped as 35 || power || address.This makes the validators sorted by power, high to low.AfterDelegationModified hookCalls initializeDelegationStore the previous periodCalculate the number of tokens from shares(shares the delegator has) * (tokens in delegation object)/(total tokens delegated to the validator)Store delegation starting info.newShares (ignored by Delegate function)Emit event: Delegation(ValidatorAddress)Emit event: Message(DelegatorAddress)telemetry(Amount, Denom) \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/staking/unbond_sequence.svg b/versioned_docs/version-0.46/integrate/modules/staking/unbond_sequence.svg deleted file mode 100644 index 7219f200a..000000000 --- a/versioned_docs/version-0.46/integrate/modules/staking/unbond_sequence.svg +++ /dev/null @@ -1,110 +0,0 @@ -UndelegatemsgServermsgServerkeeperkeeperstorestorebankKeeperbankKeeperUndelegate(delAddr, valAddr, tokenAmount)calculate number of shares the tokenAmount representsalt[wrong denom]errorUnbond(delAddr, valAddr, shares)BeforeDelegationSharesModified hookalt[no such delegation]erroralt[not enough shares]erroralt[delegator is the operator of the validatorand validator is not already jailedand unbonding would put self-delegation under min threshold]jail the validator, but proceed with unbondingDefault min delegation threshold : 1 sharealt[complete unbonding, all shares removed]remove delegation object[there are still shares delegated (not a complete undbonding)]update delegation objectAfterDelegationModified hookupdate validator power indexupdate validator information (including token amount)alt[validator status is "unbonded" and it has no more tokens]delete the validatorotherwise, do this in EndBlock once validator is unbondedalt[validator is bonded]send tokens from bonded pool to not bonded poolemit event : EventTypeUnbond(delAddr, valAddr, tokenAmount, completion time) \ No newline at end of file diff --git a/versioned_docs/version-0.46/integrate/modules/upgrade/01_concepts.md b/versioned_docs/version-0.46/integrate/modules/upgrade/01_concepts.md deleted file mode 100644 index 10ff5ad36..000000000 --- a/versioned_docs/version-0.46/integrate/modules/upgrade/01_concepts.md +++ /dev/null @@ -1,104 +0,0 @@ - - -# Concepts - -## Plan - -The `x/upgrade` module defines a `Plan` type in which a live upgrade is scheduled -to occur. A `Plan` can be scheduled at a specific block height. -A `Plan` is created once a (frozen) release candidate along with an appropriate upgrade -`Handler` (see below) is agreed upon, where the `Name` of a `Plan` corresponds to a -specific `Handler`. Typically, a `Plan` is created through a governance proposal -process, where if voted upon and passed, will be scheduled. The `Info` of a `Plan` -may contain various metadata about the upgrade, typically application specific -upgrade info to be included on-chain such as a git commit that validators could -automatically upgrade to. - -### Sidecar Process - -If an operator running the application binary also runs a sidecar process to assist -in the automatic download and upgrade of a binary, the `Info` allows this process to -be seamless. Namely, the `x/upgrade` module fulfills the -[cosmosd Upgradeable Binary Specification](https://github.com/regen-network/cosmosd#upgradeable-binary-specification) -specification and `cosmosd` can optionally be used to fully automate the upgrade -process for node operators. By populating the `Info` field with the necessary information, -binaries can automatically be downloaded. See [here](https://github.com/regen-network/cosmosd#auto-download) -for more info. - -```go -type Plan struct { - Name string - Height int64 - Info string -} -``` - -## Handler - -The `x/upgrade` module facilitates upgrading from major version X to major version Y. To -accomplish this, node operators must first upgrade their current binary to a new -binary that has a corresponding `Handler` for the new version Y. It is assumed that -this version has fully been tested and approved by the community at large. This -`Handler` defines what state migrations need to occur before the new binary Y -can successfully run the chain. Naturally, this `Handler` is application specific -and not defined on a per-module basis. Registering a `Handler` is done via -`Keeper#SetUpgradeHandler` in the application. - -```go -type UpgradeHandler func(Context, Plan, VersionMap) (VersionMap, error) -``` - -During each `EndBlock` execution, the `x/upgrade` module checks if there exists a -`Plan` that should execute (is scheduled at that height). If so, the corresponding -`Handler` is executed. If the `Plan` is expected to execute but no `Handler` is registered -or if the binary was upgraded too early, the node will gracefully panic and exit. - -## StoreLoader - -The `x/upgrade` module also facilitates store migrations as part of the upgrade. The -`StoreLoader` sets the migrations that need to occur before the new binary can -successfully run the chain. This `StoreLoader` is also application specific and -not defined on a per-module basis. Registering this `StoreLoader` is done via -`app#SetStoreLoader` in the application. - -```go -func UpgradeStoreLoader (upgradeHeight int64, storeUpgrades *store.StoreUpgrades) baseapp.StoreLoader -``` - -If there's a planned upgrade and the upgrade height is reached, the old binary writes `Plan` to the disk before panicking. - -This information is critical to ensure the `StoreUpgrades` happens smoothly at correct height and -expected upgrade. It eliminiates the chances for the new binary to execute `StoreUpgrades` multiple -times everytime on restart. Also if there are multiple upgrades planned on same height, the `Name` -will ensure these `StoreUpgrades` takes place only in planned upgrade handler. - -## Proposal - -Typically, a `Plan` is proposed and submitted through governance via a proposal -containing a `MsgSoftwareUpgrade` message. -This proposal prescribes to the standard governance process. If the proposal passes, -the `Plan`, which targets a specific `Handler`, is persisted and scheduled. The -upgrade can be delayed or hastened by updating the `Plan.Height` in a new proposal. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/upgrade/v1beta1/tx.proto#L24-L36 - -### Cancelling Upgrade Proposals - -Upgrade proposals can be cancelled. There exists a gov-enabled `MsgCancelUpgrade` -message type, which can be embedded in a proposal, voted on and, if passed, will -remove the scheduled upgrade `Plan`. -Of course this requires that the upgrade was known to be a bad idea well before the -upgrade itself, to allow time for a vote. - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/proto/cosmos/upgrade/v1beta1/tx.proto#L43-L51 - -If such a possibility is desired, the upgrade height is to be -`2 * (VotingPeriod + DepositPeriod) + (SafetyDelta)` from the beginning of the -upgrade proposal. The `SafetyDelta` is the time available from the success of an -upgrade proposal and the realization it was a bad idea (due to external social consensus). - -A `MsgCancelUpgrade` proposal can also be made while the original -`MsgSoftwareUpgrade` proposal is still being voted upon, as long as the `VotingPeriod` -ends after the `MsgSoftwareUpgrade` proposal. diff --git a/versioned_docs/version-0.46/integrate/modules/upgrade/02_state.md b/versioned_docs/version-0.46/integrate/modules/upgrade/02_state.md deleted file mode 100644 index 7508b2929..000000000 --- a/versioned_docs/version-0.46/integrate/modules/upgrade/02_state.md +++ /dev/null @@ -1,20 +0,0 @@ - - -# State - -The internal state of the `x/upgrade` module is relatively minimal and simple. The -state contains the currently active upgrade `Plan` (if one exists) by key -`0x0` and if a `Plan` is marked as "done" by key `0x1`. The state -contains the consensus versions of all app modules in the application. The versions -are stored as big endian `uint64`, and can be accessed with prefix `0x2` appended -by the corresponding module name of type `string`. The state maintains a -`Protocol Version` which can be accessed by key `0x3`. - -* Plan: `0x0 -> Plan` -* Done: `0x1 | byte(plan name) -> BigEndian(Block Height)` -* ConsensusVersion: `0x2 | byte(module name) -> BigEndian(Module Consensus Version)` -* ProtocolVersion: `0x3 -> BigEndian(Protocol Version)` - -The `x/upgrade` module contains no genesis state. diff --git a/versioned_docs/version-0.46/integrate/modules/upgrade/03_events.md b/versioned_docs/version-0.46/integrate/modules/upgrade/03_events.md deleted file mode 100644 index e4e0e6d5f..000000000 --- a/versioned_docs/version-0.46/integrate/modules/upgrade/03_events.md +++ /dev/null @@ -1,8 +0,0 @@ - - -# Events - -The `x/upgrade` does not emit any events by itself. Any and all proposal related -events are emitted through the `x/gov` module. diff --git a/versioned_docs/version-0.46/integrate/modules/upgrade/04_client.md b/versioned_docs/version-0.46/integrate/modules/upgrade/04_client.md deleted file mode 100644 index da55709ee..000000000 --- a/versioned_docs/version-0.46/integrate/modules/upgrade/04_client.md +++ /dev/null @@ -1,459 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `upgrade` module using the CLI. - -### Query - -The `query` commands allow users to query `upgrade` state. - -```bash -simd query upgrade --help -``` - -#### applied - -The `applied` command allows users to query the block header for height at which a completed upgrade was applied. - -```bash -simd query upgrade applied [upgrade-name] [flags] -``` - -If upgrade-name was previously executed on the chain, this returns the header for the block at which it was applied. -This helps a client determine which binary was valid over a given range of blocks, as well as more context to understand past migrations. - -Example: - -```bash -simd query upgrade applied "test-upgrade" -``` - -Example Output: - -```bash -"block_id": { - "hash": "A769136351786B9034A5F196DC53F7E50FCEB53B48FA0786E1BFC45A0BB646B5", - "parts": { - "total": 1, - "hash": "B13CBD23011C7480E6F11BE4594EE316548648E6A666B3575409F8F16EC6939E" - } - }, - "block_size": "7213", - "header": { - "version": { - "block": "11" - }, - "chain_id": "testnet-2", - "height": "455200", - "time": "2021-04-10T04:37:57.085493838Z", - "last_block_id": { - "hash": "0E8AD9309C2DC411DF98217AF59E044A0E1CCEAE7C0338417A70338DF50F4783", - "parts": { - "total": 1, - "hash": "8FE572A48CD10BC2CBB02653CA04CA247A0F6830FF19DC972F64D339A355E77D" - } - }, - "last_commit_hash": "DE890239416A19E6164C2076B837CC1D7F7822FC214F305616725F11D2533140", - "data_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", - "validators_hash": "A31047ADE54AE9072EE2A12FF260A8990BA4C39F903EAF5636B50D58DBA72582", - "next_validators_hash": "A31047ADE54AE9072EE2A12FF260A8990BA4C39F903EAF5636B50D58DBA72582", - "consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F", - "app_hash": "28ECC486AFC332BA6CC976706DBDE87E7D32441375E3F10FD084CD4BAF0DA021", - "last_results_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", - "evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", - "proposer_address": "2ABC4854B1A1C5AA8403C4EA853A81ACA901CC76" - }, - "num_txs": "0" -} -``` - -#### module versions - -The `module_versions` command gets a list of module names and their respective consensus versions. - -Following the command with a specific module name will return only -that module's information. - -```bash -simd query upgrade module_versions [optional module_name] [flags] -``` - -Example: - -```bash -simd query upgrade module_versions -``` - -Example Output: - -```bash -module_versions: -- name: auth - version: "2" -- name: authz - version: "1" -- name: bank - version: "2" -- name: capability - version: "1" -- name: crisis - version: "1" -- name: distribution - version: "2" -- name: evidence - version: "1" -- name: feegrant - version: "1" -- name: genutil - version: "1" -- name: gov - version: "2" -- name: ibc - version: "2" -- name: mint - version: "1" -- name: params - version: "1" -- name: slashing - version: "2" -- name: staking - version: "2" -- name: transfer - version: "1" -- name: upgrade - version: "1" -- name: vesting - version: "1" -``` - -Example: - -```bash -regen query upgrade module_versions ibc -``` - -Example Output: - -```bash -module_versions: -- name: ibc - version: "2" -``` - -#### plan - -The `plan` command gets the currently scheduled upgrade plan, if one exists. - -```bash -regen query upgrade plan [flags] -``` - -Example: - -```bash -simd query upgrade plan -``` - -Example Output: - -```bash -height: "130" -info: "" -name: test-upgrade -time: "0001-01-01T00:00:00Z" -upgraded_client_state: null -``` - -## REST - -A user can query the `upgrade` module using REST endpoints. - -### Applied Plan - -`AppliedPlan` queries a previously applied upgrade plan by its name. - -```bash -/cosmos/upgrade/v1beta1/applied_plan/{name} -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/upgrade/v1beta1/applied_plan/v2.0-upgrade" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "height": "30" -} -``` - -### Current Plan - -`CurrentPlan` queries the current upgrade plan. - -```bash -/cosmos/upgrade/v1beta1/current_plan -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/upgrade/v1beta1/current_plan" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "plan": "v2.1-upgrade" -} -``` - -### Module versions - -`ModuleVersions` queries the list of module versions from state. - -```bash -/cosmos/upgrade/v1beta1/module_versions -``` - -Example: - -```bash -curl -X GET "http://localhost:1317/cosmos/upgrade/v1beta1/module_versions" -H "accept: application/json" -``` - -Example Output: - -```bash -{ - "module_versions": [ - { - "name": "auth", - "version": "2" - }, - { - "name": "authz", - "version": "1" - }, - { - "name": "bank", - "version": "2" - }, - { - "name": "capability", - "version": "1" - }, - { - "name": "crisis", - "version": "1" - }, - { - "name": "distribution", - "version": "2" - }, - { - "name": "evidence", - "version": "1" - }, - { - "name": "feegrant", - "version": "1" - }, - { - "name": "genutil", - "version": "1" - }, - { - "name": "gov", - "version": "2" - }, - { - "name": "ibc", - "version": "2" - }, - { - "name": "mint", - "version": "1" - }, - { - "name": "params", - "version": "1" - }, - { - "name": "slashing", - "version": "2" - }, - { - "name": "staking", - "version": "2" - }, - { - "name": "transfer", - "version": "1" - }, - { - "name": "upgrade", - "version": "1" - }, - { - "name": "vesting", - "version": "1" - } - ] -} -``` - -## gRPC - -A user can query the `upgrade` module using gRPC endpoints. - -### Applied Plan - -`AppliedPlan` queries a previously applied upgrade plan by its name. - -```bash -cosmos.upgrade.v1beta1.Query/AppliedPlan -``` - -Example: - -```bash -grpcurl -plaintext \ - -d '{"name":"v2.0-upgrade"}' \ - localhost:9090 \ - cosmos.upgrade.v1beta1.Query/AppliedPlan -``` - -Example Output: - -```bash -{ - "height": "30" -} -``` - -### Current Plan - -`CurrentPlan` queries the current upgrade plan. - -```bash -cosmos.upgrade.v1beta1.Query/CurrentPlan -``` - -Example: - -```bash -grpcurl -plaintext localhost:9090 cosmos.slashing.v1beta1.Query/CurrentPlan -``` - -Example Output: - -```bash -{ - "plan": "v2.1-upgrade" -} -``` - -### Module versions - -`ModuleVersions` queries the list of module versions from state. - -```bash -cosmos.upgrade.v1beta1.Query/ModuleVersions -``` - -Example: - -```bash -grpcurl -plaintext localhost:9090 cosmos.slashing.v1beta1.Query/ModuleVersions -``` - -Example Output: - -```bash -{ - "module_versions": [ - { - "name": "auth", - "version": "2" - }, - { - "name": "authz", - "version": "1" - }, - { - "name": "bank", - "version": "2" - }, - { - "name": "capability", - "version": "1" - }, - { - "name": "crisis", - "version": "1" - }, - { - "name": "distribution", - "version": "2" - }, - { - "name": "evidence", - "version": "1" - }, - { - "name": "feegrant", - "version": "1" - }, - { - "name": "genutil", - "version": "1" - }, - { - "name": "gov", - "version": "2" - }, - { - "name": "ibc", - "version": "2" - }, - { - "name": "mint", - "version": "1" - }, - { - "name": "params", - "version": "1" - }, - { - "name": "slashing", - "version": "2" - }, - { - "name": "staking", - "version": "2" - }, - { - "name": "transfer", - "version": "1" - }, - { - "name": "upgrade", - "version": "1" - }, - { - "name": "vesting", - "version": "1" - } - ] -} -``` diff --git a/versioned_docs/version-0.46/integrate/modules/upgrade/README.md b/versioned_docs/version-0.46/integrate/modules/upgrade/README.md deleted file mode 100644 index 73ea2268a..000000000 --- a/versioned_docs/version-0.46/integrate/modules/upgrade/README.md +++ /dev/null @@ -1,31 +0,0 @@ - - -# `upgrade` - -## Abstract - -`x/upgrade` is an implementation of a Cosmos SDK module that facilitates smoothly -upgrading a live Cosmos chain to a new (breaking) software version. It accomplishes this by -providing a `BeginBlocker` hook that prevents the blockchain state machine from -proceeding once a pre-defined upgrade block height has been reached. - -The module does not prescribe anything regarding how governance decides to do an -upgrade, but just the mechanism for coordinating the upgrade safely. Without software -support for upgrades, upgrading a live chain is risky because all of the validators -need to pause their state machines at exactly the same point in the process. If -this is not done correctly, there can be state inconsistencies which are hard to -recover from. - - -1. **[Concepts](01_concepts.md)** -2. **[State](02_state.md)** -3. **[Events](03_events.md)** -4. **[Client](04_client.md)** - * [CLI](04_client.md#cli) - * [REST](04_client.md#rest) - * [gRPC](04_client.md#grpc) diff --git a/versioned_docs/version-0.46/integrate/modules/upgrade/_category_.json b/versioned_docs/version-0.46/integrate/modules/upgrade/_category_.json deleted file mode 100644 index afd841085..000000000 --- a/versioned_docs/version-0.46/integrate/modules/upgrade/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Upgrade", - "position": 13, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/user/_category_.json b/versioned_docs/version-0.46/user/_category_.json deleted file mode 100644 index 9b291ba6f..000000000 --- a/versioned_docs/version-0.46/user/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "User Guides", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versioned_docs/version-0.46/user/run-node/00-keyring.md b/versioned_docs/version-0.46/user/run-node/00-keyring.md deleted file mode 100644 index 38e9b1a02..000000000 --- a/versioned_docs/version-0.46/user/run-node/00-keyring.md +++ /dev/null @@ -1,132 +0,0 @@ -# Setting up the keyring - -This document describes how to configure and use the keyring and its various backends for an [**application**](../high-level-concepts/app-anatomy.md). {synopsis} - -The keyring holds the private/public keypairs used to interact with a node. For instance, a validator key needs to be set up before running the blockchain node, so that blocks can be correctly signed. The private key can be stored in different locations, called "backends", such as a file or the operating system's own key storage. - -## Available backends for the keyring - -Starting with the v0.38.0 release, Cosmos SDK comes with a new keyring implementation -that provides a set of commands to manage cryptographic keys in a secure fashion. The -new keyring supports multiple storage backends, some of which may not be available on -all operating systems. - -### The `os` backend - -The `os` backend relies on operating system-specific defaults to handle key storage -securely. Typically, an operating system's credential sub-system handles password prompts, -private keys storage, and user sessions according to the user's password policies. Here -is a list of the most popular operating systems and their respective passwords manager: - -* macOS (since Mac OS 8.6): [Keychain](https://support.apple.com/en-gb/guide/keychain-access/welcome/mac) -* Windows: [Credentials Management API](https://docs.microsoft.com/en-us/windows/win32/secauthn/credentials-management) -* GNU/Linux: - * [libsecret](https://gitlab.gnome.org/GNOME/libsecret) - * [kwallet](https://api.kde.org/frameworks/kwallet/html/index.html) - -GNU/Linux distributions that use GNOME as default desktop environment typically come with -[Seahorse](https://wiki.gnome.org/Apps/Seahorse). Users of KDE based distributions are -commonly provided with [KDE Wallet Manager](https://userbase.kde.org/KDE_Wallet_Manager). -Whilst the former is in fact a `libsecret` convenient frontend, the latter is a `kwallet` -client. - -`os` is the default option since operating system's default credentials managers are -designed to meet users' most common needs and provide them with a comfortable -experience without compromising on security. - -The recommended backends for headless environments are `file` and `pass`. - -### The `file` backend - -The `file` backend more closely resembles the keybase implementation used prior to -v0.38.1. It stores the keyring encrypted within the app's configuration directory. This -keyring will request a password each time it is accessed, which may occur multiple -times in a single command resulting in repeated password prompts. If using bash scripts -to execute commands using the `file` option you may want to utilize the following format -for multiple prompts: - -```sh -# assuming that KEYPASSWD is set in the environment -$ gaiacli config keyring-backend file # use file backend -$ (echo $KEYPASSWD; echo $KEYPASSWD) | gaiacli keys add me # multiple prompts -$ echo $KEYPASSWD | gaiacli keys show me # single prompt -``` - -::: tip -The first time you add a key to an empty keyring, you will be prompted to type the password twice. -::: - -### The `pass` backend - -The `pass` backend uses the [pass](https://www.passwordstore.org/) utility to manage on-disk -encryption of keys' sensitive data and metadata. Keys are stored inside `gpg` encrypted files -within app-specific directories. `pass` is available for the most popular UNIX -operating systems as well as GNU/Linux distributions. Please refer to its manual page for -information on how to download and install it. - -::: tip -**pass** uses [GnuPG](https://gnupg.org/) for encryption. `gpg` automatically invokes the `gpg-agent` -daemon upon execution, which handles the caching of GnuPG credentials. Please refer to `gpg-agent` -man page for more information on how to configure cache parameters such as credentials TTL and -passphrase expiration. -::: - -The password store must be set up prior to first use: - -```sh -pass init -``` - -Replace `` with your GPG key ID. You can use your personal GPG key or an alternative -one you may want to use specifically to encrypt the password store. - -### The `kwallet` backend - -The `kwallet` backend uses `KDE Wallet Manager`, which comes installed by default on the -GNU/Linux distributions that ships KDE as default desktop environment. Please refer to -[KWallet Handbook](https://docs.kde.org/stable5/en/kdeutils/kwallet5/index.html) for more -information. - -### The `test` backend - -The `test` backend is a password-less variation of the `file` backend. Keys are stored -unencrypted on disk. - -**Provided for testing purposes only. The `test` backend is not recommended for use in production environments**. - -### The `memory` backend - -The `memory` backend stores keys in memory. The keys are immediately deleted after the program has exited. - -**Provided for testing purposes only. The `memory` backend is not recommended for use in production environments**. - -## Adding keys to the keyring - -::: warning -Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets. -::: - -Applications developed using the Cosmos SDK come with the `keys` subcommand. For the purpose of this tutorial, we're running the `simd` CLI, which is an application built using the Cosmos SDK for testing and educational purposes. For more information, see [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/main/simapp). - -You can use `simd keys` for help about the keys command and `simd keys [command] --help` for more information about a particular subcommand. - -::: tip -You can also enable auto-completion with the `simd completion` command. For example, at the start of a bash session, run `. <(simd completion)`, and all `simd` subcommands will be auto-completed. -::: - -To create a new key in the keyring, run the `add` subcommand with a `` argument. For the purpose of this tutorial, we will solely use the `test` backend, and call our new key `my_validator`. This key will be used in the next section. - -```bash -$ simd keys add my_validator --keyring-backend test - -# Put the generated address in a variable for later use. -MY_VALIDATOR_ADDRESS=$(simd keys show my_validator -a --keyring-backend test) -``` - -This command generates a new 24-word mnemonic phrase, persists it to the relevant backend, and outputs information about the keypair. If this keypair will be used to hold value-bearing tokens, be sure to write down the mnemonic phrase somewhere safe! - -By default, the keyring generates a `secp256k1` keypair. The keyring also supports `ed25519` keys, which may be created by passing the `--algo ed25519` flag. A keyring can of course hold both types of keys simultaneously, and the Cosmos SDK's `x/auth` module (in particular its [middlewares](../../develop/advanced-concepts/00-baseapp.md#middleware)) supports natively these two public key algorithms. - -## Next {hide} - -Read about [running a node](./run-node.md) {hide} diff --git a/versioned_docs/version-0.46/user/run-node/01-run-node.md b/versioned_docs/version-0.46/user/run-node/01-run-node.md deleted file mode 100644 index e396eb609..000000000 --- a/versioned_docs/version-0.46/user/run-node/01-run-node.md +++ /dev/null @@ -1,163 +0,0 @@ -# Running a Node - -Now that the application is ready and the keyring populated, it's time to see how to run the blockchain node. In this section, the application we are running is called [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/main/simapp), and its corresponding CLI binary `simd`. {synopsis} - -## Pre-requisite Readings - -* [Anatomy of a Cosmos SDK Application](../high-level-concepts/app-anatomy.md) {prereq} -* [Setting up the keyring](./keyring.md) {prereq} - -## Initialize the Chain - -::: warning -Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets. -::: - -Before actually running the node, we need to initialize the chain, and most importantly its genesis file. This is done with the `init` subcommand: - -```bash -# The argument is the custom username of your node, it should be human-readable. -simd init --chain-id my-test-chain -``` - -The command above creates all the configuration files needed for your node to run, as well as a default genesis file, which defines the initial state of the network. All these configuration files are in `~/.simapp` by default, but you can overwrite the location of this folder by passing the `--home` flag. - -The `~/.simapp` folder has the following structure: - -```bash -. # ~/.simapp - |- data # Contains the databases used by the node. - |- config/ - |- app.toml # Application-related configuration file. - |- config.toml # Tendermint-related configuration file. - |- genesis.json # The genesis file. - |- node_key.json # Private key to use for node authentication in the p2p protocol. - |- priv_validator_key.json # Private key to use as a validator in the consensus protocol. -``` - -## Updating Some Default Settings - -If you want to change any field values in configuration files (for ex: genesis.json) you can use `jq` ([installation](https://stedolan.github.io/jq/download/) & [docs](https://stedolan.github.io/jq/manual/#Assignment)) & `sed` commands to do that. Few examples are listed here. - -```bash -# to change the chain-id -jq '.chain_id = "testing"' genesis.json > temp.json && mv temp.json genesis.json - -# to enable the api server -sed -i '/\[api\]/,+3 s/enable = false/enable = true/' app.toml - -# to change the voting_period -jq '.app_state.gov.voting_params.voting_period = "600s"' genesis.json > temp.json && mv temp.json genesis.json - -# to change the inflation -jq '.app_state.mint.minter.inflation = "0.300000000000000000"' genesis.json > temp.json && mv temp.json genesis.json -``` - -## Adding Genesis Accounts - -Before starting the chain, you need to populate the state with at least one account. To do so, first [create a new account in the keyring](./keyring.md#adding-keys-to-the-keyring) named `my_validator` under the `test` keyring backend (feel free to choose another name and another backend). - -Now that you have created a local account, go ahead and grant it some `stake` tokens in your chain's genesis file. Doing so will also make sure your chain is aware of this account's existence: - -```bash -simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000000stake -``` - -Recall that `$MY_VALIDATOR_ADDRESS` is a variable that holds the address of the `my_validator` key in the [keyring](./keyring.md#adding-keys-to-the-keyring). Also note that the tokens in the Cosmos SDK have the `{amount}{denom}` format: `amount` is is a 18-digit-precision decimal number, and `denom` is the unique token identifier with its denomination key (e.g. `atom` or `uatom`). Here, we are granting `stake` tokens, as `stake` is the token identifier used for staking in [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/main/simapp). For your own chain with its own staking denom, that token identifier should be used instead. - -Now that your account has some tokens, you need to add a validator to your chain. Validators are special full-nodes that participate in the consensus process (implemented in the [underlying consensus engine](../intro/sdk-app-architecture.md#tendermint)) in order to add new blocks to the chain. Any account can declare its intention to become a validator operator, but only those with sufficient delegation get to enter the active set (for example, only the top 125 validator candidates with the most delegation get to be validators in the Cosmos Hub). For this guide, you will add your local node (created via the `init` command above) as a validator of your chain. Validators can be declared before a chain is first started via a special transaction included in the genesis file called a `gentx`: - -```bash -# Create a gentx. -simd gentx my_validator 100000000stake --chain-id my-test-chain --keyring-backend test - -# Add the gentx to the genesis file. -simd collect-gentxs -``` - -A `gentx` does three things: - -1. Registers the `validator` account you created as a validator operator account (i.e. the account that controls the validator). -2. Self-delegates the provided `amount` of staking tokens. -3. Link the operator account with a Tendermint node pubkey that will be used for signing blocks. If no `--pubkey` flag is provided, it defaults to the local node pubkey created via the `simd init` command above. - -For more information on `gentx`, use the following command: - -```bash -simd gentx --help -``` - -## Configuring the Node Using `app.toml` and `config.toml` - -The Cosmos SDK automatically generates two configuration files inside `~/.simapp/config`: - -* `config.toml`: used to configure the Tendermint, learn more on [Tendermint's documentation](https://docs.tendermint.com/master/nodes/configuration.html), -* `app.toml`: generated by the Cosmos SDK, and used to configure your app, such as state pruning strategies, telemetry, gRPC and REST servers configuration, state sync... - -Both files are heavily commented, please refer to them directly to tweak your node. - -One example config to tweak is the `minimum-gas-prices` field inside `app.toml`, which defines the minimum gas prices the validator node is willing to accept for processing a transaction. Depending on the chain, it might be an empty string or not. If it's empty, make sure to edit the field with some value, for example `10token`, or else the node will halt on startup. For the purpose of this tutorial, let's set the minimum gas price to 0: - -```toml - # The minimum gas prices a validator is willing to accept for processing a - # transaction. A transaction's fees must meet the minimum of any denomination - # specified in this config (e.g. 0.25token1;0.0001token2). - minimum-gas-prices = "0stake" -``` - -## Run a Localnet - -Now that everything is set up, you can finally start your node: - -```bash -simd start -``` - -> Note: By default nodes are run in full node mode. Running a local network means in most cases, the node is the only node in the network, requiring you to set the mode. - -You should see blocks come in. - -The previous command allow you to run a single node. This is enough for the next section on interacting with this node, but you may wish to run multiple nodes at the same time, and see how consensus happens between them. - -The naive way would be to run the same commands again in separate terminal windows. This is possible, however in the Cosmos SDK, we leverage the power of [Docker Compose](https://docs.docker.com/compose/) to run a localnet. If you need inspiration on how to set up your own localnet with Docker Compose, you can have a look at the Cosmos SDK's [`docker-compose.yml`](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/docker-compose.yml). - -## State Sync - -State sync is the act in which a node syncs the latest or close to the latest state of a blockchain. This is useful for users who don't want to sync all the blocks in history. Read more in [CometBFT documentation](https://docs.cometbft.com/v0.34/01-tx-lifecycle.mdstate-sync). - -State sync works thanks to snapshots. Read how the SDK handles snapshots [here](https://github.com/cosmos/cosmos-sdk/blob/825245d/store/snapshots/README.md). - -### Local State Sync - -Local state sync work similar to normal state sync except that it works off a local snapshot of state instead of one provided via the p2p network. The steps to start local state sync are similar to normal state sync with a few different designs. - -1. As mentioned in https://docs.cometbft.com/v0.34/01-tx-lifecycle.mdstate-sync, one must set a height and hash in the config.toml along with a few rpc servers (the afromentioned link has instructions on how to do this). -2. Run ` ` to restore a local snapshot (note: first load it from a file with the *load* command). -3. Bootsrapping Comet state in order to start the node after the snapshot has been ingested. This can be done with the bootstrap command ` tendermint bootstrap-state` - -### Snapshots Commands - -The Cosmos SDK provides commands for managing snapshots. -These commands can be added in an app with the following snippet in `cmd//root.go`: - -```go -import ( - "github.com/cosmos/cosmos-sdk/client/snapshot" -) - -func initRootCmd(/* ... */) { - // ... - rootCmd.AddCommand( - snapshot.Cmd(appCreator), - ) -} -``` - -Then following commands are available at ` snapshots [command]`: - -* **list**: list local snapshots -* **load**: Load a snapshot archive file into snapshot store -* **restore**: Restore app state from local snapshot -* **export**: Export app state to snapshot store -* **dump**: Dump the snapshot as portable archive format -* **delete**: Delete a local snapshot diff --git a/versioned_docs/version-0.46/user/run-node/02-interact-node.md b/versioned_docs/version-0.46/user/run-node/02-interact-node.md deleted file mode 100644 index c37bfa96a..000000000 --- a/versioned_docs/version-0.46/user/run-node/02-interact-node.md +++ /dev/null @@ -1,248 +0,0 @@ -# Interacting with the Node - -There are multiple ways to interact with a node: using the CLI, using gRPC or using the REST endpoints. {synopsis} - -## Pre-requisite Readings - -* [gRPC, REST and Tendermint Endpoints](../01-tx-lifecycle.md08-grpc_rest.md) {prereq} -* [Running a Node](./run-node.md) {prereq} - -## Using the CLI - -Now that your chain is running, it is time to try sending tokens from the first account you created to a second account. In a new terminal window, start by running the following query command: - -```bash -simd query bank balances $MY_VALIDATOR_ADDRESS --chain-id my-test-chain -``` - -You should see the current balance of the account you created, equal to the original balance of `stake` you granted it minus the amount you delegated via the `gentx`. Now, create a second account: - -```bash -simd keys add recipient --keyring-backend test - -# Put the generated address in a variable for later use. -RECIPIENT=$(simd keys show recipient -a --keyring-backend test) -``` - -The command above creates a local key-pair that is not yet registered on the chain. An account is created the first time it receives tokens from another account. Now, run the following command to send tokens to the `recipient` account: - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000000stake --chain-id my-test-chain --keyring-backend test - -# Check that the recipient account did receive the tokens. -simd query bank balances $RECIPIENT --chain-id my-test-chain -``` - -Finally, delegate some of the stake tokens sent to the `recipient` account to the validator: - -```bash -simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain --keyring-backend test - -# Query the total delegations to `validator`. -simd query staking delegations-to $(simd keys show my_validator --bech val -a --keyring-backend test) --chain-id my-test-chain -``` - -You should see two delegations, the first one made from the `gentx`, and the second one you just performed from the `recipient` account. - -## Using gRPC - -The Protobuf ecosystem developed tools for different use cases, including code-generation from `*.proto` files into various languages. These tools allow the building of clients easily. Often, the client connection (i.e. the transport) can be plugged and replaced very easily. Let's explore one of the most popular transport: [gRPC](../01-tx-lifecycle.md08-grpc_rest.md). - -Since the code generation library largely depends on your own tech stack, we will only present three alternatives: - -* `grpcurl` for generic debugging and testing, -* programmatically via Go, -* CosmJS for JavaScript/TypeScript developers. - -### grpcurl - -[grpcurl](https://github.com/fullstorydev/grpcurl) is like `curl` but for gRPC. It is also available as a Go library, but we will use it only as a CLI command for debugging and testing purposes. Follow the instructions in the previous link to install it. - -Assuming you have a local node running (either a localnet, or connected a live network), you should be able to run the following command to list the Protobuf services available (you can replace `localhost:9000` by the gRPC server endpoint of another node, which is configured under the `grpc.address` field inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml)): - -```bash -grpcurl -plaintext localhost:9090 list -``` - -You should see a list of gRPC services, like `cosmos.bank.v1beta1.Query`. This is called reflection, which is a Protobuf endpoint returning a description of all available endpoints. Each of these represents a different Protobuf service, and each service exposes multiple RPC methods you can query against. - -In order to get a description of the service you can run the following command: - -```bash -grpcurl \ - localhost:9090 \ - describe cosmos.bank.v1beta1.Query # Service we want to inspect -``` - -It's also possible to execute an RPC call to query the node for information: - -```bash -grpcurl \ - -plaintext - -d '{"address":"$MY_VALIDATOR"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/AllBalances -``` - -The list of all available gRPC query endpoints is [coming soon](https://github.com/cosmos/cosmos-sdk/issues/7786). - -#### Query for historical state using grpcurl - -You may also query for historical data by passing some [gRPC metadata](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) to the query: the `x-cosmos-block-height` metadata should contain the block to query. Using grpcurl as above, the command looks like: - -```bash -grpcurl \ - -plaintext \ - -H "x-cosmos-block-height: 279256" \ - -d '{"address":"$MY_VALIDATOR"}' \ - localhost:9090 \ - cosmos.bank.v1beta1.Query/AllBalances -``` - -Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response. - -### Programmatically via Go - -The following snippet shows how to query the state using gRPC inside a Go program. The idea is to create a gRPC connection, and use the Protobuf-generated client code to query the gRPC server. - -#### Install Cosmos SDK - -Add below line to `go.mod` to replace protobuf, read more [#8469](https://github.com/cosmos/cosmos-sdk/issues/8469) - -```go -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 -``` - -```bash -go get github.com/cosmos/cosmos-sdk@main -``` - -```go -import ( - "context" - "fmt" - - "google.golang.org/grpc" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -func queryState() error { - myAddress, err := sdk.AccAddressFromBech32("cosmos1...") - if err != nil { - return err - } - - // Create a connection to the gRPC server. - grpcConn, err := grpc.Dial( - "127.0.0.1:9090", // your gRPC server address. - grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism. - // This instantiates a general gRPC codec which handles proto bytes. We pass in a nil interface registry - // if the request/response types contain interface instead of 'nil' you should pass the application specific codec. - grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(nil).GRPCCodec())), - ) - if err != nil { - return err - } - defer grpcConn.Close() - - // This creates a gRPC client to query the x/bank service. - bankClient := banktypes.NewQueryClient(grpcConn) - bankRes, err := bankClient.Balance( - context.Background(), - &banktypes.QueryBalanceRequest{Address: myAddress.String(), Denom: "atom"}, - ) - if err != nil { - return err - } - - fmt.Println(bankRes.GetBalance()) // Prints the account balance - - return nil -} -``` - -You can replace the query client (here we are using `x/bank`'s) with one generated from any other Protobuf service. The list of all available gRPC query endpoints is [coming soon](https://github.com/cosmos/cosmos-sdk/issues/7786). - -#### Query for historical state using Go - -Querying for historical blocks is done by adding the block height metadata in the gRPC request. - -```go -import ( - "context" - "fmt" - - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -func queryState() error { - // --snip-- - - var header metadata.MD - bankRes, err = bankClient.Balance( - metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, "12"), // Add metadata to request - &banktypes.QueryBalanceRequest{Address: myAddress.String(), Denom: "atom"}, - grpc.Header(&header), // Retrieve header from response - ) - if err != nil { - return err - } - blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) - - fmt.Println(blockHeight) // Prints the block height (12) - - return nil -} -``` - -### CosmJS - -CosmJS documentation can be found at [https://cosmos.github.io/cosmjs](https://cosmos.github.io/cosmjs). As of January 2021, CosmJS documentation is still work in progress. - -## Using the REST Endpoints - -As described in the [gRPC guide](../01-tx-lifecycle.md08-grpc_rest.md), all gRPC services on the Cosmos SDK are made available for more convenient REST-based queries through gRPC-gateway. The format of the URL path is based on the Protobuf service method's full-qualified name, but may contain small customizations so that final URLs look more idiomatic. For example, the REST endpoint for the `cosmos.bank.v1beta1.Query/AllBalances` method is `GET /cosmos/bank/v1beta1/balances/{address}`. Request arguments are passed as query parameters. - -As a concrete example, the `curl` command to make balances request is: - -```bash -curl \ - -X GET \ - -H "Content-Type: application/json" \ - http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR -``` - -Make sure to replace `localhost:1317` with the REST endpoint of your node, configured under the `api.address` field. - -The list of all available REST endpoints is available as a Swagger specification file, it can be viewed at `localhost:1317/swagger`. Make sure that the `api.swagger` field is set to true in your [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml) file. - -### Query for historical state using REST - -Querying for historical state is done using the HTTP header `x-cosmos-block-height`. For example, a curl command would look like: - -```bash -curl \ - -X GET \ - -H "Content-Type: application/json" \ - -H "x-cosmos-block-height: 279256" - http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR -``` - -Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response. - -### Cross-Origin Resource Sharing (CORS) - -[CORS policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are not enabled by default to help with security. If you would like to use the rest-server in a public environment we recommend you provide a reverse proxy, this can be done with [nginx](https://www.nginx.com/). For testing and development purposes there is an `enabled-unsafe-cors` field inside [`app.toml`](../run-node/run-node.md#configuring-the-node-using-apptoml). - -## Next {hide} - -Sending transactions using gRPC and REST requires some additional steps: generating the transaction, signing it, and finally broadcasting it. Read about [generating and signing transactions](./txs.md). {hide} diff --git a/versioned_docs/version-0.46/user/run-node/03-txs.md b/versioned_docs/version-0.46/user/run-node/03-txs.md deleted file mode 100644 index 35951533b..000000000 --- a/versioned_docs/version-0.46/user/run-node/03-txs.md +++ /dev/null @@ -1,379 +0,0 @@ -# Generating, Signing and Broadcasting Transactions - -This document describes how to generate an (unsigned) transaction, signing it (with one or multiple keys), and broadcasting it to the network. {synopsis} - -## Using the CLI - -The easiest way to send transactions is using the CLI, as we have seen in the previous page when [interacting with a node](./interact-node.md#using-the-cli). For example, running the following command - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --keyring-backend test -``` - -will run the following steps: - -* generate a transaction with one `Msg` (`x/bank`'s `MsgSend`), and print the generated transaction to the console. -* ask the user for confirmation to send the transaction from the `$MY_VALIDATOR_ADDRESS` account. -* fetch `$MY_VALIDATOR_ADDRESS` from the keyring. This is possible because we have [set up the CLI's keyring](./keyring.md) in a previous step. -* sign the generated transaction with the keyring's account. -* broadcast the signed transaction to the network. This is possible because the CLI connects to the node's Tendermint RPC endpoint. - -The CLI bundles all the necessary steps into a simple-to-use user experience. However, it's possible to run all the steps individually too. - -### Generating a Transaction - -Generating a transaction can simply be done by appending the `--generate-only` flag on any `tx` command, e.g.: - -```bash -simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --generate-only -``` - -This will output the unsigned transaction as JSON in the console. We can also save the unsigned transaction to a file (to be passed around between signers more easily) by appending `> unsigned_tx.json` to the above command. - -### Signing a Transaction - -Signing a transaction using the CLI requires the unsigned transaction to be saved in a file. Let's assume the unsigned transaction is in a file called `unsigned_tx.json` in the current directory (see previous paragraph on how to do that). Then, simply run the following command: - -```bash -simd tx sign unsigned_tx.json --chain-id my-test-chain --keyring-backend test --from $MY_VALIDATOR_ADDRESS -``` - -This command will decode the unsigned transaction and sign it with `SIGN_MODE_DIRECT` with `$MY_VALIDATOR_ADDRESS`'s key, which we already set up in the keyring. The signed transaction will be output as JSON to the console, and, as above, we can save it to a file by appending `> signed_tx.json`. - -Some useful flags to consider in the `tx sign` command: - -* `--sign-mode`: you may use `amino-json` to sign the transaction using `SIGN_MODE_LEGACY_AMINO_JSON`, -* `--offline`: sign in offline mode. This means that the `tx sign` command doesn't connect to the node to retrieve the signer's account number and sequence, both needed for signing. In this case, you must manually supply the `--account-number` and `--sequence` flags. This is useful for offline signing, i.e. signing in a secure environment which doesn't have access to the internet. - -#### Signing with Multiple Signers - -::: warning -Please note that signing a transaction with multiple signers or with a multisig account, where at least one signer uses `SIGN_MODE_DIRECT`, is not yet possible. You may follow [this Github issue](https://github.com/cosmos/cosmos-sdk/issues/8141) for more info. -::: - -Signing with multiple signers is done with the `tx multisign` command. This command assumes that all signers use `SIGN_MODE_LEGACY_AMINO_JSON`. The flow is similar to the `tx sign` command flow, but instead of signing an unsigned transaction file, each signer signs the file signed by previous signer(s). The `tx multisign` command will append signatures to the existing transactions. It is important that signers sign the transaction **in the same order** as given by the transaction, which is retrievable using the `GetSigners()` method. - -For example, starting with the `unsigned_tx.json`, and assuming the transaction has 4 signers, we would run: - -```bash -# Let signer1 sign the unsigned tx. -simd tx multisign unsigned_tx.json signer_key_1 --chain-id my-test-chain --keyring-backend test > partial_tx_1.json -# Now signer1 will send the partial_tx_1.json to the signer2. -# Signer2 appends their signature: -simd tx multisign partial_tx_1.json signer_key_2 --chain-id my-test-chain --keyring-backend test > partial_tx_2.json -# Signer2 sends the partial_tx_2.json file to signer3, and signer3 can append his signature: -simd tx multisign partial_tx_2.json signer_key_3 --chain-id my-test-chain --keyring-backend test > partial_tx_3.json -``` - -### Broadcasting a Transaction - -Broadcasting a transaction is done using the following command: - -```bash -simd tx broadcast tx_signed.json -``` - -You may optionally pass the `--broadcast-mode` flag to specify which response to receive from the node: - -* `block`: the CLI waits for the tx to be committed in a block. -* `sync`: the CLI waits for a CheckTx execution response only. -* `async`: the CLI returns immediately (transaction might fail). - -### Encoding a Transaction - -In order to broadcast a transaction using the gRPC or REST endpoints, the transaction will need to be encoded first. This can be done using the CLI. - -Encoding a transaction is done using the following command: - -```bash -simd tx encode tx_signed.json -``` - -This will read the transaction from the file, serialize it using Protobuf, and output the transaction bytes as base64 in the console. - -### Decoding a Transaction - -The CLI can also be used to decode transaction bytes. - -Decoding a transaction is done using the following command: - -```bash -simd tx decode [protobuf-byte-string] -``` - -This will decode the transaction bytes and output the transaction as JSON in the console. You can also save the transaction to a file by appending `> tx.json` to the above command. - -## Programmatically with Go - -It is possible to manipulate transactions programmatically via Go using the Cosmos SDK's `TxBuilder` interface. - -### Generating a Transaction - -Before generating a transaction, a new instance of a `TxBuilder` needs to be created. Since the Cosmos SDK supports both Amino and Protobuf transactions, the first step would be to decide which encoding scheme to use. All the subsequent steps remain unchanged, whether you're using Amino or Protobuf, as `TxBuilder` abstracts the encoding mechanisms. In the following snippet, we will use Protobuf. - -```go -import ( - "github.com/cosmos/cosmos-sdk/simapp" -) - -func sendTx() error { - // Choose your codec: Amino or Protobuf. Here, we use Protobuf, given by the - // following function. - encCfg := simapp.MakeTestEncodingConfig() - - // Create a new TxBuilder. - txBuilder := encCfg.TxConfig.NewTxBuilder() - - // --snip-- -} -``` - -We can also set up some keys and addresses that will send and receive the transactions. Here, for the purpose of the tutorial, we will be using some dummy data to create keys. - -```go -import ( - "github.com/cosmos/cosmos-sdk/testutil/testdata" -) - -priv1, _, addr1 := testdata.KeyTestPubAddr() -priv2, _, addr2 := testdata.KeyTestPubAddr() -priv3, _, addr3 := testdata.KeyTestPubAddr() -``` - -Populating the `TxBuilder` can be done via its [methods](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0-rc1/client/tx_config.go#L33-L50): - -```go -import ( - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -func sendTx() error { - // --snip-- - - // Define two x/bank MsgSend messages: - // - from addr1 to addr3, - // - from addr2 to addr3. - // This means that the transactions needs two signers: addr1 and addr2. - msg1 := banktypes.NewMsgSend(addr1, addr3, types.NewCoins(types.NewInt64Coin("atom", 12))) - msg2 := banktypes.NewMsgSend(addr2, addr3, types.NewCoins(types.NewInt64Coin("atom", 34))) - - err := txBuilder.SetMsgs(msg1, msg2) - if err != nil { - return err - } - - txBuilder.SetGasLimit(...) - txBuilder.SetFeeAmount(...) - txBuilder.SetMemo(...) - txBuilder.SetTimeoutHeight(...) -} -``` - -At this point, `TxBuilder`'s underlying transaction is ready to be signed. - -### Signing a Transaction - -We set encoding config to use Protobuf, which will use `SIGN_MODE_DIRECT` by default. As per [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-020-protobuf-transaction-encoding.md), each signer needs to sign the `SignerInfo`s of all other signers. This means that we need to perform two steps sequentially: - -* for each signer, populate the signer's `SignerInfo` inside `TxBuilder`, -* once all `SignerInfo`s are populated, for each signer, sign the `SignDoc` (the payload to be signed). - -In the current `TxBuilder`'s API, both steps are done using the same method: `SetSignatures()`. The current API requires us to first perform a round of `SetSignatures()` _with empty signatures_, only to populate `SignerInfo`s, and a second round of `SetSignatures()` to actually sign the correct payload. - -```go -import ( - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" -) - -func sendTx() error { - // --snip-- - - privs := []cryptotypes.PrivKey{priv1, priv2} - accNums:= []uint64{..., ...} // The accounts' account numbers - accSeqs:= []uint64{..., ...} // The accounts' sequence numbers - - // First round: we gather all the signer infos. We use the "set empty - // signature" hack to do that. - var sigsV2 []signing.SignatureV2 - for i, priv := range privs { - sigV2 := signing.SignatureV2{ - PubKey: priv.PubKey(), - Data: &signing.SingleSignatureData{ - SignMode: encCfg.TxConfig.SignModeHandler().DefaultMode(), - Signature: nil, - }, - Sequence: accSeqs[i], - } - - sigsV2 = append(sigsV2, sigV2) - } - err := txBuilder.SetSignatures(sigsV2...) - if err != nil { - return err - } - - // Second round: all signer infos are set, so each signer can sign. - sigsV2 = []signing.SignatureV2{} - for i, priv := range privs { - signerData := xauthsigning.SignerData{ - ChainID: chainID, - AccountNumber: accNums[i], - Sequence: accSeqs[i], - } - sigV2, err := tx.SignWithPrivKey( - encCfg.TxConfig.SignModeHandler().DefaultMode(), signerData, - txBuilder, priv, encCfg.TxConfig, accSeqs[i]) - if err != nil { - return nil, err - } - - sigsV2 = append(sigsV2, sigV2) - } - err = txBuilder.SetSignatures(sigsV2...) - if err != nil { - return err - } -} -``` - -The `TxBuilder` is now correctly populated. To print it, you can use the `TxConfig` interface from the initial encoding config `encCfg`: - -```go -func sendTx() error { - // --snip-- - - // Generated Protobuf-encoded bytes. - txBytes, err := encCfg.TxConfig.TxEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - - // Generate a JSON string. - txJSONBytes, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - txJSON := string(txJSONBytes) -} -``` - -### Broadcasting a Transaction - -The preferred way to broadcast a transaction is to use gRPC, though using REST (via `gRPC-gateway`) or the Tendermint RPC is also posible. An overview of the differences between these methods is exposed [here](../01-tx-lifecycle.md08-grpc_rest.md). For this tutorial, we will only describe the gRPC method. - -```go -import ( - "context" - "fmt" - - "google.golang.org/grpc" - - "github.com/cosmos/cosmos-sdk/types/tx" -) - -func sendTx(ctx context.Context) error { - // --snip-- - - // Create a connection to the gRPC server. - grpcConn := grpc.Dial( - "127.0.0.1:9090", // Or your gRPC server address. - grpc.WithInsecure(), // The Cosmos SDK doesn't support any transport security mechanism. - ) - defer grpcConn.Close() - - // Broadcast the tx via gRPC. We create a new client for the Protobuf Tx - // service. - txClient := tx.NewServiceClient(grpcConn) - // We then call the BroadcastTx method on this client. - grpcRes, err := txClient.BroadcastTx( - ctx, - &tx.BroadcastTxRequest{ - Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step. - }, - ) - if err != nil { - return err - } - - fmt.Println(grpcRes.TxResponse.Code) // Should be `0` if the tx is successful - - return nil -} -``` - -#### Simulating a Transaction - -Before broadcasting a transaction, we sometimes may want to dry-run the transaction, to estimate some information about the transaction without actually committing it. This is called simulating a transaction, and can be done as follows: - -```go -import ( - "context" - "fmt" - "testing" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/types/tx" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" -) - -func simulateTx() error { - // --snip-- - - // Simulate the tx via gRPC. We create a new client for the Protobuf Tx - // service. - txClient := tx.NewServiceClient(grpcConn) - txBytes := /* Fill in with your signed transaction bytes. */ - - // We then call the Simulate method on this client. - grpcRes, err := txClient.Simulate( - context.Background(), - &tx.SimulateRequest{ - TxBytes: txBytes, - }, - ) - if err != nil { - return err - } - - fmt.Println(grpcRes.GasInfo) // Prints estimated gas used. - - return nil -} -``` - -## Using gRPC - -It is not possible to generate or sign a transaction using gRPC, only to broadcast one. In order to broadcast a transaction using gRPC, you will need to generate, sign, and encode the transaction using either the CLI or programmatically with Go. - -### Broadcasting a Transaction - -Broadcasting a transaction using the gRPC endpoint can be done by sending a `BroadcastTx` request as follows, where the `txBytes` are the protobuf-encoded bytes of a signed transaction: - -```bash -grpcurl -plaintext \ - -d '{"tx_bytes":"{{txBytes}}","mode":"BROADCAST_MODE_SYNC"}' \ - localhost:9090 \ - cosmos.tx.v1beta1.Service/BroadcastTx -``` - -## Using REST - -It is not possible to generate or sign a transaction using REST, only to broadcast one. In order to broadcast a transaction using REST, you will need to generate, sign, and encode the transaction using either the CLI or programmatically with Go. - -### Broadcasting a Transaction - -Broadcasting a transaction using the REST endpoint (served by `gRPC-gateway`) can be done by sending a POST request as follows, where the `txBytes` are the protobuf-encoded bytes of a signed transaction: - -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -d'{"tx_bytes":"{{txBytes}}","mode":"BROADCAST_MODE_SYNC"}' \ - localhost:1317/cosmos/tx/v1beta1/txs -``` - -## Using CosmJS (JavaScript & TypeScript) - -CosmJS aims to build client libraries in JavaScript that can be embedded in web applications. Please see [https://cosmos.github.io/cosmjs](https://cosmos.github.io/cosmjs) for more information. As of January 2021, CosmJS documentation is still work in progress. diff --git a/versioned_docs/version-0.46/user/run-node/04-rosetta.md b/versioned_docs/version-0.46/user/run-node/04-rosetta.md deleted file mode 100644 index 06adaf5b4..000000000 --- a/versioned_docs/version-0.46/user/run-node/04-rosetta.md +++ /dev/null @@ -1,108 +0,0 @@ -# Rosetta - -The `rosetta` package implements Coinbase's [Rosetta API](https://www.rosetta-api.org). This document provides instructions on how to use the Rosetta API integration. For information about the motivation and design choices, refer to [ADR 035](../architecture/adr-035-rosetta-api-support.md). - -## Add Rosetta Command - -The Rosetta API server is a stand-alone server that connects to a node of a chain developed with Cosmos SDK. - -To enable Rosetta API support, it's required to add the `RosettaCommand` to your application's root command file (e.g. `appd/cmd/root.go`). - -Import the `server` package: - -```go - "github.com/cosmos/cosmos-sdk/server" -``` - -Find the following line: - -```go -initRootCmd(rootCmd, encodingConfig) -``` - -After that line, add the following: - -```go -rootCmd.AddCommand( - server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Codec) -) -``` - -The `RosettaCommand` function builds the `rosetta` root command and is defined in the `server` package within Cosmos SDK. - -Since we’ve updated the Cosmos SDK to work with the Rosetta API, updating the application's root command file is all you need to do. - -An implementation example can be found in `simapp` package. - -## Use Rosetta Command - -To run Rosetta in your application CLI, use the following command: - -```sh -appd rosetta --help -``` - -To test and run Rosetta API endpoints for applications that are running and exposed, use the following command: - -```sh -appd rosetta - --blockchain "your application name (ex: gaia)" - --network "your chain identifier (ex: testnet-1)" - --tendermint "tendermint endpoint (ex: localhost:26657)" - --grpc "gRPC endpoint (ex: localhost:9090)" - --addr "rosetta binding address (ex: :8080)" -``` - -## Extensions - -There are two ways in which you can customize and extend the implementation with your custom settings. - -### Message extension - -In order to make an `sdk.Msg` understandable by rosetta the only thing which is required is adding the methods to your messages that satisfy the `rosetta.Msg` interface. Examples on how to do so can be found in the staking types such as `MsgDelegate`, or in bank types such as `MsgSend`. - -### Client interface override - -In case more customization is required, it's possible to embed the Client type and override the methods which require customizations. - -Example: - -```go -package custom_client -import ( - -"context" -"github.com/coinbase/rosetta-sdk-go/types" -"github.com/cosmos/cosmos-sdk/server/rosetta/lib" -) - -// CustomClient embeds the standard cosmos client -// which means that it implements the cosmos-rosetta-gateway Client -// interface while at the same time allowing to customize certain methods -type CustomClient struct { - *rosetta.Client -} - -func (c *CustomClient) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) { - // provide custom signature bytes - panic("implement me") -} -``` - -NOTE: when using a customized client, the command cannot be used as the constructors required **may** differ, so it's required to create a new one. We intend to provide a way to init a customized client without writing extra code in the future. - -### Error extension - -Since rosetta requires to provide 'returned' errors to network options. In order to declare a new rosetta error, we use the `errors` package in cosmos-rosetta-gateway. - -Example: - -```go -package custom_errors -import crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors" - -var customErrRetriable = true -var CustomError = crgerrs.RegisterError(100, "custom message", customErrRetriable, "description") -``` - -Note: errors must be registered before cosmos-rosetta-gateway's `Server`.`Start` method is called. Otherwise the registration will be ignored. Errors with same code will be ignored too. diff --git a/versioned_docs/version-0.46/user/run-node/05-run-testnet.md b/versioned_docs/version-0.46/user/run-node/05-run-testnet.md deleted file mode 100644 index ce59a2492..000000000 --- a/versioned_docs/version-0.46/user/run-node/05-run-testnet.md +++ /dev/null @@ -1,95 +0,0 @@ -# Running a Testnet - -The `simd testnet` subcommand makes it easy to initialize and start a simulated test network for testing purposes. {synopsis} - -In addition to the commands for [running a node](./run-node.html), the `simd` binary also includes a `testnet` command that allows you to start a simulated test network in-process or to initialize files for a simulated test network that runs in a separate process. - -## Initialize Files - -First, let's take a look at the `init-files` subcommand. - -This is similar to the `init` command when initializing a single node, but in this case we are initializing multiple nodes, generating the genesis transactions for each node, and then collecting those transactions. - -The `init-files` subcommand initializes the necessary files to run a test network in a separate process (i.e. using a Docker container). Running this command is not a prerequisite for the `start` subcommand ([see below](#start-testnet)). - -In order to initialize the files for a test network, run the following command: - -```bash -simd testnet init-files -``` - -You should see the following output in your terminal: - -```bash -Successfully initialized 4 node directories -``` - -The default output directory is a relative `.testnets` directory. Let's take a look at the files created within the `.testnets` directory. - -### gentxs - -The `gentxs` directory includes a genesis transaction for each validator node. Each file includes a JSON encoded genesis transaction used to register a validator node at the time of genesis. The genesis transactions are added to the `genesis.json` file within each node directory during the initilization process. - -### nodes - -A node directory is created for each validator node. Within each node directory is a `simd` directory. The `simd` directory is the home directory for each node, which includes the configuration and data files for that node (i.e. the same files included in the default `~/.simapp` directory when running a single node). - -## Start Testnet - -Now, let's take a look at the `start` subcommand. - -The `start` subcommand both initializes and starts an in-process test network. This is the fastest way to spin up a local test network for testing purposes. - -You can start the local test network by running the following command: - -```bash -simd testnet start -``` - -You should see something similar to the following: - -```bash -acquiring test network lock -preparing test network with chain-id "chain-mtoD9v" - - -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -++ THIS MNEMONIC IS FOR TESTING PURPOSES ONLY ++ -++ DO NOT USE IN PRODUCTION ++ -++ ++ -++ sustain know debris minute gate hybrid stereo custom ++ -++ divorce cross spoon machine latin vibrant term oblige ++ -++ moment beauty laundry repeat grab game bronze truly ++ -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - -starting test network... -started test network -press the Enter Key to terminate -``` - -The first validator node is now running in-process, which means the test network will terminate once you either close the terminal window or you press the Enter key. In the output, the mnemonic phrase for the first validator node is provided for testing purposes. The validator node is using the same default addresses being used when initializing and starting a single node (no need to provide a `--node` flag). - -Check the status of the first validator node: - -```sh -simd status -``` - -Import the key from the provided mnemonic: - -```sh -simd keys add test --recover --keyring-backend test -``` - -Check the balance of the account address: - -```sh -simd q bank balances [address] -``` - -Use this test account to manually test against the test network. - -## Testnet Options - -You can customize the configuration of the test network with flags. In order to see all flag options, append the `--help` flag to each command. diff --git a/versioned_docs/version-0.46/user/run-node/_category_.json b/versioned_docs/version-0.46/user/run-node/_category_.json deleted file mode 100644 index 9b291ba6f..000000000 --- a/versioned_docs/version-0.46/user/run-node/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "User Guides", - "position": 0, - "link": null -} \ No newline at end of file diff --git a/versions.json b/versions.json index 6b1b18dae..fc971f3da 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,3 @@ [ - "0.47", - "0.46", - "0.45" + "0.47" ]