forked from ariel-waisburg/p2p-network-simulator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cpp
266 lines (240 loc) · 10.3 KB
/
main.cpp
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#include <iostream>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <random>
#include <fstream>
#include "models.cpp"
#include "functions.cpp"
#include "visualization.cpp"
#define P pair<int, int>
using namespace std;
// Global variable to track current time of simulation in milliseconds
int global_time = 0;
int last_txn_generation = 0;
// Logic for managing the simulation
void manager(vector<Node> &miners, int time_limit, int txn_interval, double lambda)
{
// Variables to store user input
int n_peers = miners.size();
int blkId = 1; // Unique Id for blocks created in increasing format
int txnId = 1; // Unique Id for transactions created in increasing format
double prop_delay = generatePropDelay(); // Generate a random propagation delay
char ch;
// Simulation loop
while (global_time < time_limit)
{
// Priority queue to track tasks of miners
priority_queue<P, vector<P>, greater<P > > pq;
// Loop through each peer and schedule tasks
for (int i = 0; i < n_peers; i++)
{
// Schedule block creation task if not pending
if (!miners[i].blk_crt_pending)
{
double delay = generateExponential(lambda / miners[i].hashPower);
cout << "Delay for blk_crt: " << delay << endl;
miners[i].tasks.push(prepareTaskForBlockCreate(delay * 1000));
miners[i].blk_crt_pending = true;
}
pq.push(make_pair(miners[i].tasks.top().trigger_time, i));
}
// Get the smallest time from priority queue
int smallest_time = pq.top().first;
while (!pq.empty())
{
auto m = pq.top();
pq.pop();
int current_task_time = m.first;
if (current_task_time > smallest_time)
break;
int idx = m.second;
Task task = miners[idx].tasks.top();
miners[idx].tasks.pop();
vector<int> neighbours = miners[idx].peer_nbh;
queue<Txn> txns = miners[idx].validatedTxns;
// Do whatever operation you have to do with task.
// Just make sure to invert blk_crt_pending to false if
// the current task was to create a block itself.
// Also if any events like sending a block to some other
// nodes happnes then introduce task with correct time in
// that other miners task list
// Handle different task types
switch (task.type)
{
case blk_rcv:
{
if (task.blockchain.size() > miners[idx].blockchain.size())
{
int j = 0;
for (int i = 0; i < miners[idx].blockchain.size(); i++)
{
if (miners[idx].blockchain[i].blk_id == task.blockchain[i].blk_id)
{
j++;
}
else
{
break;
}
}
bool allCorrect = true;
// Perform proof-of-work validation
for (int i = j; i < task.blockchain.size(); i++)
{
// Check if the receieved blockchain has valid transactions after fork
if (!verifyTransactions(task.blockchain[i]))
{
allCorrect = false;
break;
}
}
if (allCorrect) // Delete current miner's block creation task
{
miners[idx].blockchain.clear();
miners[idx].blockchain = task.blockchain;
if (miners[idx].blk_crt_pending)
{
vector<Task> newTasks;
while (!miners[idx].tasks.empty())
{
Task curTask = miners[idx].tasks.top();
miners[idx].tasks.pop();
if (curTask.type != blk_crt)
newTasks.push_back(curTask);
}
for (auto it : newTasks)
{
miners[idx].tasks.push(it);
}
miners[idx].blk_crt_pending = false;
}
// Broadcast block to neighbours
for (int i = 0; i < neighbours.size(); i++)
{
double delay = latency(miners[idx], miners[neighbours[i]], 'b', prop_delay);
cout << "Delay for blk_rcv: " << delay << endl;
Task rcvTask = prepareTaskForBlockRecieve(delay * 1000, miners[idx].blockchain);
miners[neighbours[i]].tasks.push(rcvTask);
}
}
}
}
break;
case blk_crt:
{
// Prepare new block and coinbase transaction
Block newBlock = prepareNewBlock(blkId++, global_time + smallest_time);
Txn reward = createCoinbaseTransaction(miners[idx].peer_id, txnId++);
newBlock.txn_tree.push_back(reward);
// Select valid transactions for the block
vector<Txn> validTxns = provideValidTransactions(miners[idx]);
for (auto txn : validTxns)
{
newBlock.txn_tree.push_back(txn);
}
miners[idx].blockchain.push_back(newBlock);
cout << "pow_done successful" << endl;
// Add new block to miner's blockchain and propagate to neighbors
for (int i = 0; i < neighbours.size(); i++)
{
double delay = latency(miners[idx], miners[neighbours[i]], 'b', prop_delay);
cout << "Delay for blk_rcv: " << delay << endl;
Task rcvTask = prepareTaskForBlockRecieve(delay * 1000, miners[idx].blockchain);
miners[neighbours[i]].tasks.push(rcvTask);
}
miners[idx].amnt += 50; // Incremented by 50 coins for coinbase transaction
miners[idx].blk_crt_pending = false;
}
break;
case txn_crt:
{
// Create transaction and validate sender balance
Txn txn = createTransaction(miners[idx], txnId++, n_peers);
if (txn.sender_bal >= txn.amount)
{
miners[idx].knownTxns.insert(txn.txn_id);
miners[idx].validatedTxns.push(task.txn);
// Send transaction to all peers
for (int i = 0; i < neighbours.size(); i++)
{
double delay = latency(miners[idx], miners[neighbours[i]], 't', prop_delay);
cout << "Delay for txn_rcv: " << delay << endl;
Task rcvTask = prepareTaskForTxnRcv(delay * 1000, txn);
miners[neighbours[i]].tasks.push(rcvTask);
}
}
}
break;
case txn_rcv:
{
// Validate sender balance
if (task.txn.sender_bal >= task.txn.amount)
{
miners[idx].validatedTxns.push(task.txn);
if (task.txn.receiver_id != miners[idx].peer_id)
{
// Send transaction to all peers
for (int i = 0; i < neighbours.size(); i++)
{
auto knownTxns = miners[neighbours[i]].knownTxns;
if (knownTxns.find(task.txn.txn_id) == knownTxns.end())
{
double delay = latency(miners[idx], miners[neighbours[i]], 't', prop_delay);
cout << "Delay for txn_rcv: " << delay << endl;
Task rcvTask = prepareTaskForTxnRcv(delay * 1000, task.txn);
miners[neighbours[i]].tasks.push(rcvTask);
}
}
}
else
{
miners[idx].amnt += task.txn.amount;
}
}
miners[idx].knownTxns.insert(task.txn.txn_id);
}
break;
}
}
global_time += smallest_time;
cout << "Global time: " << global_time << " Smallest time: " << smallest_time << endl;
if (global_time - last_txn_generation >= txn_interval) // Producing transactions
{
last_txn_generation = global_time;
map<int, Task> txnCrtTasks = prepareTasksForTxnCrt(n_peers); // Create random transaction generate events
for (auto it : txnCrtTasks)
{
int idx = it.first;
Task task = it.second;
miners[idx].tasks.push(task);
}
}
}
}
int main()
{
// Variables to store user input
int n_peers;
double time_limit;
double lambda;
double txn_arrival;
// User input for simulation parameters
cout << "\nEnter the number of peers in the network: ";
cin >> n_peers;
cout << "\nMean time of block interarrival in seconds: ";
cin >> lambda;
cout << "\nMean number of transaction per second: ";
cin >> txn_arrival;
cout << "\nDuration for the simulation to run in seconds: ";
cin >> time_limit;
// Initialize miners and simulation parameters
vector<Node> miners = initialization(n_peers, global_time);
// Run the simulation
manager(miners, time_limit * 1000, ((double)1 / txn_arrival) * 1000, lambda);
cout << "\nSimulation ended at time " << global_time << " milliseconds";
cout << "\nPrinting network topology\n";
printGraph(miners);
return dumpSimulationResults(miners);
}