From 1db58e923ab2a5bdad38ea5811033aca24e1de92 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Fri, 22 Jan 2021 18:05:33 +0000 Subject: [PATCH] changed vdf rules to be consistent and based on real world price --- onionrblocks/generators/anonvdf.py | 58 ++++++++++++++++++++++-------- onionrblocks/universalrules.py | 2 +- tests/test_anonvdf.py | 42 +++++++++++++++++----- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/onionrblocks/generators/anonvdf.py b/onionrblocks/generators/anonvdf.py index 309a108..f35fc26 100644 --- a/onionrblocks/generators/anonvdf.py +++ b/onionrblocks/generators/anonvdf.py @@ -8,26 +8,44 @@ 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 + + byte_cost = 1000 + second_cost = 25 + + @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): - # 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 - - base = 90000 - per_byte = 1000 - minimum = base + (per_byte * size_bytes) - if rounds < minimum: - raise ValueError( - "Must be at least " + str(minimum) + " for " + str(size_bytes)) - - return (rounds / minimum) * 60 + result = (rounds // cls.second_cost) - (cls.byte_cost * size_bytes) + if result < 0: + raise NotEnoughRounds + return result @classmethod def generate( - cls, packed_bytes: KastenPacked, rounds: int = 90000) -> Kasten: + 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( @@ -39,10 +57,20 @@ class AnonVDFGenerator(generator.KastenBaseGenerator): @staticmethod def validate_id( hash: KastenChecksum, - packed_bytes: KastenPacked, rounds=90000) -> None: + 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: diff --git a/onionrblocks/universalrules.py b/onionrblocks/universalrules.py index 32e4443..e7879b3 100644 --- a/onionrblocks/universalrules.py +++ b/onionrblocks/universalrules.py @@ -52,7 +52,7 @@ def check_block_sanity(raw_bytes): try: if block_timestamp + kasten.get_metadata()['expire'] >= timestamp: raise BlockExpired() - except (IndexError, TypeError): + except (IndexError, TypeError, KeyError): pass diff --git a/tests/test_anonvdf.py b/tests/test_anonvdf.py index 358755c..c5d4625 100644 --- a/tests/test_anonvdf.py +++ b/tests/test_anonvdf.py @@ -7,6 +7,7 @@ from time import time from math import floor class TestAnonVDF(unittest.TestCase): + def test_vdf_create(self): test_data = kasten.generator.pack.pack(b"test", "tst", 0) test_vdf = mimcvdf.vdf_create(test_data, 1000, dec=True) @@ -19,30 +20,53 @@ class TestAnonVDF(unittest.TestCase): self.assertEqual(generated.id, int(test_vdf).to_bytes(64, byteorder="big")) def test_vdf_block_validate_ok_time(self): - rds = 90000 - test_data = kasten.generator.pack.pack(b"test", "tst", 0) - rds += len(test_data) * 1000 + rds = 0 + test_data = kasten.generator.pack.pack(b"test", "tst") + rds += len(test_data) * 90000 test_id = mimcvdf.vdf_create(test_data, rds) anonvdf.AnonVDFGenerator.validate_id(test_id, test_data, rounds=rds) def test_vdf_block_validate_not_enough(self): - rds = 90000 + rds = 133240 t = floor(time()) - 60 - test_data = kasten.generator.pack.pack(b"test", "tst", 0, timestamp=t) + test_data = kasten.generator.pack.pack(b"test", "tst", app_metadata=None, timestamp=t) test_id = mimcvdf.vdf_create(test_data, rds) - self.assertRaises(ValueError, anonvdf.AnonVDFGenerator.validate_id, test_id, test_data, rounds=rds) + self.assertRaises(anonvdf.NotEnoughRounds, anonvdf.AnonVDFGenerator.validate_id, test_id, test_data, rounds=rds) def test_vdf_incermental(self): last = 1 - for i in range(1, 20): + for i in range(1, 5): t = time() - rds = 90000 + rds = 0 test_data = kasten.generator.pack.pack(b"test" * i, "tst", 0) - rds += len(test_data) * 1000 + rds += len(test_data) * 90000 test_id = mimcvdf.vdf_create(test_data, rds) anonvdf.AnonVDFGenerator.validate_id(test_id, test_data, rounds=rds) newT = time() self.assertGreater(newT - t, last) + def test_block_embedded_vdf_rounds(self): + t = floor(time()) + rds = 90000 + (4000 * 1000) + test_data = kasten.generator.pack.pack(b"test", "tst", app_metadata={"rds": rds}, timestamp=t) + test_vdf = anonvdf.AnonVDFGenerator.generate(test_data, None) + anonvdf.AnonVDFGenerator.validate_id(test_vdf.id, test_data, rounds=None) + + def test_block_embedded_vdf_rounds_invalid(self): + t = floor(time()) + rds = 90000 + test_data = kasten.generator.pack.pack(b"test", "tst", app_metadata={"rds": rds}, timestamp=t) + test_vdf = anonvdf.AnonVDFGenerator.generate(test_data, None) + + self.assertRaises( + anonvdf.NotEnoughRounds, + anonvdf.AnonVDFGenerator.validate_id, test_vdf.id, test_data, rounds=None) + + def test_vdf_rounds_seconds(self): + size = 10000 + per_byte = 1000 + per_second = 25 + expected_rounds = (3600 * 25) + 100000 + self.assertEqual(anonvdf.AnonVDFGenerator.get_rounds_for_ttl_seconds(3600, 100), expected_rounds) unittest.main() \ No newline at end of file