Skip to content

Commit

Permalink
CIP-0022: Pool Operator Verification
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewWestberg committed Jun 21, 2021
1 parent 7913cf5 commit 0e850d0
Showing 1 changed file with 99 additions and 0 deletions.
99 changes: 99 additions & 0 deletions CIP-0022/CIP-0022.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
CIP: 22
Title: Pool operator verification
Authors: Andrew Westberg <[email protected]>
Comments-URI: no comments yet
Status: Draft
Type: Informational
Created: 2021-06-21
License: CC-BY-4.0
---

# Abstract

This proposal describes a method allowing a stakepool operator to provide credentials to verify that they are the rightful manager for their stakepool.

# Motivation

Many websites such pooltool.io, adapools.org, and others need to allow pool operators special access to modify the way their pool appears on the website. SPOCRA and other organizations also have a need to allow voting on proposals and ensure that each vote cast is from a valid pool operator. Today, these sites and organizations all use different techniques for validating pool operators.

pooltool.io - Validates operators by receiving 1 ada spent from the pool's registered rewards account

adapools.org - Validates operators by requesting that the operator include a special generated value in their extended pool metadata json file.

This proposal desires to simplify and streamline a single approach that all can reference in order to verify that a pool operator is who they say they are.

Current Tools that have implemented it already or have plans to implement it:
* [CNCLI](https://github.com/AndrewWestberg/cncli)
* [JorManager](https://bitbucket.org/muamw10/jormanager/)

# Specification

In order to achieve the goals of this CIP, the pool operator needs to provide some credential or credentials to the validating party which cannot be spoofed. The VRF pool keys and VRF signature algorithm implemented in libsodium are chosen to build and provide this credential/signature. This signature can then be validated and the operator verified without ever exposing any of the pool's private key information. This technique is very similar to verifying that a block produced by another pool is valid. The only difference is that instead of validating the slot seed for a given pool, we're validating a pre-determined message hash.

Code Example (Pool Operator side):
```
// Node operational VRF-Verification-Key: pool.vrf.vkey
//{
// "type": "VrfVerificationKey_PraosVRF",
// "description": "VRF Verification Key",
// "cborHex": "5820e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381"
//}
//
// Node operational VRF-Signing-Key: pool.vrf.skey
//{
// "type": "VrfSigningKey_PraosVRF",
// "description": "VRF Signing Key",
// "cborHex": "5840adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381"
//}
// We assume the pool operator has access to the pool's vrf secret key
val skeyCbor = "5840adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381".hexToByteArray()
val vrfSkey = (CborReader.createFromByteArray(skeyCbor).readDataItem() as CborByteString).byteArrayValue()
val vkeyCbor = "5820e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381".hexToByteArray()
val vrfVkey = (CborReader.createFromByteArray(vkeyCbor).readDataItem() as CborByteString).byteArrayValue()
// Client side, sign the hash of a pre-determined message
val message = "pooltool.io".toByteArray()
val messageHash = SodiumLibrary.cryptoBlake2bHash(message, null)
println("messageHash: ${messageHash.toHexString()}")
val signature = SodiumLibrary.cryptoVrfProve(vrfSkey, messageHash)
println("signature: ${signature.toHexString()}")
```

Code Example (Validating server):
```
// We assume that the client has sent us the pool_id, signature, and we know the public pool.vrf.vkey
// We also know the pre-determined message that we've asked the client to sign. In this case "pooltool.io"
val message = "pooltool.io".toByteArray()
val messageHash = SodiumLibrary.cryptoBlake2bHash(message, null)
// get the vkeyHash for a pool from the "query pool-params" cardano-cli command
val vkeyHash = "f58bf0111f8e9b233c2dcbb72b5ad400330cf260c6fb556eb30cefd387e5364c".hexToByteArray()
// Verify that the vkey the client supplied is the same as the one on-chain
val vkeyHashVerify = SodiumLibrary.cryptoBlake2bHash(vrfVkey, null)
assertThat(vkeyHash).isEqualTo(vkeyHashVerify)
// Verify that the signature is a valid format. This will fail if the signature is mal-formed
val signatureHash = SodiumLibrary.cryptoVrfProofToHash(signature)
println("signatureHash: ${signatureHash.toHexString()}")
// Verify that the signature matches
val verification = SodiumLibrary.cryptoVrfVerify(vrfVkey, signature, messageHash)
println("verification: ${verification.toHexString()}")
assertThat(signatureHash).isEqualTo(verification)
println("Verification SUCCESS!")
```

# Rationale

Implementing this simplies and commonizes the process for verifying that a pool operator is who they say they are in 3rd party systems. Having a common way of verify pool operators also allows simple integration into pool management tools. There is also some overlap with [CIP-006](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0006/CIP-0006.md#extended-metadata---flexible-but-validable) and the `rawdata-sign` command although it chooses to generate a new key instead of utilizing the pool's existing vrf.skey to sign like this proposal.

# Copyright

This CIP is licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode)

0 comments on commit 0e850d0

Please sign in to comment.