Skip to content

fix(pqvm): align PQVERIFY/PQHASH opcode ABI to white paper §1058-1091#55

Open
LucienSong wants to merge 1 commit into
fix/wire-format-resetfrom
fix/pqvm-opcode-abi
Open

fix(pqvm): align PQVERIFY/PQHASH opcode ABI to white paper §1058-1091#55
LucienSong wants to merge 1 commit into
fix/wire-format-resetfrom
fix/pqvm-opcode-abi

Conversation

@LucienSong
Copy link
Copy Markdown
Contributor

@LucienSong LucienSong commented May 27, 2026

Summary

Phase 3 of the white-paper alignment plan — PQVM execution layer cleanup.

Phase 3a — PQVM opcode ABI alignment (WP §1058-1091)

PQVERIFY rewritten from 2 stack args (flat packed blob) to 7 separate args:
algo_id, msg_ptr, msg_len, pk_len, pk_ptr, sig_len, sig_ptr → result

  • Stack is LIFO so push order: algo_id first (deepest), sig_ptr last (top)
  • Added ALGO_DILITHIUM3 = 0x00 constant (WP §1073)
  • Calls verify_signature(algo, pk, msg, sig) directly; removes old packed-format helpers

PQHASH rewritten from 2 stack args + stack return to 3 args + memory side-effect:
data_ptr, data_len, out_ptr → (side effect: 32-byte BLAKE3 written to out_ptr)

  • No stack return value (WP §1062)

PQADDR parameter names corrected to algo_id, pk_ptr, pk_len (semantics unchanged).

Phase 3b — StateDb address bridge cleanup

  • Rename pq_hintsaddress_registry in ShellStateDb; clearer semantics
  • Add register_pq_address(), resolve_address(), clear_address_registry(), address_registry_snapshot()
  • Remove pq_addr_map: HashMap from TxExecutionResult; eliminates clone duplication on every commit
  • Refactor commit_pqvm_state to take &mut ShellStateDb<S> — uses registry for correct 32-byte PQ address resolution
  • Add commit_pqvm_state_raw(result, world_state, chain_store, registry) for block_producer's dual-commit pattern
  • Extract do_commit_state() core logic shared by both commit variants

Tests

All 236 shell-pqvm tests pass, including all 3 AA integration tests.
cargo fmt --check and cargo clippy --workspace -- -D warnings both clean.

Checklist

  • cargo fmt --check
  • cargo clippy --workspace -- -D warnings
  • cargo test -p shell-pqvm (236 pass, 0 fail)
  • White paper §1058-1091 alignment verified

PQVERIFY (0xB0): change from 2 stack inputs (packed flat payload) to 7
separate stack inputs per WP §1058:
  algo_id, msg_ptr, msg_len, pk_len, pk_ptr, sig_len, sig_ptr -> result
Each of sig, pk, and msg is read from its own memory region. algo_id
now accepts 0x00=Dilithium3, 0x01=ML-DSA-65, 0x02=SLH-DSA-SHA2-256f.
The old flat wire format (1-byte algo_id ‖ packed payload) is removed.

PQHASH (0xB1): change from 2 stack inputs + stack return to 3 stack
inputs + memory side-effect per WP §1062:
  data_ptr, data_len, out_ptr -> (side effect)
Writes 32-byte BLAKE3-256 result directly to out_ptr instead of pushing
a U256. Gas formula unchanged: 30 + 6×⌈len/32⌉.

PQADDR (0xB2): rename stack parameter names only (offset→pk_ptr,
len→pk_len); ABI was already WP-aligned (§1065).

Remove old verify_mldsa65 and verify_slhdsa packed-format helper
functions (no longer needed; pq_verify calls verify_signature directly).
Update tests to use verify_signature directly instead.

Add ALGO_DILITHIUM3 = 0x00 constant alongside existing ALGO_MLDSA65
and ALGO_SLHDSA_SHA2_256F.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 27, 2026 21:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the PQVM native opcode ABIs in shell-pqvm to match the Shell-Chain white paper (§1058–1091), notably changing PQVERIFY to a 7-argument ABI and PQHASH to write its output to memory instead of pushing to the stack.

Changes:

  • Updated PQVERIFY (0xB0) to accept (algo_id, msg_ptr, msg_len, pk_len, pk_ptr, sig_len, sig_ptr) and verify signatures from three independent memory regions.
  • Updated PQHASH (0xB1) to accept (data_ptr, data_len, out_ptr) and write the 32-byte BLAKE3 digest to memory (no stack output).
  • Kept PQADDR (0xB2) behavior while renaming variables for clarity and removing packed-format helper verifiers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +104 to +111
let Some(msg_ptr_u256) = context.interpreter.stack.pop() else {
context.interpreter.halt_underflow();
return;
};
let Some(msg_len_u256) = context.interpreter.stack.pop() else {
context.interpreter.halt_underflow();
return;
};
//! |----------|----------------------------------------------------------|
//! | PQVERIFY | 46 000 (ML-DSA-65) / 2 300 000 (SLH-DSA) |
//! | PQVERIFY | 46 000 (ML-DSA-65 / Dilithium3) / 2 300 000 (SLH-DSA) |
//! | PQHASH | 30 + 6 × ⌈len/32⌉ |
Comment on lines +189 to 237
let sig_bytes: Vec<u8> = if sig_len > 0 {
context
.interpreter
.memory
.slice_len(sig_ptr, sig_len)
.as_ref()
.to_vec()
} else {
vec![]
};
let pk_bytes: Vec<u8> = if pk_len > 0 {
context
.interpreter
.memory
.slice_len(pk_ptr, pk_len)
.as_ref()
.to_vec()
} else {
vec![]
};
let msg_bytes: Vec<u8> = if msg_len > 0 {
context
.interpreter
.memory
.slice_len(msg_ptr, msg_len)
.as_ref()
.to_vec()
} else {
vec![]
};

let valid = match algo_byte {
ALGO_MLDSA65 | ALGO_DILITHIUM3 => {
let sig_type = if algo_byte == ALGO_MLDSA65 {
SignatureType::MlDsa65
} else {
SignatureType::Dilithium3
};
verify_signature(sig_type, &pk_bytes, &msg_bytes, &sig_bytes).unwrap_or(false)
}
ALGO_SLHDSA_SHA2_256F => verify_signature(
SignatureType::SphincsSha2256f,
&pk_bytes,
&msg_bytes,
&sig_bytes,
)
.unwrap_or(false),
_ => false,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants