TroglodyteDerivations commited on
Commit
e98a66a
1 Parent(s): cfb439e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +178 -0
app.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+ import hashlib
3
+ import time
4
+ import plyvel
5
+ import struct
6
+ from typing import List
7
+ from hashlib import sha256
8
+ import streamlit as st
9
+
10
+ # Global constant for target bits
11
+ TARGET_BITS = 24
12
+
13
+ class Block:
14
+ def __init__(self, index, previous_hash, timestamp, data, nonce, hash):
15
+ self.index = index
16
+ self.previous_hash = previous_hash
17
+ self.timestamp = timestamp
18
+ self.data = data
19
+ self.nonce = nonce
20
+ self.hash = hash
21
+
22
+ def serialize(self):
23
+ """Serialize the Block object to a byte stream."""
24
+ return pickle.dumps(self.__dict__)
25
+
26
+ @staticmethod
27
+ def deserialize(serialized_block):
28
+ """Deserialize a byte stream into a Block object."""
29
+ block_dict = pickle.loads(serialized_block)
30
+ return Block(**block_dict)
31
+
32
+ def __repr__(self):
33
+ return (f"Block(index={self.index}, "
34
+ f"timestamp={self.timestamp}, "
35
+ f"data={self.data.decode('utf-8')}, "
36
+ f"prev_block_hash={self.previous_hash.hex()}, "
37
+ f"hash={self.hash.hex()}, "
38
+ f"nonce={self.nonce})")
39
+
40
+ class ProofOfWork:
41
+ def __init__(self, block: Block):
42
+ self.block = block
43
+ self.target = 1 << (256 - TARGET_BITS)
44
+
45
+ def prepare_data(self, nonce: int) -> bytes:
46
+ """Prepare the data to be hashed."""
47
+ return b''.join([
48
+ self.block.previous_hash,
49
+ self.block.data,
50
+ struct.pack('>I', int(self.block.timestamp)), # Convert timestamp to int
51
+ struct.pack('>I', TARGET_BITS),
52
+ struct.pack('>I', nonce)
53
+ ])
54
+
55
+ def run(self) -> (int, bytes):
56
+ """Run the Proof of Work algorithm."""
57
+ nonce = 0
58
+ st.write(f"Mining the block containing \"{self.block.data.decode('utf-8')}\"")
59
+ while nonce < 2**32: # maxNonce
60
+ data = self.prepare_data(nonce)
61
+ hash = sha256(data).digest()
62
+ st.write(f"\r{hash.hex()}", end="")
63
+
64
+ hash_int = int.from_bytes(hash, byteorder='big')
65
+ if hash_int < self.target:
66
+ break
67
+ else:
68
+ nonce += 1
69
+ st.write("\n\n")
70
+ return nonce, hash
71
+
72
+ def validate(self) -> bool:
73
+ """Validate the Proof of Work."""
74
+ data = self.prepare_data(self.block.nonce)
75
+ hash = sha256(data).digest()
76
+ hash_int = int.from_bytes(hash, byteorder='big')
77
+ return hash_int < self.target
78
+
79
+ def new_genesis_block():
80
+ return Block(index=0, previous_hash=b'0'*64, timestamp=time.time(), data='Genesis Block'.encode('utf-8'), nonce=0, hash=b'genesis_hash')
81
+
82
+ class Blockchain:
83
+ def __init__(self):
84
+ self.db = plyvel.DB('blockchain.db', create_if_missing=True)
85
+ self.tip = self._init_blockchain()
86
+
87
+ def _init_blockchain(self):
88
+ """Initialize the blockchain or load the existing one."""
89
+ blocks_bucket = self.db.prefixed_db(b'blocks')
90
+ if blocks_bucket.get(b'l') is None:
91
+ genesis = new_genesis_block()
92
+ blocks_bucket.put(genesis.hash, genesis.serialize())
93
+ blocks_bucket.put(b'l', genesis.hash)
94
+ return genesis.hash
95
+ else:
96
+ return blocks_bucket.get(b'l')
97
+
98
+ def add_block(self, data):
99
+ """Add a new block to the blockchain."""
100
+ blocks_bucket = self.db.prefixed_db(b'blocks')
101
+ last_hash = blocks_bucket.get(b'l')
102
+
103
+ new_block = Block(index=self.get_block_count(), previous_hash=last_hash, timestamp=time.time(), data=data.encode('utf-8'), nonce=0, hash=b'')
104
+ pow = ProofOfWork(new_block)
105
+ nonce, hash = pow.run()
106
+
107
+ new_block.hash = hash
108
+ new_block.nonce = nonce
109
+
110
+ blocks_bucket.put(new_block.hash, new_block.serialize())
111
+ blocks_bucket.put(b'l', new_block.hash)
112
+ self.tip = new_block.hash
113
+
114
+ def get_block_count(self):
115
+ """Get the number of blocks in the blockchain."""
116
+ blocks_bucket = self.db.prefixed_db(b'blocks')
117
+ return len(list(blocks_bucket))
118
+
119
+ def get_last_block_hash(self):
120
+ """Get the hash of the last block in the chain."""
121
+ blocks_bucket = self.db.prefixed_db(b'blocks')
122
+ return blocks_bucket.get(b'l')
123
+
124
+ def get_block(self, block_hash):
125
+ """Retrieve a block from the BoltDB by its hash."""
126
+ blocks_bucket = self.db.prefixed_db(b'blocks')
127
+ serialized_block = blocks_bucket.get(block_hash)
128
+ if serialized_block:
129
+ return Block.deserialize(serialized_block)
130
+ return None
131
+
132
+ def close(self):
133
+ """Close the database connection."""
134
+ self.db.close()
135
+
136
+ def print_blockchain(bc):
137
+ last_hash = bc.get_last_block_hash()
138
+ while last_hash:
139
+ block = bc.get_block(last_hash)
140
+ if block is None:
141
+ st.write("Block not found for hash:", last_hash.hex())
142
+ break
143
+ st.write(f"Prev. hash: {block.previous_hash.hex()}")
144
+ st.write(f"Data: {block.data.decode('utf-8')}")
145
+ st.write(f"Hash: {block.hash.hex()}")
146
+ st.write(f"Nonce: {block.nonce}")
147
+ pow = ProofOfWork(block)
148
+ st.write(f"PoW: {pow.validate()}")
149
+ st.write()
150
+ # Stop iterating when we reach the genesis block
151
+ if block.previous_hash == b'0'*64:
152
+ break
153
+ last_hash = block.previous_hash
154
+
155
+ # Initialize the blockchain
156
+ try:
157
+ bc = Blockchain()
158
+ except IOError as e:
159
+ st.write(f"Error initializing blockchain: {e}")
160
+ bc = None
161
+
162
+ if bc:
163
+ try:
164
+ # Add a new block with the Starbucks reusable Holiday cup data
165
+ starbucks_data = "Pay 0.0001 BTC for a Starbucks reusable Holiday cup coffee ($9.20 USD)"
166
+ bc.add_block(starbucks_data)
167
+
168
+ # Print the entire blockchain
169
+ print_blockchain(bc)
170
+
171
+ finally:
172
+ # Ensure the database is closed properly
173
+ bc.close()
174
+
175
+ # Streamlit App
176
+ st.title("Blockchain Prototype 4")
177
+ st.image('Starbucks BTC Model 1.jpeg', caption='Starbucks BTC Model 1')
178
+ st.image('Starbucks BTC Model 2.jpeg', caption='Starbucks BTC Model 2')