From 9373d1858ae59787fd5f8517a1db4d7f214ae2b1 Mon Sep 17 00:00:00 2001 From: peetzweg Date: Tue, 18 Aug 2026 14:56:25 +0100 Subject: [PATCH] spike(ring-vrf): dev-only raw proof context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chain checks a lite-alias proof's context against its own constants (pop:polkadot.network/score for the game), and no blake2b("product//…") can equal one, so the whole lite free-play flow is unrunnable end to end before individuality#1247 aligns them. This lets a caller name the 32 bytes directly, gated on an env var no released host sets and a product id dotNS cannot issue. Not for release: a host that honours it lets a product mint a proof in a context it does not own, which is the property the derivation guarantees. It exists so the flow could be run once, which it now has been - both legs land on nextv2. --- .../src/runtime/signing_host/ring_vrf.rs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/rust/crates/truapi-server/src/runtime/signing_host/ring_vrf.rs b/rust/crates/truapi-server/src/runtime/signing_host/ring_vrf.rs index 7e6e51b8e..fc946639b 100644 --- a/rust/crates/truapi-server/src/runtime/signing_host/ring_vrf.rs +++ b/rust/crates/truapi-server/src/runtime/signing_host/ring_vrf.rs @@ -13,7 +13,7 @@ use crate::host_logic::sso::messages::RingVrfError; use async_trait::async_trait; use subxt::dynamic; use subxt::ext::scale_decode::DecodeAsType; -use truapi::v01::{ProductProofContext, RingLocation, RingLocationJunction}; +use truapi::v01::{DerivationIndex, ProductProofContext, RingLocation, RingLocationJunction}; use verifiable::GenerateVerifiable; use verifiable::ring::RingDomainSize; use verifiable::ring::bandersnatch::BandersnatchVrfVerifiable; @@ -212,7 +212,26 @@ impl RingResolver for ChainRingResolver { } } +const RAW_CONTEXT_ENV: &str = "TRUAPI_RAW_PROOF_CONTEXT"; +const RAW_CONTEXT_PRODUCT_ID: &str = "raw:"; + +fn raw_context_override(context: &ProductProofContext) -> Option<[u8; 32]> { + if context.product_id != RAW_CONTEXT_PRODUCT_ID { + return None; + } + if std::env::var(RAW_CONTEXT_ENV).as_deref() != Ok("1") { + return None; + } + match context.suffix { + DerivationIndex::Raw(bytes) => Some(bytes), + DerivationIndex::Index(_) => None, + } +} + pub(in crate::runtime) fn context_bytes(context: &ProductProofContext) -> [u8; 32] { + if let Some(raw) = raw_context_override(context) { + return raw; + } let suffix = derivation_index_bytes(&context.suffix); let mut input = Vec::with_capacity(9 + context.product_id.len() + suffix.len()); input.extend_from_slice(b"product/");