grin 之 Cuckoo Cycle 演算法挖礦分析
Cuckoo Cycle 演算法
- 作者:John Tromp
- c++: github: https://github.com/tromp/cuckoo
- golang: https://github.com/AidosKuneen/cuckoo
- 解決的問題:給出N個節點(奇偶兩行)和M個邊,在M個邊中尋找一個閉環(即找到一個路徑使得一個節點是起點也是終點)如圖中的0-5-4-1-0:

image.png
8個節點6個邊 一個解決方案
- 特點:即時驗證,記憶體需求可擴充套件(可抵抗asic)
grin中挖礦過程
兩種演算法:Cuck(ar|at)oo cycle (抗asic | 對asic友好) (edg_bits : 29|31+)
solution 環型長度 都是42
- hash1=black2b(header+nonce) 其中參與計算的header未包含pow欄位
- solution,find=cuckoo(hash1)
cuckoo 把hash1當作SIPHAS0H的種子,然後隨機生成M個邊,再找出一個環 - 如果find,就返回一個solution,然後對該solution 計算hash並對比難度值是否符合要求
- 如果符合難度值,則廣播區塊
- 如果不符合難度值,重新組裝header+nonce,繼續步驟1
- 如果沒有find,重新組裝header+nonce,繼續步驟1
code
- 啟動solver,開始挖礦
pub unsafe extern "C" fn run_solver( ctx: *mut SolverCtx, header_ptr: *const c_uchar,//不包含pow部分 header_length: uint32_t, nonce: uint64_t,//隨機數 _range: uint32_t, solutions: *mut SolverSolutions, //輸出解決方案 stats: *mut SolverStats, ) -> uint32_t
- new cuckarooctx (19,42)
pub fn new_cuckaroo_ctx<T>( edge_bits: u8, // 2^bits 邊的圖形 2^19 proof_size: usize, //環形長度 42 ) -> Result<Box<dyn PoWContext<T>>, Error>
- 拼接 header+nonce
pub fn set_header_nonce( header: &[u8], nonce: Option<u64>, mutate_nonce: bool, ) -> Result<[u64; 4], Error>
- blake2b(header+nonce)
pub fn create_siphash_keys(header: &[u8]) -> Result<[u64; 4], Error>
- cuckoo圖形搜尋開始,返回解決方案
pub fn search(nodes: &[u32]) -> Result<Vec<Solution>, String>
- 驗證cuckoo solution
pub struct CuckooParams<T> { pub edge_bits: u8, //圖形邊數量 2^bits pub proof_size: usize, //環形邊數量 pub num_edges: u64, pub siphash_keys: [u64; 4], pub edge_mask: T, } fn verify(&self, proof: &Proof) -> Result<(), Error> { let nonces = &proof.nonces; let mut uvs = vec![0u64; 2 * proof.proof_size()]; let mut xor0: u64 = 0; let mut xor1: u64 = 0; for n in 0..proof.proof_size() { if nonces[n] > to_u64!(self.params.edge_mask) { return Err(ErrorKind::Verification("edge too big".to_owned()))?; } if n > 0 && nonces[n] <= nonces[n - 1] { return Err(ErrorKind::Verification("edges not ascending".to_owned()))?; } let edge = to_edge!(siphash_block(&self.params.siphash_keys, nonces[n])); uvs[2 * n] = to_u64!(edge & self.params.edge_mask); uvs[2 * n + 1] = to_u64!((edge >> 32) & self.params.edge_mask); xor0 ^= uvs[2 * n]; xor1 ^= uvs[2 * n + 1]; } ... ... ... }
參考:
The Mining Loop
All of these systems are put together in the mining loop, which attempts to create
valid Proofs-of-Work to create the latest block in the chain. The following is an outline of what the main mining loop does during a single iteration:
- Get the latest chain state and build a block on top of it, which includes
- A Block Header with new values particular to this mining attempt, which are:
- The latest target difficulty as selected by theevolving network difficultyalgorithm
- A set of transactions available for validation selected from the transaction pool
- A coinbase transaction (which we're hoping to give to ourselves)
- The current timestamp
- A randomly generated nonce to add further randomness to the header's hash
- The merkle root of the UTXO set and fees (not yet implemented)
- Then, a sub-loop runs for a set amount of time, currently configured at 2 seconds, where the following happens:
- The new block header is hashed to create a hash value
- The cuckoo graph generator is initialized, which accepts as parameters:
- The hash of the potential block header, which is to be used as the key to a SIPHASH function
that will generate pairs of locations for each element in a set of nonces 0..N in the graph. - The size of the graph (a consensus value).
- An easiness value, (a consensus value) representing the M/N ratio described above denoting the probability
of a solution appearing in the graph
- The hash of the potential block header, which is to be used as the key to a SIPHASH function
- The Cuckoo Cycle detection algorithm tries to find a solution (i.e. a cycle of length 42) within the generated
graph. - If a cycle is found, a Blake2b hash of the proof is created and is compared to the current target
difficulty, as outlined inAdditional Difficulty Controlabove. - If the Blake2b Hash difficulty is greater than or equal to the target difficulty, the block is sent to the
transaction pool, propagated amongst peers for validation, and work begins on the next block. - If the Blake2b Hash difficulty is less than the target difficulty, the proof is thrown out and the timed loop continues.
- If no solution is found, increment the nonce in the header by 1, and update the header's timestamp so the next iteration
hashes a different value for seeding the next loop's graph generation step. - If the loop times out with no solution found, start over again from the top, collecting new transactions and creating
a new block altogether.
- Then, a sub-loop runs for a set amount of time, currently configured at 2 seconds, where the following happens:
- A Block Header with new values particular to this mining attempt, which are:
Mining Loop Difficulty Control and Timing
Controlling the overall difficulty of the mining loop requires finding a balance between the three values outlined above:
- Graph size (currently represented as a bit-shift value n representing a size of 2^n nodes, consensus value
DEFAULT_SIZESHIFT). Smaller graphs can be exhaustively searched more quickly, but will also have fewer
solutions for a given easiness value. A very small graph needs a higher easiness value to have the same
chance to have a solution as a larger graph with a lower easiness value. - The 'Easiness' consensus value, or the M/N ratio of the graph expressed as a percentage. The higher this value, the more likely
it is a generated graph will contain a solution. In tandem with the above, the larger the graph, the more solutions
it will contain for a given easiness value. The Cuckoo Cycle implementations fix this M to N/2, giving
a ratio of 50% - The evolving network difficulty hash.