Last active
June 7, 2023 06:21
-
-
Save sphinxid/610be4302400a3c8dda95421ae0b256c to your computer and use it in GitHub Desktop.
This code simulates a client-server interaction using a Proof-of-Work (PoW) system. In such systems, the client must solve a computational challenge set by the server in order to have its request processed. This particular code makes use of SHA3-256 hashing and requires the hash of a unique challenge and a nonce to have a specific number of lead…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hashlib | |
import hmac | |
import time | |
import secrets | |
import random | |
# Secret Key for HMAC | |
SECRET_KEY = b"SayaOrangPalingGantengSedunia" # please change it to something else lol. | |
# Challenge (this could be any piece of data) | |
CHALLENGE = hashlib.sha3_256(str(secrets.randbits(64)).encode()).hexdigest() | |
# Difficulty (number of leading zeroes required in the hash) | |
DIFFICULTY = 5 | |
# Maximum time difference (in seconds) | |
TIME_DIFF = 300 | |
# Nonce Range | |
NONCE_RANGE = 10**10 | |
# The function server_set_challenge creates a unique string and a timestamp. | |
# It concatenates the unique string with the challenge and outputs the challenge, unique string, and timestamp together. | |
# The server instructs the client to find a nonce (a random value) that, when hashed with the challenge + unique string, | |
# results in a hash with a specific number of leading zeros (as per the "difficulty" level). | |
def server_set_challenge(): | |
unique_str = secrets.token_hex(16) | |
timestamp = int(time.time()) | |
challenge_with_unique_str = CHALLENGE + unique_str | |
digest = hmac.new(SECRET_KEY, str(challenge_with_unique_str + str(timestamp)).encode("utf-8"), hashlib.sha3_256).hexdigest() | |
print(f"Server: The challenge is '{CHALLENGE}', you need to find a nonce such that the SHA3-256 hash of the challenge + unique string, and nonce together has at least {DIFFICULTY} leading zeroes.") | |
return challenge_with_unique_str, timestamp, digest | |
# The function client_solve_challenge tries to solve the challenge. It begins by initializing a nonce within a given range. | |
# Then, it starts a loop where it concatenates the challenge and the nonce, hashes this data, and checks if the hash meets | |
# the difficulty level. If it does, the client has solved the challenge and outputs the nonce along with the time taken. | |
# If not, the nonce is incremented and the process repeats until a solution is found. | |
def client_solve_challenge(challenge, server_timestamp): | |
print("Client: Solving challenge...") | |
challenge_with_timestamp = challenge + str(server_timestamp) | |
start_time = time.time() | |
nonce = random.randint(0, NONCE_RANGE) | |
while True: | |
# Concatenate the challenge and nonce | |
data = challenge_with_timestamp + str(nonce) | |
# Compute the SHA3-256 hash of the data | |
hash_result = hashlib.sha3_256(data.encode()).hexdigest() | |
# Check if the hash has the required number of leading zeroes | |
if hash_result[:DIFFICULTY] == "0" * DIFFICULTY: | |
end_time = time.time() | |
print(f"hash result -> {hash_result}") | |
print(f"client timestamp -> {start_time}") | |
print(f"server timestamp -> {server_timestamp}") | |
print(f"Client: Challenge solved in {end_time - start_time} seconds, the nonce is {nonce}") | |
return nonce, start_time | |
# If the hash does not have the required number of leading zeroes, increment the nonce and try again | |
nonce = (nonce + 1) % NONCE_RANGE | |
# The function server_verify_solution checks the client's solution. It hashes the challenge and nonce again, | |
# and checks if the resultant hash meets the difficulty level and whether the solution was found within a specific | |
# time limit. If both conditions are met, the server processes the request and outputs a confirmation message. | |
# Otherwise, it rejects the request. | |
def server_verify_solution(challenge, nonce, client_timestamp, server_timestamp, digest): | |
## | |
#challenge = "3b9e9d95c105f3ca9e1ec56fdc6c15ba3a91b0b53e1ac90f105f01283a05de17" | |
#nonce = 1588079050 | |
#client_timestamp = server_timestamp + 5000 | |
## | |
data = challenge + str(server_timestamp) + str(nonce) | |
hash_result = hashlib.sha3_256(data.encode()).hexdigest() | |
original_digest = hmac.new(SECRET_KEY, str(challenge + str(server_timestamp)).encode("utf-8"), | |
hashlib.sha3_256).hexdigest() | |
# Check if the solution was found within the time limit | |
current_timestamp = int(time.time()) | |
if (digest != original_digest): | |
print("Server: challenge has been tampered.") | |
elif (current_timestamp - client_timestamp > TIME_DIFF): | |
print("Server: Solution is too old, request rejected") | |
elif hash_result[:DIFFICULTY] == "0" * DIFFICULTY: | |
print("Server: Solution is correct, processing request...") | |
# Simulate request processing | |
time.sleep(1) | |
print("Server: Request processed") | |
else: | |
print("Server: Incorrect solution") | |
# The function simulate_api_request encapsulates the whole process. | |
# The server sets a challenge, the client solves it, and then the server verifies the solution. | |
def simulate_api_request(): | |
challenge, server_timestamp, digest = server_set_challenge() | |
nonce, client_timestamp = client_solve_challenge(challenge, server_timestamp) | |
server_verify_solution(challenge, nonce, client_timestamp, server_timestamp, digest) | |
def main(): | |
simulate_api_request() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
$ python3.10 pow04.py
Server: The challenge is '45834afa64a3eb05a15e01c11feeee05f6dc1db3bae17d7dc15eb55b947de79e', you need to find a nonce such that the SHA3-256 hash of the challenge + unique string, and nonce together has at least 5 leading zeroes.
Client: Solving challenge...
hash result -> 000005faf2176f73d9ff080893587183ef6cf6ed1ef307f930d547dcf5221dc8
client timestamp -> 1686118872.015199
server timestamp -> 1686118872
Client: Challenge solved in 1.4372360706329346 seconds, the nonce is 6410631986
Server: Solution is correct, processing request...
Server: Request processed