-
Notifications
You must be signed in to change notification settings - Fork 5
[en] C
To be able to communicate with the Aergo blockchain we need to specify the host address and the TCP port.
We create an instance using the aergo_connect function.
Example:
aergo *instance;
/* connect to the blockchain */
instance = aergo_connect("testnet-api.aergo.io", 7845);
/* use the instance object on function calls */
...
/* release the object when no longer needed */
aergo_free(instance);Blockchains have many advantages but they are slower than traditional databases.
One request using a transaction can take up to 1 second. If you want to make your application responsive while a transaction is being processed on the blockchain you can use asynchronous calls and receive a transaction receipt on a callback function.
As there is no global cross-platform message loop, libaergo has a function to be called to check for incoming results and notifications:
int aergo_process_requests(aergo *instance, int timeout);If your application uses a message loop, put a call to this function on a timer
and use the timeout argument 0
void on_timer() {
aergo_process_requests(instance, 0);
}This function returns the number of active requests. If it is zero, you can stop the timer.
You can also use it in a loop and set a value for the timeout.
If your application will call smart contract functions (not query) or if it will hold and transfer tokens then it will need an account.
An account is based on a secret+public key pairs. It has an address used to identify the account and it is generated from the public key.
The account objects are allocated by your application. This is the structure:
struct aergo_account {
bool use_ledger;
int index;
unsigned char privkey[32];
unsigned char pubkey[33];
char address[64];
uint64_t nonce;
double balance;
uint8_t state_root[32];
bool is_updated;
};Make sure that the memory is zeroed after allocating the object.
If the account is stored on a Ledger Nano device you can access it by setting this element:
account.use_ledger = true;It is also possible to select the Aergo account index on the device:
account.index = <number>;The default account index is zero.
When not using a hardware wallet it is required to inform the account private key.
The private/secret key is a random 32 byte byte array.
Your application is responsible for generating a true random private key, storing it into persistent storage and loading it into the account object.
It is very important to to use a very strong source of randomness to generate the private key.
On Linux you can use getrandom if available
On Mac you can use SecRandomCopyBytes
On Windows you can use RtlGenRandom
As not any random 32 bytes are valid private keys, we must verify if the generated random bytes are valid:
do {
fill_random(account.privkey, 32);
} while (aergo_check_privkey(instance, &account) == false);After loading the private key into the account structure we can fill the remaining fields by requesting information about the state of the account to the blockchain.
bool success = aergo_get_account_state(instance, &account, error);Now you can check the account address, the balance, the nonce and the account's state root.
You can check and compile the blockchain account info example
For details about the history of transactions for a specific account please access the aergoscan
To add balance to a specific account on the testnet please access the faucet
Transactions are used for value transfers and smart contract execution (call).
A receipt is generated when a transaction is processed by the the blockchain network.
Your application will receive the receipt with this format:
struct transaction_receipt {
char contractAddress[56];
char status[16];
char ret[2048];
uint64_t blockNo;
char blockHash[32];
int32_t txIndex;
char txHash[32];
uint64_t gasUsed;
double feeUsed;
bool feeDelegation;
};The status can be:
-
SUCCESSFor smart contract calls the result is available in the
retfield -
ERRORFailed transfer or smart contract execution. The error message can be found in the
retfield
On synchronous calls the receipt is returned as a function parameter.
On asynchronous calls the receipt is returned on the registered callback function.
The callback functions used to receive the transaction receipt have this format:
void on_transaction_receipt(void *arg, transaction_receipt *receipt) {
}Your application can request transaction receipts using these functions:
bool aergo_get_receipt(aergo *instance,
const char *txn_hash,
struct transaction_receipt *receipt);
bool aergo_get_receipt_async(aergo *instance,
const char *txn_hash,
transaction_receipt_cb cb,
void *arg);Smart contracts are a group of functions stored on the blockchain that can be called by external applications or users.
Aergo uses Lua as the programming language for these contracts and they support SQL.
You can write a smart contract, publish it on the blockchain and call it from your application.
There are 2 types of function calls on smart contracts:
Those who write to the blockchain are named calls
Those who just read from the blockchain are named queries
Calls are made via blockchain transactions and they consume tokens from the caller account.
Queries are made without transactions and they don't need to be signed by an account. They do not require payment for the processing.
They are used when your application will send data to be processed by and stored into the blockchain.
The application needs to specify the contract address and the smart contract function name. The arguments to the function call are optional.
The arguments can be passed using a variadic function call specifying the type of each argument.
-
s- string -
i- integer -
l- long -
d- double -
b- binary data (expects 2 arguments: pointer and size)
Here is an example of a synchronous call using this method:
transaction_receipt receipt;
bool success = aergo_call_smart_contract(instance,
&receipt,
&account,
"...", /* contract_address */
"my_function", /* function */
"isdb", /* arguments types */
123, "testing", 2.5, pic_ptr, pic_len);You can check and compile the smart contract call example
And here is the asynchronous version of this call:
void on_transaction_receipt(void *arg, transaction_receipt *receipt) {
...
}
bool success = aergo_call_smart_contract_async(instance,
on_transaction_receipt, NULL,
&account,
"...", /* contract_address */
"my_function", /* function */
"isdb", /* arguments types */
123, "testing", 2.5, pic_ptr, pic_len);Don't forget to call the aergo_process_requests() function on a timer or a loop
to process the incoming result.
You can check and compile the smart contract asynchronous call example
If the arguments must be joined before the function call then you can serialize the arguments into a JSON array and use this functions instead:
transaction_receipt receipt;
bool success = aergo_call_smart_contract_json(instance,
&receipt,
&account,
"...", /* contract_address */
"my_function", /* function */
json_array); /* arguments */There is also the asynchronous version of this function.
They are used when your application will just read data from the blockchain.
The API are similar to the functions used to smart contract calls.
Here is an example of a synchronous query:
char result[4096];
bool success = aergo_query_smart_contract(instance,
result, sizeof result,
"...", /* contract_address */
"my_function", /* function */
"isdb", /* arguments types */
123, "testing", 2.5, data_ptr, data_len);You can check and compile the smart contract query example
And here is the asynchronous version of this call:
void on_smart_contract_query_result(void *arg, bool success, char *result){
...
}
bool aergo_query_smart_contract_async(aergo *instance,
query_smart_contract_cb cb,
void *arg,
const char *contract_address,
const char *function,
const char *types,
...);Don't forget to call the aergo_process_requests() function on a timer or a loop
to process the incoming result.
You can check and compile the smart contract asynchronous query example
If the arguments must be joined before the function call then you can serialize the arguments into a JSON array and use this functions instead:
char result[4096];
bool success = aergo_query_smart_contract_json(instance,
result, sizeof result,
"...", /* contract_address */
"my_function", /* function */
json_array); /* arguments */There is also the asynchronous version of this function.
Smart contracts can generate events and your application can subscribe to receive event notifications.
The notification data is presented to the application as a contract_event object:
struct contract_event {
char contractAddress[64];
char eventName[64];
char jsonArgs[2048];
int32_t eventIdx;
char txHash[32];
char blockHash[32];
uint64_t blockNo;
int32_t txIndex;
};Here is the structure of the callback function and the function used to subscribe to the events:
void on_smart_contract_event(void *arg, contract_event *event){
...
}
bool success = aergo_contract_events_subscribe(
instance,
"...", /* contract_address */
"", /* event_name - let empty to receive all events */
on_smart_contract_event,
NULL); /* context pointer passed to the callback function */Don't forget to call the aergo_process_requests() function on a timer or a loop
to process the incoming result.
You can check and compile the full example
Accounts can hold tokens and they can be transferred to other accounts.
libaergo supports 4 different formats for the amount when transferring tokens.
And there are synchronous and asynchronous versions for these functions.
bool aergo_transfer(aergo *instance,
transaction_receipt *receipt,
aergo_account *from_account,
const char *to_account,
double value);
bool aergo_transfer_async(aergo *instance,
transaction_receipt_cb cb,
void *arg,
aergo_account *from_account,
const char *to_account,
double value);Note: decimal part with 18 digits!
Example: in 3.5 the integer part is 3 and the decimal part is 500000000000000000
bool aergo_transfer_int(aergo *instance,
transaction_receipt *receipt,
aergo_account *from_account,
const char *to_account,
uint64_t integer,
uint64_t decimal);
bool aergo_transfer_int_async(aergo *instance,
transaction_receipt_cb cb,
void *arg,
aergo_account *from_account,
const char *to_account,
uint64_t integer,
uint64_t decimal);It must be null terminated. The unit is aergo
Example: "130.25"
bool aergo_transfer_str(aergo *instance,
transaction_receipt *receipt,
aergo_account *from_account,
const char *to_account,
const char *value);
bool aergo_transfer_str_async(aergo *instance,
transaction_receipt_cb cb,
void *arg,
aergo_account *from_account,
const char *to_account,
const char *value);This is the format exported from bignumber/biginteger implementations
bool aergo_transfer_bignum(aergo *instance,
transaction_receipt *receipt,
aergo_account *from_account,
const char *to_account,
const unsigned char *amount,
int len);
bool aergo_transfer_bignum_async(aergo *instance,
transaction_receipt_cb cb,
void *arg,
aergo_account *from_account,
const char *to_account,
const unsigned char *amount,
int len);You can check and compile the transfer example as well as the asynchronous transfer example