Skip to main content

Requesting program runs

Once programs are uploaded and users have provided encrypted inputs, program runs can be requested from SPF. This section covers parameter encoding, run submission, status checking, and result retrieval using the TypeScript client library.

Ensure the client is initialized before using these functions.

Encoding parameters

Before submitting a run, parameters must be encoded to match the FHE program’s signature. For example, the tally_votes program has the following signature:

void tally_votes([[clang::encrypted]] int8_t *votes,
uint16_t num_votes,
[[clang::encrypted]] bool *didTheIssuePass)

The TypeScript encoding maps directly to this C signature:

export function createParameterExamples(
voteCiphertextIds: spf.CiphertextId[],
): spf.SpfParameterWithAuth[] {
const parametersWithAuth: spf.SpfParameterWithAuth[] = [
// Parameter 1: Array of encrypted votes
spf.createCiphertextArrayParameter(voteCiphertextIds),

// Parameter 2: Number of votes (plaintext)
spf.createPlaintextParameter(16, voteCiphertextIds.length),

// Parameter 3: Output boolean (encrypted). The smallest addressable unit is
// 8 bits, so we use 8 bits for bool.
spf.createOutputCiphertextArrayParameter(8, 1),
];

return parametersWithAuth;
}

Parameter types

The client library provides helper functions for each parameter type:

Ciphertext array parameter: For an array of encrypted inputs (C type [[clang::encrypted]] (u)intN_t *value)

const param = spf.createCiphertextArrayParameter([
spf.asCiphertextId(ctId1),
spf.asCiphertextId(ctId2),
spf.asCiphertextId(ctId3),
]);

Single ciphertext parameter: For a single encrypted input (C type [[clang::encrypted]] (u)intN_t value)

const param = spf.createCiphertextParameter(spf.asCiphertextId(ciphertextId));

Plaintext parameter: For a plaintext input (C type (u)intN_t value)

const param = spf.createPlaintextParameter(16, 123);

Plaintext array parameter: For an array of plaintext inputs (C type (u)intN_t *value)

const param = spf.createPlaintextArrayParameter(16, [1, 2, 3, 4]);

Output ciphertext array parameter: For an array of encrypted outputs (C type [[clang::encrypted]] (u)intN_t *value)

// Create an output array for 1 encrypted boolean (8-bit)
const param = spf.createOutputCiphertextArrayParameter(8, 1);

// Create an output array for 3 encrypted 16-bit values
const param = spf.createOutputCiphertextArrayParameter(16, 3);

Submitting a program run

To submit a run request to execute an FHE program, use the submitRun() function:

export async function submitRunExample(
signer: spf.PrivateKeySigner,
libraryId: string,
voteCiphertextIds: string[],
): Promise<spf.RunHandle> {
// C: void tally_votes(int8_t* votes, uint16_t num_votes, bool* result)
const parametersWithAuth: spf.SpfParameterWithAuth[] = [
spf.createCiphertextArrayParameter(
voteCiphertextIds.map((id) => spf.asCiphertextId(id)),
),
spf.createPlaintextParameter(16, voteCiphertextIds.length),
spf.createOutputCiphertextArrayParameter(8, 1), // bool output
];

// Submit the run
const runHandle = await spf.submitRun(
signer,
spf.asLibraryId(libraryId),
spf.asProgramName("tally_votes"),
parametersWithAuth,
);

console.log("Run submitted:", runHandle);

return runHandle;
}

The submitRun() function submits the computation request to SPF and returns a unique run handle. This handle is used to check status and retrieve results.

Parameters:

  • signer: AnySigner - Signer for authentication. The signer must have the access to use all the input ciphertexts (i.e. have been granted run access to each ciphertext).
  • libraryId: LibraryId - The library identifier from program upload (use spf.asLibraryId(string) to create)
  • programName: ProgramName - The program entry point name (use spf.asProgramName(string) to create)
  • parametersWithAuth: SpfParameterWithAuth[] - Array of parameters with authentication data (returned from parameter creation functions like createCiphertextArrayParameter())

Returns: Promise<RunHandle> - The run handle (branded hex string with 0x prefix)

Checking run status

To check the status of an FHE program run, use the checkRunStatus() function:

export async function checkRunStatusExample(
runHandle: spf.RunHandle,
): Promise<spf.RunStatus> {
const status = await spf.checkRunStatus(runHandle);

// Use type narrowing to handle different status cases
if (status.status === "success") {
console.log("Run completed successfully!");
console.log("Payload:", status.payload);
} else if (status.status === "failed") {
console.error("Run failed:", status.payload?.message);
} else {
console.log("Run is still", status.status);
}

return status;
}

The function returns a discriminated union with the current status. Use TypeScript’s type narrowing to safely access status-specific fields.

Parameters:

  • runHandle: RunHandle - The run handle from submitRun()

Returns: Promise<RunStatus> - Discriminated union with status field

Waiting for run completion

For convenience, use waitForRun() to automatically poll until the run completes:

export async function waitForRunExample(
runHandle: spf.RunHandle,
): Promise<spf.RunStatusSuccess | spf.RunStatusFailed> {
// Wait for the run to complete
const finalStatus = await spf.waitForRun(runHandle);

if (finalStatus.status === "success") {
console.log("Run completed!");
} else {
throw new Error(`Run failed: ${finalStatus.payload?.message}`);
}

return finalStatus;
}

This function polls checkRunStatus() until the run succeeds or fails, then returns the final status.

Parameters:

  • runHandle: RunHandle - The run handle from submitRun()
  • signal: AbortSignal - Optional cancellation signal (use AbortSignal.timeout(ms) for timeout)

Returns: Promise<RunStatusSuccess | RunStatusFailed> - Final run status

Cancellation support

The waitForRun() function supports cancellation via AbortSignal:

export async function abortableWaitExample(
runHandle: spf.RunHandle,
): Promise<spf.RunStatusSuccess | spf.RunStatusFailed> {
try {
// Wait for run with 30 second timeout
const status = await spf.waitForRun(runHandle, AbortSignal.timeout(30000));
return status;
} catch (error) {
if (error instanceof Error && error.name === "TimeoutError") {
console.log("Run timed out after 30 seconds");
}
throw error;
}
}

This enables timeout control and the ability to cancel long-running operations.

Deriving result ciphertext identifiers

After a program run completes, the output ciphertext IDs must be derived using the deriveResultCiphertextId() function:

export function deriveResultExample(
runHandle: spf.RunHandle,
): spf.CiphertextId {
// Get the result ciphertext ID from the run
const resultId = spf.deriveResultCiphertextId(runHandle, 0);

console.log("Result ciphertext ID:", resultId);

return resultId;
}

This function deterministically computes the ciphertext ID for a program output based on the run handle and output index.

Parameters:

  • runHandle: RunHandle - The run handle from submitRun()
  • outputIndex: number - The index of the output ciphertext (0-255).

Returns: CiphertextId - The result ciphertext identifier (branded hex string with 0x prefix)

Determining the output index

The output index corresponds to the position in the flattened array of all output ciphertexts from all output parameters.

For example, if a program has these parameters:

void example(
int16_t* encrypted_inputs, // Input (ignored for index)
uint16_t num_inputs, // Plaintext (ignored for index)
int16_t* first_output, // Output array with 2 elements
uint8_t* second_output // Output array with 1 element
)

The parameters would be:

const parameters = [
spf.createCiphertextArrayParameter([...]),
spf.createPlaintextParameter(16, 5),
spf.createOutputCiphertextArrayParameter(16, 2), // Indices 0, 1
spf.createOutputCiphertextArrayParameter(8, 1), // Index 2
];

Output indices:

  • Index 0: first element in first_output
  • Index 1: second element in first_output
  • Index 2: second_output