From 27973340590842515f799971335b9b1b3f58fb61 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Sun, 13 Dec 2020 03:49:43 +0000 Subject: [PATCH] added and implemented block time validation based on VDF time --- onionrblocks/generators/anonvdf.py | 49 ++++++++++++++++++++++++++++++ onionrblocks/universalrules.py | 10 ++---- tests/test_anonvdf.py | 33 ++++++++++++++++++++ tests/test_universal_rules.py | 10 ++---- 4 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 tests/test_anonvdf.py diff --git a/onionrblocks/generators/anonvdf.py b/onionrblocks/generators/anonvdf.py index e69de29..03ea51d 100644 --- a/onionrblocks/generators/anonvdf.py +++ b/onionrblocks/generators/anonvdf.py @@ -0,0 +1,49 @@ +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 AnonVDFGenerator(generator.KastenBaseGenerator): + @classmethod + def get_ttl_seconds_per_rounds(cls, rounds: int): + # 8000 rounds = 1 second (2.8ghz python) = 1 hour storage + if rounds < 8000: + raise ValueError("Rounds must be at least 8000") + return (rounds / 8000) * 60 + + @classmethod + def generate( + cls, packed_bytes: KastenPacked, rounds: int = 5000) -> Kasten: + 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=5000) -> None: + + 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) + if time.time() > test_obj.get_timestamp() + allowed_age_seconds: + raise ValueError( + f"Block rounds only vaild through {allowed_age_seconds}") + + return None \ No newline at end of file diff --git a/onionrblocks/universalrules.py b/onionrblocks/universalrules.py index 54ad1f1..f7b2e15 100644 --- a/onionrblocks/universalrules.py +++ b/onionrblocks/universalrules.py @@ -3,7 +3,6 @@ from kasten import Kasten from time import time from math import floor -MAX_AGE_SECONDS = 60 * 60 * 24 * 30 MAX_FUTURE_SKEW_SECONDS = 60 * 2 TOTAL_MAX_SIZE = 6 * (10**6) @@ -12,9 +11,6 @@ class BlockRulesException(Exception): pass -class BlockOutdated(BlockRulesException): - pass - class BlockExpired(Exception): pass @@ -37,14 +33,12 @@ def check_block_sanity(raw_bytes): timestamp: int = floor(time()) block_timestamp = kasten.get_timestamp() difference = timestamp - block_timestamp - if difference > MAX_AGE_SECONDS: - raise BlockOutdated() + if difference < -MAX_FUTURE_SKEW_SECONDS: raise BlockFutureSkewedBeyondMax() try: - if timestamp < block_timestamp + kasten.get_metadata()[ - 'expire']: + if block_timestamp + kasten.get_metadata()['expire'] >= timestamp: raise BlockExpired() except (IndexError, TypeError): pass diff --git a/tests/test_anonvdf.py b/tests/test_anonvdf.py new file mode 100644 index 0000000..eef7b5c --- /dev/null +++ b/tests/test_anonvdf.py @@ -0,0 +1,33 @@ +import unittest +from onionrblocks.generators import anonvdf +import kasten +import mimcvdf + +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) + + self.assertEqual( + anonvdf.AnonVDFGenerator.generate(test_data, rounds=1000).get_packed(), + test_data + ) + + def test_vdf_block_validate_ok_time(self): + rds = 8000 + test_data = kasten.generator.pack.pack(b"test", "tst", 0) + 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 = 8000 + t = floor(time()) - 60 + test_data = kasten.generator.pack.pack(b"test", "tst", 0, timestamp=t) + test_id = mimcvdf.vdf_create(test_data, rds) + self.assertRaises(ValueError, anonvdf.AnonVDFGenerator.validate_id, test_id, test_data, rounds=rds) + + +unittest.main() \ No newline at end of file diff --git a/tests/test_universal_rules.py b/tests/test_universal_rules.py index 08740da..60fb1bf 100644 --- a/tests/test_universal_rules.py +++ b/tests/test_universal_rules.py @@ -13,12 +13,6 @@ class TestUniversalRules(unittest.TestCase): universalrules.BlockTooLarge, universalrules.check_block_sanity, packed) - def test_block_too_old(self): - packed = kasten.generator.pack.pack(b"1"*10, b"tst", 0, timestamp=1000) - self.assertRaises( - universalrules.BlockOutdated, - universalrules.check_block_sanity, packed) - def test_block_future_skew_ok(self): t = floor(time()) + 120 packed = kasten.generator.pack.pack(b"1"*10, b"tst", 0, timestamp=t) @@ -32,14 +26,14 @@ class TestUniversalRules(unittest.TestCase): universalrules.check_block_sanity, packed) def test_block_not_expired(self): - packed = kasten.generator.pack.pack(b"1"*10, b"tst", 0, app_metadata={"expire": 10000}) + packed = kasten.generator.pack.pack(b"1"*10, b"tst", 0, app_metadata={"expire": 7}) self.assertRaises( universalrules.BlockExpired, universalrules.check_block_sanity, packed) def test_block_expired(self): t = floor(time()) - 6 - packed = kasten.generator.pack.pack(b"1"*10, b"tst", 0, timestamp=t, app_metadata={"expire": 5}) + packed = kasten.generator.pack.pack(b"1"*10, b"tst", 0, timestamp=t, app_metadata={"expire": 6}) self.assertRaises( universalrules.BlockExpired, universalrules.check_block_sanity, packed)