Functions and Types
If you’ve read our Parasol docs regarding functions and types, this section will be content you’re already familiar with.
Types and type conversions
We currently support signed and unsigned integers up to 64 bits and booleans using the standard names (i.e uint16_t for an unsigned 16 bit value, int32_t for a signed 32 bit value, bool for booleans, etc). These are defined in the parasol.h header file.
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:
#include <parasol.h>
[[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.
#include <parasol.h>
[[clang::fhe_program]]
uint32_t f(uint32_t a,
[[clang::encrypted]] uint32_t b) {
// do something with a and b
return a + 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.
#include <parasol.h>
// 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:
#include <parasol.h>
[[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 = select32(amount < balance, amount, 0);
*balance_pointer = balance - transfer_amount;
}
In this example, the balance_pointer takes in the original 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.
#include <parasol.h>
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 = select32(has_store_membership_discount, price - 2, price);
price = select32(has_coupon_discount, price - 3, price);
return price;
}