-
Notifications
You must be signed in to change notification settings - Fork 95
Expand file tree
/
Copy pathwallet.py
More file actions
115 lines (97 loc) · 3.56 KB
/
wallet.py
File metadata and controls
115 lines (97 loc) · 3.56 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
108
109
110
111
112
113
114
115
import json
import uuid
from backend.config import STARTING_BALANCE
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
encode_dss_signature,
decode_dss_signature
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.exceptions import InvalidSignature
class Wallet:
"""
An individual wallet for a miner.
Keeps track of the miner's balance.
Allows a miner to authorize transactions.
"""
def __init__(self, blockchain=None):
self.blockchain = blockchain
self.id = str(uuid.uuid4())
self.address = self.id[0:8]
self.private_key = ec.generate_private_key(
ec.SECP256K1(),
default_backend()
)
self.public_key = self.private_key.public_key()
self.serialize_public_key()
@property
def balance(self):
return Wallet.calculate_balance(self.blockchain, self.address)
def sign(self, data):
"""
Generate a signature based on the data using the local private key.
"""
return decode_dss_signature(self.private_key.sign(
json.dumps(data).encode('utf-8'),
ec.ECDSA(hashes.SHA256())
))
def serialize_public_key(self):
"""
Reset the public key to its serialized version.
"""
self.public_key = self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode('utf-8')
@staticmethod
def verify(public_key, data, signature):
"""
Verify a signature based on the original public key and data.
"""
deserialized_public_key = serialization.load_pem_public_key(
public_key.encode('utf-8'),
default_backend()
)
(r, s) = signature
try:
deserialized_public_key.verify(
encode_dss_signature(r, s),
json.dumps(data).encode('utf-8'),
ec.ECDSA(hashes.SHA256())
)
return True
except InvalidSignature:
return False
@staticmethod
def calculate_balance(blockchain, address):
"""
Calculate the balance of the given address considering the transaction
data within the blockchain.
The balance is found by adding the output values that belong to the
address since the most recent transaction by that address.
"""
balance = STARTING_BALANCE
if not blockchain:
return balance
for block in blockchain.chain:
for transaction in block.data:
if transaction['input']['address'] == address:
# Any time the address conducts a new transaction it resets
# its balance
balance = transaction['output'][address]
elif address in transaction['output']:
balance += transaction['output'][address]
return balance
def main():
wallet = Wallet()
print(f'wallet.__dict__: {wallet.__dict__}')
data = { 'foo': 'bar' }
signature = wallet.sign(data)
print(f'signature: {signature}')
should_be_valid = Wallet.verify(wallet.public_key, data, signature)
print(f'should_be_valid: {should_be_valid}')
should_be_invalid = Wallet.verify(Wallet().public_key, data, signature)
print(f'should_be_invalid: {should_be_invalid}')
if __name__ == '__main__':
main()