Conversation
sigma-state restricts tuples to exactly 2 elements: `Tuple.eval` rejects `items.length != 2` with "Invalid tuple" (values.scala: "in v5.0 version we support only tuples of 2 elements to be equivalent with v4.x"). The check is unconditional (no version gate); sigma-state 6.0.3 is JIT-only and evaluates every height through it. sigma-rust models tuples as flat N-ary (`TupleItems = BoundedVec<2,255>`) and its `Tuple` eval had no arity check, so it accepted arity>=3 tuples the JVM rejects — a consensus accept/reject divergence: sigma-rust would accept a spend the JVM rejects (crafter-constructible). Surfaced by the santa eval fixture `tuple_triple_bool_byte_short` (tree 0086030101020703a413): JVM rejects "Invalid tuple"; sigma-rust accepted Tuple[true, 7, 1234]. Mirror the JVM: reject `items.len() != 2` at eval. No mainnet block can carry an arity>=3 tuple (the JVM rejects them), so this cannot regress historical sync. Regression test added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
powHit computes the Autolykos-2 PoW hit value (a big integer), so its
result type is UnsignedBigInt — matching Scala SGlobalMethods.powHit and
the interpreter's POW_HIT_EVAL_FN, which already yields
Value::UnsignedBigInt. The descriptor mis-declared SBoolean, so
coll.map(x => Global.powHit(..)).exists((u: UnsignedBigInt) => ..) failed
parse-time type-checking ("Invalid condition tpe"), wedging a full node
off testnet at canonical block 28,474.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TransactionContext::validate summed input and output box values with plain u64 sums, so an overflowing aggregate panicked in debug builds and wrapped in release builds before BoxValue::new could reject it — the input check misfired and the output sum had no check at all. The reference implementation sums with Math.addExact over longs (ErgoTransaction.validateStateful), trapping every addition. Sum with i64 try_fold/checked_add — the same idiom validate_stateless already uses — returning InputSumOverflow/OutputSumOverflow. This also drops the spurious lower-bound check the BoxValue::new construction applied to the aggregate (the reference bounds boxes, not the sum). Closes #881. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion The JVM reads the Option DATA tag through getOption (sigma Extensions.getOption): `if (tag != 0) Some(getValue) else None`. Our sigma-ser get_option mapped only tag 1 to Some and silently returned None on any other nonzero tag WITHOUT consuming the value bytes, desyncing the stream — a v3 tree carrying an SOption constant with DATA tag 0x02 failed to parse where the JVM evaluates it to Some(5). Fix the shared reader helper to mirror the convention; put_option still writes 0/1, so the write side is unchanged. The only other caller (ergo-p2p peer-spec parsing) aligns with the same scorex reader convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ContextExtension keys are a signed Byte JVM-side. ErgoLikeContext .toSigmaContext builds the contextVars as new Array(maxKey+1) indexed by the signed key (ErgoLikeContext.scala:140-146), so a self-box extension carrying a key >= 0x80 (signed-negative) crashes context construction (NegativeArraySizeException / ArrayIndexOutOfBoundsException) BEFORE any bytecode runs — the spend never validates. The crash is on the key's PRESENCE, independent of whether the script reads it. sigma-rust stores extension keys as unsigned u8 and resolves them lazily at GetVar, so it would otherwise ACCEPT a >= 0x80 key where the JVM rejects — a consensus fork on the attacker-supplied spending extension (the node feeds the spending input's extension into ctx.extension before reduce_to_crypto). Reject any self-extension key outside 0..=127 at the reduction boundary, mirroring the construction-time crash as a clean rejection. This is the top-level self extension only; the per-input getVarFromInput path masks the id with & 0xff by design and is untouched, and a GetVar of a >= 0x80 id with the key absent still resolves to None. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… gate
The JVM `HeaderWithoutPow` reads `version` as a signed `Byte` and gates the
`unparsedBytes` region on `version > 1`. sigma-rust compared `version` as
`u8`, so a version byte 0x80 (-128 signed) was seen as 128 > 1: the parser
consumed `unparsedBytes`, shifting the AutolykosSolution parse and yielding a
different `minerPk` than the JVM (which skips the region) -- a deserialization
fork vs sigma-state 6.0.3.
Compare the signed interpretation `(version as i8) > 1` at the parse and
serialize gates. Versions 1/2/3 are positive, so signed == unsigned; only
version >= 0x80 changes, matching the JVM. The two `version > 1` sites in the
arbitrary proptest generator are left unchanged: it emits only versions {1,2},
and one is a v2-field nulling gate (`!= 1`), not an unparsedBytes gate. Adds a
regression test on the 0x80/0x7f witness headers (minerPk infinity vs real).
fix: Option DATA parser treats any nonzero tag as Some, per JVM getOption
fix: read Header version as a signed Byte at the unparsedBytes gate (JVM parity)
Reject self ContextExtension keys >= 0x80 (JVM context-construction parity)
fix: reject non-pair tuples at eval to match sigma-state consensus
fix(ergotree-ir): Global.powHit returns UnsignedBigInt, not Boolean
…checkType) The JVM checkTypes each Tuple item (values.scala:801/804) via SType.isValueOfType, which rejects a tuple type of arity != 2 (and a function type of arity != 1) with "Unsupported tuple type" (SType.scala:200-205). sigma-rust models flat N-ary tuples (TupleItems 2..=255), so an arity-3 tuple carried as a constant (the item of a valid pair) evaluated where the JVM rejects — a consensus accept/reject divergence. Add check_value_type mirroring isValueOfType's reachable rejections and call it at the Tuple-eval items. Placeholder constants are substituted into the body before eval on this branch, so the segregated case reduces to the same inline Tuple-item case; the eni cherry-pick additionally guards ConstantPlaceholder eval (where eni resolves placeholders lazily). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the JVM order: checkType before evaluating each item (values.scala:801/804). Aborts earlier if the type is unsupported rather than doing the eval work first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: reject unsupported (arity != 2) tuple-typed values at eval (JVM checkType)
fix: checked ERG summation in stateful transaction validation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.