diff --git a/common.py b/common.py index 85f18f7..00d49bd 100644 --- a/common.py +++ b/common.py @@ -1 +1,8 @@ -SECURITY_PARAMETER = 256 \ No newline at end of file +from hashlib import sha256 + +SECURITY_PARAMETER = 256 +MAXIMUM_HASH_DIFFICULTY = 1 / 16 +SPACE_TO_PROVE_IN_BITS = 1_000 + +def hash(x): + return sha256(x.encode('ascii')).hexdigest() \ No newline at end of file diff --git a/prover.py b/prover.py index c43ebcb..66359d8 100644 --- a/prover.py +++ b/prover.py @@ -1,39 +1,75 @@ -# Proof of Space-Time prover +## Proof of Space-Time prover import math, common -from hashlib import sha256 -SPACE_TO_PROVE_IN_BITS = 1_000 -MAXIMUM_HASH_DIFFICULTY = 0.1 +# For the ratio between storing and power: +# Note that for a production ready system hashing speedup (as designed for Bitcoin in [AsicBoost - A Speedup for Bitcoin Mining, Dr. Timo Hanke, March 31, 2016 (rev. 5)](https://arxiv.org/pdf/1604.00575.pdf)) should be considered. -# 1. Receive a random bitstring from the verifier. +## Initialization phase: 1. Receive a random bitstring from the verifier. -protocolExecutionId = input('protocolExecutionId: ') +protocolInitializationPhaseId = input('protocolInitializationPhaseId: ') -# 2. Generate in a table the data that the prover has to store. +## Initialization phase: 2. Generate in a table the data that the prover has to store. # To ensure that this data is cheaper to store than to generate and to allow public verifiability by requiring the stored data to be Proof of Works. # For the ease of this implementation, the data are stored in the memory instead of the disk. +# Could rename `STORED_DATA` to what it is actually storing. STORED_DATA = [] STORED_DATA_IN_BITS = 0 counter = 0 +lastCounter = 0 LEAST_DIFFICULT_HASH = int('F' * (common.SECURITY_PARAMETER // 4), 16) # We enforce `STORED_DATA` to have its length being a power of 2, to ease Merkle tree implementation. while STORED_DATA_IN_BITS < SPACE_TO_PROVE_IN_BITS or not math.log2(len(STORED_DATA)).is_integer(): print(STORED_DATA_IN_BITS, len(STORED_DATA)) - # Note that for a production ready system hashing speedup (as designed for Bitcoin in [AsicBoost - A Speedup for Bitcoin Mining, Dr. Timo Hanke, March 31, 2016 (rev. 5)](https://arxiv.org/pdf/1604.00575.pdf)) should be considered. - hash = sha256((protocolExecutionId + str(counter)).encode('ascii')).hexdigest() - hashInteger = int(hash, 16) - hashDifficulty = hashInteger / LEAST_DIFFICULT_HASH - if hashDifficulty <= MAXIMUM_HASH_DIFFICULTY: - lastCounter = STORED_DATA[-1] if STORED_DATA != [] else -1 + hashed = common.hash(protocolInitializationPhaseId + str(counter)) + hashedInteger = int(hashed, 16) + hashedDifficulty = hashedInteger / LEAST_DIFFICULT_HASH + if hashedDifficulty <= common.MAXIMUM_HASH_DIFFICULTY: deltaCounter = counter - lastCounter - deltaCounterBits = math.ceil(math.log2(deltaCounter)) + deltaCounterBits = math.floor(math.log2(deltaCounter)) + 1 STORED_DATA += [deltaCounter] STORED_DATA_IN_BITS += deltaCounterBits - print(hash, hashDifficulty, counter, lastCounter, deltaCounterBits) + lastCounter = counter counter += 1 +## Execution phase: 1. Receive a random bitstring from the verifier. + +protocolExecutionPhaseId = input('protocolExecutionPhaseId: ') + +## Execution phase: 2. The prover commits to the table contents given this random challenge. + +def pairwise(iterable): + "s -> (s0, s1), (s2, s3), (s4, s5), ..." + a = iter(iterable) + return zip(a, a) + +def getNextMerkleTreeLevel(nodes): + return [hash(leftNode + rightNode) for leftNode, rightNode in pairwise(nodes)] + +merkleTreeLevel = [] +counter = 0 +for deltaCounter in STORED_DATA: + counter += deltaCounter + hashed = hash(protocolInitializationPhaseId + protocolExecutionPhaseId + str(counter)) + merkleTreeLevel += [hashed] + +merkleTreeLevels = [merkleTreeLevel] + +while len(merkleTreeLevels[-1]) > 1: + nextMerkleTreeLevel = getNextMerkleTreeLevel(merkleTreeLevels[-1]) + merkleTreeLevels += [nextMerkleTreeLevel] + +merkleTreeRoot = merkleTreeLevels[-1] + +print(f'{merkleTreeRoot=}') + +## Execution phase: 3. Receive a random set of indices from the prover. + + + +## Execution phase: 4. + diff --git a/verifier.py b/verifier.py index 95ab792..244d9a0 100644 --- a/verifier.py +++ b/verifier.py @@ -1,11 +1,33 @@ -# Proof of Space-Time verifier +## Proof of Space-Time verifier import secrets, common -# 1. Generate a random bitstring to send to the prover. +def generateARandomHexString(hexStringLength): + return ''.join(secrets.choice('0123456789abcdef') for _ in range(hexStringLength)) + +## Initialization phase: Generate a random bitstring to send to the prover. # To make sure that the following protocol execution can't be used partially or totally by the prover to reduce the cost of another protocol execution. -protocolExecutionId = ''.join(secrets.choice('0123456789abcdef') for _ in range(common.SECURITY_PARAMETER // 4)) -print(protocolExecutionId) +protocolInitializationPhaseId = generateARandomHexString(common.SECURITY_PARAMETER // 4) +print(f'{protocolInitializationPhaseId=}') + +## Execution phase: 1. Generate a random bitstring to send to the prover. +# TODO: improve step numbering +# Wait the initialization phase termination from the prover side before starting the execution phase. + +protocolExecutionPhaseId = generateARandomHexString(common.SECURITY_PARAMETER // 4) +print(f'{protocolExecutionPhaseId=}') + +## Execution phase: 2. Receive the prover commitment to the table contents given this random challenge. + +merkleTreeRoot = input('merkleTreeRoot: ') + +## Execution phase: 3. Generate and send a random set of indices to the prover. + +# Need to know the actual table size to choose a random set of indices. This point isn't discussed to the best of my knowledge in Simple Proofs of Space-Time and Rational Proofs of Storage, Tal Moran and Ilan Orlov, 2016. + + + +## Execution phase: 4. Receive and verify corresponding table entries and commitment openings. + -# 2. \ No newline at end of file