v0.3.4-pre.002

This commit is contained in:
2026-08-30 21:11:23 +02:00
parent 03ad0ba063
commit 6bd593f467
18 changed files with 710 additions and 46 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 364
# version: 365
[workspace]
resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"]
[workspace.package]
version = "0.3.4-pre.1"
version = "0.3.4-pre.2"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -0,0 +1,12 @@
DO $ksp$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'pk_ksp_raw_account_states'
AND conrelid = to_regclass('ksp_raw_account_states')
) THEN
ALTER TABLE ksp_raw_account_states ADD CONSTRAINT pk_ksp_raw_account_states PRIMARY KEY (pubkey, slot, state_hash);
END IF;
END
$ksp$;

View File

@@ -0,0 +1,12 @@
DO $ksp$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'pk_ksp_raw_account_observations'
AND conrelid = to_regclass('ksp_raw_account_observations')
) THEN
ALTER TABLE ksp_raw_account_observations ADD CONSTRAINT pk_ksp_raw_account_observations PRIMARY KEY (observation_key);
END IF;
END
$ksp$;

View File

@@ -0,0 +1,14 @@
DO $ksp$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'fk_ksp_raw_account_observations_state'
AND conrelid = to_regclass('ksp_raw_account_observations')
) THEN
ALTER TABLE ksp_raw_account_observations
ADD CONSTRAINT fk_ksp_raw_account_observations_state FOREIGN KEY (account_pubkey, account_slot, account_state_hash)
REFERENCES ksp_raw_account_states(pubkey, slot, state_hash) ON DELETE RESTRICT;
END IF;
END
$ksp$;

View File

@@ -0,0 +1,19 @@
CREATE TABLE IF NOT EXISTS ksp_raw_account_states (
pubkey BYTEA NOT NULL,
slot NUMERIC(20, 0) NOT NULL,
state_hash BYTEA NOT NULL,
lamports NUMERIC(20, 0) NOT NULL,
owner BYTEA NOT NULL,
executable BOOLEAN NOT NULL,
rent_epoch NUMERIC(20, 0) NOT NULL,
data BYTEA NOT NULL
);
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS pubkey BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS slot NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS state_hash BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS lamports NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS owner BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS executable BOOLEAN NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS rent_epoch NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS data BYTEA NOT NULL;

View File

@@ -0,0 +1,41 @@
CREATE TABLE IF NOT EXISTS ksp_raw_account_observations (
observation_key BYTEA NOT NULL,
account_pubkey BYTEA NOT NULL,
account_slot NUMERIC(20, 0) NOT NULL,
account_state_hash BYTEA NOT NULL,
provider TEXT NOT NULL,
protocol TEXT NOT NULL,
acquisition_method TEXT NOT NULL,
origin TEXT NOT NULL,
received_at_unix_millis BIGINT NOT NULL,
capture_session_id TEXT NULL,
commitment TEXT NULL,
endpoint_id TEXT NULL,
filter_id TEXT NULL,
observed_at_unix_millis BIGINT NULL,
source_payload_hash BYTEA NULL,
source_payload_size_bytes BIGINT NULL,
is_startup BOOLEAN NULL,
transaction_signature BYTEA NULL,
write_version NUMERIC(20, 0) NULL
);
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS observation_key BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS account_pubkey BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS account_slot NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS account_state_hash BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS provider TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS protocol TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS acquisition_method TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS origin TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS received_at_unix_millis BIGINT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS capture_session_id TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS commitment TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS endpoint_id TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS filter_id TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS observed_at_unix_millis BIGINT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS source_payload_hash BYTEA NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS source_payload_size_bytes BIGINT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS is_startup BOOLEAN NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS transaction_signature BYTEA NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS write_version NUMERIC(20, 0) NULL;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/lib.rs
// version: 14
// version: 15
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -21,6 +21,8 @@
//! with compare-and-transition outcomes and explicit rejection of `Compacted`.
//! `0.3.3-pre.008` implements all six `RawTransaction*` capabilities directly on
//! `PostgresBackend` while preserving the existing narrow backend bridge.
//! `0.3.4-pre.002` registers additive V002 and its two minimal RAW account
//! tables with canonical state/observation PKs and the observation-state FK.
//!
//! This crate depends on `ksp-store-api` and never on `ksp-store-lib`. The
//! common facade consumes only this crate's narrow backend bridge and never
@@ -93,6 +95,8 @@ pub(crate) use self::schema::SchemaResourceState;
pub(crate) use self::schema::V000_RESOURCES;
/// Private V001 schema resource inventory consumed by the migration engine.
pub(crate) use self::schema::V001_RESOURCES;
/// Private V002 schema resource inventory consumed by the migration engine.
pub(crate) use self::schema::V002_RESOURCES;
/// Private physical schema resource inspector consumed by the migration engine.
pub(crate) use self::schema::inspect_resource;
/// Private V001 adoption probe consumed by the migration engine.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/migration.rs
// version: 7
// version: 8
use sha2::Digest; // rust-rules: trait-import
@@ -19,6 +19,13 @@ const EMBEDDED_MIGRATIONS: &[EmbeddedMigration] = &[
resources: crate::V001_RESOURCES,
version: 1,
},
EmbeddedMigration {
checksum: MigrationChecksum::Resources,
hook: MigrationHook::None,
name: "raw_account_state",
resources: crate::V002_RESOURCES,
version: 2,
},
];
const HEX_LOWER: &[u8; 16] = b"0123456789abcdef";
const HISTORY_INSERT_SQL: &str = "INSERT INTO ksp_store_schema_migrations (version, name, checksum, applied_at) VALUES ($1, $2, $3, CURRENT_TIMESTAMP)";

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/schema.rs
// version: 7
// version: 8
/// Immutable V000 physical schema resource inventory.
pub(crate) const V000_RESOURCES: &[SchemaResource] = &[SchemaResource {
@@ -378,6 +378,55 @@ pub(crate) const V001_RESOURCES: &[SchemaResource] = &[
sql: include_str!("../migrations/v001_raw_transaction/indexes/001_ix_ksp_raw_transactions_slot_signature.sql"),
},
];
/// V002 physical schema resource inventory for the minimal RAW account state foundation.
pub(crate) const V002_RESOURCES: &[SchemaResource] = &[
SchemaResource {
id: "tables/001_ksp_raw_account_states.sql",
object: SchemaObjectContract::Table(TableContract {
columns: RAW_ACCOUNT_STATES_COLUMNS,
name: "ksp_raw_account_states",
primary_key_columns: std::option::Option::None,
}),
repair_existing: true,
sql: include_str!("../migrations/v002_raw_account_state/tables/001_ksp_raw_account_states.sql"),
},
SchemaResource {
id: "tables/002_ksp_raw_account_observations.sql",
object: SchemaObjectContract::Table(TableContract {
columns: RAW_ACCOUNT_OBSERVATIONS_COLUMNS,
name: "ksp_raw_account_observations",
primary_key_columns: std::option::Option::None,
}),
repair_existing: true,
sql: include_str!("../migrations/v002_raw_account_state/tables/002_ksp_raw_account_observations.sql"),
},
SchemaResource {
id: "constraints/001_pk_ksp_raw_account_states.sql",
object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_raw_account_states", table: "ksp_raw_account_states" }),
repair_existing: true,
sql: include_str!("../migrations/v002_raw_account_state/constraints/001_pk_ksp_raw_account_states.sql"),
},
SchemaResource {
id: "constraints/002_pk_ksp_raw_account_observations.sql",
object: SchemaObjectContract::Constraint(ConstraintContract {
kind: "p",
name: "pk_ksp_raw_account_observations",
table: "ksp_raw_account_observations",
}),
repair_existing: true,
sql: include_str!("../migrations/v002_raw_account_state/constraints/002_pk_ksp_raw_account_observations.sql"),
},
SchemaResource {
id: "constraints/003_fk_ksp_raw_account_observations_state.sql",
object: SchemaObjectContract::Constraint(ConstraintContract {
kind: "f",
name: "fk_ksp_raw_account_observations_state",
table: "ksp_raw_account_observations",
}),
repair_existing: true,
sql: include_str!("../migrations/v002_raw_account_state/constraints/003_fk_ksp_raw_account_observations_state.sql"),
},
];
const COLUMN_LOAD_SQL: &str = r#"SELECT column_name::TEXT, udt_name::TEXT, (is_nullable = 'YES') AS nullable, numeric_precision::INTEGER, numeric_scale::INTEGER, column_default::TEXT, is_identity::TEXT, is_generated::TEXT
FROM information_schema.columns
@@ -710,6 +759,199 @@ const RAW_TRANSACTION_OBSERVATIONS_COLUMNS: &[ColumnContract] = &[
udt_name: "int8",
},
];
const RAW_ACCOUNT_STATES_COLUMNS: &[ColumnContract] = &[
ColumnContract {
name: "pubkey",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "slot",
nullable: false,
numeric_precision: std::option::Option::Some(20),
numeric_scale: std::option::Option::Some(0),
udt_name: "numeric",
},
ColumnContract {
name: "state_hash",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "lamports",
nullable: false,
numeric_precision: std::option::Option::Some(20),
numeric_scale: std::option::Option::Some(0),
udt_name: "numeric",
},
ColumnContract {
name: "owner",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "executable",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bool",
},
ColumnContract {
name: "rent_epoch",
nullable: false,
numeric_precision: std::option::Option::Some(20),
numeric_scale: std::option::Option::Some(0),
udt_name: "numeric",
},
ColumnContract {
name: "data",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
];
const RAW_ACCOUNT_OBSERVATIONS_COLUMNS: &[ColumnContract] = &[
ColumnContract {
name: "observation_key",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "account_pubkey",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "account_slot",
nullable: false,
numeric_precision: std::option::Option::Some(20),
numeric_scale: std::option::Option::Some(0),
udt_name: "numeric",
},
ColumnContract {
name: "account_state_hash",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "provider",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "protocol",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "acquisition_method",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "origin",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "received_at_unix_millis",
nullable: false,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "int8",
},
ColumnContract {
name: "capture_session_id",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "commitment",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "endpoint_id",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "filter_id",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "text",
},
ColumnContract {
name: "observed_at_unix_millis",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "int8",
},
ColumnContract {
name: "source_payload_hash",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "source_payload_size_bytes",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "int8",
},
ColumnContract {
name: "is_startup",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bool",
},
ColumnContract {
name: "transaction_signature",
nullable: true,
numeric_precision: std::option::Option::None,
numeric_scale: std::option::Option::None,
udt_name: "bytea",
},
ColumnContract {
name: "write_version",
nullable: true,
numeric_precision: std::option::Option::Some(20),
numeric_scale: std::option::Option::Some(0),
udt_name: "numeric",
},
];
const RAW_TRANSACTION_ARCHIVE_PAYLOADS_COLUMNS: &[ColumnContract] = &[
ColumnContract {
name: "signature",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
// version: 14
// version: 15
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -23,6 +23,7 @@ fn pre_005_backend_owns_exact_physical_runtime_dependencies_without_reverse_faca
let bootstrap_sql = include_str!("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql");
assert!(migration.contains("crate::V000_RESOURCES"));
assert!(migration.contains("crate::V001_RESOURCES"));
assert!(migration.contains("crate::V002_RESOURCES"));
assert!(schema.contains("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql"));
assert!(schema.contains("../migrations/v001_raw_transaction/tables/001_ksp_store_identity.sql"));
assert!(bootstrap_sql.contains("ksp_store_schema_migrations"));
@@ -113,7 +114,7 @@ fn pre_003_fix_001_migration_engine_uses_split_schema_contract_and_binds_network
assert!(observation_table.contains("CREATE TABLE IF NOT EXISTS ksp_raw_transaction_observations"));
assert!(archive_table.contains("CREATE TABLE IF NOT EXISTS ksp_raw_transaction_archive_payloads"));
assert!(index.contains("CREATE INDEX IF NOT EXISTS ix_ksp_raw_transactions_slot_signature"));
for forbidden in ["impl ksp_store_api::RawTransaction", "repository", "sqlx", "RawAccountState"] {
for forbidden in ["impl ksp_store_api::RawTransaction", "impl ksp_store_api::RawAccount", "repository", "sqlx"] {
assert!(!migration.contains(forbidden), "repository/cross-scope implementation leaked into migration engine: {forbidden}");
assert!(!schema.contains(forbidden), "repository/cross-scope implementation leaked into schema contract: {forbidden}");
}
@@ -124,6 +125,25 @@ fn pre_003_fix_001_migration_engine_uses_split_schema_contract_and_binds_network
return;
}
#[test]
fn pre_002_v002_is_migration_only_and_does_not_open_account_repository_or_runtime_dispatch() {
let crate_root = include_str!("../src/lib.rs");
let migration = include_str!("../src/migration.rs");
let schema = include_str!("../src/schema.rs");
let runtime = include_str!("../src/runtime.rs");
let states = include_str!("../migrations/v002_raw_account_state/tables/001_ksp_raw_account_states.sql");
let observations = include_str!("../migrations/v002_raw_account_state/tables/002_ksp_raw_account_observations.sql");
assert!(migration.contains("name: \"raw_account_state\""));
assert!(migration.contains("resources: crate::V002_RESOURCES"));
assert!(schema.contains("pub(crate) const V002_RESOURCES"));
assert!(states.contains("ksp_raw_account_states"));
assert!(observations.contains("ksp_raw_account_observations"));
assert!(!crate_root.contains("mod raw_account;"));
assert!(!runtime.contains("impl ksp_store_api::RawAccount"));
assert!(!std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/raw_account.rs").exists());
return;
}
#[test]
fn pre_004_raw_read_sql_and_mapping_remain_backend_private() {
let crate_root = include_str!("../src/lib.rs");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
// version: 11
// version: 12
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -279,7 +279,9 @@ fn pre_010_raw_transaction_capability_implementation_inventory_is_exact_and_raw_
assert!(!runtime.contains(forbidden), "RawAccountState scope opened during RawTransaction hardening: {forbidden}");
}
let migration = include_str!("../src/migration.rs");
assert!(!migration.contains("ksp_raw_account"));
assert!(migration.contains("raw_account_state"));
assert!(migration.contains("crate::V002_RESOURCES"));
assert!(!runtime.contains("mod raw_account"));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/postgres_foundation_live.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -153,7 +153,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
}
*owns_schema = true;
let initial_health = initial.health().await;
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(1) || initial_health.pending_migration_count() != 0 {
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(2) || initial_health.pending_migration_count() != 0 {
return std::result::Result::Err(LiveFailure::new("initial_health"));
}
let initial_close = close_backend(initial).await;
@@ -167,7 +167,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
};
let idempotent_health = idempotent.health().await;
if !idempotent_health.is_ready()
|| idempotent_health.migration_version() != std::option::Option::Some(1)
|| idempotent_health.migration_version() != std::option::Option::Some(2)
|| idempotent_health.pending_migration_count() != 0
{
return std::result::Result::Err(LiveFailure::new("idempotent_health"));
@@ -250,7 +250,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let final_health = final_backend.health().await;
if !final_health.is_ready() || final_health.migration_version() != std::option::Option::Some(1) || final_health.pending_migration_count() != 0 {
if !final_health.is_ready() || final_health.migration_version() != std::option::Option::Some(2) || final_health.pending_migration_count() != 0 {
return std::result::Result::Err(LiveFailure::new("final_health"));
}
return close_backend(final_backend).await;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/postgres_raw_transaction_live.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -150,7 +150,7 @@ async fn run_raw_transaction_scenario(admin: &mut tokio_postgres::Client, uri: &
};
*owns_schema = true;
let initial_health = initial.health().await;
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(1) || initial_health.pending_migration_count() != 0 {
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(2) || initial_health.pending_migration_count() != 0 {
return std::result::Result::Err(LiveFailure::new("initial_health"));
}
let wrong_network_result = open_backend_result(uri, "testnet", true, true).await;
@@ -211,7 +211,7 @@ async fn run_raw_transaction_scenario(admin: &mut tokio_postgres::Client, uri: &
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let reopened_health = reopened.health().await;
if !reopened_health.is_ready() || reopened_health.migration_version() != std::option::Option::Some(1) {
if !reopened_health.is_ready() || reopened_health.migration_version() != std::option::Option::Some(2) {
return std::result::Result::Err(LiveFailure::new("reopen_health"));
}
let reference_result = raw_reference(10);

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/unit_tests/migration.rs
// version: 5
// version: 6
fn applied(version: i64, name: &str, checksum: &str) -> super::AppliedMigration {
return super::AppliedMigration { checksum: checksum.to_owned(), name: name.to_owned(), version };
@@ -17,7 +17,7 @@ fn embedded(version: i64, name: &'static str, resources: &'static [crate::Schema
#[test]
fn pre_003_fix_001_embedded_registry_keeps_v000_checksum_and_uses_resource_owned_v001() {
assert_eq!(super::EMBEDDED_MIGRATIONS.len(), 2);
assert!(super::EMBEDDED_MIGRATIONS.len() >= 2);
let v000 = &super::EMBEDDED_MIGRATIONS[0];
assert_eq!(v000.version, 0);
assert_eq!(v000.name, "bootstrap");
@@ -32,7 +32,29 @@ fn pre_003_fix_001_embedded_registry_keeps_v000_checksum_and_uses_resource_owned
assert_eq!(v001.resources.len(), 40);
assert_eq!(super::migration_checksum(v001), "31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51");
assert!(super::validate_embedded_registry(super::EMBEDDED_MIGRATIONS).is_ok());
assert_eq!(crate::current_migration_version(), 1);
return;
}
#[test]
fn pre_002_v002_registry_is_additive_minimal_and_keeps_v000_v001_checksums_stable() {
assert_eq!(super::EMBEDDED_MIGRATIONS.len(), 3);
let v000 = &super::EMBEDDED_MIGRATIONS[0];
let v001 = &super::EMBEDDED_MIGRATIONS[1];
let v002 = &super::EMBEDDED_MIGRATIONS[2];
assert_eq!(super::migration_checksum(v000), "d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450");
assert_eq!(super::migration_checksum(v001), "31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51");
assert_eq!(v002.version, 2);
assert_eq!(v002.name, "raw_account_state");
assert_eq!(v002.hook, super::MigrationHook::None);
assert_eq!(v002.resources.len(), 5);
assert!(super::validate_embedded_registry(super::EMBEDDED_MIGRATIONS).is_ok());
assert_eq!(crate::current_migration_version(), 2);
let full = [
applied(0, v000.name, super::migration_checksum(v000).as_str()),
applied(1, v001.name, super::migration_checksum(v001).as_str()),
applied(2, v002.name, super::migration_checksum(v002).as_str()),
];
assert_eq!(super::validate_history(&full, super::EMBEDDED_MIGRATIONS).ok(), std::option::Option::Some(3));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/unit_tests/schema.rs
// version: 4
// version: 5
fn actual_column(name: &str, udt_name: &str, nullable: bool) -> super::ActualColumn {
return super::ActualColumn {
@@ -48,6 +48,37 @@ fn pre_003_fix_001_v001_resources_are_split_and_idempotent_by_object_family() {
return;
}
#[test]
fn pre_002_v002_resources_are_exactly_two_tables_plus_base_pk_fk_without_indexes_or_domain_checks() {
assert_eq!(crate::V002_RESOURCES.len(), 5);
let ids = crate::V002_RESOURCES.iter().map(|resource| return resource.id).collect::<std::vec::Vec<_>>();
assert_eq!(ids[..2], ["tables/001_ksp_raw_account_states.sql", "tables/002_ksp_raw_account_observations.sql"]);
assert_eq!(
ids[2..],
[
"constraints/001_pk_ksp_raw_account_states.sql",
"constraints/002_pk_ksp_raw_account_observations.sql",
"constraints/003_fk_ksp_raw_account_observations_state.sql",
]
);
let sql = crate::V002_RESOURCES.iter().map(|resource| return resource.sql).collect::<std::vec::Vec<_>>().concat();
for required in [
"CREATE TABLE IF NOT EXISTS ksp_raw_account_states",
"CREATE TABLE IF NOT EXISTS ksp_raw_account_observations",
"slot NUMERIC(20, 0) NOT NULL",
"write_version NUMERIC(20, 0) NULL",
"PRIMARY KEY (pubkey, slot, state_hash)",
"PRIMARY KEY (observation_key)",
"FOREIGN KEY (account_pubkey, account_slot, account_state_hash) REFERENCES ksp_raw_account_states(pubkey, slot, state_hash) ON DELETE RESTRICT",
] {
assert!(sql.contains(required), "V002 pre.002 physical foundation is missing: {required}");
}
for forbidden in ["CREATE INDEX", " CHECK ", "ON DELETE CASCADE", "BIGSERIAL", "slot BIGINT", "data TEXT"] {
assert!(!sql.contains(forbidden), "pre.002 advanced V002 beyond the minimal table/PK/FK scope: {forbidden}");
}
return;
}
#[test]
fn pre_003_fix_001_external_extra_columns_are_accepted_only_when_they_cannot_block_ksp_inserts() {
let nullable = actual_column("external_nullable", "text", true);

232
deltas/0.3.4/pre.002.md Normal file
View File

@@ -0,0 +1,232 @@
<!-- file: deltas/0.3.4/pre.002.md -->
<!-- version: 1 -->
# Delta `0.3.4-pre.002` — registry V002 et tables RAW account minimales
## 1. Base requise
Base directe attendue :
```text
0.3.4-pre.001-fix.001
workspace.package.version = 0.3.4-pre.1
```
Le gate opérateur fourni pour `pre.001` est propre : audits Rust/Markdown, `cargo check --workspace`, Clippy, tests ciblés Store/Config et `cargo check -p ksp-store-lib --no-default-features` passent. `pre.001-fix.001` est ensuite un correctif strictement documentaire ; ses audits statiques sont propres et il ne modifie pas Cargo/runtime.
## 2. Objectif
Matérialiser uniquement la première fondation physique V002 décidée en `pre.001` :
```text
migration logique V002 = raw_account_state
2 tables
2 primary keys
1 foreign key observation -> state
```
Cette tranche n'ouvre aucun repository account, aucun dispatch Store, aucun index métier et aucune contrainte de domaine/fixed-width complète.
## 3. Version Cargo
Cette prerelease modifie code et migration SQL. Conformément à `VER-ID-009` :
```text
workspace.package.version : 0.3.4-pre.1 -> 0.3.4-pre.2
Cargo.toml header : 364 -> 365
```
## 4. Registry V002
`EMBEDDED_MIGRATIONS` contient désormais exactement trois migrations logiques ordonnées :
```text
V000 bootstrap
V001 raw_transaction
V002 raw_account_state
```
V002 utilise `MigrationChecksum::Resources`, `MigrationHook::None` et `V002_RESOURCES`. `current_migration_version()` passe de `1` à `2`.
Les checksums acquis restent inchangés :
```text
V000 d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450
V001 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51
```
Le checksum calculé sur les cinq resources V002 de cette tranche est :
```text
30ac87496f1bb3805d816660891d7eab2127c599a636eb40c17ade5926311f55
```
Il est volontairement **provisoire de prerelease** : `pre.003` doit encore ajouter les contraintes de domaine et l'index prévus, traiter explicitement la transition depuis cet état intermédiaire puis figer le checksum V002 final avant toute preuve live account persistante.
## 5. Ressources physiques ajoutées
```text
migrations/v002_raw_account_state/
tables/
001_ksp_raw_account_states.sql
002_ksp_raw_account_observations.sql
constraints/
001_pk_ksp_raw_account_states.sql
002_pk_ksp_raw_account_observations.sql
003_fk_ksp_raw_account_observations_state.sql
```
### 5.1 State
Colonnes matérialisées exactement selon le design `pre.001` :
```text
pubkey BYTEA NOT NULL
slot NUMERIC(20,0) NOT NULL
state_hash BYTEA NOT NULL
lamports NUMERIC(20,0) NOT NULL
owner BYTEA NOT NULL
executable BOOLEAN NOT NULL
rent_epoch NUMERIC(20,0) NOT NULL
data BYTEA NOT NULL
```
PK :
```text
(pubkey, slot, state_hash)
```
### 5.2 Observation
Les champs de provenance existants sont conservés avec les optionalités account décidées, dont :
```text
is_startup BOOLEAN NULL
transaction_signature BYTEA NULL
write_version NUMERIC(20,0) NULL
```
PK : `(observation_key)`.
FK :
```text
(account_pubkey, account_slot, account_state_hash)
-> ksp_raw_account_states(pubkey, slot, state_hash)
ON DELETE RESTRICT
```
Aucune FK vers `ksp_raw_transactions` n'est ajoutée.
## 6. Scope explicitement non avancé
```text
aucun CHECK fixed-width/u64/data/provenance account
aucun index (slot,pubkey,state_hash)
aucun index owner/provider/time/status
aucun src/raw_account.rs
aucun repository/read/write account
aucune impl RawAccount* sur PostgresBackend
aucune impl RawAccount* sur Store
aucune modification ksp-store-api
aucune retention/archive/purge account
```
Les contraintes et l'index sont réservés à `pre.003` ; le mapping/read commence seulement à `pre.004`.
## 7. Tests/canaris ajustés
Les tests historiques qui interdisaient l'existence même de V002 sont recalibrés sans ouvrir les capabilities account :
- registry V000/V001/V002 exact et `current_migration_version = 2` ;
- inventaire V002 exact `2 tables + 3 contraintes PK/FK` ;
- absence de CHECK/index dans V002 `pre.002` ;
- absence de module repository account et d'impl `RawAccount*` runtime ;
- live tests transaction/foundation existants attendent désormais migration version `2` s'ils sont exécutés.
Les canaris qui interdisent les quatre implémentations `RawAccount*` restent actifs.
## 8. Fichiers ajoutés
```text
crates/ksp-store-postgres-lib/migrations/v002_raw_account_state/tables/001_ksp_raw_account_states.sql
crates/ksp-store-postgres-lib/migrations/v002_raw_account_state/tables/002_ksp_raw_account_observations.sql
crates/ksp-store-postgres-lib/migrations/v002_raw_account_state/constraints/001_pk_ksp_raw_account_states.sql
crates/ksp-store-postgres-lib/migrations/v002_raw_account_state/constraints/002_pk_ksp_raw_account_observations.sql
crates/ksp-store-postgres-lib/migrations/v002_raw_account_state/constraints/003_fk_ksp_raw_account_observations_state.sql
deltas/0.3.4/pre.002.md
```
## 9. Fichiers modifiés
```text
Cargo.toml
crates/ksp-store-postgres-lib/src/lib.rs
crates/ksp-store-postgres-lib/src/migration.rs
crates/ksp-store-postgres-lib/src/schema.rs
crates/ksp-store-postgres-lib/unit_tests/migration.rs
crates/ksp-store-postgres-lib/unit_tests/schema.rs
crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
crates/ksp-store-postgres-lib/tests/postgres_foundation_live.rs
crates/ksp-store-postgres-lib/tests/postgres_raw_transaction_live.rs
docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md
docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md
```
## 10. Fichiers supprimés
Aucun.
## 11. Validations exécutées dans l'environnement d'assemblage
```text
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.4
Markdown table audit: clean (239 table(s), 136 file(s))
assertions structurelles locales
DIFF_SCOPE=PASS
HEADER_VERSIONS=PASS
V000_V001_IMMUTABLE=PASS
V002_MINIMAL_SCOPE=PASS
V002_CHECKSUM=30ac87496f1bb3805d816660891d7eab2127c599a636eb40c17ade5926311f55
```
Le checksum V002 intermédiaire a été recalculé indépendamment à partir du domaine `ksp-migration-resources-v1\0`, des IDs ordonnés et des bytes exacts des cinq resources.
## 12. Validations non exécutées dans l'environnement d'assemblage
L'exécutable `cargo` n'est pas disponible dans cet environnement. Après application du delta, rejouer côté opérateur :
```text
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.4
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-store-api
cargo test -p ksp-store-lib
cargo test -p ksp-store-postgres-lib
cargo test -p ksp-config-lib
cargo check -p ksp-store-lib --no-default-features
```
Aucun live PostgreSQL account n'est revendiqué dans `pre.002`.
## 13. Décisions prises
- V002 reste additive et ne modifie aucun byte V000/V001.
- Les PK/FK sont des resources séparées et inspectables par le moteur existant.
- La table state porte directement les bytes D1 RAW ; aucune représentation base64 physique n'est introduite.
- `transaction_signature` reste metadata d'observation sans FK transaction.
- La tranche ne crée pas de deuxième vérité `space`, de surrogate ID, d'archive account ni de processing ledger.
## 14. Questions ouvertes
Aucune question de contrat nouvelle. `pre.003` doit finaliser la surface physique V002 déjà décidée : contraintes de domaine/fixed-width, index de navigation, compatibilité schema et checksum final, y compris la transition depuis l'état intermédiaire de cette prerelease.

View File

@@ -1,11 +1,11 @@
<!-- file: docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Plan `0.3.4` — Store/PostgreSQL `RawAccountState` + complétude RAW
## 1. Statut du gate `pre.001`
## 1. Statut de la release
`0.3.4-pre.001` est un gate de conception. Il ne crée **aucun** SQL V002, aucune table account et aucun repository account. Il fixe la forme physique et le séquencement nécessaires avant implémentation.
`0.3.4-pre.001` a figé le design. `0.3.4-pre.002` matérialise uniquement la fondation physique V002 : registry logique, deux tables, deux clés primaires et la FK observation -> state. Aucun repository account, aucun dispatch façade, aucun index métier et aucune contrainte de domaine complète ne sont ouverts dans cette tranche.
Base canonique auditée :
@@ -569,15 +569,17 @@ Remplacement du sizing tabulaire par des sous-sections éditables avec statut ex
### `pre.002` — Registry V002 et tables minimales
**Statut : planifié.**
**Statut : réalisé ; gate Cargo opérateur post-delta à rejouer.**
Budget cible : **15-20 min**. Ajouter le registry V002 et les deux tables `RawAccountState`/observation avec PK/FK de base, sans repository account ni dispatch façade.
Budget cible : **15-20 min**. V002 `raw_account_state` est enregistrée avec exactement cinq resources : deux tables, les PK `(pubkey, slot, state_hash)` / `(observation_key)` et la FK composite observation -> state. Aucun repository account ni dispatch façade n'est ajouté.
Le checksum resources calculable sur cet état intermédiaire est `30ac87496f1bb3805d816660891d7eab2127c599a636eb40c17ade5926311f55`. Il est **provisoire de prerelease** : `pre.003` ajoute encore les contraintes de domaine et l'index prévus, puis fige le checksum V002 final avant toute preuve live account. V000/V001 restent strictement inchangées.
### `pre.003` — Contraintes, index et schema compatibility V002
**Statut : planifié.**
Budget cible : **15-20 min**. Compléter les contraintes physiques, l'index de navigation, la schema compatibility et le checksum V002, sans avancer les repositories.
Budget cible : **15-20 min**. Compléter les contraintes physiques, l'index de navigation, la schema compatibility et figer le checksum V002 final, sans avancer les repositories. Cette tranche doit traiter explicitement la transition depuis l'état V002 intermédiaire de `pre.002` avant toute utilisation live persistante.
### `pre.004` — Mapping privé et lectures `get`

View File

@@ -1,11 +1,11 @@
<!-- file: docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Validation `0.3.4` — Store/PostgreSQL `RawAccountState` + complétude RAW
## 1. Portée
Cette matrice est ouverte par `0.3.4-pre.001`. La tranche courante valide **le design et le sizing uniquement** ; elle n'affirme aucune présence de V002 ou d'implémentation account.
Cette matrice est ouverte par `0.3.4-pre.001`. La tranche courante `0.3.4-pre.002` matérialise uniquement la fondation V002 : registry, deux tables et PK/FK de base. Les contraintes de domaine, l'index, la compatibilité externe complète, les repositories et les quatre capabilities account restent volontairement pending.
Base :
@@ -133,18 +133,22 @@ observation_key family-local unique
## 7. Migration/schema V002
| Objet | Décision pre.001 | État |
|----------------------------------|---------------------------------------|-------------------------|
| V000 | immuable | PASS design |
| V001 | immuable | PASS design |
| V002 logical name | raw_account_state | PASS design |
| table state | ksp_raw_account_states | PASS design |
| table observation | ksp_raw_account_observations | PASS design |
| Objet | Décision / état physique | État |
|----------------------------------|--------------------------------------------------------------------------|---------------------|
| V000 | bytes/checksum inchangés | PASS |
| V001 | 40 resources + checksum inchangés | PASS |
| V002 logical name | `raw_account_state` | PASS pre.002 |
| table state | `ksp_raw_account_states` | PASS pre.002 |
| table observation | `ksp_raw_account_observations` | PASS pre.002 |
| PK state | `(pubkey,slot,state_hash)` | PASS pre.002 |
| PK observation | `(observation_key)` | PASS pre.002 |
| FK observation -> state | `(account_pubkey,account_slot,account_state_hash)` -> state composite PK | PASS pre.002 |
| contraintes domaine/fixed-width | non encore ajoutées | PENDING pre.003 |
| index métier | `(slot,pubkey,state_hash)` non encore ajouté | PENDING pre.003 |
| index owner/provider/time/status | aucun prévu | PASS negative scope |
| archive/tombstone account | aucune | PASS negative scope |
| index métier | (slot,pubkey,state_hash) | PASS design |
| index owner/provider/time/status | aucun | PASS negative scope |
| SQL/resource files | pas encore créés | PENDING pre.002/pre.003 |
| V002 checksum | inconnu tant que resources non créées | PENDING pre.003 |
| V002 checksum intermédiaire | `30ac8749…26311f55` sur les 5 resources pre.002 | PROVISIONAL |
| V002 checksum final | après contraintes/index de pre.003 | PENDING pre.003 |
## 8. Idempotence et concurrence
@@ -267,8 +271,8 @@ Le test live account devra être opt-in/ignored, URI stdin, sans environnement n
| Tranche | Objet | État |
|---------|-------------------------------------------------------------------|---------|
| pre.001 | audit, kbot3, threat model, V002 design, sizing, plan/validation | CURRENT |
| pre.002 | V002 registry + deux tables + PK/FK de base, sans repository | PLANNED |
| pre.001 | audit, kbot3, threat model, V002 design, sizing, plan/validation | DONE |
| pre.002 | V002 registry + deux tables + PK/FK de base, sans repository | CURRENT |
| pre.003 | contraintes complètes, index, schema compatibility, checksum V002 | PLANNED |
| pre.004 | mapping privé state/observation + get reads + hostile rows | PLANNED |
| pre.005 | acquisition atomique state+observation + idempotence/conflict | PLANNED |
@@ -318,10 +322,10 @@ cargo tree --duplicates
Les preuves live restent séparées et explicitement `--ignored --nocapture`.
## 17. Verdict `pre.001`
## 17. Verdict `pre.002`
Design : **PASS**. Les audits Rust/Markdown de l'overlay sont propres.
Fondation V002 minimale : **PASS statique**. Le registry logique V002 expose exactement cinq resources et aucune surface repository/runtime account. Les audits Rust/Markdown applicables sont rejoués dans l'environnement d'assemblage.
Compilation/Clippy/tests post-overlay : **NON EXÉCUTÉS dans le conteneur d'assemblage**, faute d'exécutable `cargo`; ils restent à rejouer par l'opérateur.
Compilation/Clippy/tests Cargo post-delta : **NON EXÉCUTÉS dans le conteneur d'assemblage**, faute d'exécutable `cargo`; ils restent à rejouer par l'opérateur.
Implémentation V002/account : **PENDING**, volontairement non ouverte dans cette tranche.
Contraintes complètes/index/schema compatibility/checksum final : **PENDING `pre.003`**. Repository et capabilities account : **PENDING `pre.004+`**.