Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0412c53
add compiler support for validateOutputStateWithTemplate + tests
michaelsutton Mar 16, 2026
b4260cb
add negative compiler tests for validateOutputStateWithTemplate
michaelsutton Mar 17, 2026
358708c
document and highlight validateOutputStateWithTemplate
michaelsutton Mar 17, 2026
a257c54
fix exponential script growth from conditional assignments in unrolle…
michaelsutton Mar 17, 2026
1a3af5c
expose `OpTxInputDaaScore` in silverscript
michaelsutton Mar 18, 2026
bf003ae
fmt
michaelsutton Mar 18, 2026
f3f0469
support validateOutputStateWithTemplate with passed struct layouts
michaelsutton Mar 19, 2026
8921d81
add cross-template input-state reading with template and P2SH validation
michaelsutton Mar 20, 2026
9e865a4
add documented state-routing builtin signatures to std/builtins.sil
michaelsutton Mar 21, 2026
34eb170
add return types to std input-state builtin signatures
michaelsutton Mar 21, 2026
22bf6ea
update tutorial docs for typed validateOutputStateWithTemplate
michaelsutton Mar 22, 2026
e824f9b
refactor compiler stack bindings into BindingStack
michaelsutton Mar 22, 2026
02dc870
extract stack binding pick and rebinding helpers
michaelsutton Mar 23, 2026
16367fe
reconcile stack bindings after O(1) reassignments
michaelsutton Mar 23, 2026
88b3c9a
extract stack bindings module and streamline stack reordering
michaelsutton Mar 23, 2026
564eb3b
add unit tests for stack binding reordering and rebinding
michaelsutton Mar 23, 2026
9d0734b
refactor stack bindings to use IndexSet and enforce dense depths
michaelsutton Mar 23, 2026
e734078
import some tests from `reassign-opt` branch and fix struct reassign
michaelsutton Mar 23, 2026
c6e99ae
Merge branch 'master' into compiler-misc
michaelsutton Mar 23, 2026
87f49f5
add branch heavy script validation tests (with rust ref)
michaelsutton Mar 23, 2026
281efd9
streamline stack reordering over IndexSet
michaelsutton Mar 24, 2026
1f0c7fc
fix inline stack-local reassignment env syncing
michaelsutton Mar 24, 2026
1384e8a
Some PR fixes
someone235 Mar 25, 2026
3424b43
remove redundant dup after deep pick
michaelsutton Mar 25, 2026
6bd31a7
gitignore codex
michaelsutton Mar 25, 2026
9b31bb9
expand stack reordering tests against txscript ground truth
michaelsutton Mar 25, 2026
7604b36
update compiler tests for dup and over fast paths
michaelsutton Mar 25, 2026
d323dbb
update compiler stress-test size snapshots
michaelsutton Mar 25, 2026
42c6e64
document longest_keepable_suffix_start with visual examples
michaelsutton Mar 25, 2026
8c9c7f0
rename depth_from_top -> depth
michaelsutton Mar 25, 2026
061533f
add compiler-owned chess app regression tests
michaelsutton Mar 25, 2026
ca03c8c
document and simplify local stack opcode helpers
michaelsutton Mar 25, 2026
997c650
rename stack binding storage field from `names` to `stack`
michaelsutton Mar 25, 2026
bd5d48f
simplify local opcode engine test with sentinel stack values
michaelsutton Mar 25, 2026
7dd5d56
optimize stack depth helpers and refresh size snapshots
michaelsutton Mar 25, 2026
5fd6edb
rewrite stack depth helpers as match arms with visuals
michaelsutton Mar 25, 2026
0990c3f
move stack reordering planning to a permutation model
michaelsutton Mar 25, 2026
c2b9abe
clean up stack binding helper names and docs
michaelsutton Mar 25, 2026
7e8ce1b
address review comment: support stack-bound reassignment for non-stru…
michaelsutton Mar 26, 2026
c167cbd
use slice destructuring for builtin arg counts
michaelsutton Mar 26, 2026
c881990
document pseudocode for compile_read_input_state_with_template_valida…
michaelsutton Mar 26, 2026
d56ae55
Merge branch 'master' into compiler-misc
michaelsutton Mar 26, 2026
4d06f31
cache compiled chess test contracts and shared fixtures
michaelsutton Mar 26, 2026
4f7a5f7
inline chess fixture constants and annotate their numeric values
michaelsutton Mar 26, 2026
c30540b
rename stack binding insertion helper and tighten invariants
michaelsutton Mar 26, 2026
7372b27
document target-indexed permutation convention
michaelsutton Mar 26, 2026
e88b352
implement ordered PartialEq/Eq for StackBindings
michaelsutton Mar 26, 2026
85212ec
review comment: factor non-struct assignment check into a named local
michaelsutton Mar 26, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target
.cargo
.vscode/*
.codex/*
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kaspa-hashes = { git = "https://github.com/kaspanet/rusty-kaspa", branch = "tn12
kaspa-txscript = { git = "https://github.com/kaspanet/rusty-kaspa", branch = "tn12" }
kaspa-txscript-errors = { git = "https://github.com/kaspanet/rusty-kaspa", branch = "tn12" }
blake2b_simd = "1.0.2"
indexmap = "2.7.0"
rand = "0.8.5"
secp256k1 = { version = "0.29.0", features = [
"global-context",
Expand Down
6 changes: 5 additions & 1 deletion debugger/session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,11 @@ impl<'a, 'i> DebugSession<'a, 'i> {

let mut shadow_bindings = shadow_by_name.into_values().collect::<Vec<_>>();
shadow_bindings.sort_by(|left, right| right.stack_index.cmp(&left.stack_index));
let stack_bindings = shadow_bindings.iter().map(|binding| (binding.name.clone(), binding.stack_index)).collect();
let stack_bindings = shadow_bindings
.iter()
.enumerate()
.map(|(index, binding)| (binding.name.clone(), (shadow_bindings.len() - 1 - index) as i64))
.collect();
Ok((shadow_bindings, env, stack_bindings, eval_types))
}

Expand Down
20 changes: 13 additions & 7 deletions debugger/session/tests/debug_session_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,18 +322,15 @@ contract Virtuals() {
session.run_to_first_executed_statement()?;
let first = session.current_step().ok_or("missing first location")?;
assert!(matches!(first.kind, StepKind::Source {}));
assert_eq!(first.bytecode_start, first.bytecode_end, "first step should be zero-width");
let first_pc = session.state().pc;
assert!(first.bytecode_end > first.bytecode_start, "first step should execute bytecode");

let second = session.step_over()?.ok_or("missing second step")?.step.ok_or("missing second step payload")?;
assert!(matches!(second.kind, StepKind::Source {}));
assert_eq!(second.bytecode_start, second.bytecode_end, "second step should be zero-width");
assert_eq!(session.state().pc, first_pc, "virtual step should not execute opcodes");
assert!(second.bytecode_end > second.bytecode_start, "second step should execute bytecode");

let third = session.step_over()?.ok_or("missing third step")?.step.ok_or("missing third step payload")?;
assert!(matches!(third.kind, StepKind::Source {}));
assert!(third.bytecode_end > third.bytecode_start, "third step should execute bytecode");
assert_eq!(session.state().pc, first_pc, "first real statement should still be at same pc boundary");
Ok(())
})
}
Expand All @@ -356,12 +353,21 @@ contract OpcodeCursor() {
let start = session.current_span().ok_or("missing start span")?;
assert_eq!(start.line, 5);

session.step_opcode()?.ok_or("expected si to execute one opcode")?;
// `si` should eventually refresh the statement cursor once execution crosses a statement boundary.
// The exact opcode count is not stable when compiler lowering changes.
for _ in 0..50 {
session.step_opcode()?.ok_or("expected si to execute one opcode")?;
let after_si = session.current_span().ok_or("missing span after si")?;
if after_si.line != start.line {
break;
}
}
let after_si = session.current_span().ok_or("missing span after si")?;
assert_ne!(after_si.line, start.line, "si should refresh statement cursor");

let x = session.variable_by_name("x")?;
assert_eq!(format_value(&x.type_name, &x.value), "1");
// After crossing the first statement boundary, `x = a + 1` should have executed.
assert_eq!(format_value(&x.type_name, &x.value), "4");
Ok(())
})
}
Expand Down
4 changes: 2 additions & 2 deletions docs/DECL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This document specifies the covenant declaration API, where users declare policy functions and the compiler generates the corresponding covenant entrypoints and wrappers.

Without declarations, these patterns are written manually with `OpAuth*`/`OpCov*` plus `readInputState`/`validateOutputState`. The declaration layer standardizes that pattern, removes user boilerplate, and acts as a security guard so users do not need to be experts in covenant opcodes to write secure covenants.
Without declarations, these patterns are written manually with `OpAuth*`/`OpCov*` plus `readInputState`/`validateOutputState` (or `validateOutputStateWithTemplate` for cross-template routing). The declaration layer standardizes that pattern, removes user boilerplate, and acts as a security guard so users do not need to be experts in covenant opcodes to write secure covenants.

Scope: syntax and lowering semantics.

Expand Down Expand Up @@ -369,5 +369,5 @@ contract SeqCommitMirror(byte[32] init_seqcommit) {

1. `State` is an implicit compiler type synthesized from contract fields.
2. Internally the compiler can lower `State`/`State[]` into any representation; this doc only fixes the user-facing API.
3. Existing `readInputState`/`validateOutputState` remain the codegen backbone.
3. Existing `readInputState`/`validateOutputState` remain the codegen backbone; `validateOutputStateWithTemplate` is available for manual cross-template routing, not declaration lowering.
4. `N:M` lowering keeps one transition group per transaction.
64 changes: 50 additions & 14 deletions docs/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
- [Output Introspection](#output-introspection)
11. [Covenants](#covenants)
- [Creating ScriptPubKey](#creating-scriptpubkey)
- [State Transition Helper](#state-transition-helper)
- [State Transition Builtins](#state-transition-builtins)
- [Covenant Examples](#covenant-examples)
12. [Advanced Features](#advanced-features)
- [Constants](#constants)
Expand Down Expand Up @@ -926,15 +926,54 @@ Create a P2SH scriptPubKey directly from a redeem script:
byte[35] outputScriptPubKey = new ScriptPubKeyP2SHFromRedeemScript(redeemScript);
```

### State Transition Helper
### State Transition Builtins

**`validateOutputState(int outputIndex, object newState)`**
SilverScript provides four builtins for state routing and cross-template state inspection.

Validates that `tx.outputs[outputIndex].scriptPubKey` is a P2SH paying to the **same contract code** with **updated state fields**.
- **Validate Output State**: validate continuation into the same contract template. `newState` must provide every state field exactly once in the local `State` layout.

Small example:
```js
validateOutputState(int outputIndex, object newState)
```

```javascript
- **Validate Output State With Template**: validate continuation into a foreign contract template. `newState` is encoded using the struct layout implied by the value you pass, then inserted between `templatePrefix` and `templateSuffix`.

```js
validateOutputStateWithTemplate(
int outputIndex,
object newState,
byte[] templatePrefix,
byte[] templateSuffix,
byte[32] expectedTemplateHash
)
```

- **Read Input State**: read another input as this contract's own `State`.

```js
readInputState(int inputIndex)
```

- **Read Input State With Template**: read another input using a foreign struct layout. It checks the foreign template hash and the foreign input's P2SH commitment before decoding.

```js
readInputStateWithTemplate(
int inputIndex,
int templatePrefixLen,
int templateSuffixLen,
byte[32] expectedTemplateHash
)
```

Use it with a direct struct binding or destructuring assignment:

```js
OtherState other = readInputStateWithTemplate(inputIndex, templatePrefixLen, templateSuffixLen, expectedTemplateHash);
```

Same-template example:

```js
pragma silverscript ^0.1.0;

contract Counter(int initCount, byte[2] initTag) {
Expand All @@ -947,15 +986,12 @@ contract Counter(int initCount, byte[2] initTag) {
}
```

What this checks:

- Reads the current redeem script from the active input sigscript.
- Keeps the contract tail (the immutable "rest of script") unchanged.
- Rebuilds a new redeem script using the provided next field values (`count`, `tag`) + the same tail.
- Computes the P2SH scriptPubKey for that new redeem script.
- Verifies output `0` has exactly that scriptPubKey.
Input-side note:

In practice, this enforces that the transaction creates the next valid contract state rather than an arbitrary output script.
- `readInputState(...)` and `readInputStateWithTemplate(...)` are input-state decoders. They read bytes from another input's sigscript and decode them as state.
- `readInputState(...)` is appropriate when the surrounding covenant domain guarantees a single allowed contract/layout for the foreign input.
- `readInputStateWithTemplate(...)` is appropriate when multiple templates may share a covenant domain; it additionally validates the foreign input's template hash and checks that the claimed redeem-script bytes match the foreign input's P2SH `scriptPubKey`.
- Without those surrounding guarantees, plain `readInputState(...)` would also need extra correlation checks between the foreign input and the inspected part of its sigscript.

### Covenant Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
(function_call
(identifier) @function.builtin
(#match? @function.builtin
"^(readInputState|validateOutputState|verifyOutputState|verifyOutputStates|OpSha256|sha256|OpTxSubnetId|OpTxGas|OpTxPayloadLen|OpTxPayloadSubstr|OpOutpointTxId|OpOutpointIndex|OpTxInputScriptSigLen|OpTxInputScriptSigSubstr|OpTxInputSeq|OpTxInputIsCoinbase|OpTxInputSpkLen|OpTxInputSpkSubstr|OpTxOutputSpkLen|OpTxOutputSpkSubstr|OpAuthOutputCount|OpAuthOutputIdx|OpInputCovenantId|OpCovInputCount|OpCovInputIdx|OpCovOutputCount|OpCovOutputIdx|OpNum2Bin|OpBin2Num|OpChainblockSeqCommit|checkDataSig|checkSig|checkMultiSig|blake2b)$"))
"^(readInputState|readInputStateWithTemplate|validateOutputState|validateOutputStateWithTemplate|verifyOutputState|verifyOutputStates|OpSha256|sha256|OpTxSubnetId|OpTxGas|OpTxPayloadLen|OpTxPayloadSubstr|OpOutpointTxId|OpOutpointIndex|OpTxInputScriptSigLen|OpTxInputScriptSigSubstr|OpTxInputSeq|OpTxInputIsCoinbase|OpTxInputSpkLen|OpTxInputSpkSubstr|OpTxOutputSpkLen|OpTxOutputSpkSubstr|OpAuthOutputCount|OpAuthOutputIdx|OpInputCovenantId|OpCovInputCount|OpCovInputIdx|OpCovOutputCount|OpCovOutputIdx|OpNum2Bin|OpBin2Num|OpChainblockSeqCommit|checkDataSig|checkSig|checkMultiSig|blake2b)$"))

(unary_suffix) @property

Expand Down
2 changes: 1 addition & 1 deletion extensions/vscode/queries/highlights.scm
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
(function_call
(identifier) @function.builtin
(#match? @function.builtin
"^(readInputState|validateOutputState|verifyOutputState|verifyOutputStates|OpSha256|sha256|OpTxSubnetId|OpTxGas|OpTxPayloadLen|OpTxPayloadSubstr|OpOutpointTxId|OpOutpointIndex|OpTxInputScriptSigLen|OpTxInputScriptSigSubstr|OpTxInputSeq|OpTxInputIsCoinbase|OpTxInputSpkLen|OpTxInputSpkSubstr|OpTxOutputSpkLen|OpTxOutputSpkSubstr|OpAuthOutputCount|OpAuthOutputIdx|OpInputCovenantId|OpCovInputCount|OpCovInputIdx|OpCovOutputCount|OpCovOutputIdx|OpNum2Bin|OpBin2Num|OpChainblockSeqCommit|checkDataSig|checkSig|checkMultiSig|blake2b)$"))
"^(readInputState|readInputStateWithTemplate|validateOutputState|validateOutputStateWithTemplate|verifyOutputState|verifyOutputStates|OpSha256|sha256|OpTxSubnetId|OpTxGas|OpTxPayloadLen|OpTxPayloadSubstr|OpOutpointTxId|OpOutpointIndex|OpTxInputScriptSigLen|OpTxInputScriptSigSubstr|OpTxInputSeq|OpTxInputIsCoinbase|OpTxInputSpkLen|OpTxInputSpkSubstr|OpTxOutputSpkLen|OpTxOutputSpkSubstr|OpAuthOutputCount|OpAuthOutputIdx|OpInputCovenantId|OpCovInputCount|OpCovInputIdx|OpCovOutputCount|OpCovOutputIdx|OpNum2Bin|OpBin2Num|OpChainblockSeqCommit|checkDataSig|checkSig|checkMultiSig|blake2b)$"))

(unary_suffix) @property

Expand Down
2 changes: 1 addition & 1 deletion extensions/zed/languages/silverscript/highlights.scm
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
(function_call
(identifier) @function.builtin
(#match? @function.builtin
"^(readInputState|validateOutputState|verifyOutputState|verifyOutputStates|OpSha256|sha256|OpTxSubnetId|OpTxGas|OpTxPayloadLen|OpTxPayloadSubstr|OpOutpointTxId|OpOutpointIndex|OpTxInputScriptSigLen|OpTxInputScriptSigSubstr|OpTxInputSeq|OpTxInputIsCoinbase|OpTxInputSpkLen|OpTxInputSpkSubstr|OpTxOutputSpkLen|OpTxOutputSpkSubstr|OpAuthOutputCount|OpAuthOutputIdx|OpInputCovenantId|OpCovInputCount|OpCovInputIdx|OpCovOutputCount|OpCovOutputIdx|OpNum2Bin|OpBin2Num|OpChainblockSeqCommit|checkDataSig|checkSig|checkMultiSig|blake2b)$"))
"^(readInputState|readInputStateWithTemplate|validateOutputState|validateOutputStateWithTemplate|verifyOutputState|verifyOutputStates|OpSha256|sha256|OpTxSubnetId|OpTxGas|OpTxPayloadLen|OpTxPayloadSubstr|OpOutpointTxId|OpOutpointIndex|OpTxInputScriptSigLen|OpTxInputScriptSigSubstr|OpTxInputSeq|OpTxInputIsCoinbase|OpTxInputSpkLen|OpTxInputSpkSubstr|OpTxOutputSpkLen|OpTxOutputSpkSubstr|OpAuthOutputCount|OpAuthOutputIdx|OpInputCovenantId|OpCovInputCount|OpCovInputIdx|OpCovOutputCount|OpCovOutputIdx|OpNum2Bin|OpBin2Num|OpChainblockSeqCommit|checkDataSig|checkSig|checkMultiSig|blake2b)$"))

(unary_suffix) @property

Expand Down
1 change: 1 addition & 0 deletions silverscript-lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ kaspa-consensus-core.workspace = true
kaspa-txscript.workspace = true
kaspa-txscript-errors.workspace = true
blake2b_simd.workspace = true
indexmap.workspace = true
chrono = "0.4"
pest = "2.7"
pest_derive = "2.7"
Expand Down
Loading