-
Notifications
You must be signed in to change notification settings - Fork 8
PYVSYSTEMS User Guide Specification (English)
Install Python and PIP in your machine. Both Python 2.7 and 3 are supported. In this instruction, we just use Python 3.6 as example.
$ sudo apt-get install python3.6 python3-dev
$ curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
$ python3 get-pip.py
Download vsys python library source code from GitHub. And then install the required library.
$ git clone https://github.com/virtualeconomy/pyvsystems.git
$ pip3 install -r ./pyvsystems.git/requirements.txt
Create new screen and start up Python console
$ screen -S vsys-client
$ python3
Python 3.6.0
[Clang 9.1.0 (clang-902.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
First of all, import pyvsystems package and Account class
>>> import pyvsystems as pv
>>> from pyvsystems import Account
>>> from pyvsystems import Wrapper
>>> import datetime
>>> import base58
We could create chain object like this:
# (TestNet)
>>> chain = pv.testnet_chain()
or
# (MainNet)
>>> m_wrapper = pv.create_api_wrapper('http://<full node ip>:9922')
>>> chain = pv.Chain(chain_name='mainnet', chain_id='M', address_version=5, api_wrapper= m_wrapper)
- Look up current block height of the chain:
>>> chain.height()
643306
- Look up the last block info of the chain:
>>> chain.lastblock()
{u'SPOSConsensus': {u'mintTime': 1538055636000000000, u'mintBalance': 105251999961600}, u'fee': 0, u'resourcePricingData': {u'sequentialIO': 0, u'storage': 0, u'randomIO': 0, u'computation': 0, u'memory': 0}, u'reference': u'2THdUU3R7qCSs6Wq9VcD76EnZvVL7XjJsDNSLKteUDdFJmRLoJsGqNrkrDvopjhGeDvL84cqw1BTdKVY53scaaFZ', u'transactions': [{u'status': u'Success', u'type': 5, u'feeCharged': 0, u'amount': 900000000, u'timestamp': 1538055636000742434, u'currentBlockHeight': 643642, u'recipient': u'AUF4We2c4TAFr6KDsQy6WCwSYDx1VUih283', u'id': u'GfihGXgsf9Uu44uoGG2sp3yHktyGffxUcfijbA8CbBAB'}], u'timestamp': 1538055636000742434, u'generator': u'ATyLwDefhDnAaeaqtKhF3rWetkoYw1r4Gi6', u'signature': u'DAbYoJ4WJLZT25MC1ScFxwRv6Hv95TSKJ7XBsAzPzqvFaAij4Komx75zGTDakizZBQbmx9Nt2xHAAav6tdFFqZs', u'blocksize': 330, u'version': 1, u'TransactionMerkleRoot': u'4dBVqrH4hocfjNovDTWgLf2de42xVrxJjmNAXcqpWLeF', u'height': 643642, u'transaction count': 1}
- Look up a block info at n in the chain:
>>> n = 1
>>> chain.block(n)
{u'SPOSConsensus': {u'mintTime': 1535356440000000000, u'mintBalance': 0}, u'fee': 0, u'resourcePricingData': {u'sequentialIO': 0, u'storage': 0, u'randomIO': 0, u'computation': 0, u'memory': 0}, u'reference': u'67rpwLCuS5DGA8KGZXKsVQ7dnPb9goRLoKfgGbLfQg9WoLUgNY77E2jT11fem3coV9nAkguBACzrU1iyZM4B8roQ', u'transactions': [{u'recipient': u'ATxpELPa3yhE5h4XELxtPrW9TfXPrmYE7ze', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 0, u'amount': 300000000000000000, u'feeCharged': 0, u'signature': u'4N1NnwqJ6MTF46VeQLnQDSvDqxnYetqd6t8WMfKp1ZTwG1RhboS9KV1tZ21xy6QujpbrArsYfYamizg2GU5UUoNz', u'type': 1, u'id': u'4JqUudiHYHkwCPvdZDTw59qBJpqmQgvVxMzS7Bfc1EJZ'}, {u'recipient': u'ATtRykARbyJS1RwNsA6Rn1Um3S7FuVSovHK', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 1, u'amount': 200000000000000000, u'feeCharged': 0, u'signature': u'5B5awtvy7U62vGAjPyM1nGCytG9mKN3Map7EZuetpBSFL9VsVCRW787ZwMTJuDnFj2mkWWgMvfq18xyCLYPseEpM', u'type': 1, u'id': u'A7mgwn98VyzcEC2hmrgt9sRPbFfyBvs44rjGPu7dHxbH'}, {u'recipient': u'ATtchuwHVQmNTsRA8ba19juGK9m1gNsUS1V', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 2, u'amount': 150000000000000000, u'feeCharged': 0, u'signature': u'3t3Dh49RmvrEnG3nQvNFQN4bLN1Jvkw1Ffa2u1AXQdU8jetngagdD2n3pWmUeXWXbVRYSNgQX8My3WEGteQTUVDy', u'type': 1, u'id': u'ZdpAvtegbnPSBTyRDD2xTvbQ5LxhAX6jrykuG7hxugB'}, {u'recipient': u'AU4AoB2WzeXiJvgDhCZmr6B7uDqAzGymG3L', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 3, u'amount': 50000000000000000, u'feeCharged': 0, u'signature': u'33EftL6giA2xYBkx9x4R2xdd3ikcDBb3aw3mDQAHt7Y3QkoEvozKKsnqGGTYiRVut8sexSE3EuDYxyviXA6hhkzT', u'type': 1, u'id': u'257QZpUxYRiKVRVQr8FgzTngbzF6MW7fMWqyxB7wFawq'}, {u'recipient': u'AUBHchRBY4mVNktgCgJdGNcYbwvmzPKgBgN', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 4, u'amount': 60000000000000000, u'feeCharged': 0, u'signature': u'2Thag4Lo4ixcyw9p2p4dMHRwoqs4LJ3Jwkjdf9qNT34FNk7sKRAGFbNUezgCc9XfVw6obTn1m4zRjx4kRLCYyWcz', u'type': 1, u'id': u'5G27tFPDFBXSmS6muMiXMDc3VuAyoA4TtSWtgJaWrfY7'}, {u'recipient': u'AU6qstXoazCHDK5dmuCqEnnTWgTqRugHwzm', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 5, u'amount': 60000000000000000, u'feeCharged': 0, u'signature': u'z7MzwmKLJQJLMu8Z2ivFCTBTwNKYyfomdxN65cdyCwy6YsL7UbBFciQXUo7u1W5Uikwwx8dePPzYfKokxBfaJn5', u'type': 1, u'id': u'GnLWJrk8cs9p8MzasEiAPmH77atNuCo2U58Yk46EBqvi'}, {u'recipient': u'AU9HYFXuPZPbFVw8vmp7mFmHb7qiaMmgEYE', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 6, u'amount': 60000000000000000, u'feeCharged': 0, u'signature': u'2PPHXX9UjwFxFBujqE2bMzqEYTbJy1ydS6b5BywAymrgUXhpsWQHa84uUpEdbh9PcFSjehbbSvw8b9Yt74S1W58W', u'type': 1, u'id': u'DXWZgR3KPXYuHccAXY6uLSrW3VWVT3uNVKkjqWmfFPeV'}, {u'recipient': u'AUBLPMpHVV74fHQD8D6KosA76nusw4FqRr1', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 7, u'amount': 60000000000000000, u'feeCharged': 0, u'signature': u'bBJ6eTAUgMP8TbmafSwLUT3vFvNY955ddpYuByk6v8CFn6VzeuMUz11veM9bGAuwki3gTevDYf9N8SVPy5AhA8N', u'type': 1, u'id': u'8JEe9ibrSajZXpQxhxNzxPZ81RRbXSBy4EpRpophFCvz'}, {u'recipient': u'AUBbpPbymsrM8QiXqS3NU7CrD1vy1EyonCa', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 8, u'amount': 40000000000000000, u'feeCharged': 0, u'signature': u'5fbpe2GsLHaGEATiEZL7EgsfxGTAsbTRg14UzGkRcQiTpHhzxdqx1CJ31nVr4ndBgkkkKG1xc9waVBtBKjagDF59', u'type': 1, u'id': u'3XECBDJVne2SwSnct48LE3fM8FEYVWgpLx3iSfqh9ABx'}, {u'recipient': u'AU7nJLcT1mThXGTT1KDkoAtfPzc82Sgay1V', u'status': u'Success', u'fee': 0, u'timestamp': 1535356447650226656, u'slotId': 9, u'amount': 20000000000000000, u'feeCharged': 0, u'signature': u'59k1YWqXgermPUocbS6JtJMmkSBsWSiyJtiGQd7Artkgddns6WdSmUPYezymSXPo9gpEpgbRGEKsFsxRC16pwoXz', u'type': 1, u'id': u'FMJd2cyYasRPm6jWqGi3CuMhwE5dF7LiNQCXbKAsAhPi'}], u'timestamp': 1535356447650226656, u'generator': u'ATrBur3QznqYo8kouKPgDYki7AtbHEsmVid', u'signature': u'5n4ewwZh9F4MMpSvtdxLCu5MUKnhEyUth2w3zEfpuiX3vwS1STNCdi51fmowJuLT1CfFg1DuodSvxwBZDANvGNej', u'blocksize': 870, u'version': 1, u'TransactionMerkleRoot': u'85w9jZL7o6evWqt3qpy4JGC3144ekGcSfxVnznMBwUAP', u'height': 1, u'transaction count': 10}
- Get a transaction info by transacion id in the chain:
>>> tx_id = "EBuXG2y5yLa8j9QUaXK535sU7xUjokVuCkmgwFKAjJw6"
>>> resp = chain.tx(tx_id)
>>> resp
{u'status': u'Success', u'fee': 10000000, u'timestamp': 1536230749375858944, u'feeCharged': 10000000, u'feeScale': 100, u'height': 143985, u'amount': 100000000, u'attachment': u'', u'type': 2, u'proofs': [{u'publicKey': u'CbUPwcCJaMqYSjZGXy4LrkTfV2ncP27Chqyd2QKXfJxn', u'proofType': u'Curve25519', u'signature': u'64cyK8CdgaaNh199VghmrUieCF7XVQmiV5EmpwJaHRZ9mWbMBs87saPCrvrdWELBn9sqLpiJ5KicqBQMM77jYvpr'}], u'recipient': u'ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX', u'id': u'EBuXG2y5yLa8j9QUaXK535sU7xUjokVuCkmgwFKAjJw6'}
To show the transaction time from response,
>>> display_time = datetime.datetime.fromtimestamp(resp['timestamp'] // 1000000000)
>>> print("Time: {}".format(display_time))
Time: 2018-09-06 18:45:49
The sender address is hidden in public key in proofs. To show the sender from response,
>>> sender_public_key = base58.b58decode(resp['proofs'][0]['publicKey'])
>>> sender_address = ts_chain.public_key_to_address(sender_public_key)
>>> print("From: {}".format(sender_address))
AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q
To show the recipient from response,
>>> print("To: {}".format(resp['recipient']))
ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX
To show the amount from response,
>>> print("Amount: {}".format(resp['amount']))
Amount: 100000000
To show the transaction fee from response,
>>> print("Transaction fee: {}".format(resp['fee']))
Transaction fee: 10000000
- Validate an address of the chain:
>>> addr = "AUC63SmgmUNmie784Ld9CRjbecvPg2beShy"
>>> chain.validate_address(addr)
True
>>> addr = "An invalid address"
>>> chain.validate_address(addr)
False
- Create new account
>>> my_account = Account(chain=chain)
- Get account information
>>> my_account.get_info()
{u'available': 0, u'mintingAverage': 0, u'effective': 0, u'height': 643936, 'publicKey': '8PtidoeohqEyAyuqdBGudWfr7i1mUVrhNkQHNRfvaXvq', u'regular': 0, u'address': u'AU1vwC7C6CqV2q4oZYFNTbBJYNaakMBQyZV'}
The detail explain of reponse JSON is:
{
'regular': 0, # regular balance
'available': 0, # available balance (regular - leased out)
'effective': 0, # effective balance (regular - leases out + leased in)
'mintingAverage': 0, # for minter used
'height': 643936,
'publicKey': '8PtidoeohqEyAyuqdBGudWfr7i1mUVrhNkQHNRfvaXvq',
'address': 'AU1vwC7C6CqV2q4oZYFNTbBJYNaakMBQyZV'
}
Among of these balances, available
is final balance which user could be used.
For security cosideration, the private key will not be shown in geranal account info. To get account private key, please use:
>>> my_account.privateKey
'28XHDwLzKaXRLspTBuxqurrah6apEfbJGh6Nnt914u2T'
The account data is only stored in memory. It would be better to backup the private key into your database.
- Restore account by private key
>>> pk = "28XHDwLzKaXRLspTBuxqurrah6apEfbJGh6Nnt914u2T"
>>> restore_account = Account(chain=chain, private_key=pk)
- Check full node is active or not
To ensure the payment could be sent to the chain, we could use check_node()
method to check whether the full node is keeping update height. There is two ways to call this method.
The following method will check the full node is keeping update height or not. We suggest call this method in scheduled task.
>>> # Return True if the full node is OK. Otherwise, return False
>>> my_account.check_node()
True
The other method is check with reference full node. Not only check high, but also ensure no short-term fork. We suggest call this method before do important payment.
>>> # Return True if the full node is OK. Otherwise, return False
>>> my_account.check_node("http://<reference node ip>:9922")
False
- Send payment transaction
(The default transaction fee is 0.1 VSYS, which is minimum transaction fee. fee_scale should be 100.)
>>> # For example, the address of recipient is "AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8"
>>> recipient_address = "AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8"
>>> recipient = Account(chain=chain, address=recipient_address)
>>> # send payment (100000000 = 1 VSYS)
>>> resp = my_account.send_payment(recipient, amount=100000000)
>>> resp
{u'fee': 10000000, u'timestamp': 1538059747188733952, u'feeScale': 100, u'amount': 100000000, u'attachment': u'', u'type': 2, u'proofs': [{u'publicKey': u'B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV', u'proofType': u'Curve25519', u'signature': u'4ZR6FW68YbwPUnxt18hx4df7Nrdx5th3YruHR672JGw3U4simXgXGSNvxodDV8q7H4cm4XwZCkGDF9ic1aPiExMh'}], u'recipient': u'AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8', u'id': u'ABgT8xs3EtR9spKopMdSmsdCE5pZRoZEqdnKX3nmD9fv'}
To show the transaction time from response,
>>> display_time = datetime.datetime.fromtimestamp(resp['timestamp'] // 1000000000)
>>> print("Time: {}".format(display_time))
Time: 2018-09-27 22:49:07
The sender address is hidden in public key in proofs. To show the sender from response,
>>> sender_public_key = base58.b58decode(resp['proofs'][0]['publicKey'])
>>> sender_address = ts_chain.public_key_to_address(sender_public_key)
>>> print("From: {}".format(sender_address))
ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX
To show the recipient from response,
>>> print("To: {}".format(resp['recipient']))
AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8
To show the amount from response,
>>> print("Amount: {}".format(resp['amount']))
Amount: 100000000
To show the transaction fee from response,
>>> print("Transaction fee: {}".format(resp['fee']))
Transaction fee: 10000000
- Confirm the transaction in Chain
Call check_tx
method with transaction id to confirm. The confirmations
should set to M * super_node_num + 1 (M = max accept waiting minutes since transaction sent). As the short-term fork case would be happened, some blocks have chance to be rolled back. So the greater confirmations you set, the less false confirm in chain case will be occurred. Currently, super node num is 15. So we suggest the confirmations
set more than 15. For example, if you could wait 2 minutes to confirm a transaction, which just sent, in chain, then confirmations should be 2*15+1=31:
>>> tx_id = "dE4s1joxLqP1sSVRBB1KmEfB6a3vND2k3Uwsao12paG"
>>> my_account.check_tx(tx_id, 31)
True
- Get transation history
>>> # For example, we get last 10 transations and show payment transations only:
>>> limit = 10
>>> tx_filter = pv.setting.PAYMENT_TX_TYPE
>>> my_account.get_tx_history(limit, tx_filter)
[{u'status': u'Success', u'fee': 10000000, u'timestamp': 1538059747188733952, u'feeCharged': 10000000, u'feeScale': 100, u'amount': 100000000, u'attachment': u'', u'type': 2, u'proofs': [{u'publicKey': u'B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV', u'proofType': u'Curve25519', u'signature': u'4ZR6FW68YbwPUnxt18hx4df7Nrdx5th3YruHR672JGw3U4simXgXGSNvxodDV8q7H4cm4XwZCkGDF9ic1aPiExMh'}], u'recipient': u'AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8', u'id': u'ABgT8xs3EtR9spKopMdSmsdCE5pZRoZEqdnKX3nmD9fv'}, {u'status': u'Success', u'fee': 10000000, u'timestamp': 1536663958559000000, u'feeCharged': 10000000, u'feeScale': 100, u'amount': 210000000000000, u'attachment': u'', u'type': 2, u'proofs': [{u'publicKey': u'B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV', u'proofType': u'Curve25519', u'signature': u'4VkfFztUQUPBTcKWBLrwcPfSgtHnAuHYYyWSMfVAu2Z1WZyWE2i5VPBghCkJCJCacqHjALKHr8qRGD9PW6WTBZU'}], u'recipient': u'AU7nJLcT1mThXGTT1KDkoAtfPzc82Sgay1V', u'id': u'Mv2WZyurYtKAMhdFazvPZy2Nt8NapmHuwvAR8KKrbog'}]
To exit the Python console, press Ctrl + D
or type "exit()"
>>> exit()
import base58
import datetime
import pyvsystems as pv
from pyvsystems import Account
from pyvsystems.error import *
def test_payment():
# set chain
ts_chain = pv.testnet_chain()
# get block height
try:
height = ts_chain.height()
print("The current block height of the chain: {}".format(height))
except NetworkException as ex:
# Handle Network issue here
print("Failed to get block height: {}".format(ex))
return False
# create / restore account
try:
# retrieve account by private key
private_key = "XXXXXXXXXX"
my_account = Account(chain=ts_chain, private_key=private_key)
# create recipient with address
recipient_address = "XXXXXXXXXXXXXXXXX"
recipient = Account(chain=ts_chain, address=recipient_address)
except InvalidParameterException as ex:
# Handle Invalid Parameter issue here
print("Invalid Parameter: {}".format(ex))
return False
except InvalidAddressException as ex:
# Handle Invalid Address issue here
print("Invalid Address: {}".format(ex))
return False
# send payment (100000000 = 1 VSYS)
try:
print("========Do payment transaction===========")
resp = my_account.send_payment(recipient, amount=100000000)
print("Payment TX result: {}".format(resp))
print("Transaction ID: {}".format(resp['id']))
display_time = datetime.datetime.fromtimestamp(resp['timestamp'] // 1000000000)
print("Time: {}".format(display_time))
sender_public_key = base58.b58decode(resp['proofs'][0]['publicKey'])
sender_address = ts_chain.public_key_to_address(sender_public_key)
print("From: {}".format(sender_address))
print("To: {}".format(resp['recipient']))
print("Amount: {}".format(resp['amount']))
print("Transaction fee: {}".format(resp['fee']))
except InvalidParameterException as ex:
# Handle Invalid Parameter issue here
print("Invalid Parameter: {}".format(ex))
return False
except MissingPrivateKeyException:
# Handle Missing Private Key issue here
print("No private key for `my_account`")
return False
except InvalidAddressException as ex:
# Handle Invalid Address issue here
print("Invalid Address: {}".format(ex))
return False
except InsufficientBalanceException:
# Handle Insufficient Balance issue here
print("Insufficient Balance.")
return False
except NetworkException as ex:
# Handle Network issue here
print("Failed to get HTTP response: {}".format(ex))
return False
# check payment history
try:
print("========Check payment history===========")
history = my_account.get_tx_history(10)
print("Payment history: {}".format(history))
for record in history:
if not record.get('proofs'):
continue
sender_public_key = base58.b58decode(record['proofs'][0]['publicKey'])
sender_address = ts_chain.public_key_to_address(sender_public_key)
if sender_address == my_account.address:
print("####### Send #######")
else:
print("####### Received #######")
print("From: {}".format(sender_address))
print("To: {}".format(record['recipient']))
print("Transaction ID: {}".format(record['id']))
display_time = datetime.datetime.fromtimestamp(record['timestamp'] // 1000000000)
print("Time: {}".format(display_time))
print("Amount: {}".format(record['amount']))
print("Transaction fee: {}".format(record['fee']))
except InvalidParameterException as ex:
# Handle Invalid Parameter issue here
print("Invalid Parameter: {}".format(ex))
return False
except MissingAddressException:
# Handle Address issue here
print("No address for `my_account`")
return False
except NetworkException as ex:
# Handle Network issue here
print("Failed to get HTTP response: {}".format(ex))
return False
return True
if __name__ == "__main__":
test_payment()
To run it, save above python script to your disk, for example, "sample.py". Finally, execute the command
$ python3 sample.py