Expand description
Host function API for delegates.
This module provides synchronous access to delegate context, secrets, and contract state via host functions, eliminating the need for message round-trips.
§Example
use freenet_stdlib::prelude::*;
#[delegate]
impl DelegateInterface for MyDelegate {
fn process(
ctx: &mut DelegateCtx,
_params: Parameters<'static>,
_attested: Option<&'static [u8]>,
message: InboundDelegateMsg,
) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
// Read/write temporary context
let data = ctx.read();
ctx.write(b"new state");
// Access persistent secrets
if let Some(key) = ctx.get_secret(b"private_key") {
// use key...
}
ctx.set_secret(b"new_secret", b"value");
// Read contract state the node already holds (no round-trip)
let contract_id = [0u8; 32]; // your contract instance ID
if let Some(state) = ctx.get_contract_state(&contract_id) {
// process state...
}
Ok(vec![])
}
}§Context vs Secrets vs Contracts
-
Context (
read/write): Temporary state within a single message batch. Reset between separate runtime calls. Use for intermediate processing state. -
Secrets (
get_secret/set_secret): Persistent encrypted storage. Survives across all delegate invocations. Use for private keys, tokens, etc. -
Contracts (
get_contract_state): a synchronous read of contract state the node already holds locally, with no request/response round-trip.There is no host function for writing or subscribing. A delegate does both by emitting the corresponding
OutboundDelegateMsg—PutContractRequest,UpdateContractRequest,SubscribeContractRequest— which go through the node’s normal contract path.
§Adding a host function is the additive way to extend this API
Host functions are resolved by name at module instantiation. A delegate that imports one an older node does not provide fails to load, with a named missing-import error; a delegate that does not import it is unaffected. So adding a host function is additive for every existing delegate, and its failure mode for a too-old node is loud and diagnosable at load time.
Contrast the message API (OutboundDelegateMsg): a new variant sent to an
older host fails mid-protocol at bincode decode, with no way for the
delegate to have detected the host’s version first. Where a capability can
be expressed either way, prefer the host function.
§Error Codes
Host functions return negative values to indicate errors:
| Code | Meaning |
|---|---|
| 0 | Success |
| -1 | Called outside process() context |
| -2 | Secret not found |
| -3 | Storage operation failed |
| -4 | Invalid parameter (e.g., negative length) |
| -5 | Context too large (exceeds i32::MAX) |
| -6 | Buffer too small |
| -7 | Contract not found in local store |
| -8 | Internal state store error |
| -9 | WASM memory bounds violation |
| -10 | Contract code not registered |
The wrapper methods in DelegateCtx handle these error codes and present
a more ergonomic API.
Modules§
- error_
codes - Error codes returned by host functions.
Structs§
- Delegate
Ctx - Opaque handle to the delegate’s execution environment.
Functions§
- decode_
secret_ key_ list - Inverse of
encode_secret_key_list. A truncated trailing record (which can only happen if the buffer was clipped mid-record) is dropped rather than panicking, so a short read degrades to “fewer keys” instead of a trap. - encode_
secret_ key_ list - Serialize a list of raw secret keys into the wire format read back by
decode_secret_key_list: for each key, a 4-byte little-endian length followed by that many key bytes. This is the encoding the host (__frnt__delegate__list_secrets) writes into the delegate’s output buffer.