diff --git a/docs/integration_consumer.md b/docs/integration_consumer.md new file mode 100644 index 00000000..bda4d9a8 --- /dev/null +++ b/docs/integration_consumer.md @@ -0,0 +1,152 @@ +# Mesh-security +Cosmos module implementation + +# Integrate the mesh security module + +## Prerequisites +Projects that want to integrate the meshsecurity module onto their Cosmos SDK chain must enable the following modules: +- [x/staking](https://github.com/cosmos/cosmos-sdk/tree/main/x/staking) +- [x/auth](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth) +- [x/bank](https://github.com/cosmos/cosmos-sdk/tree/main/x/bank) +- [x/wasm](github.com/CosmWasm/wasmd/x/wasm) + +## Configuring and Adding Module +1. Add the mesh security package to the go.mod and install it. + ``` + require ( + ... + github.com/osmosis-labs/mesh-security + ... + ) + ``` + +2. Add the following modules to `app.go` + ``` + import ( + ... + "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity" + meshseckeeper "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper" + meshsectypes "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types" + ... + ) + ``` +3. In `app.go`: Register the AppModule for the mesh security module. + ``` + ModuleBasics = module.NewBasicManager( + ... + meshsecurity.AppModuleBasic{}, + ... + ) + ``` +4. In `app.go`: Add module account permissions: + ``` + maccPerms = map[string][]string{ + ... + meshsectypes.ModuleName: {authtypes.Minter, authtypes.Burner} + } + ``` +5. In `app.go`: Add mesh security keeper. + ``` + type App struct { + ... + MeshSecKeeper *meshseckeeper.Keeper + ... + } + ``` +6. In `app.go`: Add mesh security store key. + ``` + keys := sdk.NewKVStoreKeys( + ... + meshsectypes.StoreKey, + ... + ) + ``` +7. In `app.go`: Instantiate mesh security keeper + ``` + app.MeshSecKeeper = meshseckeeper.NewKeeper( + app.appCodec, + keys[meshsectypes.StoreKey], + memKeys[meshsectypes.MemStoreKey], + app.BankKeeper, + app.StakingKeeper, + &app.WasmKeeper, // ensure this is a pointer as we instantiate the keeper a bit later + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + ``` +8. In `app.go`: Add the mesh security module to the app manager instantiation. + ``` + app.mm = module.NewManager( + ... + meshsecurity.NewAppModule(appCodec, app.MeshSecKeeper), + ... + ) + ``` +9. In `app.go`: Add the module as the final element to the following: +- SetOrderBeginBlockers +- SetOrderEndBlockers +- SetOrderInitGenesis + ``` + // Add mesh security to begin blocker logic + app.moduleManager.SetOrderBeginBlockers( + ... + meshsectypes.ModuleName, + ... + ) + + // Add mesh security to end blocker logic + app.moduleManager.SetOrderEndBlockers( + ... + meshsectypes.ModuleName, + ... + ) + + // Add mesh security to init genesis logic + app.moduleManager.SetOrderInitGenesis( + ... + meshsectypes.ModuleName, + ... + ) + ``` +10. In `app.go`: Add the mesh security staking decorator to the slashing module. + ``` + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, + legacyAmino, + keys[slashingtypes.StoreKey], + // decorate the sdk keeper to capture all jail/ unjail events for MS + meshseckeeper.NewStakingDecorator(app.StakingKeeper, app.MeshSecKeeper), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + ``` +11. In `app.go`: Add the mesh security hooks to the staking module. + ``` + app.StakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks( + ... + // register hook to capture valset updates + app.MeshSecKeeper.Hooks() + ), + ) + ``` +12. In `app.go`: Add the mesh security hooks to the evidence module. + ``` + evidenceKeeper := evidencekeeper.NewKeeper( + ... + // decorate the SlashingKeeper to capture the tombstone event + meshseckeeper.CaptureTombstoneDecorator(app.MeshSecKeeper, app.SlashingKeeper, app.StakingKeeper), + ) + ``` +13. In `app.go`: Add the mesh security wasm message handler decorator to the wasm module. + ``` + meshMessageHandler := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { + return wasmkeeper.NewMessageHandlerChain( + meshseckeeper.NewIntegrityHandler(app.MeshSecKeeper), + nested, + meshseckeeper.NewDefaultCustomMsgHandler(app.MeshSecKeeper), + ) + }) + wasmOpts = append(wasmOpts, meshMessageHandler, + // add support for the mesh-security queries + wasmkeeper.WithQueryHandlerDecorator(meshseckeeper.NewQueryDecorator(app.MeshSecKeeper, app.SlashingKeeper)), + ) + ``` \ No newline at end of file diff --git a/docs/integration_provider.md b/docs/integration_provider.md new file mode 100644 index 00000000..b42cb214 --- /dev/null +++ b/docs/integration_provider.md @@ -0,0 +1,111 @@ +# Mesh security provider +Cosmos module implementation + +# Integrate the mesh security provider module + +## Prerequisites +Projects that want to integrate the meshsecurityprovider module onto their Cosmos SDK chain must enable the following modules: +- [x/staking](https://github.com/cosmos/cosmos-sdk/tree/main/x/staking) +- [x/auth](https://github.com/cosmos/cosmos-sdk/tree/main/x/auth) +- [x/bank](https://github.com/cosmos/cosmos-sdk/tree/main/x/bank) +- [x/wasm](github.com/CosmWasm/wasmd/x/wasm) + +## Configuring and Adding Module +1. Add the mesh security package to the go.mod and install it. + ``` + require ( + ... + github.com/osmosis-labs/mesh-security + ... + ) + ``` + +2. Add the following modules to `app.go` + ``` + import ( + ... + meshsecprov "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider" + meshsecprovkeeper "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/keeper" + meshsecprovtypes "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/types" + ... + ) + ``` +3. In `app.go`: Register the AppModule for the mesh security provider module. + ``` + ModuleBasics = module.NewBasicManager( + ... + meshsecprov.AppModuleBasic{}, + ... + ) + ``` +4. In `app.go`: Add mesh security provider keeper. + ``` + type App struct { + ... + MeshSecProvKeeper *meshsecprovkeeper.Keeper + ... + } + ``` +5. In `app.go`: Add mesh security provider store key. + ``` + keys := sdk.NewKVStoreKeys( + ... + meshsecprovtypes.StoreKey, + ... + ) + ``` +6. In `app.go`: Instantiate mesh security provider keeper + ``` + app.MeshSecProvKeeper = meshsecprovkeeper.NewKeeper( + appCodec, + keys[meshsecprovtypes.StoreKey], + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.BankKeeper, + &app.WasmKeeper, + app.StakingKeeper, + ) + ``` +7. In `app.go`: Add the mesh security provider module to the app manager instantiation. + ``` + app.mm = module.NewManager( + ... + meshsecprov.NewAppModule(app.MeshSecProvKeeper) + ... + ) + ``` +8. In `app.go`: Add the module as the final element to the following: +- SetOrderBeginBlockers +- SetOrderEndBlockers +- SetOrderInitGenesis + ``` + // Add mesh security to begin blocker logic + app.moduleManager.SetOrderBeginBlockers( + ... + meshsecprovtypes.ModuleName, + ... + ) + + // Add mesh security to end blocker logic + app.moduleManager.SetOrderEndBlockers( + ... + meshsecprovtypes.ModuleName, + ... + ) + + // Add mesh security to init genesis logic + app.moduleManager.SetOrderInitGenesis( + ... + meshsecprovtypes.ModuleName, + ... + ) + ``` +9. In `app.go`: Add the mesh security wasm message handler decorator to the wasm module. + ``` + meshMessageHandler := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { + return wasmkeeper.NewMessageHandlerChain( + meshsecprovkeeper.CustomMessageDecorator(app.MeshSecProvKeeper), + nested, + ) + }) + wasmOpts = append(wasmOpts, meshMessageHandler) + ``` \ No newline at end of file diff --git a/docs/v2.md b/docs/v2.md new file mode 100644 index 00000000..c1e0fda5 --- /dev/null +++ b/docs/v2.md @@ -0,0 +1,197 @@ +### [Allow depositing vesting tokens in the vault](https://github.com/osmosis-labs/mesh-security/issues/90) +#### Bonding +```mermaid +%%{init: {'theme': 'forest'}}%% +flowchart TD + A{{User}} -- bond --> B(Vault); + B -- ProviderMsg::Bond --> C(Provider Module); + C -- Delegate --> B +``` +Rather than directly transferring funds when invoking the `bond` method, users specify the amount of tokens they wish to deposit into the `Vault` contract. The `Vault` contract then issues a custom message, `ProviderMsg::Bond`, to the Provider module. This module manages both the user's vesting and transferable tokens, coordinating with the Bank module to delegate tokens from the user's account to the `Vault` contract address. +#### Unbonding +```mermaid +%%{init: {'theme': 'forest'}}%% +flowchart TD + A{{User}} -- unbond --> B(Vault); + B -- ProviderMsg::Unbond --> C(Provider Module); + C -- Undelegate --> B +``` +When a user wishes to retrieve their tokens, they simply invoke the `unbond` method on the `Vault` contract. The `Vault` contract validates the user's available withdrawable balance, and upon successful validation, issues a custom message, `ProviderMsg::Unbond`, to the Provider module. The Provider module then interacts with the Bank module to undelegate the tokens from the `Vault` contract address back to the user's account. After this process, the user receives both their vesting and transferable tokens that were previously bonded. + +With this process, users can engage with other methods available in the `Vault` contract without distinction between vesting and transferable tokens, as both types of tokens are treated uniformly once bonded and can be utilized seamlessly within the contract. And the Provider module must define the Vault contract address by configuring it via the MsgUpdateParams. + +#### Messages +- `bond` method has been modified as follows: + +| | Parameters | Funds | +|-----|----------------|-------| +| Old | [] | Yes | +| New | [amount: Coin] | No | + +#### PR for the issue +- Handle on chain: [mesh-security-sdk#182](https://github.com/osmosis-labs/mesh-security-sdk/pull/182) +- Contract: [mesh-security#196](https://github.com/osmosis-labs/mesh-security/pull/196) + +### [Native "immediate unbonding" mechanism](https://github.com/osmosis-labs/mesh-security/issues/175) +#### Flow +```mermaid +%%{init: {'theme': 'forest'}}%% +flowchart TD + A{{User}} -- unstake --> B(Native staking proxy); + B -- StakingMsg::Undelegate --x C(Staking Module); + B -- ProviderMsg::Unstake --> D(Provider module); + D -- Undelegate --> C; +``` + +To implement an instant undelegation feature, when a user invokes the `unstake` method on the `native staking proxy` contract, instead of sending a `StakingMsg::Undelegate` to the staking module, the contract will issue a custom message, `ProviderMsg::Unstake`, to the Provider module. The Provider module will then validate the unbond amount and immediately undelegate the tokens from the validator if the validator has already been unbonded. To ensure that the contract call is originating from the authorized `native staking proxy` contract, the Provider module will query the `native staking` contract, as specified via `MsgUpdateParams`, and verify that the caller is indeed the `native staking proxy` contract associated with the delegator. + +#### PR for the issue +- Handle on chain: [mesh-security-sdk#183](https://github.com/osmosis-labs/mesh-security-sdk/pull/183) +- Contract: [mesh-security#197](https://github.com/osmosis-labs/mesh-security/pull/197) + +### [Transition stake](https://github.com/osmosis-labs/mesh-security/issues/89) +#### Flow +```mermaid +%%{init: {'theme': 'forest'}}%% +flowchart TD + A{{User}} -- 1. restake --> B(Vault); + B -- 2. ProviderMsg::Restake --> C(Provider module); + B -- 5. StakeMsg --> D(Native staking); + C -- 3. Instant Undelegate --> E{{Validator}}; + C -- 4. Delegate --> B; +``` + +When users have tokens already staked, they can transfer those tokens to the `Vault` and restake them with the same validator by invoking the `restake` method on the `Vault` contract. This method will record the bonding amount and send two messages. The first message is sent to the Provider module, which, upon receipt, will verify the unbonding amount and confirm that the caller is the authorized `Vault` contract, as specified through `MsgUpdateParams`. The Provider module will then instantly undelegate the user's existing delegation and re-delegate those tokens to the `Vault` contract. The second message is sent to the `native staking` contract to manage the staking of tokens with the same validator. + +#### Messages +- New `restake` method has been established as follows: + +| Parameters | Type | +|------------|--------| +| amount | Coin | +| validator | String | + +#### PR for the issue +- Handle on chain: [mesh-security-sdk#184](https://github.com/osmosis-labs/mesh-security-sdk/pull/184) +- Contract: [mesh-security#199](https://github.com/osmosis-labs/mesh-security/pull/199) + +### [Zero max cap](https://github.com/osmosis-labs/mesh-security/issues/100) +#### Flow +```mermaid +%%{init: {'theme': 'forest'}}%% +flowchart TD + A(Gov module) -- 1.Set zero max cap --> B(MeshSec module); + B -- 2. Schedule task --> C(Virtual staking); + C -- 3. Query AllDelegations --> B; + B -- 4. Return AllDelegations --> C; + C -- 5. Internal unstake --> D(Converter); + D -- 6. IBC --> E(External staking); + E -- 7. Internal unstake --> E; + E -- 8. Ack --> D; + D -- 9. Internal unbond --> C; + C -- 10. Delete all tasks --> B; + F{{User}} -- 1. Withdraw unbonded --> E; + E -- 2. Release cross stake --> G(Vault) +``` + +In order to let the contract know about the delegation of each user to each validator, we added **`UpdateDelegation`** msg. The msg will set delegation on consumer chain when user **`Bond`** or **`Unbond`**. With that, when the cap is set to zero, contract can use **`AllDelegations`** query to get all delegations saved. Due to virtual staking and converter contract does not save delegator info in its own state, we need to resend the msg to external staking contract. After external staking is called via ibc, converter contract get the Ack message, if it success, a msg to virtual staking contract will be executed to unbond the given amount by delegator. + +On the next schedule task, if query **`AllDelegations`** returns empty, that mean all users's delegation has been unbond completely, virtual staking contract will send **`DeleteScheduledTasks`** to mesh security module and stop creating tasks. +#### Messages +- `AllDelegations` query msg has been established as folows: +AllDelegationsQuery: + +| Parameters | Type | +|------------|--------| +|Contract | String | +|MaxRetrieve | uint16 | + +AllDelegationsResponse: + +| Parameters | Type | +|------------|--------------| +| Delegations| [Delegation] | + +Delegation: + +| Parameters | Type | +|------------|--------| +|Delegator | String | +|Validator | String | +|Amount | String | + +- `UpdateDelegation` msg has been established as folows: + +| Parameters | Type | +|------------|--------| +|Amount |Coin | +|IsDeduct |bool | +|Delegator |String | +|Validator |String | + +#### PR for the issue +- Handle on chain: [mesh-security-sdk#173](https://github.com/osmosis-labs/mesh-security-sdk/pull/173) +- Contract: [mesh-security#192](https://github.com/osmosis-labs/mesh-security/pull/192) +### [Auto unbond tombstoned validators](https://github.com/osmosis-labs/mesh-security/issues/154) +We had modifed [inactive list](https://github.com/osmosis-labs/mesh-security/blob/c170596e515dd9f2247a1de26097c10104b2e6a1/contracts/consumer/virtual-staking/src/contract.rs#L47) in virtual-staking to check if a validator is tombstoned or not. If validator is tombstoned, the `handle_epoch` function will query `TotalDelegation` from mesh scecurity module to get the amount should be unbond and unbond it automatically. As we save `tombstoned_unbond_enable` when instantiate contract, blockchain implementations may decide to unbond automatically when tombstoning. + +#### Messages +- `TotalDelegation` query msg has been established as folows: +TotalDelegationQuery: + +| Parameters | Type | +|------------|--------| +|Contract | String | +|Validator | String | + +TotalDelegationResponse: + +| Parameters | Type | +|------------|--------| +|Delegation | Coin | + +#### PR for the issue +- Handle on chain: [mesh-security-sdk#185](https://github.com/osmosis-labs/mesh-security-sdk/pull/185) +- Contract: [mesh-security#201](https://github.com/osmosis-labs/mesh-security/pull/201) + +### [Handle slashing from provider chain to native slashing contract](https://github.com/osmosis-labs/mesh-security/issues/174) +Slashing events can be forwarded to the contracts for processing by `JailingMsg`: +```go +SudoMsg struct { + Jailing *ValidatorSlash `json:"jailing,omitempty"` + } + // ValidatorAddr alias for the Bech32 address string of sdk.ValAddress + ValidatorAddr = string + + ValidatorSlash struct { + Jailed []ValidatorAddr `json:"jailed"` + Tombstoned []ValidatorAddr `json:"tombstoned"` + } +``` + +Using Staking Decorate, we modify the SlashWithInfractionReason function. This change enables forwarding of slashing events to the contract via `JailingMsg`. The slashing handler on the contract side has already been implemented in https://github.com/osmosis-labs/mesh-security/issues/129. + +#### PR for the issue +- Handle on chain: [mesh-security-sdk#187](https://github.com/osmosis-labs/mesh-security-sdk/pull/187) + +### [Multitest improvement](https://github.com/osmosis-labs/mesh-security/issues/95) + +Due to current sylvia version does not support multitest in IBC, and the issue [sylvia #206: msg(exec) macro conditional](https://github.com/CosmWasm/sylvia/issues/206) is not resolved yet, we are only able to finish the work [remove the virtual staking mock](https://github.com/osmosis-labs/mesh-security/pull/200). + +### [IBC improvement](https://github.com/osmosis-labs/mesh-security/issues/96) +#### Flow +```mermaid +%%{init: {'theme': 'forest'}}%% +flowchart TD + A(Consumer chain) -- 1. Execute on close channel --> B(Converter contract); + B -- 2.Query AllDelegations --> A; + A -- 3.Return AllDelegations --> B; + B -- 4.Unstake all delegation --> C(Virtual staking contract); + B -- 6.Send OnChannelCloseConfirm package --> D(Ext staking contract); + C -- 5.Return Unbond, UpdateDelegation, DeleteScheduledTask --> E(Mesh security module); + D -- 7.Handle on close channel to unbond all tokens --> F(Vault contract); +``` +IBC close channel logic is considered similar to zero max cap logic. When close channel, the consumer chain will query `AllDelegations` to get all delegations in KV store. The virtual staking contract will unbond all tokens and dispatch `Unbond` msg to mesh security module. After that, a `OnChannelCloseConfirm` will be send to provider chain. The vault contract will execute `handle_channel_close` function, which unbond all tokens in provider chain from external staking contract. + +#### PR for the issue +- Contract: [mesh-security#203](https://github.com/osmosis-labs/mesh-security/pull/203) \ No newline at end of file