onionrblocks/onionrblocks/generators/anonvdf.py

109 lines
3.3 KiB
Python
Raw Normal View History

import time
from binascii import a2b_hex
from kasten import Kasten, generator
from kasten.types import KastenPacked, KastenChecksum
from kasten.exceptions import InvalidID
from mimcvdf import vdf_create, vdf_verify
from onionrblocks.universalrules import check_block_sanity
class NotEnoughRounds(Exception):
pass
2022-01-30 02:19:07 +00:00
class NoTTLSet(Exception):
pass
2022-01-30 07:26:16 +00:00
class RoundsOutOfBounds(Exception):
pass
MAX_ROUNDS = 999_999_999
MINIMUM_ROUNDS = 10_000
class AnonVDFGenerator(generator.KastenBaseGenerator):
# 90000 rounds = 1 second (2.8ghz C) = 1 hour storage of 1 MB storage
# jan 2021 disk price metric = 0.000017 USD per MB
# mobile MB transfer US/Canada = 0.0125 USD
# Max cost of block storage + American mobile data transfer = 0.0751 USD
2021-01-24 22:05:21 +00:00
# Goal: approximately 2 seconds of computation for 1 hour of 1 MB storage
2021-01-24 22:05:21 +00:00
byte_cost = 10
second_cost = 4
@classmethod
def get_rounds_for_ttl_seconds(cls, seconds: int, size_bytes: int):
return (seconds * cls.second_cost) + (size_bytes * cls.byte_cost)
2021-01-20 20:31:50 +00:00
@classmethod
def get_ttl_seconds_per_rounds(cls, rounds: int, size_bytes: int):
return (rounds - (size_bytes * cls.byte_cost)) // cls.second_cost
@classmethod
def generate(
cls, packed_bytes: KastenPacked, ttl: int = None) -> Kasten:
2022-01-30 02:19:07 +00:00
k: Kasten = Kasten(
None,
packed_bytes, None,
2022-01-30 02:19:07 +00:00
auto_check_generator=False)
try:
ttl: int = k.get_metadata()['ttl']
except (KeyError, TypeError) as e:
raise NoTTLSet(e)
rounds = AnonVDFGenerator.get_rounds_for_ttl_seconds(ttl, len(packed_bytes))
2022-01-30 07:26:16 +00:00
if rounds > MAX_ROUNDS or rounds < MINIMUM_ROUNDS:
raise RoundsOutOfBounds()
2021-01-20 20:31:50 +00:00
check_block_sanity(packed_bytes)
2022-01-30 02:19:07 +00:00
vdf = vdf_create(
packed_bytes,
rounds, dec=True
2022-01-30 02:19:07 +00:00
).to_bytes(64, "big")
return Kasten(vdf, packed_bytes, cls, auto_check_generator=False)
@staticmethod
def validate_id(
hash: KastenChecksum,
packed_bytes: KastenPacked) -> None:
2021-01-20 20:31:50 +00:00
check_block_sanity(packed_bytes)
2022-01-30 02:19:07 +00:00
k: Kasten = Kasten(
None,
packed_bytes, None,
2022-01-30 02:19:07 +00:00
auto_check_generator=False)
try:
ttl: int = k.get_metadata()['ttl']
except (KeyError, TypeError) as e:
raise NoTTLSet(e)
ttl = int(ttl)
rounds = AnonVDFGenerator.get_rounds_for_ttl_seconds(ttl, len(packed_bytes))
2022-01-30 07:26:16 +00:00
if rounds > MAX_ROUNDS or rounds < MINIMUM_ROUNDS:
raise RoundsOutOfBounds()
2022-01-30 02:19:07 +00:00
#hash = hash.lstrip(b"0")
hash = int(hash, 16)
try:
hash = int.from_bytes(hash, byteorder="big")
except TypeError:
pass
if not vdf_verify(packed_bytes, hash, rounds):
raise InvalidID
test_obj = Kasten(
None, packed_bytes, None, auto_check_generator=False)
allowed_age_seconds = AnonVDFGenerator.get_ttl_seconds_per_rounds(
2021-01-20 20:31:50 +00:00
rounds, len(packed_bytes))
if time.time() > test_obj.get_timestamp() + allowed_age_seconds:
raise ValueError(
2021-01-20 20:31:50 +00:00
f"Block rounds only valid through {allowed_age_seconds}")
return None