Testnet #4: Shielded Staking Is Here

In our last update, we announced our first testnet, codenamed “Valetudo”, with an implementation of our MVP1 milestone: multi-asset shielded transactions, and a basic, fully-trusting light wallet protocol that does client-side scanning of the private chain state.

Today, we're excited to announce that shielded staking has come to the Cosmos, with our fourth testnet, codenamed “Thelxinoë”, after the fourth-smallest named moon of Jupiter.

The Thelxinoë testnet brings us partway to our MVP2 milestone: shielded staking and delegation mechanics, providing privacy to delegators while retaining accountability for validators. This is a key part of our vision of a truly private proof-of-stake network, and in this post, we'll walk through the challenges involved in shielded staking, how our new staking design addresses them, and give an update on our progress and next steps.

We're aiming to move to a weekly testnet cadence, so stay tuned for more updates!

The Challenge of Shielded Staking Rewards

Integrating privacy with proof of stake is a key goal for Penumbra. Without private staking, holders of the staking token would have to choose between helping to secure the network, participate in governance, and receive staking rewards on the one hand, and maintaining privacy on the other hand. This would put the project's goal, providing a shielded pool and DEX for the entire Cosmos, in conflict with the incentives of its stakeholders.

It's also critical that the staking system provide native, permissionless, non-custodial delegation, as in the design of the Cosmos SDK. These properties should be considered table stakes for new proof of stake deployments, but are unfortunately missing from many, most notably Eth2. They're important to prevent winners-take-all dynamics by allowing all participants equal access to staking, and to minimize the risk of re-centralization when these missing features are inevitably provided by third parties.

We want a system that provides privacy to delegators, but accountability for the validators who participate in consensus. However, the key challenge in building such a system is how to handle staking rewards. Unlike a transparent chain, where all delegations are public, and the chain can simply credit delegators' accounts with staking rewards, if the delegations are private, the chain has no way to know the amount and duration of each delegation.

To sidestep this problem, Penumbra uses a new staking mechanism that eliminates staking rewards for delegators, while preserving the economic incentives for staking.

How is this possible? Rather than treating "unbonded" and "bonded" as different states of a single staking token, we record bonded stake using delegation tokens, which represent a share of a particular validator's delegation pool, similar to the way that LP shares represent shares of a liquidity pool. The exchange rate between staking tokens and delegation tokens prices in what would be a staking reward in other systems.

Delegation Tokens

Each validator has a delegation pool, containing all the stake bonded to that validator's behavior. The size of the delegation pool for each validator is part of the public chain state, and determines the validator's voting power.

Delegations are recorded using delegation tokens, which represent a share of a validator's delegation pool. These can be thought of as a first-class staking derivative or liquid staking token, though we don't use those terms to avoid carrying over assumptions from other system designs; we think "delegation token" is a better term.

Instead of tracking staking rewards directly, the chain tracks an exchange rate between the staking token and a validator's delegation token. This exchange rate prices in the cumulative staking rewards since genesis. When a user delegates stake to a validator, they exchange their stake for delegation tokens at the current rate. This discounts their share of the delegation pool for the rewards from genesis up to their delegation, which they didn't earn. Later, when they undelegate stake from that validator, they exchange their delegation tokens for stake, and get credited for the cumulative staking rewards since genesis. Since they discounted their share on delegation, this nets out to the staking reward exactly over the period they delegated, without requiring the chain to track that information!

How does this provide delegation privacy? Because all delegations are fungible with each other, they can be recorded with a single delegation token, and this token is just another asset recorded in Penumbra's multi-asset shielded pool. To ensure that amounts are not revealed during (un)delegations, we'll use flow encryption to shield the amounts in each (un)delegation transaction, revealing only the aggregate flows into and out of each validator's delegation pool on epoch boundaries.

Validator Commission via Funding Streams

The exchange rate mechanism described above works for delegators, but we also need a way to compensate validators for the cost of providing security to the network. In the Cosmos SDK, this is done using a commission parameter, which determines a portion of the staking reward sent to the validator operator.

However, this mechanism is somewhat limiting, since it only allows commission to be distributed directly to validator operators. While some validators use their commission to fund public goods for the ecosystem, that funding is generally opaque and off-chain. It would be more flexible if validators could dedicate some portion of their commission to a DAO or other public goods mechanism transparently and non-custodially.

To enable this, our design generalizes a single validator commission to a set of funding streams, each with their own address. Validators can declare funding streams to addresses they don't directly control, and delegators can factor those funding streams into their decisionmaking.

To exercise this functionality, we've added all of the addresses posted in the #testnet-faucet channel on Discord during the previous three testnets as funding streams in the current testnet, so if you already posted your address, you should see your address receive rewards.

Slashing, Unbonding, and Quarantine

The exchange rate mechanism neatly solves the problem of shielding staking rewards, and it also neatly handles slashing: to slash a malicious validator's delegation pool, the chain can adjust the exchange rate to price in the slashing penalty. The last essential component is an unbonding period, so that a malicious validator cannot misbehave, then withdraw their stake before they're slashed.

This also poses a challenge for Penumbra, since all transactions are private by default and all value is privately recorded in a shielded pool. Let's look at an undelegation transaction from the point of view of a user, and from the point of view of the chain:

 User's View           Chain's View      
┌──────────────────┐ ┌──────────────────┐
│┌────────────────┐│ │┌────────────────┐│
││Spend           ││ ││Spend           ││
││                ││ ││                ││
││+100delegation_v││ ││+??? (shielded) ││
│├────────────────┤│ │├────────────────┤│
││Undelegate      ││ ││Undelegate      ││
││                ││ ││                ││
││Validator: v    ││ ││Validator: v    ││
││-100delegation_v││ ││-??? (shielded) ││
││+120penumbra    ││ ││+??? (shielded) ││
│├────────────────┤│ │├────────────────┤│
││Output          ││ ││Output          ││
││                ││ ││                ││
││-119.99penumbra ││ ││-??? (shielded) ││
│├────────────────┤│ │├────────────────┤│
││Fee             ││ ││Fee             ││
││                ││ ││                ││
││-0.01penumbra   ││ ││-0.01penumbra   ││
│└────────────────┘│ │└────────────────┘│
│                  │ │                  │
│Net: 0            │ │Net: 0 (zk proved)│
└──────────────────┘ └──────────────────┘

The transaction spends some delegations out of the shielded pool (unlinkably from any previous transaction), exchanges them for staking tokens at the current exchange rate (using flow encryption to shield the amounts in this specific transaction), and then creates a new output note in the shielded pool.

We want the user to be able to submit the transaction to the chain, and for the chain to record that it occurred, but still preserve the ability to slash the delegation until the end of the unbonding period. Because spends and outputs completely unlinkable, if the chain adds the output with the staking tokens to the shielded pool, it can never even identify them to slash them.

Instead, we hold any outputs from an undelegation transaction in a special quarantine state, along with the nullifiers for the input notes that prevent double-spending. If the validator is slashed before the end of the unbonding period, we discard the quarantined notes, and remove the nullifiers from the spent set. This unwinds the effect of the undelegation and allows the user to resubmit a new conversion of their delegation tokens with the post-slashing exchange rate.

This rule is simple, but it turns out to also enable "instant" redelegations, at least up to slashing. A redelegation just combines undelegation and delegation actions in a single transaction:

 User's View             Chain's View      
┌────────────────────┐ ┌──────────────────┐
│┌──────────────────┐│ │┌────────────────┐│
││Spend             ││ ││Spend           ││
││                  ││ ││                ││
││+100delegation_v  ││ ││+??? (shielded) ││
│├──────────────────┤│ │├────────────────┤│
││Undelegate        ││ ││Undelegate      ││
││                  ││ ││                ││
││Validator: v      ││ ││Validator: v    ││
││-100delegation_v  ││ ││-??? (shielded) ││
││+120penumbra      ││ ││+??? (shielded) ││
│├──────────────────┤│ │├────────────────┤│
││Delegate          ││ ││Delegate        ││
││                  ││ ││                ││
││Validator: w      ││ ││Validator: w    ││
││-119.99penumbra   ││ ││-??? (shielded) ││
││+97.84delegation_w││ ││+??? (shielded) ││
│├──────────────────┤│ │├────────────────┤│
││Output            ││ ││Output          ││
││                  ││ ││                ││
││-97.84delegation_w││ ││-??? (shielded) ││
│├──────────────────┤│ │├────────────────┤│
││Fee               ││ ││Fee             ││
││                  ││ ││                ││
││-0.01penumbra     ││ ││-0.01penumbra   ││
│└──────────────────┘│ │└────────────────┘│
│Net: 0              │ │Net: 0 (zk proved)│
└────────────────────┘ └──────────────────┘

Here, even though the present value of the two delegation tokens is the same, the amounts of each token might be slightly different, since the exchange rate prices in cumulative rewards, and the validators may have different commissions.

Now, what happens when we apply the quarantine rule? The transaction is finalized, and the output with the new delegation tokens is quarantined until the end of the unbonding period. But the exchange rate is locked in when the transaction is submitted, not when the unbonding period ends. So, in the optimistic case without slashing, the effect is as if the user had "instantly" redelegated their stake, even though they weren't able to access the new delegation tokens during the unbonding period. On the other hand, we still protect against rug-pulls from misbehaving validators, since it's not possible to immediately redelegate to escape slashing.

Status and Next Steps

Thelxinoë implements the core economic mechanism for our staking design: per-validator delegation pools represented by delegation tokens, with the ability to delegate and undelegate stake, and accounting for the staking economics as part of the chain state.

This is most of the way to our MVP2 milestone. What's left to implement is:

  • moving from immediate undelegation to an unbonding period, with correct tracking of quarantined undelegations;
  • the complete validator state machine, so validators can be declared, delegated to, enter the consensus set, and exit the consensus set (either honorably, by unbonding, or dishonorably via slashing);
  • writing back the voting powers to Tendermint, so that the consensus set follows the application state.

We've also started some preparatory work for our MVP3 milestone, IBC support, which enables Penumbra to act as a shielded pool for the entire Cosmos ecosystem. The public part of Penumbra's chain state will be recorded using the Jellyfish Merkle Tree originally designed for Libra/Diem; our jmt crate extracts the Diem implementation into a standalone, async-compatible implementation. Thanks to Mark Zuckerberg and David Marcus for funding the development of this technology and donating it to the community. To enable IBC, we've started working on adding support for JMT proofs to ICS23, which will also help enable IBC support between Cosmos chains and 0L (OpenLibra).

Using the Testnet

The testnet is only accessible by a command-line interface. Instructions on how to get started with it can be found on Github. We don’t have an automated faucet set up, but we do have a dedicated discord channel (#testnet-faucet) where you can paste an address and have someone send you tokens, or send tokens to someone else in the discord.

If you posted your address in the discord during one of the previous three testnets, we've added your address as one of the testnet validator's funding streams with 4 basis points of commission, so you should see your address receive rewards.

We’ve created a few different test assets to exercise the transaction functionality:

  • penumbra tokens, which can be used for paying fees;
  • gm and gn tokens, allowing you to send gm and gn to your friends;
  • tungsten_cube tokens, a fan favorite;
  • pizza tokens, representing a tasty and nutritious snack;
  • nala tokens, since no blockchain is complete without a dog token.

To be very explicit: this is not an incentivized testnet. These tokens have no value whatsoever, no purpose other than playing with our unfinished software, and we’ll be shutting down the testnet as soon as next week. In the meantime, though, if you’re interested in giving it a spin, why not try it out?