-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransaction.py
More file actions
executable file
·107 lines (88 loc) · 3.73 KB
/
transaction.py
File metadata and controls
executable file
·107 lines (88 loc) · 3.73 KB
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
from string_util import string_util
from Crypto.PublicKey import ECC
class TransactionOutput:
def __init__(self, recipient: ECC.EccKey, value: float, parent_transaction_id: str) -> None:
self.recipient = recipient
self.value = value
self.parent_transaction_id = parent_transaction_id
self.id = self.calculate_id()
def calculate_id(self):
return string_util.apply_sha_256(
string_util.get_string_from_key(self.recipient) +
str(self.value) +
self.parent_transaction_id
)
def is_mine(self, public_key: ECC.EccKey) -> bool:
return public_key == self.recipient
class TransactionInput:
def __init__(self, transaction_output_id: str) -> None:
self.transaction_output_id = transaction_output_id
self.UTXO = None
class Transaction:
transaction_number = 0
def __init__(self, sender: ECC.EccKey, recipient: ECC.EccKey, value: float, inputs: list) -> None:
self.sender = sender
self.recipient = recipient
self.value = value
self.inputs = inputs
self.outputs = []
self.transaction_id = self.calculate_hash()
self.signature = None
def calculate_hash(self):
Transaction.transaction_number += 1
data = (
string_util.get_string_from_key(self.sender) +
string_util.get_string_from_key(self.recipient) +
str(self.value) +
str(Transaction.transaction_number)
)
return string_util.apply_sha_256(data)
def generate_signature(self, private_key: ECC.EccKey) -> None:
data = (
string_util.get_string_from_key(self.sender) +
string_util.get_string_from_key(self.recipient) +
str(self.value)
)
self.signature = string_util.apply_ECDSA_sig(private_key, data)
def verify_signature(self) -> bool:
data = (
string_util.get_string_from_key(self.sender) +
string_util.get_string_from_key(self.recipient) +
str(self.value)
)
return string_util.verify_ECDSA_sig(self.sender, data, self.signature)
def get_inputs_value(self) -> float:
total = 0
for input in self.inputs:
if input.UTXO:
total += input.UTXO.value
return total
def get_outputs_value(self) -> float:
return sum(output.value for output in self.outputs)
def generate_outputs(self) -> None:
left_over = self.get_inputs_value() - self.value
self.outputs.append(TransactionOutput(self.recipient, self.value, self.transaction_id))
if left_over > 0:
self.outputs.append(TransactionOutput(self.sender, left_over, self.transaction_id))
def process_transaction(self, blockchain) -> bool:
if not self.verify_signature():
print("#Transaction Signature failed to verify")
return False
# gather transaction inputs and verify they are unspent
for input in self.inputs:
input.UTXO = blockchain.get_UTXO(input.transaction_output_id)
if not input.UTXO:
print(f"#Referenced input on Transaction({self.transaction_id}) is Missing")
return False
if self.get_inputs_value() < blockchain.minimum_transaction:
print(f"#Transaction Inputs too small: {self.get_inputs_value()}")
return False
self.generate_outputs()
# add outputs to blockchain's UTXO list
for output in self.outputs:
blockchain.UTXOs[output.id] = output
# remove transaction inputs from UTXO list as spent
for input in self.inputs:
if input.UTXO:
blockchain.UTXOs.pop(input.UTXO.id, None)
return True