-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathwallet.py
175 lines (143 loc) · 7.14 KB
/
wallet.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
from argparse import ArgumentParser
from time import time
from json import dumps
from binascii import hexlify
from binascii import unhexlify
from os import urandom
from os.path import isfile
from sys import exit
from sys import argv
import ed25519
import requests
import base64
parser = ArgumentParser("Simple wallet for SimpleCoin")
parser.add_argument('-n', '--new', help="Create new keypair", action="store_true")
parser.add_argument('-w', '--wallet', help="Path to the .sc wallet", default="my.sc")
parser.add_argument('-t', '--transaction', help="Create & sign a txn", action="store_true")
# Arguments for working with Tendermint
parser.add_argument('-b', '--broadcast', help="Broadcast txn to the network", default="")
parser.add_argument('-g', '--get_balance', help="Get balance for some address", default="")
# Arguments for message signing
parser.add_argument('-s', '--sign', help="Sign a message", action="store_true")
parser.add_argument('-c', '--check_sign', help="Specify the signature", default="")
parser.add_argument('-m', '--message', help="Message to sign or to check",
required='-s' in argv or '--sign' in argv or '-c' in argv or '--check_sign' in argv, default=""
)
parser.add_argument('-p', '--pub_key', help="Public key, corresponding to the signature",
required='-c' in argv or '--check_sign' in argv, default=""
)
# Arguments for creating new txn
parser.add_argument('-a', '--amount', help="Amount of coins to send in txn",
required='-t' in argv or '--transaction' in argv, type=int
)
parser.add_argument('-r', '--receiver', help="Transaction receiver",
required='-t' in argv or '--transaction' in argv, type=str
)
parser.add_argument('-d', '--data', help="Small piece of data to store in txn",
type=str, default=""
)
def read_signing_key(path):
with open(path, "rb") as ff:
keydata = ff.read().splitlines()[0]
return ed25519.SigningKey(keydata, encoding="base64")
def read_verifying_key(path):
with open(path, "rb") as ff:
keydata = ff.read().splitlines()[1]
return ed25519.VerifyingKey(keydata, encoding="base64")
if __name__ == "__main__":
options = parser.parse_args()
# _ __ _____ __ __ ____ _| | | ___| |_
# | '_ \ / _ \ \ /\ / / \ \ /\ / / _` | | |/ _ \ __|
# | | | | __/\ V V / \ V V / (_| | | | __/ |_
# |_| |_|\___| \_/\_/ \_/\_/ \__,_|_|_|\___|\__|
if options.new and not isfile(options.wallet): # Create new Ed25519 keypair
signing_key, verifying_key = ed25519.create_keypair(entropy=urandom)
with open(options.wallet, "w") as ff:
ff.write(signing_key.to_ascii(encoding="base64").decode())
ff.write("\n")
ff.write(verifying_key.to_ascii(encoding="base64").decode())
ff.write("\n")
exit("New keypair saved into the {}".format(options.wallet))
elif options.new and isfile(options.wallet): # There's a wallet already
exit("Wallet already exists!")
# _
# ___(_) __ _ _ __ _ __ ___ ___ ___ ___ __ _ __ _ ___
# / __| |/ _` | '_ \ | '_ ` _ \ / _ \/ __/ __|/ _` |/ _` |/ _ \
# \__ \ | (_| | | | | | | | | | | __/\__ \__ \ (_| | (_| | __/
# |___/_|\__, |_| |_| |_| |_| |_|\___||___/___/\__,_|\__, |\___|
# |___/ |___/
if options.sign and isfile(options.wallet):
signing_key = read_signing_key(options.wallet)
sig = signing_key.sign(options.message.encode(), encoding="base64")
exit("The signature is:\t {}".format(sig.decode("ascii")))
elif options.sign and not isfile(options.wallet):
exit("SD Can't find wallet, use 'python wallet.py -n'")
# _ _ _
# ___| |__ ___ ___| | __ ___(_) __ _ _ __
# / __| '_ \ / _ \/ __| |/ / / __| |/ _` | '_ \
# | (__| | | | __/ (__| < \__ \ | (_| | | | |
# \___|_| |_|\___|\___|_|\_\ |___/_|\__, |_| |_|
# |___/
if options.check_sign:
verifying_key = ed25519.VerifyingKey(options.pub_key, encoding="base64")
try:
verifying_key.verify(
options.check_sign.encode(),
options.message.encode(),
encoding="base64"
)
exit("Valid signature!")
except ed25519.BadSignatureError:
exit("Invalid signature!")
# _ _
# ___ _ __ ___ __ _| |_ ___ | |___ ___ __
# / __| '__/ _ \/ _` | __/ _ \ | __\ \/ / '_ \
# | (__| | | __/ (_| | || __/ | |_ > <| | | |
# \___|_| \___|\__,_|\__\___| \__/_/\_\_| |_|
if options.transaction and isfile(options.wallet):
signing_key = read_signing_key(options.wallet)
verifying_key = read_verifying_key(path=options.wallet)
txn = {
"sender" : verifying_key.to_ascii(encoding="base64").decode(),
"receiver" : options.receiver,
"amount" : options.amount,
"data" : options.data,
"timestamp" : int(time())
}
# To sign a message you need to be sure, that the bytes sequence is imutable
# So it's better to sign not the JSON, but the values in immutable order
# To specify an order, I sorted all the keys lexicographically
keys_sequence = sorted(txn.keys())
msg_to_sign = ";".join([str(txn[k]) for k in keys_sequence])
txn["signature"] = signing_key.sign(msg_to_sign.encode(), encoding="base64").decode("ascii")
print ("Your txn is printed bellow. Copy as it is and send with the ABCI query\n")
exit('0x' + hexlify((dumps(txn).encode())).decode('utf-8'))
elif options.transaction and not isfile(options.wallet):
exit("Can't find wallet, use 'python wallet.py -n'")
# ___ ___ _ __ __| | | |___ ___ __
# / __|/ _ \ '_ \ / _` | | __\ \/ / '_ \
# \__ \ __/ | | | (_| | | |_ > <| | | |
# |___/\___|_| |_|\__,_| \__/_/\_\_| |_|
if options.broadcast:
r = requests.get("http://localhost:26657/broadcast_tx_async?tx={}".format(options.broadcast))
if r.status_code == 200:
txn_hash = r.json()['result']['hash']
exit("Txn broadcasted, txn hash: {}".format(txn_hash))
else:
err_log = r.json()['result']['log']
exit("Can't broadcast your txn: {}".format(err_log))
# __ _ ___| |_ | |__ __ _| | __ _ _ __ ___ ___
# / _` |/ _ \ __| | '_ \ / _` | |/ _` | '_ \ / __/ _ \
# | (_| | __/ |_ | |_) | (_| | | (_| | | | | (_| __/
# \__, |\___|\__| |_.__/ \__,_|_|\__,_|_| |_|\___\___|
# |___/
if options.get_balance:
encoded_address = str(hexlify(options.get_balance.encode()), 'utf-8')
# 0x62616c616e6365 = 'balance'
r = requests.get("http://localhost:26657/abci_query?path=0x62616c616e6365&data=0x{}".format(encoded_address))
if r.status_code == 200:
encoded_balance = r.json()['result']['response']['value']
exit("There are {amount} SimpleCoins on the {address}".format(
amount=int.from_bytes(base64.b64decode(encoded_balance), byteorder='big'),
address=options.get_balance
))