-
Notifications
You must be signed in to change notification settings - Fork 16
Plugin API
The plugins will be able to be distributed and loaded in 3 ways:
-
Bundled in the AergoLite library (either static or dynamic), compiled using native code (C, C++ and maybe Swift, Rust or any LLVM/JIT based language). Useful for platforms in which it is not easy to use dynamic libraries and for resource constrained devices.
-
In a separate loadable library that can be loaded using the SQLite's
load_extension()SQL function:
SELECT load_extension('aergolite-mini-raft-consensus')
- For interpreted languages (Javascript, Python, Lua...) the application can contain the code for the plugin. In this case it must register the plugin interface before opening the database. It is done using an exported function that can be called via ctypes (Python) or node-ffi (node.js) before starting the SQLite wrapper.
The plugin must be registered in the core to be able to be used. This is done using the aergolite_plugin_register function:
SQLITE_API int aergolite_plugin_register(
char *name, /* name of the plugin */
void* (*xInit)(aergolite*, char* uri), /* initializes a new plugin instance */
void (*xEnd)(void*), /* terminates the instance */
void (*xOnNewLocalTransaction)(void*), /* on_new_local_transaction notification */
char* (*xStatus)(void*, int extended) /* used to retrieve the protocol status */
);The registration must be done when the plugin is loaded, on the library's entry point.
For plugins written in interpreted languages (Python, Javascript, Lua...) the plugin code can be in the same application that uses the database. In this case the plugin registration is done before loading the SQLite wrapper.
The plugin must implement the functions bellow. They will be called by the AergoLite core.
Reminder: these methods on the plugin are called by the core using the main thread! (Use mutex when needed.)
The application is opening a local database that uses this plugin. The core is requesting the plugin to create a new instance.
It must:
- Allocate a new object to store information for this instance
- Store the aergolite instance reference on the new allocated object
- Create a worker thread for this instance
- Return a pointer/handle that the core will use on the other callbacks
void* plugin_init(void* aergolite_instance, char* uri)The application is closing a database that has an instance of this plugin. The core is requesting the plugin to terminate this instance releasing all the allocated resources, including connections and the worker thread.
void plugin_end(void* plugin_instance)The application executed a new database transaction that was successfully committed.
The plugin method should just send a notification to its worker thread and it must return as fast as possible.
The worker thread, once received the notification, must:
- Process its queue of local transactions that must be sent to other nodes
void on_new_local_transaction(void* plugin_instance)The application is requesting information about the plugin state.
The plugin should return a JSON document [allocated using sqlite3_malloc ?] containing information about the connections to other peers, the consensus protocol and any useful information.
Each plugin may have different information to present as they implement different protocols.
The application makes this request via the PRAGMA protocol_status command. The core will transfer the argument to the plugin, if one is informed.
char* get_status(void* plugin_instance, char* argument)These are the functions that should be used by the plugin:
This function must be called by the plugin periodically, using a timer. The suggested interval is 500 ms.
void aergolite_periodic(aergolite *this_node)Retrieve this node's id
Each node has a unique node identifier: a 31 bit integer (no sign bit, always positive).
It is chosen randomly by the AergoLite core the first time it opens a non-initialized database.
Setting the node id by the plugin is not yet supported, but it can retrieve the value with:
int aergolite_get_node_id(aergolite *this_node);Retrieve application defined node info
This info is set by the application using the PRAGMA node_info command in
the following format:
PRAGMA node_info=<string>
The information should be serialized in a single final string, using any format (JSON, netstring...) as it will be used by the application itself or its peers.
The returned memory must be released with sqlite3_free()
char* aergolite_get_node_info(aergolite *this_node);When starting from the first time, if the local db is not empty this function can be called
not fully implemented yet
int aergolite_store_and_empty_local_db(aergolite *this_node);Load the local database, the current state agreement (last block) and verify the database integrity based on the agreed db state.
Returns the last block information in the case of success.
SQLITE_API int aergolite_load_current_state(
aergolite *this_node,
int64 *pblock_height,
void **pheader,
void **pbody,
void **psignatures
);Begin the process of updating the local database with data retrieved from a peer
SQLITE_API int aergolite_begin_state_update(aergolite* this_node);Update a database page with content received from a peer
SQLITE_API int aergolite_update_db_page(aergolite* this_node, uint32_t pgno, char *data, int size);Apply the database state update with data received from the peer: the block header, the signatures and the list of modified pages.
SQLITE_API int aergolite_apply_state_update(aergolite* this_node, void *header, void *signatures, void *modified_pages);Cancel the process of state update
SQLITE_API void aergolite_cancel_state_update(aergolite* this_node);Start the process of reading the local database state
SQLITE_API int aergolite_begin_state_read(aergolite* this_node);Retrieves the list of modified pages since height from
SQLITE_API int aergolite_get_modified_pages(aergolite* this_node, int64 from, int64 to, binn **plist);Load the database page data into memory
SQLITE_API int aergolite_get_db_page(aergolite* this_node, uint32_t pgno, void *data, int *psize);End the process of reading the local database state
SQLITE_API int aergolite_end_state_read(aergolite* this_node);Retrieve the local node's last nonce
SQLITE_API int64 aergolite_get_node_last_nonce(aergolite *this_node);Retrieve a transaction from the local queue.
If the nonce is set to 0, it will retrieve the first transaction that was not yet processed.
Subsequent calls can specify the nonce.
It will write the transaction nonce in the given pointer and return a binn map with transaction data.
The plugin should transfer the local transaction to its leader or peers.
int aergolite_get_local_transaction(aergolite *this_node, int64 *pnonce, binn **plog);Returns a transaction id based on the node id and the nonce
SQLITE_API int64 aergolite_get_transaction_id(int node_id, int64 nonce);Used when creating or applying a new block
SQLITE_API int aergolite_execute_transaction(
aergolite *this_node, int node_id, int64 nonce, void *list
);Release the binn map object containing the local transaction
void aergolite_free_transaction(binn *log);Start the process of creating or applying a new block
SQLITE_API int aergolite_begin_block(aergolite *this_node);Creates a new block
SQLITE_API int aergolite_create_block(aergolite *this_node, void **pheader, void **pbody);Applies a block on the local blockchain
SQLITE_API int aergolite_apply_block(aergolite *this_node, void *header, void *body, void *signatures);Cancel the process of creating or applying a new block
SQLITE_API int aergolite_rollback_block(aergolite *this_node);Retrieve the blockchain status as an allocated string in JSON format
The returned memory must be released with sqlite3_free()
char * aergolite_get_blockchain_status(aergolite *this_node);int aergolite_set_node_config_str(aergolite *this_node, char *key, char *value);
int aergolite_set_node_config_int(aergolite *this_node, char *key, int64 value);
int aergolite_set_node_config_double(aergolite *this_node, char *key, double value);
int aergolite_set_node_config_blob(aergolite *this_node, char *key, char *value, int size);char* aergolite_get_node_config_str(aergolite *this_node, char *key);
int64 aergolite_get_node_config_int(aergolite *this_node, char *key);
double aergolite_get_node_config_double(aergolite *this_node, char *key);
char* aergolite_get_node_config_blob(aergolite *this_node, char *key, int *psize);Returned memory from _str and _blob must be released with
sqlite3_free()
int aergolite_queue_db_exec(aergolite *this_node, const char *sql, ...);int aergolite_queue_db_query_int32(aergolite *this_node, int *pvalue, char *sql, ...);
int aergolite_queue_db_query_int64(aergolite *this_node, int64 *pvalue, char *sql, ...);
int aergolite_queue_db_query_double(aergolite *this_node, double *pvalue, char *sql, ...);
int aergolite_queue_db_query_str(aergolite *this_node, char **pvalue, char *sql, ...);
int aergolite_queue_db_query_blob(aergolite *this_node, char **pvalue, int *psize, char *sql, ...);Returned memory from _str and _blob must be released with
sqlite3_free()
int aergolite_consensus_db_exec(aergolite *this_node, const char *sql, ...);int aergolite_consensus_db_query_int32(aergolite *this_node, int *pvalue, char *sql, ...);
int aergolite_consensus_db_query_int64(aergolite *this_node, int64 *pvalue, char *sql, ...);
int aergolite_consensus_db_query_double(aergolite *this_node, double *pvalue, char *sql, ...);
int aergolite_consensus_db_query_str(aergolite *this_node, char **pvalue, char *sql, ...);
int aergolite_consensus_db_query_blob(aergolite *this_node, char **pvalue, int *psize, char *sql, ...);Returned memory from _str and _blob must be released with
sqlite3_free()
uchar* aergolite_encrypt(aergolite *this_node, uchar *data, int *psize, int counter);
uchar* aergolite_decrypt(aergolite *this_node, uchar *data, int *psize, int counter);SYNCTRACE(format, ...)The plugin can also use the native SQLite functions. Just remind that the plugin is running on a separate thread from the application.
Some useful are:
- sqlite3_malloc
- sqlite3_free
void *sqlite3_malloc_zero(int64 n);
char *sqlite3_memdup(char *source, int size);
char *sqlite3_strdup(char *text);
char *stripchr(char *mainstr, int separator);