Skip to content

Commit a6142eb

Browse files
committed
Validate a block and test is_valid_block
1 parent 44abb72 commit a6142eb

2 files changed

Lines changed: 76 additions & 3 deletions

File tree

backend/blockchain/block.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import time
22

33
from backend.util.crypto_hash import crypto_hash
4+
from backend.util.hex_to_binary import hex_to_binary
45
from backend.config import MINE_RATE
56

67
GENESIS_DATA = {
@@ -48,7 +49,7 @@ def mine_block(last_block, data):
4849
nonce = 0
4950
hash = crypto_hash(timestamp, last_hash, data, difficulty, nonce)
5051

51-
while hash[0:difficulty] != '0' * difficulty:
52+
while hex_to_binary(hash)[0:difficulty] != '0' * difficulty:
5253
nonce += 1
5354
timestamp = time.time_ns()
5455
difficulty = Block.adjust_difficulty(last_block, timestamp)
@@ -78,10 +79,44 @@ def adjust_difficulty(last_block, new_timestamp):
7879

7980
return 1
8081

82+
@staticmethod
83+
def is_valid_block(last_block, block):
84+
"""
85+
Validate block by enforcing the following rules:
86+
- the block must have the proper last_hash reference
87+
- the block must meet the proof of work requirement
88+
- the difficulty must only adjust by 1
89+
- the block hash must be a valid combination of the block fields
90+
"""
91+
if block.last_hash != last_block.hash:
92+
raise Exception('The block last_hash must be correct')
93+
94+
if hex_to_binary(block.hash)[0:block.difficulty] != '0' * block.difficulty:
95+
raise Exception('The proof of work requirement was not met')
96+
97+
if abs(last_block.difficulty - block.difficulty) > 1:
98+
raise Exception('The block difficulty must only adjust by 1')
99+
100+
reconstructed_hash = crypto_hash(
101+
block.timestamp,
102+
block.last_hash,
103+
block.data,
104+
block.nonce,
105+
block.difficulty
106+
)
107+
108+
if block.hash != reconstructed_hash:
109+
raise Exception('The block hash must be correct')
110+
81111
def main():
82112
genesis_block = Block.genesis()
83-
block = Block.mine_block(genesis_block, 'foo')
84-
print(block)
113+
bad_block = Block.mine_block(genesis_block, 'foo')
114+
bad_block.last_hash = 'evil_data'
115+
116+
try:
117+
Block.is_valid_block(genesis_block, bad_block)
118+
except Exception as e:
119+
print(f'is_valid_block: {e}')
85120

86121
if __name__ == '__main__':
87122
main()

backend/tests/blockchain/test_block.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import pytest
12
import time
23

34
from backend.blockchain.block import Block, GENESIS_DATA
@@ -48,3 +49,40 @@ def test_mined_block_difficulty_limits_at_1():
4849
mined_block = Block.mine_block(last_block, 'bar')
4950

5051
assert mined_block.difficulty == 1
52+
53+
@pytest.fixture
54+
def last_block():
55+
return Block.genesis()
56+
57+
@pytest.fixture
58+
def block(last_block):
59+
return Block.mine_block(last_block, 'test_data')
60+
61+
def test_is_valid_block(last_block, block):
62+
Block.is_valid_block(last_block, block)
63+
64+
def test_is_valid_block_bad_last_hash(last_block, block):
65+
block.last_hash = 'evil_last_hash'
66+
67+
with pytest.raises(Exception, match='last_hash must be correct'):
68+
Block.is_valid_block(last_block, block)
69+
70+
def test_is_valid_block_bad_proof_of_work(last_block, block):
71+
block.hash = 'fff'
72+
73+
with pytest.raises(Exception, match='proof of work requirement was not met'):
74+
Block.is_valid_block(last_block, block)
75+
76+
def test_is_valid_block_jumped_difficulty(last_block, block):
77+
jumped_difficulty = 10
78+
block.difficulty = jumped_difficulty
79+
block.hash = f'{"0" * jumped_difficulty}111abc'
80+
81+
with pytest.raises(Exception, match='difficulty must only adjust by 1'):
82+
Block.is_valid_block(last_block, block)
83+
84+
def test_is_valid_block_bad_block_hash(last_block, block):
85+
block.hash = '0000000000000000bbbabc'
86+
87+
with pytest.raises(Exception, match='block hash must be correct'):
88+
Block.is_valid_block(last_block, block)

0 commit comments

Comments
 (0)