Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions libs/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,14 @@ automatically.
| `st_set_insert(old, v, new)`, `st_set_delete(old, v, new)` | `new` is `old` with `v` added / removed |
| `st_array_update(old, i, v, new)` | `new` is `old` with index `i` set to `v` |

Two limits are worth knowing before reaching for these:
One limit is worth knowing before reaching for these:

- The transition statements (`st_*_insert` / `_update` / `_delete`) constrain
a relation between two container values. They do not compute the new
container, so it has to come from somewhere else: an object's entry, or a
witness from an `unsafe` block. Until the SDK can build container values
(see Missing features), the reachable use is relating two containers a
script already holds.
- A field read of an object's whole-dict form is fine on inputs and mutates,
but reading a field of an *output* you just built (`out.durability`) is not
supported: the emitter renders it as `initials.out.durability`, a double
anchor podlang has no syntax for, and the module fails to compile.

## Type checking

Expand Down Expand Up @@ -238,7 +234,6 @@ as opaque entropy, not for byte-exact comparison with the L1 hash.
- [x] manifest support
- [ ] error pretty print
- [x] forbid Object::set after the object has been used in other operations
- [ ] read a field of an output created in the same action (`out.field`)

# Test example

Expand Down
70 changes: 41 additions & 29 deletions libs/sdk/src/fmt_podlang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ pub(crate) const fn output_max_ts(base_ts: usize, is_output: bool) -> usize {
if is_output { base_ts + 1 } else { base_ts }
}

/// Inverse of the ts `output_max_ts` reserves: where an Output's
/// script-final form sits, which is what the `initials` record holds and
/// what TxInsert takes as the object's initial state.
pub(crate) const fn initials_ts(max_ts: usize) -> usize {
max_ts.saturating_sub(1)
}

/// An action's chain max_ts must be at least this for the SDK to pack
/// intermediate chain states into a `<Action>Chain` record. Below the
/// threshold, the per-step scalar wildcards (`chain1`, `chain2`, ...)
Expand Down Expand Up @@ -246,12 +253,12 @@ fn fmt_record_decls(loader: &Loader, w: &mut dyn fmt::Write) -> fmt::Result {
)?;
}
if let Some(initials) = &meta.initials_entries {
writeln!(
w,
"record {} = ({})",
schema_name_initials(&meta.name),
render(initials),
)?;
let names = initials
.iter()
.map(|e| e.varname.as_str())
.collect::<Vec<_>>()
.join(", ");
writeln!(w, "record {} = ({names})", schema_name_initials(&meta.name),)?;
}
}
Ok(())
Expand Down Expand Up @@ -422,29 +429,34 @@ fn fmt_action(action: &ActionContext, loader: &Loader, w: &mut dyn fmt::Write) -
// sides that need a wildcard; collapsed sides drop the clause.
for o in &meta.object_refs {
let max_ts = meta.max_ts(&o.varname);
if meta
.in_entry(&o.varname)
.is_some_and(|(_, e)| e.needs_wildcard)
{
writeln!(
w,
" ArrayContains(io, {}::in_{}, {})",
schema_name_io(&action.name),
o.varname,
fmt_var_at(&o.varname, 0, max_ts),
)?;
}
if meta
.out_entry(&o.varname)
.is_some_and(|(_, e)| e.needs_wildcard)
{
writeln!(
w,
" ArrayContains(io, {}::out_{}, {})",
schema_name_io(&action.name),
o.varname,
fmt_var_at(&o.varname, max_ts, max_ts),
)?;
let io_schema = schema_name_io(&action.name);
for (entry, record, entry_name, ts) in [
(
meta.in_entry(&o.varname),
"io",
format!("{io_schema}::in_{}", o.varname),
0,
),
(
meta.out_entry(&o.varname),
"io",
format!("{io_schema}::out_{}", o.varname),
max_ts,
),
(
meta.initials_entry(&o.varname),
"initials",
format!("{}::{}", schema_name_initials(&action.name), o.varname),
initials_ts(max_ts),
),
] {
if entry.is_some_and(|(_, e)| e.needs_wildcard) {
writeln!(
w,
" ArrayContains({record}, {entry_name}, {})",
fmt_var_at(&o.varname, ts, max_ts),
)?;
}
}
}
// Pin each referenced sub-action alias to its sub's first out
Expand Down
Loading