Change to compressed quadratic forms for VDFs
Using compressed representation of quadratic forms reduces their serialized size from 130 to 100 bytes (for forms with 1024-bit discriminant). This shrinks the size of VDF outputs and VDF proofs, and it's a breaking change as compressed representation is not compatible with older uncompressed (a, b) representation. Compressed forms are also used in calls to chiavdf and in timelord's communication with VDF client. Update to version 0.14.0 of chiavdf and version 0.2.0 of test-cache. Older versions of these do not support compressed forms.
This commit is contained in:
parent
5052eb730c
commit
65dcae570e
2
setup.py
2
setup.py
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
dependencies = [
|
||||
"aiter==0.13.20191203", # Used for async generator tools
|
||||
"blspy==0.3.5", # Signature library
|
||||
"chiavdf==0.13.4", # timelord and vdf verification
|
||||
"chiavdf==0.14.0", # timelord and vdf verification
|
||||
"chiabip158==0.19", # bip158-style wallet filters
|
||||
"chiapos==0.12.43", # proof of space
|
||||
"clvm==0.8.0",
|
||||
|
|
|
@ -31,7 +31,7 @@ from src.types.slots import (
|
|||
SubSlotProofs,
|
||||
)
|
||||
from src.types.vdf import VDFInfo, VDFProof
|
||||
from src.util.ints import uint64, uint8, int512, uint32
|
||||
from src.util.ints import uint64, uint8, uint32
|
||||
from src.types.sub_epoch_summary import SubEpochSummary
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -757,13 +757,10 @@ class Timelord:
|
|||
writer.write((prefix + str(disc)).encode())
|
||||
await writer.drain()
|
||||
|
||||
# Send (a, b) from 'initial_form'.
|
||||
for num in [initial_form.a, initial_form.b]:
|
||||
prefix_l = len(str(num))
|
||||
prefix_len = len(str(prefix_l))
|
||||
async with self.lock:
|
||||
writer.write((str(prefix_len) + str(prefix_l) + str(num)).encode())
|
||||
await writer.drain()
|
||||
# Send initial_form prefixed with its length.
|
||||
async with self.lock:
|
||||
writer.write(bytes([len(initial_form.data)]) + initial_form.data)
|
||||
await writer.drain()
|
||||
try:
|
||||
ok = await reader.readexactly(2)
|
||||
except (asyncio.IncompleteReadError, ConnectionResetError, Exception) as e:
|
||||
|
@ -832,10 +829,8 @@ class Timelord:
|
|||
proof_bytes: bytes = stdout_bytes_io.read()
|
||||
|
||||
# Verifies our own proof just in case
|
||||
int_size = (self.constants.DISCRIMINANT_SIZE_BITS + 16) >> 4
|
||||
a = int.from_bytes(y_bytes[:int_size], "big", signed=True)
|
||||
b = int.from_bytes(y_bytes[int_size:], "big", signed=True)
|
||||
output = ClassgroupElement(int512(a), int512(b))
|
||||
form_size = ClassgroupElement.get_size(self.constants)
|
||||
output = ClassgroupElement(y_bytes[:form_size])
|
||||
time_taken = time.time() - self.chain_start_time[chain]
|
||||
ips = int(iterations_needed / time_taken * 10) / 10
|
||||
log.info(
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from src.util.ints import int512
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.util.streamable import Streamable, streamable
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@streamable
|
||||
class ClassgroupElement(Streamable):
|
||||
a: int512
|
||||
b: int512
|
||||
data: bytes
|
||||
|
||||
@staticmethod
|
||||
def get_default_element():
|
||||
return ClassgroupElement(int512(2), int512(1))
|
||||
# Bit 3 in the first byte of serialized compressed form indicates if
|
||||
# it's the default generator element.
|
||||
return ClassgroupElement(b"\x08")
|
||||
|
||||
@staticmethod
|
||||
def get_bad_element(constants: ConsensusConstants):
|
||||
# Used by test_blockchain to check that bad VDF outputs and proofs are
|
||||
# rejected. Use the default element for simplicity.
|
||||
return ClassgroupElement(b"\x08")
|
||||
|
||||
@staticmethod
|
||||
def get_size(constants: ConsensusConstants):
|
||||
return (constants.DISCRIMINANT_SIZE_BITS + 31) // 32 * 3 + 4
|
||||
|
|
|
@ -6,7 +6,6 @@ from typing import Optional, Tuple, Dict
|
|||
from chiavdf import create_discriminant
|
||||
from src.types.classgroup import ClassgroupElement
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.classgroup_utils import ClassGroup
|
||||
from chiavdf import verify_n_wesolowski
|
||||
from src.util.ints import uint8, uint64
|
||||
from src.util.streamable import Streamable, streamable
|
||||
|
@ -69,17 +68,14 @@ class VDFProof(Streamable):
|
|||
return False
|
||||
try:
|
||||
disc: int = get_discriminant(info.challenge, constants.DISCRIMINANT_SIZE_BITS)
|
||||
# x = ClassGroup.from_ab_discriminant(input_el.a, input_el.b, disc)
|
||||
y = ClassGroup.from_ab_discriminant(info.output.a, info.output.b, disc)
|
||||
except Exception:
|
||||
return False
|
||||
# TODO: parallelize somehow, this might included multiple mini proofs (n weso)
|
||||
# TODO: check for maximum witness type
|
||||
return verify_n_wesolowski(
|
||||
str(disc),
|
||||
str(input_el.a),
|
||||
str(input_el.b),
|
||||
y.serialize() + bytes(self.witness),
|
||||
input_el.data,
|
||||
info.output.data + bytes(self.witness),
|
||||
info.number_of_iterations,
|
||||
constants.DISCRIMINANT_SIZE_BITS,
|
||||
self.witness_type,
|
||||
|
|
|
@ -6,7 +6,7 @@ from src.consensus.constants import ConsensusConstants
|
|||
from src.types.classgroup import ClassgroupElement
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.types.vdf import VDFProof, VDFInfo
|
||||
from src.util.ints import uint64, int512, uint8
|
||||
from src.util.ints import uint64, uint8
|
||||
|
||||
|
||||
def get_vdf_info_and_proof(
|
||||
|
@ -15,30 +15,14 @@ def get_vdf_info_and_proof(
|
|||
challenge_hash: bytes32,
|
||||
number_iters: uint64,
|
||||
) -> Tuple[VDFInfo, VDFProof]:
|
||||
int_size = (constants.DISCRIMINANT_SIZE_BITS + 16) >> 4
|
||||
form_size = ClassgroupElement.get_size(constants)
|
||||
result: bytes = prove(
|
||||
bytes(challenge_hash),
|
||||
str(vdf_input.a),
|
||||
str(vdf_input.b),
|
||||
vdf_input.data,
|
||||
constants.DISCRIMINANT_SIZE_BITS,
|
||||
number_iters,
|
||||
)
|
||||
|
||||
output = ClassgroupElement(
|
||||
int512(
|
||||
int.from_bytes(
|
||||
result[0:int_size],
|
||||
"big",
|
||||
signed=True,
|
||||
)
|
||||
),
|
||||
int512(
|
||||
int.from_bytes(
|
||||
result[int_size : 2 * int_size],
|
||||
"big",
|
||||
signed=True,
|
||||
)
|
||||
),
|
||||
)
|
||||
proof_bytes = result[2 * int_size : 4 * int_size]
|
||||
output = ClassgroupElement(result[:form_size])
|
||||
proof_bytes = result[form_size : 2 * form_size]
|
||||
return VDFInfo(challenge_hash, number_iters, output), VDFProof(uint8(0), proof_bytes)
|
||||
|
|
|
@ -988,7 +988,7 @@ class TestBlockHeaderValidation:
|
|||
block_bad = recursive_replace(
|
||||
blocks[-1],
|
||||
"reward_chain_sub_block.reward_chain_sp_vdf.output",
|
||||
ClassgroupElement(int512(10), int512(2)),
|
||||
ClassgroupElement.get_bad_element(test_constants),
|
||||
)
|
||||
assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_SP_VDF
|
||||
block_bad = recursive_replace(
|
||||
|
@ -1032,7 +1032,7 @@ class TestBlockHeaderValidation:
|
|||
block_bad = recursive_replace(
|
||||
blocks[-1],
|
||||
"reward_chain_sub_block.challenge_chain_sp_vdf.output",
|
||||
ClassgroupElement(int512(10), int512(2)),
|
||||
ClassgroupElement.get_bad_element(test_constants),
|
||||
)
|
||||
assert (await empty_blockchain.receive_block(block_bad))[0] == ReceiveBlockResult.INVALID_BLOCK
|
||||
block_bad = recursive_replace(
|
||||
|
@ -1321,7 +1321,7 @@ class TestBlockHeaderValidation:
|
|||
block_bad = recursive_replace(
|
||||
blocks[-1],
|
||||
"reward_chain_sub_block.challenge_chain_ip_vdf.output",
|
||||
ClassgroupElement(int512(10), int512(2)),
|
||||
ClassgroupElement.get_bad_element(test_constants),
|
||||
)
|
||||
assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_CC_IP_VDF
|
||||
block_bad = recursive_replace(
|
||||
|
@ -1351,7 +1351,7 @@ class TestBlockHeaderValidation:
|
|||
block_bad = recursive_replace(
|
||||
blocks[-1],
|
||||
"reward_chain_sub_block.reward_chain_ip_vdf.output",
|
||||
ClassgroupElement(int512(10), int512(2)),
|
||||
ClassgroupElement.get_bad_element(test_constants),
|
||||
)
|
||||
assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_RC_IP_VDF
|
||||
block_bad = recursive_replace(
|
||||
|
@ -1382,7 +1382,7 @@ class TestBlockHeaderValidation:
|
|||
block_bad = recursive_replace(
|
||||
blocks[-1],
|
||||
"reward_chain_sub_block.infused_challenge_chain_ip_vdf.output",
|
||||
ClassgroupElement(int512(10), int512(2)),
|
||||
ClassgroupElement.get_bad_element(test_constants),
|
||||
)
|
||||
assert (await empty_blockchain.receive_block(block_bad))[1] == Err.INVALID_ICC_VDF
|
||||
block_bad = recursive_replace(
|
||||
|
|
Loading…
Reference in New Issue