Dagger-Hashimoto
Dagger-Hashimoto என்பது எத்திரியம் சுரங்கப்பணி அல்காரிதத்திற்கான அசல் ஆராய்ச்சி செயலாக்கம் மற்றும் விவரக்குறிப்பாகும். Dagger-Hashimoto-க்கு பதிலாக எத்ஹாஷ் கொண்டுவரப்பட்டது. 15 செப்டம்பர் 2022 அன்று ஒருங்கிணைப்பு நிகழ்வில் சுரங்கப்பணி முற்றிலுமாக நிறுத்தப்பட்டது. அதிலிருந்து, எத்திரியம் அதற்குப் பதிலாக உரிமைச் சான்று (PoS) பொறிமுறையைப் பயன்படுத்திப் பாதுகாக்கப்படுகிறது. இந்தப் பக்கம் வரலாற்று ஆர்வத்திற்காக மட்டுமே - இங்குள்ள தகவல்கள் ஒருங்கிணைப்புக்குப் பிந்தைய எத்திரியத்திற்கு இனி பொருந்தாது.
முன்நிபந்தனைகள்
இந்தப் பக்கத்தை நன்கு புரிந்துகொள்ள, முதலில் பணிச் சான்று (PoW) ஒருமித்த கருத்து, சுரங்கப்பணி மற்றும் சுரங்கப்பணி அல்காரிதங்கள் பற்றிப் படிக்குமாறு பரிந்துரைக்கிறோம்.
Dagger-Hashimoto
Dagger-Hashimoto இரண்டு இலக்குகளைப் பூர்த்தி செய்வதை நோக்கமாகக் கொண்டுள்ளது:
- ASIC-எதிர்ப்பு: அல்காரிதத்திற்காகச் சிறப்பு வன்பொருளை உருவாக்குவதன் மூலம் கிடைக்கும் பலன் முடிந்தவரை சிறியதாக இருக்க வேண்டும்
- இலகுரக கிளையண்ட் சரிபார்ப்புத் திறன்: ஒரு தொகுதியை இலகுரக கிளையண்ட் மூலம் திறமையாகச் சரிபார்க்க முடிய வேண்டும்.
கூடுதல் மாற்றத்துடன், விரும்பினால் மூன்றாவது இலக்கை எவ்வாறு நிறைவேற்றுவது என்பதையும் நாங்கள் குறிப்பிடுகிறோம், ஆனால் இது கூடுதல் சிக்கலான தன்மையை ஏற்படுத்தும்:
முழுச் சங்கிலி சேமிப்பகம்: சுரங்கப்பணிக்கு முழுமையான தொகுதிச்சங்கிலி நிலையைச் சேமிக்க வேண்டியது அவசியம் (எத்திரியம் நிலை ட்ரையின் ஒழுங்கற்ற கட்டமைப்பு காரணமாக, சில கத்தரிப்புகள் சாத்தியமாகும் என்று நாங்கள் எதிர்பார்க்கிறோம், குறிப்பாக அடிக்கடி பயன்படுத்தப்படும் சில ஒப்பந்தங்களில், ஆனால் இதை நாங்கள் குறைக்க விரும்புகிறோம்).
DAG உருவாக்கம்
அல்காரிதத்திற்கான குறியீடு கீழே Python-இல் வரையறுக்கப்படும். முதலில், குறிப்பிட்ட துல்லியத்தின் கையொப்பமிடப்படாத முழு எண்களை சரங்களாக மாற்றுவதற்கு encode_int-ஐ வழங்குகிறோம். அதன் தலைகீழ் மாற்றமும் கொடுக்கப்பட்டுள்ளது:
NUM_BITS = 512
def encode_int(x):
"Encode an integer x as a string of 64 characters using a big-endian scheme"
o = ''
for _ in range(NUM_BITS / 8):
o = chr(x % 256) + o
x //= 256
return o
def decode_int(s):
"Unencode an integer x from a string using a big-endian scheme"
x = 0
for c in s:
x *= 256
x += ord(c)
return x
அடுத்து sha3 என்பது ஒரு முழு எண்ணை எடுத்து ஒரு முழு எண்ணை வெளியிடும் ஒரு செயற்கூறு என்றும், dbl_sha3 என்பது ஒரு double-sha3 செயற்கூறு என்றும் கருதுகிறோம்; இந்தக் குறிப்புக் குறியீட்டை ஒரு செயலாக்கமாக மாற்றினால், இதைப் பயன்படுத்தவும்:
from pyethereum import utils
def sha3(x):
if isinstance(x, (int, long)):
x = encode_int(x)
return decode_int(utils.sha3(x))
def dbl_sha3(x):
if isinstance(x, (int, long)):
x = encode_int(x)
return decode_int(utils.sha3(utils.sha3(x)))
அளவுருக்கள்
அல்காரிதத்திற்குப் பயன்படுத்தப்படும் அளவுருக்கள்:
SAFE_PRIME_512 = 2**512 - 38117 # 2**512 ஐ விட குறைவான மிகப்பெரிய பாதுகாப்பான பகா எண்
params = {
"n": 4000055296 * 8 // NUM_BITS, # தரவுத்தொகுப்பின் அளவு (4 ஜிகாபைட்); 65536 இன் மடங்காக இருக்க வேண்டும்
"n_inc": 65536, # ஒரு காலகட்டத்திற்கு n இன் மதிப்பில் ஏற்படும் அதிகரிப்பு; 65536 இன் மடங்காக இருக்க வேண்டும்
# epochtime=20000 உடன் வருடத்திற்கு 882 MB வளர்ச்சியை அளிக்கிறது
"cache_size": 2500, # இலகுரக கிளையண்டின் தற்காலிக சேமிப்பகத்தின் அளவு (இலகுரக
# கிளையண்டால் தேர்ந்தெடுக்கப்படலாம்; அல்காரிதம் விவரக்குறிப்பின் ஒரு பகுதி அல்ல)
"diff": 2**14, # கடினத்தன்மை (தொகுதி மதிப்பீட்டின் போது சரிசெய்யப்படுகிறது)
"epochtime": 100000, # தொகுதிகளில் ஒரு சகாப்தத்தின் நீளம் (தரவுத்தொகுப்பு எவ்வளவு அடிக்கடி புதுப்பிக்கப்படுகிறது)
"k": 1, # ஒரு கணுவின் பெற்றோர்களின் எண்ணிக்கை
"w": w, # மட்டு அடுக்குக்குறி ஹாஷ் செய்தலுக்குப் பயன்படுத்தப்படுகிறது
"accesses": 200, # hashimoto இன் போது தரவுத்தொகுப்பு அணுகல்களின் எண்ணிக்கை
"P": SAFE_PRIME_512 # ஹாஷ் செய்தல் மற்றும் சீரற்ற எண் உருவாக்கத்திற்கான பாதுகாப்பான பகா எண்
}
இந்த நிலையில் P என்பது log₂(P) 512-ஐ விடச் சற்று குறைவாக இருக்கும்படி தேர்ந்தெடுக்கப்பட்ட ஒரு பகா எண்ணாகும், இது நமது எண்களைக் குறிக்க நாம் பயன்படுத்தும் 512 பிட்களுக்கு ஒத்திருக்கிறது. DAG-இன் பிற்பாதியை மட்டுமே உண்மையில் சேமிக்க வேண்டும் என்பதை நினைவில் கொள்ளவும், எனவே நடைமுறையில் RAM தேவை 1 GB-இல் தொடங்கி ஆண்டுக்கு 441 MB வீதம் வளர்கிறது.
Dagger வரைபடம் உருவாக்குதல்
Dagger வரைபடம் உருவாக்கும் அடிப்படை பின்வருமாறு வரையறுக்கப்படுகிறது:
def produce_dag(params, seed, length):
P = params["P"]
picker = init = pow(sha3(seed), params["w"], P)
o = [init]
for i in range(1, length):
x = picker = (picker * init) % P
for _ in range(params["k"]):
x ^= o[x % i]
o.append(pow(x, params["w"], P))
return o
அடிப்படையில், இது ஒரு வரைபடத்தை sha3(seed) என்ற ஒற்றைக் கணுவாகத் தொடங்குகிறது, அங்கிருந்து முந்தைய சீரற்ற கணுக்களின் அடிப்படையில் மற்ற கணுக்களை வரிசையாகச் சேர்க்கத் தொடங்குகிறது. ஒரு புதிய கணு உருவாக்கப்படும்போது, i-ஐ விடக் குறைவான சில குறியீடுகளைச் சீரற்ற முறையில் தேர்ந்தெடுக்க (மேலே உள்ள x % i-ஐப் பயன்படுத்தி) விதையின் மாடுலர் அடுக்கு கணக்கிடப்படுகிறது, மேலும் அந்தக் குறியீடுகளில் உள்ள கணுக்களின் மதிப்புகள் x-க்கான புதிய மதிப்பை உருவாக்க ஒரு கணக்கீட்டில் பயன்படுத்தப்படுகின்றன, இது பின்னர் குறியீடு i-இல் வரைபடத்தின் மதிப்பை இறுதியாக உருவாக்க ஒரு சிறிய பணிச் சான்று செயற்கூறில் (XOR அடிப்படையில்) செலுத்தப்படுகிறது. இந்த குறிப்பிட்ட வடிவமைப்பின் பின்னணியில் உள்ள காரணம், DAG-இன் தொடர்ச்சியான அணுகலைக் கட்டாயப்படுத்துவதாகும்; தற்போதைய மதிப்பு அறியப்படும் வரை அணுகப்படும் DAG-இன் அடுத்த மதிப்பைத் தீர்மானிக்க முடியாது. இறுதியாக, மாடுலர் அடுக்குக்குறி முடிவை மேலும் ஹாஷ் செய்கிறது.
இந்த அல்காரிதம் எண் கோட்பாட்டின் பல முடிவுகளைச் சார்ந்துள்ளது. இது குறித்த விவாதத்திற்கு கீழே உள்ள பிற்சேர்க்கையைப் பார்க்கவும்.
இலகுரக கிளையண்ட் மதிப்பீடு
மேற்கண்ட வரைபடக் கட்டுமானம், வரைபடத்தில் உள்ள ஒவ்வொரு கணுவையும் ஒரு சிறிய எண்ணிக்கையிலான கணுக்களின் துணை மரத்தைக் கணக்கிடுவதன் மூலம் மறுகட்டமைக்க அனுமதிக்க உத்தேசித்துள்ளது, இதற்குச் சிறிய அளவிலான துணை நினைவகம் மட்டுமே தேவைப்படும். k=1 உடன், துணை மரம் என்பது DAG-இல் உள்ள முதல் உறுப்பு வரை செல்லும் மதிப்புகளின் சங்கிலி மட்டுமே என்பதை நினைவில் கொள்ளவும்.
DAG-க்கான இலகுரக கிளையண்ட் கணினி செயற்கூறு பின்வருமாறு செயல்படுகிறது:
def quick_calc(params, seed, p):
w, P = params["w"], params["P"]
cache = {}
def quick_calc_cached(p):
if p in cache:
pass
elif p == 0:
cache[p] = pow(sha3(seed), w, P)
else:
x = pow(sha3(seed), (p + 1) * w, P)
for _ in range(params["k"]):
x ^= quick_calc_cached(x % p)
cache[p] = pow(x, w, P)
return cache[p]
return quick_calc_cached(p)
அடிப்படையில், இது மேற்கண்ட அல்காரிதத்தின் மறுஎழுத்தாகும், இது முழு DAG-க்கான மதிப்புகளைக் கணக்கிடும் லூப்பை அகற்றி, முந்தைய கணு தேடலை ஒரு சுழல்நிலை அழைப்பு அல்லது கேச் தேடலுடன் மாற்றுகிறது. k=1-க்கு கேச் தேவையற்றது என்பதை நினைவில் கொள்ளவும், இருப்பினும் மேலும் ஒரு உகப்பாக்கம் உண்மையில் DAG-இன் முதல் சில ஆயிரம் மதிப்புகளை முன்கூட்டியே கணக்கிட்டு, அதைக் கணக்கீடுகளுக்கான நிலையான கேச்சாக வைத்திருக்கிறது; இதற்கான குறியீட்டுச் செயலாக்கத்திற்குப் பிற்சேர்க்கையைப் பார்க்கவும்.
DAG-களின் இரட்டை பஃபர்
ஒரு முழு கிளையண்டில், மேற்கண்ட சூத்திரத்தால் உருவாக்கப்பட்ட 2 DAG-களின் இரட்டை பஃபர் (opens in a new tab) பயன்படுத்தப்படுகிறது. மேலே உள்ள அளவுருக்களின்படி ஒவ்வொரு epochtime தொகுதிகளுக்கும் DAG-கள் உருவாக்கப்படுகின்றன என்பதே இதன் கருத்தாகும். கிளையண்ட் உருவாக்கப்பட்ட சமீபத்திய DAG-ஐப் பயன்படுத்துவதற்குப் பதிலாக, முந்தையதைப் பயன்படுத்துகிறது. இதன் நன்மை என்னவென்றால், சுரங்கப்பணியாளர்கள் திடீரென அனைத்துத் தரவையும் மறுகணக்கீடு செய்ய வேண்டிய ஒரு படியைச் சேர்க்க வேண்டிய அவசியமின்றி, காலப்போக்கில் DAG-களை மாற்ற இது அனுமதிக்கிறது. இல்லையெனில், சீரான இடைவெளியில் சங்கிலிச் செயலாக்கத்தில் திடீர் தற்காலிக மந்தநிலை ஏற்படவும், மையப்படுத்தல் வியத்தகு முறையில் அதிகரிக்கவும் வாய்ப்புள்ளது. இதனால் அனைத்துத் தரவும் மறுகணக்கீடு செய்யப்படுவதற்கு முந்தைய அந்தச் சில நிமிடங்களில் 51% தாக்குதல் அபாயங்கள் ஏற்படும்.
ஒரு தொகுதிக்கான பணியைக் கணக்கிடப் பயன்படுத்தப்படும் DAG-களின் தொகுப்பை உருவாக்கப் பயன்படுத்தப்படும் அல்காரிதம் பின்வருமாறு:
def get_prevhash(n):
from pyethereum.blocks import GENESIS_PREVHASH
from pyethereum import chain_manager
if n <= 0:
return hash_to_int(GENESIS_PREVHASH)
else:
prevhash = chain_manager.index.get_block_by_number(n - 1)
return decode_int(prevhash)
def get_seedset(params, block):
seedset = {}
seedset["back_number"] = block.number - (block.number % params["epochtime"])
seedset["back_hash"] = get_prevhash(seedset["back_number"])
seedset["front_number"] = max(seedset["back_number"] - params["epochtime"], 0)
seedset["front_hash"] = get_prevhash(seedset["front_number"])
return seedset
def get_dagsize(params, block):
return params["n"] + (block.number // params["epochtime"]) * params["n_inc"]
def get_daggerset(params, block):
dagsz = get_dagsize(params, block)
seedset = get_seedset(params, block)
if seedset["front_hash"] <= 0:
# பின் இடையகம் சாத்தியமில்லை, முன் இடையகத்தை மட்டும் உருவாக்கவும்
return {"front": {"dag": produce_dag(params, seedset["front_hash"], dagsz),
"block_number": 0}}
else:
return {"front": {"dag": produce_dag(params, seedset["front_hash"], dagsz),
"block_number": seedset["front_number"]},
"back": {"dag": produce_dag(params, seedset["back_hash"], dagsz),
"block_number": seedset["back_number"]}}
Hashimoto
அசல் Hashimoto-வின் பின்னணியில் உள்ள கருத்து, தொகுதிச்சங்கிலியை ஒரு தரவுத்தொகுப்பாகப் பயன்படுத்துவதாகும், இது தொகுதிச்சங்கிலியிலிருந்து N குறியீடுகளைத் தேர்ந்தெடுக்கும் ஒரு கணக்கீட்டைச் செய்கிறது, அந்தக் குறியீடுகளில் உள்ள பரிவர்த்தனைகளைச் சேகரிக்கிறது, இந்தத் தரவின் XOR-ஐச் செய்கிறது மற்றும் முடிவின் ஹாஷை வழங்குகிறது. நிலைத்தன்மைக்காக Python-க்கு மொழிபெயர்க்கப்பட்ட Thaddeus Dryja-வின் அசல் அல்காரிதம் பின்வருமாறு:
def orig_hashimoto(prev_hash, merkle_root, list_of_transactions, nonce):
hash_output_A = sha256(prev_hash + merkle_root + nonce)
txid_mix = 0
for i in range(64):
shifted_A = hash_output_A >> i
transaction = shifted_A % len(list_of_transactions)
txid_mix ^= list_of_transactions[transaction] << i
return txid_mix ^ (nonce << 192)
துரதிர்ஷ்டவசமாக, Hashimoto RAM கடினமானதாகக் கருதப்பட்டாலும், இது 256-பிட் எண்கணிதத்தைச் சார்ந்துள்ளது, இது கணிசமான கணக்கீட்டுச் சுமையைக் கொண்டுள்ளது. இருப்பினும், Dagger-Hashimoto இந்தச் சிக்கலைத் தீர்க்க அதன் தரவுத்தொகுப்பைக் குறியிடும்போது மிகக் குறைந்த முக்கியத்துவம் வாய்ந்த 64 பிட்களை மட்டுமே பயன்படுத்துகிறது.
def hashimoto(dag, dagsize, params, header, nonce):
m = dagsize / 2
mix = sha3(encode_int(nonce) + header)
for _ in range(params["accesses"]):
mix ^= dag[m + (mix % 2**64) % m]
return dbl_sha3(mix)
இரட்டை SHA3-இன் பயன்பாடு, சரியான இடைநிலை மதிப்பு வழங்கப்பட்டதா என்பதை மட்டுமே சரிபார்த்து, பூஜ்ஜிய-தரவு, உடனடி முன்-சரிபார்ப்பு வடிவத்தை அனுமதிக்கிறது. பணிச் சான்றின் இந்த வெளி அடுக்கு மிகவும் ASIC-நட்பானது மற்றும் மிகவும் பலவீனமானது, ஆனால் உடனடியாக நிராகரிக்கப்படாத ஒரு தொகுதியை உருவாக்க அந்தச் சிறிய அளவிலான பணி செய்யப்பட வேண்டும் என்பதால் DDoS-ஐ இன்னும் கடினமாக்க இது உள்ளது. இலகுரக கிளையண்ட் பதிப்பு இதோ:
def quick_hashimoto(seed, dagsize, params, header, nonce):
m = dagsize // 2
mix = sha3(nonce + header)
for _ in range(params["accesses"]):
mix ^= quick_calc(params, seed, m + (mix % 2**64) % m)
return dbl_sha3(mix)
சுரங்கப்பணி மற்றும் சரிபார்த்தல்
இப்போது, இவை அனைத்தையும் சுரங்கப்பணி அல்காரிதத்தில் இணைப்போம்:
def mine(daggerset, params, block):
from random import randint
nonce = randint(0, 2**64)
while 1:
result = hashimoto(daggerset, get_dagsize(params, block),
params, decode_int(block.prevhash), nonce)
if result * params["diff"] < 2**256:
break
nonce += 1
if nonce >= 2**64:
nonce = 0
return nonce
சரிபார்ப்பு அல்காரிதம் இதோ:
def verify(daggerset, params, block, nonce):
result = hashimoto(daggerset, get_dagsize(params, block),
params, decode_int(block.prevhash), nonce)
return result * params["diff"] < 2**256
இலகுரக கிளையண்ட் நட்பு சரிபார்ப்பு:
def light_verify(params, header, nonce):
seedset = get_seedset(params, block)
result = quick_hashimoto(seedset["front_hash"], get_dagsize(params, block),
params, decode_int(block.prevhash), nonce)
return result * params["diff"] < 2**256
மேலும், Dagger-Hashimoto தொகுதித் தலைப்பில் கூடுதல் தேவைகளை விதிக்கிறது என்பதை நினைவில் கொள்ளவும்:
- இரண்டு அடுக்கு சரிபார்ப்பு வேலை செய்ய, ஒரு தொகுதித் தலைப்பில் நான்ஸ் மற்றும் நடுத்தர மதிப்பு pre-sha3 ஆகிய இரண்டும் இருக்க வேண்டும்
- எங்காவது, ஒரு தொகுதித் தலைப்பு தற்போதைய விதைத்தொகுப்பின் sha3-ஐச் சேமிக்க வேண்டும்
மேலும் படிக்க
உங்களுக்கு உதவிய சமூக வளம் பற்றித் தெரியுமா? இந்தப் பக்கத்தைத் திருத்தி அதைச் சேர்க்கவும்!
பிற்சேர்க்கை
மேலே குறிப்பிட்டுள்ளபடி, DAG உருவாக்கத்திற்குப் பயன்படுத்தப்படும் RNG எண் கோட்பாட்டின் சில முடிவுகளைச் சார்ந்துள்ளது. முதலாவதாக, picker மாறிக்கு அடிப்படையான Lehmer RNG ஒரு பரந்த காலத்தைக் கொண்டுள்ளது என்பதற்கான உத்தரவாதத்தை நாங்கள் வழங்குகிறோம். இரண்டாவதாக, தொடங்குவதற்கு x ∈ [2,P-2] வழங்கப்பட்டால் pow(x,3,P) ஆனது x-ஐ 1 அல்லது P-1-க்கு மேப் செய்யாது என்பதைக் காட்டுகிறோம். இறுதியாக, ஒரு ஹாஷ் செயற்கூறாகக் கருதப்படும்போது pow(x,3,P) குறைந்த மோதல் விகிதத்தைக் கொண்டுள்ளது என்பதைக் காட்டுகிறோம்.
Lehmer சீரற்ற எண் உருவாக்கி
produce_dag செயற்கூறு சார்பற்ற சீரற்ற எண்களை உருவாக்க வேண்டிய அவசியமில்லை என்றாலும், seed**i % P ஒரு சில மதிப்புகளை மட்டுமே எடுத்துக்கொள்கிறது என்பது ஒரு சாத்தியமான அச்சுறுத்தலாகும். இது வடிவத்தை அங்கீகரிக்காதவர்களை விட அங்கீகரிக்கும் சுரங்கப்பணியாளர்களுக்கு ஒரு நன்மையை வழங்கக்கூடும்.
இதைத் தவிர்க்க, எண் கோட்பாட்டிலிருந்து ஒரு முடிவு பயன்படுத்தப்படுகிறது. ஒரு பாதுகாப்பான பகா எண் (Safe Prime) (opens in a new tab) என்பது (P-1)/2-உம் ஒரு பகா எண்ணாக இருக்கும்படியான ஒரு பகா எண் P என வரையறுக்கப்படுகிறது. பெருக்கல் குழுவின் (opens in a new tab) ℤ/nℤ உறுப்பினர் x-இன் வரிசை என்பது
xᵐ mod P ≡ 1என்றவாறு இருக்கும் குறைந்தபட்ச
m என வரையறுக்கப்படுகிறது.
இந்த வரையறைகளைக் கொண்டு, நாம் பெறுவது:
கவனிப்பு 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 ஆக இருக்க முடியாது, ஏனெனில் Fermat-இன் சிறிய தேற்றத்தின்படி நாம் பெறுவது:
xP-1 mod P ≡ 1
எனவே x என்பது ℤ/nℤ-இன் பெருக்கல் அடையாளமாக இருக்க வேண்டும், இது தனித்துவமானது. அனுமானத்தின்படி x ≠ 1 என்று நாம் கருதியதால், இது சாத்தியமில்லை.
x = P-1 ஆக இருந்தாலொழிய x-இன் வரிசை 2 ஆக இருக்க முடியாது, ஏனெனில் இது 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ஒரு பகா எண்ணாக இருக்கட்டும்;ℤ/Pℤ-இல் உள்ள அனைத்துaமற்றும்b-க்கும்:என்ற நிபந்தனை பூர்த்தியானால், பூர்த்தியானால் மட்டுமே a mod P ≡ b mod Pஆக இருந்தால், இருந்தால் மட்டுமேaʷ mod P ≡ bʷ mod Pஆக இருக்கும்wமற்றும்P-1ஆகியவை சார்புப் பகா எண்களாக இருக்கும்.
எனவே, 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 மேற்கண்ட முன்மொழிவைப் பூர்த்தி செய்கிறது.
மிகவும் திறமையான கேச் அடிப்படையிலான மதிப்பீட்டு அல்காரிதம்
def quick_calc(params, seed, p):
cache = produce_dag(params, seed, params["cache_size"])
return quick_calc_cached(cache, params, p)
def quick_calc_cached(cache, params, p):
P = params["P"]
if p < len(cache):
return cache[p]
else:
x = pow(cache[0], p + 1, P)
for _ in range(params["k"]):
x ^= quick_calc_cached(cache, params, x % p)
return pow(x, params["w"], P)
def quick_hashimoto(seed, dagsize, params, header, nonce):
cache = produce_dag(params, seed, params["cache_size"])
return quick_hashimoto_cached(cache, dagsize, params, header, nonce)
def quick_hashimoto_cached(cache, dagsize, params, header, nonce):
m = dagsize // 2
mask = 2**64 - 1
mix = sha3(encode_int(nonce) + header)
for _ in range(params["accesses"]):
mix ^= quick_calc_cached(cache, params, m + (mix & mask) % m)
return dbl_sha3(mix)