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
22 changes: 22 additions & 0 deletions knowledge/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,28 @@
"u32_add_drop_stack"
]
},
{
"id": "conditional-negate",
"label": "u32_conditional_negate()",
"parameters": {
"condition": "nonzero means negate",
"word_representation": "four byte limbs"
},
"includes": "fragment-only: condition normalization, branch, zero-word construction, and modulo-2^32 subtraction; excludes word pushes and output check",
"script_bytes": 83,
"witness_bytes": 0,
"witness_bytes_max": 0,
"max_stack_items": 9,
"executed_opcodes": 52,
"validation_weight": null,
"setup_script_bytes": 0,
"per_use_script_bytes": 83,
"metric_keys": [
"u32_conditional_negate",
"u32_conditional_negate_stack",
"u32_conditional_negate_opcodes"
]
},
{
"id": "xor-memory",
"label": "u8_push_xor_table()",
Expand Down
1 change: 1 addition & 0 deletions knowledge/comparisons/arithmetic.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ differ. Follow each catalog configuration before comparing numbers.
| Need | Local construction | Representative script bytes | Main constraint |
| --- | --- | ---: | --- |
| Small constant product | ScriptNum × 13 | 10 | Four-byte ScriptNum domain |
| Conditional u32 sign routing | `u32_conditional_negate()` | 83 | 9-item peak; condition normalized before branch |
| Small-field add | M31 u31 add | 18 | Canonical field input |
| Small-field variable multiply | M31 u31 multiply | 1,370 | Witness quotient relation |
| 32 checked nibbles to 128 bits | u4 staggered batch table | 924 | 189-item peak; tapscript-oriented |
Expand Down
6 changes: 4 additions & 2 deletions knowledge/primitives/u32.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

Represents a 32-bit word as four byte-valued stack items. The module provides
addition, subtraction, comparison, Boolean operations, shifts, rotations, and
stack manipulation.
stack manipulation. `u32_conditional_negate` adds normalized conditional sign
routing for a byte-word.

- **Position:** the main local byte-oriented backend for SHA-family and
RIPEMD-family constructions.
- **Evidence:** locally reproduced, including exhaustive byte-level logic tests.
- **Tradeoff:** operation fragments are moderate, while a reusable Boolean table
occupies 256 stack items.
- **Representative results:** add is 78 bytes; subtract is 77; less-than is 39.
- **Representative results:** add is 78 bytes; subtract is 77; conditional
negate is 83; less-than is 39.
- **Consumers:** SHA-1, SHA-256, RIPEMD-160, and SHAKE256.

See the [implementation README](../../src/arithmetic/u32/README.md),
Expand Down
11 changes: 11 additions & 0 deletions src/arithmetic/u32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ they do not use BN254 or any other field modulus.
`2^32`. The non-`drop` form preserves the word selected by `a`.
- `u32_sub[_drop](a, b)` computes `a - b` modulo `2^32` for either ordering of
two distinct offsets. The non-`drop` form preserves the minuend.
- `u32_conditional_negate()` normalizes a top condition and negates the next
word modulo `2^32` when it is nonzero.
- `u32_{less,greater}than[orequal]()` compares the top two words as unsigned
integers and consumes both.
- `u32_or(a, b, stack_size)`, like XOR and AND, takes distinct word offsets.
Expand All @@ -33,13 +35,16 @@ as less-than-or-equal.
| --- | ---: | ---: | ---: |
| `u32_add_drop(0, 1)` | <!-- metric:u32_add_drop -->78<!-- /metric:u32_add_drop --> bytes | 0 bytes | <!-- metric:u32_add_drop_stack -->10<!-- /metric:u32_add_drop_stack --> items |
| `u32_sub_drop(0, 1)` | <!-- metric:u32_sub_drop -->77<!-- /metric:u32_sub_drop --> bytes | 0 bytes | <!-- metric:u32_sub_drop_stack -->9<!-- /metric:u32_sub_drop_stack --> items |
| `u32_conditional_negate()` | <!-- metric:u32_conditional_negate -->83<!-- /metric:u32_conditional_negate --> bytes | 0 bytes | <!-- metric:u32_conditional_negate_stack -->9<!-- /metric:u32_conditional_negate_stack --> items |
| `u32_lessthan()` | <!-- metric:u32_lessthan -->38<!-- /metric:u32_lessthan --> bytes | 0 bytes | <!-- metric:u32_lessthan_stack -->9<!-- /metric:u32_lessthan_stack --> items |
| `u32_lessthanorequal()` | <!-- metric:u32_lessthanorequal -->61<!-- /metric:u32_lessthanorequal --> bytes | 0 bytes | <!-- metric:u32_lessthanorequal_stack -->13<!-- /metric:u32_lessthanorequal_stack --> items |
| `u32_or(0, 1, 3)` (table excluded) | <!-- metric:u32_or -->326<!-- /metric:u32_or --> bytes | 0 bytes | <!-- metric:u32_or_stack -->272<!-- /metric:u32_or_stack --> items, including table |
| `u32_notequal()` | <!-- metric:u32_notequal -->19<!-- /metric:u32_notequal --> bytes | 0 bytes | <!-- metric:u32_notequal_stack -->9<!-- /metric:u32_notequal_stack --> items |
| `u8_push_xor_table()` | <!-- metric:u8_logic_table_push -->236<!-- /metric:u8_logic_table_push --> bytes | 0 bytes | 256 table items |
| `u8_drop_xor_table()` | <!-- metric:u8_logic_table_drop -->128<!-- /metric:u8_logic_table_drop --> bytes | 0 bytes | consumes 256 table items |

The conditional-negation fragment contains <!-- metric:u32_conditional_negate_opcodes -->52<!-- /metric:u32_conditional_negate_opcodes --> static non-push opcodes under the repository's compilation policy. The local tapscript executor does not expose a useful dynamic opcode counter for this fragment.

Operand witness serialization is deliberately excluded: callers may construct
words inside the locking script or supply four witness items per word. No
operation-specific hint is needed. The logic table can be shared by any number
Expand Down Expand Up @@ -68,3 +73,9 @@ caller.
No hints are required. A witness-supplied word occupies four stack items, most
significant byte first in the module's normal representation. Binary operation
inputs and any shared logic table must already be at the documented depths.

`u32_conditional_negate()` consumes a condition above one word and leaves the
word unchanged for zero, or returns its modulo-`2^32` negation for any nonzero
condition. It normalizes the condition before `OP_IF`, so non-minimal boolean
values such as `2` and `-1` are accepted as true. It inherits the module's
byte-limb contract and does not itself range-check the four word limbs.
39 changes: 39 additions & 0 deletions src/arithmetic/u32/stack.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::arithmetic::u32::sub::u32_sub_drop;
use crate::support::script::*;
use crate::support::script_ops::{push_to_stack, OP_256MUL, OP_4DUP};

Expand Down Expand Up @@ -55,6 +56,22 @@ pub fn u32_notequal() -> Script {
}
}

/// Conditionally negates the top u32 word modulo 2^32.
///
/// Before: `preserved | word | condition`.
/// After: `preserved | (condition == 0 ? word : -word)`.
/// The condition is normalized with `OP_0NOTEQUAL`; the word must already use
/// the module's four-byte representation.
pub fn u32_conditional_negate() -> Script {
script! {
OP_0NOTEQUAL
OP_IF
{ u32_push(0) }
{ u32_sub_drop(0, 1) }
OP_ENDIF
}
}

pub fn u32_toaltstack() -> Script {
script! {
OP_TOALTSTACK
Expand Down Expand Up @@ -178,4 +195,26 @@ mod tests {
run(script);
}
}

#[test]
fn test_u32_conditional_negate() {
let words = [0, 1, 0xff, 0x100, 0x7fff_ffff, 0x8000_0000, u32::MAX];
for word in words {
for condition in [0i64, 1, 2, -1] {
let expected = if condition == 0 {
word
} else {
word.wrapping_neg()
};
let script = script! {
{ u32_push(word) }
{ condition }
{ u32_conditional_negate() }
{ u32_push(expected) }
{ u32_equal() }
};
run(script);
}
}
}
}
23 changes: 23 additions & 0 deletions tests/primitive_metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,29 @@ fn metrics() -> Vec<Metric> {
vec![],
),
},
Metric {
readme: "src/arithmetic/u32/README.md",
key: "u32_conditional_negate",
value: script_len(u32::stack::u32_conditional_negate()),
},
Metric {
readme: "src/arithmetic/u32/README.md",
key: "u32_conditional_negate_stack",
value: max_stack_items(
script! {
{ u32::stack::u32_push(0x8000_0000) }
{ 1 }
{ u32::stack::u32_conditional_negate() }
OP_TRUE
},
vec![],
),
},
Metric {
readme: "src/arithmetic/u32/README.md",
key: "u32_conditional_negate_opcodes",
value: static_non_push_opcodes(u32::stack::u32_conditional_negate()),
},
Metric {
readme: "src/arithmetic/u32/README.md",
key: "u32_lessthan",
Expand Down