├── .gitignore
├── .idea
├── misc.xml
├── modules.xml
├── p2p_ns_basic.iml
└── workspace.xml
├── .vscode
└── settings.json
├── README.md
├── animate.py
├── block.py
├── manager.py
├── peer.py
├── requirements
├── run.py
└── transaction.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.txt
3 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/p2p_ns_basic.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
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 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 1517300491948
87 |
88 |
89 | 1517300491948
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 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Simulation of a P2P Cryptocurrency Network
2 |
3 | Features
4 | ======================
5 | For the assignment we have used a python package 'SimPy' to do discrete event simulation in time.
6 |
7 | We demonstrate a simulation of
8 | - random peer connections in the network
9 | - random transaction generation by each peer with a value chosen from poisson distribution
10 | - broadcasting the messages (transactions/blocks) in a loopless manner
11 | - simulating network latencies based on propogation delay, message size, link speeds of nodes, and queing delay
12 | - random block creating with arrival times chosen from a poisson distribution
13 | - propogation of blocks on the block chain
14 | - addition of blocks to a local blockchain of a node and resolution of forks based on block arrival time, the chain with first arrived one is extended.
15 |
16 | Project Structure
17 | ======================
18 | Classes Defined:
19 |
20 | 1. Peer - to define a peer, create a p2p network
21 | 2. Manager - to simulate each peer in time, a peer manager
22 | 3. Transaction
23 | 4. Block
24 | 5. Blockchain
25 | 6. Connection - to create a connection bw 2 peers
26 |
27 | Usage
28 | ======================
29 | Input Parameter:
30 |
31 | 1. n = No. of peers
32 | 2. z = percent of slow nodes
33 | 3. txn_interval_mean = mean for dist. to choose arrival time from, in ms
34 | 4. mean_Tk = mean value for dist. for choosing Tk for each node,in ms
35 | 5. mean_links = mean for binomial dist., higher the value, denser is the network
36 | 6. SIM_DURATION = duration of simulation in ms
37 |
38 | To start the simulation-
39 |
40 | 1. In the project folder, '$python run.py' [with proper arguments].
41 | 2. To see the network graph, set VISUALIZATION = True in 'run.py', this can be done to check if the graph is connected/ dissconnected.
42 | 3. At the end of the simulation, for each peer, file having tree at the node is created in the project folder.
43 |
--------------------------------------------------------------------------------
/animate.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import networkx as nx
3 | import matplotlib
4 | import matplotlib.pyplot as plt
5 | from matplotlib.animation import FuncAnimation
6 | import pygraphviz
7 | from networkx.drawing.nx_agraph import graphviz_layout
8 |
9 | def avg_bandwidth(peers):
10 | bws = []
11 | for peer in peers:
12 | for c in peer.connections.values():
13 | bws.append(c.bandwidth)
14 | return sum(bws)/len(bws)
15 |
16 | def median_bandwidth(peers):
17 | bws = []
18 | for peer in peers:
19 | for c in peer.connections.values():
20 | bws.append(c.bandwidth)
21 | bws.sort()
22 | return bws[len(bws)/2]
23 |
24 | def max_peers(peers):
25 | return max(len(p.connections) for p in peers)
26 |
27 | def min_peers(peers):
28 | return min(len(p.connections) for p in peers)
29 |
30 |
31 | class Visualizer(object):
32 |
33 | def __init__(self, env, peers):
34 | self.env = env
35 | self.peers = peers
36 | fig = plt.figure(figsize=(8, 8))
37 | # interval: draws a new frame every *interval* milliseconds
38 | anim = FuncAnimation(fig, self.update, interval=50, blit=False)
39 | plt.show()
40 |
41 | def update_simulation(self):
42 | self.env.run(self.env.now + 1)
43 |
44 | def update(self, n):
45 | # print 'update simulation'
46 | self.update_simulation()
47 | # print 'update visualization'
48 | # create graph
49 | G = nx.Graph()
50 | for peer in self.peers:
51 | G.add_node(peer, label=peer.name)
52 | for peer in self.peers:
53 | for other, cnx in peer.connections.items():
54 | G.add_edge(peer, other, weight=cnx.bandwidth)
55 | #pos = nx.graphviz_layout(G)
56 | pos = graphviz_layout(G)
57 |
58 | #pos = nx.spring_layout(G)
59 | plt.cla()
60 |
61 | edges = nx.draw_networkx_edges(G, pos)
62 | nodes = nx.draw_networkx_nodes(G, pos, node_size=20)
63 |
64 | #nx.draw_networkx(G)
65 |
66 | #labels = dict((p, p.name) for p in self.peers)
67 | #nx.draw_networkx_nodes(G, pos, labels=labels, font_color='k')
68 |
69 | plt.axis('off')
70 |
71 | KBit = 1024 / 8
72 |
73 | plt.text(0.5, 1.1, "time: %.2f" % self.env.now,
74 | horizontalalignment='left',
75 | transform=plt.gca().transAxes)
76 | plt.text(0.5, 1.07, "avg bandwidth = %d KBit" % (avg_bandwidth(self.peers)/KBit),
77 | horizontalalignment='left',
78 | transform=plt.gca().transAxes)
79 | plt.text(0.5, 1.04, "median bandwidth = %d KBit" % (median_bandwidth(self.peers)/KBit),
80 | horizontalalignment='left',
81 | transform=plt.gca().transAxes)
82 | plt.text(0.5, 1.01, "min/max connections %d/%d" % (min_peers(self.peers), max_peers(self.peers)),
83 | horizontalalignment='left',
84 | transform=plt.gca().transAxes)
85 |
86 |
87 |
88 | #nx.draw_networkx_labels(G, pos, labels)
89 | return nodes,
90 |
--------------------------------------------------------------------------------
/block.py:
--------------------------------------------------------------------------------
1 | import time
2 | import hashlib
3 |
4 | class Block:
5 | size = 10 # limit on number of transactions
6 | def __init__(self, listofTransactions, miner):
7 | self.timestamp = time.time()
8 | self.blkid = hashlib.md5(str(self.timestamp)).hexdigest()
9 | self.transactions = listofTransactions
10 | self.miner = miner
11 | self.parentlink = None
12 |
13 | def __repr__(self):
14 | return str(self.blkid)
15 |
16 | class BlockChain:
17 | listofBlocks = []
18 |
19 | def __init__(self, newBlock):
20 | self.newBlock = newBlock
21 | self.listofBlocks.append(self.newBlock)
22 |
23 | def addBlock(self,newBlock):
24 | for b in self.listofBlocks:
25 | if newBlock.blkid == b.blkid:
26 | print "adding a block already present"
27 | return
28 |
29 | lastblkid = self.listofBlocks[len(self.listofBlocks)-1].blkid
30 | newBlock.parentlink = lastblkid
31 | self.listofBlocks.append(newBlock)
32 | return
33 |
34 | def getLast(self):
35 | return self.listofBlocks[len(self.listofBlocks)-1]
36 |
37 | def removeLast(self):
38 | self.listofBlocks = self.listofBlocks[:len(self.listofBlocks)-1]
39 | return
40 |
41 | def displayChain(self):
42 | l=0
43 | while l < len(self.listofBlocks):
44 | print "parent: " + str(self.listofBlocks[l].parentlink) + " <------ block :"+ str(self.listofBlocks[l].blkid)
45 | l+=1
46 | return
--------------------------------------------------------------------------------
/manager.py:
--------------------------------------------------------------------------------
1 | import peer
2 | import random
3 | import time
4 | import numpy as np
5 | import operator
6 |
7 | class Manager:
8 |
9 | def __init__(self, peer):
10 | self.peer = peer
11 | self.env.process(self.run())
12 |
13 | def __repr__(self):
14 | return "ConnectionManager(%s)" % self.peer.name
15 |
16 | @property
17 | def env(self):
18 | return self.peer.env
19 |
20 | @property
21 | def connected_peers(self):
22 | return self.peer.connections.keys()
23 |
24 | def simulate(self):
25 | #check the time constraint before generating the txn
26 |
27 | if (self.peer.sim_time - self.peer.lasttransactiontime ) >= float(np.random.poisson(self.peer.txn_interval_mean,1)[0])/1000:
28 | self.peer.generateTransaction()
29 |
30 | #call the create block function with some distribution to keep a check on rate of block arrival
31 | if (self.peer.sim_time - self.peer.lastBlockArrTime) >= float(np.random.poisson(self.peer.Tk_mean,1)[0])/1000: #hasnt heard a block, so create one
32 | self.peer.createBlock() #if it hasn't heard a block in tk+Tk time
33 |
34 | #check if all the nodes have the block last propogated
35 | return
36 |
37 | def getConsensus(self):
38 | blocks_on_nw = {}
39 | listofblocks_nw = []
40 | majority = 1 + len(self.peer.all_peers)/2
41 |
42 | for p in self.peer.all_peers:
43 | #case when the block has not yet reached a peer
44 | #in that case we wait to get consensus or do it by majority
45 | x = p.blk_queue #sort the blk_queue for all on arrival time
46 | sorted_x = sorted(x.items(), key=operator.itemgetter(1))
47 | latestblock = sorted_x[0][0]
48 | listofblocks_nw.append(next((x for x in p.listofBlocks if x.blkid == latestblock),[]))
49 |
50 | if latestblock in blocks_on_nw.keys():
51 | l = [p]
52 | l.extend(blocks_on_nw[latestblock]) #no of peers having this block
53 | blocks_on_nw[latestblock] = l #append peer to listofpeers having that block
54 |
55 | else:
56 | blocks_on_nw[latestblock] = [p]
57 |
58 | if len(blocks_on_nw.keys()) == 1: #no fork
59 | #check if majority satisfied ,update timestamp of block after adding
60 | if len(blocks_on_nw[latestblock]) >= majority:
61 | #add to global chain, local chain of peers, update balance, UTXO, pop blk_q
62 | self.peer.globalChain.addBlock(next((x for x in listofblocks_nw if x.blkid == latestblock), None))
63 | else:
64 | self.getConsensus() #try again
65 | else:#resolve fork
66 | pass
67 |
68 | return
69 |
70 | def run(self):
71 | while True:
72 | self.peer.sim_time = time.time()
73 | self.simulate()
74 | #self.getConsensus()
75 | yield self.env.timeout(1)
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/peer.py:
--------------------------------------------------------------------------------
1 | import simpy
2 | from transaction import Transaction
3 | import random
4 | import numpy as np
5 | from block import Block
6 | from block import BlockChain
7 | import time
8 |
9 | class Connection(object):
10 | def __init__(self, env, sender, receiver):
11 | self.env = env
12 | self.sender = sender
13 | self.receiver = receiver
14 |
15 | def __repr__(self):
16 | return ' %r>' % (self.sender, self.receiver)
17 |
18 | class Peer(object):
19 |
20 | UTXO = [] #unspent txn pool
21 | sim_time = time.time() #this is the global time in seconds
22 | pij = np.random.uniform(10,500) #fixed prop delay, choosen from uniform dist.
23 | dij = 96*1000 #bits
24 | genesisBlock = Block([],None) #initialize a genesis block for the nw
25 | genesisBlock.blkid = '00000000000000000000000000000000'
26 | globalChain = BlockChain(genesisBlock) #initialize a blockchain with genesis block
27 | all_peers = [] #list of all the peers in nw
28 | txn_interval_mean = 10 #avg. time bw two txns in ms
29 | mean_Tk = 3000 #from my observation of 3 sec avg. prop delay on nw
30 | AVG_BLK_ARR_TIME = len(all_peers)*mean_Tk #no. of peers*max_delay
31 |
32 | def __init__(self, name, peer_type, env):
33 | self.name = name
34 | self.type = peer_type
35 | self.unspentTransactions = []
36 | self.balance = 100
37 | self.lasttransactiontime = self.sim_time
38 | self.listofBlocks = [self.genesisBlock]
39 | self.lastBlockHeard = self.genesisBlock #default time of genesis block
40 | self.lastBlockArrTime = self.sim_time
41 | self.localChain = self.globalChain
42 | self.Tk_mean = float(np.random.poisson(self.AVG_BLK_ARR_TIME,1)[0]) #average arrival time of a block proportion to cpu power
43 | self.env = env
44 | self.connections = dict()
45 | self.txn_queue = {}
46 | self.blk_queue = {'00000000000000000000000000000000':self.sim_time}
47 |
48 | def __repr__(self):
49 | return '<%s %s>' % (self.__class__.__name__, self.name)
50 |
51 | def connect(self, other):
52 | if not self.is_connected(other):
53 | print "%r connecting to %r" % (self, other)
54 | self.connections[other] = Connection(self.env, self, other)
55 | if not other.is_connected(self):
56 | other.connect(self)
57 |
58 | def is_connected(self, other):
59 | return other in self.connections
60 |
61 | def computeDelay(self,other,msg):
62 | size = 0
63 | if isinstance(msg,Block):
64 | size = 8*pow(10,6) #bits
65 |
66 | delay = self.pij
67 | cij = 5*pow(10,3) #link speed bits per ms
68 |
69 | if self.type == other.type == 'fast':
70 | cij = 100*pow(10,3)
71 |
72 | prop = float(size)/cij
73 | queing = np.random.exponential((float(self.dij)/cij),1)[0]
74 |
75 | delay += prop + queing #in ms
76 | return float(delay)/1000
77 |
78 | def broadcast(self, msg, delay): #broadcast msg to all other peer
79 | #ensure there are no loops by checking that the msg has not already been received
80 |
81 | #broadcast rules if a msg is a txn
82 | if isinstance(msg,Transaction):
83 | for other in self.connections:
84 | if not(other == self):
85 | if msg.txid not in other.txn_queue.keys():
86 | other.unspentTransactions.append(msg)
87 | arrival_time = delay + self.computeDelay(other,msg)
88 | #other.lasttransactiontime = arrival_time
89 | other.txn_queue[msg.txid] = arrival_time
90 | other.broadcast(msg, arrival_time)
91 |
92 | #broadcast a block
93 | else:
94 | for other in self.connections:
95 | if not(other == self):
96 | if msg.blkid not in other.blk_queue.keys():
97 | #adding any block heard by a peer
98 | print "sending block.. " + str(msg.blkid) + " to " + str(other) + str(other.blk_queue.keys())
99 | arrival_time = delay + self.computeDelay(other,msg)
100 | other.blk_queue[msg.blkid] = arrival_time
101 |
102 | #detect fork here by checking the arrival time and lastblock time
103 | flag = other.detectFork(msg, arrival_time) #boolean
104 | if not flag:
105 | other.updateChain(msg,arrival_time)
106 | other.broadcast(msg, arrival_time)
107 |
108 | if other.name == 'p1':
109 | print "Block heard by p1"
110 | print other.blk_queue
111 | print "----------Local Chain for p1--------"
112 | #print other.unspentTransactions
113 | other.localChain.displayChain()
114 | return
115 |
116 | def detectFork(self, msg, arrival_time):
117 | print "Two new blocks received...detecting fork.."
118 | #same parentlink with different arrival times, different block, resolve fork with latest
119 |
120 | if (msg.parentlink == self.localChain.getLast().parentlink) and (msg.blkid != self.localChain.getLast().blkid):
121 | print "Fork detected....at peer: " + str(self.name)
122 |
123 | if arrival_time > self.lastBlockArrTime:
124 | self.listofBlocks.append(msg) #select the chain with first arrived block
125 | self.lastBlockHeard = msg
126 | self.lastBlockArrTime = arrival_time
127 | self.broadcast(msg, arrival_time)
128 | return True #just add the new block to the list and broadcast
129 | else:
130 | self.resolveFork(msg, arrival_time) #extend with msg block
131 | self.listofBlocks.append(msg) #select the chain with first arrived block
132 | self.broadcast(msg, arrival_time)
133 |
134 | return False
135 |
136 | def resolveFork(self, msg, arrival_time):
137 | #here we remove the last block from the localchain and add msg
138 | self.localChain.removeLast()
139 | self.localChain.addBlock(msg)
140 | return
141 |
142 | def updateChain(self,newBlock, arrival_time):
143 | if newBlock == self.localChain.getLast():
144 | return
145 | newBlock.parentlink = self.localChain.getLast().blkid
146 | self.listofBlocks.append(newBlock)
147 | self.localChain.addBlock(newBlock)
148 | self.lastBlockArrTime = arrival_time
149 | return
150 |
151 | def generateTransaction(self):
152 | receiver = self
153 | for other in self.connections:
154 | #select a random peer to whom to pay the coins
155 | if random.randint(0,1) and other.name !='PeerServer':
156 | receiver = other
157 | break
158 |
159 | sender=self.name
160 |
161 | if self.balance < 1:
162 | print "insufficient balance"
163 | return
164 |
165 | coins = random.randint(1,self.balance)
166 | #update balance
167 |
168 | tx = Transaction(self.name, receiver, coins)
169 | self.lasttransactiontime = time.time()
170 | #add the new transaction to the unspent pool
171 | self.UTXO.append(tx)
172 | self.unspentTransactions.append(tx)
173 |
174 | self.broadcast(tx, tx.timestamp)
175 | return
176 |
177 | def updateUTXO(self):
178 | # we need to make all the transactions in the chain marked spend in UTXO, local txn list
179 | # update the balance for all the nodes
180 | return
181 |
182 | def createBlock(self):
183 | #check to see if it has number of unspent transactions >= required block size
184 | #select the transactions sorted on timestamp
185 | self.updateUTXO()
186 |
187 | if len(self.unspentTransactions) == 0:
188 | #check in the UTXO
189 | self.unspentTransactions.extend(self.UTXO)
190 | if len(self.unspentTransactions) == 0:
191 | #print 'There are no unspent transactions'
192 | return
193 | #add the transactions to the block, max 10
194 |
195 | lisofTransactions = self.unspentTransactions[:10]
196 | self.unspentTransactions = self.unspentTransactions[10:]
197 |
198 | newBlock = Block(lisofTransactions,self)
199 | newBlock.parentlink = self.lastBlockHeard.blkid
200 | for i in self.listofBlocks:
201 | if newBlock.blkid == i.blkid:
202 | self.unspentTransactions.extend(newBlock.transactions)
203 | #print "mining a block already present"
204 | return
205 |
206 | print str(self) + " is mining...."
207 | self.listofBlocks.append(newBlock)
208 | self.blk_queue[newBlock.blkid] = newBlock.timestamp
209 | self.localChain.addBlock(newBlock)
210 | self.lastBlockArrTime = newBlock.timestamp
211 | print "block successfully created by " + str(self) + " "+ str(newBlock)
212 | print "linked to " + str(newBlock.parentlink)
213 | self.broadcast(newBlock, newBlock.timestamp)
214 |
215 | return
216 | #mark the txns in the UTXO spent
217 | #have to update the balance, the mining fees once the block gets added to the chain
--------------------------------------------------------------------------------
/requirements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | import random
2 | import simpy
3 | import sys
4 | from peer import Peer
5 | from manager import Manager
6 | import numpy as np
7 |
8 | n = int(sys.argv[1]) # No. of peers
9 | z = int(sys.argv[2]) #percent of slow nodes
10 | txn_interval_mean = int(sys.argv[3]) #in ms
11 | mean_Tk = int(sys.argv[4]) #in ms
12 | mean_links = int(sys.argv[5]) #mean for binomial dist., higher the value, denser is the network
13 | SIM_DURATION = int(sys.argv[6]) #in ms
14 | VISUALIZATION = False
15 |
16 | Peer.mean_Tk = mean_Tk #default (3000*n) ms
17 | Peer.txn_interval_mean = txn_interval_mean #default 10 ms
18 |
19 | def initializePeer(peer_id, peer_type, env):
20 | return Peer(peer_id, peer_type, env)
21 |
22 | def createPeers(peer_server, numOfPeers):
23 | peers = []
24 | for i in range(numOfPeers):
25 | if i <= int(numOfPeers * (float(z) / 100)):
26 | p = initializePeer('p%d' % i, 'slow', env)
27 | else:
28 | p = initializePeer('p%d' % i, 'fast', env)
29 | #p.connect(peer_server)
30 | peers.append(p)
31 | return peers
32 |
33 | env = simpy.Environment()
34 |
35 | #make a peer server, a boot node which is slow in nature
36 | pserver = initializePeer('PeerServer', 'slow', env)
37 |
38 | #dist to select number of connections for a peer
39 | peers = createPeers(pserver, n)
40 | Peer.all_peers=peers
41 |
42 | print("Starting Simulator")
43 | print "Peers Connecting...."
44 |
45 | for p in peers:
46 | links = 1 + np.random.binomial(n,float(mean_links)/100,1)
47 | while len(p.connections.keys()) < links:
48 | for other in peers:
49 | prob = random.randint(0,1)
50 | if prob and (p != other):
51 | p.connect(other)
52 |
53 | m = Manager(p)
54 | m.simulate()
55 |
56 | if VISUALIZATION:
57 | from animate import Visualizer
58 | Visualizer(env, peers)
59 | else:
60 | env.run(until=SIM_DURATION)
61 |
62 | print "simulation has ended..."
63 |
64 | #output each node's tree to a file
65 | for p in Peer.all_peers:
66 | print p, p.type
67 | filename = p.name + ".txt"
68 | f = open(filename,'w')
69 | f.write('[ "None", ')
70 | for b in p.blk_queue.keys():
71 | f.write("'")
72 | f.write(str(b))
73 | #f.write(str(" ArrTime : ")str(p.blk_queue[b]))
74 | f.write("'")
75 | f.write(',')
76 | f.write(']')
77 | f.write('\n')
78 | f.write('[')
79 | f.write('\n')
80 | count =0
81 | for b in p.listofBlocks:
82 | f.write('{ from:')
83 | if b.parentlink ==None:
84 | f.write("'None'")
85 | else:
86 | arr_time_parent = str(p.blk_queue[b.parentlink])
87 | f.write("'")
88 | f.write(str(b.parentlink))# +" "+ arr_time_parent)
89 | f.write("'")
90 | f.write(', to:')
91 | if b.blkid =='00000000000000000000000000000000':
92 | f.write("'00000000000000000000000000000000'")
93 | else:
94 | arr_time_block = str(p.blk_queue[b.blkid])
95 | f.write("'")
96 | f.write(str(b.blkid))# +" "+ arr_time_block)
97 | f.write("'")
98 | edge = 'e'+str(count)
99 | f.write(", name: '")
100 | f.write(edge)
101 | f.write("'},")
102 | f.write('\n')
103 | f.write(']')
104 | f.close()
--------------------------------------------------------------------------------
/transaction.py:
--------------------------------------------------------------------------------
1 | import time
2 | import random
3 | import hashlib
4 |
5 | class Transaction:
6 |
7 | def __init__(self, sender,receiver, coins):
8 | self.timestamp = time.time()
9 | self.txid = hashlib.md5(str(random.randint(1,100))+str(self.timestamp)).hexdigest()
10 | self.sender = sender
11 | self.receiver = receiver
12 | self.coins = coins
13 | self.status = False #transaction not included in the block
14 |
15 | def __repr__(self):
16 | #TxnID: ID x pays ID y C coins
17 | return str(self.txid) + ": " + str(self.sender) + " pays " + str(self.receiver.name) + " " + str(self.coins) +" coins"
18 |
19 |
--------------------------------------------------------------------------------