This commit is contained in:
2026-07-23 16:37:12 +02:00
parent 99c345f2f2
commit 0da75c1311
2159 changed files with 230833 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# file: kb_decoder_api/Cargo.toml
# version: 2
[package]
name = "kb_decoder_api"
version.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
base64.workspace = true
kb_core = { path = "../kb_core" }
kb_model = { path = "../kb_model" }
kb_store_core = { path = "../kb_store_core" }
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
[dev-dependencies]
[lints]
workspace = true

View File

@@ -0,0 +1,25 @@
<!-- file: kb_decoder_api/README.md -->
<!-- version: 3 -->
# kb_decoder_api
Ce crate déclare l'interface commune des décodeurs protocolaires.
## Rôle dans l'écosystème
Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace.
## Règles locales
- Les commentaires de code restent en anglais.
- La documentation Markdown reste en français.
- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque.
- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires.
## Contrats `0.4.0` et contexte core `2`
Le trait `InstructionDecoder` reçoit exclusivement `CoreInstructionReplayInput`. Il expose une identité et une version stables, les surfaces supportées, la couverture déclarée, la reconnaissance déterministe et un résultat explicite `decoded`, `ignored`, `unsupported` ou `failed`. Les observations produites conservent la preuve, la confiance, le statut on-chain et le caractère commité ou annulé.
Depuis `0.4.1-pre.014`, le même contrat inclut les payloads ordonnés des instructions outer. `contextual_input_hash` sérialise lensemble du contexte, trie récursivement les clés dobjets et conserve lordre des tableaux. Un changement de payload outer modifie donc le hash, tandis quun contexte identique produit un hash stable.
Le crate reste indépendant de PostgreSQL, Tauri et des fournisseurs RPC.

View File

@@ -0,0 +1,5 @@
// file: kb_decoder_api/src/constants.rs
// version: 1
/// Canonical tracing target for this crate.
pub(crate) const TRACING_TARGET: &str = "kb_decoder_api";

View File

@@ -0,0 +1,577 @@
// file: kb_decoder_api/src/contracts.rs
// version: 7
//! Backend-neutral contextual instruction decoder contracts.
use sha2::Digest; // rust-rules: trait-import
/// Current contextual core instruction input contract version.
pub const CORE_INSTRUCTION_INPUT_CONTRACT_VERSION: u32 =
kb_store_core::CORE_REPLAY_INPUT_CONTRACT_VERSION;
/// Stable identity of one contextual instruction decoder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecoderIdentity {
/// Stable lower snake case processor name.
pub name: std::string::String,
/// Semantic or deterministic implementation version.
pub version: std::string::String,
}
impl DecoderIdentity {
/// Builds a validated decoder identity.
pub fn new(
name: impl std::convert::Into<std::string::String>,
version: impl std::convert::Into<std::string::String>,
) -> kb_core::Result<Self> {
let value = Self {
name: name.into(),
version: version.into(),
};
if value.name.trim().is_empty() || value.version.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoder identity fields must not be empty",
));
}
return std::result::Result::Ok(value);
}
}
/// One statically declared Solana surface supported by a decoder.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DecoderSurface {
/// Exact Solana program identifier.
pub program_id: &'static str,
/// Stable surface code.
pub surface_code: &'static str,
/// Explicit dispatch priority, where larger values win.
pub priority: u16,
}
/// Declared decoder coverage entry kind.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum DecoderCoverageEntryKind {
/// Program instruction entry.
Instruction,
/// Program event entry.
Event,
/// Raw discriminator entry.
Discriminator,
}
/// Machine-readable decoder coverage declaration.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecoderCoverageDeclaration {
/// Solana program identifier.
pub program_id: std::string::String,
/// Optional stable surface code.
pub surface_code: std::option::Option<std::string::String>,
/// Entry kind.
pub entry_kind: DecoderCoverageEntryKind,
/// Stable instruction, event or discriminator code.
pub entry_code: std::string::String,
/// Optional normalized lowercase hexadecimal discriminator.
pub discriminator_hex: std::option::Option<std::string::String>,
/// Whether the entry is historical or deprecated but still supported.
pub historical: bool,
}
impl DecoderCoverageDeclaration {
/// Builds a validated coverage declaration.
pub fn new(
program_id: impl std::convert::Into<std::string::String>,
surface_code: std::option::Option<std::string::String>,
entry_kind: DecoderCoverageEntryKind,
entry_code: impl std::convert::Into<std::string::String>,
discriminator_hex: std::option::Option<std::string::String>,
historical: bool,
) -> kb_core::Result<Self> {
let value = Self {
program_id: program_id.into(),
surface_code,
entry_kind,
entry_code: entry_code.into(),
discriminator_hex,
historical,
};
if value.program_id.trim().is_empty() || value.entry_code.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoder coverage program id and entry code must not be empty",
));
}
if value
.surface_code
.as_deref()
.is_some_and(|surface| return surface.trim().is_empty())
{
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoder coverage surface code must not be empty when present",
));
}
if value
.discriminator_hex
.as_deref()
.is_some_and(|discriminator| return discriminator.trim().is_empty())
{
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoder coverage discriminator must not be empty when present",
));
}
return std::result::Result::Ok(value);
}
}
/// Deterministic recognition result used by dispatch.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecoderRecognition {
/// Whether the decoder is compatible with the input.
pub compatible: bool,
/// Whether recognition is exact rather than program-only.
pub exact: bool,
/// Explicit priority used after program and surface matching.
pub priority: u16,
/// Optional recognized surface code.
pub surface_code: std::option::Option<std::string::String>,
/// Optional recognized instruction or discriminator code.
pub entry_code: std::option::Option<std::string::String>,
/// Optional normalized lowercase hexadecimal discriminator.
pub discriminator_hex: std::option::Option<std::string::String>,
}
impl DecoderRecognition {
/// Builds a non-compatible recognition result.
pub fn incompatible() -> Self {
return Self {
compatible: false,
exact: false,
priority: 0,
surface_code: std::option::Option::None,
entry_code: std::option::Option::None,
discriminator_hex: std::option::Option::None,
};
}
/// Builds a compatible recognition result.
pub fn compatible(
exact: bool,
priority: u16,
surface_code: std::option::Option<std::string::String>,
entry_code: std::option::Option<std::string::String>,
discriminator_hex: std::option::Option<std::string::String>,
) -> Self {
return Self {
compatible: true,
exact,
priority,
surface_code,
entry_code,
discriminator_hex,
};
}
}
/// Decoder terminal result status.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum DecoderOutcomeStatus {
/// At least one stable decoded observation was produced.
Decoded,
/// The decoder intentionally ignored a recognized input.
Ignored,
/// The input belongs to the program but its entry is unsupported.
Unsupported,
/// Decoding failed and diagnostics were produced.
Failed,
}
/// Proof category attached to one decoded observation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum DecoderProofKind {
/// Exact instruction discriminator match.
ExactDiscriminator,
/// Exact binary layout decode.
ExactLayout,
/// Exact IDL-based decode.
Idl,
/// Manually verified exact decode.
Manual,
/// Correlation with runtime logs.
LogCorrelation,
/// Balance delta evidence.
BalanceDelta,
/// Heuristic evidence that must not be treated as exact.
Heuristic,
/// Audit-only classification.
Audit,
/// Unknown or unclassified evidence.
Unknown,
}
/// Explicit proof attached to one decoded observation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecoderProof {
/// Proof category.
pub kind: DecoderProofKind,
/// Decoder confidence.
pub confidence: kb_model::DecoderConfidence,
/// Stable evidence strings, hashes or classifier codes.
pub evidence: std::vec::Vec<std::string::String>,
}
/// Structured decoder diagnostic without third-party error wrappers.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecoderDiagnostic {
/// Stable machine-readable diagnostic code.
pub code: std::string::String,
/// Human-readable diagnostic message.
pub message: std::string::String,
/// Whether retrying the same version and input may succeed.
pub retriable: bool,
}
impl DecoderDiagnostic {
/// Builds a validated decoder diagnostic.
pub fn new(
code: impl std::convert::Into<std::string::String>,
message: impl std::convert::Into<std::string::String>,
retriable: bool,
) -> kb_core::Result<Self> {
let value = Self {
code: code.into(),
message: message.into(),
retriable,
};
if value.code.trim().is_empty() || value.message.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoder diagnostic code and message must not be empty",
));
}
return std::result::Result::Ok(value);
}
}
/// Stable contextual decoded observation persisted by the decode pipeline.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecodedObservation {
/// Stable processor-owned event key within one input.
pub event_key: std::string::String,
/// Shared typed protocol event identity.
pub event: kb_model::DecodedProtocolEvent,
/// Typed event payload serialized as deterministic JSON.
pub payload_json: serde_json::Value,
/// Whether the source transaction failed on-chain.
pub transaction_failed: bool,
/// Optional transaction error JSON.
pub transaction_error: std::option::Option<serde_json::Value>,
/// Whether the observed state mutation was committed on-chain.
pub observation_committed: bool,
/// Explicit decoding proof.
pub proof: DecoderProof,
}
impl DecodedObservation {
/// Validates failed transaction commit semantics and stable identity fields.
pub fn validate(&self) -> kb_core::Result<()> {
if self.event_key.trim().is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoded observation event key must not be empty",
));
}
if self.transaction_failed && self.observation_committed {
return std::result::Result::Err(kb_core::Error::invalid_state(
"failed transaction observations must not be marked committed",
));
}
if self.event.signature.0.trim().is_empty()
|| self.event.instruction_path.0.trim().is_empty()
|| self.event.program_id.0.trim().is_empty()
|| self.event.event_code.0.trim().is_empty()
{
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoded observation event identity fields must not be empty",
));
}
return std::result::Result::Ok(());
}
}
/// Complete explicit result produced by one decoder invocation.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecoderExecutionResult {
/// Terminal decoder status.
pub status: DecoderOutcomeStatus,
/// Optional recognized instruction or discriminator code.
pub recognized_entry_code: std::option::Option<std::string::String>,
/// Decoded observations.
pub observations: std::vec::Vec<DecodedObservation>,
/// Structured diagnostics.
pub diagnostics: std::vec::Vec<DecoderDiagnostic>,
}
impl DecoderExecutionResult {
/// Builds an unsupported result with no false decoded observation.
pub fn unsupported(entry_code: std::option::Option<std::string::String>) -> Self {
return Self {
status: DecoderOutcomeStatus::Unsupported,
recognized_entry_code: entry_code,
observations: std::vec::Vec::new(),
diagnostics: std::vec::Vec::new(),
};
}
/// Builds an ignored result.
pub fn ignored(entry_code: std::option::Option<std::string::String>) -> Self {
return Self {
status: DecoderOutcomeStatus::Ignored,
recognized_entry_code: entry_code,
observations: std::vec::Vec::new(),
diagnostics: std::vec::Vec::new(),
};
}
/// Validates status and observation consistency.
pub fn validate(&self) -> kb_core::Result<()> {
if self.status == DecoderOutcomeStatus::Decoded && self.observations.is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"decoded status requires at least one observation",
));
}
if self.status != DecoderOutcomeStatus::Decoded && !self.observations.is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"non-decoded status must not contain observations",
));
}
if self.status == DecoderOutcomeStatus::Failed && self.diagnostics.is_empty() {
return std::result::Result::Err(kb_core::Error::invalid_state(
"failed decoder status requires at least one diagnostic",
));
}
for observation in &self.observations {
let validation_result = observation.validate();
if let std::result::Result::Err(error) = validation_result {
return std::result::Result::Err(error);
}
}
return std::result::Result::Ok(());
}
}
/// Contextual instruction decoder contract used by the common pipeline.
pub trait InstructionDecoder: std::marker::Send + std::marker::Sync {
/// Returns the stable decoder identity.
fn identity(&self) -> DecoderIdentity;
/// Returns every exact Solana surface supported by the decoder.
fn surfaces(&self) -> &'static [DecoderSurface];
/// Returns the machine-readable declared coverage matrix.
fn coverage(&self) -> std::vec::Vec<DecoderCoverageDeclaration>;
/// Recognizes one contextual core instruction for deterministic dispatch.
fn recognize(&self, input: &kb_store_core::CoreInstructionReplayInput) -> DecoderRecognition;
/// Decodes one recognized contextual core instruction.
fn decode(&self, input: &kb_store_core::CoreInstructionReplayInput) -> DecoderExecutionResult;
}
/// Returns true when a decoder declares the exact program id.
pub fn decoder_handles_program_id(decoder: &dyn InstructionDecoder, program_id: &str) -> bool {
for surface in decoder.surfaces() {
if surface.program_id == program_id {
return true;
}
}
return false;
}
/// Computes a deterministic lowercase SHA-256 hash for one contextual input.
pub fn contextual_input_hash(
input: &kb_store_core::CoreInstructionReplayInput,
) -> kb_core::Result<std::string::String> {
let serialization_result = serde_json::to_value(input);
let mut value = match serialization_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::json(format!(
"cannot serialize contextual decoder input: {error}"
)));
},
};
return crate::deterministic_json_hash(&mut value);
}
/// Computes a deterministic lowercase SHA-256 hash for one JSON value.
pub fn deterministic_json_hash(
value: &mut serde_json::Value,
) -> kb_core::Result<std::string::String> {
sort_json_value(value);
let bytes_result = serde_json::to_vec(value);
let bytes = match bytes_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::json(format!(
"cannot serialize deterministic JSON bytes: {error}"
)));
},
};
let digest = sha2::Sha256::digest(bytes);
let mut output = std::string::String::with_capacity(64);
for byte in digest {
output.push_str(format!("{byte:02x}").as_str());
}
return std::result::Result::Ok(output);
}
/// Extracts the first eight payload bytes as normalized lowercase hexadecimal when available.
pub fn discriminator_8_hex(
input: &kb_store_core::CoreInstructionReplayInput,
) -> std::option::Option<std::string::String> {
let payload = match &input.instruction_payload_json {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let decoded_result =
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, encoded.as_bytes());
let decoded = match decoded_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_error) => return std::option::Option::None,
};
if decoded.len() < 8 {
return std::option::Option::None;
}
let mut output = std::string::String::with_capacity(16);
for byte in &decoded[..8] {
output.push_str(format!("{byte:02x}").as_str());
}
return std::option::Option::Some(output);
}
fn sort_json_value(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(map) => {
let mut entries = std::mem::take(map).into_iter().collect::<std::vec::Vec<_>>();
entries.sort_by(|left, right| return left.0.cmp(&right.0));
for (key, mut child) in entries {
sort_json_value(&mut child);
map.insert(key, child);
}
},
serde_json::Value::Array(values) => {
for child in values {
sort_json_value(child);
}
},
_ => (),
}
}
#[cfg(test)]
mod tests {
fn replay_input() -> kb_store_core::CoreInstructionReplayInput {
let result = kb_store_core::CoreInstructionReplayInput::new(
"signature:0",
"signature",
42,
"0",
"program",
false,
std::option::Option::None,
serde_json::json!([{"accountIndex": 0, "accountKey": "account"}]),
serde_json::json!([{"accountIndex": 0, "accountKey": "account"}]),
std::option::Option::Some(serde_json::json!({"dataBase64": "AQIDBAUGBwg="})),
std::option::Option::Some("payload-hash".to_string()),
serde_json::json!([{
"instructionIndex": 0,
"instructionPath": "0",
"programId": "program",
"payloadJson": {"dataBase64": "AQIDBAUGBwg="},
"payloadHash": "payload-hash"
}]),
serde_json::json!([]),
serde_json::json!([]),
serde_json::json!([]),
);
return match result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected replay input error: {error}"),
};
}
#[test]
fn contextual_input_hash_is_stable() {
let first = crate::contextual_input_hash(&replay_input());
let second = crate::contextual_input_hash(&replay_input());
assert!(first.is_ok());
assert_eq!(first, second);
}
#[test]
fn contextual_input_hash_changes_when_outer_context_changes() {
let first = replay_input();
let mut second = replay_input();
second.outer_instructions_json = serde_json::json!([{
"instructionIndex": 0,
"instructionPath": "0",
"programId": "program",
"payloadJson": {"dataBase64": "CAcGBQQDAgE="},
"payloadHash": "different-payload-hash"
}]);
let first_hash = crate::contextual_input_hash(&first);
let second_hash = crate::contextual_input_hash(&second);
assert!(first_hash.is_ok());
assert!(second_hash.is_ok());
assert_ne!(first_hash, second_hash);
}
#[test]
fn contextual_input_serialization_is_deterministic_with_outer_context() {
let first_result = serde_json::to_string(&replay_input());
let first = match first_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"),
};
let second_result = serde_json::to_string(&replay_input());
let second = match second_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"),
};
assert_eq!(first, second);
}
#[test]
fn discriminator_uses_first_eight_bytes() {
let value = crate::discriminator_8_hex(&replay_input());
assert_eq!(value.as_deref(), std::option::Option::Some("0102030405060708"));
}
#[test]
fn failed_observation_cannot_be_committed() {
let observation = crate::DecodedObservation {
event_key: "event".to_string(),
event: kb_model::DecodedProtocolEvent {
signature: kb_model::Signature("signature".to_string()),
slot: kb_model::Slot(42),
instruction_path: kb_model::InstructionPath("0".to_string()),
program_id: kb_model::ProgramId("program".to_string()),
protocol_code: kb_model::ProtocolCode("solana".to_string()),
surface_code: kb_model::SurfaceCode("system_program".to_string()),
event_code: kb_model::EventCode("system_program.attempt".to_string()),
event_name: kb_model::EventName("attempt".to_string()),
event_family: kb_model::EventFamily::Audit,
source_kind: kb_model::EventSourceKind::Instruction,
confidence: kb_model::DecoderConfidence::Exact,
},
payload_json: serde_json::json!({}),
transaction_failed: true,
transaction_error: std::option::Option::Some(
serde_json::json!({"InstructionError": [0, "Custom"]}),
),
observation_committed: true,
proof: crate::DecoderProof {
kind: crate::DecoderProofKind::ExactLayout,
confidence: kb_model::DecoderConfidence::Exact,
evidence: std::vec!["layout".to_string()],
},
};
assert!(observation.validate().is_err());
}
}

View File

@@ -0,0 +1,74 @@
// file: kb_decoder_api/src/decoder.rs
// version: 4
//! Shared decoder contracts for protocol decoder crates.
/// Decoder support level for a program observation.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecoderSupport {
/// The decoder does not support the observation.
No,
/// The decoder may support the observation after deeper inspection.
Maybe,
/// The decoder explicitly supports the observation.
Yes,
}
/// Common trait implemented by every protocol decoder crate.
pub trait ProtocolDecoder {
/// Returns the stable decoder name.
fn decoder_name(&self) -> &'static str;
/// Returns the decoder implementation version.
fn decoder_version(&self) -> &'static str;
/// Returns the Solana program ids handled by this decoder.
fn program_ids(&self) -> &'static [&'static str];
/// Checks whether the decoder handles a program id.
fn handles_program_id(&self, program_id: &kb_model::ProgramId) -> bool {
for candidate in self.program_ids() {
if program_id.0.as_str() == *candidate {
return true;
}
}
return false;
}
/// Checks whether the decoder supports a generic program observation.
fn supports_observation(&self, observation: &kb_model::ProgramObservation) -> DecoderSupport;
/// Decodes a generic program observation into protocol events.
fn decode_observation(
&self,
observation: &kb_model::ProgramObservation,
) -> kb_core::Result<std::vec::Vec<kb_model::DecodedProtocolEvent>>;
}
/// Initial decoder implementation placeholder for API-level tests.
#[derive(Clone, Debug, Default)]
pub struct InitialDecoder;
impl ProtocolDecoder for InitialDecoder {
fn decoder_name(&self) -> &'static str {
return "kb_decoder_api";
}
fn decoder_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return &[];
}
fn supports_observation(&self, observation: &kb_model::ProgramObservation) -> DecoderSupport {
if ProtocolDecoder::handles_program_id(self, &observation.program_id) {
return DecoderSupport::Maybe;
}
return DecoderSupport::No;
}
fn decode_observation(
&self,
_observation: &kb_model::ProgramObservation,
) -> kb_core::Result<std::vec::Vec<kb_model::DecodedProtocolEvent>> {
let _target = crate::TRACING_TARGET;
return std::result::Result::Ok(std::vec::Vec::new());
}
}

View File

@@ -0,0 +1,56 @@
// file: kb_decoder_api/src/lib.rs
// version: 5
//! Decoder API shared by protocol decoder crates.
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
mod constants;
mod contracts;
mod decoder;
/// Canonical tracing target for this crate.
pub(crate) use crate::constants::TRACING_TARGET;
/// Current contextual core instruction input contract version.
pub use crate::contracts::CORE_INSTRUCTION_INPUT_CONTRACT_VERSION;
/// Stable contextual decoded observation.
pub use crate::contracts::DecodedObservation;
/// Machine-readable decoder coverage declaration.
pub use crate::contracts::DecoderCoverageDeclaration;
/// Declared decoder coverage entry kind.
pub use crate::contracts::DecoderCoverageEntryKind;
/// Structured decoder diagnostic.
pub use crate::contracts::DecoderDiagnostic;
/// Complete decoder execution result.
pub use crate::contracts::DecoderExecutionResult;
/// Stable decoder identity.
pub use crate::contracts::DecoderIdentity;
/// Decoder terminal result status.
pub use crate::contracts::DecoderOutcomeStatus;
/// Explicit decoding proof.
pub use crate::contracts::DecoderProof;
/// Decoding proof category.
pub use crate::contracts::DecoderProofKind;
/// Deterministic decoder recognition result.
pub use crate::contracts::DecoderRecognition;
/// One statically declared decoder surface.
pub use crate::contracts::DecoderSurface;
/// Contextual instruction decoder contract.
pub use crate::contracts::InstructionDecoder;
/// Computes the deterministic contextual input hash.
pub use crate::contracts::contextual_input_hash;
/// Returns true when a decoder declares the exact program id.
pub use crate::contracts::decoder_handles_program_id;
/// Computes a deterministic lowercase SHA-256 hash for one JSON value.
pub use crate::contracts::deterministic_json_hash;
/// Extracts the first eight payload bytes as hexadecimal when available.
pub use crate::contracts::discriminator_8_hex;
/// Exposes the decoder support level.
pub use crate::decoder::DecoderSupport;
/// Initial decoder implementation placeholder for API-level tests.
pub use crate::decoder::InitialDecoder;
/// Exposes the common protocol decoder trait.
pub use crate::decoder::ProtocolDecoder;