-
Notifications
You must be signed in to change notification settings - Fork 45
Instructions for Exchanges
The purpose of this document is to describe how to integrate the V Systems (VSYS) blockchain into your exchange step-by-step.
General VPS:
- 2 CPU
- 8GB of RAM
- 512GB HDD
AWS:
- m5d.large
Install a v.systems full node according to the instructions here
Set your configuration file, which you can find at /etc/vsys/vsys.conf
. You can edit it with nano or vi or vim or even emacs!
# V Systems node settings
vsys {
# Path Settings
directory = <system data & wallet data folder path>
data-directory = <block data folder path>
# Application logging level. Could be DEBUG | INFO | WARN | ERROR. Default value is INFO.
logging-level = INFO
# P2P Network settings
network {
known-peers = ["<peer ip>:<peer port>"]
black-list-residence-time = 30s
peers-broadcast-interval = 5s
connection-timeout = 30s
# Network address to bind to
bind-address = "0.0.0.0"
# Node name to send during handshake. Comment this string out to set random node name.
# node-name = "My MAINNET node"
# String with IP address and port to send as external address during handshake. Could be set automatically if uPnP is enabled.
declared-address = "localhost:9921"
}
# Wallet settings
wallet {
# Password to protect wallet file
password = ""
# Wallet seed as string
# seed = ""
}
# Blockchain settings
blockchain.type = TESTNET # Should be TESTNET or MAINNET
# Matcher settings
matcher.enable = no
# Minter settings
miner {
enable = yes
offline = no
quorum = 1
generation-delay = 1s
interval-after-last-block-then-generation-is-allowed = 120h
tf-like-scheduling = no
# Left to empty as default to minter address
reward-address = ""
}
# Node's REST API settings
rest-api {
# Enable/disable node's REST API
enable = yes
# Network address to bind to
bind-address = "0.0.0.0"
# Hash of API key string
api-key-hash = "Fo8fR7J1gB3k2WoaE6gYKMwgWfoh9EtZtXAMBxYYCJWG"
}
checkpoints.public-key = "A9MX22tXpNdTTx5wBf3CunZz299c1nnca6kH3MzL312L"
}
-
The directory and data-directory should be set to your own path. For data-directory, we suggest you mount a large disk and set the directory to this disk.
-
It is better to choose more than 3 peers to fill the known-peers field. You could check known-peers via default config file (Testnet Config, Mainnet Config). Some known-peers for current reference:
# For TestNet known-peers = ["gabija.vos.systems:9923","klymena.vos.systems:9923","vakarine.vos.systems:9923"] # For MainNet (contact us to get more known peers) known-peers = ["klymena.vos.systems:9921","gabija.vos.systems:9921","zemyna.vos.systems:9921","austeja.vos.systems:9921","vakarine.vos.systems:9921","13.55.174.115:9921","13.113.98.91:9921","3.121.94.10:9921"]
-
The blockchain.type should be filled with TESTNET or MAINNET.
-
To start the node with your chosen settings do a:
systemctl restart vsys
Security warning: Every full node provides RESTful API for interaction with chain. The RESTful API service will use port 9922. For security reason, we suggest the exchange modify firewall rule and not open 9922 in public network, only for internal network using. To make communication among the nodes easy and smooth, please keep port 9921 (mainnet) and 9923 (testnet) opening in public.
You could use the following method to call APIs.
We strongly suggest you use SDK to do integration. Currently, we provide the following version of SDK:
Python version SDK pyvsystems. The pyvsystems specification is here. Sample code refers here.
JavaScript version SDK js-v-sdk
Java version SDK java-v-sdk
C# version SDK cs-v-sdk
Golang version SDK go-v-sdk
You could open http://<full node ip>:9922
in browser to find all APIs you can use.
Install curl for post http request if you do not have curl.
$ sudo apt-get install curl
To test connection, you could use
$ curl -X GET 'http://<full node ip>:9922/blocks/height'
If success, you will get more or less like the following response:
{
"height": 2400326
}
Use HTTP POST to call /addresses API.
$ curl -X POST --header 'Accept: application/json' --header 'api_key: <full node api key>' 'http://<full node ip>:9922/addresses'
If wallet is created, you will get address of the wallet in response:
{
"address": "AUBBmrf5cuBf9XrSaX98mxWmcNBwULqQhQK"
}
The created wallets will be stored in <data path>/wallet/wallet.dat
. Remember to backup the file in schedule.
And you can recover a wallet from seed as well. To find the seed of wallet, use HTTP GET to call /addresses/seed/{address} with node API key. For example,
$ curl -X GET --header 'Accept: application/json' --header 'api_key: <full node api key>' 'http://<full node ip>:9922/addresses/seed/AU9KCwJm6mG9YxSb3LdjVi6LDwRGey1knfy'
Response:
{
"address": "AU9KCwJm6mG9YxSb3LdjVi6LDwRGey1knfy",
"seed": "GY7T8WpppuficZJs9CnEuntLkk4vXw7qkZ1SMtZ3qAas"
}
Once you known the seed, you can recover the wallet by wallet-generator
Use HTTP GET calling /addresses API to get all addresses:
$ curl -X GET 'http://<full node ip>:9922/addresses'
Response:
[
"ATy98tPdobDBKA35n5CJed6u3AmxKLT3TTV",
"ATys7iafCN4xHz9bJyKm4JNfKpk9f1uBBXT",
"AU1TFgjs3g1NMkXW5CGTGj96t8qijs6ScrP",
"ATubqbssJKtPfyebkmct4jv9YSue8xrhMLa",
"AU9KCwJm6mG9YxSb3LdjVi6LDwRGey1knfy",
"ATwMEAGfNhbRCRSApro8HWG2L65HMMa42KP",
"AU4rMtj3zEJesVQ94Az8ajncNMgtK6uzeHB",
"ATtdkLaHDPZQx3LsUfrKisRcrMkvfg18LGa",
"ATxa6h87rBrNYKDCkagageiPzTMFgcGmefA",
"AU8R9ri7eG968zuJuLQVLMiUzRNXvQwNPwE"
]
As more and more wallets created, it is better to query the wallet address with number limit. You can use HTTP GET /addresses/seq/{from}/{to} to get any range of addresses. For example:
$ curl -X GET 'http://<full node ip>:19922/addresses/seq/5/10'
Response:
[
"ATwMEAGfNhbRCRSApro8HWG2L65HMMa42KP",
"AU4rMtj3zEJesVQ94Az8ajncNMgtK6uzeHB",
"ATtdkLaHDPZQx3LsUfrKisRcrMkvfg18LGa",
"ATxa6h87rBrNYKDCkagageiPzTMFgcGmefA",
"AU8R9ri7eG968zuJuLQVLMiUzRNXvQwNPwE"
]
To check balance of address, use HTTP GET to call /addresses/balance/details/{address}
$ curl -X GET 'http://<full node ip>:9922/addresses/balance/details/AU8R9ri7eG968zuJuLQVLMiUzRNXvQwNPwE'
Response:
{
'address': 'AU8R9ri7eG968zuJuLQVLMiUzRNXvQwNPwE',
'regular': 109010000000, # regular balance
'available': 108910000000, # available balance (regular - leased out)
'effective': 108910000000, # effective balance (regular - leases out + leased in)
'mintingAverage': 108909964800, # for minter used
'height': 643936
}
The available
balance is the actual balance which wallet currently could use (100000000 = 1 VSYS).
Use HTTP POST to call /vsys/payment API.
$ curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'api_key: <full node api key>' -d '{ \
"amount": 100000000, \
"fee": 10000000, \
"feeScale": 100, \
"sender": "ATtRykARbyJS1RwNsA6Rn1Um3S7FuVSovHK", \
"attachment": "", \
"recipient": "ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX" \
}' 'http://<full node ip>:9922/vsys/payment'
In request,
amount
is the payment amount. 100000000 = 1 VSYS.
fee
is the transaction fee. Currently it is constant value 10000000(0.1 VSYS).
feeScale
currently is a fixed value. It should be 100.
sender
is the sender wallet address.
recipient
is the recipient wallet address.
attachment
could be any text (max 140 characters) with base58 encoding.
Response:
{
"type": 2,
"id": "EoNQyNouEKg8pDcEEPY2dJL9FMQx61YFk1Sn5EJN8H7K",
"fee": 10000000,
"timestamp": 1544083814291691000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "3orvgyRKf45FRyiCkcA3CzAGDvyEpBpXZzYGEGZnpZK5",
"signature": "t1X2zmw5a2b9iaLgtsHyHKgEmKo6GCFuMFQsZNqj8ZkzpVbRKhWUttqUDfcjzcn5w7VgVVvf8cetr1mh2d2xypQ"
}
],
"recipient": "ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX",
"feeScale": 100,
"amount": 100000000,
"attachment": ""
}
Use HTTP GET to call /transactions/address/{address}/limit/{limit} API. For example,
$ curl -X GET --header 'Accept: application/json' 'http://<full node ip>:9922/transactions/address/ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX/limit/5'
Response:
[
[
{
"type": 2,
"id": "EoNQyNouEKg8pDcEEPY2dJL9FMQx61YFk1Sn5EJN8H7K",
"fee": 10000000,
"timestamp": 1544083814291691000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "3orvgyRKf45FRyiCkcA3CzAGDvyEpBpXZzYGEGZnpZK5",
"signature": "t1X2zmw5a2b9iaLgtsHyHKgEmKo6GCFuMFQsZNqj8ZkzpVbRKhWUttqUDfcjzcn5w7VgVVvf8cetr1mh2d2xypQ"
}
],
"recipient": "ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX",
"feeScale": 100,
"amount": 100000000,
"attachment": "",
"status": "Success",
"feeCharged": 10000000
},
{
"type": 4,
"id": "FiMiErppddPfFCmehu1ziKNTqyzBFsLRj6gh9y45JKKD",
"fee": 10000000,
"timestamp": 1543569020372515800,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV",
"signature": "2hpsVXZVs2Wmg5ixD8PqvMJoC3CAqgTqvapYkuFAxbLvoyXRu45q9HXZQyqCzHeiHocGFM8phPkmDuM566Xu59em"
}
],
"feeScale": 100,
"leaseId": "D8mGb2YSGyKr5Q3WATnpQP8JvyDdteXwieo5khwsTEyY",
"status": "Success",
"feeCharged": 10000000,
"lease": {
"type": 3,
"id": "D8mGb2YSGyKr5Q3WATnpQP8JvyDdteXwieo5khwsTEyY",
"fee": 10000000,
"timestamp": 1543569009108564000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV",
"signature": "8TDUgnkNbrPL6VMLFzDnhZvABfRqXitFX46mmvpohsdeRHKaNtWCs5C7m6avaUH2NjiFS7jGFov1CY5s3W8Zc5V"
}
],
"amount": 100000000,
"recipient": "AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q",
"feeScale": 100
}
},
{
"type": 3,
"id": "D8mGb2YSGyKr5Q3WATnpQP8JvyDdteXwieo5khwsTEyY",
"fee": 10000000,
"timestamp": 1543569009108564000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV",
"signature": "8TDUgnkNbrPL6VMLFzDnhZvABfRqXitFX46mmvpohsdeRHKaNtWCs5C7m6avaUH2NjiFS7jGFov1CY5s3W8Zc5V"
}
],
"amount": 100000000,
"recipient": "AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q",
"feeScale": 100,
"status": "Success",
"feeCharged": 10000000
},
{
"type": 2,
"id": "He17g3JXtbXgMiWCTGwnNMPfvfFH5tvyJfYZ7BiWGBZK",
"fee": 10000000,
"timestamp": 1543568995612184000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "CbUPwcCJaMqYSjZGXy4LrkTfV2ncP27Chqyd2QKXfJxn",
"signature": "24fNpVr8qjrKuDNd8JSeZgkSa4BuS44kxupiAYPHxCbbPMBs2DyT7VDnqAWsJkYZxXWadqiQs7HFxW9uVULrGAtt"
}
],
"recipient": "ATt6P4vSpBvBTHdV5V9PJEHMFp4msJ1fkkX",
"feeScale": 100,
"amount": 100000000,
"attachment": "",
"status": "Success",
"feeCharged": 10000000
},
{
"type": 2,
"id": "2FVTJUpUJAhZJWkVYHHCG4nRXkYqwQcKsGEK8uwx3A58",
"fee": 10000000,
"timestamp": 1543568982176328000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV",
"signature": "3DShPFQLidR1nbTKDrrhpp6SZdiL1hKtjYaGANzGuaWqjpGggPgtrCzw5XYXXktt2sFgWnmFVfTf4gNkmNPNyS2v"
}
],
"recipient": "AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q",
"feeScale": 100,
"amount": 100000000,
"attachment": "",
"status": "Success",
"feeCharged": 10000000
}
]
]
Some common id of transaction types:
2 = Payment transaction
3 = Lease transaction
4 = Cancel lease transaction
5 = Minting transaction
8 = Register contract transaction
9 = Execute contract function transaction
Use HTTP GET to call /transactions/info/{id} API. For example,
$ curl -X GET 'http://<node ip>:9922/transactions/info/EhmLJA5H5LG8a69eQEpKtbPZf8KGMCSH5w4MPDMoaGR3'
Response:
{
"type": 2,
"id": "EhmLJA5H5LG8a69eQEpKtbPZf8KGMCSH5w4MPDMoaGR3",
"fee": 10000000,
"timestamp": 1562339456608000000,
"proofs": [
{
"proofType": "Curve25519",
"publicKey": "EaxkrqBySftSD7M9WJiBKxLPjugtjUqDCJK3Lf3aTq1E",
"signature": "4zxLLLpBmERW7zwTTyRamrQDgeQPSe2gwFwsUKoVtBvsPjz73n2fHFLBxAyYtJop3yrKs9LFiirNZ5VUDahD4ao7"
}
],
"recipient": "AU83FKKzTYCue5ZQPweCzJ68dQE4HtdMv5U",
"feeScale": 100,
"amount": 50000000,
"attachment": "",
"status": "Success",
"feeCharged": 10000000,
"height": 5414065
}
PS: If a transaction if packaged into a block from Unconfirmed transaction pool, this API will return the height of transaction. We can confirm a transaction if node block height is 31 blocks higher than the height of transaction.
To make sure asset is safty, the exchange collects coins from hot wallet and stores these coins in cold wallet. When user widthdraw coins, the exchange transfer out to user from cold wallet. To transfer out the coins from cold wallet, the key point is generating the payment signature. The steps can refer the send_payment(...)
method of account.py in pyvsystems.
And you can write your own program to generate payment signature as well. For example, if you want to send a payment which JSON format like this:
{
"amount": 1000000000,
"fee": 10000000,
"feeScale": 100,
"timestamp": 1547722056762119200,
"recipient": "AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q",
"senderPublicKey": "B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV",
"attachment": "HXRC"
}
Translate these fields into bytes:
type_id: 02
timestamp: 15 7a 9d 02 ac 57 d4 20
amount: 00 00 00 00 3b 9a ca 00
tx_fee: 00 00 00 00 00 98 96 80
fee_scale: 00 64
recipient: 05 54 9c 6d f7 b3 76 77 1b 19 ff 3b db 58 d0 4b 49 99 91 66 3c 47 44 4e 42 5f
length of attachment: 00 03
attachment: 31 32 33
And then combine togather:
02 15 7a 9d 02 ac 57 d4 20 00 00 00 00 3b 9a ca 00 00 00 00 00 00 98 96 80 00 64 05 54 9c 6d f7 b3 76 77 1b 19 ff 3b db 58 d0 4b 49 99 91 66 3c 47 44 4e 42 5f 00 03 31 32 33
Finally, we used ed25519 of curve25519 library to signature.
(For reference only. The signature will be different if generate again)
72 74 61 73 6d 50 31 4c 63 48 79 5a 63 71 35 36 67 52 34 78 57 45 35 78 68 54 78 59 35 33 6f 6f 6f 4d 32 53 61 36 63 75 42 52 61 78 72 71 33 39 63 54 56 6b 39 4d 67 4d 76 6e 38 61 5a 45 6d 4e 78 6b 56 39 55 39 63 62 41 6a 43 50 4d 68 48 46 6f 51 33 57 69 66 57
And then encode with base58 and put in signature
field.
{
"amount": 1000000000,
"fee": 10000000,
"feeScale": 100,
"timestamp": 1547722056762119200,
"recipient": "AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q",
"senderPublicKey": "B2Khd89jtnpuzGdnyGRcnKycZMBCo6PsotFcWWi1wMDV",
"attachment": "HXRC",
"signature": "rtasmP1LcHyZcq56gR4xWE5xhTxY53oooM2Sa6cuBRaxrq39cTVk9MgMvn8aZEmNxkV9U9cbAjCPMhHFoQ3WifW"
}
Pass this JSON to full node. And full node broadcasts to network with API /vsys/broadcast/payment
.
If using BIP44, please set coin_type=360 ("44'/360'/[account]'/0/[address index]")
- Wallet Generator (Scala version, similar to BIP39)
- V HD Key (Java version, similar to BIP32)
- V HD Key (Go version, similar to BIP32)
- pyvsystems SDK (Python version)