Skip to content

feat(vm-runner): add p256_verify host function (NEP-635)#15588

Merged
r-near merged 18 commits intomasterfrom
r-near/vm-runner/add-p256-verify
Apr 22, 2026
Merged

feat(vm-runner): add p256_verify host function (NEP-635)#15588
r-near merged 18 commits intomasterfrom
r-near/vm-runner/add-p256-verify

Conversation

@r-near
Copy link
Copy Markdown
Contributor

@r-near r-near commented Apr 16, 2026

Summary

Implements the NEP-635 p256_verify host function — ECDSA signature verification on the NIST P-256 curve with SHA-256 message hashing. Gated behind protocol version 85.

Signature: p256_verify(sig, msg, pubkey) -> u64 returning 1 on success, 0 on verification failure. Takes a raw 64-byte r||s signature, arbitrary-length message, and 33-byte SEC1 compressed public key. Aborts with HostError::P256VerifyInvalidInput on malformed input lengths.

Motivated by TEE attestation (DCAP quote verification) and WebAuthn use cases where P-256 is the required curve and today's contracts have to bundle ~120 KB of verification code.

Gas calibration

  • wasm_p256_verify_base: 1_300_000_000_000 (1,300 Bgas)
  • wasm_p256_verify_byte: 13_000_000 (13 Mgas)

Derived from --metric icount (hw-agnostic, via Docker+QEMU instrumentation), anchored against ed25519_verify's already-calibrated 210 Bgas / 9 Mgas to get a combined hw-correction-plus-safety factor. Cross-checked with --metric time on a Threadripper 3970X. The icount-derived P-256/Ed25519 ratio of 6.7× matches the time-derived ratio of 6.2×, which matches library-level benchmarks.

Design notes

  • High-s signatures are accepted (FIPS 186-5 compliant, non-BIP-62). The p256 crate does not enforce low-s normalization and we pass that through. Changing this later would be protocol-breaking. Documented in tests.
  • No byte-level streaming; the full message is materialized in the host before hashing, consistent with ed25519_verify.

Testing

  • 23 unit tests in logic/tests/p256_verify.rs, including 8 Wycheproof adversarial vectors (r=0, s=0, r=n, modified r, invalid SEC1 tag, high-s, malleability edge cases)
  • 7 integration tests in tests/p256_verify_integration.rs including a feature-gate test that confirms contracts importing env.p256_verify fail to link when Config::p256_verify = false
  • 1 e2e test in test-loop-tests/src/tests/p256_verify.rs covering the full transaction → receipt → outcome pipeline at protocol version 85

Incidental changes

Adding p256 transitively bumped generic-array 0.14.5 → 0.14.9, which emits new deprecation warnings. Suppressed the few spots this affects in core/crypto/src/hash.rs and replaced value_hash.as_slice() calls with &value_hash[..] in near-vm-runner.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.32%. Comparing base (9cf5dbf) to head (4f86065).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
runtime/runtime-params-estimator/src/lib.rs 0.00% 18 Missing ⚠️
runtime/near-vm-runner/src/logic/logic.rs 85.00% 1 Missing and 5 partials ⚠️
...untime/near-vm-runner/src/wasmtime_runner/logic.rs 91.52% 0 Missing and 5 partials ⚠️
core/parameters/src/view.rs 50.00% 3 Missing ⚠️
runtime/near-vm-runner/src/logic/errors.rs 0.00% 2 Missing ⚠️
...me-params-estimator/src/costs_to_runtime_config.rs 0.00% 2 Missing ⚠️
core/parameters/src/parameter_table.rs 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #15588      +/-   ##
==========================================
- Coverage   69.38%   69.32%   -0.06%     
==========================================
  Files         942      942              
  Lines      212994   213123     +129     
  Branches   212994   213123     +129     
==========================================
- Hits       147781   147749      -32     
- Misses      59357    59501     +144     
- Partials     5856     5873      +17     
Flag Coverage Δ
pytests-nightly 1.14% <0.00%> (-0.01%) ⬇️
unittests 68.73% <75.00%> (-0.04%) ⬇️
unittests-nightly 68.90% <71.64%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

r-near added 2 commits April 16, 2026 00:31
Gas calibration lives in the params estimator (icount + estimator-contract),
not in cargo bench — no other host function in this crate has a criterion
microbench. The bench measured native P-256 rather than the host function, so
it didn't feed the calibration flow that produced the yaml numbers.
@r-near r-near marked this pull request as ready for review April 16, 2026 23:50
@r-near r-near requested review from a team and frol as code owners April 16, 2026 23:50
@r-near r-near requested review from Copilot and ssavenko-near April 16, 2026 23:50
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

Adds NEP-635 p256_verify as a protocol-gated VM host function (protocol v85) with calibrated gas costs, wiring it through runtime configs / schemas, and validating it via unit, integration, and test-loop e2e coverage.

Changes:

  • Implement env.p256_verify(sig, msg, pubkey) -> u64 in near-vm-runner (both NearVM/Wasmtime paths) with new HostError::P256VerifyInvalidInput for malformed input lengths.
  • Introduce new ext costs (p256_verify_base, p256_verify_byte) and integrate them into runtime configs, estimator tooling, profiling, and JSON/OpenAPI views.
  • Add comprehensive tests (logic unit tests, near-vm-runner integration tests, test-loop e2e test) and update protocol/schema snapshots accordingly.

Reviewed changes

Copilot reviewed 104 out of 105 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tools/protocol-schema-check/res/protocol_schema.toml Updates protocol schema hashes to reflect new/changed types due to added costs/config/errors.
test-loop-tests/src/tests/p256_verify.rs New test-loop e2e coverage for deploy/call behavior and error propagation.
test-loop-tests/src/tests/mod.rs Registers the new p256_verify test module.
test-loop-tests/Cargo.toml Adds p256 and wat dependencies for test-loop contract generation and signing.
runtime/runtime-params-estimator/src/lib.rs Adds p256 cost measurement functions to the estimator cost table.
runtime/runtime-params-estimator/src/estimator_context.rs Caches p256_verify_base measurement similarly to ed25519.
runtime/runtime-params-estimator/src/costs_to_runtime_config.rs Maps new ExtCosts::{p256_verify_base,p256_verify_byte} to estimator Cost entries.
runtime/runtime-params-estimator/src/cost.rs Adds Cost::{P256VerifyBase,P256VerifyByte} variants and documentation.
runtime/near-vm-runner/src/wasmtime_runner/logic.rs Implements p256_verify host function in Wasmtime runner path.
runtime/near-vm-runner/src/logic/logic.rs Implements p256_verify host function in VMLogic path.
runtime/near-vm-runner/src/logic/errors.rs Adds HostError::P256VerifyInvalidInput and Display formatting.
runtime/near-vm-runner/src/imports.rs Adds p256_verify import gated behind Config::p256_verify.
runtime/near-vm-runner/src/tests/p256_verify_integration.rs New near-vm-runner integration tests for correctness and gating/link behavior.
runtime/near-vm-runner/src/tests.rs Wires the new integration test module into the test suite.
runtime/near-vm-runner/src/logic/tests/p256_verify.rs New logic-level unit tests, including Wycheproof vectors and gas accounting assertions.
runtime/near-vm-runner/src/logic/tests/mod.rs Registers the new logic test module.
runtime/near-vm-runner/src/profile.rs Updates profiling cost index mapping to include the new ExtCosts entries.
runtime/near-vm-runner/Cargo.toml Adds the p256 dependency to the runner crate.
runtime/near-test-contracts/estimator-contract/src/lib.rs Adds estimator-contract entrypoints for measuring p256_verify_base and p256_verify_byte.
cspell.json Adds cryptography/test terminology words used by the new tests/docs.
core/primitives/src/errors.rs Adds HostError::P256VerifyInvalidInput to stable primitives error enum.
core/primitives-core/src/version.rs Adds ProtocolFeature::P256Verify and sets activation at protocol version 85.
core/parameters/src/vm.rs Adds Config::p256_verify flag and enables it in enable_all_features().
core/parameters/src/view.rs Exposes p256_verify and p256 cost fields in config views and conversions.
core/parameters/src/parameter.rs Adds parameters for p256 costs and the P256Verify feature flag parameter.
core/parameters/src/parameter_table.rs Plumbs Parameter::P256Verify into RuntimeConfig construction.
core/parameters/src/cost.rs Adds new ExtCosts discriminants and default test cost values for p256.
core/parameters/res/runtime_configs/parameters.yaml Adds default p256 cost params and the p256_verify flag to runtime config template.
core/parameters/res/runtime_configs/parameters_testnet.yaml Adds default p256 cost params and the p256_verify flag to testnet template.
core/parameters/res/runtime_configs/parameters.snap Updates autogenerated runtime config snapshot for new costs/flag.
core/parameters/res/runtime_configs/85.yaml Marks p256_verify toggling on at protocol version 85.
core/primitives/src/profile_data_v3.rs Updates profile cost index mapping to include p256 verify costs.
core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap Updates runtime config view snapshot with p256 costs/flag.
core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v2_view.snap Updates exec-metadata v2 view snapshot with new cost entries.
core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v3_view.snap Updates exec-metadata v3 view snapshot with new cost entries.
core/parameters/src/snapshots/near_parameters__view__tests__runtime_config_view.snap Updates parameters view runtime config snapshot with p256 costs/flag.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_0.json.snap Updates stored config snapshot to include p256 costs/flag defaults for v0.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_42.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_46.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_48.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_49.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_50.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_52.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_53.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_55.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_57.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_59.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_61.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_62.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_63.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_64.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_66.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_67.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_68.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_69.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_70.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_72.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_73.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_74.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_77.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_78.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_79.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_82.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_83.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_84.json.snap Snapshot update for p256 costs/flag inclusion and gate off at v84.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_85.json.snap Snapshot update for p256 costs/flag inclusion and gate on at v85.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_129.json.snap Snapshot update for p256 costs/flag inclusion for higher protocol versions.
core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_149.json.snap Snapshot update for p256 costs/flag inclusion for higher protocol versions.
core/parameters/src/snapshots/near_parameters__config_store__tests__0.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__42.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__46.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__48.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__49.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__50.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__52.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__53.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__55.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__57.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__59.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__61.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__62.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__63.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__64.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__66.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__67.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__68.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__69.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__70.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__72.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__73.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__74.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__77.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__78.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__79.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__82.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__83.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__84.json.snap Snapshot update for p256 costs/flag inclusion and gate off at v84.
core/parameters/src/snapshots/near_parameters__config_store__tests__85.json.snap Snapshot update for p256 costs/flag inclusion and gate on at v85.
core/parameters/src/snapshots/near_parameters__config_store__tests__129.json.snap Snapshot update for p256 costs/flag inclusion.
core/parameters/src/snapshots/near_parameters__config_store__tests__149.json.snap Snapshot update for p256 costs/flag inclusion.
core/crypto/src/hash.rs Suppresses generic-array deprecation warnings in blake2 integration until upstream bumps.
runtime/near-vm-runner/src/wasmtime_runner/logic.rs Replaces as_slice() with &[..] per generic-array deprecation behavior.
runtime/near-vm-runner/src/logic/logic.rs Replaces as_slice() with &[..] per generic-array deprecation behavior.
chain/jsonrpc/openapi/openrpc.json Extends OpenRPC schema to expose p256 costs/flag and new HostError variant.
chain/jsonrpc/openapi/openapi.json Extends OpenAPI schema to expose p256 costs/flag and new HostError variant.
Cargo.toml Adds workspace dependency on p256 (RustCrypto) with ecdsa support.
Cargo.lock Locks new transitive dependencies introduced by p256 and bumps generic-array.

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

Comment thread runtime/near-vm-runner/src/logic/tests/p256_verify.rs Outdated
Comment thread test-loop-tests/src/tests/p256_verify.rs Outdated
Comment thread runtime/near-test-contracts/estimator-contract/src/lib.rs Outdated
Comment thread runtime/near-vm-runner/src/logic/errors.rs Outdated
Comment thread core/primitives/src/errors.rs Outdated
Comment thread core/crypto/src/hash.rs Outdated
Comment thread runtime/runtime-params-estimator/src/costs_to_runtime_config.rs
/// Then call with a tampered message and assert the return value is 0.
/// Finally, call with an invalid signature length and assert the host error.
#[test]
fn test_p256_verify_e2e() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For testing the host functions in test-loop there is a pre-existing pattern, please see if you could adapt it here. It would be part of near-test-contracts::rs_contract, and calling "call_promise"

fn test_gas_key_transfer_host_function() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok I tried the rs_contract route (d51f259) and CI caught something I hadn't realized: adding a latest_protocol-gated import to rs_contract breaks any test that deploys it at a pre-85 protocol version...

Reverted in 1efd4c1 and kept the WAT helper scoped to the test file, (but with Copilot's offset concern actually fixed this time).

Happy to revisit if there's a cleaner home for the contract... maybe a dedicated mini-contract in near-test-contracts alongside estimator-contract? Wanted to avoid that churn in this PR.

Comment thread runtime/near-vm-runner/src/tests/p256_verify_integration.rs Outdated
Comment thread runtime/near-vm-runner/src/logic/tests/p256_verify.rs Outdated
}

#[test]
fn test_p256_verify_integration_empty_signature_length_aborts() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if we can just have unit tests and test-loop tests. The unit tests are thorough, and we can keep test-loop for:

  • Signature valid,
  • Signature invalid,
  • PV disabled.

I'm not against to keep the other cases as test loop either.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Test-loop is now exactly those three (1efd4c1)

I kept the six with_vm_variants integration tests in near-vm-runner since they're the only thing exercising both NearVm and Wasmtime for this host function, but we could even get rid of that too 🤷

@wacban
Copy link
Copy Markdown
Contributor

wacban commented Apr 17, 2026

@jakmeier Can you have a quick look at it too please?

Copy link
Copy Markdown
Contributor

@jakmeier jakmeier left a comment

Choose a reason for hiding this comment

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

@jakmeier Can you have a quick look at it too please?

@darioush already raised some good points in his comments. Otherwise, this looks good to me overall. Happy to see this included!

Comment thread core/parameters/res/runtime_configs/85.yaml Outdated
Comment thread runtime/runtime-params-estimator/src/costs_to_runtime_config.rs
r-near added 9 commits April 21, 2026 15:13
The variant docs claimed this abort covers generic "signature cannot be
derived from bytes" cases, but parse failures of well-sized inputs
return 0 from the host function — this variant only fires on length
mismatch (signature != 64 bytes or public key != 33 bytes). Align the
docstring with actual behavior in both logic/errors.rs and the stable
primitives enum, and regenerate the corresponding OpenAPI/OpenRPC
descriptions so downstream clients see the same wording.
The comment claimed ECDSA signing uses a random nonce, so signatures
are nondeterministic. Not true for RustCrypto's p256: SigningKey via
signature::Signer uses deterministic RFC6979 by default, so a given
key+message always produces the same signature. Update the comment so
a future reader isn't misled into switching to randomized signing
unnecessarily.
The comment on p256_verify_16kib_64 said the per-byte fee is charged
regardless of how many bytes participate in verification — misleading,
since SHA-256 hashes every byte of the message. Rewrite it to say what
the per-byte charge actually covers: the linear, message-dependent
cost of marshalling plus hashing.
The generic-array 0.14.9 bump from pulling in `p256` deprecated the
`blake2::digest::generic_array` re-export. Instead of carrying two
`#[allow(deprecated)]` annotations, write the impl against the
`Output<Self>` type alias that `digest` exposes for exactly this
purpose — it resolves to the same underlying type but stays on the
non-deprecated API surface.

Same approach as #14927.
The wycheproof block tests the RustCrypto p256 crate's handling of
adversarial signatures, not our own wrapper, so it reads better as a
dedicated sibling module than appended to the main test file. Move
the eight tests and their `run_wycheproof` helper into
`p256_verify_wycheproof.rs` and scope the cryptography-specific
vocabulary (`Wycheproof`, `Shamir`, `FIPS`, `Pubkeys`) to that file
via a `// cspell:words` directive, removing them from the global
cspell.json. Also drop `Microbenchmark`, which was only referenced by
the criterion bench removed earlier in this PR.
The test-loop coverage used to build a bespoke WAT module inline,
which laid inputs out at fixed offsets (0, 128, 192) — fine for a
32-byte message, but it'd silently overlap the public key for any
longer one. Swap it for the pattern reviewers asked for: expose a
small `ext_p256_verify` entry point from `rs_contract`, have the test
deploy the standard contract and invoke it with a JSON payload of
base64-encoded bytes, and forward the host function's `u64` result
back out through `value_return`. No more WAT generation in the test
harness, and message length stops being a footgun.

With the contract in place, collapse the test-loop side to the three
scenarios that matter e2e: valid signature returns 1, invalid returns
0, and a contract that imports `env.p256_verify` can't link against a
runtime at PV < 85. The PV-disabled case was previously a vm-runner
integration test that toggled `Config::p256_verify = false`; the
test-loop version exercises the real protocol gate end-to-end, so
drop the vm-runner test. Remove `wat` from test-loop-tests and add
`base64` for the JSON payload encoding.

Addresses the test-organization comments from darioush (#15588) and
the Copilot note about fixed-offset WAT layouts.
Follow-up to b2272dc. The wycheproof test file has its own
`cspell:words` directive, but the `mod p256_verify_wycheproof;`
declaration in the parent mod.rs wasn't covered — and since the
global `cspell.json` no longer carries "Wycheproof", CI spellcheck
tripped on the lowercase form there. Add a file-level directive.
Adding `ext_p256_verify` to rs_contract put a new `latest_protocol`-
gated import into the shared stable test contract. That was fine for
every earlier `latest_protocol` host function because each was already
available at the oldest supported PV — but `p256_verify` activates at
PV 85 (via a runtime Config flag). As soon as rs_contract imports it,
any test that deploys rs_contract at a pre-85 protocol version fails
to link; CI caught this via `test_yield_resume_across_protocol_upgrade`
(runs at PV 82 through 83).

Rather than pollute the cross-team contract, revert that pair of
additions and build the p256_verify contract inline in the test-loop
test file. Reorder the data layout so Copilot's original concern
doesn't return: `signature (0..64) | public_key (64..97) | message
(97..)`, with fixed-length inputs first. Result is written to the
last 8 bytes of the memory page so it can never collide with the
message, regardless of length.

Also mark the pre-activation test `#[cfg_attr(feature =
"protocol_feature_spice", ignore)]` since Spice runs at PROTOCOL_VERSION
200 and rejects test-loop genesis at arbitrary older PVs (spice_core
requires non-genesis blocks to carry uncertified chunks).
Copy link
Copy Markdown
Contributor

@darioush darioush left a comment

Choose a reason for hiding this comment

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

LGTM, but would like @pugachAG to have a look as well

)
)"#,
);
wat::parse_str(&wat).expect("failed to parse wat")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: can we use near_test_contracts::wat_contract here and revert the Cargo.toml change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 4ff42c9

@darioush darioush requested a review from pugachAG April 22, 2026 01:14
Copy link
Copy Markdown
Contributor

@pugachAG pugachAG left a comment

Choose a reason for hiding this comment

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

please remove unused protocol feature, otherwise looks good

Comment thread core/primitives-core/src/version.rs Outdated
FixDelegateActionDepositWithFunctionCallError,
/// Enable P-256 ECDSA signature verification host function.
/// NEP-635: <https://github.com/near/NEPs/pull/635>
P256Verify,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You don't need protocol feature, p256_verify runtime parameter is sufficient

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, removed in e9df2be

Comment thread core/parameters/src/vm.rs Outdated

/// Whether to enable the P-256 ECDSA signature verification host function.
/// NEP-635: <https://github.com/near/NEPs/pull/635>
pub p256_verify: bool,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: p256_verify -> p256_verify_host_fn for consistency

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done here: bc515cf

r-near added 3 commits April 22, 2026 09:49
Replaces the inline `wat::parse_str(...).expect(...)` call in the
p256_verify test with `near_test_contracts::wat_contract`, which is the
existing helper for this pattern. Drops `wat` from test-loop-tests
dev-dependencies since it's no longer referenced directly.
…ost_fn

The `p256_verify` flag in `VMConfig` (and the corresponding
`Parameter::P256Verify` / YAML key) is renamed to `p256_verify_host_fn`
for consistency with the other host-function feature flags
(`gas_key_host_fns`, `global_contract_host_fns`). Singular `host_fn`
since there is only a single P-256 host function.

All snapshots, OpenAPI/OpenRPC descriptors, and the `#[p256_verify]`
import attribute are updated to match.
The feature flag was only used to look up the activation protocol
version in a single test. The runtime config parameter
(p256_verify_host_fn) is the authoritative gate for the host function,
so a ProtocolFeature variant isn't needed. The test now hardcodes
protocol version 84 (activation - 1) with a comment pointing at
85.yaml.
r-near added 2 commits April 22, 2026 09:50
The manual p256_verify -> p256_verify_host_fn rename missed the
`required` array in openrpc.json (the field declaration was renamed but
the name still appeared in the list of required fields).

The manual merge of master's protocol_schema.toml kept master's hashes
for the new BlockBatch / BlockBatchV1 types, but my changes transitively
affect their hash, so the tool now reports different values. Copied the
current hashes reported by check-protocol-schema.
@r-near r-near enabled auto-merge April 22, 2026 17:31
@r-near r-near added this pull request to the merge queue Apr 22, 2026
Merged via the queue into master with commit beebaca Apr 22, 2026
29 of 32 checks passed
@r-near r-near deleted the r-near/vm-runner/add-p256-verify branch April 22, 2026 21:16
}
};

match public_key.verify(&message, &signature) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reposting from here: it should use PrehashVerifier::verify_prehash instead:

Suggested change
match public_key.verify(&message, &signature) {
match public_key.verify_prehash(&message, &signature) {

pull Bot pushed a commit to See887777/nearcore that referenced this pull request Apr 28, 2026
Followup to near#15588, addressing [mitinarseny's
review](near#15588 (review)).

[NEP-635](near/NEPs#635) says `p256_verify` does
not hash — the caller supplies a prehashed digest (e.g. `SHA-256(msg)`
for WebAuthn). The merged implementation used `Verifier::verify`, which
SHA-256-hashes the message internally, so a contract passing a raw
32-byte prehash would fail to verify.

Switches to `PrehashVerifier::verify_prehash` in both host-function
copies (`VMLogic::p256_verify` and the wasmtime-runner `p256_verify`),
and updates unit / integration / test-loop tests plus the estimator
contract's hardcoded signatures accordingly. Wycheproof
`ecdsa_secp256r1_sha256_p1363` vectors still expect `SHA-256(msg)` to be
signed, so the test helper now hashes the message before handing it to
`p256_verify`.

Gas model is unchanged.
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.

7 participants