Dagger-Hashimoto
صفحہ کی آخری تازہ کاری: 11 اپریل، 2024
Dagger-Hashimoto Ethereum کے مائننگ الگورتھم کے لیے اصل تحقیقی نفاذ اور تفصیلات تھیں۔ Ethash نے Dagger-Hashimoto کی جگہ لے لی۔ 15 ستمبر 2022 کو The Merge پر مائننگ مکمل طور پر بند کر دی گئی تھی۔ تب سے، Ethereum کو اس کے بجائے proof-of-stake میکانزم کا استعمال کرکے محفوظ کیا گیا ہے۔ یہ صفحہ تاریخی دلچسپی کے لیے ہے - یہاں دی گئی معلومات The Merge کے بعد کے Ethereum کے لیے اب متعلقہ نہیں ہیں۔
شرائط
اس صفحہ کو بہتر طور پر سمجھنے کے لیے، ہم تجویز کرتے ہیں کہ آپ پہلے proof-of-work consensus، مائننگ، اور مائننگ الگورتھم کے بارے میں پڑھیں۔
Dagger-Hashimoto
Dagger-Hashimoto کا مقصد دو اہداف کو پورا کرنا ہے:
- ASIC-مزاحمت: الگورتھم کے لیے خصوصی ہارڈ ویئر بنانے سے فائدہ ہر ممکن حد تک کم ہونا چاہیے۔
- لائٹ کلائنٹ کی تصدیق کی اہلیت: ایک بلاک کی لائٹ کلائنٹ کے ذریعے مؤثر طریقے سے تصدیق کی جانی چاہیے۔
ایک اضافی ترمیم کے ساتھ، ہم یہ بھی بتاتے ہیں کہ اگر چاہیں تو تیسرے مقصد کو کیسے پورا کیا جائے، لیکن اضافی پیچیدگی کی قیمت پر:
مکمل چین اسٹوریج: مائننگ کے لیے مکمل بلاک چین اسٹیٹ کے اسٹوریج کی ضرورت ہونی چاہیے (Ethereum اسٹیٹ ٹرائی کی بے قاعدہ ساخت کی وجہ سے، ہم توقع کرتے ہیں کہ کچھ کانٹ چھانٹ ممکن ہو گی، خاص طور پر کچھ اکثر استعمال ہونے والے معاہدوں کی، لیکن ہم اسے کم سے کم کرنا چاہتے ہیں)۔
DAG جنریشن
الگورتھم کا کوڈ ذیل میں Python میں بیان کیا جائے گا۔ سب سے پہلے، ہم مخصوص درستگی کے غیر دستخط شدہ انٹس کو سٹرنگز میں مارشل کرنے کے لیے encode_int دیتے ہیں۔ اس کا الٹا بھی دیا گیا ہے:
1NUM_BITS = 51223def encode_int(x):4 "ایک big-endian اسکیم کا استعمال کرتے ہوئے ایک انٹیجر x کو 64 حروف کی ایک سٹرنگ کے طور پر انکوڈ کریں"5 o = ''6 for _ in range(NUM_BITS / 8):7 o = chr(x % 256) + o8 x //= 2569 return o1011def decode_int(s):12 "ایک big-endian اسکیم کا استعمال کرتے ہوئے ایک سٹرنگ سے ایک انٹیجر x کو انکوڈ کریں"13 x = 014 for c in s:15 x *= 25616 x += ord(c)17 return xسب دکھائیںہم آگے یہ فرض کرتے ہیں کہ sha3 ایک فنکشن ہے جو ایک انٹیجر لیتا ہے اور ایک انٹیجر آؤٹ پٹ کرتا ہے، اور dbl_sha3 ایک ڈبل-sha3 فنکشن ہے؛ اگر اس حوالہ جاتی کوڈ کو نفاذ میں تبدیل کر رہے ہیں تو استعمال کریں:
1from pyethereum import utils2def sha3(x):3 if isinstance(x, (int, long)):4 x = encode_int(x)5 return decode_int(utils.sha3(x))67def dbl_sha3(x):8 if isinstance(x, (int, long)):9 x = encode_int(x)10 return decode_int(utils.sha3(utils.sha3(x)))سب دکھائیںپیرامیٹرز
الگورتھم کے لیے استعمال ہونے والے پیرامیٹرز یہ ہیں:
1SAFE_PRIME_512 = 2**512 - 38117 # 2**512 سے کم سب سے بڑا سیف پرائم23params = {4 "n": 4000055296 * 8 // NUM_BITS, # ڈیٹاسیٹ کا سائز (4 گیگا بائٹس)؛ 65536 کا ملٹیپل ہونا چاہیے5 "n_inc": 65536, # فی مدت n کی قدر میں اضافہ؛ 65536 کا ملٹیپل ہونا چاہیے6 # epochtime=20000 کے ساتھ فی سال 882 MB کی نمو دیتا ہے7 "cache_size": 2500, # لائٹ کلائنٹ کے کیشے کا سائز (لائٹ کے ذریعے منتخب کیا جا سکتا ہے8 # کلائنٹ؛ الگورتھم کی تفصیلات کا حصہ نہیں)9 "diff": 2**14, # مشکل (بلاک کی تشخیص کے دوران ایڈجسٹ کی گئی)10 "epochtime": 100000, # بلاکس میں ایک ایپوک کی لمبائی (ڈیٹاسیٹ کتنی بار اپ ڈیٹ ہوتا ہے)11 "k": 1, # ایک نوڈ کے والدین کی تعداد12 "w": w, # ماڈیولر ایکسپونینشن ہیشنگ کے لیے استعمال کیا جاتا ہے13 "accesses": 200, # ہاشیموٹو کے دوران ڈیٹاسیٹ تک رسائی کی تعداد14 "P": SAFE_PRIME_512 # ہیشنگ اور بے ترتیب نمبر بنانے کے لیے سیف پرائم15}سب دکھائیںاس معاملے میں P ایک پرائم ہے جسے اس طرح منتخب کیا گیا ہے کہ log₂(P) 512 سے تھوڑا کم ہے، جو ان 512 بٹس سے مطابقت رکھتا ہے جنہیں ہم اپنے نمبروں کی نمائندگی کے لیے استعمال کر رہے ہیں۔ نوٹ کریں کہ اصل میں DAG کے صرف آخری نصف کو ذخیرہ کرنے کی ضرورت ہے، لہذا ڈی-فیکٹو RAM کی ضرورت 1 GB سے شروع ہوتی ہے اور فی سال 441 MB بڑھتی ہے۔
Dagger گراف بنانا
Dagger گراف بنانے کا پرمیٹیو مندرجہ ذیل طور پر بیان کیا گیا ہے:
1def produce_dag(params, seed, length):2 P = params["P"]3 picker = init = pow(sha3(seed), params["w"], P)4 o = [init]5 for i in range(1, length):6 x = picker = (picker * init) % P7 for _ in range(params["k"]):8 x ^= o[x % i]9 o.append(pow(x, params["w"], P))10 return oسب دکھائیںبنیادی طور پر، یہ ایک گراف کو ایک واحد نوڈ، sha3(seed) کے طور پر شروع کرتا ہے، اور وہاں سے بے ترتیب پچھلے نوڈز کی بنیاد پر ترتیب وار دوسرے نوڈز کو شامل کرنا شروع کرتا ہے۔ جب ایک نیا نوڈ بنایا جاتا ہے، تو i سے کم کچھ انڈیکس کو بے ترتیب طور پر منتخب کرنے کے لیے سیڈ کی ایک ماڈیولر پاور کا حساب لگایا جاتا ہے (اوپر x % i کا استعمال کرتے ہوئے)، اور ان انڈیکس پر نوڈز کی قدروں کو x کے لیے ایک نئی قدر پیدا کرنے کے لیے ایک حساب میں استعمال کیا جاتا ہے، جسے پھر ایک چھوٹے پروف آف ورک فنکشن (XOR پر مبنی) میں فیڈ کیا جاتا ہے تاکہ آخر کار انڈیکس i پر گراف کی قدر پیدا کی جا سکے۔ اس خاص ڈیزائن کے پیچھے استدلال DAG کی ترتیب وار رسائی کو مجبور کرنا ہے؛ DAG کی اگلی قدر جس تک رسائی حاصل کی جائے گی اس کا تعین اس وقت تک نہیں کیا جا سکتا جب تک کہ موجودہ قدر معلوم نہ ہو۔ آخر میں، ماڈیولر ایکسپونینشن نتیجے کو مزید ہیش کرتا ہے۔
یہ الگورتھم نمبر تھیوری کے کئی نتائج پر انحصار کرتا ہے۔ بحث کے لیے نیچے ضمیمہ دیکھیں۔
لائٹ کلائنٹ کی تشخیص
مذکورہ بالا گراف کی تعمیر کا مقصد گراف میں ہر نوڈ کو صرف چند نوڈز کے سب ٹری کا حساب لگا کر دوبارہ تعمیر کرنے کی اجازت دینا ہے اور صرف تھوڑی مقدار میں معاون میموری کی ضرورت ہوتی ہے۔ نوٹ کریں کہ k=1 کے ساتھ، سب ٹری صرف DAG میں پہلے عنصر تک جانے والی قدروں کا ایک سلسلہ ہے۔
DAG کے لیے لائٹ کلائنٹ کمپیوٹنگ فنکشن مندرجہ ذیل طور پر کام کرتا ہے:
1def quick_calc(params, seed, p):2 w, P = params["w"], params["P"]3 cache = {}45 def quick_calc_cached(p):6 if p in cache:7 pass8 elif p == 0:9 cache[p] = pow(sha3(seed), w, P)10 else:11 x = pow(sha3(seed), (p + 1) * w, P)12 for _ in range(params["k"]):13 x ^= quick_calc_cached(x % p)14 cache[p] = pow(x, w, P)15 return cache[p]1617 return quick_calc_cached(p)سب دکھائیںبنیادی طور پر، یہ صرف مذکورہ بالا الگورتھم کی دوبارہ تحریر ہے جو پورے DAG کے لیے قدروں کا حساب لگانے کے لوپ کو ہٹاتا ہے اور پہلے والے نوڈ لک اپ کو ریکرسیو کال یا کیشے لک اپ سے بدل دیتا ہے۔ نوٹ کریں کہ k=1 کے لیے کیشے غیر ضروری ہے، حالانکہ مزید اصلاح اصل میں DAG کی پہلی چند ہزار قدروں کا پہلے سے حساب لگاتی ہے اور اسے حساب کے لیے ایک جامد کیشے کے طور پر رکھتی ہے؛ اس کے کوڈ کے نفاذ کے لیے ضمیمہ دیکھیں۔
DAGs کا ڈبل بفر
ایک مکمل کلائنٹ میں، مذکورہ بالا فارمولے سے تیار کردہ 2 DAGs کا ایک ڈبل بفر (opens in a new tab) استعمال کیا جاتا ہے۔ خیال یہ ہے کہ DAGs مذکورہ بالا پیرامیٹرز کے مطابق ہر epochtime تعداد کے بلاکس پر تیار کیے جاتے ہیں۔ کلائنٹ تازہ ترین تیار کردہ DAG استعمال کرنے کے بجائے، پچھلا والا استعمال کرتا ہے۔ اس کا فائدہ یہ ہے کہ یہ DAGs کو وقت کے ساتھ تبدیل کرنے کی اجازت دیتا ہے بغیر کسی ایسے قدم کو شامل کرنے کی ضرورت کے جہاں مائنرز کو اچانک تمام ڈیٹا کا دوبارہ حساب لگانا پڑے۔ بصورت دیگر، باقاعدہ وقفوں پر چین پروسیسنگ میں اچانک عارضی سست روی اور مرکزیت میں ڈرامائی طور پر اضافہ ہونے کا امکان ہے۔ اس طرح تمام ڈیٹا کا دوبارہ حساب لگانے سے پہلے ان چند منٹوں کے اندر 51% حملے کا خطرہ ہوتا ہے۔
ایک بلاک کے لیے کام کا حساب لگانے کے لیے استعمال ہونے والے DAGs کا سیٹ بنانے کے لیے استعمال ہونے والا الگورتھم مندرجہ ذیل ہے:
1def get_prevhash(n):2 from pyethereum.blocks import GENESIS_PREVHASH3 from pyethereum import chain_manager4 if n <= 0:5 return hash_to_int(GENESIS_PREVHASH)6 else:7 prevhash = chain_manager.index.get_block_by_number(n - 1)8 return decode_int(prevhash)910def get_seedset(params, block):11 seedset = {}12 seedset["back_number"] = block.number - (block.number % params["epochtime"])13 seedset["back_hash"] = get_prevhash(seedset["back_number"])14 seedset["front_number"] = max(seedset["back_number"] - params["epochtime"], 0)15 seedset["front_hash"] = get_prevhash(seedset["front_number"])16 return seedset1718def get_dagsize(params, block):19 return params["n"] + (block.number // params["epochtime"]) * params["n_inc"]2021def get_daggerset(params, block):22 dagsz = get_dagsize(params, block)23 seedset = get_seedset(params, block)24 if seedset["front_hash"] <= 0:25 # No back buffer is possible, just make front buffer26 return {"front": {"dag": produce_dag(params, seedset["front_hash"], dagsz),27 "block_number": 0}}28 else:29 return {"front": {"dag": produce_dag(params, seedset["front_hash"], dagsz),30 "block_number": seedset["front_number"]},31 "back": {"dag": produce_dag(params, seedset["back_hash"], dagsz),32 "block_number": seedset["back_number"]}}سب دکھائیںHashimoto
اصل Hashimoto کے پیچھے خیال یہ ہے کہ بلاک چین کو بطور ڈیٹاسیٹ استعمال کیا جائے، ایک ایسا حساب انجام دیا جائے جو بلاک چین سے N انڈیکس منتخب کرتا ہے، ان انڈیکس پر ٹرانزیکشنز کو جمع کرتا ہے، اس ڈیٹا کا XOR انجام دیتا ہے، اور نتیجے کا ہیش واپس کرتا ہے۔ تھڈیئس ڈرائجا کا اصل الگورتھم، مطابقت کے لیے Python میں ترجمہ کیا گیا، مندرجہ ذیل ہے:
1def orig_hashimoto(prev_hash, merkle_root, list_of_transactions, nonce):2 hash_output_A = sha256(prev_hash + merkle_root + nonce)3 txid_mix = 04 for i in range(64):5 shifted_A = hash_output_A >> i6 transaction = shifted_A % len(list_of_transactions)7 txid_mix ^= list_of_transactions[transaction] << i8 return txid_mix ^ (nonce << 192)بدقسمتی سے، جبکہ Hashimoto کو RAM ہارڈ سمجھا جاتا ہے، یہ 256 بٹ ریاضی پر انحصار کرتا ہے، جس میں کافی کمپیوٹیشنل اوور ہیڈ ہوتا ہے۔ تاہم، Dagger-Hashimoto اس مسئلے کو حل کرنے کے لیے اپنے ڈیٹاسیٹ کو انڈیکس کرتے وقت صرف سب سے کم اہم 64 بٹس کا استعمال کرتا ہے۔
1def hashimoto(dag, dagsize, params, header, nonce):2 m = dagsize / 23 mix = sha3(encode_int(nonce) + header)4 for _ in range(params["accesses"]):5 mix ^= dag[m + (mix % 2**64) % m]6 return dbl_sha3(mix)ڈبل SHA3 کا استعمال صفر-ڈیٹا، قریب-فوری پری-ویریفکیشن کی ایک شکل کی اجازت دیتا ہے، صرف اس بات کی تصدیق کرتا ہے کہ ایک درست درمیانی قدر فراہم کی گئی تھی۔ پروف آف ورک کی یہ بیرونی تہہ انتہائی ASIC-فرینڈلی اور کافی کمزور ہے، لیکن DDoS کو مزید مشکل بنانے کے لیے موجود ہے کیونکہ ایک ایسا بلاک بنانے کے لیے کام کی وہ چھوٹی سی مقدار کرنا ضروری ہے جسے فوری طور پر مسترد نہیں کیا جائے گا۔ یہاں لائٹ کلائنٹ ورژن ہے:
1def quick_hashimoto(seed, dagsize, params, header, nonce):2 m = dagsize // 23 mix = sha3(nonce + header)4 for _ in range(params["accesses"]):5 mix ^= quick_calc(params, seed, m + (mix % 2**64) % m)6 return dbl_sha3(mix)مائننگ اور تصدیق
اب، آئیے اس سب کو مائننگ الگورتھم میں ایک ساتھ رکھتے ہیں:
1def mine(daggerset, params, block):2 from random import randint3 nonce = randint(0, 2**64)4 while 1:5 result = hashimoto(daggerset, get_dagsize(params, block),6 params, decode_int(block.prevhash), nonce)7 if result * params["diff"] < 2**256:8 break9 nonce += 110 if nonce >= 2**64:11 nonce = 012 return nonceسب دکھائیںیہاں تصدیقی الگورتھم ہے:
1def verify(daggerset, params, block, nonce):2 result = hashimoto(daggerset, get_dagsize(params, block),3 params, decode_int(block.prevhash), nonce)4 return result * params["diff"] < 2**256لائٹ کلائنٹ فرینڈلی تصدیق:
1def light_verify(params, header, nonce):2 seedset = get_seedset(params, block)3 result = quick_hashimoto(seedset["front_hash"], get_dagsize(params, block),4 params, decode_int(block.prevhash), nonce)5 return result * params["diff"] < 2**256نیز، نوٹ کریں کہ Dagger-Hashimoto بلاک ہیڈر پر اضافی تقاضے عائد کرتا ہے:
- دو-تہہ کی تصدیق کے کام کرنے کے لیے، ایک بلاک ہیڈر میں نونس اور درمیانی قدر دونوں پری-sha3 ہونی چاہئیں۔
- کہیں، ایک بلاک ہیڈر کو موجودہ سیڈ سیٹ کا sha3 ذخیرہ کرنا چاہیے۔
مزید پڑھیں
کسی کمیونٹی وسیلے کے بارے میں جانتے ہیں جس نے آپ کی مدد کی ہو؟ اس صفحہ میں ترمیم کریں اور اسے شامل کریں!
ضمیمہ
جیسا کہ اوپر ذکر کیا گیا ہے، DAG جنریشن کے لیے استعمال ہونے والا RNG نمبر تھیوری کے کچھ نتائج پر انحصار کرتا ہے۔ سب سے پہلے، ہم یہ یقین دہانی فراہم کرتے ہیں کہ Lehmer RNG جو picker متغیر کی بنیاد ہے اس کا ایک وسیع دور ہے۔ دوسرا، ہم دکھاتے ہیں کہ pow(x,3,P)، x کو 1 یا P-1 پر میپ نہیں کرے گا بشرطیکہ شروع کرنے کے لیے x ∈ [2,P-2] ہو۔ آخر میں، ہم دکھاتے ہیں کہ جب ہیشنگ فنکشن کے طور پر علاج کیا جاتا ہے تو pow(x,3,P) کی تصادم کی شرح کم ہوتی ہے۔
Lehmer رینڈم نمبر جنریٹر
جبکہ produce_dag فنکشن کو غیر جانبدارانہ بے ترتیب نمبر تیار کرنے کی ضرورت نہیں ہے، ایک ممکنہ خطرہ یہ ہے کہ seed**i % P صرف مٹھی بھر قدریں لیتا ہے۔ یہ ان مائنرز کو فائدہ فراہم کر سکتا ہے جو پیٹرن کو پہچانتے ہیں ان لوگوں پر جو نہیں پہچانتے ہیں۔
اس سے بچنے کے لیے، نمبر تھیوری کے ایک نتیجے کی اپیل کی جاتی ہے۔ ایک سیف پرائم (opens in a new tab) کو ایک پرائم P کے طور پر بیان کیا گیا ہے جیسے (P-1)/2 بھی پرائم ہے۔ ضرب گروپ (opens in a new tab) ℤ/nℤ کے ایک رکن x کا آرڈر کم سے کم m کے طور پر بیان کیا گیا ہے جیسے کہ
xᵐ mod P ≡ 1ان تعریفوں کو دیکھتے ہوئے، ہمارے پاس ہے:
مشاہدہ 1۔
xکو ایک سیف پرائمPکے لیے ضرب گروپℤ/Pℤکا رکن ہونے دیں۔ اگرx mod P ≠ 1 mod Pاورx mod P ≠ P-1 mod P، توxکا آرڈر یا توP-1ہے یا(P-1)/2۔
ثبوت۔ چونکہ P ایک سیف پرائم ہے، تب [Lagrange کے تھیورم][lagrange] کے ذریعے ہمارے پاس یہ ہے کہ x کا آرڈر یا تو 1، 2، (P-1)/2، یا P-1 ہے۔
x کا آرڈر 1 نہیں ہو سکتا، چونکہ فرما کے چھوٹے تھیورم کے ذریعے ہمارے پاس ہے:
xP-1 mod P ≡ 1
لہذا x کو ℤ/nℤ کی ایک ضربی شناخت ہونا چاہیے، جو کہ منفرد ہے۔ چونکہ ہم نے مفروضے کے مطابق یہ فرض کیا ہے کہ x ≠ 1 ہے، یہ ممکن نہیں ہے۔
x کا آرڈر 2 نہیں ہو سکتا جب تک کہ x = P-1 نہ ہو، چونکہ یہ اس بات کی خلاف ورزی کرے گا کہ P پرائم ہے۔
مذکورہ بالا تجویز سے، ہم یہ پہچان سکتے ہیں کہ (picker * init) % P کو دہرانے سے سائیکل کی لمبائی کم از کم (P-1)/2 ہو گی۔ اس کی وجہ یہ ہے کہ ہم نے P کو دو کی اعلی طاقت کے تقریباً برابر ایک سیف پرائم کے طور پر منتخب کیا ہے، اور init وقفہ [2,2**256+1] میں ہے۔ P کی شدت کو دیکھتے ہوئے، ہمیں ماڈیولر ایکسپونینشن سے کبھی بھی سائیکل کی توقع نہیں کرنی چاہیے۔
جب ہم DAG میں پہلا سیل تفویض کر رہے ہیں (متغیر جس پر init کا لیبل لگا ہوا ہے)، ہم pow(sha3(seed) + 2, 3, P) کا حساب لگاتے ہیں۔ پہلی نظر میں، یہ اس بات کی ضمانت نہیں دیتا کہ نتیجہ نہ تو 1 ہے اور نہ ہی P-1۔ تاہم، چونکہ P-1 ایک سیف پرائم ہے، ہمارے پاس مندرجہ ذیل اضافی یقین دہانی ہے، جو مشاہدہ 1 کا نتیجہ ہے:
مشاہدہ 2۔
xکو ایک سیف پرائمPکے لیے ضرب گروپℤ/Pℤکا رکن ہونے دیں، اورwکو ایک قدرتی عدد ہونے دیں۔ اگرx mod P ≠ 1 mod Pاورx mod P ≠ P-1 mod P، نیزw mod P ≠ P-1 mod Pاورw mod P ≠ 0 mod P، توxʷ mod P ≠ 1 mod Pاورxʷ mod P ≠ P-1 mod P
ماڈیولر ایکسپونینشن بطور ہیش فنکشن
P اور w کی کچھ قدروں کے لیے، فنکشن pow(x, w, P) میں بہت سے تصادم ہو سکتے ہیں۔ مثال کے طور پر، pow(x,9,19) صرف {1,18} کی قدریں لیتا ہے۔
یہ دیکھتے ہوئے کہ P پرائم ہے، تب ایک ماڈیولر ایکسپونینشن ہیشنگ فنکشن کے لیے ایک مناسب w مندرجہ ذیل نتیجے کا استعمال کرتے ہوئے منتخب کیا جا سکتا ہے:
مشاہدہ 3۔
Pکو ایک پرائم ہونے دیں؛wاورP-1نسبتاً پرائم ہیں اگر اور صرف اگرℤ/Pℤمیں تمامaاورbکے لیے:aʷ mod P ≡ bʷ mod Pاگر اور صرف اگرa mod P ≡ b mod P
اس طرح، یہ دیکھتے ہوئے کہ P پرائم ہے اور w نسبتاً P-1 کے پرائم ہے، ہمارے پاس یہ ہے کہ |{pow(x, w, P) : x ∈ ℤ}| = P، جس کا مطلب ہے کہ ہیشنگ فنکشن میں تصادم کی کم سے کم ممکنہ شرح ہے۔
خاص صورت میں کہ P ایک سیف پرائم ہے جیسا کہ ہم نے منتخب کیا ہے، تب P-1 کے صرف عوامل 1، 2، (P-1)/2 اور P-1 ہیں۔ چونکہ P > 7، ہم جانتے ہیں کہ 3 نسبتاً P-1 کے پرائم ہے، لہذا w=3 مذکورہ بالا تجویز کو پورا کرتا ہے۔
زیادہ موثر کیشے پر مبنی تشخیصی الگورتھم
1def quick_calc(params, seed, p):2 cache = produce_dag(params, seed, params["cache_size"])3 return quick_calc_cached(cache, params, p)45def quick_calc_cached(cache, params, p):6 P = params["P"]7 if p < len(cache):8 return cache[p]9 else:10 x = pow(cache[0], p + 1, P)11 for _ in range(params["k"]):12 x ^= quick_calc_cached(cache, params, x % p)13 return pow(x, params["w"], P)1415def quick_hashimoto(seed, dagsize, params, header, nonce):16 cache = produce_dag(params, seed, params["cache_size"])17 return quick_hashimoto_cached(cache, dagsize, params, header, nonce)1819def quick_hashimoto_cached(cache, dagsize, params, header, nonce):20 m = dagsize // 221 mask = 2**64 - 122 mix = sha3(encode_int(nonce) + header)23 for _ in range(params["accesses"]):24 mix ^= quick_calc_cached(cache, params, m + (mix & mask) % m)25 return dbl_sha3(mix)سب دکھائیں