-
Notifications
You must be signed in to change notification settings - Fork 0
/
pychain.py
252 lines (186 loc) · 8.45 KB
/
pychain.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# PyChain Ledger
################################################################################
# You’ll make the following updates to the provided Python file for this
# Challenge, which already contains the basic `PyChain` ledger structure that
# you created throughout the module:
# Step 1: Create a Record Data Class
# * Create a new data class named `Record`. This class will serve as the
# blueprint for the financial transaction records that the blocks of the ledger
# will store.
# Step 2: Modify the Existing Block Data Class to Store Record Data
# * Change the existing `Block` data class by replacing the generic `data`
# attribute with a `record` attribute that’s of type `Record`.
# Step 3: Add Relevant User Inputs to the Streamlit Interface
# * Create additional user input areas in the Streamlit application. These
# input areas should collect the relevant information for each financial record
# that you’ll store in the `PyChain` ledger.
# Step 4: Test the PyChain Ledger by Storing Records
# * Test your complete `PyChain` ledger.
################################################################################
# Imports
import streamlit as st
from dataclasses import dataclass
from typing import Any, List
import datetime as datetime
import pandas as pd
import hashlib
################################################################################
# Step 1:
# Create a Record Data Class
# Define a new Python data class named `Record`. Give this new class a
# formalized data structure that consists of the `sender`, `receiver`, and
# `amount` attributes. To do so, complete the following steps:
# 1. Define a new class named `Record`.
# 2. Add the `@dataclass` decorator immediately before the `Record` class
# definition.
# 3. Add an attribute named `sender` of type `str`.
# 4. Add an attribute named `receiver` of type `str`.
# 5. Add an attribute named `amount` of type `float`.
# Note that you’ll use this new `Record` class as the data type of your `record` attribute in the next section.
# @TODO
# Create a Record Data Class that consists of the `sender`, `receiver`, and
# `amount` attributes
@dataclass
class Record:
sender: str
receiver: str
amount: float
################################################################################
# Step 2:
# Modify the Existing Block Data Class to Store Record Data
# Rename the `data` attribute in your `Block` class to `record`, and then set
# it to use an instance of the new `Record` class that you created in the
# previous section. To do so, complete the following steps:
# 1. In the `Block` class, rename the `data` attribute to `record`.
# 2. Set the data type of the `record` attribute to `Record`.
@dataclass
class Block:
# @TODO
# Rename the `data` attribute to `record`, and set the data type to `Record`
record: Record
creator_id: int
prev_hash: str = "0"
timestamp: str = datetime.datetime.utcnow().strftime("%H:%M:%S")
nonce: int = 0
def hash_block(self):
sha = hashlib.sha256()
record = str(self.record).encode()
sha.update(record)
creator_id = str(self.creator_id).encode()
sha.update(creator_id)
timestamp = str(self.timestamp).encode()
sha.update(timestamp)
prev_hash = str(self.prev_hash).encode()
sha.update(prev_hash)
nonce = str(self.nonce).encode()
sha.update(nonce)
return sha.hexdigest()
@dataclass
class PyChain:
chain: List[Block]
difficulty: int = 4
def proof_of_work(self, block):
calculated_hash = block.hash_block()
num_of_zeros = "0" * self.difficulty
while not calculated_hash.startswith(num_of_zeros):
block.nonce += 1
calculated_hash = block.hash_block()
print("Wining Hash", calculated_hash)
return block
def add_block(self, candidate_block):
block = self.proof_of_work(candidate_block)
self.chain += [block]
def is_valid(self):
block_hash = self.chain[0].hash_block()
for block in self.chain[1:]:
if block_hash != block.prev_hash:
print("Blockchain is invalid!")
return False
block_hash = block.hash_block()
print("Blockchain is Valid")
return True
################################################################################
# Streamlit Code
# Adds the cache decorator for Streamlit
@st.cache(allow_output_mutation=True)
def setup():
print("Initializing Chain")
return PyChain([Block("Genesis", 0)])
st.markdown("# PyChain")
st.markdown("## Store a Transaction Record in the PyChain")
pychain = setup()
################################################################################
# Step 3:
# Add Relevant User Inputs to the Streamlit Interface
# Code additional input areas for the user interface of your Streamlit
# application. Create these input areas to capture the sender, receiver, and
# amount for each transaction that you’ll store in the `Block` record.
# To do so, complete the following steps:
# 1. Delete the `input_data` variable from the Streamlit interface.
# 2. Add an input area where you can get a value for `sender` from the user.
# 3. Add an input area where you can get a value for `receiver` from the user.
# 4. Add an input area where you can get a value for `amount` from the user.
# 5. As part of the Add Block button functionality, update `new_block` so that `Block` consists of an attribute named `record`, which is set equal to a `Record` that contains the `sender`, `receiver`, and `amount` values. The updated `Block`should also include the attributes for `creator_id` and `prev_hash`.
# @TODO:
# Delete the `input_data` variable from the Streamlit interface.
# input_data = st.text_input("Block Data")
# @TODO:
# Add an input area where you can get a value for `sender` from the user.
input_sender = st.text_input("Sender")
# @TODO:
# Add an input area where you can get a value for `receiver` from the user.
input_receiver = st.text_input("Receiver")
# @TODO:
# Add an input area where you can get a value for `amount` from the user.
input_amount = st.text_input("Amount")
if st.button("Add Block"):
prev_block = pychain.chain[-1]
prev_block_hash = prev_block.hash_block()
# @TODO
# Update `new_block` so that `Block` consists of an attribute named `record`
# which is set equal to a `Record` that contains the `sender`, `receiver`,
# and `amount` values
new_block = Block(
record= Record(sender=input_sender,
receiver=input_receiver,
amount=input_amount),
creator_id=42,
prev_hash=prev_block_hash
)
pychain.add_block(new_block)
st.balloons()
################################################################################
# Streamlit Code (continues)
st.markdown("## The PyChain Ledger")
pychain_df = pd.DataFrame(pychain.chain).astype(str)
st.write(pychain_df)
difficulty = st.sidebar.slider("Block Difficulty", 1, 5, 2)
pychain.difficulty = difficulty
st.sidebar.write("# Block Inspector")
selected_block = st.sidebar.selectbox(
"Which block would you like to see?", pychain.chain
)
st.sidebar.write(selected_block)
if st.button("Validate Chain"):
st.write(pychain.is_valid())
################################################################################
# Step 4:
# Test the PyChain Ledger by Storing Records
# Test your complete `PyChain` ledger and user interface by running your
# Streamlit application and storing some mined blocks in your `PyChain` ledger.
# Then test the blockchain validation process by using your `PyChain` ledger.
# To do so, complete the following steps:
# 1. In the terminal, navigate to the project folder where you've coded the
# Challenge.
# 2. In the terminal, run the Streamlit application by
# using `streamlit run pychain.py`.
# 3. Enter values for the sender, receiver, and amount, and then click the "Add
# Block" button. Do this several times to store several blocks in the ledger.
# 4. Verify the block contents and hashes in the Streamlit drop-down menu.
# Take a screenshot of the Streamlit application page, which should detail a
# blockchain that consists of multiple blocks. Include the screenshot in the
# `README.md` file for your Challenge repository.
# 5. Test the blockchain validation process by using the web interface.
# Take a screenshot of the Streamlit application page, which should indicate
# the validity of the blockchain. Include the screenshot in the `README.md`
# file for your Challenge repository.