Skip to content

Commit d5264dc

Browse files
schoenclaude
andcommitted
docs(sdk): document programs, verbosity and utilities in rustdoc
Describe how a `Program` compiles and caches, which settings determine its CMR and its address, the shape of its Taproot tree with storage, which getters panic on a compile error, and what `execute` and `finalize` check and return. Correct three doc comments: `ScriptPubkeyMismatch` holds HASH160 script hashes, not CMRs; `Program` reports Bit Machine failures as `Pruning` and never returns `ProgramError::Execution`; and which signer methods print execution output, and when. Also document the verbosity's effect on addresses, the utilities that compute hashes and entropy, and what `DUMMY_SIGNATURE` and `MIN_FEE` are for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 2312b5b commit d5264dc

7 files changed

Lines changed: 120 additions & 60 deletions

File tree

‎crates/sdk/src/constants.rs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
/// Dummy signature, which is used for fee estimation
1+
/// A placeholder for a witness signature that the signer replaces, for example at the end of a
2+
/// [`ProgramSignature::at_path`](crate::transaction::ProgramSignature::at_path) path.
23
pub const DUMMY_SIGNATURE: [u8; 64] = [1; 64];
34

4-
/// Minimal acceptable fee for nodes to relay a transaction
5+
/// The smallest policy-asset surplus `Signer::finalize_strict` accepts, and the smallest blinded
6+
/// change output the signer creates. It isn't a minimum fee: the fee follows the fee rate and can
7+
/// be smaller.
58
pub const MIN_FEE: u64 = 10;
69

710
/// The smallest explicit output Elements relays by default for any standard script.

‎crates/sdk/src/global.rs‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,28 @@ use crate::program::TrackerLogLevel;
66

77
static GLOBAL_CONFIG: OnceLock<GlobalConfig> = OnceLock::new();
88

9-
/// A structure to represent the global configuration settings for the application.
9+
/// The process-wide verbosity of program execution.
10+
///
11+
/// Verbosity `Debug` or `Trace` also compiles programs with debug symbols, which changes their
12+
/// CMRs and addresses. An application that works with contracts deployed by others should leave
13+
/// verbosity at `None`, or set [`Program::with_debug_symbols`](crate::program::Program::with_debug_symbols)
14+
/// on each program.
1015
#[derive(Clone, Copy, Debug, Default)]
1116
pub struct GlobalConfig {
1217
verbosity: Verbosity,
1318
}
1419

1520
impl GlobalConfig {
16-
/// Sets the global configuration for the SDK.
17-
///
18-
/// This function allows setting a global configuration which includes
19-
/// the logging level for `simplicity` contracts execution.
20-
/// It must be called exactly once during the application's lifetime.
21+
/// Sets the process-wide verbosity. It can be set only once per process.
2122
///
2223
/// # Errors
2324
/// Returns an error containing the newly created `GlobalConfig` if the global configuration has already been initialized.
2425
pub fn set_global_config(verbosity: Verbosity) -> Result<(), GlobalConfig> {
2526
GLOBAL_CONFIG.set(GlobalConfig { verbosity })
2627
}
2728

28-
/// Returns the default log level if `GLOBAL_CONFIG` is not initialized
29+
/// The tracker log level for the configured verbosity, or `TrackerLogLevel::None` if it was
30+
/// never set.
2931
pub fn get_log_level() -> TrackerLogLevel {
3032
GLOBAL_CONFIG
3133
.get()

‎crates/sdk/src/program/core.rs‎

Lines changed: 80 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ use super::error::ProgramError;
2020
use crate::provider::SimplicityNetwork;
2121
use crate::utils::{check_budget, hash_script, tap_data_hash, tr_unspendable_key};
2222

23-
/// Executes `simplicity` programs at runtime.
23+
/// What the [`Signer`](crate::signer::Signer) needs from a program to sign for and finalize a
24+
/// program input.
2425
///
25-
/// This trait defines a core behavior related to testing and execution.
26+
/// [`Program`] implements it, as does `&T` for any `T: ProgramTrait`.
27+
/// [`ProgramInput`](crate::transaction::ProgramInput) holds a `Box<dyn ProgramTrait>`, which is
28+
/// `Clone`.
2629
pub trait ProgramTrait: DynClone + Send + Sync {
2730
/// Retrieves the types of arguments required by a `simplicity` program.
2831
///
@@ -36,25 +39,36 @@ pub trait ProgramTrait: DynClone + Send + Sync {
3639
/// Returns a `ProgramError` if parsing or generating ABI metadata fails.
3740
fn get_witness_types(&self) -> Result<WitnessTypes, ProgramError>;
3841

39-
/// Constructs the Elements environment for a specified input index, PST, and network for further program execution.
42+
/// The Simplicity transaction environment for input `input_index` of `pst`: what the
43+
/// program's transaction jets, such as `jet::sig_all_hash()`, read.
4044
///
4145
/// # Errors
42-
/// Returns a `ProgramError` if the input index is out of bounds or if the script pubkey of the UTXO mismatches the expected program script.
46+
/// Returns `ProgramError::MissingWitnessUtxo` if an input of `pst` doesn't carry the output it
47+
/// spends, `ProgramError::UtxoIndexOutOfBounds` if `input_index` is past the inputs, and
48+
/// `ProgramError::ScriptPubkeyMismatch` if the input doesn't spend this program's address.
4349
fn get_env(
4450
&self,
4551
pst: &PartiallySignedTransaction,
4652
input_index: usize,
4753
network: &SimplicityNetwork,
4854
) -> Result<ElementsEnv<Arc<Transaction>>, ProgramError>;
4955

50-
/// Executes a Simplicity program for the given input index of a partially signed transaction.
56+
/// Runs the program locally on input `input_index` of `pst` with `witness`: satisfies it,
57+
/// prunes it, and runs the pruned program on the Bit Machine. Returns the pruned program and
58+
/// its output.
5159
///
52-
/// This function evaluates a Simplicity script associated with a specific transaction input
53-
/// in a given network, producing the result of the computation along with the redeem node
54-
/// used during execution.
60+
/// Doesn't check the witness budget; [`ProgramTrait::finalize`] does. With [`Verbosity`](crate::global::Verbosity)
61+
/// `Debug` or `Trace`, the execution output is buffered, not printed: call
62+
/// [`ProgramLogger::flush_logs`] to print it.
5563
///
5664
/// # Errors
57-
/// Returns a `ProgramError` if loading the program, satisfying the witness, retrieving the environment, or executing the `BitMachine` fails.
65+
/// - `ProgramError::WitnessSatisfaction` if the witness doesn't fit the program's witness types.
66+
/// - `ProgramError::Pruning` if the program fails. A failed `assert!` or jet is
67+
/// `ExecutionError::JetFailed`. Reaching a pruned branch, for example with `unwrap(None)`, is
68+
/// `ExecutionError::ReachedPrunedBranch`. [`Program`] reports a failure in the Bit Machine run
69+
/// after pruning with this variant too.
70+
/// - `ProgramError::BitMachineCreation` if the program exceeds the Bit Machine's limits.
71+
/// - The errors of [`ProgramTrait::get_env`] and of compiling the program.
5872
fn execute(
5973
&self,
6074
pst: &PartiallySignedTransaction,
@@ -63,14 +77,16 @@ pub trait ProgramTrait: DynClone + Send + Sync {
6377
network: &SimplicityNetwork,
6478
) -> Result<(Arc<RedeemNode>, Value), ProgramError>;
6579

66-
/// Finalizes and returns `pruned_witness` as output after executing the program on certain parameters.
80+
/// Runs the program as [`ProgramTrait::execute`] does and returns the input's witness stack:
81+
/// `[witness, program, cmr, control_block]`.
6782
///
6883
/// Implementations must compare the pruned program's static cost with the budget from the
6984
/// exact serialized witness stack they return. Use [`check_budget`] for this check,
7085
/// as the built-in `Program` does.
7186
///
7287
/// # Errors
73-
/// Returns a `ProgramError` if execution, control block construction or budget validation fails.
88+
/// Returns the errors of [`ProgramTrait::execute`], and `ProgramError::InsufficientBudget` if the
89+
/// witness stack doesn't pay for the program's execution cost.
7490
fn finalize(
7591
&self,
7692
pst: &PartiallySignedTransaction,
@@ -81,6 +97,9 @@ pub trait ProgramTrait: DynClone + Send + Sync {
8197
}
8298

8399
/// Static description of a program's inputs and integer literals. Implemented by `include_simf!()`.
100+
///
101+
/// The fuzz engine uses it to generate arguments and witnesses for a program it knows only by
102+
/// type.
84103
pub trait ProgramSchema {
85104
/// Generated arguments struct.
86105
type Arguments;
@@ -94,22 +113,31 @@ pub trait ProgramSchema {
94113
/// Witness names and types.
95114
fn witness_types() -> Vec<(TemplateProgramWitness, ResolvedType)>;
96115

97-
/// Integer literals in the source.
116+
/// The integer literals in the source, sorted and without duplicates.
98117
fn literals() -> Vec<U256>;
99118
}
100119

101-
/// A trait for creating instances of a program. The `ProgramFactory` trait defines a mechanism
102-
/// for constructing and returning a program instance of a type that implements `AsRef<Program>`.
103-
/// Only by generic struct name we have a possibility to create an instance of a program.
120+
/// Creates a program of type `P` from arguments, for code that knows the program only by type,
121+
/// such as the fuzz engine. Implemented by `include_simf!()`.
104122
pub trait ProgramFactory<P: AsRef<Program> + Sized> {
105123
/// Instantiates a program with the given arguments.
106124
fn instantiate_program(args: impl Into<Arguments>) -> Box<P>;
107125
}
108126

109-
/// Represents a program structure containing its public key, compiled program, and associated storage.
110-
/// The compiled program with its CMR and the taproot spend info act as caches, filled on first use.
127+
/// A SimplicityHL program with its arguments, its Taproot internal key, and its storage slots.
128+
///
129+
/// The program compiles on first use, and the result is cached and shared between clones. The
130+
/// getters that need the compiled program panic if it doesn't compile. Call [`Program::load`]
131+
/// to get the compile error instead.
132+
///
133+
/// The program's address is a Taproot output. Its script tree holds the program as one leaf and
134+
/// one hidden leaf per storage slot, `tagged_hash("TapData", slot)`. The tree is left-folded:
135+
/// `tapbranch(tapbranch(tapbranch(program, slot0), slot1), slot2)`. So the source, the arguments
136+
/// and the debug-symbol setting determine the CMR, and the address also depends on the internal
137+
/// key and the storage.
111138
///
112-
/// Abstraction giving the power to execute Simplicity contracts without specifying any additional parameters.
139+
/// Code generated by `simplex build` wraps a `Program` in one type per contract, which reaches it
140+
/// with `as_ref()`.
113141
#[derive(Clone)]
114142
pub struct Program {
115143
source: Arc<str>,
@@ -287,10 +315,14 @@ impl Program {
287315
/// The width of a storage slot.
288316
pub const STORAGE_SLOT_BYTES: usize = 32;
289317

290-
/// Each slot deepens the taproot tree by one and the tree is at most 128 layers deep.
318+
/// The most storage slots a program can have. Each slot deepens the Taproot tree by one, and
319+
/// the tree is at most 128 layers deep.
291320
pub const MAX_STORAGE_SLOTS: usize = 128;
292321

293-
/// Creates a new instance of the struct with the provided source string and arguments.
322+
/// Creates a program from SimplicityHL source and its `param::` arguments, without compiling
323+
/// it.
324+
///
325+
/// The Taproot internal key is [`tr_unspendable_key`], and there aren't any storage slots.
294326
#[must_use]
295327
pub fn new(source: impl Into<Arc<str>>, arguments: impl Into<Arguments>) -> Self {
296328
Self {
@@ -304,8 +336,7 @@ impl Program {
304336
}
305337
}
306338

307-
/// Sets the `pub_key` field of the struct to the provided `XOnlyPublicKey` value and returns the updated builder instance.
308-
/// This is used to set the taproot public key for the program.
339+
/// Sets the Taproot internal key, which changes the address.
309340
#[must_use]
310341
pub fn with_taproot_pubkey(mut self, pub_key: XOnlyPublicKey) -> Self {
311342
self.pub_key = pub_key;
@@ -314,7 +345,11 @@ impl Program {
314345
self
315346
}
316347

317-
/// Builds this program in the mode the protocol declares.
348+
/// Compiles the program with or without debug symbols, overriding [`GlobalConfig`].
349+
///
350+
/// Debug symbols change the CMR and so the address. A program whose address must match one
351+
/// computed elsewhere, such as a deployed contract, should set this explicitly, because
352+
/// verbosity `Debug` or `Trace` turns debug symbols on by default.
318353
#[must_use]
319354
pub fn with_debug_symbols(mut self, include: bool) -> Self {
320355
self.include_debug_symbols = Some(include);
@@ -325,7 +360,7 @@ impl Program {
325360
self
326361
}
327362

328-
/// Sets storage capacity for further usage.
363+
/// Replaces the storage with `capacity` zeroed slots.
329364
///
330365
/// # Panics
331366
/// Panics if `capacity` exceeds `MAX_STORAGE_SLOTS`.
@@ -383,19 +418,19 @@ impl Program {
383418
&self.arguments
384419
}
385420

386-
/// Returns the number of storage chunks for a program.
421+
/// The number of storage slots.
387422
#[must_use]
388423
pub fn get_storage_len(&self) -> usize {
389424
self.storage.len()
390425
}
391426

392-
/// Returns storage as a whole array of 32-byte chunks.
427+
/// The storage slots, each [`Program::STORAGE_SLOT_BYTES`] long.
393428
#[must_use]
394429
pub fn get_storage(&self) -> &[Vec<u8>] {
395430
&self.storage
396431
}
397432

398-
/// Returns storage value at a certain index.
433+
/// The storage slot at `index`.
399434
///
400435
/// # Panics
401436
/// Panics if the `index` is out of bounds for the initiated storage.
@@ -404,10 +439,10 @@ impl Program {
404439
self.storage[index].clone()
405440
}
406441

407-
/// Returns a taproot address for a defined `SimplicityNetwork`.
442+
/// The program's unconfidential Taproot address on `network`.
408443
///
409444
/// # Panics
410-
/// Panics if generating the taproot spending information fails.
445+
/// Panics if the program doesn't compile.
411446
#[must_use]
412447
pub fn get_tr_address(&self, network: &SimplicityNetwork) -> Address {
413448
let spend_info = self.taproot_spending_info().unwrap_or_else(|error| panic!("{error}"));
@@ -421,19 +456,27 @@ impl Program {
421456
)
422457
}
423458

424-
/// Retrieves the `ScriptPubKey` associated with the Simplicity address for the specified network.
459+
/// The script of the program's address on `network`, which locks the outputs the program
460+
/// controls.
461+
///
462+
/// # Panics
463+
/// Panics if the program doesn't compile.
425464
#[must_use]
426465
pub fn get_script_pubkey(&self, network: &SimplicityNetwork) -> Script {
427466
self.get_tr_address(network).script_pubkey()
428467
}
429468

430-
/// Retrieves the 32-byte `ScriptPubKey` hash associated with the Simplicity address for the specified network.
469+
/// The SHA-256 hash of [`Program::get_script_pubkey`], as Esplora's `scripthash` endpoints
470+
/// and the `jet::output_script_hash` family use.
471+
///
472+
/// # Panics
473+
/// Panics if the program doesn't compile.
431474
#[must_use]
432475
pub fn get_script_hash(&self, network: &SimplicityNetwork) -> [u8; 32] {
433476
hash_script(&self.get_script_pubkey(network))
434477
}
435478

436-
/// Compiles the program and returns its Commitment Merkle Root.
479+
/// The program's Commitment Merkle Root.
437480
///
438481
/// # Panics
439482
/// Panics if the `SimplicityHL` compilation fails.
@@ -442,7 +485,7 @@ impl Program {
442485
self.cmr().unwrap_or_else(|error| panic!("{error}")).to_byte_array()
443486
}
444487

445-
/// Returns the 32-byte tapleaf hash of the program's Simplicity script.
488+
/// The tapleaf hash of the program's leaf. Storage doesn't change it.
446489
///
447490
/// # Panics
448491
/// Panics if the `SimplicityHL` compilation fails.
@@ -453,7 +496,7 @@ impl Program {
453496
taproot::TapLeafHash::from_script(&script, version).to_byte_array()
454497
}
455498

456-
/// Retrieves program ABI metadata for argument types.
499+
/// The names and types of the program's `param::` arguments.
457500
///
458501
/// # Errors
459502
/// Returns a `ProgramError` if compilation fails or generating ABI metadata fails.
@@ -466,18 +509,18 @@ impl Program {
466509
Ok(abi_meta.param_types)
467510
}
468511

469-
/// Retrieves the witness types of the compiled program.
512+
/// The names and types of the program's `witness::` fields.
470513
///
471514
/// # Errors
472515
/// Returns a `ProgramError` if compilation fails.
473516
pub fn get_witness_types(&self) -> Result<WitnessTypes, ProgramError> {
474517
Ok(self.load()?.witness_types().shallow_clone())
475518
}
476519

477-
/// Compiles program by providing saved `Arguments`.
520+
/// Compiles the program, or returns the cached result of an earlier compilation.
478521
///
479522
/// # Errors
480-
/// Retruns an error we have problems in a program compilation.
523+
/// Returns `ProgramError::Compilation` if the source doesn't compile with these arguments.
481524
pub fn load(&self) -> Result<&CompiledProgram, ProgramError> {
482525
Ok(&self.load_with_cmr()?.0)
483526
}

‎crates/sdk/src/program/error.rs‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,20 @@ pub enum ProgramError {
3131
#[error("Failed to satisfy witness: {0}")]
3232
WitnessSatisfaction(String),
3333

34-
/// Error indicating pruning the node tree during execution failed safely.
34+
/// The program failed. `ExecutionError::JetFailed` is a failed `assert!` or jet, and
35+
/// `ExecutionError::ReachedPrunedBranch` is, for example, `unwrap(None)`.
36+
///
37+
/// [`Program`](super::Program) reports both a failure during pruning and a failure in the Bit
38+
/// Machine run after pruning with this variant.
3539
#[error("Failed to prune program: {0}")]
3640
Pruning(#[from] simplicityhl::simplicity::bit_machine::ExecutionError),
3741

3842
/// Error thrown when the bit machine cannot be initialized due to complexity or limit restrictions.
3943
#[error("Failed to construct a Bit Machine with enough space: {0}")]
4044
BitMachineCreation(#[from] simplicityhl::simplicity::bit_machine::LimitError),
4145

42-
/// Error thrown during bit machine execution due to underlying logical or environment validation errors.
46+
/// The Bit Machine rejected the pruned program. [`Program`](super::Program) doesn't return
47+
/// this variant; it reports such failures as `Pruning`.
4348
#[error("Failed to execute program on the Bit Machine: {0}")]
4449
Execution(simplicityhl::simplicity::bit_machine::ExecutionError),
4550

@@ -59,12 +64,13 @@ pub enum ProgramError {
5964
utxo_count: usize,
6065
},
6166

62-
/// Error indicating the script pubkey present on the targeted UTXO differs from the expectation.
67+
/// The input spends an output whose script isn't the program's.
6368
#[error("Script pubkey mismatch: expected hash {expected_hash}, got {actual_hash}")]
6469
ScriptPubkeyMismatch {
65-
/// The expected CMR (Commitment Merkle Root) hash.
70+
/// The HASH160 of the script of the program's address, not the SHA-256 that
71+
/// [`Program::get_script_hash`](super::Program::get_script_hash) returns.
6672
expected_hash: String,
67-
/// The actual CMR hash present in the script pubkey.
73+
/// The HASH160 of the script of the spent output.
6874
actual_hash: String,
6975
},
7076

‎crates/sdk/src/program/logger.rs‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ pub struct ProgramInfo {
3232
pub trace_buffer: Vec<String>,
3333
}
3434

35-
/// Logger state for buffering execution output.
35+
/// Buffers program execution output, per input and per thread, until it is printed.
3636
///
37-
/// Buffers are flushed to stderr only on successful transaction finalization,
38-
/// discarding output from intermediate estimation passes.
37+
/// With [`Verbosity`](crate::global::Verbosity) `Debug` or `Trace`, every
38+
/// [`ProgramTrait::execute`](super::ProgramTrait::execute) replaces its input's buffer, so only
39+
/// the last run's output is kept. `Signer::finalize` and `estimate_fee` print the buffers after
40+
/// their last signing pass, and `finalize_strict` does when it succeeds. Code that calls
41+
/// `ProgramTrait::execute` or `Signer::sign_tx` directly must call [`ProgramLogger::flush_logs`]
42+
/// to see the output. `Signer::sign_witnesses` doesn't run any program.
3943
pub struct ProgramLogger {
4044
/// A mapping of program ids to program logs
4145
pub logs: HashMap<usize, ProgramInfo>,

‎crates/sdk/src/program/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pub mod core;
33
/// Error types and definitions for program compilation, manipulation, and execution failures.
44
pub mod error;
5-
/// Program execution's specific logger
5+
/// Buffering of the output of program execution.
66
pub mod logger;
77

88
pub use core::{Program, ProgramFactory, ProgramSchema, ProgramTrait};

0 commit comments

Comments
 (0)