Mining-Pools

Stratum 協議 - python 中的實現問題

  • January 27, 2022

幾天來,我試圖在 python 中編寫簡單的腳本來與地層池伺服器進行通信。我正在使用 NightMiner 的簡化程式碼(<https://github.com/ricmoo/nightminer/>)和一些我發現的測試網(stratum+tcp://pool.bitcoincloud.net:4008),但我不能得到正確的結果。也許更有經驗的人可以幫助我。下面我展示了我的腳本的每個步驟(步驟,沒有 python 原始碼)。

1.連接伺服器,授權,獲取job數據

連接到伺服器後,我發送此請求:

b'{"id": 1, "method": "mining.subscribe", "params": []}\n'
b'{"params": ["testuser", "anything"], "id": 2, "method": "mining.authorize"}\n'

並得到以下響應:

{"id":1,"result":[[["mining.set_difficulty","deadbeefcafebabea200000000000000"],["mining.notify","deadbeefcafebabea200000000000000"]],"40000004",4],"error":null}
{"id":null,"method":"mining.set_difficulty","params":[0.01]}
{"id":null,"method":"mining.notify","params":["4ca","4128bf630f57387b00e25b419d1bb77e667e4036a7d8fee80000015600000000","01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2503a77614048c8a145e08","122f626974636f696e636c6f75642e6e65742f0000000002062c9c04000000001976a91423e020eacd64acfe093150331d44fdbcc0c7ce0688acc2eb0b00000000001976a91400bf6d61c2a34df5a9ea338fcad188c31bb4a52388ac00000000",[],"20000000","1a02b098","5e148a8c",true]}

2.獲取coinbase和merkle root

使用這些數據和 extranounce=‘00000000’ 我可以建構 coinbase(以字節形式,但在此範例中我將顯示為十六進制):

coinbase=coinb1+extranounce1+extranounce2+coinb2
coinbase=b'01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2503a77614048c8a145e084000000400000000122f626974636f696e636c6f75642e6e65742f0000000002062c9c04000000001976a91423e020eacd64acfe093150331d44fdbcc0c7ce0688acc2eb0b00000000001976a91400bf6d61c2a34df5a9ea338fcad188c31bb4a52388ac00000000'

接下來要做的是使用 SHA256 算法對 coinbase 進行雙重雜湊,它給出以下字節:

b'9b9c9ab0b1c92844e4fed3f895f9443fefcda5ae02cc5a8ad4444f9303081a23'

沒有 merkle 分支(merkle 分支數組是空的),所以上面的雜湊是我們的 merkle 根。

3.建構標題

在步驟 1 中收到 merkle 根和數據後,我可以開始建構標頭:

header= version + prevhash + merkle root + ntime + nbits + nonce

我使用的值如下:

version - received '20000000', after little-endian conversion it is '00000020'
prevhash - received '4128bf630f57387b00e25b419d1bb77e667e4036a7d8fee80000015600000000', after conversion it is '63bf28417b38570f415be2007eb71b9d36407e66e8fed8a75601000000000000' (reverse bytes order every 4 bytes)
merkle root - get '9b9c9ab0b1c92844e4fed3f895f9443fefcda5ae02cc5a8ad4444f9303081a23' in step 2, stays that way
ntime - received '5e148a8c', after conversion it is '8c8a145e'
nbits - received '1a02b098', after conversion it is '98b0021a'

所以標題是

b'0000002063bf28417b38570f415be2007eb71b9d36407e66e8fed8a756010000000000009b9c9ab0b1c92844e4fed3f895f9443fefcda5ae02cc5a8ad4444f9303081a238c8a145e98b0021a' + nonce

4. 搜尋隨機數

難度是0.01,所以我的目標是

00000063ff9c0000000000000000000000000000000000000000000000000000

經過一些計算,我發現這 4 個字節可以用作隨機數:

b'd0cf1040'

所以我的完整標題現在是

b'0000002063bf28417b38570f415be2007eb71b9d36407e66e8fed8a756010000000000009b9c9ab0b1c92844e4fed3f895f9443fefcda5ae02cc5a8ad4444f9303081a238c8a145e98b0021ad0cf1040'

在使用 SHA256 算法和反向字節順序對其進行雙重散列後,我得到了這個散列:

000000032f201567fc4d02fa468bf2dc6a5c21ae70098c124e99ee7d2520ca26

這低於我的目標,所以’d0cf1040’應該是正確的隨機數。

5. 送出通知

最後要做的是送出結果,所以我將此請求發送到伺服器:

b'{"id":2, "method":"mining.submit","params":["testuser","4ca","00000000","5e148a8c","d0cf1040"]}\n'

這是我得到的回應:

{"id":2,"result":false,"error":null}

我不知道為什麼我得到結果=假。我只能假設第 3 步(建構標頭)中存在錯誤,並且此錯誤與某些值的字節順序有關,但我找不到此錯誤(我嘗試了許多字節順序變體,但從未得到響應結果=真)。

如果有人可以檢查此計算並給我一些提示,我將非常感激。

有(至少)一個錯誤:

沒有 merkle 分支(merkle 分支數組為空),所以上面的 hash 是我們的 merkle 根

該特定作業的 merkle 數組不為空。它包含以下分支:

["12ae2555f80b865bac89fa3aeb4df41b730422670fb80d1a0536276b481fab53","e1f7983af4815cb2116f16c6af916f0a139ad5a7588fd029572c6e403f9fed5d","6d5861e558d057993fdae3007ff5b49807d6f43b218e1ef85ed81223747278f1","847e7a24328302a2a8dbdad177d44d5a20916a0ba36edf2dc667c01a09da298c"]

那麼就需要計算默克爾根的正確方法。

好的,我搞定了。

我發現了兩種錯誤(更不用說送出結果期間的字節順序了——謝謝@Charlie 的提示):

1. 結果(假)我從池伺服器得到的最後一個響應不是對’mining.submit’方法的響應

在我的範例通信日誌(問題)中,我粘貼(除其他外)這些請求:

b'{"params": ["testuser", "anything"], "id": 2, "method": "mining.authorize"}\n'

b'{"id":2, "method":"mining.submit","params":["testuser","4ca","00000000","5e148a8c","d0cf1040"]}\n'

您可以看到我對這兩個請求都使用了相同的“id”值 (2)。所以當我得到回复時:

{"id":2,"result":false,"error":null}

我假設這是對我的“mining.submit”請求的“錯誤”響應。但後來我為每個請求使用了單獨的 id,發現這個“錯誤”結果是對“mining.authorize”方法的響應。所以我的第一個問題是授權問題,而不是隨機數計算/送出。

我不知道為什麼在其他一些請求之後我會收到對“mining.authorize”請求的響應,但這是一些改進;)

2. ‘mining.authorize’ 方法送出數據錯誤

在我的範例通信日誌(問題)中,我粘貼了這個請求:

b'{"params": ["testuser", "anything"], "id": 2, "method": "mining.authorize"}\n'

但根據<https://pool.bitcoincloud.net/>我應該使用使用者名“你的地址(測試池上的測試網地址)”。所以我將此請求更改為:

b'{"params": ["2N16oE62ZjAPup985dFBQYAuy5zpDraH7Hk", "anything"], "id": 2, "method": "mining.authorize"}\n'

現在它可以工作了,在送出結果(‘mining.submit’方法)後,我得到了這個響應:

b'{"id":3,"result":true,"error":null}\n'

我唯一需要弄清楚的是為什麼我得到對“mining.authorize”請求的響應作為對其他請求的響應(“mining.authorize”請求之後的下一個請求)。任何幫助,將不勝感激。

引用自:https://bitcoin.stackexchange.com/questions/92705