Stratum 協議 - python 中的實現問題
幾天來,我試圖在 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”請求之後的下一個請求)。任何幫助,將不勝感激。