87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
import time
|
|
|
|
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
|
|
|
|
|
|
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
|
|
# Goal: approximately 2 seconds of computation for 1 hour of 1 MB storage
|
|
|
|
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)
|
|
|
|
@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, rounds: int = None) -> Kasten:
|
|
if not rounds:
|
|
try:
|
|
rounds = int(Kasten(
|
|
None,
|
|
packed_bytes, None,
|
|
auto_check_generator=False).get_metadata()['rds'])
|
|
except (KeyError, TypeError) as _: # noqa
|
|
raise ValueError(
|
|
"Rounds not specified either in block or as argument")
|
|
check_block_sanity(packed_bytes)
|
|
return Kasten(
|
|
vdf_create(
|
|
packed_bytes,
|
|
rounds, dec=True
|
|
).to_bytes(
|
|
64, "big"), packed_bytes, cls, auto_check_generator=False)
|
|
|
|
@staticmethod
|
|
def validate_id(
|
|
hash: KastenChecksum,
|
|
packed_bytes: KastenPacked, rounds: int = None) -> None:
|
|
|
|
check_block_sanity(packed_bytes)
|
|
|
|
if not rounds:
|
|
try:
|
|
rounds = int(Kasten(
|
|
None,
|
|
packed_bytes, None,
|
|
auto_check_generator=False).get_metadata()['rds'])
|
|
except (KeyError, TypeError) as _: # noqa
|
|
raise ValueError(
|
|
"Rounds not specified either in block or as argument")
|
|
|
|
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(
|
|
rounds, len(packed_bytes))
|
|
if time.time() > test_obj.get_timestamp() + allowed_age_seconds:
|
|
raise ValueError(
|
|
f"Block rounds only valid through {allowed_age_seconds}")
|
|
|
|
return None |