Collateral-based Pool for Ergo

After multiple iterations on the design, the following is a final version of the collateral scheme for an upcoming Ergo mining pool, for public review.

Perpetual Token

This is a singleton token, which is intended to last “forever”, with an additional restriction that prevents it from being spent more than once per block.

{
  val createdPreviousBlock = {(b: Box) => b.creationInfo._1 < HEIGHT}
  val createdCurrentBlock = {(b: Box) => b.creationInfo._1 == HEIGHT}

  val isPerpetualWithHeight = {(b: Box) =>
    b.propositionBytes == SELF.propositionBytes &&
    b.tokens == SELF.tokens &&
    createdCurrentBlock(b)
  }

  sigmaProp(OUTPUTS.exists(isPerpetualWithHeight) &&
    createdPreviousBlock(SELF))
}

Without this technique, I was concerned that users might accidentally create more than one unspent collateral box for the same miner public key, which would then allow a pool to claim more than one reward in the same block.

Collateral

This is the main pool collateral script, which allows a pool to take a reward from the collateral box when the miner finds a block.

{
  val minersReward = fixedRate - foundersInitialReward
  val minersFixedRatePeriod = fixedRatePeriod + 2 * epochLength
  val epoch = 1 + ((HEIGHT - fixedRatePeriod) / epochLength)
  val coinsToIssue = if (HEIGHT < minersFixedRatePeriod) {
    minersReward
  } else {
    fixedRate - oneEpochReduction * epoch
  }
  val totalSelf = INPUTS.fold(0L, {(a: Long, b:Box) =>
    if (b.propositionBytes == SELF.propositionBytes) {
      a + b.value
    } else {
      a
    }
  })
  val remainder = totalSelf - coinsToIssue

  val remainderToSelf = {(b: Box) =>
    (remainder <= 0) || (
      b.propositionBytes == SELF.propositionBytes &&
      b.value == remainder
    )
  }

  val perpetualToken = {(b: Box) =>
    b.tokens(0)._1 == perpetualTokenId
  }

  val aliceFoundBlock = sigmaProp(
    CONTEXT.preHeader.minerPk == alice &&
    perpetualToken(INPUTS(0)) &&
    remainderToSelf(OUTPUTS(0))
  )

  val aliceWithdraw = proveDlog(alice)

  aliceFoundBlock || aliceWithdraw
}

Pool Reward Transaction

Let’s call the spending transaction involving one or more collateral boxes as inputs the pool reward transaction. The following properties should hold for this transaction in the case of aliceFoundBlock:

The following properties hold for the special perpetual token (and for the pool reward transaction, since it is forced to spend this token):

Since the pool reward transaction is forced to spend this token, then by extension, there can only be one pool reward transaction per block.

Note that there is currently no restriction on where the pool might send its reward, or indeed which pool might spend this reward transaction.

Final notes: