Harpoon Module Explanation
This document provides a technical explanation of the Harpoon module, which allows CosmWasm smart contracts to subscribe to staking module hooks.Architecture
The Harpoon module integrates with the Cosmos SDK staking module through its hooks interface. It acts as an intermediary between the staking module’s core functions and CosmWasm smart contracts.Integration with Staking Module
The Harpoon module connects to the staking module by implementing the standardStakingHooks
interface plus the additional BeforeValidatorSlashedWithTokensToBurn
method. This extended interface (StakingHooksBeforeValidatorSlashedHasTokensToBurn
) exists only in Neutron’s fork of the Cosmos SDK and allows contracts to receive the actual tokensToBurn
argument during slashing events.
These hooks are registered in the application’s initialization code (typically in app.go
):
Hook Types
The Harpoon module supports all the hooks defined in the staking interface:AfterValidatorCreated
: Called after a new validator is createdBeforeValidatorModified
: Called before an existing validator is modifiedAfterValidatorRemoved
: Called after a validator is removedAfterValidatorBonded
: Called after a validator is bondedAfterValidatorBeginUnbonding
: Called after a validator begins the unbonding processBeforeDelegationCreated
: Called before a new delegation is createdBeforeDelegationSharesModified
: Called before an existing delegation’s shares are modifiedBeforeDelegationRemoved
: Called before a delegation is removedAfterDelegationModified
: Called after a delegation is modifiedBeforeValidatorSlashed
: Special case - this method panics and should never be calledBeforeValidatorSlashedWithTokensToBurn
: The actual implementation for slashing hooksAfterUnbondingInitiated
: Called after the unbonding process is initiated
Special Slashing Hook Implementation
The Harpoon module implements a special version of the validator slashing hook:tokensToBurn
) compared to the standard slashing hook.
Contract Subscription Storage
The module stores subscriptions in a way that optimizes for the most frequent operation: retrieving all contracts subscribed to a specific hook type. The storage structure is:Contract Interaction
When a hook is triggered, the Harpoon module calls thesudo
method on each subscribed contract. Each hook type has its own specific message format.
Sudo Message Formats
Each hook type uses a specific message structure. Here are the actual formats used:Validator Hooks
Delegation Hooks
Unbonding Hooks
Error Handling
The Harpoon module implements strict error handling. When a contract returns an error during a sudo call, the module does not continue execution:- Transaction hooks: Error aborts the transaction
- End-blocker hooks: Error can halt the chain
Governance Control
The Harpoon module only allows subscription management through governance proposals. TheManageHookSubscription
message can only be executed by the module’s authority (governance).
Subscription Management Process
TheUpdateHookSubscription
method handles subscription changes:
- Determine hooks to remove: Any hook not in the new subscription list
- Add new subscriptions: Add the contract to each requested hook type
- Remove old subscriptions: Remove the contract from hooks no longer requested
- Clean up empty subscriptions: Delete storage entries with no subscribed contracts
Design Rationale
Why Not Direct Queries?
The staking module does not store historical data. By subscribing to hooks, contracts can maintain their own historical records, enabling governance systems to calculate voting power at specific block heights.Why Governance-Only Subscriptions?
Security and performance considerations:- Malicious contracts could deliberately return errors to disrupt operations
- Poorly implemented contracts might unintentionally cause chain halts
- Too many subscribed contracts could impact performance
Why No Cached Context?
The module uses the original context (not cached) when calling sudo on contracts. This ensures:- State consistency between staking module and subscribed contracts
- If the original transaction fails, contract state changes are also rolled back
- Contracts can directly impact the success/failure of staking operations
Performance Considerations
Performance depends on:- Number of subscribed contracts per hook: Each hook calls sudo on all subscribed contracts
- Contract complexity: More complex contract logic increases execution time
- Staking operation frequency: More frequent operations mean more hook executions