v0.5.3-pre.003

This commit is contained in:
2026-08-12 11:00:59 +02:00
parent 8448ad1079
commit 400ced4832
313 changed files with 7773 additions and 2623 deletions

View File

@@ -1,8 +1,14 @@
<!-- file: ks-lib/CHANGELOG.md -->
<!-- version: 53 -->
<!-- version: 54 -->
# CHANGELOG — ks-lib
## `0.5.3-pre.003`
- fait évoluer `MdCoreInstructionReplayInput` vers le contrat version 3 afin de transporter le contexte Core nécessaire aux futurs décodeurs : `block_time`, `stack_height`, parent CPI et `returnData` transactionnelle ;
- renomme le contexte de transaction `outer_instructions_json` en `top_level_instructions_json` dans ce nouveau contrat store/replay, sans supprimer les usages historiques du terme `outer` lorsqu'ils décrivent encore explicitement une sémantique de décodeur existante ;
- conserve le constructeur historique de replay pour les tests/sources synthétiques et ajoute l'enrichissement `with_core_context` utilisé par `ks-store`.
## `0.5.1-pre.008`
- retire la dépendance `ts-rs`, les dérivations/attributs TS-RS et les bindings TypeScript générés historiques de `ks-lib` ;

View File

@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/api/contracts.rs
// version: 9
// version: 10
//! Backend-neutral contextual instruction decoder contracts.
@@ -511,7 +511,7 @@ mod tests {
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!([{
second.top_level_instructions_json = serde_json::json!([{
"instructionIndex": 0,
"instructionPath": "0",
"programId": "program",

View File

@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/compute_budget.rs
// version: 7
// version: 8
//! Maximal current and historical Compute Budget instruction decoding.
@@ -341,7 +341,7 @@ fn compute_budget_outer_rows(
input: &crate::MdCoreInstructionReplayInput,
) -> std::vec::Vec<ComputeBudgetProfileRow> {
let mut rows = std::vec::Vec::new();
let outer_values = match input.outer_instructions_json.as_array() {
let outer_values = match input.top_level_instructions_json.as_array() {
std::option::Option::Some(value) => value,
std::option::Option::None => return rows,
};
@@ -705,7 +705,7 @@ mod tests {
fn replay_input_with_outer(
bytes: &[u8],
instruction_path: &str,
outer_instructions_json: serde_json::Value,
top_level_instructions_json: serde_json::Value,
) -> crate::MdCoreInstructionReplayInput {
let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
let result = crate::MdCoreInstructionReplayInput::new(
@@ -720,7 +720,7 @@ mod tests {
serde_json::json!([]),
std::option::Option::Some(serde_json::json!({"dataBase64": encoded})),
std::option::Option::Some("payload-hash".to_string()),
outer_instructions_json,
top_level_instructions_json,
serde_json::json!([]),
serde_json::json!([]),
serde_json::json!([]),

View File

@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/payload.rs
// version: 7
// version: 8
//! Bounded native instruction payload helpers.
@@ -228,7 +228,7 @@ fn resolve_explicit_outer_instruction(
input: &crate::MdCoreInstructionReplayInput,
instruction_index: usize,
) -> ks_core::Result<crate::DcSolanaCoreResolvedInstructionPayload> {
let entries = match input.outer_instructions_json.as_array() {
let entries = match input.top_level_instructions_json.as_array() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ks_core::Error::invalid_state(

View File

@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/precompiles.rs
// version: 6
// version: 7
//! Bounded structural decoding for Solana signature verification precompiles.
@@ -1599,11 +1599,11 @@ mod tests {
for (program_id, target, external) in cases {
let mut missing =
replay_input(program_id, target.clone(), vec![(0, external.clone())], false);
missing.outer_instructions_json[0]["payloadJson"] = serde_json::Value::Null;
missing.top_level_instructions_json[0]["payloadJson"] = serde_json::Value::Null;
let missing_result = crate::decoder_solana_core_precompiles_decode(&missing);
assert_eq!(missing_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
let mut invalid = replay_input(program_id, target, vec![(0, external)], false);
invalid.outer_instructions_json[0]["payloadJson"] =
invalid.top_level_instructions_json[0]["payloadJson"] =
serde_json::json!({"dataBase64": "%%%"});
let invalid_result = crate::decoder_solana_core_precompiles_decode(&invalid);
assert_eq!(invalid_result.status, crate::DcApiDecoderOutcomeStatus::Failed);

View File

@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/slashing.rs
// version: 4
// version: 5
//! Exact structural decoding for the enshrined stateless Slashing Program.
@@ -413,7 +413,7 @@ fn preceding_ed25519_instruction(input: &crate::MdCoreInstructionReplayInput) ->
});
},
};
let previous = input.outer_instructions_json.as_array().and_then(|instructions| {
let previous = input.top_level_instructions_json.as_array().and_then(|instructions| {
return instructions.iter().find(|instruction| {
return instruction
.get("instructionIndex")

View File

@@ -1,10 +1,10 @@
// file: ks-lib/src/model/replay.rs
// version: 4
// version: 5
//! Source-neutral instruction replay input shared by decoders and stores.
/// Version of the normalized core replay contract.
pub const MD_CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 2;
pub const MD_CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 3;
/// Decoder replay input containing one instruction plus extracted transaction context.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -17,8 +17,14 @@ pub struct MdCoreInstructionReplayInput {
pub signature: std::string::String,
/// Transaction slot in Solana unsigned representation.
pub slot: u64,
/// Optional block timestamp as Unix seconds supplied by Solana.
pub block_time: std::option::Option<i64>,
/// Stable instruction path, for example `0` or `2/1`.
pub instruction_path: std::string::String,
/// Optional immediate or best-known parent instruction path for CPI inputs.
pub parent_instruction_path: std::option::Option<std::string::String>,
/// Optional runtime invocation stack height.
pub stack_height: std::option::Option<u32>,
/// Program id as non-empty base58 text.
pub program_id: std::string::String,
/// Optional surface hint supplied by an operator or upstream classifier.
@@ -35,8 +41,10 @@ pub struct MdCoreInstructionReplayInput {
pub instruction_payload_json: std::option::Option<serde_json::Value>,
/// Optional deterministic hash of the retained instruction payload.
pub instruction_payload_hash: std::option::Option<std::string::String>,
/// Ordered outer instructions for the transaction.
pub outer_instructions_json: serde_json::Value,
/// Optional transaction return-data fact retained by Core.
pub return_data: std::option::Option<crate::MdCanonicalReturnData>,
/// Ordered top-level instructions for the transaction.
pub top_level_instructions_json: serde_json::Value,
/// Inner instruction tree or subtree relevant to the target instruction.
pub inner_instructions_json: serde_json::Value,
/// Ordered logs relevant to the transaction or target instruction.
@@ -45,7 +53,7 @@ pub struct MdCoreInstructionReplayInput {
pub balance_changes_json: serde_json::Value,
}
impl MdCoreInstructionReplayInput {
impl crate::MdCoreInstructionReplayInput {
/// Builds a decoder replay input after validating its stable identity and array context.
#[allow(clippy::too_many_arguments)]
pub fn new(
@@ -60,14 +68,14 @@ impl MdCoreInstructionReplayInput {
instruction_accounts_json: serde_json::Value,
instruction_payload_json: std::option::Option<serde_json::Value>,
instruction_payload_hash: std::option::Option<std::string::String>,
outer_instructions_json: serde_json::Value,
top_level_instructions_json: serde_json::Value,
inner_instructions_json: serde_json::Value,
logs_json: serde_json::Value,
balance_changes_json: serde_json::Value,
) -> ks_core::Result<Self> {
if !outer_instructions_json.is_array() {
if !top_level_instructions_json.is_array() {
return std::result::Result::Err(ks_core::Error::invalid_state(
"core replay input outer instructions must be a JSON array",
"core replay input top-level instructions must be a JSON array",
));
}
let value = Self {
@@ -75,7 +83,10 @@ impl MdCoreInstructionReplayInput {
replay_input_key: replay_input_key.into(),
signature: signature.into(),
slot,
block_time: std::option::Option::None,
instruction_path: instruction_path.into(),
parent_instruction_path: std::option::Option::None,
stack_height: std::option::Option::None,
program_id: program_id.into(),
surface_code_hint: std::option::Option::None,
transaction_failed,
@@ -84,7 +95,8 @@ impl MdCoreInstructionReplayInput {
instruction_accounts_json,
instruction_payload_json,
instruction_payload_hash,
outer_instructions_json,
return_data: std::option::Option::None,
top_level_instructions_json,
inner_instructions_json,
logs_json,
balance_changes_json,
@@ -124,6 +136,47 @@ impl MdCoreInstructionReplayInput {
return std::result::Result::Ok(());
}
/// Adds transaction time, CPI ancestry and return-data context retained by Core.
pub fn with_core_context(
mut self,
block_time: std::option::Option<i64>,
parent_instruction_path: std::option::Option<std::string::String>,
stack_height: std::option::Option<u32>,
return_data: std::option::Option<crate::MdCanonicalReturnData>,
) -> ks_core::Result<Self> {
if parent_instruction_path
.as_deref()
.is_some_and(|value| return value.trim().is_empty())
{
return std::result::Result::Err(ks_core::Error::invalid_state(
"core replay parent instruction path must not be empty when present",
));
}
if stack_height.is_some_and(|value| return value == 0) {
return std::result::Result::Err(ks_core::Error::invalid_state(
"core replay stack height must be greater than zero when present",
));
}
if parent_instruction_path.is_some() && stack_height.is_some_and(|value| return value <= 1)
{
return std::result::Result::Err(ks_core::Error::invalid_state(
"core replay CPI stack height must be greater than one when present",
));
}
if let std::option::Option::Some(value) = &return_data {
if value.program_id.trim().is_empty() {
return std::result::Result::Err(ks_core::Error::invalid_state(
"core replay return-data program id must not be empty",
));
}
}
self.block_time = block_time;
self.parent_instruction_path = parent_instruction_path;
self.stack_height = stack_height;
self.return_data = return_data;
return std::result::Result::Ok(self);
}
/// Adds an optional stable surface hint after validation.
pub fn with_surface_code_hint(
mut self,