Stake UTXOs

A 'stake' records how much GT has been staked by a user, and on which proposals it has been locked against.

data StakeDatum = StakeDatum
  { stakedAmount :: Tagged GTTag Integer
  -- ^ Tracks the amount of governance token staked in the datum.
  --   This also acts as the voting weight for 'Agora.Proposal.Proposal's.
  , owner :: Credential
  -- ^ The owner this stake belongs to.
  , lockedBy :: [ProposalLock]
  -- ^ The current proposals locking this stake. This field must be empty
  --   for the stake to be usable for deposits and withdrawals.
  }

Proposal locks can be one of two types, a vote lock, or a creation lock. Creating proposals is artificially limited per stake in order to prevent spam.

data ProposalLock
  = -- | The stake was used to create a proposal.
    --
    --   This kind of lock is placed upon the creation of a proposal, in order
    --    to limit creation of proposals per stake.
    Created
      ProposalId
      -- ^ The identifier of the proposal.
  | -- | The stake was used to vote on a proposal.
    --
    --   This kind of lock is placed while voting on a proposal, in order to
    --    prevent depositing and withdrawing when votes are in place.
    Voted
      ProposalId
      -- ^ The identifier of the proposal.
      ResultTag
      -- ^ The option which was voted on. This allows votes to be retracted.

When voting for a proposal, the stake UTXO is used to witness the user's staked amount. As a result, the two following state transitions take place:

proposal.votes += (stake.stakedAmount, vote)
stake.lockedBy += Voted (hash proposal.settings) vote

This forms a mutual binding between the proposal and the stake.

A stake may be used to vote on an unlimited number of proposals. Consider a user staking 50GT. They may pledge that 50GT against a proposal p and another proposal q.

Altering the amount positioned in a stake is not possible, for as long as that stake is locked against any proposals. This is to prevent vote manipulation. Consider:

It should be clear how users could alter their stakes to reduce and manipulate the vote count. Preventing alteration of GT in stakes which have voted ensures that there is never a discrepancy between the amount of GT a user holds and the amount the proposal believes that they hold, expressed in the total vote count.

If a user wished to stake more GT, they could always create a new stake that they would be free to lock on proposals. For this reason, it may be useful to include a method of 'merging' stakes, when they are not being locked against any proposals, to allow users to 'streamline' their GT portfolio. A user may, of course, always retract their vote, deposit more GT, and vote again.

A note on creating proposals

Creating proposals works similarly to how voting on them works when it comes to the locking of stakes: When you create a proposal, you must add a Created lock (see ProposalLock above). You can only create proposals if you have sufficiently low amount of existing locks. This lock will prevent spamming of proposals to a certain extent. You can still create infinitely many proposals, it’s just that there is a throughput limit now. Of course, a user may create multiple stakes, if they have access to enough GT, but this should make truly malicious DoS a lot harder.

Guidelines for configuring params to prevent DoS in this situation.

We say that the throughput of creating proposals (in frequency of blocks) of a particular actor is defined as the following:

$$ \text{creationThroughput}(gtOwned) = \frac{gtOwned}{\text{proposalMinGTCreate} \times \text{proposalMinTime}} $$