Genesis stake

The Consensus::GenesisStake function is used for bootstrapping the Proof of Stake (PoS) network. Using this, we are able to create an initial staking coin that participates in consensus and is able to propose blocks. We can gather any number of these calls/transactions and hardcode them into a constant genesis block, so anyone is able to deterministically reproduce the genesis block and begin syncing the blockchain.

The parameters to execute this function are a single clear input, and a single anonymous output:

pub struct ConsensusGenesisStakeParamsV1 {
    /// Clear input
    pub input: ClearInput,
    /// Anonymous output
    pub output: ConsensusOutput,
}

For transparency, we use a clear input in order to show how many tokens are initially minted at genesis, and an anonymous output in order to anonymise the staker.

The ZK proof we use to prove the minting of the anonymous output is the ConsensusMint_V1 circuit:

k = 13;
field = "pallas";

constant "ConsensusMint_V1" {
	EcFixedPointShort VALUE_COMMIT_VALUE,
	EcFixedPoint VALUE_COMMIT_RANDOM,
}

witness "ConsensusMint_V1" {
	# X coordinate for public key
	Base pub_x,
	# Y coordinate for public key
	Base pub_y,
	# The value of this coin
	Base value,
	# The epoch this coin was minted on
	Base epoch,
	# Unique serial number corresponding to this coin
	Base serial,
	# Random blinding factor for the value commitment
	Scalar value_blind,
}

circuit "ConsensusMint_V1" {
	# Constrain the epoch this coin was minted on
	constrain_instance(epoch);

	# Poseidon hash of the coin
	C = poseidon_hash(
		pub_x,
		pub_y,
		value,
		epoch,
		serial,
	);
	constrain_instance(C);

	# Pedersen commitment for coin's value
	vcv = ec_mul_short(value, VALUE_COMMIT_VALUE);
	vcr = ec_mul(value_blind, VALUE_COMMIT_RANDOM);
	value_commit = ec_add(vcv, vcr);
	# Since the value commit is a curve point, we fetch its coordinates
	# and constrain them:
	constrain_instance(ec_get_x(value_commit));
	constrain_instance(ec_get_y(value_commit));

	# At this point we've enforced all of our public inputs.
}

Important to note here is that in the case of genesis, this mint will have epoch set to 0 (zero) in order for these stakers to be able to immediately propose blocks without a grace period in order to advance the blockchain.

Contract logic

get_metadata()

In the consensus_genesis_stake_get_metadata_v1 function, we gather the public key used to verify the transaction signature from the clear input, and we extract the necessary public inputs that go into the ConsensusMint_V1 proof verification.

process_instruction()

In the consensus_genesis_stake_process_instruction_v1 function, we perform the state transition. We enforce that:

  • The verifying slot for this function is actually the genesis slot (0)
  • The token ID from the clear input is the native network token
  • The output coin was not already seen in the set of staked or unstaked coins
  • The value commitments in the clear input and anon output match

If these checks pass, we create a state update with the output coin:

pub struct ConsensusGenesisStakeUpdateV1 {
    /// The newly minted coin
    pub coin: Coin,
}

process_update()

For the state update, we use the consensus_stake_process_update_v1 function. This will simply take the state update produced by consensus_genesis_stake_process_instruction_v1 and add the coin to the set of seen coins in the consensus state, and append it to the Merkle tree of coins in the consensus Merkle tree of coins.