Handling events

Responding to the library's events.

Event handling

Events will be passed to the user using the callback passed to init the Vertices library, from vertex_t:

typedef struct
{
    provider_info_t *provider;
    ret_code_t
    (*vertices_evt_handler)(vtc_evt_t *evt);
} vertex_t;

You can see here that the handler vertices_evt_handler takes a pointer to vtc_evt_t:

/// Events contains a type and a bufid. When implementing the event handler, the `bufid`
/// should be used to retrieve the data to be processed. Data type depends on event type.
typedef struct
{
    vtc_evt_type_t type; ///< \see vtc_evt_type_t
    size_t bufid; ///< internal buffer ID, used to identify a pending transaction
} vtc_evt_t;

Two things are passed to the user: the event type and a bufid which is an identifier to the internal transaction being processed. In order to get the event's transaction, the user must use the function available:

ret_code_t
vertices_event_tx_get(size_t bufid, signed_transaction_t **tx);

This function will set a local pointer to the signed_transaction_t generating the event.

Here are the different events needing the user attention:

/// Asynchronous operations can be handled using Vertices events types
typedef enum
{
    VTC_EVT_TX_READY_TO_SIGN = 0, ///< transaction's payload must be signed: the user must provide the signing function
    VTC_EVT_TX_SENDING, ///< transaction is being sent to the blockchain API. When the user got the event, the transaction has probably been already sent.
    VTC_EVT_TX_SUCCESS, ///< transaction has been successfully sent and executed, after that event, the buffer is freed.
} vtc_evt_type_t;

Let's go through the handling of those events.

Signing

VTC_EVT_TX_READY_TO_SIGN

This is the most critical event to handle: signing the payload. The payload is divided into two parts: the header and the body. Only the body part has to be signed, but with a subtility: we need to add the "TX" string before the body.

The first step is to get a pointer to the payload using vertices_event_tx_get:

typedef struct
{
    unsigned char payload[TX_PAYLOAD_MAX_LENGTH]; ///< Full payload, comprised of the header + body. The body part is the signed part (prepended with "TX")
    size_t
        payload_header_length;  ///< Header size. Do not use for signing. Full \c payload size is: \c payload_header_length + \c payload_body_length
    size_t
        payload_body_length;  ///< Body size. Length of data to be signed. if not 0, indicates that TX is pending.
    unsigned char signature[SIGNATURE_LENGTH];
    unsigned char id[TRANSACTION_HASH_STR_MAX_LENGTH
    ]; ///< Unique Identifier of the transaction. It can be used to find the transaction in the ledger
} signed_transaction_t;

We now have the payload with payload_header_length and payload_body_length. In order to have the byte array to sign, we need to init a new byte array starting with "TX" and then copy the full body into that byte array.

Now that we have a buffer ready to be signed, sign it using the Ed25519 algorithm. A private key will be necessary. Put the generated signature into the signed_transaction_t, signature field.

Once done and to send the transaction, we need to emit the event VTC_EVT_TX_SENDING using:

evt->type = VTC_EVT_TX_SENDING;
err_code = vertices_event_schedule(evt);

Sending

VTC_EVT_TX_SENDING

The transaction is being sent. When the user gets that event in the event handler, the event has already been processed by the library, meaning the transaction has been sent.

We can use that event to have the full payload of the packed transaction (message pack).

Transaction success

VTC_EVT_TX_SUCCESS

Nice! The transaction has passed. I guess you can print a hoora message.

Event processing

Last but not least, for the Vertices SDK to process the event, we need to give the library some CPU time by calling as many times as needed the event processing routine:

/// Call this function to have the Vertices SDK process pending events. Whenever calling a Vertices
/// function, make sure to process all the events.
/// To make sure all the events are processed, this function should be called from an infinite loop or OS thread/task.
/// This function will call the user-defined callback passed when using \see vertices_new
/// \param queue_size
/// \return \c VTC_SUCCESS if event has correctly been processed
ret_code_t
vertices_event_process(size_t *queue_size);

queue_size can give the user an indication about the queued events waiting to be processed but it is not mandatory.

Depending on whether you are using an RTOS or not, you might want to call this function from an indefinite loop.

Here is the link to our implementation of the given step.

You can use the repository as a template to get you started. 🚀

If you have more questions, don't hesitate to open an issue on the Github repository.

Last updated