Skip to content
Cosmopediaby Unity Nodes
Documentationinformalsystems/auditsinformalsystems/audits › AnomaView on informalsystems/audits ↗

2024-12-11 Namada Governance & PGF

Security Audit Report

Namada Governance & PGF

Authors: Ivan Gavran, Ivan Golubovic, Manuel Bravo, Tatjana Kirda

Last revised 11 December, 2024 © 2024 Informal Systems Namada Governance & PGF

Table of Contents Audit Overview ............................................................................................................ 1 The Project 1 Audit Timeline 1 Conclusion 1 Audit Dashboard ......................................................................................................... 2 Target Summary 2 Engagement Summary 2 Severity Summary 2 System Overview......................................................................................................... 3 Governance 3 Public Goods Funding (PGF) 4 Threat Analysis ............................................................................................................ 5 Safety Properties 5 Liveness Properties 15 Findings ..................................................................................................................... 18 Malicious user could prevent honest users from installing valid governance proposals by not increasing the governance counter 20 A governance proposal may prevent creation of future proposals 22 Adding an incomplete proposal may cause panic in finalize_block 26 Proposal type key modification 29 Proposal funds key modification 31 Some checks for governance-vetted updates are justified and beneficial 33 Voting starting epoch starts only strictly after the proposal epoch 34 There's no need to deal with multiple governance proposals being installed in parallel 35 A wrong check for max_proposal_period 36 Max Proposal Latency Parameter Not Included in the is_parameter_key function 37 Some VP-functions are rendered trivial by their reachability conditions 39 Miscellaneous Code Findings 41 Disclaimer.................................................................................................................. 43 Appendix: Vulnerability Classification ..................................................................... 44 © 2024 Informal Systems Namada Governance & PGF

Impact Score 44 Exploitability Score 44 Severity Score 45 © 2024 Informal Systems Namada Governance & PGF

Audit Overview The Project In November and December 2024, the Anoma Foundation engaged Informal Systems to work on a partnership and conduct a security audit of the following items in Namada’s software:

  1. Governance crate including PGF (Public Goods Funding): namada/crates/governance
  2. Governance and PGF transactions: a. namada/wasm/tx_update_steward_commission b. namada/wasm/tx_resign_steward c. namada/wasm/tx_init_proposal
  3. Related entrypoints in namada/crates/apps_lib/src/cli/client.rs as well as finalize_block handler in namada/crates/node/src/shell/finalize_block.rs. The audit was performed from November 18th, 2024 to December 9th, 2024 by the following personnel: • Ivan Gavran • Ivan Golubovic • Manuel Bravo • Tatjana Kirda

Relevant Code Commits The audited code was from:

• commit hash 207fe50a8fab63e0c84a0e6b4a4ef31de7f4ce98 (tag v0.45.1 ).

Audit Timeline • 19.11.2024. Kick-off meeting and code walkthrough by the Namada team. • 25.11.2024. First weekly sync meeting. The auditing team has shared issues that surfaced. • 02.12.2024. Second weekly sync meeting. The auditing team has shared issues that surfaced. • 09.12.2024. Closure meeting. The draft report was shared, and all findings surfaced were shared.

Conclusion After conducting a thorough review of the project, we found it to be carefully designed and generally well- implemented. The current Namada’s deployment only allows a set of whitelisted transactions to be executed. Our analysis concludes that there are no major issues in the governance and PGF modules under such conditions. We have also considered the case when Namada executes arbitrary transactions, as this is the roadmap. Our analysis concludes that the code is not ready for such a challenging deployment: we found some problems that if left unattended, would violate both safety and liveness properties (more on them in the Findings section).

Audit Overview 1 © 2024 Informal Systems Namada Governance & PGF

Audit Dashboard Target Summary • Type: Protocol and Implementation • Platform: Rust • Artifacts audited over the fixed 207fe50 commit hash. • namada/crates/governance • namada/wasm/tx_update_steward_commission • namada/wasm/tx_resign_steward • namada/wasm/tx_init_proposal • Related entrypoints in namada/crates/apps_lib/src/cli/client.rs as well as finalize_block handler in namada/crates/node/src/shell/finalize_block.rs.

Engagement Summary • Dates: 18.11.2024. - 09.12.2024. • Method: Manual code review, protocol analysis

Severity Summary Finding Severity #

Critical 5*

High 0

Medium 1

Low 1

Informational 5

Total 12

[*] - only for arbitrary transactions

Audit Dashboard 2 © 2024 Informal Systems Namada Governance & PGF

System Overview Governance The Namada governance mechanism serves at least two purposes: to allow users and operators to upgrade the protocol dynamically and to enable social coordination. Any user account can submit a governance proposal on-chain and optionally some code for the protocol to execute if the proposal is accepted. A proposal without code is of type Default and a proposal with code is of type DefaultWithWasm . Validators and delegators with active bonds can vote on the proposals with voting power proportional to their bonded token amount. The execution of a governance proposal is valid if: • The proposal id is unique: there is no previously installed proposal with the same proposal id • It should write into storage all the required fields encoding the proposal’s id into the fields' keys. The required fields are: content , author , committing_epoch , voting_start_epoch , voting_end_epoch , and activation_epoch . • If the proposal includes a non-empty data field, then its content depends on the proposal type: • content must satisfy the following: • size(content) <= max_proposal_content_size • author must satisfy the following: • It is an existing Namada user • Its identity can be verified • voting_start_epoch must satisfy the following: • voting_start_epoch >= current_epoch • voting_end_epoch - voting_start_epoch >= min_proposal_voting_period • activation_epoch - voting_start_epoch <= max_proposal_period • voting_start_epoch - current_epoch <= max_proposal_latency • voting_end_epoch must satisfy the following: • voting_end_epoch >= current_epoch • voting_end_epoch - voting_start_epoch >= min_proposal_voting_period • activation_epoch - voting_end_epoch >= min_proposal_grace_epochs • activation_epoch must satisfy the following: • activation_epoch >= current_epoch • activation_epoch - voting_start_epoch <= max_proposal_period • activation_epoch - voting_end_epoch >= min_proposal_grace_epochs • The user that submits the proposal transfers at least min_proposal_fund to the governance internal address. • Only a proposal of type DefaultWithWasm can write the PROPOSAL_CODE key. • The length of the WASM code written to the proposal code key by a DefaultWithWasm proposal must not exceed the maximum allowed size defined by max_proposal_length

The execution of a vote transaction is valid if: • It votes for an existing proposal • The voter is either an existing validator or delegator with active bonds at the current epoch.

System Overview 3 © 2024 Informal Systems Namada Governance & PGF

• The vote is being executed within the voting period • If the voter is a validator, the vote is being executed within the 2/3 of the voting period

Public Goods Funding (PGF) Namada funds public goods by regularly minting NAM tokens into an on-chain PGF account and allocating certain amounts for certain entities. The inflation rate is a constant parameter pgf_inflation_rate that is mutable by governance. PGF builds on top of governance as follows. The entities that receive such funding are approved either via governance (via a PGFPayment proposal) or by a multi-signature account known as a "public goods steward". Stewards must also be elected with a governance proposal (via a PGFSteward proposal) and are meant to be trusted authorities for responsibly identifying good candidates to receive public goods funding. As such, a steward may submit a funding proposal that does not need to receive any positive votes to be passed. There is also a mechanism in place to veto stewards. In exchange for their work, stewards receive rewards at the end of each epoch. Since in most cases it is assumed that a steward is a multi-signature account, Namada allows stewards to distribute their rewards among multiple accounts. Specific validity conditions for PGF governance proposals:

• PGFSteward proposals must also ensure: • Add at most one steward • Include no more than MAX_PGF_ACTIONS actions • If a steward is being added, only he can be the author • Uniqueness of addresses in actions • PGFPayment proposals must also ensure: • Uniqueness of target for each action type: add/remove continuous, add retro • No continuous target is removed and added within the same transaction Finally, stewards can resign or change their reward distribution at will by submitting the appropriate transactions. A reward distribution is valid if it does not exceed the limit of addresses ( REWARD_DISTRIBUTION_LIMIT ), each distribution is between 0 and 1, and the total sum is less or equal to 1.

System Overview 4 © 2024 Informal Systems Namada Governance & PGF

Threat Analysis In our threat analysis, we start by defining a set of properties required for the correctness of the governance and PGF modules in Namada. We separate them into safety and liveness properties. For each property, we define one or more threats. We then analyze them individually to see if they could be violated, resulting in the findings presented in the Findings section. Note that each threat is marked with one of the following: • Indicates that the threat does not materialize, and no vulnerabilities have been identified in the current implementation, or • Indicates that the threat is valid, and vulnerabilities have been identified, or • / Indicates that vulnerabilities exist, but they can only be exploited under the assumption arbitrary transactions are used. Scenarios restricted to whitelisted transactions do not result in exploitable conditions. Let us first start with a set of definitions: • A proposal can be any of the following states: • Installed: a transaction with the proposal is included in a decided block and accepted by the relevant validity predicates, i.e., the proposal is persisted in Namada’s state. • Accepted: it has enough valid votes by the end of the voting period. • Executed: Namada has executed it during finalize block. • The definitions of validity, e.g., proposal and vote validity, are in System Overview.

Safety Properties

  1. A governance proposal is accepted only if it has enough valid votes Threat 1.1: A malicious user sends messages in the name of other users and Namada does not authenticate votes properly.

Conclusion: The threat does not hold. The is_valid_vote_key function ensures that voters are authorized by verifying the voter specified in GovAction::VoteProposal . Each vote key is tied to the correct validator and voter addresses, and the function ensures that only accounts in the verifiers set are permitted to participate in governance actions.

Proposal authorship and voter eligibility are validated through GovAction::InitProposal and GovAction::VoteProposal , respectively. Additionally, pre-storage checks in is_valid_author confirm that no proposal with the same ID and author already exists in the storage.

Votes are stored with a unique key structure: [ADDRESS, "proposal", <proposal_id>, "vote", <validator_address>, <voter_address>] , which ensures that each voter can cast only one vote per proposal. If a voter submits multiple votes, subsequent votes overwrite the initial one. Unauthorized votes are further prevented by signature verification, which ties transactions to the voter’s address. Scenarios where users attempt to create duplicate proposals, impersonate voters, or vote multiple times on the same proposal are all mitigated by these safeguards. Transactions from unauthorized users are rejected due to the governance VP’s verifiers set checks.

Threat 1.2: Namada does not check for duplicate votes. Duplicate votes may be sent maliciously to exploit such a vulnerability or non-maliciously by a voter who does not see its vote processed after a while.

Conclusion: The threat does not hold. Votes are stored using a unique key structure: [ADDRESS, "proposal", <proposal_id>, "vote", <validator_address>, <voter_address>] . This ensures that duplicate votes overwrite any existing vote for the same voter, proposal, and validator.

Threat Analysis 5 © 2024 Informal Systems Namada Governance & PGF

During tallying in finalize_block , votes are processed into runtime HashMaps for validators and delegators. These structures inherently prevent duplicates because HashMap::insert replaces any previous entry with the same key. This guarantees that only the latest vote is considered in the tally. Voting power computation at the proposal's activation epoch further improves defense against double counting. Stake changes or redelegations during the voting period are resolved by considering only the bond state at the activation epoch, ensuring accurate voting power attribution. Scenarios analyzed include voters submitting multiple votes for the same proposal, changing their vote during the voting period, or redelegating and voting again. These scenarios are consistently resolved due to the key structure, HashMap deduplication, and the final bond checks. While the storage may hold multiple votes in some cases, tallying logic ensures only valid, non-duplicate votes are considered. Threat 1.3: Namada mixes the votes of different proposals.

Conclusion: The threat does not hold. Votes are stored with a unique key structure tied to the proposal_id , ensuring strict separation between votes for different proposals. Each proposal establishes its own namespace for votes.

The get_proposal_votes function retrieves votes explicitly associated with a given proposal_id . This ensures that only votes relevant to the targeted proposal are considered during tallying. The finalize_block logic isolates proposals by processing them individually, fetching only those active in the current epoch using load_proposals .

Scenarios analyzed include delegators voting multiple times on the same proposal, votes being added to a different proposal, and proposals with overlapping activation epochs. These scenarios are consistently resolved by the proposal_id isolation in storage, targeted retrieval of votes, and scoped execution during finalize_block .

Threat 1.4: Namada counts invalid votes as valid, e.g., votes that were installed before or after the proposals' voting period. Conclusion: The threat does not hold in the current implementation. Votes are validated through the is_valid_voting_window function, which ensures that only votes cast within the designated start and end epochs of the voting period are considered. This safeguard effectively filters out votes that fall outside the allowed time frame.

Validator votes are further validated using the is_active_validator check, ensuring that only votes from active validators at the time of the proposal’s activation epoch are included in the tally. Delegator votes tied to inactive or jailed validators are also excluded during this process, as their validity depends on the status of the associated validator. Scenarios analyzed include votes cast outside the voting period, votes from validators who become inactive or jailed, and votes from reactivated validators. Threat 1.5: Vote overwriting is not correctly implemented such that the last valid vote does not overwrite previous ones. Conclusion: The threat does not hold in the current governance module implementation. The compute_proposal_result function ensures that delegators' votes are correctly accounted for, even when they differ from their validator's votes.

The validator_vote_is_same_side logic compares delegator votes with their validator's vote. If they differ, the delegator's stake is subtracted from the validator's tally and added to the tally associated with the delegator's vote. This mechanism ensures that delegators' voting power is accurately reflected in the final tally. The tallying process employs a runtime HashMap to dynamically adjust voting power, effectively managing situations where delegators vote differently from their validator.

Threat Analysis 6 © 2024 Informal Systems Namada Governance & PGF

Scenarios analyzed include delegators voting in alignment or opposition to their validator's vote and cases where delegators' votes differ among themselves. In all cases, the logic correctly adjusts the tallies to reflect the voting power of individual delegators, ensuring an accurate and fair tallying process. Threat 1.6: The voting power computation is incorrect. Conclusion: The threat does not hold. The computation of voting power for both validators and delegators is scoped to the activation epoch, ensuring accurate and fair tallies.

Validator voting power is retrieved using the PoS::read_validator_stake function, which dynamically fetches the stake at the activation epoch. This guarantees that only valid and active voting power is included.

Delegator voting power is calculated using the PoS::bond_amount function. This function respects redelegations, unbonds, and stake changes, ensuring the tally reflects the state as of the activation epoch without including outdated or double-counted values. All calculations are scoped to the activation epoch. This prevents discrepancies caused by stake adjustments or redelegations during the voting period. Additionally, inactive or jailed validators are excluded from the tally through the is_active_validator function, maintaining the integrity of the voting process.

Scenarios analyzed include changes to validator or delegator stakes, redelegations, and validator status changes during the voting period. In all cases, the tallying logic correctly reflects the voting power at the activation epoch, ensuring consistency and accuracy. Threat 1.7: Validators can vote in the last third of the voting period. Conclusion: The threat does not hold. The governance VP enforces strict validation on the voting period for validators, ensuring that their votes are only considered if submitted within the first two-thirds of the voting period.

The function is_valid_validator_voting_period , called during vote validation, ensures compliance with the rule. It verifies that the current epoch is less than the start epoch plus two-thirds of the total voting period length ( end_epoch - start_epoch ). If this condition is not met, the validator's vote is rejected.

In finalize_block , when tallying votes during the activation epoch, only votes validated by the VP are included in the computation. This mechanism ensures that votes cast in the last third of the voting period are excluded from the tally.

  1. An accepted governance proposal is executed exactly once at the beginning of its activation epoch Threat 2.1: Malicious users can mutate proposals' activation epoch such that Namada executes them more than once. This assumes that Namada does not garbage collect them. Conclusion: The threat does not hold in the current implementation. The VP enforces validation to ensure that a pre-existing activation epoch key associated with a proposal ID cannot be overwritten. Transactions attempting to modify the activation epoch of an existing proposal fail validation.

Proposals are uniquely identified by their proposal_id , with the activation epoch and proposal ID tightly linked in storage via the proposal commit key. This ensures proposals are

Excerpt (19998 of 94219 characters). Read the whole page on informalsystems/audits ↗