Python based Bytom wallet tools
Revision | e5969733256d162121350eedc3b9c8dc537c8ae3 (tree) |
---|---|
Time | 2020-04-24 21:14:36 |
Author | DeKaiju <longjinglv@gmai...> |
Commiter | GitHub |
Merge pull request #9 from Bytom/asset
feat(asset): support transaction asset is not BTM
@@ -23,7 +23,7 @@ Tool send BTM to large numbers of address | ||
23 | 23 | |
24 | 24 | Usage: |
25 | 25 | ``` |
26 | - spanner.py btmsender [-h] -n N -i I -a A [-c C] [-u] [-t T] | |
26 | + spanner.py btmsender [-h] -n N -i I -a A [-s S] [-c C] [-u] [-t T] | |
27 | 27 | ``` |
28 | 28 | Options: |
29 | 29 | ``` |
@@ -31,6 +31,7 @@ Options: | ||
31 | 31 | -n node bytomd or vapord node address |
32 | 32 | -i input transaction txt file |
33 | 33 | -a account wallet account id |
34 | + -s asset_id transaction asset id | |
34 | 35 | -c count transaction output count |
35 | 36 | -u use unconfirmed UTXO build transaction |
36 | 37 | -t time_range the transaction will not be submitted into block after this height |
@@ -10,6 +10,7 @@ Options: | ||
10 | 10 | -n node bytomd or vapord node address |
11 | 11 | -i input transaction txt file |
12 | 12 | -a account wallet account id |
13 | + -s asset_id transaction asset id | |
13 | 14 | -c count transaction output count |
14 | 15 | -u use unconfirmed UTXO build transaction |
15 | 16 | -t time_range the transaction will not be submitted into block after this height |
@@ -17,7 +18,7 @@ Options: | ||
17 | 18 | |
18 | 19 | Example: |
19 | 20 | ``` |
20 | - spanner.py btmsender -i btmsender/btm.txt -a 0F0BV1OLG0A04 -p 123456 -c 1000 -u -t 96310 | |
21 | + spanner.py btmsender -i btmsender/btm.txt -a 0F0BV1OLG0A04 -s d50a426bdaaf1458d161aba4d8c3ebdd095eac7e1bbeb4a0252a3737ccf2d492 -p 123456 -c 1000 -u -t 96310 | |
21 | 22 | ``` |
22 | 23 | |
23 | 24 | Transaction txt file format: |
@@ -3,7 +3,6 @@ from . import transaction | ||
3 | 3 | |
4 | 4 | |
5 | 5 | def sender(): |
6 | - node_address, file_path, account_id, password, output_count, use_unconfirmed, time_range = validation.validate_input() | |
7 | - btmsender = \ | |
8 | - transaction.BTMSender(node_address, file_path, account_id, password, output_count, use_unconfirmed, time_range) | |
6 | + _input = validation.validate_input() | |
7 | + btmsender = transaction.BTMSender(_input) | |
9 | 8 | btmsender.handle_input() |
@@ -0,0 +1,3 @@ | ||
1 | +miner_fee = 40000000 | |
2 | +max_output_count = 1500 | |
3 | +btm_asset_id = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' |
@@ -1,30 +1,22 @@ | ||
1 | 1 | import os |
2 | 2 | import sys |
3 | + | |
4 | +from . import const | |
3 | 5 | from . import httprequest |
4 | 6 | |
5 | 7 | |
6 | 8 | class BTMSender: |
7 | - miner_fee = 40000000 | |
8 | - max_output_count = 1500 | |
9 | - | |
10 | - def __init__(self, _node_address, _input_path, _account_id, _password, _output_count, _use_unconfirmed, | |
11 | - _time_range): | |
12 | - self.node_address = _node_address | |
13 | - self.input_path = _input_path | |
14 | - self.account_id = _account_id | |
15 | - self.password = _password | |
16 | - self.output_count = _output_count | |
17 | - self.use_unconfirmed = _use_unconfirmed | |
18 | - self.time_range = _time_range | |
9 | + def __init__(self, _input): | |
10 | + self.input = _input | |
19 | 11 | |
20 | 12 | def handle_input(self): |
21 | - if self.output_count <= 0: | |
22 | - self.output_count = self.max_output_count | |
13 | + if self.input.output_count <= 0: | |
14 | + self.input.output_count = const.max_output_count | |
23 | 15 | lines = list() |
24 | - with open(self.input_path, 'r', encoding='utf-8') as file: | |
16 | + with open(self.input.input_path, 'r', encoding='utf-8') as file: | |
25 | 17 | for line in file: |
26 | 18 | line = line.strip() |
27 | - if len(lines) < self.output_count: | |
19 | + if len(lines) < self.input.output_count: | |
28 | 20 | lines.append(line) |
29 | 21 | else: |
30 | 22 | self.handle_transaction(lines) |
@@ -42,7 +34,7 @@ class BTMSender: | ||
42 | 34 | tx_id = data['tx_id'] |
43 | 35 | if tx_id: |
44 | 36 | print('transaction_id:\n' + tx_id) |
45 | - write_lines_to_file(lines, self.input_path) | |
37 | + write_lines_to_file(lines, self.input.input_path) | |
46 | 38 | else: |
47 | 39 | print('Sign transaction is failed.Please check account password.') |
48 | 40 | sys.exit(1) |
@@ -56,24 +48,40 @@ class BTMSender: | ||
56 | 48 | address = data[0] |
57 | 49 | amount = int(data[1]) |
58 | 50 | amount_sum += amount |
59 | - address_dict = get_address_dict(amount, address) | |
51 | + address_dict = self.get_address_dict(amount, address) | |
60 | 52 | action_list.append(address_dict) |
61 | - amount_sum += self.miner_fee | |
62 | - spend_dict = get_spend_dict(self.account_id, int(amount_sum), self.use_unconfirmed) | |
53 | + fee_dict = self.get_spend_dict(const.miner_fee, const.btm_asset_id) | |
54 | + spend_dict = self.get_spend_dict(int(amount_sum), self.input.asset_id) | |
63 | 55 | action_list.insert(0, spend_dict) |
56 | + action_list.insert(1, fee_dict) | |
64 | 57 | parameter = {'base_transaction': None, 'actions': action_list, 'ttl': 0} |
65 | - if self.time_range != 0: | |
66 | - parameter.update(time_range=self.time_range) | |
67 | - return httprequest.post(self.node_address, 'build-transaction', parameter) | |
58 | + if self.input.time_range != 0: | |
59 | + parameter.update(time_range=self.input.time_range) | |
60 | + return httprequest.post(self.input.node_address, 'build-transaction', parameter) | |
68 | 61 | |
69 | 62 | # parameter transaction: dict from build_transaction function return |
70 | 63 | def sign_transaction(self, _transaction): |
71 | - parameter = {'password': self.password, 'transaction': _transaction} | |
72 | - return httprequest.post(self.node_address, 'sign-transaction', parameter) | |
64 | + parameter = {'password': self.input.password, 'transaction': _transaction} | |
65 | + return httprequest.post(self.input.node_address, 'sign-transaction', parameter) | |
73 | 66 | |
74 | 67 | def submit_transaction(self, _transaction): |
75 | 68 | parameter = {'raw_transaction': _transaction['transaction']['raw_transaction']} |
76 | - return httprequest.post(self.node_address, 'submit-transaction', parameter) | |
69 | + return httprequest.post(self.input.node_address, 'submit-transaction', parameter) | |
70 | + | |
71 | + # control_address action | |
72 | + def get_address_dict(self, _amount, _address): | |
73 | + return {'amount': _amount, | |
74 | + 'asset_id': self.input.asset_id, | |
75 | + 'address': _address, | |
76 | + 'type': 'control_address'} | |
77 | + | |
78 | + # spend_account action | |
79 | + def get_spend_dict(self, _amount_sum, _asset_id): | |
80 | + return {'account_id': self.input.account_id, | |
81 | + 'amount': _amount_sum, | |
82 | + 'asset_id': _asset_id, | |
83 | + 'type': 'spend_account', | |
84 | + 'use_unconfirmed': self.input.use_unconfirmed} | |
77 | 85 | |
78 | 86 | |
79 | 87 | # write complete transactions lines to file |
@@ -81,20 +89,3 @@ def write_lines_to_file(lines, _path): | ||
81 | 89 | _path = os.path.abspath(os.path.dirname(_path)) + os.path.sep + 'completed.txt' |
82 | 90 | with open(_path, 'a', encoding='utf-8') as file: |
83 | 91 | file.write('\n'.join(lines) + '\n\n') |
84 | - | |
85 | - | |
86 | -# control_address action | |
87 | -def get_address_dict(_amount, _address): | |
88 | - return {'amount': _amount, | |
89 | - 'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', | |
90 | - 'address': _address, | |
91 | - 'type': 'control_address'} | |
92 | - | |
93 | - | |
94 | -# spend_account action | |
95 | -def get_spend_dict(_account_id, _amount_sum, _use_unconfirmed): | |
96 | - return {'account_id': _account_id, | |
97 | - 'amount': _amount_sum, | |
98 | - 'asset_id': 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', | |
99 | - 'type': 'spend_account', | |
100 | - 'use_unconfirmed': _use_unconfirmed} |
@@ -1,9 +1,24 @@ | ||
1 | 1 | import os |
2 | 2 | import sys |
3 | 3 | import argparse |
4 | + | |
5 | +from . import const | |
4 | 6 | from . import httprequest |
5 | 7 | |
6 | 8 | |
9 | +class Input: | |
10 | + def __init__(self, node_address, input_path, account_id, asset_id, password, output_count, use_unconfirmed, | |
11 | + time_range): | |
12 | + self.node_address = node_address | |
13 | + self.input_path = input_path | |
14 | + self.account_id = account_id | |
15 | + self.asset_id = asset_id | |
16 | + self.password = password | |
17 | + self.output_count = output_count | |
18 | + self.use_unconfirmed = use_unconfirmed | |
19 | + self.time_range = time_range | |
20 | + | |
21 | + | |
7 | 22 | def validate_address(node_address, line, address): |
8 | 23 | parameter = {'address': address} |
9 | 24 | data = httprequest.post(node_address, 'validate-address', parameter) |
@@ -25,30 +40,42 @@ def get_input(): | ||
25 | 40 | parser.add_argument('-n', required=True, help='bytomd or vapord node api address') |
26 | 41 | parser.add_argument('-i', required=True, help='transaction input file') |
27 | 42 | parser.add_argument('-a', required=True, help='wallet account id') |
43 | + parser.add_argument('-s', type=str, default=const.btm_asset_id, help='transaction asset id') | |
28 | 44 | parser.add_argument('-c', type=int, default=0, help='transaction max output count') |
29 | 45 | parser.add_argument('-u', action='store_true', help='use unconfirmed UTXO build transaction') |
30 | 46 | parser.add_argument('-t', type=int, default=0, |
31 | 47 | help='the transaction will not be submitted into block after this height') |
32 | 48 | args = parser.parse_args() |
33 | - return args.n, args.i, args.a, args.c, args.u, args.t | |
49 | + _input = Input(node_address=args.n, input_path=args.i, account_id=args.a, asset_id=args.s, password="", | |
50 | + output_count=args.c, use_unconfirmed=args.u, time_range=args.t) | |
51 | + return _input | |
34 | 52 | |
35 | 53 | |
36 | 54 | def validate_input(): |
37 | - node_address, input_path, account_id, output_count, use_unconfirmed, time_range = get_input() | |
55 | + _input = get_input() | |
38 | 56 | # relative path |
39 | - file_path = os.path.abspath('.') + os.path.sep + input_path | |
57 | + file_path = os.path.abspath('.') + os.path.sep + _input.input_path | |
40 | 58 | if not os.path.exists(file_path): |
41 | 59 | # absolute path |
42 | - file_path = input_path | |
60 | + file_path = _input.input_path | |
43 | 61 | total_amount = 0 |
44 | 62 | # read file |
45 | 63 | with open(file_path, 'r', encoding='utf-8') as file: |
46 | 64 | for line in file: |
47 | 65 | splits = line.strip().split(',') |
48 | - validate_address(node_address, line, splits[0]) | |
66 | + validate_address(_input.node_address, line, splits[0]) | |
49 | 67 | validate_amount(line, splits[1]) |
50 | 68 | total_amount += int(splits[1]) |
51 | - print('Transactions address and amount are valid.' + | |
52 | - '\nTotal amount is %.2f BTM(without gas).' % (total_amount / pow(10, 8))) | |
69 | + | |
70 | + if _input.asset_id == const.btm_asset_id: | |
71 | + print('Transactions address and amount are valid.' + | |
72 | + '\nTotal amount is %.2f BTM(without gas).' % (total_amount / pow(10, 8))) | |
73 | + else: | |
74 | + print('Transactions address and amount are valid.' + | |
75 | + '\nTotal amount is {total_amount} {asset_id}.' | |
76 | + .format(total_amount=total_amount, asset_id=_input.asset_id)) | |
77 | + | |
53 | 78 | password = input("Please input your account password:") |
54 | - return node_address, file_path, account_id, password, output_count, use_unconfirmed, time_range | |
79 | + _input.input_path = file_path | |
80 | + _input.password = password | |
81 | + return _input |