+++ /dev/null
-import json
-
-
-class Asset(object):
-
- @staticmethod
- def list(connection):
- response = connection.request("/list-assets")
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return resp_json['data']
- elif resp_json['status'] == 'fail':
- return resp_json['msg']
- else:
- return resp_json
-
- @staticmethod
- def get_asset(connection, asset_id):
- asset_list = Asset.list(connection)
- for asset in asset_list:
- if asset['id'] == asset_id:
- return asset
-
- @staticmethod
- def get_asset_by_alias(connection, asset_alias):
- asset_list = Asset.list(connection)
- for asset in asset_list:
- if asset['alias'] == asset_alias.upper():
- return asset
-
- @staticmethod
- def create(connection, root_xpubs, alias, quorum):
- body_json = {"root_xpubs": root_xpubs, "alias": alias, "quorum": quorum}
- response = connection.request("/create-asset", body_json)
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return resp_json['data']
- elif resp_json['status'] == 'fail':
- return resp_json['msg']
- else:
- return resp_json
-
- @staticmethod
- def update_alias(connection, asset_id, new_alias):
- body_json = {"id": asset_id, "alias": new_alias}
- response = connection.request("/update-asset-alias", body_json)
-
- resp_json = json.loads(response.text)
-
- return resp_json
+++ /dev/null
-import json
-
-
-class Balance(object):
-
- @staticmethod
- def list(connection):
- response = connection.request("/list-balances")
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return resp_json['data']
- elif resp_json['status'] == 'fail':
- return resp_json['msg']
- else:
- return resp_json
-
- @staticmethod
- def get_by_account_alias(connection, account_alias):
- balance_list = Balance.list(connection)
- account_balances = []
- for balance in balance_list:
- if balance['account_alias'] == account_alias:
- account_balances.append(balance)
-
- return account_balances
-
- @staticmethod
- def get_by_asset_alias(connection, asset_alias):
- balance_list = Balance.list(connection)
- asset_balances = []
- for balance in balance_list:
- if balance['asset_alias'] == asset_alias.upper():
- asset_balances.append(balance)
-
- return asset_balances
+++ /dev/null
-# UTXO combiner
-
-Requirements: Python 3.x, with requests package
-
-Dependencies:
- ```
- pip install requests
- ```
-
-Usage:
- ```
- ./bytomd init --chain_id mainnet
- ```
- ```
- python utxo_combiner.py [options]
- ```
- If you don't know how to run bytomd please check this [wiki](https://github.com/Bytom/bytom/wiki/Build-and-Install)
-
-Options:
- ```
- -h, --help show this help message and exit
- -c connection connection url
- -a account account_alias
- -p password account password
- -m max_amount utxo less than max_amount to combine
- -s size combine utxo size every time
- ```
-
-Example:
- ```shell
-python utxo_combiner.py -c http://127.0.0.1:9888 -a sheng -p 123456 -m 41250000000 -s 20
- ```
-
- or
-
- ```shell
- python utxo_combiner.py --connection http://127.0.0.1:9888 --account sheng --password 123456 --max_amount 41250000000 --size 20
- ```
\ No newline at end of file
+++ /dev/null
-import json
-import os
-import time
-
-from Account import Account
-from Asset import Asset
-from Transaction import Action, Transaction
-
-
-class UnspentOutputs(object):
-
- @staticmethod
- def get_block_height(connection):
- response = connection.request("/get-block-count")
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return resp_json['data']['block_count'], 1
- elif resp_json['status'] == 'fail':
- return resp_json['msg'], -1
- else:
- return resp_json, 0
-
- @staticmethod
- def list_UTXO(connection):
- response = connection.request("/list-unspent-outputs")
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return resp_json['data'], 1
- elif resp_json['status'] == 'fail':
- return resp_json['msg'], -1
- else:
- return resp_json, 0
-
- @staticmethod
- def list_mature_UTXO(connection):
- result = []
- utxos, ret = UnspentOutputs.list_UTXO(connection=connection)
- block_height, ret_code = UnspentOutputs.get_block_height(connection=connection)
- if ret == 1 and ret_code == 1:
- for utxo in utxos:
- # append mature utxo to set
- if utxo['valid_height'] < block_height:
- result.append(utxo)
- return result
-
- @staticmethod
- def list_by_account_asset(connection, account_alias, asset_alias):
- utxos = UnspentOutputs.list_mature_UTXO(connection)
- result = []
- for utxo in utxos:
- if utxo['account_alias'] == account_alias and utxo['asset_alias'] == asset_alias:
- result.append(utxo)
-
- return result
-
- @staticmethod
- def find_little_utxo(max_amount=5000000000, utxos=[]):
- result = []
- for utxo in utxos:
- if utxo['amount'] <= max_amount:
- result.append(utxo)
- return result
-
- @staticmethod
- def combine_actions(connection, account_alias, asset_alias, max_amount=5000000000, size=20):
- actions = []
- gas_amount = 40000000
- amount = 0
- asset = Asset.get_asset_by_alias(connection, asset_alias)
- asset_id = asset['id']
- address = Account.find_address_by_alias(connection, account_alias)
-
- result = UnspentOutputs.list_by_account_asset(connection, account_alias, asset_alias)
- utxos = UnspentOutputs.find_little_utxo(max_amount=max_amount, utxos=result);
-
- if utxos.__len__() <= size:
- for utxo in utxos:
- actions.append(Action.unspent_output(output_id=utxo['id']))
- amount = amount + utxo['amount']
- else:
- for i in range(0, size):
- actions.append(Action.unspent_output(output_id=utxos[i]['id']))
- amount = amount + utxos[i]['amount']
-
- # consider gas amount
- if amount > gas_amount:
- amount = amount - gas_amount
- else:
- print('\nAttention: The amount of all utxos is too little, less than tx gas.')
- os._exit(0)
- actions.append(Action.control_address(amount=amount, asset_id=asset_id, address=address))
-
- return actions
-
- @staticmethod
- def combine_utxos(connection, account_alias, password, asset_alias='BTM', max_amount=5000000000, size=20):
- actions = UnspentOutputs.combine_actions(connection, account_alias, asset_alias, max_amount, size)
- # print(actions)
- issuance = Transaction.build_transaction(connection, actions)
- # print("issuance:", issuance)
- # sign transaction
- signed_raw_transaction = Transaction.sign_transaction(connection, password, issuance)
- # print("signed_raw_transaction:", signed_raw_transaction)
- # submit transaction
- tx_submit = Transaction.submit_transaction(connection, signed_raw_transaction)
- tx_id = tx_submit['tx_id']
- print("tx_id:", tx_id)
- if tx_id is not None:
- print("success to combine utxos.")
-
- print("wait combine tx to chain...(about 2.5 minutes)")
-
- while True:
- tx, ret = Transaction.get_transaction(connection=connection, tx_id=tx_id)
- if ret == 1:
- # print(tx)
- if int(tx['block_index']) > 0:
- print("combine tx to chain. You can combine next utxos.")
- break
- elif ret == -1:
- print("tx:", tx)
- break
- time.sleep(2)
- else:
- print(actions)
- print("issuance:", issuance)
- print("signed_raw_transaction:", signed_raw_transaction)
+++ /dev/null
-import unittest
-
-from Account import Account
-from Asset import Asset
-from connection import Connection
-from keys import Keys
-
-
-class TestAssetMethods(unittest.TestCase):
-
- def test_list(self):
- con = Connection("http://127.0.0.1:9888")
- asset_list = Asset.list(con)
- for asset in asset_list:
- print(str(asset))
-
- def test_get_asset(self):
- con = Connection("http://127.0.0.1:9888")
- asset_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
- asset = Asset.get_asset(con, asset_id)
- print(asset)
-
- def test_get_by_alias(self):
- con = Connection("http://127.0.0.1:9888")
- asset = Asset.get_asset_by_alias(con, "gold")
- print(asset)
-
- def test_create(self):
- con = Connection("http://127.0.0.1:9888")
- xpub = Keys.find_by_alias(con, "sheng").xpub
- asset = Asset.create(con, [xpub], "shengsheng", 1)
- print(asset.id)
-
- def test_update_alias(self):
- con = Connection("http://127.0.0.1:9888")
- response = Asset.update_alias(con, "d05b4cc1615509bf0d58cda7be4c5da43a33cc920ae439eaaac6507853e3cbf1", "shengsheng")
- print(response)
-
- if __name__ == '__main__':
- unittest.main()
+++ /dev/null
-import unittest
-
-from Account import Account
-from Asset import Asset
-from Balance import Balance
-from connection import Connection
-from keys import Keys
-
-
-class TestAssetMethods(unittest.TestCase):
-
- def test_list(self):
- con = Connection("http://127.0.0.1:9888")
- balance_list = Balance.list(con)
- for balance in balance_list:
- print(str(balance))
-
- def test_get_by_account_alias(self):
- con = Connection("http://127.0.0.1:9888")
- account_alias = 'test'
- balance = Balance.get_by_account_alias(con, account_alias)
- print(balance)
- print(balance.__len__())
-
- def test_get_by_asset_alias(self):
- con = Connection("http://127.0.0.1:9888")
- balance = Balance.get_by_asset_alias(con, 'btm')
- print(balance)
- print(balance.__len__())
-
- def test_create(self):
- con = Connection("http://127.0.0.1:9888")
- xpub = Keys.find_by_alias(con, "sheng").xpub
- asset = Asset.create(con, [xpub], "shengsheng", 1)
- print(asset.id)
-
- def test_update_alias(self):
- con = Connection("http://127.0.0.1:9888")
- response = Asset.update_alias(con, "d05b4cc1615509bf0d58cda7be4c5da43a33cc920ae439eaaac6507853e3cbf1", "shengsheng")
- print(response)
-
- if __name__ == '__main__':
- unittest.main()
+++ /dev/null
-import json
-
-
-class Keys(object):
- def __init__(self, alias, xpub, file, *args, **kwargs):
- self.alias = alias
- self.xpub = xpub
- self.file = file
-
- @staticmethod
- def list(connection):
- response = connection.request("/list-keys")
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- key_list = list(map(lambda x: Keys(**x), resp_json['data']))
- return key_list
- elif resp_json['status'] == 'fail':
- return resp_json['msg']
- else:
- return resp_json
-
- @staticmethod
- def find_by_alias(connection, alias):
- key_list = Keys.list(connection)
- for key in key_list:
- if key.alias == alias:
- return key
-
- @staticmethod
- def create(connection, alias, password):
- body_json = {"alias": alias, "password": password}
- response = connection.request("/create-key", body_json)
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return Keys(**resp_json['data'])
- elif resp_json['status'] == 'fail':
- return resp_json['msg']
- else:
- return resp_json
-
- @staticmethod
- def delete(connection, xpub, password):
- body_json = {"xpub": xpub, "password": password}
-
- response = connection.request("/delete-key", body_json)
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == 'success':
- return "true"
- else:
- return "false"
-
- @staticmethod
- def delete_by_alias(connection, alias, password):
- key = Keys.find_by_alias(connection, alias)
- key_xpub = key.xpub
- return Keys.delete(connection, key_xpub, password)
-
- @staticmethod
- def reset_password(connection, xpub, old_password, new_password):
- body_json = {"xpub": xpub, "old_password": old_password, "new_password": new_password}
-
- response = connection.request("/reset-key-password", body_json)
-
- resp_json = json.loads(response.text)
-
- if resp_json['status'] == "success":
- return "true"
- else:
- return "false"
+++ /dev/null
-import unittest
-
-from connection import Connection
-from keys import Keys
-
-
-class TestKeysMethods(unittest.TestCase):
-
- def test_list(self):
- con = Connection("http://127.0.0.1:9888")
- key_list = Keys.list(con)
- for key in key_list:
- print(key.alias, key.xpub, key.file)
- self.assertIsNotNone(key_list)
-
- def test_find_by_alias(self):
- con = Connection("http://127.0.0.1:9888")
- key = Keys.find_by_alias(con, "sender-key")
- print(key.alias, key.xpub, key.file)
- self.assertIsNotNone(key)
-
- def test_create(self):
- con = Connection("http://127.0.0.1:9888")
- key = Keys.create(con, "sheng", "123456")
- print(key.alias, key.xpub, key.file)
- self.assertIsNotNone(key)
-
- def test_delete(self):
- con = Connection("http://127.0.0.1:9888")
- xpub = Keys.find_by_alias(con, "sheng").xpub
- status = Keys.delete(con, xpub, "123456")
- self.assertIs("true", status)
- pass
-
- def test_delete_by_alias(self):
- con = Connection("http://127.0.0.1:9888")
- status = Keys.delete_by_alias(con, "sheng", "123456")
- self.assertIs("true", status)
-
- def test_reset_password(self):
- con = Connection("http://127.0.0.1:9888")
- xpub = Keys.find_by_alias(con, "sheng").xpub
- print(xpub)
- status = Keys.reset_password(con, xpub, "567890", "123456")
- self.assertIs("true", status)
-
- if __name__ == '__main__':
- unittest.main()
+++ /dev/null
-import unittest
-
-from Transaction import Transaction
-from connection import Connection
-from UnspentOutputs import UnspentOutputs
-
-
-class TestUTXOMethods(unittest.TestCase):
-
- def test_get_block_height(self):
- con = Connection("http://127.0.0.1:9888")
- block_height, ret = UnspentOutputs.get_block_height(connection=con)
- if ret == 1:
- print(block_height)
-
- def test_list_UTXO(self):
- con = Connection("http://127.0.0.1:9888")
- utxos, ret = UnspentOutputs.list_UTXO(con)
- if ret == 1:
- for utxo in utxos:
- print(utxo)
-
- def test_list_by_account_asset(self):
- con = Connection("http://127.0.0.1:9888")
- account_alias = "zhangsan"
- asset_alias = "BTM"
- utxos = UnspentOutputs.list_by_account_asset(con, account_alias, asset_alias)
- for utxo in utxos:
- if utxo['amount'] < 41250000000:
- print(utxo)
-
- def test_find_little_utxo(self):
- con = Connection("http://127.0.0.1:9888")
- account_alias = "zhangsan"
- asset_alias = "BTM"
- utxos = UnspentOutputs.list_by_account_asset(con, account_alias, asset_alias)
- result = UnspentOutputs.find_little_utxo(max_amount=5000000000, utxos=utxos)
- print(result.__len__())
- for utxo in result:
- print(utxo)
-
- def test_combine_actions(self):
- con = Connection("http://127.0.0.1:9888")
- account_alias = "zhangsan"
- asset_alias = "BTM"
- actions = UnspentOutputs.combine_actions(con, account_alias, asset_alias)
- print("action begin")
- for action in actions:
- print(action)
- print("action end")
-
- def test_combine_utxos(self):
- con = Connection("http://127.0.0.1:9888")
- account_alias = "zhangsan"
- asset_alias = "BTM"
- UnspentOutputs.combine_utxos(connection=con, account_alias=account_alias, password='123456', max_amount=50, size=20)
-
- if __name__ == '__main__':
- unittest.main()
+++ /dev/null
-import sys, getopt
-from UnspentOutputs import UnspentOutputs
-from connection import Connection
-
-
-def main(argv):
- try:
- opts, args = getopt.getopt(argv, "hc:a:p:m:s:",
- ["connection=", "account=", "password=", "max_amount=", "size="])
- except getopt.GetoptError:
- print('python test.py -c <connection> -a <account_alias> -p <password>',
- '[optional] -m <max_amount> -s <size>')
- print(
- 'python test.py --connection <connection> --account <account_alias> --password <password>',
- '[optional] --max_amount <max_amount> --size <size>')
- sys.exit(2)
-
- # print("opts", opts)
-
- connection = Connection.generate()
- account_alias = ""
- password = ""
- max_amount = ""
- size = ""
-
- for op, value in opts:
- if op in ("-c", "--connection"):
- connection = Connection(value)
- # print(connection)
- elif op in ("-a", "--account"):
- account_alias = value
- # print(account_alias)
- elif op in ("-p", "--password"):
- password = value
- # print(password)
- elif op in ("-m", "--max_amount"):
- max_amount = int(value)
- # print(max_amount)
- elif op in ("-s", "--size"):
- size = int(value)
- # print(size)
- elif op in ("-h", "--help"):
- print('python test.py -c <connection> -a <account_alias> -p <password>',
- '[optional] -m <max_amount> -s <size>')
- print(
- 'python test.py --connection <connection> --account <account_alias> --password <password>',
- '[optional] --max_amount <max_amount> --size <size>')
- sys.exit()
-
- UnspentOutputs.combine_utxos(connection=connection, account_alias=account_alias,
- password=password, max_amount=max_amount, size=size)
-
-
-if __name__ == '__main__':
- main(sys.argv[1:])
--- /dev/null
+# UTXO combiner
+
+Requirements: Python 3.x, with requests package
+
+Dependencies:
+ ```
+ pip install requests
+ ```
+
+Usage:
+ ```
+ ./bytomd init --chain_id mainnet
+ ```
+ ```
+ python merge_utxo.py [options]
+ ```
+ If you don't know how to run bytomd please check this [wiki](https://github.com/Bytom/bytom/wiki/Build-and-Install)
+
+Options:
+ ```shell
+ usage: merge_utxo.py [-h] [-o URL] [-a ACCOUNT_ALIAS] [-p PASSWORD]
+ [-x MAX_AMOUNT] [-s MIN_AMOUNT] [-l] [-m MERGE_LIST] [-y]
+
+Bytom merge utxo tool
+
+optional arguments:
+ -h, --help show this help message and exit
+ -o URL, --url URL API url to connect
+ -a ACCOUNT_ALIAS, --account ACCOUNT_ALIAS
+ account alias
+ -p PASSWORD, --pass PASSWORD
+ account password
+ -x MAX_AMOUNT, --max MAX_AMOUNT
+ range lower than max_amount
+ -s MIN_AMOUNT, --min MIN_AMOUNT
+ range higher than min_amount
+ -l, --list Show UTXO list without merge
+ -m MERGE_LIST, --merge MERGE_LIST
+ UTXO to merge
+ -y, --yes confirm transfer
+ ```
+
+Example:
+ ```shell
+python merge_utxo.py -o http://127.0.0.1:9888 -a sheng -p 123456 -x 41250000000 -s 0 -l
+ ```
+
+ or
+
+ ```shell
+python merge_utxo.py -o http://127.0.0.1:9888 -a sheng -p 123456 -x 41250000000 -s 0 -m 10
+ ```
+
+Result:
+```shell
+$ python merge_utxo.py -o http://127.0.0.1:9888 -a sheng -p 123456 -x 41250000000 -s 0 -m 10
+ 0. 412.50000000 BTM f9c0d3831061edcf045e13e1a6408cb1794817290fceed1ef41fcbd809ef24d4 (mature)
+ 1. 412.50000000 BTM f98e8faf54ee9731578e973ab44dbdd2001fd254d34f5e8982de1cee0fc95126 (mature)
+ 2. 325.00000000 BTM f9864118a26f852149ac84d7d5b5da2bb97fb390a929a98faf3f3187c6ac6c35 (mature)
+ 3. 412.50000000 BTM f860fb39404fac64806eeae5f5ce7cc221fc7ab49386f84d19010fc4b0fd60c4 (mature)
+ 4. 412.50000000 BTM f7d895890405f228e709160231d1db4bd08fe56b20b0be92f13cd778105ce8fd (mature)
+ 5. 412.50000000 BTM f7d5180377f0b87dcb12ce3804bb47abe034edf5bc9d82349a87ca5533781b38 (mature)
+ 6. 412.50000000 BTM f7cccc6ace814de907e4ee71d8d3a8741fb341bfea96e03abcf3dc88d0a6aaa7 (mature)
+ 7. 412.50000000 BTM f76003ff07c92f5ddd1806d51e94c847d60b35b26da1479976fcb1d85aac3358 (mature)
+ 8. 412.50000000 BTM f73565bdcc54eb749599ef1ed5118381f133b10bce6cb8fcd863a9a5b133689d (mature)
+ 9. 412.50000000 BTM f7220f8e8f6d80bf75a6bd5af3a7f24831ff806816928d82c1df498b2cc55136 (mature)
+ 10. 412.50000000 BTM f6a21da21b50bed465a12f0028e01870866e2a5ddea047588b8d7f60e97c7eca (mature)
+ 11. 412.50000000 BTM f6933473a9ee26cb48a5ff0c4fa2c3c3464290ee5294accef7efb315a8d7efa0 (mature)
+ 12. 412.50000000 BTM f4372863e40dcb0092e9d4c358b7875aae57c5b41a76b88cb8e5b0c4115a8edb (mature)
+ 13. 412.50000000 BTM f3f07faef598e6934e1bbd5b842dd4fa91961b8efc2f782331429a423d0c2fc8 (mature)
+ 14. 412.50000000 BTM f355cb331def6878b118d9c99c2d915005c399c6c7af2d430da7fc4d3d495f3d (mature)
+ 15. 412.50000000 BTM f26d274a4374b06a1fba02a33d282cea223435c215239236ad99065007697457 (mature)
+ 16. 412.50000000 BTM f0be46800428a4f3dec4c167f8593a9ca0e791b08bb07b142040b3c5ac8a33a5 (mature)
+ 17. 412.50000000 BTM ef95348ac7e42d47af6853666bcb356b3b1a13fa1af02cb9d156cbcd18d3a519 (mature)
+ 18. 412.50000000 BTM ef664a0594ad4009c067a9b2556f6297f0c2deaba0d1226f782864480d0bd43e (mature)
+ 19. 412.50000000 BTM ef0c18355207ee87645bf43ccceca5ce4060c677b88cf5cff9dd49dd684a9e15 (mature)
+ 20. 412.50000000 BTM ec27d6edef04156433d7e888e5c9afddfdb04f0f737044f8ede0202ff5e71368 (mature)
+ 21. 412.50000000 BTM ec1ef67d64d0d35d2937fd810acd23ca30f0a54a9aa85d46d207f07a117108a1 (mature)
+total size of available utxos is 395
+To merge 10 UTXOs with 4037.50000000 BTM
+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.
+You will transfer BTM to an address, please check this python code and DO IT later.
+Confirm [y/N] y
+fba70efdbdfc4901adaea5ffb56c1c79b9d6bed3e38ac9946c7925d00bc4b7f3
+```
\ No newline at end of file
resp_json = json.loads(response.text)
if resp_json['status'] == 'success':
- data = resp_json['data']
- raw_transaction = data['transaction']['raw_transaction']
- return raw_transaction
+ return resp_json['data']
elif resp_json['status'] == 'fail':
return resp_json['msg']
else:
--- /dev/null
+import json
+
+
+class UnspentOutputs(object):
+
+ @staticmethod
+ def get_block_height(connection):
+ response = connection.request("/get-block-count")
+
+ resp_json = json.loads(response.text)
+
+ if resp_json['status'] == 'success':
+ return resp_json['data']['block_count'], 1
+ elif resp_json['status'] == 'fail':
+ return resp_json['msg'], -1
+ else:
+ return resp_json, 0
+
+ @staticmethod
+ def list_UTXO(connection):
+ response = connection.request("/list-unspent-outputs")
+
+ resp_json = json.loads(response.text)
+
+ if resp_json['status'] == 'success':
+ return resp_json['data'], 1
+ elif resp_json['status'] == 'fail':
+ return resp_json['msg'], -1
+ else:
+ return resp_json, 0
--- /dev/null
+import argparse
+import getpass
+import json
+import os
+import time
+
+from Account import Account
+from Transaction import Action, Transaction
+from UnspentOutputs import UnspentOutputs
+from connection import Connection
+
+parser = argparse.ArgumentParser(description='Bytom merge utxo tool')
+parser.add_argument('-o', '--url', default='http://127.0.0.1:9888', dest='url', help='API url to connect')
+parser.add_argument('-a', '--account', default=None, dest='account_alias', help='account alias')
+parser.add_argument('-p', '--pass', default=None, dest='password', help='account password')
+parser.add_argument('-x', '--max', default=41250000000, type=int, dest='max_amount', help='range lower than max_amount')
+parser.add_argument('-s', '--min', default=1, type=int, dest='min_amount', help='range higher than min_amount')
+parser.add_argument('-l', '--list', action='store_true', dest='only_list', help='Show UTXO list without merge')
+parser.add_argument('-m', '--merge', default=None, type=int, dest='merge_list', help='UTXO to merge')
+parser.add_argument('-y', '--yes', action='store_true', default=None, dest='confirm', help='confirm transfer')
+
+
+class BytomException(Exception):
+ pass
+
+
+class JSONRPCException(Exception):
+ pass
+
+
+def list_utxo(connection, account_alias, min_amount, max_amount):
+ mature_utxos = []
+ data, ret = UnspentOutputs.list_UTXO(connection=Connection(connection))
+ block_height, ret_code = UnspentOutputs.get_block_height(connection=Connection(connection))
+ if ret == 1 and ret_code == 1:
+ for utxo in data:
+ # append mature utxo to set
+ if utxo['valid_height'] < block_height \
+ and utxo['account_alias'] == account_alias \
+ and utxo['asset_alias'] == 'BTM':
+ mature_utxos.append(utxo)
+ elif ret == -1:
+ raise BytomException(data)
+
+ result = []
+ for utxo in mature_utxos:
+ if utxo['amount'] <= max_amount and utxo['amount'] >= min_amount:
+ result.append(utxo)
+
+ return result
+
+
+def send_tx(connection, utxo_list, to_address, password):
+ gas_amount = 40000000
+ actions = []
+ amount = 0
+ asset_id = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
+
+ for utxo in utxo_list:
+ actions.append(Action.unspent_output(output_id=utxo['id']))
+ amount += utxo['amount']
+
+ # consider gas amount
+ if amount > gas_amount:
+ amount = amount - gas_amount
+ else:
+ print('\nAttention: The amount of all utxos is too little, less than tx gas.')
+ os._exit(0)
+
+ actions.append(Action.control_address(amount=amount, asset_id=asset_id, address=to_address))
+
+ time.sleep(1)
+
+ transaction = Transaction.build_transaction(connection, actions)
+
+ signed_transaction = Transaction.sign_transaction(connection, password, transaction)
+
+ if signed_transaction['sign_complete']:
+ raw_transaction = signed_transaction['transaction']['raw_transaction']
+ result = Transaction.submit_transaction(connection, raw_transaction)
+ return result['tx_id']
+ else:
+ raise BytomException('Sign not complete')
+
+
+def main():
+ options = parser.parse_args()
+ utxolist = list_utxo(options.url, options.account_alias, options.min_amount, options.max_amount)
+ for i, utxo in enumerate(utxolist):
+ print('{:4}. {:13.8f} BTM {}{}'.format(i, utxo['amount'] / 1e8, utxo['id'], ' (mature)'))
+ if i >= 21:
+ break
+ print("total size of available utxos is {}".format(len(utxolist)))
+
+ if options.only_list:
+ return
+
+ merge_size = options.merge_list or input('Merge size of UTXOs (5, 13 or 20): ')
+ utxo_mergelist = []
+
+ for i in range(merge_size if merge_size <= len(utxolist) else len(utxolist)):
+ utxo_mergelist.append(utxolist[i])
+
+ if len(utxo_mergelist) < 2:
+ print('Not Merge UTXOs, Exit...')
+ return
+
+ print('To merge {} UTXOs with {:13.8f} BTM'.format(len(utxo_mergelist),
+ sum(utxo['amount'] for utxo in utxo_mergelist) / 1e8))
+
+ if not options.account_alias:
+ options.account_alias = input('Transfer account alias: ')
+
+ if not options.password:
+ options.password = getpass.getpass('Bytom Account Password: ')
+
+ print(
+ '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. ')
+ print('You will transfer BTM to an address, please check this python code and DO IT later.')
+
+ if not (options.confirm or input('Confirm [y/N] ').lower() == 'y'):
+ print('Not Merge UTXOs, Exit...')
+ return
+
+ to_address = Account.find_address_by_alias(Connection(options.url), options.account_alias)
+ if not to_address:
+ to_address = input('Transfer address: ')
+
+ print(send_tx(Connection(options.url), utxo_mergelist, to_address, options.password))
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+import unittest
+
+from Transaction import Transaction
+from connection import Connection
+from UnspentOutputs import UnspentOutputs
+
+
+class TestUTXOMethods(unittest.TestCase):
+
+ def test_get_block_height(self):
+ con = Connection("http://127.0.0.1:9888")
+ block_height, ret = UnspentOutputs.get_block_height(connection=con)
+ if ret == 1:
+ print(block_height)
+
+ def test_list_UTXO(self):
+ con = Connection("http://127.0.0.1:9888")
+ utxos, ret = UnspentOutputs.list_UTXO(con)
+ if ret == 1:
+ for utxo in utxos:
+ print(utxo)
+
+ if __name__ == '__main__':
+ unittest.main()