Rivellum Governance System
Overview
The Rivellum governance system provides on-chain, council-based governance for managing dynamic protocol parameters. This prototype enables a council of trusted addresses to propose, vote on, and execute changes to node configuration parameters through Move smart contracts.
Architecture
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā On-Chain (Move Contract) ā
ā RivellumGovernance Module ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā GovernanceConfig ProposalRegistry ā
ā āā council_members āā proposals[] ā
ā āā next_proposal_id ā āā id, proposer ā
ā āā min_quorum ā āā param_key, new_value ā
ā āā min_approval_bps ā āā yes_votes, no_votes ā
ā ā āā executed ā
ā ā ā
ā Entry Functions: Events: ā
ā āā init_council() āā ProposalCreatedEvent ā
ā āā create_proposal() āā VoteCastEvent ā
ā āā vote() āā ProposalExecutedEvent ā
ā āā execute() ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā Polling (OnChainConfigWatcher)
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Off-Chain (Rust Node) ā
ā rivellum-governance crate ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā OnChainConfigWatcher ā
ā āā Poll interval: 5s ā
ā āā Fetch executed proposals ā
ā āā Decode param_key + new_value ā
ā āā Apply DynamicConfigPatch ā
ā ā
ā DynamicConfigPatch ā
ā āā GasPrice(u64) ā
ā āā MaxBatchSize(u64) ā
ā āā BridgeMaxOutbound(u64) ā
ā āā ... (extensible) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā Apply to runtime config
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā DynamicConfig ā
ā (Runtime Parameters) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ⢠gas_price ⢠max_batch_size ā
ā ⢠bridge_max_outbound ⢠epoch_duration ā
ā ⢠min_stake_amount ⢠... (future params) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Key Features
1. Council-Based Governance
- Council Members: Static list of authorized addresses
- Proposal Creation: Only council members can propose
- Voting: One vote per council member per proposal
- Execution: Any council member can trigger execution of passed proposals
2. Proposal Lifecycle
1. CREATE ā 2. VOTE ā 3. EXECUTE
(council) (council) (council or auto)
āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāāā
ā Pending ā -> ā Active ā -> ā Passed ā -> ā Executed ā
āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāāā
ā ā
āāāāāāāāāāāāāāāā“āāāāā> Failed
(quorum not met)
States:
- Pending: Proposal created, waiting for voting period start
- Active: Voting period in progress
- Passed: Voting ended, quorum met, approval threshold met
- Failed: Voting ended, but quorum or approval not met
- Executed: Proposal passed and executed, parameter changed
3. Parameter Encoding
Parameters are encoded as JSON for flexibility:
{
"param_key": "gas_price",
"new_value": "200"
}
Supported Parameters:
| Parameter Key | Type | Description | Example Value |
|---|---|---|---|
gas_price | u64 | Gas price multiplier | 100 |
max_batch_size | u64 | Maximum intents per batch | 1000 |
bridge_max_outbound | u64 | Max outbound bridge transfers | 100 |
min_stake_amount | u128 | Minimum stake for validators | 1000000 |
epoch_duration | u64 | Epoch duration in blocks | 1000 |
4. Quorum and Approval
Quorum: Minimum number of votes required for a proposal to be valid.
- Default: 3 votes
- Configurable in
GovernanceConfig
Approval Threshold: Minimum percentage of yes votes required.
- Default: 50% (5000 basis points)
- Formula:
approval_bps = (yes_votes * 10000) / total_votes - Example: 5 yes, 1 no ā 83.3% approval (8333 bps)
Usage Guide
Step 1: Initialize Governance
Deploy the Move module and initialize the council:
# Using wallet CLI (once RPC is implemented)
rivellum-wallet governance-init \
--council 0xmember1,0xmember2,0xmember3 \
--min-quorum 3 \
--min-approval-bps 5000
Or programmatically via SDK:
import { RivellumClient } from '@rivellum/sdk';
const client = new RivellumClient('http://localhost:8080');
// This would call the init_council entry function
// (Implementation depends on contract deployment flow)
Step 2: Create a Proposal
Council members can propose parameter changes:
# Using wallet CLI
rivellum-wallet governance-propose \
--governance 0xgovernance_address \
--param-key gas_price \
--value 200 \
--voting-period-ms 86400000 \
--from alice
Via SDK:
const result = await client.createProposal({
governanceAddress: '0x1234...',
paramKey: 'gas_price',
newValue: 200,
votingPeriodMs: 86400000, // 24 hours
from: '0xalice...',
});
console.log('Proposal created:', result.transactionId);
Step 3: Vote on Proposal
Council members vote during the voting period:
# Vote YES
rivellum-wallet governance-vote \
--governance 0xgovernance_address \
--proposal-id 1 \
--support true \
--from bob
# Vote NO
rivellum-wallet governance-vote \
--governance 0xgovernance_address \
--proposal-id 1 \
--support false \
--from charlie
Via SDK:
// Yes vote
await client.vote({
governanceAddress: '0x1234...',
proposalId: 1,
support: true,
from: '0xbob...',
});
// No vote
await client.vote({
governanceAddress: '0x1234...',
proposalId: 1,
support: false,
from: '0xcharlie...',
});
Step 4: Execute Proposal
After voting ends and proposal passes, execute it:
rivellum-wallet governance-execute \
--governance 0xgovernance_address \
--proposal-id 1 \
--from dave
Via SDK:
await client.executeProposal('0x1234...', 1, '0xdave...');
Step 5: Observe Parameter Change
The OnChainConfigWatcher polls the governance contract every 5 seconds:
[INFO] OnChainConfigWatcher: Polling governance contract
[INFO] Applied governance patch: gas_price=200
[INFO] DynamicConfig updated: gas_price changed from 100 to 200
The new gas_price is now active in the node!
Configuration
Add to config/default.toml:
[governance]
# Enable governance watcher
enabled = true
# Governance contract address (deploy contract first)
governance_contract_address = "0x1234567890abcdef..."
# Poll interval in milliseconds
poll_interval_ms = 5000
# Minimum quorum (number of votes)
min_quorum = 3
# Minimum approval in basis points (5000 = 50%)
min_approval_bps = 5000
Move Contract API
Entry Functions
init_council
public entry fun init_council(
admin: &signer,
members: vector<address>,
min_quorum: u64,
min_approval_bps: u64,
)
Initialize governance with council members. Can only be called once.
create_proposal
public entry fun create_proposal(
proposer: &signer,
governance_addr: address,
param_key: vector<u8>,
new_value: vector<u8>,
voting_period_ms: u64,
)
Create a new proposal. Only council members can call.
vote
public entry fun vote(
voter: &signer,
governance_addr: address,
proposal_id: u64,
support: bool,
)
Cast a vote. Only council members can call. One vote per address per proposal.
execute
public entry fun execute(
executor: &signer,
governance_addr: address,
proposal_id: u64,
)
Execute a passed proposal. Checks quorum and approval thresholds.
View Functions
get_all_proposals
public fun get_all_proposals(governance_addr: address): vector<Proposal>
Returns all proposals.
get_proposal
public fun get_proposal(governance_addr: address, proposal_id: u64): Proposal
Get a specific proposal by ID.
get_council_members
public fun get_council_members(governance_addr: address): vector<address>
Get list of council members.
has_proposal_passed
public fun has_proposal_passed(
governance_addr: address,
proposal_id: u64
): bool
Check if a proposal has passed (met quorum and approval).
SDK API
import { RivellumClient, Proposal, ProposalStatus } from '@rivellum/sdk';
const client = new RivellumClient('http://localhost:8080');
// Create proposal
await client.createProposal({
governanceAddress: '0x...',
paramKey: 'gas_price',
newValue: 200,
votingPeriodMs: 86400000,
});
// Vote
await client.vote({
governanceAddress: '0x...',
proposalId: 1,
support: true,
from: '0xvoter...',
});
// Execute
await client.executeProposal('0x...', 1);
// List all proposals
const proposals: Proposal[] = await client.listProposals('0x...');
// Get specific proposal
const proposal: Proposal = await client.getProposal('0x...', 1);
Security Considerations
Access Control
- Council-only: Only pre-authorized addresses can propose and vote
- One vote per address: Prevents vote spamming
- Execution checks: Validates quorum and approval before execution
Parameter Validation
- Type checking: Rust decoder validates parameter types
- Range validation: (TODO) Add min/max bounds per parameter
- Unknown parameters: Rejected with error
Time Handling
- Voting periods: (Prototype uses simplified time - TODO: integrate with timestamp oracle)
- Execution delay: (TODO) Add timelock delay between passing and execution
Attack Vectors
| Attack | Mitigation |
|---|---|
| Council capture | Static council in prototype; future: token-weighted voting |
| Parameter griefing | Council-only + quorum requirement |
| Execution DOS | Anyone can execute after passing (incentivized) |
| Time manipulation | TODO: Use trusted timestamp oracle |
Limitations (Prototype)
This is a prototype implementation with the following limitations:
Current Limitations
- Static Council: Council membership cannot be changed after initialization
- No Token Voting: No stake-weighted or token-based voting
- No Delegation: Council members cannot delegate votes
- Simplified Time: Time checks are simplified (voting period not strictly enforced)
- No Timelock: Passed proposals can be executed immediately
- Limited Parameters: Only 5 parameters supported currently
- Stub VM Integration:
OnChainConfigWatcherpolling is stubbed (TODO: read from VM)
Not Implemented
- ā Proposal cancellation
- ā Vote changes (voting is final)
- ā Emergency pause mechanism
- ā Proposal dependencies
- ā Multi-sig execution requirements
- ā Stake slashing for malicious proposals
- ā Off-chain signature aggregation
- ā Governance treasury
Roadmap
v2.0 (Future Enhancements)
- Token-weighted voting
- Delegation support
- Dynamic council membership (add/remove via governance)
- Timelock delays
- Proposal dependencies
- Emergency pause/unpause
- Governance treasury
- Optimistic governance (instant execution with fraud proofs)
- Vote encryption (private voting)
Integration Roadmap
- Move contract implementation
- Rust governance crate
- SDK governance methods
- Wallet CLI commands (WIP)
- Explorer UI
- RPC endpoints for contract interaction
- VM read/write integration
- Event indexing
- Proposal history tracking
Testing
Unit Tests
# Rust tests
cargo test -p rivellum-governance
# SDK tests
cd sdk
npm test -- governance.test.ts
Integration Test Example
#[tokio::test]
async fn test_full_governance_flow() {
// 1. Initialize council
let council = vec![address1, address2, address3];
init_council(&admin, council, 2, 5000).await?;
// 2. Create proposal
let proposal_id = create_proposal(
&member1,
gov_addr,
b"gas_price",
b"200",
86400000,
).await?;
// 3. Vote
vote(&member1, gov_addr, proposal_id, true).await?;
vote(&member2, gov_addr, proposal_id, true).await?;
// 4. Execute
execute(&member3, gov_addr, proposal_id).await?;
// 5. Verify config updated
assert_eq!(get_gas_price(), 200);
}
Troubleshooting
Common Issues
Issue: Proposal creation fails with "Not council member"
- Fix: Ensure the proposer address is in the council member list
Issue: Vote fails with "Already voted"
- Fix: Each address can only vote once per proposal
Issue: Execution fails with "Quorum not met"
- Fix: Ensure enough council members have voted (min_quorum)
Issue: Execution fails with "Approval not met"
- Fix: Ensure yes_votes/(yes_votes+no_votes) >= min_approval_bps/10000
Issue: Parameter change not applied
- Fix: Check
OnChainConfigWatcheris running and governance is enabled in config
Debugging
Enable debug logs:
RUST_LOG=rivellum_governance=debug rivellum-node --config config/default.toml
View governance events:
curl http://localhost:8080/governance/events
Examples
Example 1: Change Gas Price
# Create proposal
rivellum-wallet governance-propose \
--governance 0xgov \
--param-key gas_price \
--value 150
# Vote (as 3 council members)
rivellum-wallet governance-vote --proposal-id 1 --support true --from alice
rivellum-wallet governance-vote --proposal-id 1 --support true --from bob
rivellum-wallet governance-vote --proposal-id 1 --support true --from charlie
# Execute
rivellum-wallet governance-execute --proposal-id 1
# Observe: gas_price changed from 100 to 150
Example 2: Increase Batch Size
# Propose larger batch size
rivellum-wallet governance-propose \
--governance 0xgov \
--param-key max_batch_size \
--value 2000
# Vote and execute...
# Result: max_batch_size changed from 1000 to 2000
References
- Move Governance Contract
- Rust Governance Crate
- SDK Documentation
- Architecture Overview
- Security Implementation
Status: Prototype (Phase 15) Last Updated: November 2025 Maintainer: Rivellum Core Team