TroglodyteDerivations
commited on
Commit
•
e98a66a
1
Parent(s):
cfb439e
Create app.py
Browse files
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')
|