Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
381 changes: 381 additions & 0 deletions qip-0016.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,381 @@
# QIP-16 — SOAP: Subsidy Mining via **Traditional AuxPoW** (no LTC↔DOGE merge mining)

**Status:** Draft
**Authors:** <you>
**Last updated:** 2025-09-15
**Scope:** Quai mainnet/testnets
**Audience:** core node implementers, pool/template operators

---

## 0. Abstract

SOAP (“**S**ubsidized **O**pen-market **A**cquisition **P**rogram”) imports external Proof-of-Work from **Bitcoin-family donors** (BTC, LTC, DOGE, RVN) to fund a **protocol coinbase payout (QADDR)** and to **commit** a Quai WorkObject reference inside donor blocks. Quai will switch from using Progpow to **KAWPOW** which is a slight modification from Progpow and will produce blocks with **KAWPOW** only. **Auxiliary shares** from other algos (SHA256d, Scrypt, …) may be *included* in Quai blocks for a QUAI payout, governed by a **separate DAA** targeting **3 included shares per block** with a hard cap of **9**.

Donor participation is verified on Quai using a **traditional AuxPoW-style proof**: donor **header (80B)** + **coinbase tx** + **merkle branch**. We do **not** rely on LTC↔DOGE merged mining; DOGE and LTC are mined independently.

---

## 1. Goals

- Do not allow any non-KAWPOW algorithm to author Quai blocks.
- Do not require miners to run donor chain state.
- Do not depend on pool code changes beyond standard Stratum v1 job fields.

---

## 2. Terminology

- **WorkObjectHeader (WOHeader):** The unit Quai miners build and commit to in donor blocks.
- **QADDR:** Protocol-specified donor payout scriptPubKey; 100% of donor subsidy+fees must pay QADDR.
- **SOAP push:** A fixed push of `"SOAP" 0x01 || 32B WO_HASH` placed in the **coinbase input scriptSig**.
- **AuxPow (in this spec):** A donor proof object included in a Quai block: `{header, coinbase, merkle branch, index, chain_id, height}`.

---

## 3. On-chain objects (Quai)

### 3.1 WorkObjectHeader (wire)

```go
type WorkObjectHeader struct {
headerHash common.Hash
parentHash common.Hash
number *big.Int
difficulty *big.Int
primeTerminusNumber *big.Int
txHash common.Hash
primaryCoinbase common.Address
location common.Location
mixHash common.Hash
time uint64
nonce BlockNonce
data []byte
lock uint8
auxpow *AuxPow // optional; donor proof (this spec)
}

```

### 3.2 AuxPow (donor proof)

> “Traditional AuxPoW structure” = SPV-style: donor header + coinbase + merkle path. No LTC→DOGE merge minining.

```go
type PowID uint16 // 0=ProgPow, 1=KawPow, 2=DOGE, 3=BTC/LTC(extensible)

type AuxPow struct {
powID PowID // PoW algorithm identifier
header []byte // 120B donor header for KAWPOW
signature []byte // Signature proving the work
merkleBranch [][]byte // siblings for coinbase index=0 up to root (little endian 32-byte hashes)
coinbase *wire.MsgTx // Full coinbase transaction (contains value in TxOut[0])
prevHash []byte // Previous block hash reference
signatureTime uint64 // Timestamp when signature was created
}

```

## coinbase will have a scriptsig commitment
```go
QAddrScript []byte // exact scriptPubKey bytes required for output[0]
// The commitment format we expect in coinbase scriptSig:
// PUSHDATA(height) PUSHDATA("SOAP" 0x01 || 32B WO_HASH) PUSHDATA(extranonce1) PUSHDATA(extranonce2)
```

---

## 4. Donor coinbase **consensus requirements**

For a donor block to count:

1. **Payout rule (MUST):** The coinbase **first spendable output** MUST pay **100% subsidy + fees** to **QADDR** (`QAddrScript` exact bytes).
- (Optional network param: allow additional zero-value OP_RETURN outputs.)
2. **Commitment rule (MUST):** The coinbase **input scriptSig** MUST include a fixed push:
```
PUSHDATA("SOAP" 0x01 || 32B WO_HASH)
```
placed after the BIP34 **height** push, before or after pool extranonces.
ScriptSig total size MUST be **2..100 bytes** (Bitcoin-family consensus).
3. **Header inclusion (MUST):** The AuxPow proof MUST show `txid(coinbase)` included in `Header.merkleRoot` via `Branch, Index`.
4. **(Optional) Donor PoW check:** Nodes MAY verify donor header PoW under that chain’s rules (policy toggle).
5. **Freshness (RECOMMENDED):** The donor `Height` SHOULD be within **Δ blocks** of a committee-advertised tip (policy; not consensus). This is also acieved by the signature time and prevhash committed in the AuxPow.

> **Rationale:** scriptSig is universally available, keeps miners stock, and avoids OP_RETURN size/policy variability across fleets.

---

## 5. Quai consensus changes

1. **Block PoW algorithm:** **KAWPOW** ONLY for block authoring.
2. **AuxPow binding (MUST):** If `auxpow != nil`, `woHeader.auxCommit = H("QUAI:AUXv1" || serialize(auxpow))` and `auxCommit` MUST be included in the Quai block header preimage.
3. **AuxPow effect (policy → can be elevated later):**
- **Soft mode (RECOMMENDED initial):** Valid AuxPow adds reward/weight bonus (see §7). No liveness coupling.
- **Epoch mode (optional future):** Require ≥1 valid AuxPow per **E** Quai blocks to avoid reward decay.
4. **Shares (MUST):** Non-KAWPOW algos **cannot** produce Quai blocks; they produce **shares** only (§6).
5. **Transition Period** where both progpow and kawpow blocks are accepted, but progpow rewards will slowly be phased out.

---

## 6. WorkShares (using existing WorkObject)

### 6.1 Classification & validity

A **WorkObjectHeader** `woHeader` is classified at validation time as:

- **Block:** if the KAWPOW seal on `woHeader` meets the current block target.
- **WorkShare:** if **not Block**, and `wo.auxpow` passes **VerifyAuxPow(wo)** and the donor header PoW satisfies the per‑chain **share target** `T_share[wo.auxpow.Chain]`.
- **Invalid:** otherwise.

**Tip binding / freshness:** Because `woHeader.parentHash` binds the object to the current Quai tip, separate tip-commit fields are unnecessary. Nodes SHOULD reject WorkShares older than **TTL = 2** Quai blocks (policy). Additional checks based on the signature time is also imposed.

### 6.2 Scoring & normalization

Two options are supported; implementers SHOULD start with **Equal Weight** for simplicity.

- **Equal Weight (default):** Every valid WorkShare counts as `w_norm = 1`.
- **Difficulty‑proportional (optional):** Let `D_share[chain]` be the difficulty implied by `T_share[chain]`, and `D_sol` be the difficulty implied by the donor solution’s PoW (stateless from `Header`). Score
`w_norm = min(D_sol / D_share[chain], W_max)`.

### 6.3 Inclusion & ordering

- Producers MUST include up to **S_max = 9** highest‑`w_norm` WorkShares.
- Tie‑break by arrival time.
- MAY enforce a per‑chain soft cap (e.g., ≤ 3) to avoid dominance.

### 6.4 Rewards (QUAI-only)

- **Each included WorkShare:** pays its submitter under Equal‑Weight, or proportionally to `w_norm` under Difficulty‑proportional.
- **Payout address:** the QUAI address is taken from `wo.woHeader.primaryCoinbase` (no extra fields required).

### 6.5 Adjustment of rewards for the additional chains

- If more than expected shares are found for additional chains, the reward is scaled down relative to the average expectation, indirectly creating an incentive for the miners to adjust hash rate



---

## 7. Reward & verification pseudocode

The only layout rule now is:
**SOAP must appear immediately after the BIP34 height push and entirely in the fixed (non-extranonce) part of the coinbase scriptSig.**

---

# 7. Verification

This defines **consensus** checks when AuxPoW requires a **quorum-signed AuxTemplate** and strict **correspondence** between that template, the donor **header**, and the **coinbase**. SOAP placement is minimal: **height push → SOAP push → (extranonce pushes, if any).**

## 7.1 Data shapes

```go
type AuxTemplate struct {
// Enforced correspondence
ChainID ChainID // must match ap.Chain
PrevHash [32]byte // must equal donor_header.hashPrevBlock
PayoutScript []byte // must equal coinbase.outputs[0].scriptPubKey
ScriptSigMaxLen uint16 // ≤100; template may be tighter

// Advisory (policy/UX; NOT consensus unless elevated)
Extranonce2Size uint8
NBits uint32
NTimeMask NTimeMask

// Quorum signatures over CanonicalEncode(AuxTemplate) WITHOUT Sigs
Sigs []SignerEnvelope // (SignerID, Signature)
}
```

> **Signatures:** sign `SHA256d("QUAI:SOAP:AUXTEMPLATEv1" || CanonicalEncode(tmpl_without_Sigs))`.
> No embedded template hash field; nodes recompute the digest for signature checks.

## 7.2 Top-level verification

```go
func VerifyWorkObjectAuxPow_WithAuxTemplate(wo *WorkObject, tmpl *AuxTemplate, params Params) error {
ap := wo.auxpow
if ap == nil { return ErrNoAuxPow }

// (1) SPV: coinbase ∈ donor header’s Merkle root
if !VerifyMerkle(ap.Coinbase, ap.Branch, ap.Index, HeaderMerkleRoot(ap.Header)) {
return ErrBadAuxMerkle
}

// (2) SOAP: scriptSig must be [BIP34 height][SOAP("SOAP\x01||WO_HASH")][... extranonce pushes ...]
cb := ParseTx(ap.Coinbase)
if !HasSoapImmediatelyAfterHeight(cb.ScriptSig, WOHash(wo)) {
return ErrMissingOrMisplacedSOAP
}

// (3) Quorum signatures over canonical AuxTemplate (no embedded hash)
digest := AuxTemplateDigest(*tmpl)
if !VerifyQuorumSigs(tmpl.Sigs, digest, params.ActiveSignerSet, params.QuorumThreshold) {
return ErrBadQuorum
}

// (4) Correspondence: template ↔ donor header & coinbase
if tmpl.ChainID != ap.Chain { return ErrChainMismatch }

donorPrev := DonorHeaderPrevHash(ap.Header) // hashPrevBlock from 80B header
if !bytes.Equal(tmpl.PrevHash[:], donorPrev[:]) {
return ErrPrevhashMismatch
}

if len(cb.Outputs) == 0 || !EqualScript(cb.Outputs[0].Script, tmpl.PayoutScript) {
return ErrWrongPayoutScript
}

// (5) Coinbase scriptSig bound (≤100 consensus; template may be tighter)
if ScriptSigLen(cb) > min(uint16(100), tmpl.ScriptSigMaxLen) {
return ErrScriptSigTooLong
}

// (Optional policy — NOT consensus unless elevated)
// if params.EnforceShareTarget && !VerifyDonorPoW(ap.Chain, ap.Header, params.Tshare[ap.Chain]) {
// return ErrBelowShareTarget
// }

return nil
}
```

## 7.3 Helpers

```go
func AuxTemplateDigest(t AuxTemplate) [32]byte {
enc := CanonicalEncode_WithoutSigs(t)
return SHA256d( append([]byte("QUAI:SOAP:AUXTEMPLATEv1"), enc...) )
}

func VerifyMerkle(coinbase []byte, branch [][]byte, index uint32, root [32]byte) bool {
h := DblSHA256(coinbase)
for i, sib := range branch {
if (index>>uint(i))&1 == 0 {
h = DblSHA256( append(h[:], sib...) )
} else {
h = DblSHA256( append(sib, h[:]...) )
}
}
return bytes.Equal(h[:], root[:])
}

// Minimal placement rule: SOAP push must be the SECOND push (right after BIP34 height)
func HasSoapImmediatelyAfterHeight(scriptSig []byte, expectWO [32]byte) bool {
pushes := ParseScriptSigPushes(scriptSig) // returns the sequence of PUSHDATA payloads
if len(pushes) < 2 { return false }
if !IsBIP34HeightPush(pushes[0]) { return false }
return IsSoapPush(pushes[1], expectWO)
}

// A SOAP push is exactly "SOAP\x01" || 32-byte WO hash (37 bytes total)
func IsSoapPush(b []byte, expectWO [32]byte) bool {
if len(b) != 37 { return false }
if !bytes.Equal(b[0:5], []byte{'S','O','A','P',0x01}) { return false }
return bytes.Equal(b[5:], expectWO[:])
}
```

## 7.4 Notes

* **Consensus requires:** SPV inclusion, SOAP presence/placement, **quorum-signed AuxTemplate**, and correspondence (`ChainID`, `PrevHash == hashPrevBlock`, `outputs[0] == PayoutScript`, `ScriptSigLen ≤ bound`).
* **No freshness rule** at consensus; a stale template won’t match current `hashPrevBlock` anyway.


## 8. Donor **coinbase construction** (template/pool)

**ScriptSig (no LTC↔DOGE AuxPoW header in this spec):**
```
PUSHDATA(BIP34_height)
PUSHDATA("SOAP" 0x01 || 32B WO_HASH) // fixed by server; miners do NOT change
PUSHDATA(extranonce1) // server
PUSHDATA(extranonce2) // miner, size typically 4–8 bytes
```
- **Keep total scriptSig ≤ 100 bytes.**
- Leave **witness-reserved 32B** (BTC/LTC segwit) untouched (it lives in the coinbase *witness*, not scriptSig).

**Outputs:**
1) `value = subsidy+fees` → **QADDR** (exact `QAddrScript`).
2) *(Optional)* zero-value **OP_RETURN** for an audit breadcrumb; not required by consensus.

**Stratum v1 job fields (unchanged):**
- `coinb1` ends after SOAP push and before miner’s `extranonce2`.
- `coinb2` starts at `extranonce2` placeholder and contains the rest of scriptSig + outputs.
- `extranonce1`, `extranonce2_size` (keep 4–8 for ASIC compatibility), `merkle_branch[]`, `prevhash`, `version`, `nbits`, `ntime`.

**Miner behavior:** builds coinbase (`coinb1||ex1||ex2||coinb2`), computes txid, folds `merkle_branch` to root, hashes header nonce/time.

---
## 9. Parameters

## 10. Security & liveness

- **No liveness coupling:** AuxPow is optional (soft mode). Blocks can be mined with zero shares included.
- **ScriptSig bounds:** enforce 2..100-byte consensus window; reject oversize coinbases.
- **DoS control:** bound AuxPow size (~1–2 KB typical), share size (≤~300B), per-block share cap (`S_max`).
- **Replay safety:** WO hash includes a **domain tag**; optionally add an 8-byte Quai tip prefix inside the SOAP push if desired.

---

## 11. Compatibility notes

- **BTC/LTC:** SegWit witness commitment uses the coinbase **witness reserved value**, not scriptSig; unaffected.
- **DOGE (no LTC merge):** Treat like LTC: its own coinbase scriptSig/outputs; same SOAP push fits.
- **RVN (KAWPOW):** No SegWit; scriptSig ≤100 still applies. KAWPOW-specific fields are irrelevant for donor proof.

---

## 12. Deployment plan

1. **Phase A:** Ship sharepool, DAA, QUAI payouts; accept AuxPow in soft mode (bonus only).
2. **Phase B:** Provide public donor **template relays** (BTC/LTC/DOGE/RVN) that fix QADDR + SOAP push; miners can point rigs with zero firmware changes.
3. **Phase C (optional):** Make AuxPow participation economically stronger (higher bonus) or add epoch requirement with decay.

---

## 13. Test guidance (devnets/testnets)

- Stand up a donor regtest/testnet node; generate coinbase-only templates with SOAP push; validate AuxPow in Quai.
- Ensure scriptSig length stays under 100 **with** height + SOAP + ex1 + ex2.
- Verify that modifying only `extranonce2` on the miner recomputes Merkle root correctly via the provided `merkle_branch`.

---

## 14. Reference encodings

**SOAP scriptSig push (hex):**
```
25 53 4f 41 50 01 <32-byte WO_HASH>
^^ len=37 "SOAP" 01
```

**OP_RETURN audit (optional)**
```
6a 25 53 4f 41 50 01 <32-byte WO_HASH>
OP_RETURN len=37 "SOAP" 01
```
*(Do not rely on OP_RETURN for consensus in this draft; SOAP-in-scriptSig is normative.)*

---

## 15. Open questions / future work

- Whether to elevate donor header PoW verification from policy → consensus.
- Optional inclusion of an 8-byte **Quai tip prefix** in SOAP push for tighter replay binding.
- Extending AuxPow to support compact proofs (NiPoPoW/FlyClient) if donor policy limits coinbase sizes.

---

## 16. Appendix — Minimal verifier sketch

```go
func VerifyAuxPowStrict(ap *AuxPow, expectWO Hash, expectQ Script) error {
if !VerifyMerkle(ap.Coinbase, ap.Branch, ap.Index, HeaderMerkleRoot(ap.Header)) {
return ErrBadAuxMerkle
}
cb := ParseTx(ap.Coinbase)
if !PaysExactly(cb, expectQ) { return ErrWrongPayout }
if !HasSoapScriptSig(cb, expectWO) { return ErrMissingSOAP }
return nil
}
```