Skip to content

Instantly share code, notes, and snippets.

@veox
Last active July 8, 2022 10:25
Show Gist options
  • Save veox/ec01ef51b01f8a2bebaa0ae51374f796 to your computer and use it in GitHub Desktop.
Save veox/ec01ef51b01f8a2bebaa0ae51374f796 to your computer and use it in GitHub Desktop.
`namehash()` for Python 3.6
#!/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()
''' 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