Functions and Types

Types and type conversions

Programs written for the Parasol CPU currently supports signed and unsigned integers up to 32 bits. You can use the following types, which are synonyms for the corresponding types in the C standard library:

// Boolean value
typedef _Bool bool;

// 8 bit unsigned integer
typedef unsigned char uint8_t;

// 16 bit unsigned integer
typedef unsigned short uint16_t;

// 32 bit unsigned integer
typedef unsigned int uint32_t;

// 8 bit signed integer
typedef signed char int8_t;

// 16 bit signed integer
typedef signed short int16_t;

// 32 bit signed integer
typedef signed int int32_t;

When a program is running, it is possible to convert between different integer sizes. When converting from a smaller integer to a larger one, the higher bits are padded and therefore the value does not change. However, when converting from a larger integer to a smaller one, the higher bits are discarded, leading to a different value.

Here is an example:

[[clang::fhe_program]] void size_conversions(
    [[clang::encrypted]] uint8_t u8_a,
    [[clang::encrypted]] uint32_t u32_b,
    [[clang::encrypted]] uint32_t *u32_output,
    [[clang::encrypted]] uint8_t *u8_output
) {
    // Convert to a larger integer
    uint32_t u32_a = u8_a;
    *u32_output = u32_a + u32_b;

    // Truncate to a smaller integer
    char u8_b = u32_b;
    *u8_output = u8_a + u8_b;
}

Function parameter annotations

FHE programs can be run with either plaintext or ciphertext arguments. In order to specify what argument is of what type, you can use the [[clang::encrypted]] annotation before a function’s parameter.

[[clang::fhe_program]] uint32_t f(
    uint32_t a,
    [[clang::encrypted]] uint32_t b
) {
    // do something with a and b
}

In this particular example function, the first parameter is expected to be a plaintext value, while the second parameter is expected to be an encrypted value. When the program is run, the parameters passed to the function should match the annotation given at the time of compilation.

Return parameters

One can specify a return value (signed or unsigned integer) by using the standard C return type syntax.

// Function to return a value
[[clang::fhe_program]] uint32_t add(
    [[clang::encrypted]] uint32_t a,
    [[clang::encrypted]] uint32_t b
) {
    return a + b;
}

We can also return values by modifying the arguments. Here we take two numbers, add them together, and store the result in the second parameter. This overwrites the value in the memory location pointed to by the balance_pointer parameter.

The fact that you can overwrite a pointer is important. Here is an example of a private transfer function:

[[clang::fhe_program]] void transfer(
    [[clang::encrypted]] uint32_t amount,
    [[clang::encrypted]] uint32_t *balance_pointer
) {
    uint32_t balance = *balance_pointer;

    uint32_t transfer_amount = amount < balance ? amount : 0;
    *balance_pointer = balance - transfer_amount;
}

In this example, the balance_pointer takes in the orignal balance and after the transfer program has run, that same argument is updated with the new balance.

Inlining

One of the tenets of making maintainable code is developing smaller functions that can be reused in several places, increasing the readability of a program. For our FHE programs, functions marked as inline can be used to reduce common/boilerplate code in the program, making the final code easier to reason about and test.

The following example demonstrates this modularity. It is a program that calculates the price of some item given its base price and a packed byte representing the discounts a user could have applied to their purchase. The get_bit function is used several times to extract the specific discounts from the discounts_flags argument. These discounts are then used to calculate the final price of the item. Inlining here enables us to follow DRY principles and reduce code duplication.

inline bool get_bit(uint32_t flags, unsigned int n) {
    return ((flags >> n) & 0x1);
}

[[clang::fhe_program]] uint32_t price_item(
    uint32_t base_price,
    [[clang::encrypted]] uint32_t discounts_flags
) {
    bool has_store_membership_discount = get_bit(discounts_flags, 0);
    bool has_coupon_discount = get_bit(discounts_flags, 1);

    uint32_t price = base_price;

    // Discount the items based on the discounts the customer has
    price = has_store_membership_discount ? price - 2 : price;
    price = has_coupon_discount ? price - 3 : price;

    return price;
}