Μετάβαση στο κύριο περιεχόμενο
Change page

Dagger-Hashimoto

Το Dagger-Hashimoto ήταν η αρχική υλοποίηση έρευνας και προδιαγραφή για τον αλγόριθμο κρυπτόρυξης του Ethereum. Το Dagger-Hashimoto αντικαταστάθηκε από το Ethash. Η κρυπτόρυξη απενεργοποιήθηκε πλήρως με τη Συγχώνευση στις 15 Σεπτεμβρίου 2022. Από τότε, η ασφάλεια του Ethereum επιτυγχάνεται μέσω ενός μηχανισμού απόδειξης συμμετοχής. Αυτή η σελίδα είναι ιστορικού ενδιαφέροντος. Οι πληροφορίες εδώ δεν είναι πλέον σχετικές για τη μετά τη Συγχώνευση εποχή του Ethereum.

Προαπαιτούμενα

Για να κατανοήσετε καλύτερα αυτή τη σελίδα, σας συνιστούμε να διαβάσετε πρώτα για τη συναίνεση απόδειξης εργασίας, την κρυπτόρυξη και τους αλγόριθμους κρυπτόρυξης.

Dagger-Hashimoto

Το Dagger-Hashimoto στοχεύει στην ικανοποίηση δύο στόχων:

  1. Αντίσταση στο ASIC: το όφελος από τη δημιουργία εξειδικευμένου υλικού για τον αλγόριθμο θα πρέπει να είναι όσο το δυνατόν μικρότερο
  2. Επαληθευσιμότητα ελαφρού πελάτη: ένα μπλοκ πρέπει να είναι αποτελεσματικά επαληθεύσιμο από έναν ελαφρύ πελάτη.

Με μια πρόσθετη τροποποίηση, καθορίζουμε επίσης πώς να εκπληρώσουμε έναν τρίτο στόχο εάν το επιθυμούμε, αλλά με κόστος πρόσθετης πολυπλοκότητας:

Αποθήκευση πλήρους αλυσίδας: η εξόρυξη θα πρέπει να απαιτεί αποθήκευση της πλήρους κατάστασης της αλυσίδας μπλοκ (λόγω της ακανόνιστης δομής της trie κατάστασης Ethereum, προβλέπουμε ότι θα είναι δυνατή κάποια περικοπή, ιδιαίτερα ορισμένων συχνά χρησιμοποιούμενων συμβολαίων, αλλά θέλουμε να το ελαχιστοποιήσουμε αυτό).

Δημιουργία DAG

Ο κώδικας για τον αλγόριθμο θα οριστεί παρακάτω σε Python. Πρώτον, δίνουμε encode_int για τη διευθέτηση μη υπογεγραμμένων int συγκεκριμένης ακρίβειας σε συμβολοσειρές. Η αντιστροφή του δίνεται επίσης:

1NUM_BITS = 512
2
3def encode_int(x):
4 "Encode an integer x as a string of 64 characters using a big-endian scheme"
5 o = ''
6 for _ in range(NUM_BITS / 8):
7 o = chr(x % 256) + o
8 x //= 256
9 return o
10
11def decode_int(s):
12 "Unencode an integer x from a string using a big-endian scheme"
13 x = 0
14 for c in s:
15 x *= 256
16 x += ord(c)
17 return x
Εμφάνιση όλων
Αντιγραφή

Στη συνέχεια υποθέτουμε ότι το sha3 είναι μια συνάρτηση που παίρνει έναν ακέραιο και επιστρέφει έναν ακέραιο, και το dbl_sha3 είναι μια συνάρτηση διπλού sha3. Εάν μετατρέπετε αυτόν τον κώδικα αναφοράς σε μια εφαρμογή, χρησιμοποιήστε:

1from pyethereum import utils
2def sha3(x):
3 if isinstance(x, (int, long)):
4 x = encode_int(x)
5 return decode_int(utils.sha3(x))
6
7def 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 # Largest Safe Prime less than 2**512
2
3params = {
4 "n": 4000055296 * 8 // NUM_BITS, # Size of the dataset (4 Gigabytes); MUST BE MULTIPLE OF 65536
5 "n_inc": 65536, # Increment in value of n per period; MUST BE MULTIPLE OF 65536
6 # with epochtime=20000 gives 882 MB growth per year
7 "cache_size": 2500, # Size of the light client's cache (can be chosen by light
8 # client; not part of the algo spec)
9 "diff": 2**14, # Difficulty (adjusted during block evaluation)
10 "epochtime": 100000, # Length of an epoch in blocks (how often the dataset is updated)
11 "k": 1, # Number of parents of a node
12 "w": w, # Used for modular exponentiation hashing
13 "accesses": 200, # Number of dataset accesses during hashimoto
14 "P": SAFE_PRIME_512 # Safe Prime for hashing and random number generation
15}
Εμφάνιση όλων
Αντιγραφή

Το P σε αυτή την περίπτωση είναι ένας πρώτος αριθμός επιλεγμένος έτσι ώστε το log₂(P) να είναι ελαφρώς μικρότερο από 512, το οποίο αντιστοιχεί στα 512 bits που έχουμε χρησιμοποιήσει για να αναπαραστήσουμε τους αριθμούς μας. Σημειώστε ότι μόνο το δεύτερο μισό του 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) % P
7 for _ in range(params["k"]):
8 x ^= o[x % i]
9 o.append(pow(x, params["w"], P))
10 return o
Εμφάνιση όλων
Αντιγραφή

Ουσιαστικά, ξεκινάει έναν γράφο ως μεμονωμένο κόμβο, sha3(seed), και από εκεί αρχίζει να προσθέτει διαδοχικά άλλους κόμβους με βάση τυχαίους προηγούμενους κόμβους. Όταν δημιουργείται ένας νέος κόμβος, υπολογίζεται μια ακέραια δύναμη του 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 = {}
4
5 def quick_calc_cached(p):
6 if p in cache:
7 pass
8 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]
16
17 return quick_calc_cached(p)
Εμφάνιση όλων
Αντιγραφή

Ουσιαστικά, είναι απλώς μια εκ νέου συγγραφή του παραπάνω αλγόριθμου που αφαιρεί τον βρόχο υπολογισμού των τιμών για ολόκληρο το DAG και αντικαθιστά την προηγούμενη αναζήτηση κόμβου με μια αναδρομική κλήση ή μια αναζήτηση cache. Σημειώστε ότι για k=1 το cache δεν είναι απαραίτητο, αν και μια περαιτέρω βελτιστοποίηση προϋπολογίζει στην πραγματικότητα τις πρώτες χιλιάδες τιμές του DAG και τις διατηρεί ως ένα στατικό cache για υπολογισμούς. Δείτε το παράρτημα για μια υλοποίηση κώδικα αυτού.

Διπλός χώρος μνήμης για DAG

Σε έναν πλήρη πελάτη, χρησιμοποιείται ένας διπλός χώρος μνήμης(opens in a new tab) 2 DAG που παράγονται από τον παραπάνω τύπο. Η ιδέα είναι ότι τα DAG παράγονται ανά κάθε αριθμό μπλοκ epochtime σύμφωνα με τις προαναφερθείσες παραμέτρους. Αντί ο πελάτης να χρησιμοποιεί το πιο πρόσφατο παραγόμενο DAG, χρησιμοποιεί το προηγούμενο. Το πλεονέκτημα αυτού είναι ότι επιτρέπει την αντικατάσταση των DAG με την πάροδο του χρόνου χωρίς την ανάγκη να ενσωματώσει ένα βήμα όπου οι εξορύκτες πρέπει ξαφνικά να επανυπολογίσουν όλα τα δεδομένα. Διαφορετικά, υπάρχει η πιθανότητα μιας απότομης προσωρινής επιβράδυνσης στην επεξεργασία της αλυσίδας σε τακτά χρονικά διαστήματα και δραματική αύξηση της κεντρικοποίησης. Έτσι, οι κίνδυνοι επίθεσης 51% εντός αυτών των λίγων λεπτών πριν από την επανυπολογισμό όλων των δεδομένων.

Ο αλγόριθμος που χρησιμοποιείται για τη δημιουργία του συνόλου των DAG που χρησιμοποιούνται για τον υπολογισμό της εργασίας για ένα μπλοκ είναι ο εξής:

1def get_prevhash(n):
2 from pyethereum.blocks import GENESIS_PREVHASH
3 from pyethereum import chain_manager
4 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)
9
10def 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 seedset
17
18def get_dagsize(params, block):
19 return params["n"] + (block.number // params["epochtime"]) * params["n_inc"]
20
21def 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 buffer
26 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 είναι να χρησιμοποιηθεί το blockchain ως σύνολο δεδομένων, το οποίο εκτελεί έναν υπολογισμό που επιλέγει Ν δείκτες από το blockchain, συλλέγει τις συναλλαγές σε αυτούς τους δείκτες, εκτελεί ένα XOR αυτών των δεδομένων και επιστρέφει το hash του αποτελέσματος. Ο αρχικός αλγόριθμος του Thaddeus Dryja, μεταφρασμένος σε 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 = 0
4 for i in range(64):
5 shifted_A = hash_output_A >> i
6 transaction = shifted_A % len(list_of_transactions)
7 txid_mix ^= list_of_transactions[transaction] << i
8 return txid_mix ^ (nonce << 192)
Αντιγραφή

Δυστυχώς, ενώ το Hashimoto θεωρείται δύσκολο για τη RAM, βασίζεται σε αριθμητική 256-bit, η οποία έχει σημαντικό υπολογιστικό κόστος. Ωστόσο, το Dagger-Hashimoto χρησιμοποιεί μόνο τα λιγότερο σημαντικά 64 bits κατά την ευρετηρίαση του συνόλου δεδομένων του για να αντιμετωπίσει αυτό το πρόβλημα.

1def hashimoto(dag, dagsize, params, header, nonce):
2 m = dagsize / 2
3 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 // 2
3 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 randint
3 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 break
9 nonce += 1
10 if nonce >= 2**64:
11 nonce = 0
12 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 επιβάλλει πρόσθετες απαιτήσεις στην κεφαλίδα μπλοκ:

  • Για να λειτουργήσει η επαλήθευση δύο επιπέδων, μια κεφαλίδα μπλοκ πρέπει να έχει τόσο το nonce όσο και την ενδιάμεση τιμή pre-sha3
  • Κάπου, μια κεφαλίδα μπλοκ πρέπει να αποθηκεύσει το sha3 του τρέχοντος seedset

Περισσότερες πληροφορίες

Γνωρίζετε κάποιο πόρο της κοινότητας που σας βοήθησε; Επεξεργαστείτε αυτή τη σελίδα και προσθέστε το!

Appendix

Όπως σημειώθηκε παραπάνω, ο RNG που χρησιμοποιείται για τη δημιουργία DAG βασίζεται σε ορισμένα αποτελέσματα από τη θεωρία αριθμών. Πρώτον, παρέχουμε διαβεβαίωση ότι η γεννήτρια τυχαίων αριθμών Lehmer που αποτελεί τη βάση για τη μεταβλητή 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 να είναι επίσης πρώτος. Η τάξη ενός μέλους x της πολλαπλασιαστικής ομάδας(opens in a new tab) ℤ/nℤ ορίζεται ως το ελάχιστο m έτσι ώστε

1xᵐ 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, καθώς από το μικρό θεώρημα του Fermat έχουμε:

1xP-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 είναι σχετικά πρώτοι αν και μόνο αν για όλα τα a και b σε ℤ/Pℤ:

`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 ικανοποιεί την παραπάνω πρόταση.

Πιο αποδοτικός αλγόριθμος αξιολόγησης βάσης cache

1def quick_calc(params, seed, p):
2 cache = produce_dag(params, seed, params["cache_size"])
3 return quick_calc_cached(cache, params, p)
4
5def 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)
14
15def 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)
18
19def quick_hashimoto_cached(cache, dagsize, params, header, nonce):
20 m = dagsize // 2
21 mask = 2**64 - 1
22 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)
Εμφάνιση όλων
Αντιγραφή

Ήταν χρήσιμο αυτό το άρθρο;