diff --git a/contrib/py/vanity/.gitignore b/contrib/py/vanity/.gitignore new file mode 100644 index 000000000..8e5737fb0 --- /dev/null +++ b/contrib/py/vanity/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*.private \ No newline at end of file diff --git a/contrib/py/vanity/bencode.py b/contrib/py/vanity/bencode.py new file mode 100644 index 000000000..5c131fc9b --- /dev/null +++ b/contrib/py/vanity/bencode.py @@ -0,0 +1,112 @@ +# +# super freaking dead simple wicked awesome bencode library +# +from io import BytesIO + +class BCodec: + encoding = 'utf-8' + def __init__(self, fd): + self._fd = fd + + def _write_bytestring(self, bs): + self._fd.write('{}:'.format(len(bs)).encode('ascii')) + self._fd.write(bs) + + def _write_list(self, l): + self._fd.write(b'l') + for item in l: + self.encode(item) + self._fd.write(b'e') + + def _write_dict(self, d): + self._fd.write(b'd') + keys = list(d.keys()) + keys.sort() + for k in keys: + if isinstance(k, str): + self._write_bytestring(k.encode(self.encoding)) + elif isinstance(k, bytes): + self._write_bytestring(k) + else: + self._write_bytestring('{}'.format(k).encode(self.encoding)) + self.encode(d[k]) + self._fd.write(b'e') + + def _write_int(self, i): + self._fd.write('i{}e'.format(i).encode(self.encoding)) + + def encode(self, obj): + if isinstance(obj, dict): + self._write_dict(obj) + elif isinstance(obj, list): + self._write_list(obj) + elif isinstance(obj, int): + self._write_int(obj) + elif isinstance(obj, str): + self._write_bytestring(obj.encode(self.encoding)) + elif isinstance(obj, bytes): + self._write_bytestring(obj) + elif hasattr(obj, bencode): + obj.bencode(self._fd) + else: + raise ValueError("invalid object type") + + def _readuntil(self, delim): + b = bytes() + while True: + ch = self._fd.read(1) + if ch == delim: + return b + b += ch + + def _decode_list(self): + l = list() + while True: + b = self._fd.read(1) + if b == b'e': + return l + l.append(self._decode(b)) + + def _decode_dict(self): + d = dict() + while True: + ch = self._fd.read(1) + if ch == b'e': + return d + k = self._decode_bytestring(ch) + d[k] = self.decode() + + def _decode_int(self): + return int(self._readuntil(b'e'), 10) + + def _decode_bytestring(self, ch): + ch += self._readuntil(b':') + l = int(ch, base=10) + return self._fd.read(l) + + def _decode(self, ch): + if ch == b'd': + return self._decode_dict() + elif ch == b'l': + return self._decode_list() + elif ch == b'i': + return self._decode_int() + elif ch in [b'0',b'1',b'2',b'3',b'4',b'5',b'6',b'7',b'8',b'9']: + return self._decode_bytestring(ch) + else: + raise ValueError(ch) + + def decode(self): + return self._decode(self._fd.read(1)) + + +def bencode(obj): + buf = BytesIO() + b = BCodec(buf) + b.encode(obj) + return buf.bytes() + +def bdecode(bytestring): + buf = BytesIO() + buf.write(bytestring) + return BCodec(buf).decode() \ No newline at end of file diff --git a/contrib/py/vanity/lokinet-vanity.py b/contrib/py/vanity/lokinet-vanity.py new file mode 100644 index 000000000..09dc061ff --- /dev/null +++ b/contrib/py/vanity/lokinet-vanity.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +import bencode +import sys +import libnacl +import base64 +import struct +from io import BytesIO +import time +from multiprocessing import Process, Array, Value + + + +def print_help(): + print('usage: {} keyfile.private prefix numthreads'.format(sys.argv[0])) + return 1 + +_zalpha = ['y', 'b', 'n', 'd', 'r', 'f', 'g', '8', + 'e', 'j', 'k', 'm', 'c', 'p', 'q', 'x', + 'o', 't', '1', 'u', 'w', 'i', 's', 'z', + 'a', '3', '4', '5', 'h', '7', '6', '9'] + +def zb32_encode(buf): + s = str() + bits = 0 + l = len(buf) + idx = 0 + tmp = buf[idx] + while bits > 0 or idx < l: + if bits < 5: + if idx < l: + tmp <<= 8 + tmp |= buf[idx] & 0xff + idx += 1 + bits += 8 + else: + tmp <<= 5 - bits + bits = 5 + bits -= 5 + s += _zalpha[(tmp >> bits) & 0x1f] + return s + + +def _gen_si(keys): + e = keys[b'e'][32:] + s = keys[b's'][32:] + v = keys[b'v'] + return {'e': e, 's':s, 'v':v} + + +class AddrGen: + + def __init__(self, threads, keys, prefix): + self._inc = threads + self._keys = keys + self._c = Value('i') + self.sync = Array('i', 3) + self._procs = [] + self.prefix = prefix + + def runit(self): + for ch in self.prefix: + if ch not in _zalpha: + print("invalid prefix, {} not a valid character".format(ch)) + return None, None + print("find ^{}.loki".format(self.prefix)) + i = self._inc + while i > 0: + p = Process(target=self._gen_addr_tick, args=(self.prefix, abs(libnacl.randombytes_random()), abs(libnacl.randombytes_random()), _gen_si(self._keys))) + p.start() + self._procs.append(p) + i -=1 + return self._runner() + + def _gen_addr_tick(self, prefix, lo, hi, si): + print(prefix) + fd = BytesIO() + addr = '' + enc = bencode.BCodec(fd) + while self.sync[2] == 0: + si['x'] = struct.pack('>QQ', lo, hi) + fd.seek(0,0) + enc.encode(si) + pub = bytes(fd.getbuffer()) + addr = zb32_encode(libnacl.crypto_generichash(pub)) + if addr.startswith(prefix): + self.sync[2] = 1 + self.sync[0] = hi + self.sync[1] = lo + return + hi += self._inc + if hi == 0: + lo += 1 + self._c.value += 1 + + def _print_stats(self): + print('{} H/s'.format(self._c.value)) + self._c.value = 0 + + def _joinall(self): + for p in self._procs: + p.join() + + def _runner(self): + while self.sync[2] == 0: + time.sleep(1) + self._print_stats() + self._joinall() + fd = BytesIO() + enc = bencode.BCodec(fd) + hi = self.sync[0] + lo = self.sync[1] + si = _gen_si(self._keys) + si['x'] = struct.pack('>QQ', lo, hi) + enc.encode(si) + pub = bytes(fd.getbuffer()) + addr = zb32_encode(libnacl.crypto_generichash(pub)) + return si['x'], addr + + +def main(args): + if len(args) != 3: + return print_help() + keys = None + with open(args[0], 'rb') as fd: + dec = bencode.BCodec(fd) + keys = dec.decode() + runner = AddrGen(int(args[2]), keys, args[1]) + keys[b'x'], addr = runner.runit() + if addr: + print("found {}.loki".format(addr)) + with open(args[0], 'wb') as fd: + enc = bencode.BCodec(fd) + enc.encode(keys) + +if __name__ == '__main__': + main(sys.argv[1:]) \ No newline at end of file diff --git a/contrib/py/vanity/readme.md b/contrib/py/vanity/readme.md new file mode 100644 index 000000000..8c2203309 --- /dev/null +++ b/contrib/py/vanity/readme.md @@ -0,0 +1,10 @@ +# lokinet vanity address generator + +installing deps: + + sudo apt install libsodium-dev + pip3 install --user -r requirements.txt + +to generate a nonce with a prefix `^7oki` using 8 cpu threads: + + python3 lokinet-vanity.py keyfile.private 7oki 8 \ No newline at end of file diff --git a/contrib/py/vanity/requirements.txt b/contrib/py/vanity/requirements.txt new file mode 100644 index 000000000..15d991e7b --- /dev/null +++ b/contrib/py/vanity/requirements.txt @@ -0,0 +1 @@ +libnacl \ No newline at end of file