-
Notifications
You must be signed in to change notification settings - Fork 0
/
poc.py
139 lines (100 loc) · 3.59 KB
/
poc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import hashlib
from typing import List
from merkletree import MerkleTree
import time
import random
def HASH_FUNCTION(input): return hashlib.sha256(input).digest()
DIFFICULTY = 1500000
''' amount of leaves, difficulty: (DIFFICULTY*2)-1 - hashes required in total'''
PREV_BLOCK_HASH = b'qwertyuiopasdfghjklzxcvbnmasdfew'
TIMESTAMP = int(time.time())
''' timestamp of the last block '''
MASK = 0b01111111
def num_to_bytes(number: int) -> bytes:
'''
Basically it just uses the lowest amount of bytes to represent a number in a big endian notation
'''
to_return = b''
if number != 0:
i_clone = number
while i_clone > 0:
to_return = (i_clone & 0xff).to_bytes(1, 'big') + to_return
i_clone >>= 8
else:
to_return = b'\x00'
return to_return
def generate_initial_data(prev_hash: bytes,
difficulty: int,
nonce: bytes) -> List[bytes]:
'''
Generates initial data for a mining process
The data is generated as a hash chain
Hash(prev_hash+nonce)
Parameters:
prev_hash: bytes - hash of the previous block
difficulty: int - difficulty
nonce: bytes - a nonce to add for each iteration
Returns:
A list with hashes
'''
to_return = [prev_hash]
for _ in range(difficulty-1):
to_hash = prev_hash + nonce
hash = HASH_FUNCTION(to_hash)
to_return.append(hash)
prev_hash = hash
return to_return
def mine(prev_hash: bytes, difficulty: int, target: int) -> bytes:
'''
Proof of concept for a mining algorithm
The nonce can be a string of bytes of an arbitrary length.
Parameters:
prev_hash: bytes - hash of the previous block
difficulty: int - difficulty
target: int - the target we are looking for
Returns:
Nonce
'''
nonce = 0
while True:
# generate leaves with a nonce
initial_data = generate_initial_data(
prev_hash, difficulty, nonce.to_bytes(8, 'big'))
# create merkle tree from leaves
tree = MerkleTree(HASH_FUNCTION, initial_data)
# build the tree
tree.build_tree()
# apply a mask to the calculated root to make the hash into a number in the range [0, MASK]
num = int.from_bytes(tree.root, 'big') % (MASK+1)
# if the found number is equal to the target - return found data
if num == target:
return nonce.to_bytes(8, 'big')
nonce += 1
def verification(prev_hash: bytes, difficulty: int, nonce: bytes, target: int):
'''
calculated PoW verification
'''
initial_data = generate_initial_data(
prev_hash, difficulty, nonce)
#proof.insert(0, initial_data[1])
tree = MerkleTree(HASH_FUNCTION, initial_data)
tree.build_tree()
num = int.from_bytes(tree.root, 'big') % (MASK+1)
if num == target:
print("Target found")
def main():
global DIFFICULTY, PREV_BLOCK_HASH, TIMESTAMP
target = random.randint(0, MASK)
print("Target:", target)
start_mining = time.time()
nonce = mine(
PREV_BLOCK_HASH, DIFFICULTY, target)
delta_mining = time.time() - start_mining
print("Time taken by mining:", delta_mining)
start_verification = time.time()
verification(PREV_BLOCK_HASH, DIFFICULTY, nonce, target)
delta_verification = time.time() - start_verification
print("Time taken by verification:", delta_verification)
print(f"Verification was {delta_mining/delta_verification} times faster")
if __name__ == "__main__":
main()