Adjusting block generation to use ttl instead of rounds in metadata

This commit is contained in:
Kevin F 2022-01-19 18:48:42 -06:00
parent 7d8781c57d
commit 6537362e3d
11 changed files with 96 additions and 147 deletions

View File

@ -2,6 +2,10 @@
This project uses Semantic Versioning This project uses Semantic Versioning
## 5.0.0
- Removed signedby
## 4.0.0 ## 4.0.0
* Make blocks less expensive * Make blocks less expensive

View File

@ -1,7 +1,3 @@
from typing import Union from .blockcreator import create_anonvdf_block
from time import time
from binascii import hexlify
import kasten from .block import Block
from . import generators

30
onionrblocks/block.py Normal file
View File

@ -0,0 +1,30 @@
from enum import auto
from hashlib import new
from typing import Union, TYPE_CHECKING
from binascii import hexlify
if TYPE_CHECKING:
from kasten.generator import KastenPacked
from kasten import Kasten
from . import generators
class Block:
def __init__(
self, block_hash: str,
raw_block_data: 'KastenPacked', auto_verify=True):
generator = generators.AnonVDFGenerator
bl = Kasten(
block_hash,
raw_block_data,
generator, auto_check_generator=auto_verify)
self.timestamp = bl.get_timestamp()
self.metadata = bl.get_metadata()
self.id = block_hash
self.type = bl.get_data_type()
self.data = bl.data
self.raw = bl.get_packed()
def kasten_to_block(kasten: Kasten):
return Block(hexlify(kasten.id), kasten.get_packed(), auto_verify=False)

View File

@ -16,17 +16,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from time import time from time import time
from kasten import Kasten
from kasten.generator.pack import pack from kasten.generator.pack import pack
from ..generators import anonvdf from ..generators import anonvdf
from ..block import Block
from ..block import kasten_to_block
def create_anonvdf_block( def create_anonvdf_block(
block_data: bytes, block_data: bytes,
block_type: bytes, block_type: bytes,
ttl: int, ttl: int,
**block_metadata) -> Kasten: **block_metadata) -> Block:
try: try:
block_data = block_data.encode('utf-8') block_data = block_data.encode('utf-8')
except AttributeError: except AttributeError:
@ -39,12 +41,12 @@ def create_anonvdf_block(
packed = pack( packed = pack(
block_data, block_type, block_data, block_type,
app_metadata=block_metadata, timestamp=ts) app_metadata=block_metadata, timestamp=ts)
block_metadata['ttl'] = ttl
rounds_needed = anonvdf.AnonVDFGenerator.get_rounds_for_ttl_seconds( rounds_needed = anonvdf.AnonVDFGenerator.get_rounds_for_ttl_seconds(
ttl, len(packed) + 10) ttl, len(packed))
block_metadata['rds'] = rounds_needed
packed = pack( packed = pack(
block_data, block_data,
block_type, app_metadata=block_metadata, timestamp=ts) block_type, app_metadata=block_metadata, timestamp=ts)
return anonvdf.AnonVDFGenerator.generate( k = anonvdf.AnonVDFGenerator.generate(
packed, block_metadata['rds']) packed, block_metadata['ttl'])
return kasten_to_block(k)

View File

@ -1,2 +1 @@
from .anonvdf import AnonVDFGenerator from .anonvdf import AnonVDFGenerator
from .signedby import Signed

View File

@ -1,4 +1,5 @@
import time import time
from binascii import a2b_hex
from kasten import Kasten, generator from kasten import Kasten, generator
from kasten.types import KastenPacked, KastenChecksum from kasten.types import KastenPacked, KastenChecksum
@ -34,16 +35,13 @@ class AnonVDFGenerator(generator.KastenBaseGenerator):
@classmethod @classmethod
def generate( def generate(
cls, packed_bytes: KastenPacked, rounds: int = None) -> Kasten: cls, packed_bytes: KastenPacked, ttl: int = None) -> Kasten:
if not rounds:
try: ttl = int(Kasten(
rounds = int(Kasten(
None, None,
packed_bytes, None, packed_bytes, None,
auto_check_generator=False).get_metadata()['rds']) auto_check_generator=False).get_metadata()['ttl'])
except (KeyError, TypeError) as _: # noqa rounds = AnonVDFGenerator.get_rounds_for_ttl_seconds(ttl, len(packed_bytes))
raise ValueError(
"Rounds not specified either in block or as argument")
check_block_sanity(packed_bytes) check_block_sanity(packed_bytes)
return Kasten( return Kasten(
vdf_create( vdf_create(
@ -55,19 +53,18 @@ class AnonVDFGenerator(generator.KastenBaseGenerator):
@staticmethod @staticmethod
def validate_id( def validate_id(
hash: KastenChecksum, hash: KastenChecksum,
packed_bytes: KastenPacked, rounds: int = None) -> None: packed_bytes: KastenPacked) -> None:
check_block_sanity(packed_bytes) check_block_sanity(packed_bytes)
if not rounds: ttl = int(Kasten(
try:
rounds = int(Kasten(
None, None,
packed_bytes, None, packed_bytes, None,
auto_check_generator=False).get_metadata()['rds']) auto_check_generator=False).get_metadata()['ttl'])
except (KeyError, TypeError) as _: # noqa rounds = AnonVDFGenerator.get_rounds_for_ttl_seconds(ttl, len(packed_bytes))
raise ValueError(
"Rounds not specified either in block or as argument") hash = hash.lstrip(b"0")
hash = int(hash, 16)
try: try:
hash = int.from_bytes(hash, byteorder="big") hash = int.from_bytes(hash, byteorder="big")

View File

@ -1,57 +0,0 @@
from hashlib import sha3_256
from typing import Union
from nacl.signing import SigningKey, VerifyKey
from nacl.exceptions import BadSignatureError
from kasten import generator, Kasten
from kasten.exceptions import InvalidID
from kasten.types import KastenPacked, KastenChecksum
from onionrblocks.customtypes import RawEd25519PrivateKey, RawEd25519PublicKey
from onionrblocks.universalrules import check_block_sanity
class Signed(generator.KastenBaseGenerator):
@classmethod
def generate(
cls,
packed_bytes: KastenPacked,
signingKey: Union[SigningKey, RawEd25519PrivateKey]
) -> Kasten:
"""Sign a digest of packed bytes, return a Kasten instance of it."""
# Use libsodium/pynacl (ed25519)
hashed = sha3_256(packed_bytes).digest()
try:
signed = signingKey.sign(hashed)
except AttributeError:
signingKey = SigningKey(signingKey)
signed = signingKey.sign(hashed)
# The KastenChecksum will be 64 bytes message then 32 bytes of the hash
# This can be fed right back into VerifyKey without splitting up
return Kasten(signed, packed_bytes, None,
auto_check_generator=False)
@staticmethod
def validate_id(
hash: KastenChecksum,
packed_bytes: KastenPacked,
verify_key: Union[VerifyKey, RawEd25519PublicKey]) -> None:
check_block_sanity(packed_bytes)
# Hash is 64 bytes message then 32 bytes of the hash of the packed_bytes
if len(hash) != 86:
raise InvalidID("Block not have proper signature length")
actual_hash = sha3_256(packed_bytes).digest()
if not isinstance(verify_key, VerifyKey):
verify_key = VerifyKey(verify_key)
# Ensure that the digest is correct
# Done in addition of the signature bc the sha3 can still ID blocks
# and to prevent swapping sigs
if actual_hash != hash[64:]:
raise InvalidID("Invalid sha3_256 digest")
# Ensure that the signature is correct
try:
verify_key.verify(hash)
except BadSignatureError as e:
raise InvalidID(repr(e))

View File

@ -1,2 +1 @@
kasten==3.0.0 kasten==3.0.0
PyNaCl==1.4.0

View File

@ -1,7 +1,7 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
setup(name='onionrblocks', setup(name='onionrblocks',
version='4.0.0', version='5.0.0',
description='Onionr message format', description='Onionr message format',
author='Kevin Froman', author='Kevin Froman',
author_email='beardog@mailbox.org', author_email='beardog@mailbox.org',

View File

@ -1,33 +1,56 @@
from binascii import a2b_hex
from onionrblocks.generators import anonvdf from onionrblocks.generators import anonvdf
import unittest import unittest
import kasten import kasten
from onionrblocks.blockcreator import create_anonvdf_block from onionrblocks.blockcreator import create_anonvdf_block
def get_rounds_for_ttl_seconds(seconds: int, size_bytes: int):
second_cost = 4
byte_cost = 10
return (seconds * second_cost) + (size_bytes * byte_cost)
class TestBlockCreator(unittest.TestCase): class TestBlockCreator(unittest.TestCase):
"""
def test_create_anonvdf(self): def test_create_anonvdf(self):
bl = create_anonvdf_block(b"Test", "txt", 3600) seconds = 3600
bl = create_anonvdf_block(b"Test", "txt", seconds)
byte_cost = 10
second_cost = 4
# (rounds - (size_bytes * cls.byte_cost)) // cls.second_cost # (rounds - (size_bytes * cls.byte_cost)) // cls.second_cost
expected_rounds = (3600 * 4) + (len(bl.get_packed()) * 100) + 100 expected_rounds = (seconds * second_cost) + (len(bl.get_packed()) * byte_cost)
self.assertEqual(expected_rounds, bl.get_metadata()['rds']) self.assertTrue(abs(expected_rounds - bl.get_metadata()['rds']) < 91)
def test_create_anonvdf_half_hour(self): def test_create_anonvdf_half_hour(self):
bl = create_anonvdf_block(b"Test", "txt", 1800) bl = create_anonvdf_block(b"Test", "txt", 1800)
expected_rounds = (len(bl.get_packed()) * anonvdf.AnonVDFGenerator.byte_cost) + (1800 * anonvdf.AnonVDFGenerator.second_cost) + 100 expected_rounds = (len(bl.get_packed()) * anonvdf.AnonVDFGenerator.byte_cost) + (1800 * anonvdf.AnonVDFGenerator.second_cost)
self.assertEqual(expected_rounds, bl.get_metadata()['rds']) self.assertTrue(abs(expected_rounds - bl.get_metadata()['rds']) < 91)
def test_create_anonvdf_odd(self): def test_create_anonvdf_odd(self):
#(rounds - (size_bytes * cls.byte_cost)) // cls.second_cost #(rounds - (size_bytes * cls.byte_cost)) // cls.second_cost
bl = create_anonvdf_block(b"Test", "txt", 1303) bl = create_anonvdf_block(b"Test", "txt", 1303)
expected_rounds = (len(bl.get_packed()) * anonvdf.AnonVDFGenerator.byte_cost) + (1303 * anonvdf.AnonVDFGenerator.second_cost) + 100 expected_rounds = (len(bl.get_packed()) * anonvdf.AnonVDFGenerator.byte_cost) + (1303 * anonvdf.AnonVDFGenerator.second_cost)
self.assertEqual(expected_rounds, bl.get_metadata()['rds']) self.assertTrue(abs(expected_rounds - bl.get_metadata()['rds']) < 91)
"""
def test_create_anonvdf_verify(self): def test_create_anonvdf_verify(self):
bl = create_anonvdf_block(b"Test", "txt", 3600) bl = create_anonvdf_block(b"Test", "txt", 3600)
expected_rounds = (len(bl.get_packed()) * anonvdf.AnonVDFGenerator.byte_cost) + (3600 * anonvdf.AnonVDFGenerator.second_cost) + 100
self.assertEqual(expected_rounds, bl.get_metadata()['rds'])
packed = bl.get_packed() packed = bl.raw
id = bl.id
kasten.Kasten(id, packed, anonvdf.AnonVDFGenerator, auto_check_generator=True) kasten.Kasten(bl.id, packed, anonvdf.AnonVDFGenerator, auto_check_generator=True)
print(bl.id)
fake_id = b'01' + bl.id[2:]
self.assertRaises(kasten.exceptions.InvalidID, kasten.Kasten, fake_id, packed, anonvdf.AnonVDFGenerator, auto_check_generator=True)
fake_id = bytearray(bl.id)
fake_id[32] = 13
fake_id[34] = 13
fake_id[120] = 13
fake_id = bytes(fake_id).replace(b'\r', b'')
print(fake_id.replace(b'\r', b''))
kasten.Kasten(fake_id, packed, anonvdf.AnonVDFGenerator, auto_check_generator=True)
unittest.main() unittest.main()

View File

@ -1,44 +0,0 @@
import unittest
from onionrblocks.generators import signedby
import kasten
import hashlib
from nacl.signing import SigningKey, VerifyKey
from time import time
from math import floor
def sha3_hash_bytes(data):
return hashlib.sha3_256(data).digest()
class TestSignedByProof(unittest.TestCase):
def test_signed_by_create(self):
key = SigningKey.generate()
test_data = kasten.generator.pack.pack(
b"test", "tst")
gen = signedby.Signed.generate(test_data, key)
self.assertEqual(
gen.get_packed(),
test_data
)
hashed = hashlib.sha3_256(test_data).digest()
signed = key.sign(hashed)
self.assertEqual(signed, gen.id)
key.verify_key.verify(signed)
self.assertEqual(hashlib.sha3_256(test_data).digest(), signed[64:])
def test_signed_by_create_raw_keys(self):
key = SigningKey.generate()
test_data = kasten.generator.pack.pack(
b"test", "tst")
gen = signedby.Signed.generate(test_data, key.encode())
self.assertEqual(
gen.get_packed(),
test_data
)
hashed = hashlib.sha3_256(test_data).digest()
signed = key.sign(hashed)
self.assertEqual(signed, gen.id)
key.verify_key.verify(signed)
self.assertEqual(hashlib.sha3_256(test_data).digest(), signed[64:])
unittest.main()