Skip to content

security: PQVM-01 CREATE nonce, C-2 batch gas pre-check, PQVM-02/03 intrinsic gas#2

Merged
LucienSong merged 1 commit into
mainfrom
security/pqvm-01-04
May 25, 2026
Merged

security: PQVM-01 CREATE nonce, C-2 batch gas pre-check, PQVM-02/03 intrinsic gas#2
LucienSong merged 1 commit into
mainfrom
security/pqvm-01-04

Conversation

@LucienSong
Copy link
Copy Markdown
Contributor

Summary

  • Fix PQVM-01 in crates/pqvm-interpreter/src/lib.rs:969-1019 by rejecting CREATE collisions when the derived target already has code or storage, and by incrementing the creator nonce only after successful deployment state writes.
  • Fix C-2 in crates/pqvm-precompiles/src/lib.rs:16,101-115,206-250 by adding MAX_BATCH_SIGNATURES, parsing the batch count up front, charging ML_DSA_65_BATCH_VERIFY gas before verification work, and rejecting oversized batches.
  • Fix PQVM-02/03 in crates/pqvm/src/lib.rs:134-315 by computing intrinsic gas (including calldata cost), escrowing gas_limit * max_fee before execution, refunding unused gas, and limiting signature verification to the post-intrinsic gas budget instead of u64::MAX.
  • Add regression coverage in crates/pqvm-interpreter/src/lib.rs:1776-1874, crates/pqvm-precompiles/src/lib.rs:314-372, and crates/pqvm/src/lib.rs:532-596.

Validation

  • cargo fmt --all -- --check
  • cargo clippy --workspace -- -D warnings
  • cargo test --workspace

Impact-Deferred

  • shell-chain: Not run in this PQVM security-fix session; downstream cargo test --workspace / make e2e remain to be executed against the updated executor.
  • shell-sdk: Not run in this PQVM security-fix session; downstream SDK/API validation remains pending.
  • shell-dex: Not run in this PQVM security-fix session; downstream frontend smoke/build checks remain pending.
  • shell-explorer: Not run in this PQVM security-fix session; downstream decode/render smoke checks remain pending.
  • shella-chrome-wallet: Not run in this PQVM security-fix session; downstream sign/send and keystore checks remain pending.
  • shell-site: Not run in this PQVM security-fix session; downstream docs/build/link checks remain pending.
  • shell-chain-white-paper: Not updated here; if these gas-accounting semantics are intended as spec changes, the illustrative text should be reviewed separately.

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

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 implements several security hardenings across PQVM transaction execution, precompiles, and the interpreter, with accompanying regression tests. It primarily tightens CREATE collision rules, adds batch signature size/gas pre-checks, and enforces intrinsic gas + bounded signature verification within the transaction gas budget.

Changes:

  • Add intrinsic gas calculation (incl. calldata costs), escrow gas_limit * max_fee, refund unused gas fees, and cap signature verification gas to the post-intrinsic budget.
  • Add MAX_BATCH_SIGNATURES and upfront batch-count parsing + gas charging for ML_DSA_65_BATCH_VERIFY, rejecting oversized batches.
  • Reject CREATE collisions when the derived target has existing code or storage, and adjust nonce increment timing; add has_storage to the DB trait/state plus regression coverage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/pqvm/tests/conformance_vectors.rs Updates conformance checks for new gas constants and DB trait (has_storage) in test scaffolding.
crates/pqvm/src/lib.rs Implements intrinsic gas + fee escrow/refund and limits signature verification gas; adds related tests.
crates/pqvm-state/src/lib.rs Extends PqvmDatabase with has_storage and adds a PqvmState implementation.
crates/pqvm-precompiles/src/lib.rs Adds batch-size limit + pre-charged gas behavior for batch signature verification; adds tests.
crates/pqvm-interpreter/src/lib.rs Adds CREATE collision detection (code/storage) and defers creator nonce increment until successful deployment; adds tests.
crates/pqvm-gas/src/lib.rs Introduces calldata byte gas constants used by intrinsic gas calculation.

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

Comment thread crates/pqvm/src/lib.rs
Comment on lines +171 to +176
let max_fee_charge = fee_for_gas(tx.max_fee, tx.gas_limit)?;
deduct_balance(state, sender, max_fee_charge)?;
state.increment_nonce(sender)?;

fn verify_transaction_signature(tx: &PQTx) -> Result<(), TxExecutionError> {
let public_key = tx
.public_key
.as_ref()
.ok_or(TxExecutionError::MissingPublicKey)?;
let precompile = match AlgoId::try_from(tx.sig_type)
.map_err(|_| TxExecutionError::UnknownSignatureAlgorithm(tx.sig_type))?
{
AlgoId::MlDsa65 => ML_DSA_65_VERIFY,
AlgoId::SlhDsaSha2256f => SLH_DSA_SHA2_256F_VERIFY,
};
let mut input = Vec::with_capacity(public_key.len() + tx.signature.len() + 32);
input.extend_from_slice(public_key);
input.extend_from_slice(&tx.signature);
input.extend_from_slice(tx.signing_payload().as_slice());
let output = precompiles::BasicPqPrecompiles
.execute(precompile, &input, u64::MAX)
.map_err(|err| TxExecutionError::SignaturePrecompile(err.to_string()))?
.ok_or_else(|| TxExecutionError::SignaturePrecompile("missing verifier".into()))?;
if output.output.first().copied() != Some(1) {
return Err(TxExecutionError::InvalidSignature);
if let Some(to) = tx.to {
state.transfer(sender, to, tx.value)?;
Comment thread crates/pqvm/src/lib.rs
let account = state
.account_mut(address)
.ok_or(StateError::MissingAccount(address))?;
account.balance += amount;
Comment on lines +178 to +179
.keys()
.any(|(storage_address, _)| *storage_address == address)
@LucienSong LucienSong merged commit 48fbac5 into main May 25, 2026
1 check passed
@LucienSong LucienSong deleted the security/pqvm-01-04 branch May 25, 2026 17:33
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