OSDN Git Service

combine with btmsender
[bytom/bytom-spanner.git] / utxomerger / merge_utxo.py
1 import argparse
2 import getpass
3 import os
4 import time
5
6
7 from .Account import Account
8 from .Transaction import Action, Transaction
9 from .UnspentOutputs import UnspentOutputs
10 from .connection import Connection
11
12 parser = argparse.ArgumentParser(description='Bytom merge utxo tool')
13 parser.add_argument('-o', '--url', default='http://127.0.0.1:9888', dest='url', help='API url to connect')
14 parser.add_argument('-a', '--account', default=None, dest='account_alias', help='account alias')
15 parser.add_argument('-p', '--pass', default=None, dest='password', help='account password')
16 parser.add_argument('-x', '--max', default=41250000000, type=int, dest='max_amount', help='range lower than max_amount')
17 parser.add_argument('-s', '--min', default=1, type=int, dest='min_amount', help='range higher than min_amount')
18 parser.add_argument('-l', '--list', action='store_true', dest='only_list', help='Show UTXO list without merge')
19 parser.add_argument('-m', '--merge', default=None, type=int, dest='merge_list', help='UTXO to merge')
20 parser.add_argument('-y', '--yes', action='store_true', default=None, dest='confirm', help='confirm transfer')
21
22
23 class BytomException(Exception):
24     pass
25
26
27 class JSONRPCException(Exception):
28     pass
29
30
31 def list_utxo(connection, account_alias, min_amount, max_amount):
32     mature_utxos = []
33     data, ret = UnspentOutputs.list_UTXO(connection=Connection(connection))
34     block_height, ret_code = UnspentOutputs.get_block_height(connection=Connection(connection))
35     if ret == 1 and ret_code == 1:
36         for utxo in data:
37             # append mature utxo to set
38             if utxo['valid_height'] < block_height \
39                     and utxo['account_alias'] == account_alias \
40                     and utxo['asset_alias'] == 'BTM':
41                 mature_utxos.append(utxo)
42     elif ret == -1:
43         raise BytomException(data)
44
45     result = []
46     for utxo in mature_utxos:
47         if utxo['amount'] <= max_amount and utxo['amount'] >= min_amount:
48             result.append(utxo)
49
50     return result
51
52
53 def send_tx(connection, utxo_list, to_address, password):
54     gas_amount = 40000000
55     actions = []
56     amount = 0
57     asset_id = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
58
59     for utxo in utxo_list:
60         actions.append(Action.unspent_output(output_id=utxo['id']))
61         amount += utxo['amount']
62
63     # consider gas amount
64     if amount > gas_amount:
65         amount = amount - gas_amount
66     else:
67         print('\nAttention: The amount of all utxos is too little, less than tx gas.')
68         os._exit(0)
69
70     actions.append(Action.control_address(amount=amount, asset_id=asset_id, address=to_address))
71
72     time.sleep(1)
73
74     transaction = Transaction.build_transaction(connection, actions)
75
76     signed_transaction = Transaction.sign_transaction(connection, password, transaction)
77
78     if signed_transaction['sign_complete']:
79         raw_transaction = signed_transaction['transaction']['raw_transaction']
80         result = Transaction.submit_transaction(connection, raw_transaction)
81         return result['tx_id']
82     else:
83         raise BytomException('Sign not complete')
84
85
86 def main():
87     options = parser.parse_args()
88     utxolist = list_utxo(options.url, options.account_alias, options.min_amount, options.max_amount)
89     for i, utxo in enumerate(utxolist):
90         print('{:4}. {:13.8f} BTM {}{}'.format(i, utxo['amount'] / 1e8, utxo['id'], ' (mature)'))
91         if i >= 21:
92             break
93     print("total size of available utxos is {}".format(len(utxolist)))
94
95     if options.only_list:
96         return
97
98     merge_size = options.merge_list or input('Merge size of UTXOs (5, 13 or 20): ')
99     utxo_mergelist = []
100
101     for i in range(merge_size if merge_size <= len(utxolist) else len(utxolist)):
102         utxo_mergelist.append(utxolist[i])
103
104     if len(utxo_mergelist) < 2:
105         print('Not Merge UTXOs, Exit...')
106         return
107
108     print('To merge {} UTXOs with {:13.8f} BTM'.format(len(utxo_mergelist),
109                                                        sum(utxo['amount'] for utxo in utxo_mergelist) / 1e8))
110
111     if not options.account_alias:
112         options.account_alias = input('Transfer account alias: ')
113
114     if not options.password:
115         options.password = getpass.getpass('Bytom Account Password: ')
116
117     print(
118         'One last disclaimer: the code we are about to go over is in no way intended to be used as an example of a robust solution. ')
119     print('You will transfer BTM to an address, please check this python code and DO IT later.')
120
121     if not (options.confirm or input('Confirm [y/N] ').lower() == 'y'):
122         print('Not Merge UTXOs, Exit...')
123         return
124
125     to_address = Account.find_address_by_alias(Connection(options.url), options.account_alias)
126     if not to_address:
127         to_address = input('Transfer address: ')
128
129     print('tx_id:', send_tx(Connection(options.url), utxo_mergelist, to_address, options.password))
130
131
132 if __name__ == '__main__':
133     main()