Skip to content

test(vm): frozen bytecode corpus for backward compatibility - #218

Open
ascandone wants to merge 1 commit into
mainfrom
test/frozen-bytecode-corpus
Open

ascandone wants to merge 1 commit into
mainfrom
test/frozen-bytecode-corpus

Conversation

@ascandone

Copy link
Copy Markdown
Contributor

Why

The ledger stores the compiled bytecode (program and vars blobs) in its Raft log and re-runs it when a node restarts. So a build that can read a blob must run it to the same result as the build that wrote it. Nothing checked that: every VM test recompiles with the current compiler, so a change to the decoder, the verifier or Exec that alters what existing 0.1 bytecode does, without a version bump, went unnoticed.

What

  • testdata/bytecode/0.1/: the bytecode compiled from every script fixture (225 programs, 237 cases, 136 KB), with each case's vars blob, inputs and the output it produced. Layout:
    testdata/bytecode/0.1/
      kept-inorder.numb                 # CompiledProgram.Encode()
      kept-inorder/<case>/vars.nvar     # Vars.Encode()
      kept-inorder/<case>/balances.json
      kept-inorder/<case>/metadata.json # only when the case has any
      kept-inorder/<case>/postings.json # or error.json
    
    A freeze directory is named after the bytecode version (0.1, or 0.1-<suffix> for a later freeze at the same version) and is never edited once committed. The asset-scaling fixtures are skipped: the compiler has no lowering for them yet.
  • bytecode_corpus_test.go (module root, public API only, the same calls the ledger makes):
    • TestBytecodeCorpus: for every frozen case, decode, verify and run the blobs and compare with the frozen output. A blob this build cannot read must be rejected with UnsupportedBytecodeVersionError, which is what makes the ledger recompile from source. For the current version, re-encoding the decoded blobs must give back the same bytes.
    • TestBytecodeCorpusHasCurrentVersion: fails when CurrentBytecodeVersion has no freeze.
    • TestFreezeBytecodeCorpus: the writer, skipped unless NUMSCRIPT_FREEZE_BYTECODE=1.
  • just freeze-bytecode [suffix]: runs the compiler suite first, then writes a new directory. It refuses to overwrite an existing one.
  • compiler-architecture.md: a "Frozen bytecode" section with the rules below.

Rules

  • Freeze after every change of CurrentBytecodeVersion (enforced by the guard test).
  • Optionally freeze again, under a suffix, before a release whose compiler emits different bytes for the fixtures. That keeps every kind of bytecode the ledger may have stored under test.
  • Compiler changes never invalidate a freeze: the frozen files test the decoder, the verifier and the VM, not the compiler.
  • A red TestBytecodeCorpus on a readable directory means this build runs existing bytecode differently. Either the change is a bug, or the bytecode version has to be bumped so that older blobs are rejected.
  • Under 0.x every bump rejects all older bytecode, so until 1.0 this only catches a forgotten bump. From 1.0 a 1.1 VM must run frozen 1.0 bytecode identically, and a VM fix that changes a frozen output is a major bump.

Checked

  • A second freeze is byte-identical to the first.
  • Making AddInt subtract instead: 84 frozen cases fail. Bumping the version to 0.2 without freezing: the guard fails and every 0.1 entry passes as rejected. Both reverted.
  • go test ./... green; the root package is green under -race.

Freeze the bytecode compiled from every script fixture, with each case's vars blob, inputs and output, and check that this build runs it to the same result or rejects it with UnsupportedBytecodeVersionError. A guard fails when CurrentBytecodeVersion has no freeze.
@NumaryBot NumaryBot added risk: high NumaryBot classified this pull request as high risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-inconclusive labels Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-reviewed NumaryBot completed its review workflow for the current head. review-inconclusive risk: high NumaryBot classified this pull request as high risk.

Development

Successfully merging this pull request may close these issues.

2 participants