diff --git a/prover.py b/prover.py index 21ac83d..cb4c647 100755 --- a/prover.py +++ b/prover.py @@ -29,85 +29,86 @@ while len(STORED_NONCES) < common.ENTRIES_NUMBER: print(len(STORED_NONCES), common.ENTRIES_NUMBER, file=sys.stderr) counter += 1 -## Execution phase: 1. Receive a random bitstring from the verifier. +while True: + print() + ## Execution phase: 1. Receive a random bitstring from the verifier. -protocolExecutionPhaseId = input('protocolExecutionPhaseId: ') + protocolExecutionPhaseId = input('protocolExecutionPhaseId: ') -## Execution phase: 2. The prover commits to the table contents given this random challenge. + ## 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 pairwise(iterable): + "s -> (s0, s1), (s2, s3), (s4, s5), ..." + a = iter(iterable) + return zip(a, a) -def getNextMerkleTreeLevel(nodes): - return [common.hash(leftNode + rightNode) for leftNode, rightNode in pairwise(nodes)] + def getNextMerkleTreeLevel(nodes): + return [common.hash(leftNode + rightNode) for leftNode, rightNode in pairwise(nodes)] -merkleTreeLevel = [] -counter = 0 -for deltaCounter in STORED_NONCES: - counter += deltaCounter - hashed = common.hash(protocolInitializationPhaseId + protocolExecutionPhaseId + str(counter)) - merkleTreeLevel += [hashed] + merkleTreeLevel = [] + counter = 0 + for deltaCounter in STORED_NONCES: + counter += deltaCounter + hashed = common.hash(protocolInitializationPhaseId + protocolExecutionPhaseId + str(counter)) + merkleTreeLevel += [hashed] -merkleTreeLevels = [merkleTreeLevel] + merkleTreeLevels = [merkleTreeLevel] -while len(merkleTreeLevels[-1]) > 1: - nextMerkleTreeLevel = getNextMerkleTreeLevel(merkleTreeLevels[-1]) - merkleTreeLevels += [nextMerkleTreeLevel] + while len(merkleTreeLevels[-1]) > 1: + nextMerkleTreeLevel = getNextMerkleTreeLevel(merkleTreeLevels[-1]) + merkleTreeLevels += [nextMerkleTreeLevel] -merkleTreeRoot = merkleTreeLevels[-1][0] + merkleTreeRoot = merkleTreeLevels[-1][0] -print(f'{merkleTreeRoot=}') + print(f'{merkleTreeRoot=}') -## Execution phase: 3. Receive a random set of indexes from the verifier. + ## Execution phase: 3. Receive a random set of indexes from the verifier. -with open(input('indexesRequest: ')) as f: - indexesRequest = ast.literal_eval(f.read()) + with open(input('indexesRequest: ')) as f: + indexesRequest = ast.literal_eval(f.read()) -## Execution phase: 4. Respond with the corresponding table entries and commitment openings. + ## Execution phase: 4. Respond with the corresponding table entries and commitment openings. -# First computing indexes of fields required in the different levels. -# Then just transmit the hashes of these indexes, as the verifier can compute these indexes on its own. -# Then proceed to the Merkle tree root validation. Designing this first to make sure transmit in the easiest way make sense. + # First computing indexes of fields required in the different levels. + # Then just transmit the hashes of these indexes, as the verifier can compute these indexes on its own. + # Then proceed to the Merkle tree root validation. Designing this first to make sure transmit in the easiest way make sense. -# For verifying treat level by level + # For verifying treat level by level -# Just sending a list of hashes that the verifier fills its data structure with, as the prover generated this list. -# So have to just verify that there's no such attack when filling the first level. -# Verify especially my parsing even with the use of `ast.literal_eval` like the type and the size of entries and possibly subentries. + # Just sending a list of hashes that the verifier fills its data structure with, as the prover generated this list. + # So have to just verify that there's no such attack when filling the first level. + # Verify especially my parsing even with the use of `ast.literal_eval` like the type and the size of entries and possibly subentries. -# Could also proceed index by index by providing entry and siblings up to the root for each index. -# Could use page 28 of FlyClient: Super-Light Clients for Cryptocurrencies for the implementation. + # Could also proceed index by index by providing entry and siblings up to the root for each index. + # Could use page 28 of FlyClient: Super-Light Clients for Cryptocurrencies for the implementation. -verificationMerkleTreeLevels = [{} for _ in range(common.LEVELS)] + verificationMerkleTreeLevels = [{} for _ in range(common.LEVELS)] -for indexRequest in indexesRequest: - otherIndex = common.otherIndex(indexRequest) - for index in [indexRequest, otherIndex]: - verificationMerkleTreeLevels[0][index] = merkleTreeLevels[0][index] + for indexRequest in indexesRequest: + otherIndex = common.otherIndex(indexRequest) + for index in [indexRequest, otherIndex]: + verificationMerkleTreeLevels[0][index] = merkleTreeLevels[0][index] -# Should rename `entries`, as they aren't *entries* for higher levels. -entries = [] -for verificationMerkleTreeLevelsIndex, verificationMerkleTreeLevel in enumerate(verificationMerkleTreeLevels[:-1]): - for index in verificationMerkleTreeLevel: - otherIndex = common.otherIndex(index) - leftIndex, rightIndex = sorted([index, otherIndex]) - leftHash, rightHash = [merkleTreeLevels[verificationMerkleTreeLevelsIndex][i] for i in [leftIndex, rightIndex]] - otherHash = merkleTreeLevels[verificationMerkleTreeLevelsIndex][otherIndex] - verificationMerkleTreeLevels[verificationMerkleTreeLevelsIndex + 1][leftIndex // 2] = common.hash(leftHash + rightHash) - if verificationMerkleTreeLevelsIndex > 0 or leftIndex == index: - if verificationMerkleTreeLevelsIndex == 0: - leftNonce = sum(STORED_NONCES[:leftIndex + 1]) - rightNonce = leftNonce + STORED_NONCES[rightIndex] - entries += [leftNonce, rightNonce] - else: - entries += [otherHash] + # Should rename `entries`, as they aren't *entries* for higher levels. + entries = [] + for verificationMerkleTreeLevelsIndex, verificationMerkleTreeLevel in enumerate(verificationMerkleTreeLevels[:-1]): + for index in verificationMerkleTreeLevel: + otherIndex = common.otherIndex(index) + leftIndex, rightIndex = sorted([index, otherIndex]) + leftHash, rightHash = [merkleTreeLevels[verificationMerkleTreeLevelsIndex][i] for i in [leftIndex, rightIndex]] + otherHash = merkleTreeLevels[verificationMerkleTreeLevelsIndex][otherIndex] + verificationMerkleTreeLevels[verificationMerkleTreeLevelsIndex + 1][leftIndex // 2] = common.hash(leftHash + rightHash) + if verificationMerkleTreeLevelsIndex > 0 or leftIndex == index: + if verificationMerkleTreeLevelsIndex == 0: + leftNonce = sum(STORED_NONCES[:leftIndex + 1]) + rightNonce = leftNonce + STORED_NONCES[rightIndex] + entries += [leftNonce, rightNonce] + else: + entries += [otherHash] -entriesFilePath = 'entries.txt' + entriesFilePath = 'entries.txt' -with open(entriesFilePath, 'w') as f: - f.write(str(entries)) - -print(f'entries: {entriesFilePath}') + with open(entriesFilePath, 'w') as f: + f.write(str(entries)) + print(f'entries: {entriesFilePath}') diff --git a/verifier.py b/verifier.py index 7555b5e..dda765c 100755 --- a/verifier.py +++ b/verifier.py @@ -10,77 +10,79 @@ import secrets, common, ast, sys protocolInitializationPhaseId = secrets.token_hex(common.SECURITY_PARAMETER // 8) if len(sys.argv) == 1 else sys.argv[1] print(f'{protocolInitializationPhaseId=}') -## Execution phase: 1. Generate a random bitstring to send to the prover. -# TODO: improve step numbering, as it's a bit unclear what step of each to run after which one -# Wait the initialization phase termination from the prover side before starting the execution phase. +while True: + print() + ## Execution phase: 1. Generate a random bitstring to send to the prover. + # TODO: improve step numbering, as it's a bit unclear what step of each to run after which one + # Wait the initialization phase termination from the prover side before starting the execution phase. -protocolExecutionPhaseId = secrets.token_hex(common.SECURITY_PARAMETER // 8) if len(sys.argv) <= 2 else sys.argv[2] -print(f'{protocolExecutionPhaseId=}') + protocolExecutionPhaseId = secrets.token_hex(common.SECURITY_PARAMETER // 8) if len(sys.argv) <= 2 else sys.argv[2] + print(f'{protocolExecutionPhaseId=}') -## Execution phase: 2. Receive the prover commitment to the table contents given this random challenge. + ## Execution phase: 2. Receive the prover commitment to the table contents given this random challenge. -merkleTreeRoot = input('merkleTreeRoot: ') + merkleTreeRoot = input('merkleTreeRoot: ') -## Execution phase: 3. Generate and send a random set of indexes to the prover. + ## Execution phase: 3. Generate and send a random set of indexes to the prover. -# Need to know the actual table size to choose a random set of indexes. 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. In a more important manner, it's unclear how the prover proves to the verifier precisely with high probability or *for sure* that he is actually storing a given number of bits. -# For this prototype the prover claims a given table size and the verifier estimate the total table number of bits thanks to the requested set of its entries. + # Need to know the actual table size to choose a random set of indexes. 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. In a more important manner, it's unclear how the prover proves to the verifier precisely with high probability or *for sure* that he is actually storing a given number of bits. + # For this prototype the prover claims a given table size and the verifier estimate the total table number of bits thanks to the requested set of its entries. -# Note that there's no advantage to request the last table entry by hoping that the prover is storing the nonces with delta in such order, for instance he can have stored the delta the other way around which screw up this additional request aim. + # Note that there's no advantage to request the last table entry by hoping that the prover is storing the nonces with delta in such order, for instance he can have stored the delta the other way around which screw up this additional request aim. -indexesRequest = [] if len(sys.argv) <= 3 else ast.literal_eval(sys.argv[3]) + indexesRequest = [] if len(sys.argv) <= 3 else ast.literal_eval(sys.argv[3]) -if indexesRequest == []: - while len(indexesRequest) < common.NUMBER_OF_ENTRIES_TO_VERIFY: - index = secrets.randbelow(common.ENTRIES_NUMBER) - if not common.otherIndex(index) in indexesRequest and not index in indexesRequest: - indexesRequest += [index] + if indexesRequest == []: + while len(indexesRequest) < common.NUMBER_OF_ENTRIES_TO_VERIFY: + index = secrets.randbelow(common.ENTRIES_NUMBER) + if not common.otherIndex(index) in indexesRequest and not index in indexesRequest: + indexesRequest += [index] -indexesRequestFilePath = 'indexesRequest.txt' + indexesRequestFilePath = 'indexesRequest.txt' -with open(indexesRequestFilePath, 'w') as f: - f.write(str(indexesRequest)) + with open(indexesRequestFilePath, 'w') as f: + f.write(str(indexesRequest)) -print(f'indexesRequest: {indexesRequestFilePath}') + print(f'indexesRequest: {indexesRequestFilePath}') -## Execution phase: 4. Receive and verify corresponding table entries and commitment openings. + ## Execution phase: 4. Receive and verify corresponding table entries and commitment openings. -# Add mentions of rationality and code its factor? or/and precise when to run initialization and execution phases -# TODO: precising `entries` size could be interesting. + # Add mentions of rationality and code its factor? or/and precise when to run initialization and execution phases + # TODO: precising `entries` size could be interesting. -# Proceeding in a minimum number of passes could be interesting but this can be left for future work. + # Proceeding in a minimum number of passes could be interesting but this can be left for future work. -with open(input('entries: ')) as f: - entries = ast.literal_eval(f.read()) + with open(input('entries: ')) as f: + entries = ast.literal_eval(f.read()) -merkleTreeLevels = [{} for _ in range(common.LEVELS)] + merkleTreeLevels = [{} for _ in range(common.LEVELS)] -for index in indexesRequest: - otherIndex = common.otherIndex(index) - leftIndex, rightIndex = sorted([index, otherIndex]) - for i, nonce in zip([leftIndex, rightIndex], [entries.pop(0) for _ in range(2)]): - hashed = common.hash(protocolInitializationPhaseId + str(nonce)) - if not common.doesHashMatchDifficulty(hashed): - print("The received difficulty doesn't match the claimed one!") - exit(1) - hashed = common.hash(protocolInitializationPhaseId + protocolExecutionPhaseId + str(nonce)) - merkleTreeLevels[0][i] = hashed - -for merkleTreeLevelsIndex, merkleTreeLevel in enumerate(merkleTreeLevels[:-1]): - for index in merkleTreeLevel.copy(): + for index in indexesRequest: otherIndex = common.otherIndex(index) leftIndex, rightIndex = sorted([index, otherIndex]) - if merkleTreeLevelsIndex > 0: - merkleTreeLevels[merkleTreeLevelsIndex][otherIndex] = entries.pop(0) - leftHash, rightHash = [merkleTreeLevels[merkleTreeLevelsIndex][i] for i in [leftIndex, rightIndex]] - merkleTreeLevels[merkleTreeLevelsIndex + 1][leftIndex // 2] = common.hash(leftHash + rightHash) + for i, nonce in zip([leftIndex, rightIndex], [entries.pop(0) for _ in range(2)]): + hashed = common.hash(protocolInitializationPhaseId + str(nonce)) + if not common.doesHashMatchDifficulty(hashed): + print("The received difficulty doesn't match the claimed one!") + exit(1) + hashed = common.hash(protocolInitializationPhaseId + protocolExecutionPhaseId + str(nonce)) + merkleTreeLevels[0][i] = hashed -# Note that `actualStorage` is the average storage that the prover should have dedicated to be able to solve this execution phase challenge. -# If any entry corresponding to requested indexes is incorrect, the Merkle tree won't match or the verification of the claimed difficulty of the provided hash will fail. -actualStorage = common.ENTRIES_NUMBER * common.NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE + for merkleTreeLevelsIndex, merkleTreeLevel in enumerate(merkleTreeLevels[:-1]): + for index in merkleTreeLevel.copy(): + otherIndex = common.otherIndex(index) + leftIndex, rightIndex = sorted([index, otherIndex]) + if merkleTreeLevelsIndex > 0: + merkleTreeLevels[merkleTreeLevelsIndex][otherIndex] = entries.pop(0) + leftHash, rightHash = [merkleTreeLevels[merkleTreeLevelsIndex][i] for i in [leftIndex, rightIndex]] + merkleTreeLevels[merkleTreeLevelsIndex + 1][leftIndex // 2] = common.hash(leftHash + rightHash) -if merkleTreeRoot == merkleTreeLevels[-1][0] and actualStorage >= common.SPACE_TO_PROVE_IN_BITS: - print(f'Verified with probability {common.PROBABILITY_TO_CATCH_MALICIOUS} that the prover is storing in average {common.INITIAL_SPACE_TO_PROVE_IN_BITS} (actually the prover is storing in average {actualStorage} bits)') -else: - print("The prover hasn't provided a correct proof!") + # Note that `actualStorage` is the average storage that the prover should have dedicated to be able to solve this execution phase challenge. + # If any entry corresponding to requested indexes is incorrect, the Merkle tree won't match or the verification of the claimed difficulty of the provided hash will fail. + actualStorage = common.ENTRIES_NUMBER * common.NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE + + if merkleTreeRoot == merkleTreeLevels[-1][0] and actualStorage >= common.SPACE_TO_PROVE_IN_BITS: + print(f'Verified with probability {common.PROBABILITY_TO_CATCH_MALICIOUS} that the prover is storing in average {common.INITIAL_SPACE_TO_PROVE_IN_BITS} (actually the prover is storing in average {actualStorage} bits)') + else: + print("The prover hasn't provided a correct proof!")