Skip to content

PYVSYSTEMS User Guide Specification (English)

Sheldon edited this page Oct 28, 2019 · 3 revisions

Start V Systems Python client

Step 1: Prepare

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

Step 2: Run

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)

Chain related statement

  • 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

Account related statement

  • 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()

Sample Code

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