Dagger-Hashimoto
Ukurasa ulihaririwa mwisho: 14 Februari 2026
Dagger-Hashimoto ilikuwa utekelezaji wa awali wa utafiti na maelezo ya kanuni ya uchimbaji ya Ethereum. Dagger-Hashimoto ilibadilishwa na Ethash. Uchimbaji ulizimwa kabisa wakati wa Muungano mnamo tarehe 15 Septemba 2022. Tangu wakati huo, Ethereum imekuwa ikilindwa kwa kutumia utaratibu wa uthibitisho wa hisa badala yake. Ukurasa huu ni kwa ajili ya maslahi ya kihistoria - maelezo yaliyomo hapa hayahusiani tena na Ethereum ya baada ya Muungano.
Mahitaji ya awali
Ili kuelewa ukurasa huu vizuri zaidi, tunapendekeza kwanza usome kuhusu makubaliano ya uthibitishaji-wa-kazi, uchimbaji, na kanuni za uchimbaji.
Dagger-Hashimoto
Dagger-Hashimoto inalenga kutimiza malengo mawili:
- Ukinzani-wa-ASIC: faida ya kutengeneza maunzi maalum kwa ajili ya kanuni inapaswa kuwa ndogo iwezekanavyo
- Uthibitisho wa mteja mwepesi: bloku inapaswa kuthibitishwa kwa ufanisi na mteja mwepesi.
Kwa marekebisho ya ziada, pia tunaeleza jinsi ya kutimiza lengo la tatu ikiwa inahitajika, lakini kwa gharama ya utata wa ziada:
Hifadhi kamili ya mnyororo: uchimbaji unapaswa kuhitaji hifadhi ya hali kamili ya mnyororo wa bloku (kutokana na muundo usio wa kawaida wa trie ya hali ya Ethereum, tunatarajia kuwa upunguzaji fulani utawezekana, hasa wa baadhi ya mikataba inayotumiwa mara kwa mara, lakini tunataka kupunguza hili).
Uzazi wa DAG
Msimbo wa kanuni utaelezwa katika Python hapa chini. Kwanza, tunatoa encode_int kwa ajili ya kupanga nambari kamili zisizo na alama za usahihi maalum kuwa mifuatano. Kinyume chake pia kimetolewa:
1NUM_BITS = 51223def encode_int(x):4 "Weka nambari kamili x kama mfuatano wa herufi 64 ukitumia mpango wa big-endian"5 o = ''6 for _ in range(NUM_BITS / 8):7 o = chr(x % 256) + o8 x //= 2569 return o1011def decode_int(s):12 "Ondoa msimbo wa nambari kamili x kutoka kwenye mfuatano ukitumia mpango wa big-endian"13 x = 014 for c in s:15 x *= 25616 x += ord(c)17 return xOnyesha yoteIfuatayo, tunadhani kwamba sha3 ni chaguo la kukokotoa linalochukua nambari kamili na kutoa nambari kamili, na dbl_sha3 ni chaguo la kukokotoa la double-sha3; ikiwa unabadilisha msimbo huu wa marejeleo kuwa utekelezaji, tumia:
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)))Onyesha yoteVigezo
Vigezo vinavyotumika kwa kanuni ni:
1SAFE_PRIME_512 = 2**512 - 38117 # Nambari Kuu Salama iliyo chini ya 2**51223params = {4 "n": 4000055296 * 8 // NUM_BITS, # Ukubwa wa seti ya data (Gigabaiti 4); LAZIMA IWE ZIDISHO LA 655365 "n_inc": 65536, # Ongezeko la thamani ya n kwa kila kipindi; LAZIMA IWE ZIDISHO LA 655366 # na epochtime=20000 inatoa ukuaji wa MB 882 kwa mwaka7 "cache_size": 2500, # Ukubwa wa kache ya mteja mwepesi (inaweza kuchaguliwa na mteja mwepesi; si sehemu ya vipimo vya kanuni)8 "diff": 2**14, # Ugumu (hurekebishwa wakati wa tathmini ya bloku)9 "epochtime": 100000, # Urefu wa kipindi katika bloku (mara ngapi seti ya data inasasishwa)10 "k": 1, # Idadi ya wazazi wa nodi11 "w": w, # Inatumika kwa ajili ya uhasishaji wa kipeo cha kimodula12 "accesses": 200, # Idadi ya ufikiaji wa seti ya data wakati wa hashimoto13 "P": SAFE_PRIME_512 # Nambari Kuu Salama kwa ajili ya uhasishaji na uzalishaji wa nambari nasibu14}Onyesha yoteP katika kesi hii ni nambari kuu iliyochaguliwa kiasi kwamba log₂(P) ni chini kidogo ya 512, ambayo inalingana na biti 512 ambazo tumekuwa tukitumia kuwakilisha nambari zetu. Kumbuka kwamba ni nusu ya mwisho tu ya DAG ndiyo inayohitaji kuhifadhiwa, kwa hivyo mahitaji halisi ya RAM huanza kwa GB 1 na kukua kwa MB 441 kwa mwaka.
Uundaji wa grafu ya Dagger
Asili ya uundaji wa grafu ya dagger imefafanuliwa kama ifuatavyo:
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 oOnyesha yoteKimsingi, inaanza grafu kama nodi moja, sha3(seed), na kutoka hapo huanza kuongeza kwa mfuatano nodi zingine kulingana na nodi za awali za nasibu. Wakati nodi mpya inaundwa, nguvu ya kimodula ya mbegu huhesabiwa ili kuchagua kwa nasibu fahirisi fulani zilizo chini ya i (kwa kutumia x % i hapo juu), na thamani za nodi kwenye fahirisi hizo hutumiwa katika hesabu ili kuzalisha thamani mpya ya x, ambayo hupelekwa kwenye chaguo dogo la uthibitisho wa kazi (kulingana na XOR) ili hatimaye kuzalisha thamani ya grafu kwenye fahirisi i. Sababu ya muundo huu maalum ni kulazimisha ufikiaji wa mfuatano wa DAG; thamani inayofuata ya DAG itakayofikiwa haiwezi kubainishwa hadi thamani ya sasa ijulikane. Mwishowe, upeo wa kimodula huhasisha matokeo zaidi.
Kanuni hii inategemea matokeo kadhaa kutoka kwa nadharia ya nambari. Tazama kiambatisho hapa chini kwa majadiliano.
Tathmini ya mteja mwepesi
Uundaji wa grafu hapo juu unakusudia kuruhusu kila nodi kwenye grafu kujengwa upya kwa kukokotoa mti mdogo wa idadi ndogo tu ya nodi na kuhitaji kiasi kidogo tu cha kumbukumbu saidizi. Kumbuka kuwa kwa k=1, mti mdogo ni mnyororo tu wa thamani zinazopanda hadi kwenye elementi ya kwanza katika DAG.
Chaguo la kukokotoa la kompyuta ya mteja mwepesi kwa DAG hufanya kazi kama ifuatavyo:
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)Onyesha yoteKimsingi, ni uandishi upya wa kanuni iliyo hapo juu ambao huondoa kitanzi cha kukokotoa thamani za DAG nzima na kubadilisha utafutaji wa nodi ya awali na wito wa kujirudia au utafutaji wa kache. Kumbuka kuwa kwa k=1 kache si lazima, ingawa uboreshaji zaidi kwa kweli huhesabu mapema thamani elfu chache za kwanza za DAG na kuweka hiyo kama kache tuli kwa ajili ya hesabu; tazama kiambatisho kwa utekelezaji wa msimbo wa hili.
Bafa maradufu ya DAGs
Katika mteja kamili, bafa maradufuopens in a new tab ya DAGs 2 zinazozalishwa na fomula iliyo hapo juu hutumiwa. Wazo ni kwamba DAGs huzalishwa kila epochtime idadi ya bloku kulingana na vigezo vilivyo hapo juu. Badala ya mteja kutumia DAG ya hivi karibuni iliyozalishwa, hutumia ile ya awali. Faida ya hili ni kwamba inaruhusu DAGs kubadilishwa kwa muda bila kuhitaji kujumuisha hatua ambapo wachimbaji lazima ghafla wahesabu upya data yote. Vinginevyo, kuna uwezekano wa kupungua kwa ghafla kwa muda katika usindikaji wa mnyororo kwa vipindi vya kawaida na kuongezeka kwa kiasi kikubwa kwa umilikishwaji. Hivyo hatari za shambulizi la asilimia 51% ndani ya dakika hizo chache kabla ya data yote kuhesabiwa upya.
Kanuni inayotumika kuzalisha seti ya DAGs zinazotumika kukokotoa kazi kwa ajili ya bloku ni kama ifuatavyo:
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"]}}Onyesha yoteHashimoto
Wazo la awali la Hashimoto ni kutumia mnyororo wa bloku kama seti ya data, kufanya hesabu inayochagua fahirisi za N kutoka kwenye mnyororo wa bloku, kukusanya miamala kwenye fahirisi hizo, kufanya XOR ya data hii, na kurudisha hashi ya matokeo. Kanuni ya awali ya Thaddeus Dryja, iliyotafsiriwa kwa Python kwa ajili ya uwiano, ni kama ifuatavyo:
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)Kwa bahati mbaya, ingawa Hashimoto inachukuliwa kuwa ngumu kwa RAM, inategemea hesabu za biti 256, ambazo zina gharama kubwa za kikokotozi. Hata hivyo, Dagger-Hashimoto hutumia tu biti 64 za chini kabisa wakati wa kufaharisi seti yake ya data ili kushughulikia suala hili.
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)Matumizi ya SHA3 maradufu huruhusu aina ya uthibitishaji wa awali wa data-sifuri, karibu wa papo hapo, kuthibitisha tu kwamba thamani sahihi ya kati ilitolewa. Safu hii ya nje ya uthibitishaji-wa-kazi inafaa sana kwa ASIC na ni dhaifu kiasi, lakini ipo ili kufanya DDoS iwe ngumu zaidi kwa kuwa kiasi hicho kidogo cha kazi lazima kifanyike ili kuzalisha bloku ambayo haitakataliwa mara moja. Hii ndiyo toleo la mteja-mwepesi:
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)Uchimbaji na uthibitishaji
Sasa, hebu tuviweke vyote pamoja katika kanuni ya uchimbaji:
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 nonceOnyesha yoteHii ndiyo kanuni ya uthibitishaji:
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**256Uthibitishaji unaofaa kwa mteja-mwepesi:
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**256Pia, kumbuka kuwa Dagger-Hashimoto inaweka mahitaji ya ziada kwenye kichwa cha bloku:
- Ili uthibitishaji wa safu-mbili ufanye kazi, kichwa cha bloku lazima kiwe na nonce na thamani ya kati kabla ya sha3
- Mahali fulani, kichwa cha bloku lazima kihifadhi sha3 ya seti ya mbegu ya sasa
Masomo zaidi
Unajua rasilimali ya jamii iliyokusaidia? Hariri ukurasa huu na uiongeze!_
Kiambatisho
Kama ilivyoelezwa hapo juu, RNG inayotumika kwa uzalishaji wa DAG inategemea matokeo fulani kutoka kwa nadharia ya nambari. Kwanza, tunatoa uhakikisho kwamba Lehmer RNG ambayo ni msingi wa kigezo cha picker ina kipindi kirefu. Pili, tunaonyesha kwamba pow(x,3,P) haitapanga x kwa 1 au P-1 mradi x ∈ [2,P-2] mwanzoni. Mwishowe, tunaonyesha kwamba pow(x,3,P) ina kiwango cha chini cha mgongano inapotumika kama chaguo la kukokotoa la uhasishaji.
Jenereta ya nambari nasibu ya Lehmer
Ingawa chaguo la kukokotoa la produce_dag halihitaji kutoa nambari nasibu zisizo na upendeleo, tishio linalowezekana ni kwamba seed**i % P huchukua thamani chache tu. Hii inaweza kuwapa faida wachimbaji wanaotambua mchoro kuliko wale wasiotambua.
Ili kuepuka hili, matokeo kutoka kwa nadharia ya nambari yanatumika. Nambari Kuu Salamaopens in a new tab inafafanuliwa kuwa nambari kuu P kiasi kwamba (P-1)/2 pia ni nambari kuu. Mpangilio wa mwanachama x wa kikundi cha kuzidishaopens in a new tab ℤ/nℤ unafafanuliwa kuwa m ndogo zaidi kiasi kwamba
xᵐ mod P ≡ 1Kutokana na ufafanuzi huu, tuna:
Uchunguzi 1. Acha
xiwe mwanachama wa kikundi cha kuzidishaℤ/Pℤkwa nambari kuu salamaP. Ikiwax mod P ≠ 1 mod Pnax mod P ≠ P-1 mod P, basi mpangilio waxniP-1au(P-1)/2.
Uthibitisho. Kwa kuwa P ni nambari kuu salama, basi kwa [Nadharia ya Lagrange][lagrange] tuna kwamba mpangilio wa x ni 1, 2, (P-1)/2, au P-1.
Mpangilio wa x hauwezi kuwa 1, kwa kuwa kwa Nadharia Ndogo ya Fermat tuna:
xP-1 mod P ≡ 1
Kwa hiyo x lazima iwe utambulisho wa kuzidisha wa ℤ/nℤ, ambayo ni ya kipekee. Kwa kuwa tulidhani kwamba x ≠ 1 kwa dhana, hili haliwezekani.
Mpangilio wa x hauwezi kuwa 2 isipokuwa x = P-1, kwa kuwa hili lingekiuka kwamba P ni nambari kuu.
Kutoka kwa pendekezo lililo hapo juu, tunaweza kutambua kwamba kurudia (picker * init) % P kutakuwa na urefu wa mzunguko wa angalau (P-1)/2. Hii ni kwa sababu tulichagua P kuwa nambari kuu salama takriban sawa na kuwa nguvu ya juu ya mbili, na init iko katika muda wa [2,2**256+1]. Kutokana na ukubwa wa P, hatupaswi kamwe kutarajia mzunguko kutoka kwa upeo wa kimodula.
Tunapogawa seli ya kwanza katika DAG (kigezo kilichoandikwa init), tunakokotoa pow(sha3(seed) + 2, 3, P). Kwa mtazamo wa kwanza, hili halihakikishi kwamba matokeo si 1 wala P-1. Hata hivyo, kwa kuwa P-1 ni nambari kuu salama, tuna uhakikisho wa ziada ufuatao, ambao ni matokeo ya Uchunguzi 1:
Uchunguzi 2. Acha
xiwe mwanachama wa kikundi cha kuzidishaℤ/Pℤkwa nambari kuu salamaP, na achawiwe nambari asilia. Ikiwax mod P ≠ 1 mod Pnax mod P ≠ P-1 mod P, na vile vilew mod P ≠ P-1 mod Pnaw mod P ≠ 0 mod P, basixʷ mod P ≠ 1 mod Pnaxʷ mod P ≠ P-1 mod P
Upeo wa kimodula kama chaguo la kukokotoa la hashi
Kwa thamani fulani za P na w, chaguo la kukokotoa la pow(x, w, P) linaweza kuwa na migongano mingi. Kwa mfano, pow(x,9,19) huchukua tu thamani za {1,18}.
Kwa kuwa P ni nambari kuu, basi w inayofaa kwa chaguo la kukokotoa la uhasishaji wa upeo wa kimodula inaweza kuchaguliwa kwa kutumia matokeo yafuatayo:
Uchunguzi 3. Acha
Piwe nambari kuu;wnaP-1ni nambari kuu za jamaa ikiwa na tu ikiwa kwaanabzote katikaℤ/Pℤ:aʷ mod P ≡ bʷ mod Pikiwa na tu ikiwaa mod P ≡ b mod P
Hivyo, kwa kuwa P ni nambari kuu na w ni nambari kuu ya jamaa na P-1, tuna |{pow(x, w, P) : x ∈ ℤ}| = P, ikimaanisha kwamba chaguo la kukokotoa la uhasishaji lina kiwango cha chini zaidi cha mgongano kinachowezekana.
Katika kesi maalum ambapo P ni nambari kuu salama kama tulivyochagua, basi P-1 ina vigawanyo 1, 2, (P-1)/2 na P-1 pekee. Kwa kuwa P > 7, tunajua kwamba 3 ni nambari kuu ya jamaa na P-1, kwa hivyo w=3 inakidhi pendekezo lililo hapo juu.
Kanuni ya tathmini yenye ufanisi zaidi inayotegemea kache
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)Onyesha yote