Kin SDK for Python
The Kin SDK for Python is meant to be used as a back-end service. It can perform actions for your client apps (iOS, Android, etc.) and also operate as a server for you to build services on top of the Kin blockchain. The SDK can for example take care of communicating with the Kin Blockchain on behalf of the client to create accounts and whitelist transactions. It can also monitor blockchain transactions so that you can implement broader services. It's up to you how to integrate the SDK in your overall architecture and managing server up-time.
Requirements.
Make sure you have Python 3 >= 3.4
Installation
pip install kin-sdk
Track the development of this SDK on GitHub.
Overview
In this introduction we will look at a few basic operations on the Kin Blockchain and some features that are exclusive to the Kin SDK for Python.
You will find:
- Accessing the Kin blockchain
- Managing Kin accounts
- Executing transactions against Kin accounts
- Monitoring Kin Payments (Python SDK can monitor all accounts)
- Channels (unique to the Python SDK)
Accessing the Kin blockchain
The SDK has two main components, KinClient
and KinAccount
.
KinClient - Is used to query the blockchain and perform actions that don't require authentication (e.g get an account balance)
KinAccount - Is used to perform authenticated actions on the blockchain (e.g Send payment)
To initialize the Kin Client you will need to provide an environment (Test and Production environments are pre-configured)
from kin import KinClient, TEST_ENVIRONMENT
client = KinClient(TEST_ENVIRONMENT)
Or you can configure a custom environment with your own parameters:
from kin import Environment
MY_CUSTOM_ENVIRONMENT = Environemnt('name','horizon endpoint','network passphrase','friendbot url'(optional))
Once you have a KinClient, you can use it to get a KinAccount object and its associated keypair. The Kin SDK for Python generates a keypair based on a secret seed
. There is a unique relationship between seed and keypair; if you save a secret seed you can regenerate the associated keypair.
The KinAccount object can be initialized in two ways:
# With a single seed:
account = client.kin_account('seed')
# With channels:
account = client.kin_account('seed', channel_secret_keys=['seed1','seed2','seed3'...])
# Additionally, a unique app-id can be provided, this will be added to all your transactions
account = client.kin_account('seed',app_id='unique_app_id')
Read more about channels in the "Channels" section
See Going live with Kin learn more about what an appID is and how to get it.
Checking configuration
The handy get_config
method will return some parameters the client was configured with, along with Horizon status:
# Disclaimer: the below JSON is outdated
status = client.get_config()
print status
{
"horizon": {
"uri": "https://horizon-playground.kininfrastructure.com",
"online": true,
"error": null
},
"sdk_version": "2.0.0",
"environment": "PLAYGROUND",
"kin_asset": {
"code": "KIN",
"issuer": "GBC3SG6NGTSZ2OMH3FFGB7UVRQWILW367U4GSOOF4TFSZONV42UJXUH7"
},
"transport": {
"pool_size": 10,
"request_timeout": 11,
"backoff_factor": 0.5,
"num_retries": 5,
"retry_statuses": [
503,
413,
429,
504
]
}
}
sdk_version
- the version of this SDK.address
- the SDK wallet address.kin_asset
- the KIN asset the SDK was configured with.environment
- the environment the SDK was configured with (TEST/PROD/CUSTOM).horizon
:uri
- the endpoint URI of the Horizon server.online
- Horizon online status.error
- Horizon error (when notonline
) .
transport
:pool_size
- number of pooled connections to Horizon.num_retries
- number of retries on failed request.request_timeout
- single request timeout.retry_statuses
- a list of statuses to retry on.backoff_factor
- a backoff factor to apply between retry attempts.
Managing Kin accounts
Most methods provided by the KinClient to query the blockchain about a specific account, can also be used from the KinAccount object to query the blockchain about itself
Creating and retrieving a Kin account
The very first thing we need to do before you can send or receive Kin is creating an account on the blockchain. This is how you do it:
# the KIN amount can be specified in numbers or as a string
tx_hash = account.create_account('address', starting_balance=1000, fee=100)
# a text memo can also be added; memos cannot exceed 21 characters:
tx_hash = account.create_account('address', starting_balance=1000, fee=100, memo_text='My first account')
Account Details
Each account on the Kin blockchain has a public address. The address is identical to the public portion of the keypair created during account creation.
address = account.get_public_address()
Checking if an account exists on the blockchain
There is one thing you can do even without an account, it's checking if an account already existing on the blockchain.
client.does_account_exists('address')
Account balance and data
Now that you have an account you can check its balance.
balance = client.get_account_balance('address')
There is of course a lot more about an account besides its balance. You can get that information with get_account_data
.
account_data = client.get_account_data('address')
The output will look something like this:
{'_data':
{
'id': 'GDNGBE7S3ZHXAUAGSMVDNUM2FRTIRNDT3QHRMR5CVPI4YYSLL5ZUM2ME',
'account_id': 'GDNGBE7S3ZHXAUAGSMVDNUM2FRTIRNDT3QHRMR5CVPI4YYSLL5ZUM2ME',
'sequence': '15167341998374912',
'data': {},
'thresholds': _data='{
'low_threshold': 0,
'med_threshold': 0,
'high_threshold': 0
}',
'balances': [ _data='{
asset_type': 'native',
'asset_code': None,
'asset_issuer': None,
'balance': 100.0,
'limit': None
}'],
'flags': _data='{
'auth_required': False,
'auth_revocable': False
}',
'paging_token': '',
'subentry_count': 0,
'signers': [ _data='{
'public_key': 'GDNGBE7S3ZHXAUAGSMVDNUM2FRTIRNDT3QHRMR5CVPI4YYSLL5ZUM2ME',
'key': 'GDNGBE7S3ZHXAUAGSMVDNUM2FRTIRNDT3QHRMR5CVPI4YYSLL5ZUM2ME',
'weight': 1,
'signature_type':
'ed25519_public_key'
}']
}
}
Get account status
Often times you will want to know the status of an account, you can do this easily with get_status
. The function expects a single parameter, boolean, if set to True
all channels and statuses will be printed.
account.get_status(True)
{
"client": {
"sdk_version": "2.2.0",
"environment": "LOCAL",
"horizon": {
"uri": "http://localhost:8000",
"online": true,
"error": null
},
"transport": {
"pool_size": 10,
"num_retries": 5,
"request_timeout": 11,
"retry_statuses": [
503,
413,
429,
504
],
"backoff_factor": 0.5
}
},
"account": {
"app_id": "anon",
"public_address": "GCLBBAIDP34M4JACPQJUYNSPZCQK7IRHV7ETKV6U53JPYYUIIVDVJJFQ",
"balance": 9999989999199.979,
"channels": {
"total_channels": 5,
"free_channels": 4,
"non_free_channels": 1,
"channels": {
"SBS3O5BGCPDIYWTTOV7TGLXFRPFSD6ACBEAEHJUMMPF5DUDF732MX6LL": "free",
"SC65CIJCAWJEJX5IVHDJK6FO6DM5BVPIUX5F7EULIC3C4PF7KTAUHHE2": "free",
"SABWFQ2HOYPQGCWN7INIV2RNZZLAZDOX67R3VHMGQAFF6FA3JIA2E7BB": "free",
"SBBQJTYF6K2TDUJ2LBUSXICUEEX75RXAQZRP6LLVF3JDXK5D4SVYX3X4": "taken",
"SCD36QIV3SFEGZDHRZZXO7MICNMOHSRAOV6L2MQKSW4TO4OTCR4IF2FD": "free"
}
}
}
}
Keypairs
Earlier we talked about the relationship between keypairs and secret seeds. Here are a few associated functions.
Creating a new keypair
from kin import Keypair
my_keypair = Keypair()
# Or, you can create a keypair from an existing seed
my_keypair = Keypair('seed')
Getting the public address from a seed
public_address = Keypair.address_from_seed('seed')
Generate a new random seed
seed = Keypair.generate_seed()
Generate a deterministic seed
# Given the same seed and salt, the same seed will always be generated
seed = Keypair.generate_hd_seed('seed','salt')
Generate a mnemonic seed:
Not implemented yet
Transactions
Transactions are executed on the Kin blockchain in a two-step process.
- Build the transaction, including calculation of the transaction hash. The hash is used as a transaction ID and is necessary to query the status of the transaction.
- Send the transaction to servers for execution on the blockchain.
Transferring Kin to another account
To transfer Kin to another account, you need the public address of the account to which you want to transfer Kin.
By default, your user will need to spend Fee to transfer Kin or process any other blockchain transaction. Fee for individual transactions are trivial 1 Kin = 10E5 Fee.
Some apps can be added to the Kin whitelist, a set of pre-approved apps whose users will not be charged Fee to execute transactions. If your app is in the whitelist then refer to transferring Kin to another account using whitelist service.
The snippet Transfer Kin will transfer 20 Kin to the recipient account "GDIRGGTBE3H4CUIHNIFZGUECGFQ5MBGIZTPWGUHPIEVOOHFHSCAGMEHO".
In most cases you will want to prepare your parameters ahead of time and execute a transaction in one line, here's how to do it:
# the KIN amount can be specified in numbers or as a string
tx_hash = account.send_kin('destination', 20, fee=100, memo_text='Thank you Kin')
If for some reason you need to split the process in multiple steps you can first build the transaction, update any parameters and then execute. Although this is possible in most cases you will not want to do it this way.
Step 1: Build the transaction
destination = 'GDIRGGTBE3H4CUIHNIFZGUECGFQ5MBGIZTPWGUHPIEVOOHFHSCAGMEHO'
builder = account.build_send_kin(destination, 1000, fee=100, memo_text='tx in 3-steps')
Step 2: Update the transaction
# Configure additional parameters
with account.channel_manager.get_channel() as channel:
builder.set_channel(channel)
builder.sign(channel)
# If you used additional channels apart from your main account,
# sign with your main account
builder.sign(account.keypair.secret_seed)
Step 3: Send the transaction
tx_hash = account.submit_transaction(builder)
Transferring Kin to another account using whitelist service
The Kin Blockchain also allows for transactions to be executed with no fee. Apps and services must first be approved, to learn more see Going live with Kin. If your service has been added to the whitelist you will be able to whitelist transactions for your clients.
Clients will send an http request to your Python app containing their transaction, you can then whitelist it and return it to the client to send to the blockchain.
whitelisted_tx = account.whitelist_transaction(client_transaction)
Please note that if you are whitelisted, any payment sent from you (an app developed with the Python SDK) is already considered whitelisted, so there is no need for this step for the server transactions.
Decode_transaction
When clients send you transactions for whitelisting they will be encoded. You can use decode_transaction
to read and then verify the contents.
from kin import decode_transaction
decoded_tx = decode_transaction(encoded_tx)
Getting the minimum acceptable fee from the blockchain
Transactions usually require a fee to be processed. To know what is the minimum fee that the blockchain will accept, use:
minimum_fee = client.get_minimum_fee()
Getting Transaction Data
Often times you will want to review a transaction, get_transaction_data
is here to help you.
The function is pretty simple and expects a transaction hash and a second parameter called simple
:
- True will return the object
kin.RawTransaction
, this is good for debugging and testing, but not for user messages - False will return the object
kin.SimpleTransaction
, this is likely OK to be formatted and showed to end users. Notice: if the transaction if too complex to be simplified, aCantSimplifyError
will be raised
tx_data = sdk.get_transaction_data(tx_hash, True)
Friendbot
If a friendbot endpoint is provided when creating the environment (it is provided with the TEST_ENVIRONMENT), you will be able to use the friendbot method to call a service that will create an account for you
client.friendbot('address')
Monitoring Kin Payments
These methods can be used to monitor the Kin payment that an account or multiple accounts are sending or receiving.
SDKs designed for client apps such as iOS and Android can monitor the accounts associated with their local users, the Python SDK can monitor other users' accounts. This is currently unique the the Python SDK. Currently, due to a bug on the blockchain frontend, the monitor may also return 1 tx that happened before the monitoring request
The monitor will run in a background thread (accessible via monitor.thread
), and will call the callback function every time it finds a kin payment for the given address.
Monitor a single account
Monitoring a single account will continuously get data about this account from the blockchain and filter it.
def callback_fn(address, tx_data, monitor)
print ('Found tx: {} for address: {}'.format(address,tx_data.id))
monitor = client.monitor_account_payments('address', callback_fn)
Monitor multiple accounts
It is possible to monitor multiple accounts using monitor_accounts_payments
, the function will continuously get data about all accounts on the blockchain, and will filter for the selected accounts.
def callback_fn(address, tx_data, monitor)
print ('Found tx: {} for address: {}'.format(address,tx_data.id))
monitor = client.monitor_accounts_payments(['address1','address2'], callback_fn)
You can freely add or remove accounts to this monitor
monitor.add_address('address3')
monitor.remove_address('address1')
Stopping a monitor
When you are done monitoring, make sure to stop the monitor, to terminate the thread and the connection to the blockchain.
monitor.stop()
Channels
The Kin Blockchain is based on the the Stellar blockchain. One of the most sensitive points in Stellar is transaction sequence. In order for a transaction to be submitted successfully, this number should be correct. However, if you have several SDK instances, each working with the same wallet account or channel accounts, sequence collisions will occur.
We highly recommend to keep only one KinAccount instance in your application, having unique channel accounts. Depending on the nature of your application, here are our recommendations:
You have a simple (command line) script that sends transactions on demand or only once in a while. In this case, the SDK can be instantiated with only the wallet key, the channel accounts are not necessary.
You have a single application server that should handle a stream of concurrent transactions. In this case, you need to make sure that only a single instance of a KinAccount initialized with multiple channel accounts. This is an important point, because if you use a standard
gunicorn/Flask
setup for example, gunicorn will spawn several worker processes, each containing your Flask application, each containing your KinAccount instance, so multiple KinAccount instances will exist, having the same channel accounts. The solution is to use gunicorn thread workers instead of process workers, for example run gunicorn with--threads
switch instead of--workers
switch, so that only one Flask application is created, containing a single KinAccount instance.You have a number of load-balanced application servers. Here, each application server should a) have the setup outlined above, and b) have its own channel accounts. This way, you ensure you will not have any collisions in your transaction sequences.
Creating Channels
The kin sdk allows you to create HD (highly deterministic) channels based on your seed and a passphrase to be used as a salt. As long as you use the same seed and passphrase, you will always get the same seeds.
import kin.utils
channels = utils.create_channels(master_seed, environment, amount, starting_balance, salt)
channels
will be a list of seeds the sdk created for you, that can be used when initializing the KinAccount object.
If you just wish to get the list of the channels generated from your seed + passphrase combination without creating them
channels = utils.get_hd_channels(master_seed, salt, amount)
License
The code is currently released under MIT license.