Table of contents

Signed mining

This article discusses the idea of signed mining, which tries to provide a safe construct with a high degree of delibration, in a Proof of Work blockchain system.

A good mining algorithm for a blockchain must ensure delibration. That is, it must enforce that partcipants on the network are carrying out the action of mining as a deliberate action. Put it simply, the owner of miners must be aware that they are mining, and miners must be aware that they are actively spending their resources. Deliberation makes it more likely that the fundamental assumption of honest majority holds, and thus less likely to be the subject of 51% attacks.

Absence of delibration leads to botnets and malicious browser-based mining. In all those cases, the owners of the hardwares are not aware that they are participating in the mining process. If a mining algorithm cannot prevent those types of issues, controllers of botnets may be able to obtain a large share of the network hashrate, and carry out attacks such as double spending.

Signed mining adds an additional requirement for mining hardware to hold the private key of coinbase of mining rewards. In this way, botnets and browser-based mining become much less efficient to carry out.

Basic

Signed mining can be applied to any nonce-based mining algorithms. Those mining algorithms can be generalized to expose the following function:

fn verify(pre_hash: Hash, nonce: Nonce, seal: Seal) -> bool;

pre_hash is the hash of the header without PoW portion. nonce is a random number. The function verify checks that pre_hash and nonce evaluates to seal, and the resulting seal is above a given difficulty.

The mining process can then be expressed as follows:

loop {
  let nonce = Nonce::random();
  let seal = Seal::generate_from(pre_hash, nonce);
  if verify(pre_hash, nonce, seal) {
    return nonce
  }
}

Signed mining proposes to add an additional signing together with the nonce:

fn verify_signed(
  pre_hash: Hash,
  nonce: Nonce,
  signature: Signature,
  seal: Seal
) -> bool {
  signature.verify_with_message(pre_hash, nonce) &&
    seal.verify_with_pre(signature)
}

pre_hash and nonce are first signed by coinbase key producing signature. The signature is then fed into a normal nonce-based mining algorithm to produce the seal. The signing process happens once per nonce, and for mining, practically a large number of nonces are always tried before a block is produced. As a result, this enforces that mining hardwares must keep the private key together with the miner. Transmitting large amount of signatures through the network is impractical and inefficient.

Kulupu

This section describes a specification, with identifier 53-KSIGNED. (Discuss)

Specification

Previously, an optional pre-runtime digest can be included in PoW consensus engine, under POW_ENGINE_ID. After signed minig, the pre-runtime digest becomes required, and it encodes a sr25519 public key, representing the coinbase author.

The header seal is changed to included an additional signature field.

struct Seal {
  difficulty: Difficulty,
  nonce: H256,
  signature: sr25519::Signature,
}

The RandomX work is computed as follows. First, we prepare the Calculation, using the block header pre_hash, targeted difficulty, and the chosen nonce.

struct Calculation {
  pre_hash: H256,
  difficulty: Difficulty,
  nonce: H256,
}

The Calculation is then encoded by SCALE, hashed by 256-bit blake2b to produce a hash. The hash is then signed by the coinbase author sr25519 private key to produce a signature.

Then we encode (Calculation, sr25519::signature) using SCALE, and feed it as the input to the RandomX hashing function to produce the final mining work.

Ethereum

This section describes a specification, with identifier 52-ESIGNED. (Discuss)

Specification

Replace the coinbase field of block header by r. Add two new fields into the block header, v and s. Together, v, r, s hold an secp256k1 signature, whose public key is interpreted as the new coinbase address for a block.

When verifying the Ethash PoW for a block, change the logic to be the following:

  • Given a header, get the hash \(H_1\) where v equals CHAIN_ID. r, s and mixHash equals 0.

  • Check that \(H_1\) is the valid preimage of the signature consisting of v, r and s, with v equal to CHAIN_ID * 2
    35
    or CHAIN_ID * 2 + 36.

  • Calculate hash \(H_2\), with mixHash set to 0 in the original header.

  • Check that \(H_2\) is the valid preimage for Ethash of mixHash and nonce.