Last active
July 8, 2022 10:25
-
-
Save veox/ec01ef51b01f8a2bebaa0ae51374f796 to your computer and use it in GitHub Desktop.
`namehash()` for Python 3.6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import timeit | |
import matplotlib.pyplot as plt | |
from namehash import * | |
basename = 'a.b.c.d.e.f.g.h' | |
labels = 8 | |
TIMESLABELS = 32*8 | |
MAXTIMES = 1024*8 | |
def longname(base: str, times: int): | |
return '.'.join([base]*times) | |
def test(statement: str, times: int): | |
return timeit.timeit(statement, globals = globals(), number = times) | |
graphs = {} | |
print('function,nlabels,singletime') | |
for f in ['namehash', 'namehashg']: | |
graph = graphs[f] = [] | |
for n in range(1, TIMESLABELS+1): | |
name = longname(basename, n) | |
nlabels = n * labels | |
timethis = ''.join([f, '("', name, '")']) | |
times = int(MAXTIMES/n) | |
try: | |
total = test(timethis, times) | |
except RecursionError: | |
continue | |
single = total/times | |
graph.append(single) | |
print(f, nlabels, single, sep = ',') | |
for k,v in graphs.items(): | |
xtix = [str(int(x)*labels) for x in range(1, len(v)+1)] | |
plt.plot(xtix, v, label = k) | |
plt.xlabel('number of labels') | |
plt.ylabel('average time (seconds)') | |
plt.legend() | |
plt.grid() | |
plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' namehash() | |
Author: Noel Maersk (veox) | |
Version: 0.1.3 | |
License: LGPLv3 | |
''' | |
# PyPI package 'pysha3' | |
import sha3 | |
def _namehashgen(encoding): | |
'''Internal, generator iterator. | |
Takes next label (string). | |
Yields nodehash (bytes(32)), accounting for label.''' | |
# start from nullhash | |
nodehash = bytes(32) | |
while True: | |
label = (yield nodehash) | |
# first hash the label... | |
lh = sha3.keccak_256(bytes(label, encoding = encoding)) | |
labelhash = lh.digest() | |
# then hash the result together with the node | |
nh = sha3.keccak_256(nodehash + labelhash) | |
nodehash = nh.digest() | |
def namehashg(name: str, encoding = 'utf-8'): | |
'''ENS "namehash()" convention mapping of strings to bytes(32) hashes. | |
Generator-based function variant. Performs worse than the recursive | |
variant, but is not limited by the system stack depth limit. | |
:param name: name to hash, labels separated by dots | |
:type name: str | |
:returns: bytes(32)''' | |
hg = _namehashgen(encoding) | |
h = hg.send(None) | |
# short-circuit for empty name | |
if name == '': | |
return h | |
labels = name.split('.') | |
for l in reversed(labels): | |
h = hg.send(l) | |
return h | |
def namehash(name: str, encoding = 'utf-8'): | |
'''ENS "namehash()" convention mapping of strings to bytes(32) hashes. | |
Recursive function variant. Performs slightly better than the | |
generator-based variant, but can't handle names with infinite (or | |
extremely large) number of labels. | |
:param name: name to hash, labels separated by dots | |
:type name: str | |
:returns: bytes(32)''' | |
if name == '': | |
return b'\x00' * 32 | |
else: | |
label, _, remainder = name.partition('.') | |
return sha3.keccak_256(namehash(remainder) + | |
sha3.keccak_256(bytes(label, encoding = encoding)).digest()).digest() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment