Rivellum

Rivellum Portal

Checking...
testnet

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 KeyTypeDescriptionExample Value
gas_priceu64Gas price multiplier100
max_batch_sizeu64Maximum intents per batch1000
bridge_max_outboundu64Max outbound bridge transfers100
min_stake_amountu128Minimum stake for validators1000000
epoch_durationu64Epoch duration in blocks1000

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

AttackMitigation
Council captureStatic council in prototype; future: token-weighted voting
Parameter griefingCouncil-only + quorum requirement
Execution DOSAnyone can execute after passing (incentivized)
Time manipulationTODO: Use trusted timestamp oracle

Limitations (Prototype)

This is a prototype implementation with the following limitations:

Current Limitations

  1. Static Council: Council membership cannot be changed after initialization
  2. No Token Voting: No stake-weighted or token-based voting
  3. No Delegation: Council members cannot delegate votes
  4. Simplified Time: Time checks are simplified (voting period not strictly enforced)
  5. No Timelock: Passed proposals can be executed immediately
  6. Limited Parameters: Only 5 parameters supported currently
  7. Stub VM Integration: OnChainConfigWatcher polling 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 OnChainConfigWatcher is 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


Status: Prototype (Phase 15) Last Updated: November 2025 Maintainer: Rivellum Core Team