v0.4.8-pre.007

This commit is contained in:
2026-08-06 00:52:08 +02:00
parent 80c439c988
commit 44088c71c9
122 changed files with 8903 additions and 3914 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
// version: 31
// version: 32
//! Exact contextual dispatch for Metaplex Token Metadata.
@@ -144,9 +144,6 @@ impl crate::DcApiInstructionDecoder for crate::DcMetadataMetaplexTokenMetadataDe
mod tests {
use base64::Engine; // rust-rules: trait-import
const AUDIT_MATRIX_JSON: &str = include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
);
const MAX_SDK_INSTRUCTION_MODULES: usize = 128;
const MIN_SDK_INSTRUCTION_MODULES: usize = 90;
const MIN_PDA_FAMILIES: usize = 8;
@@ -2023,7 +2020,9 @@ mod tests {
#[test]
fn audit_matrix_remains_machine_readable_and_bounded() {
let parsed = serde_json::from_str::<serde_json::Value>(AUDIT_MATRIX_JSON);
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
));
assert!(parsed.is_ok());
let matrix = match parsed {
std::result::Result::Ok(value) => value,
@@ -3053,7 +3052,9 @@ mod tests {
#[test]
fn pre_002_contract_closes_all_instruction_statuses_and_idl_accounts() {
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
};
@@ -3093,7 +3094,9 @@ mod tests {
#[test]
fn pre_002_fact_ownership_is_unique_and_keeps_metadata_domains_separate() {
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
};

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/account.rs
// version: 2
// version: 3
//! Bounded account decoding for the Solana Program Metadata program.
@@ -61,66 +61,61 @@ pub enum DcMetadataSpmAccountDecodeError {
impl std::fmt::Display for DcMetadataSpmAccountDecodeError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
return match self {
Self::InvalidAccountKey => {
return formatter.write_str("program metadata account key is invalid");
},
Self::InvalidOwner => {
return formatter.write_str("program metadata account owner is invalid");
formatter.write_str("program metadata account key is invalid")
},
Self::InvalidOwner => formatter.write_str("program metadata account owner is invalid"),
Self::ForeignOwner => {
return formatter.write_str("program metadata account is owned by another program");
},
Self::EmptyAccountData => {
return formatter.write_str("program metadata account data is empty");
formatter.write_str("program metadata account is owned by another program")
},
Self::EmptyAccountData => formatter.write_str("program metadata account data is empty"),
Self::UninitializedAccount => {
return formatter.write_str("program metadata account is uninitialized");
formatter.write_str("program metadata account is uninitialized")
},
Self::AccountDataTooLarge { actual_bytes, max_bytes } => {
return write!(
write!(
formatter,
"program metadata account data length {actual_bytes} exceeds {max_bytes} bytes",
);
)
},
Self::TruncatedHeader { actual_bytes, minimum_bytes } => {
return write!(
write!(
formatter,
"program metadata account header length {actual_bytes} is below {minimum_bytes} bytes",
);
)
},
Self::UnexpectedAccountDiscriminator { expected, actual } => {
return write!(
write!(
formatter,
"program metadata account discriminator {actual:?} does not match {expected:?}",
);
)
},
Self::LogicalDataOutOfBounds { declared_bytes, available_bytes } => {
return write!(
write!(
formatter,
"program metadata logical data length {declared_bytes} exceeds {available_bytes} allocated bytes",
);
)
},
Self::LogicalDataLengthUnsupported { declared_bytes } => {
return write!(
write!(
formatter,
"program metadata logical data length {declared_bytes} cannot be represented on this architecture",
);
)
},
Self::MissingNonCanonicalAuthority => {
return formatter
.write_str("non-canonical program metadata account has no authority");
formatter.write_str("non-canonical program metadata account has no authority")
},
Self::InvalidMetadataPda => {
return formatter.write_str("program metadata account PDA is invalid");
formatter.write_str("program metadata account PDA is invalid")
},
Self::InvalidUrlUtf8 => {
return formatter.write_str("program metadata URL data is not valid UTF-8");
formatter.write_str("program metadata URL data is not valid UTF-8")
},
Self::Model(error) => {
return write!(formatter, "program metadata model conversion failed: {error}");
write!(formatter, "program metadata model conversion failed: {error}")
},
}
};
}
}
@@ -207,7 +202,7 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
return std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error));
},
};
match discriminator {
return match discriminator {
crate::DcMetadataSpmAccountDiscriminator::Buffer => {
let snapshot =
match crate::decoder_metadata_solana_program_metadata_decode_buffer_account(
@@ -216,7 +211,7 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Buffer(snapshot));
std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Buffer(snapshot))
},
crate::DcMetadataSpmAccountDiscriminator::Metadata => {
let snapshot =
@@ -226,16 +221,12 @@ pub fn decoder_metadata_solana_program_metadata_decode_account(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Metadata(
snapshot,
));
std::result::Result::Ok(crate::DcMetadataSpmAccountSnapshot::Metadata(snapshot))
},
crate::DcMetadataSpmAccountDiscriminator::Empty => {
return std::result::Result::Err(
crate::DcMetadataSpmAccountDecodeError::UninitializedAccount,
);
std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::UninitializedAccount)
},
}
};
}
/// Decodes one Solana Program Metadata Buffer account without claiming a stable PDA after authority changes.
@@ -530,12 +521,12 @@ fn decoder_metadata_solana_program_metadata_decode_typed_data(
crate::DcMetadataSpmData::External(external)
},
};
match model.validate() {
std::result::Result::Ok(()) => return std::result::Result::Ok(model),
return match model.validate() {
std::result::Result::Ok(()) => std::result::Result::Ok(model),
std::result::Result::Err(error) => {
return std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error));
std::result::Result::Err(crate::DcMetadataSpmAccountDecodeError::Model(error))
},
}
};
}
fn decoder_metadata_solana_program_metadata_validate_metadata_pda(
@@ -576,12 +567,12 @@ fn decoder_metadata_solana_program_metadata_validate_metadata_pda(
#[cfg(test)]
mod tests {
fn metadata_program_id() -> std::option::Option<solana_pubkey::Pubkey> {
match <solana_pubkey::Pubkey as std::str::FromStr>::from_str(
return match <solana_pubkey::Pubkey as std::str::FromStr>::from_str(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
) {
std::result::Result::Ok(value) => return std::option::Option::Some(value),
std::result::Result::Err(_) => return std::option::Option::None,
}
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[derive(Clone, Copy)]

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/instruction.rs
// version: 3
// version: 4
//! Exact contextual instruction decoding for Solana Program Metadata.
@@ -890,10 +890,6 @@ fn failed(
mod tests {
use base64::Engine; // rust-rules: trait-import
const MATRIX_JSON: &str = include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json"
);
fn input(
payload: &[u8],
accounts: &[(&str, bool, bool)],
@@ -1262,7 +1258,9 @@ mod tests {
#[test]
fn instruction_matrix_is_machine_readable_and_matches_compiled_inventory() {
let matrix: serde_json::Value = match serde_json::from_str(MATRIX_JSON) {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("instruction matrix must parse: {error}"),
};

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/solana_program_metadata/model.rs
// version: 2
// version: 3
//! Public bounded models for the Solana Program Metadata wire contract.
@@ -24,16 +24,14 @@ impl DcMetadataSpmAccountDiscriminator {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::Empty),
1 => return std::result::Result::Ok(Self::Buffer),
2 => return std::result::Result::Ok(Self::Metadata),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::Empty),
1 => std::result::Result::Ok(Self::Buffer),
2 => std::result::Result::Ok(Self::Metadata),
_ => std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownAccountDiscriminator { value },
),
};
}
}
@@ -60,17 +58,15 @@ impl DcMetadataSpmEncoding {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::None),
1 => return std::result::Result::Ok(Self::Utf8),
2 => return std::result::Result::Ok(Self::Base58),
3 => return std::result::Result::Ok(Self::Base64),
return match value {
0 => std::result::Result::Ok(Self::None),
1 => std::result::Result::Ok(Self::Utf8),
2 => std::result::Result::Ok(Self::Base58),
3 => std::result::Result::Ok(Self::Base64),
_ => {
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding {
value,
});
std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownEncoding { value })
},
}
};
}
}
@@ -95,16 +91,14 @@ impl DcMetadataSpmCompression {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::None),
1 => return std::result::Result::Ok(Self::Gzip),
2 => return std::result::Result::Ok(Self::Zlib),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownCompression { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::None),
1 => std::result::Result::Ok(Self::Gzip),
2 => std::result::Result::Ok(Self::Zlib),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownCompression {
value,
}),
};
}
}
@@ -131,17 +125,13 @@ impl DcMetadataSpmFormat {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::None),
1 => return std::result::Result::Ok(Self::Json),
2 => return std::result::Result::Ok(Self::Yaml),
3 => return std::result::Result::Ok(Self::Toml),
_ => {
return std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat {
value,
});
},
}
return match value {
0 => std::result::Result::Ok(Self::None),
1 => std::result::Result::Ok(Self::Json),
2 => std::result::Result::Ok(Self::Yaml),
3 => std::result::Result::Ok(Self::Toml),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownFormat { value }),
};
}
}
@@ -166,16 +156,14 @@ impl DcMetadataSpmDataSource {
/// Resolves an exact wire discriminant.
pub fn from_wire_value(value: u8) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::Direct),
1 => return std::result::Result::Ok(Self::Url),
2 => return std::result::Result::Ok(Self::External),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownDataSource { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::Direct),
1 => std::result::Result::Ok(Self::Url),
2 => std::result::Result::Ok(Self::External),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownDataSource {
value,
}),
};
}
}
@@ -204,18 +192,16 @@ impl DcMetadataSpmProgramError {
/// Resolves an exact custom program error code.
pub fn from_code(value: u32) -> std::result::Result<Self, crate::DcMetadataSpmModelError> {
match value {
0 => return std::result::Result::Ok(Self::NotExecutableAccount),
1 => return std::result::Result::Ok(Self::InvalidProgramState),
2 => return std::result::Result::Ok(Self::InvalidProgramDataAccount),
3 => return std::result::Result::Ok(Self::ImmutableMetadataAccount),
4 => return std::result::Result::Ok(Self::InvalidDataLength),
_ => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::UnknownProgramError { value },
);
},
}
return match value {
0 => std::result::Result::Ok(Self::NotExecutableAccount),
1 => std::result::Result::Ok(Self::InvalidProgramState),
2 => std::result::Result::Ok(Self::InvalidProgramDataAccount),
3 => std::result::Result::Ok(Self::ImmutableMetadataAccount),
4 => std::result::Result::Ok(Self::InvalidDataLength),
_ => std::result::Result::Err(crate::DcMetadataSpmModelError::UnknownProgramError {
value,
}),
};
}
}
@@ -272,12 +258,12 @@ impl DcMetadataSpmSeed {
while length > 0 && self.0[length - 1] == 0 {
length -= 1;
}
match std::str::from_utf8(&self.0[..length]) {
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
return match std::str::from_utf8(&self.0[..length]) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(_) => {
return std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedUtf8);
std::result::Result::Err(crate::DcMetadataSpmModelError::InvalidSeedUtf8)
},
}
};
}
}
@@ -361,11 +347,11 @@ pub enum DcMetadataSpmData {
impl DcMetadataSpmData {
/// Returns the source discriminator associated with this content.
pub const fn source(&self) -> crate::DcMetadataSpmDataSource {
match self {
Self::Direct(_) => return crate::DcMetadataSpmDataSource::Direct,
Self::Url(_) => return crate::DcMetadataSpmDataSource::Url,
Self::External(_) => return crate::DcMetadataSpmDataSource::External,
}
return match self {
Self::Direct(_) => crate::DcMetadataSpmDataSource::Direct,
Self::Url(_) => crate::DcMetadataSpmDataSource::Url,
Self::External(_) => crate::DcMetadataSpmDataSource::External,
};
}
/// Validates source-specific length requirements and the `u32` header bound.
@@ -389,11 +375,11 @@ impl DcMetadataSpmData {
/// Returns the exact number of bytes represented on wire.
pub fn wire_byte_len(&self) -> usize {
match self {
Self::Direct(value) => return value.len(),
Self::Url(value) => return value.len(),
Self::External(_) => return crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
}
return match self {
Self::Direct(value) => value.len(),
Self::Url(value) => value.len(),
Self::External(_) => crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
};
}
/// Returns the exact little-endian header length after validation.
@@ -403,14 +389,14 @@ impl DcMetadataSpmData {
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
let actual_bytes = self.wire_byte_len();
match u32::try_from(actual_bytes) {
std::result::Result::Ok(value) => return std::result::Result::Ok(value),
return match u32::try_from(actual_bytes) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(_) => {
return std::result::Result::Err(
crate::DcMetadataSpmModelError::DataLengthExceedsU32 { actual_bytes },
);
std::result::Result::Err(crate::DcMetadataSpmModelError::DataLengthExceedsU32 {
actual_bytes,
})
},
}
};
}
/// Serializes the exact content bytes without fetching URL or external-account data.
@@ -421,15 +407,11 @@ impl DcMetadataSpmData {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
match self {
Self::Direct(value) => return std::result::Result::Ok(value.clone()),
Self::Url(value) => {
return std::result::Result::Ok(value.as_bytes().to_vec());
},
Self::External(value) => {
return std::result::Result::Ok(value.to_wire_bytes().to_vec());
},
}
return match self {
Self::Direct(value) => std::result::Result::Ok(value.clone()),
Self::Url(value) => std::result::Result::Ok(value.as_bytes().to_vec()),
Self::External(value) => std::result::Result::Ok(value.to_wire_bytes().to_vec()),
};
}
}
@@ -503,56 +485,50 @@ pub enum DcMetadataSpmModelError {
impl std::fmt::Display for DcMetadataSpmModelError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
return match self {
Self::UnknownAccountDiscriminator { value } => {
return write!(formatter, "unknown account discriminator {value}");
write!(formatter, "unknown account discriminator {value}")
},
Self::UnknownEncoding { value } => {
return write!(formatter, "unknown encoding discriminator {value}");
write!(formatter, "unknown encoding discriminator {value}")
},
Self::UnknownCompression { value } => {
return write!(formatter, "unknown compression discriminator {value}");
write!(formatter, "unknown compression discriminator {value}")
},
Self::UnknownFormat { value } => {
return write!(formatter, "unknown format discriminator {value}");
write!(formatter, "unknown format discriminator {value}")
},
Self::UnknownDataSource { value } => {
return write!(formatter, "unknown data-source discriminator {value}");
write!(formatter, "unknown data-source discriminator {value}")
},
Self::UnknownProgramError { value } => {
return write!(formatter, "unknown program metadata error code {value}");
write!(formatter, "unknown program metadata error code {value}")
},
Self::SeedUtf8TooLong { actual_bytes, max_bytes } => {
return write!(
formatter,
"seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",
);
write!(formatter, "seed UTF-8 length {actual_bytes} exceeds {max_bytes} bytes",)
},
Self::InvalidSeedLength { actual_bytes, expected_bytes } => {
return write!(
write!(
formatter,
"seed length {actual_bytes} does not equal {expected_bytes} bytes",
);
)
},
Self::InvalidSeedUtf8 => {
return formatter.write_str("seed contains invalid UTF-8 before terminal padding");
formatter.write_str("seed contains invalid UTF-8 before terminal padding")
},
Self::InvalidExternalDataLength { actual_bytes, expected_bytes } => {
return write!(
write!(
formatter,
"external data length {actual_bytes} does not equal {expected_bytes} bytes",
);
)
},
Self::EmptyData { source } => {
return write!(formatter, "metadata data source {source:?} cannot be empty");
write!(formatter, "metadata data source {source:?} cannot be empty")
},
Self::DataLengthExceedsU32 { actual_bytes } => {
return write!(
formatter,
"metadata data length {actual_bytes} exceeds the u32 wire bound",
);
write!(formatter, "metadata data length {actual_bytes} exceeds the u32 wire bound",)
},
}
};
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor.rs
// version: 14
// version: 16
//! Consolidated executor modules.
@@ -208,6 +208,30 @@ pub use self::lending::ExLendingKaminoExecutor;
pub use self::lending::ExLendingMarginfiV2Executor;
/// Exposes the reserved `lock/raydium_lp` executor.
pub use self::lock::ExLockRaydiumLpExecutor;
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::metadata::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
pub use self::metadata::EX_METADATA_SPM_CLOSE_OPERATION;
/// Solana Program Metadata operations requiring explicit destructive approval.
pub use self::metadata::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable Solana Program Metadata `Extend` operation code.
pub use self::metadata::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable Solana Program Metadata `Initialize` operation code.
pub use self::metadata::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline Solana Program Metadata content size.
pub use self::metadata::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable Solana Program Metadata `SetAuthority` operation code.
pub use self::metadata::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable Solana Program Metadata `SetData` operation code.
pub use self::metadata::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable Solana Program Metadata `SetImmutable` operation code.
pub use self::metadata::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact Solana Program Metadata operation inventory.
pub use self::metadata::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable Solana Program Metadata `Trim` operation code.
pub use self::metadata::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable Solana Program Metadata `Write` operation code.
pub use self::metadata::EX_METADATA_SPM_WRITE_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION`.
pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_BURN_OPERATION`.
@@ -284,14 +308,38 @@ pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_UTILIZE_OPERATION;
pub use self::metadata::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION;
/// Exposes the Metaplex Token Metadata executor.
pub use self::metadata::ExMetadataMetaplexTokenMetadataExecutor;
/// Reserved `kb-lib.executor.metadata.solana_program_metadata` executor.
/// Typed Solana Program Metadata executor.
pub use self::metadata::ExMetadataSolanaProgramMetadataExecutor;
/// Exposes the reserved `metadata/spl_name_service` executor.
pub use self::metadata::ExMetadataSplNameServiceExecutor;
/// Solana Program Metadata compression intent.
pub use self::metadata::ExMetadataSpmCompression;
/// Solana Program Metadata inline data intent.
pub use self::metadata::ExMetadataSpmDataInput;
/// Solana Program Metadata data-source intent.
pub use self::metadata::ExMetadataSpmDataSource;
/// Solana Program Metadata encoding intent.
pub use self::metadata::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::metadata::ExMetadataSpmExecutionIntent;
/// Solana Program Metadata format intent.
pub use self::metadata::ExMetadataSpmFormat;
/// One Solana Program Metadata operation.
pub use self::metadata::ExMetadataSpmOperation;
/// Optional Solana Program Metadata program context.
pub use self::metadata::ExMetadataSpmProgramContext;
/// Solana Program Metadata `SetData` source intent.
pub use self::metadata::ExMetadataSpmSetDataSource;
/// Solana Program Metadata `Write` source intent.
pub use self::metadata::ExMetadataSpmWriteSource;
/// Exposes the complete Metaplex Token Metadata execution intent.
pub use self::metadata::ExMetaplexTokenMetadataExecutionIntent;
/// Exposes one current Metaplex Token Metadata operation.
pub use self::metadata::ExMetaplexTokenMetadataOperation;
/// Derives the canonical Solana Program Metadata PDA.
pub use self::metadata::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Solana Program Metadata PDA.
pub use self::metadata::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Exposes the reserved `nft/metaplex_bubblegum` executor.
pub use self::nft::ExNftMetaplexBubblegumExecutor;
/// Reserved `kb-lib.executor.nft.tensor_cnft` executor.
@@ -993,6 +1041,10 @@ pub use self::wallet::ExWalletJupiterApeproSmartWalletExecutor;
/// Exposes the reserved `weighted/swap_stabble` executor.
pub use self::weighted::ExWeightedSwapStabbleExecutor;
/// Stable Solana Program Metadata executor name.
pub(crate) use self::metadata::EX_METADATA_SPM_EXECUTOR_NAME;
/// Internal Solana Program Metadata prepared-plan builder.
pub(crate) use self::metadata::executor_metadata_solana_program_metadata_build_prepared_plan;
/// Internal Metaplex Token Metadata prepared-plan builder.
pub(crate) use self::metadata::executor_metaplex_token_metadata_build_prepared_plan;
/// Stable operation code for the nonce advance injected by durable transaction assembly.
@@ -1078,11 +1130,10 @@ pub(crate) use self::spl::executor_spl_token_build_prepared_plan;
mod reserved_executor_tests {
#[test]
fn every_reserved_executor_is_registered_and_builds_only_an_empty_plan() {
let executors: [&dyn crate::ExApiInstructionExecutor; 104] = [
let executors: [&dyn crate::ExApiInstructionExecutor; 103] = [
&crate::ExAdapterSaberDecimalWrapperExecutor,
&crate::ExAdapterSplTokenWrapExecutor,
&crate::ExGovernanceSquadsMultisigExecutor,
&crate::ExMetadataSolanaProgramMetadataExecutor,
&crate::ExNftTensorCnftExecutor,
&crate::ExPerpetualsPhoenixEternalExecutor,
&crate::ExStakingJitoTipDistributionExecutor,
@@ -1184,7 +1235,7 @@ mod reserved_executor_tests {
&crate::ExWalletJupiterApeproSmartWalletExecutor,
&crate::ExWeightedSwapStabbleExecutor,
];
assert_eq!(executors.len(), 104);
assert_eq!(executors.len(), 103);
for executor in executors {
assert!(!executor.program_ids().is_empty());
for program_id in executor.program_ids() {

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/metadata.rs
// version: 8
// version: 9
//! `metadata` executor family.
@@ -87,9 +87,61 @@ pub use self::metaplex_token_metadata::ExMetadataMetaplexTokenMetadataExecutor;
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataExecutionIntent;
/// Exposes the Metaplex Token Metadata operation intent.
pub use self::metaplex_token_metadata::ExMetaplexTokenMetadataOperation;
/// Exposes the reserved `metadata/solana_program_metadata` executor.
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_CLOSE_OPERATION;
/// Solana Program Metadata operations requiring explicit destructive approval.
pub use self::solana_program_metadata::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable Solana Program Metadata `Extend` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable Solana Program Metadata `Initialize` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline Solana Program Metadata content size.
pub use self::solana_program_metadata::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable Solana Program Metadata `SetAuthority` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable Solana Program Metadata `SetData` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable Solana Program Metadata `SetImmutable` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact Solana Program Metadata operation inventory.
pub use self::solana_program_metadata::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable Solana Program Metadata `Trim` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable Solana Program Metadata `Write` operation code.
pub use self::solana_program_metadata::EX_METADATA_SPM_WRITE_OPERATION;
/// Typed Solana Program Metadata executor.
pub use self::solana_program_metadata::ExMetadataSolanaProgramMetadataExecutor;
/// Solana Program Metadata compression intent.
pub use self::solana_program_metadata::ExMetadataSpmCompression;
/// Solana Program Metadata inline data intent.
pub use self::solana_program_metadata::ExMetadataSpmDataInput;
/// Solana Program Metadata data-source intent.
pub use self::solana_program_metadata::ExMetadataSpmDataSource;
/// Solana Program Metadata encoding intent.
pub use self::solana_program_metadata::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::solana_program_metadata::ExMetadataSpmExecutionIntent;
/// Solana Program Metadata format intent.
pub use self::solana_program_metadata::ExMetadataSpmFormat;
/// One Solana Program Metadata operation.
pub use self::solana_program_metadata::ExMetadataSpmOperation;
/// Optional Solana Program Metadata program context.
pub use self::solana_program_metadata::ExMetadataSpmProgramContext;
/// Solana Program Metadata `SetData` source intent.
pub use self::solana_program_metadata::ExMetadataSpmSetDataSource;
/// Solana Program Metadata `Write` source intent.
pub use self::solana_program_metadata::ExMetadataSpmWriteSource;
/// Derives the canonical Solana Program Metadata PDA.
pub use self::solana_program_metadata::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Solana Program Metadata PDA.
pub use self::solana_program_metadata::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Exposes the reserved `metadata/spl_name_service` executor.
pub use self::spl_name_service::ExMetadataSplNameServiceExecutor;
pub(crate) use self::metaplex_token_metadata::executor_metaplex_token_metadata_build_prepared_plan;
/// Stable Solana Program Metadata executor name.
pub(crate) use self::solana_program_metadata::EX_METADATA_SPM_EXECUTOR_NAME;
/// Internal Solana Program Metadata prepared-plan builder.
pub(crate) use self::solana_program_metadata::executor_metadata_solana_program_metadata_build_prepared_plan;

View File

@@ -1,49 +1,65 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata.rs
// version: 1
// version: 2
//! Reserved executor for `metadata_solana_program_metadata`.
//! Typed executor for Solana Program Metadata.
/// Reserved executor for the `metadata_solana_program_metadata` program surface.
#[derive(Clone, Debug, Default)]
pub struct ExMetadataSolanaProgramMetadataExecutor;
mod builder;
mod constants;
mod executor;
mod intent;
impl crate::ExApiInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
fn executor_name(&self) -> &'static str {
return "kb-lib.executor.metadata.solana_program_metadata";
}
/// Derives the canonical Metadata PDA for one program and seed.
pub use self::builder::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Metadata PDA for one program, authority and seed.
pub use self::builder::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Stable operation code for `Allocate`.
pub use self::constants::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable operation code for `Close`.
pub use self::constants::EX_METADATA_SPM_CLOSE_OPERATION;
/// Operations requiring explicit destructive-operation approval.
pub use self::constants::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable operation code for `Extend`.
pub use self::constants::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable operation code for `Initialize`.
pub use self::constants::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline content accepted by Solana Program Metadata builders.
pub use self::constants::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable operation code for `SetAuthority`.
pub use self::constants::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable operation code for `SetData`.
pub use self::constants::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable operation code for `SetImmutable`.
pub use self::constants::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact current operation inventory implemented by the executor.
pub use self::constants::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable operation code for `Trim`.
pub use self::constants::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable operation code for `Write`.
pub use self::constants::EX_METADATA_SPM_WRITE_OPERATION;
/// Typed Solana Program Metadata executor.
pub use self::executor::ExMetadataSolanaProgramMetadataExecutor;
/// Compression written by execution intents.
pub use self::intent::ExMetadataSpmCompression;
/// Exact inline content accepted by metadata instructions.
pub use self::intent::ExMetadataSpmDataInput;
/// On-chain data-source discriminator.
pub use self::intent::ExMetadataSpmDataSource;
/// Encoding written by execution intents.
pub use self::intent::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::intent::ExMetadataSpmExecutionIntent;
/// Structured content format written by execution intents.
pub use self::intent::ExMetadataSpmFormat;
/// One exact Solana Program Metadata operation.
pub use self::intent::ExMetadataSpmOperation;
/// Optional program-upgrade-authority validation context.
pub use self::intent::ExMetadataSpmProgramContext;
/// Source used by `SetData`.
pub use self::intent::ExMetadataSpmSetDataSource;
/// Source used by `Write`.
pub use self::intent::ExMetadataSpmWriteSource;
fn executor_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return &[kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID];
}
fn supports_request(
&self,
request: &crate::ExApiExecutionRequest,
) -> crate::ExApiExecutionSupport {
if crate::ExApiInstructionExecutor::handles_program_id(self, &request.program_id) {
return crate::ExApiExecutionSupport::Maybe;
}
return crate::ExApiExecutionSupport::No;
}
fn build_plan(
&self,
_request: &crate::ExApiExecutionRequest,
) -> kb_core::Result<crate::ExApiExecutionPlan> {
let payload_json = match crate::executor_api_serialize_payload_json(
&serde_json::json!({"status":"reserved_executor","surface":"metadata_solana_program_metadata"}),
) {
std::result::Result::Ok(serialized) => serialized,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::ExApiExecutionPlan {
executor_name: "kb-lib.executor.metadata.solana_program_metadata".to_string(),
instruction_count: 0,
payload_json,
});
}
}
/// Crate-root access to the exact prepared-plan builder.
pub(crate) use self::builder::executor_metadata_solana_program_metadata_build_prepared_plan;
/// Stable executor name.
pub(crate) use self::constants::EX_METADATA_SPM_EXECUTOR_NAME;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata/constants.rs
// version: 1
//! Stable Solana Program Metadata executor constants.
/// Stable executor name.
pub(crate) const EX_METADATA_SPM_EXECUTOR_NAME: &str =
"kb-lib.executor.metadata.solana_program_metadata";
/// Conservative maximum inline content accepted by one instruction builder.
///
/// Larger payloads must use a pre-funded Buffer account so the final transaction remains within
/// the Solana packet boundary after signatures, account keys and message framing are included.
pub const EX_METADATA_SPM_MAX_INLINE_DATA_BYTES: usize = 1_024;
/// Stable operation code for `Write`.
pub const EX_METADATA_SPM_WRITE_OPERATION: &str = "metadata.solana_program_metadata.write";
/// Stable operation code for `Initialize`.
pub const EX_METADATA_SPM_INITIALIZE_OPERATION: &str =
"metadata.solana_program_metadata.initialize";
/// Stable operation code for `SetAuthority`.
pub const EX_METADATA_SPM_SET_AUTHORITY_OPERATION: &str =
"metadata.solana_program_metadata.set_authority";
/// Stable operation code for `SetData`.
pub const EX_METADATA_SPM_SET_DATA_OPERATION: &str = "metadata.solana_program_metadata.set_data";
/// Stable operation code for `SetImmutable`.
pub const EX_METADATA_SPM_SET_IMMUTABLE_OPERATION: &str =
"metadata.solana_program_metadata.set_immutable";
/// Stable operation code for `Trim`.
pub const EX_METADATA_SPM_TRIM_OPERATION: &str = "metadata.solana_program_metadata.trim";
/// Stable operation code for `Close`.
pub const EX_METADATA_SPM_CLOSE_OPERATION: &str = "metadata.solana_program_metadata.close";
/// Stable operation code for `Allocate`.
pub const EX_METADATA_SPM_ALLOCATE_OPERATION: &str = "metadata.solana_program_metadata.allocate";
/// Stable operation code for `Extend`.
pub const EX_METADATA_SPM_EXTEND_OPERATION: &str = "metadata.solana_program_metadata.extend";
/// Exact current operation inventory implemented by the executor.
pub const EX_METADATA_SPM_SUPPORTED_OPERATION_CODES: &[&str] = &[
crate::EX_METADATA_SPM_WRITE_OPERATION,
crate::EX_METADATA_SPM_INITIALIZE_OPERATION,
crate::EX_METADATA_SPM_SET_AUTHORITY_OPERATION,
crate::EX_METADATA_SPM_SET_DATA_OPERATION,
crate::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION,
crate::EX_METADATA_SPM_TRIM_OPERATION,
crate::EX_METADATA_SPM_CLOSE_OPERATION,
crate::EX_METADATA_SPM_ALLOCATE_OPERATION,
crate::EX_METADATA_SPM_EXTEND_OPERATION,
];
/// Operations that require explicit operator approval before plan construction.
pub const EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES: &[&str] = &[
crate::EX_METADATA_SPM_WRITE_OPERATION,
crate::EX_METADATA_SPM_SET_AUTHORITY_OPERATION,
crate::EX_METADATA_SPM_SET_DATA_OPERATION,
crate::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION,
crate::EX_METADATA_SPM_TRIM_OPERATION,
crate::EX_METADATA_SPM_CLOSE_OPERATION,
];

View File

@@ -0,0 +1,224 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata/executor.rs
// version: 1
//! Exact capability dispatch for Solana Program Metadata.
/// Typed Solana Program Metadata executor.
#[derive(Clone, Debug, Default)]
pub struct ExMetadataSolanaProgramMetadataExecutor;
impl crate::ExMetadataSolanaProgramMetadataExecutor {
fn exact_capability(
&self,
program_id: &crate::MdProgramId,
operation_code: &str,
) -> crate::ExApiExecutionCapability {
if program_id.0 != kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID {
return crate::ExApiExecutionCapability::unsupported(
"execution_metadata_spm_program_not_owned",
format!("program {} is not Solana Program Metadata", program_id.0),
);
}
if crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES
.iter()
.any(|candidate| return *candidate == operation_code)
{
return crate::ExApiExecutionCapability::supported(operation_code);
}
return crate::ExApiExecutionCapability::unsupported(
"execution_metadata_spm_operation_unsupported",
format!("Solana Program Metadata operation {operation_code} is not implemented"),
);
}
}
impl crate::ExApiTypedInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
type Intent = crate::ExMetadataSpmExecutionIntent;
fn capability(
&self,
program_id: &crate::MdProgramId,
operation_code: &str,
) -> crate::ExApiExecutionCapability {
return self.exact_capability(program_id, operation_code);
}
fn build_prepared_plan(
&self,
intent: &Self::Intent,
) -> kb_core::Result<crate::ExApiPreparedExecutionPlan> {
let program_id = crate::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
);
return match self.exact_capability(&program_id, intent.operation.operation_code()) {
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {
crate::executor_metadata_solana_program_metadata_build_prepared_plan(intent)
},
crate::ExApiExecutionCapability::Unsupported { reason_code, reason } => {
std::result::Result::Err(kb_core::Error::new(reason_code, reason))
},
};
}
}
impl crate::ExApiInstructionExecutor for crate::ExMetadataSolanaProgramMetadataExecutor {
fn executor_name(&self) -> &'static str {
return crate::EX_METADATA_SPM_EXECUTOR_NAME;
}
fn executor_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn program_ids(&self) -> &'static [&'static str] {
return &[kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID];
}
fn supports_request(
&self,
request: &crate::ExApiExecutionRequest,
) -> crate::ExApiExecutionSupport {
return if self
.exact_capability(&request.program_id, &request.operation_code)
.is_supported()
{
crate::ExApiExecutionSupport::Yes
} else {
crate::ExApiExecutionSupport::No
};
}
fn build_plan(
&self,
request: &crate::ExApiExecutionRequest,
) -> kb_core::Result<crate::ExApiExecutionPlan> {
match self.exact_capability(&request.program_id, &request.operation_code) {
crate::ExApiExecutionCapability::Supported { operation_code: _ } => {},
crate::ExApiExecutionCapability::Unsupported { reason_code, reason } => {
return std::result::Result::Err(kb_core::Error::new(reason_code, reason));
},
}
let intent = match serde_json::from_str::<crate::ExMetadataSpmExecutionIntent>(
&request.payload_json,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_metadata_spm_intent_deserialize_failed",
error.to_string(),
));
},
};
if intent.operation.operation_code() != request.operation_code {
return std::result::Result::Err(kb_core::Error::new(
"execution_operation_code_mismatch",
"request and typed Solana Program Metadata intent operations differ",
));
}
let prepared =
match crate::ExApiTypedInstructionExecutor::build_prepared_plan(self, &intent) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let payload = match serde_json::to_value(&prepared) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_metadata_spm_plan_serialize_failed",
error.to_string(),
));
},
};
let payload_json = match crate::executor_api_serialize_payload_json(&payload) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::ExApiExecutionPlan {
executor_name: crate::EX_METADATA_SPM_EXECUTOR_NAME.to_string(),
instruction_count: prepared.instructions.len(),
payload_json,
});
}
}
#[cfg(test)]
mod tests {
#[test]
fn executor_matrix_matches_the_compiled_capability_inventory() {
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_EXECUTOR_MATRIX.json"
));
assert!(parsed.is_ok());
let matrix = match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let operations = match matrix.get("operations").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert_eq!(
matrix.get("maxInlineContentBytes").and_then(serde_json::Value::as_u64),
std::option::Option::Some(crate::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES as u64)
);
assert_eq!(
matrix.get("canonicitySelection").and_then(serde_json::Value::as_str),
std::option::Option::Some(
"explicit_intent_flag_not_inferred_from_program_data_presence"
)
);
assert_eq!(operations.len(), crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.len());
for (position, row) in operations.iter().enumerate() {
assert_eq!(
row.get("tag").and_then(serde_json::Value::as_u64),
std::option::Option::Some(position as u64)
);
assert_eq!(
row.get("operationCode").and_then(serde_json::Value::as_str),
std::option::Option::Some(
crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES[position]
)
);
assert_eq!(
row.get("requiresExplicitApproval").and_then(serde_json::Value::as_bool),
std::option::Option::Some(
crate::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES
.contains(&crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES[position])
)
);
assert_eq!(
row.get("deprecated").and_then(serde_json::Value::as_bool),
std::option::Option::Some(false)
);
assert_eq!(
row.get("replaced").and_then(serde_json::Value::as_bool),
std::option::Option::Some(false)
);
}
}
#[test]
fn capabilities_cover_exactly_the_nine_stable_operations() {
let executor = crate::ExMetadataSolanaProgramMetadataExecutor;
let program_id = crate::MdProgramId(
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
);
for operation in crate::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES {
let capability =
crate::ExApiTypedInstructionExecutor::capability(&executor, &program_id, operation);
assert!(capability.is_supported());
}
let unknown = crate::ExApiTypedInstructionExecutor::capability(
&executor,
&program_id,
"metadata.solana_program_metadata.unknown",
);
assert!(!unknown.is_supported());
let foreign = crate::ExApiTypedInstructionExecutor::capability(
&executor,
&crate::MdProgramId(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()),
crate::EX_METADATA_SPM_WRITE_OPERATION,
);
assert!(!foreign.is_supported());
}
}

View File

@@ -0,0 +1,471 @@
// file: kb-lib/src/executor/metadata/solana_program_metadata/intent.rs
// version: 1
//! Typed intents for the Solana Program Metadata executor.
use ts_rs::TS; // rust-rules: derive-import
/// Encoding written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmEncoding.ts"
)]
pub enum ExMetadataSpmEncoding {
/// No encoding declaration.
None = 0,
/// UTF-8 encoding.
Utf8 = 1,
/// Base58 encoding.
Base58 = 2,
/// Base64 encoding.
Base64 = 3,
}
impl crate::ExMetadataSpmEncoding {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// Compression written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmCompression.ts"
)]
pub enum ExMetadataSpmCompression {
/// No compression declaration.
None = 0,
/// Gzip compression.
Gzip = 1,
/// Zlib compression.
Zlib = 2,
}
impl crate::ExMetadataSpmCompression {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// Structured format written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmFormat.ts"
)]
pub enum ExMetadataSpmFormat {
/// No structured format declaration.
None = 0,
/// JSON format.
Json = 1,
/// YAML format.
Yaml = 2,
/// TOML format.
Toml = 3,
}
impl crate::ExMetadataSpmFormat {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// On-chain data source written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[repr(u8)]
#[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataSource.ts"
)]
pub enum ExMetadataSpmDataSource {
/// Direct on-chain bytes.
Direct = 0,
/// UTF-8 URL stored on-chain without fetching it.
Url = 1,
/// Fixed reference to another Solana account.
External = 2,
}
impl crate::ExMetadataSpmDataSource {
/// Returns the exact wire value.
pub const fn wire_value(self) -> u8 {
return self as u8;
}
}
/// Exact inline content accepted by `Initialize` and `SetData`.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataInput.ts"
)]
pub enum ExMetadataSpmDataInput {
/// Direct on-chain bytes.
Direct {
/// Exact bytes to store.
bytes: std::vec::Vec<u8>,
},
/// UTF-8 URL retained on-chain without remote resolution.
Url {
/// URL text to store.
url: std::string::String,
},
/// Fixed reference to another account's data.
External {
/// Referenced account.
address: crate::MdPubkey,
/// Byte offset inside the referenced account.
offset: u32,
/// Optional non-zero length; `None` means the remainder of the account.
length: std::option::Option<u32>,
},
}
impl crate::ExMetadataSpmDataInput {
/// Returns the exact data-source discriminator.
pub const fn data_source(&self) -> crate::ExMetadataSpmDataSource {
return match self {
Self::Direct { bytes: _ } => crate::ExMetadataSpmDataSource::Direct,
Self::Url { url: _ } => crate::ExMetadataSpmDataSource::Url,
Self::External { address: _, offset: _, length: _ } => {
crate::ExMetadataSpmDataSource::External
},
};
}
}
/// Source used by the `Write` instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmWriteSource.ts"
)]
pub enum ExMetadataSpmWriteSource {
/// Inline bytes placed after the offset argument.
Inline {
/// Exact bytes to write.
data: std::vec::Vec<u8>,
},
/// Copy all data from another Buffer account.
Buffer {
/// Source Buffer account.
source_buffer: crate::MdPubkey,
},
}
/// Source used by the `SetData` instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmSetDataSource.ts"
)]
pub enum ExMetadataSpmSetDataSource {
/// Runtime-supported three-byte variant that preserves current data bytes.
PreserveExisting,
/// Replace data with inline content.
Inline {
/// Exact inline content.
data: crate::ExMetadataSpmDataInput,
},
/// Copy data from one Buffer account.
Buffer {
/// Source Buffer account.
buffer: crate::MdPubkey,
/// Source discriminator that the resulting metadata account must declare.
data_source: crate::ExMetadataSpmDataSource,
},
}
/// Optional program-upgrade-authority validation context.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmProgramContext.ts"
)]
pub struct ExMetadataSpmProgramContext {
/// Executable program account.
pub program: crate::MdPubkey,
/// Optional upgradeable-loader ProgramData account used when the runtime must validate it.
pub program_data: std::option::Option<crate::MdPubkey>,
}
/// One exact Solana Program Metadata operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmOperation.ts"
)]
pub enum ExMetadataSpmOperation {
/// Write bytes to a Buffer account.
Write {
/// Buffer account to mutate.
buffer: crate::MdPubkey,
/// Current Buffer authority.
authority: crate::MdPubkey,
/// Byte offset inside the Buffer payload.
offset: u32,
/// Inline or Buffer source.
source: crate::ExMetadataSpmWriteSource,
},
/// Initialize one canonical or non-canonical Metadata PDA.
Initialize {
/// Metadata PDA to initialize.
metadata: crate::MdPubkey,
/// Authority signing the initialization.
authority: crate::MdPubkey,
/// Program described by this metadata account.
program: crate::MdPubkey,
/// Optional ProgramData account used for an upgradeable program.
program_data: std::option::Option<crate::MdPubkey>,
/// Whether the Metadata PDA uses the canonical `[program, seed]` derivation.
canonical: bool,
/// Exact fixed-size seed bytes.
seed: std::vec::Vec<u8>,
/// Content encoding.
encoding: crate::ExMetadataSpmEncoding,
/// Content compression.
compression: crate::ExMetadataSpmCompression,
/// Structured content format.
format: crate::ExMetadataSpmFormat,
/// Declared data source when using a preallocated Buffer.
data_source: crate::ExMetadataSpmDataSource,
/// Optional inline content; absent means the Metadata account is a preallocated Buffer.
data: std::option::Option<crate::ExMetadataSpmDataInput>,
/// Whether the instruction may invoke the System Program to allocate the PDA.
allocate_account: bool,
},
/// Change or remove one Buffer or Metadata authority.
SetAuthority {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// New authority, or `None` to remove a Metadata authority.
new_authority: std::option::Option<crate::MdPubkey>,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Change Metadata content descriptors and data.
SetData {
/// Metadata account.
metadata: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Content encoding.
encoding: crate::ExMetadataSpmEncoding,
/// Content compression.
compression: crate::ExMetadataSpmCompression,
/// Structured content format.
format: crate::ExMetadataSpmFormat,
/// Inline, Buffer or preserve-existing source.
source: crate::ExMetadataSpmSetDataSource,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Permanently make one Metadata account immutable.
SetImmutable {
/// Metadata account.
metadata: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Resize one Buffer or Metadata account to its logical minimum and refund excess lamports.
Trim {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Lamport destination.
destination: crate::MdPubkey,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Close one mutable Buffer or Metadata account.
Close {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Lamport destination.
destination: crate::MdPubkey,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
/// Allocate one pre-funded Buffer account.
Allocate {
/// Buffer account.
buffer: crate::MdPubkey,
/// Authority signing the allocation.
authority: crate::MdPubkey,
/// Optional seed for a PDA Buffer; absent selects a pre-created keypair account.
seed: std::option::Option<std::vec::Vec<u8>>,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
/// Whether a PDA Buffer uses the canonical `[program, seed]` derivation.
canonical: bool,
/// Whether the instruction may invoke the System Program to allocate the account.
allocate_account: bool,
},
/// Extend one pre-funded Buffer or Metadata account.
Extend {
/// Buffer or Metadata account.
account: crate::MdPubkey,
/// Current authority.
authority: crate::MdPubkey,
/// Number of bytes to append to account capacity.
length: u16,
/// Optional upgrade-authority validation context.
program_context: std::option::Option<crate::ExMetadataSpmProgramContext>,
},
}
impl crate::ExMetadataSpmOperation {
/// Returns the stable operation code.
pub const fn operation_code(&self) -> &'static str {
return match self {
Self::Write {
buffer: _,
authority: _,
offset: _,
source: _,
} => crate::EX_METADATA_SPM_WRITE_OPERATION,
Self::Initialize {
metadata: _,
authority: _,
program: _,
program_data: _,
canonical: _,
seed: _,
encoding: _,
compression: _,
format: _,
data_source: _,
data: _,
allocate_account: _,
} => crate::EX_METADATA_SPM_INITIALIZE_OPERATION,
Self::SetAuthority {
account: _,
authority: _,
new_authority: _,
program_context: _,
} => crate::EX_METADATA_SPM_SET_AUTHORITY_OPERATION,
Self::SetData {
metadata: _,
authority: _,
encoding: _,
compression: _,
format: _,
source: _,
program_context: _,
} => crate::EX_METADATA_SPM_SET_DATA_OPERATION,
Self::SetImmutable {
metadata: _,
authority: _,
program_context: _,
} => crate::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION,
Self::Trim {
account: _,
authority: _,
destination: _,
program_context: _,
} => crate::EX_METADATA_SPM_TRIM_OPERATION,
Self::Close {
account: _,
authority: _,
destination: _,
program_context: _,
} => crate::EX_METADATA_SPM_CLOSE_OPERATION,
Self::Allocate {
buffer: _,
authority: _,
seed: _,
program_context: _,
canonical: _,
allocate_account: _,
} => crate::EX_METADATA_SPM_ALLOCATE_OPERATION,
Self::Extend {
account: _,
authority: _,
length: _,
program_context: _,
} => crate::EX_METADATA_SPM_EXTEND_OPERATION,
};
}
/// Returns whether the operation can overwrite, destroy or irreversibly constrain state.
pub const fn requires_explicit_approval(&self) -> bool {
return matches!(
self,
Self::Write {
buffer: _,
authority: _,
offset: _,
source: _
} | Self::SetAuthority {
account: _,
authority: _,
new_authority: _,
program_context: _,
} | Self::SetData {
metadata: _,
authority: _,
encoding: _,
compression: _,
format: _,
source: _,
program_context: _,
} | Self::SetImmutable {
metadata: _,
authority: _,
program_context: _
} | Self::Trim {
account: _,
authority: _,
destination: _,
program_context: _
} | Self::Close {
account: _,
authority: _,
destination: _,
program_context: _
}
);
}
}
/// Complete Solana Program Metadata execution intent.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmExecutionIntent.ts"
)]
pub struct ExMetadataSpmExecutionIntent {
/// Caller-provided correlation identifier.
pub intent_id: std::string::String,
/// Transaction fee payer.
pub fee_payer: crate::MdPubkey,
/// Conservative common execution policy.
pub policy: crate::ExApiExecutionPolicy,
/// Explicit approval for overwrite, destructive or irreversible operations.
pub allow_destructive_operation: bool,
/// Exact operation to build.
pub operation: crate::ExMetadataSpmOperation,
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/lib.rs
// version: 33
// version: 36
//! Consolidated decoder, executor, materializer and shared model library.
#![warn(missing_docs)]
@@ -401,6 +401,30 @@ pub use self::decoder::decoder_metadata_solana_program_metadata_decode_metadata_
pub use self::decoder::decoder_spl_elgamal_registry_parse_elgamal_registry_state;
/// Parses one Token-2022 Mint, Account, or Multisig state.
pub use self::decoder::decoder_spl_token_2022_parse_token_2022_state;
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::executor::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
pub use self::executor::EX_METADATA_SPM_CLOSE_OPERATION;
/// Solana Program Metadata operations requiring explicit destructive approval.
pub use self::executor::EX_METADATA_SPM_DESTRUCTIVE_OPERATION_CODES;
/// Stable Solana Program Metadata `Extend` operation code.
pub use self::executor::EX_METADATA_SPM_EXTEND_OPERATION;
/// Stable Solana Program Metadata `Initialize` operation code.
pub use self::executor::EX_METADATA_SPM_INITIALIZE_OPERATION;
/// Conservative maximum inline Solana Program Metadata content size.
pub use self::executor::EX_METADATA_SPM_MAX_INLINE_DATA_BYTES;
/// Stable Solana Program Metadata `SetAuthority` operation code.
pub use self::executor::EX_METADATA_SPM_SET_AUTHORITY_OPERATION;
/// Stable Solana Program Metadata `SetData` operation code.
pub use self::executor::EX_METADATA_SPM_SET_DATA_OPERATION;
/// Stable Solana Program Metadata `SetImmutable` operation code.
pub use self::executor::EX_METADATA_SPM_SET_IMMUTABLE_OPERATION;
/// Exact Solana Program Metadata operation inventory.
pub use self::executor::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES;
/// Stable Solana Program Metadata `Trim` operation code.
pub use self::executor::EX_METADATA_SPM_TRIM_OPERATION;
/// Stable Solana Program Metadata `Write` operation code.
pub use self::executor::EX_METADATA_SPM_WRITE_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION`.
pub use self::executor::EX_METAPLEX_TOKEN_METADATA_APPROVE_USE_AUTHORITY_OPERATION;
/// Exposes `EX_METAPLEX_TOKEN_METADATA_BURN_OPERATION`.
@@ -1135,10 +1159,30 @@ pub use self::executor::ExLendingMarginfiV2Executor;
pub use self::executor::ExLockRaydiumLpExecutor;
/// Exposes the Metaplex Token Metadata executor.
pub use self::executor::ExMetadataMetaplexTokenMetadataExecutor;
/// Exposes the reserved `ExMetadataSolanaProgramMetadataExecutor` implementation.
/// Exposes the typed `ExMetadataSolanaProgramMetadataExecutor` implementation.
pub use self::executor::ExMetadataSolanaProgramMetadataExecutor;
/// Exposes the reserved `ExMetadataSplNameServiceExecutor` implementation.
pub use self::executor::ExMetadataSplNameServiceExecutor;
/// Solana Program Metadata compression intent.
pub use self::executor::ExMetadataSpmCompression;
/// Solana Program Metadata inline data intent.
pub use self::executor::ExMetadataSpmDataInput;
/// Solana Program Metadata data-source intent.
pub use self::executor::ExMetadataSpmDataSource;
/// Solana Program Metadata encoding intent.
pub use self::executor::ExMetadataSpmEncoding;
/// Complete Solana Program Metadata execution intent.
pub use self::executor::ExMetadataSpmExecutionIntent;
/// Solana Program Metadata format intent.
pub use self::executor::ExMetadataSpmFormat;
/// One Solana Program Metadata operation.
pub use self::executor::ExMetadataSpmOperation;
/// Optional Solana Program Metadata program context.
pub use self::executor::ExMetadataSpmProgramContext;
/// Solana Program Metadata `SetData` source intent.
pub use self::executor::ExMetadataSpmSetDataSource;
/// Solana Program Metadata `Write` source intent.
pub use self::executor::ExMetadataSpmWriteSource;
/// Exposes the complete Metaplex Token Metadata execution intent.
pub use self::executor::ExMetaplexTokenMetadataExecutionIntent;
/// Exposes one current Metaplex Token Metadata operation.
@@ -1349,6 +1393,10 @@ pub use self::executor::ExWeightedSwapStabbleExecutor;
pub use self::executor::executor_api_serialize_payload_json;
/// Exposes the pretty JSON payload serializer.
pub use self::executor::executor_api_serialize_payload_json_pretty;
/// Derives the canonical Solana Program Metadata PDA.
pub use self::executor::executor_metadata_solana_program_metadata_derive_canonical_pda;
/// Derives the non-canonical Solana Program Metadata PDA.
pub use self::executor::executor_metadata_solana_program_metadata_derive_non_canonical_pda;
/// Builds an unsigned durable nonce transaction from a prepared execution plan and validated nonce state.
pub use self::executor::executor_solana_build_durable_nonce_transaction;
/// Builds an unsigned legacy Solana transaction from a prepared execution plan.
@@ -1391,7 +1439,7 @@ pub use self::materializer::MtLifecycleMaterializer;
pub use self::materializer::MtLiquidityMaterializer;
/// Exposes the legacy common business materializer trait.
pub use self::materializer::MtMaterializer;
/// Token-2022, Metaplex and Solana Program Metadata materializer.
/// Compatibility wrapper for the Solana Program Metadata materializer.
pub use self::materializer::MtMetadataMaterializer;
/// Reserved NFT materializer.
pub use self::materializer::MtNftMaterializer;
@@ -1409,6 +1457,8 @@ pub use self::materializer::MtRewardsMaterializer;
pub use self::materializer::MtRiskMaterializer;
/// Reserved routing materializer.
pub use self::materializer::MtRoutingMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::materializer::MtSolanaProgramMetadataMaterializer;
/// Stable native stake and vote materializer.
pub use self::materializer::MtStakingMaterializer;
/// Stable Token, ATA and Token-2022 account materializer.
@@ -1972,6 +2022,8 @@ pub(crate) use self::decoder::decoder_spl_token_decode;
pub(crate) use self::decoder::decoder_spl_token_entry_for_tag;
/// Returns the first byte of one retained classic SPL Token payload.
pub(crate) use self::decoder::decoder_spl_token_payload_tag;
/// Stable Solana Program Metadata executor name.
pub(crate) use self::executor::EX_METADATA_SPM_EXECUTOR_NAME;
/// Stable operation code for the nonce advance injected by durable transaction assembly.
pub(crate) use self::executor::EX_SOLANA_TRANSACTION_DURABLE_NONCE_ADVANCE_OPERATION;
/// Maximum over-the-wire size of a Solana transaction packet.
@@ -2004,6 +2056,8 @@ pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_MEMO;
pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_TOKEN;
/// Canonical SPL Token-2022 tracing target.
pub(crate) use self::executor::TRACING_TARGET_EXECUTOR_SPL_TOKEN_2022;
/// Internal Solana Program Metadata prepared-plan builder.
pub(crate) use self::executor::executor_metadata_solana_program_metadata_build_prepared_plan;
/// Internal Metaplex Token Metadata prepared-plan builder.
pub(crate) use self::executor::executor_metaplex_token_metadata_build_prepared_plan;
/// Crate-root access to `build_prepared_plan` from `address_lookup_table`.
@@ -2052,41 +2106,315 @@ pub(crate) use self::executor::executor_spl_memo_build_prepared_plan;
pub(crate) use self::executor::executor_spl_token_2022_build_prepared_plan;
/// Crate-root access to `executor_spl_token_build_prepared_plan`.
pub(crate) use self::executor::executor_spl_token_build_prepared_plan;
/// Canonical processor name for the native and SPL administration materializer.
/// Crate-root access to `MT_ADMIN_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ADMIN_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES;
/// Crate-root access to `MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-root access to `MT_ADMIN_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ADMIN_COMPONENT_NAME;
/// Crate-root access to `MT_ADMIN_CONFIG_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_CONFIG_ENTRIES;
/// Crate-root access to `MT_ADMIN_CONFIG_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_CONFIG_SURFACE;
/// Crate-root access to `MT_ADMIN_LOADER_V4_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_LOADER_V4_ENTRIES;
/// Crate-root access to `MT_ADMIN_LOADER_V4_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_LOADER_V4_SURFACE;
/// Crate-root access to `MT_ADMIN_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ADMIN_PROCESSOR_NAME;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::materializer::MT_METADATA_SPM_ACCEPTED_FAMILIES;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::materializer::MT_METADATA_SPM_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::materializer::MT_METADATA_SPM_PROJECTION_VERSION;
/// Stable processor name for SPL Token and ATA risk facts.
/// Crate-root access to `MT_ADMIN_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_ADMIN_PROJECTION_VERSION;
/// Crate-root access to `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_2022_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_2022_SURFACE;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES;
/// Crate-root access to `MT_ADMIN_SPL_TOKEN_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SPL_TOKEN_SURFACE;
/// Crate-root access to `MT_ADMIN_SYSTEM_ENTRIES`.
pub(crate) use self::materializer::MT_ADMIN_SYSTEM_ENTRIES;
/// Crate-root access to `MT_ADMIN_SYSTEM_SURFACE`.
pub(crate) use self::materializer::MT_ADMIN_SYSTEM_SURFACE;
/// Crate-root access to `MT_BRIDGE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_BRIDGE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_BRIDGE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_BRIDGE_COMPONENT_NAME;
/// Crate-root access to `MT_BRIDGE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_BRIDGE_PROCESSOR_NAME;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-root access to `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES`.
pub(crate) use self::materializer::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Crate-root access to `MT_FEES_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_FEES_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_FEES_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_FEES_COMPONENT_NAME;
/// Crate-root access to `MT_FEES_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_FEES_PROCESSOR_NAME;
/// Crate-root access to `MT_FEES_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_FEES_PROJECTION_VERSION;
/// Crate-root access to `MT_FEES_SPL_TOKEN_2022_SURFACE`.
pub(crate) use self::materializer::MT_FEES_SPL_TOKEN_2022_SURFACE;
/// Crate-root access to `MT_FEES_TOKEN_2022_FEE_ENTRIES`.
pub(crate) use self::materializer::MT_FEES_TOKEN_2022_FEE_ENTRIES;
/// Crate-root access to `MT_GOVERNANCE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_GOVERNANCE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_GOVERNANCE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_GOVERNANCE_COMPONENT_NAME;
/// Crate-root access to `MT_GOVERNANCE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_GOVERNANCE_PROCESSOR_NAME;
/// Crate-root access to `MT_LENDING_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_LENDING_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_LENDING_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_LENDING_COMPONENT_NAME;
/// Crate-root access to `MT_LENDING_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_LENDING_PROCESSOR_NAME;
/// Crate-root access to `MT_LIFECYCLE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_BPF_LOADER_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_BPF_LOADER_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_LIFECYCLE_COMPONENT_NAME;
/// Crate-root access to `MT_LIFECYCLE_FEATURE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_FEATURE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_FEATURE_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_FEATURE_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_LOADER_V4_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_LOADER_V4_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_LOADER_V4_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_LOADER_V4_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_LIFECYCLE_PROCESSOR_NAME;
/// Crate-root access to `MT_LIFECYCLE_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_LIFECYCLE_PROJECTION_VERSION;
/// Crate-root access to `MT_LIFECYCLE_SLASHING_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_SLASHING_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_SLASHING_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_SLASHING_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_SYSTEM_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_SYSTEM_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES;
/// Crate-root access to `MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE`.
pub(crate) use self::materializer::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE;
/// Crate-root access to `MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES`.
pub(crate) use self::materializer::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES;
/// Crate-root access to `MT_LIQUIDITY_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_LIQUIDITY_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_LIQUIDITY_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_LIQUIDITY_COMPONENT_NAME;
/// Crate-root access to `MT_LIQUIDITY_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_LIQUIDITY_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Crate-root access to `MT_METADATA_TOKEN_2022_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Crate-root access to `MT_METADATA_TOKEN_2022_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Crate-root access to `MT_METADATA_TOKEN_2022_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Crate-root access to `MT_NFT_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_NFT_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_NFT_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_NFT_COMPONENT_NAME;
/// Crate-root access to `MT_NFT_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_NFT_PROCESSOR_NAME;
/// Crate-root access to `MT_ORACLE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ORACLE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ORACLE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ORACLE_COMPONENT_NAME;
/// Crate-root access to `MT_ORACLE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ORACLE_PROCESSOR_NAME;
/// Crate-root access to `MT_ORDERBOOK_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ORDERBOOK_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ORDERBOOK_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ORDERBOOK_COMPONENT_NAME;
/// Crate-root access to `MT_ORDERBOOK_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ORDERBOOK_PROCESSOR_NAME;
/// Crate-root access to `MT_PERPETUALS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_PERPETUALS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_PERPETUALS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_PERPETUALS_COMPONENT_NAME;
/// Crate-root access to `MT_PERPETUALS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_PERPETUALS_PROCESSOR_NAME;
/// Crate-root access to `MT_POOL_STATE_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_POOL_STATE_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_POOL_STATE_COMPONENT_NAME;
/// Crate-root access to `MT_POOL_STATE_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_POOL_STATE_PROCESSOR_NAME;
/// Crate-root access to `MT_REWARDS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_REWARDS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_REWARDS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_REWARDS_COMPONENT_NAME;
/// Crate-root access to `MT_REWARDS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_REWARDS_PROCESSOR_NAME;
/// Crate-root access to `MT_RISK_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_RISK_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_RISK_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_RISK_COMPONENT_NAME;
/// Crate-root access to `MT_RISK_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_RISK_PROCESSOR_NAME;
/// Stable projection contract version for SPL Token and ATA risk facts.
/// Crate-root access to `MT_RISK_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_RISK_PROJECTION_VERSION;
/// Stable processor name for Token, ATA and Token-2022 account projections.
/// Crate-root access to `MT_RISK_RISK_ENTRIES`.
pub(crate) use self::materializer::MT_RISK_RISK_ENTRIES;
/// Crate-root access to `MT_ROUTING_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_ROUTING_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_ROUTING_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_ROUTING_COMPONENT_NAME;
/// Crate-root access to `MT_ROUTING_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_ROUTING_PROCESSOR_NAME;
/// Crate-root access to `MT_STAKING_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_STAKING_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_STAKING_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_STAKING_COMPONENT_NAME;
/// Crate-root access to `MT_STAKING_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_STAKING_PROCESSOR_NAME;
/// Crate-root access to `MT_STAKING_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_STAKING_PROJECTION_VERSION;
/// Crate-root access to `MT_STAKING_STAKE_ACCOUNT_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_ACCOUNT_ENTRIES;
/// Crate-root access to `MT_STAKING_STAKE_AUTHORITY_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_AUTHORITY_ENTRIES;
/// Crate-root access to `MT_STAKING_STAKE_LOCKUP_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_LOCKUP_ENTRIES;
/// Crate-root access to `MT_STAKING_STAKE_SURFACE`.
pub(crate) use self::materializer::MT_STAKING_STAKE_SURFACE;
/// Crate-root access to `MT_STAKING_STAKE_VALUE_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_STAKE_VALUE_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_ACCOUNT_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_ACCOUNT_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_ADMIN_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_ADMIN_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_AUTHORITY_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_AUTHORITY_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_REWARD_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_REWARD_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_STATE_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_STATE_ENTRIES;
/// Crate-root access to `MT_STAKING_VOTE_SURFACE`.
pub(crate) use self::materializer::MT_STAKING_VOTE_SURFACE;
/// Crate-root access to `MT_STAKING_VOTE_VALUE_ENTRIES`.
pub(crate) use self::materializer::MT_STAKING_VOTE_VALUE_ENTRIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Crate-root access to `MT_TOKEN_ACCOUNTS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
/// Crate-root access to `MT_TOKEN_ACCOUNTS_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Maximum Memo payload accepted by transaction annotations.
/// Crate-root access to `MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TOKEN_METADATA_RISK_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Crate-root access to `MT_TOKEN_METADATA_RISK_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;
/// Crate-root access to `MT_TRADES_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TRADES_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TRADES_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TRADES_COMPONENT_NAME;
/// Crate-root access to `MT_TRADES_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TRADES_PROCESSOR_NAME;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Stable processor name for transaction annotations.
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
/// Crate-root access to `MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION`.
pub(crate) use self::materializer::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for the native and SPL administration materializer.
/// Crate-root access to `MT_VAULT_ACCEPTED_FAMILIES`.
pub(crate) use self::materializer::MT_VAULT_ACCEPTED_FAMILIES;
/// Crate-root access to `MT_VAULT_COMPONENT_NAME`.
pub(crate) use self::materializer::MT_VAULT_COMPONENT_NAME;
/// Crate-root access to `MT_VAULT_PROCESSOR_NAME`.
pub(crate) use self::materializer::MT_VAULT_PROCESSOR_NAME;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_ADMIN`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_ADMIN;
/// Canonical tracing target for the native compliance audit materializer.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
/// Canonical tracing target for the native lifecycle materializer.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_FEES`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_FEES;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_LIFECYCLE`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
/// Canonical tracing target for Solana Program Metadata materialization.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
/// Canonical tracing target for SPL Token and ATA risk facts.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_RISK`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_RISK;
/// Canonical tracing target for the native stake and vote materializer.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_STAKING`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_STAKING;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
/// Canonical tracing target for transaction annotations.
/// Crate-root access to `TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS`.
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer.rs
// version: 9
// version: 14
//! Consolidated materializer modules.
@@ -73,8 +73,10 @@ pub use self::lending::MtLendingMaterializer;
pub use self::lifecycle::MtLifecycleMaterializer;
/// Reserved liquidity materializer.
pub use self::liquidity::MtLiquidityMaterializer;
/// Token-2022, Metaplex and Solana Program Metadata materializer.
/// Compatibility wrapper for the Solana Program Metadata materializer.
pub use self::metadata::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::metadata::MtSolanaProgramMetadataMaterializer;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::metadata::materializer_metadata_materialize_metaplex_account_snapshot;
/// Materializes one canonical Metaplex Metadata account snapshot.
@@ -118,100 +120,533 @@ pub use self::transaction::MtTransactionAnnotationMaterializer;
/// Reserved vault materializer.
pub use self::vault::MtVaultMaterializer;
/// Canonical processor name for the native and SPL administration materializer.
/// Crate-visible `MT_ADMIN_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::admin::MT_ADMIN_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-visible `MT_ADMIN_COMPONENT_NAME` component constant.
pub(crate) use self::admin::MT_ADMIN_COMPONENT_NAME;
/// Crate-visible `MT_ADMIN_CONFIG_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_CONFIG_ENTRIES;
/// Crate-visible `MT_ADMIN_CONFIG_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_CONFIG_SURFACE;
/// Crate-visible `MT_ADMIN_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_LOADER_V4_ENTRIES;
/// Crate-visible `MT_ADMIN_LOADER_V4_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_LOADER_V4_SURFACE;
/// Crate-visible `MT_ADMIN_PROCESSOR_NAME` component constant.
pub(crate) use self::admin::MT_ADMIN_PROCESSOR_NAME;
/// Canonical tracing target for the native and SPL administration materializer.
/// Crate-visible `MT_ADMIN_PROJECTION_VERSION` component constant.
pub(crate) use self::admin::MT_ADMIN_PROJECTION_VERSION;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SPL_TOKEN_SURFACE;
/// Crate-visible `MT_ADMIN_SYSTEM_ENTRIES` component constant.
pub(crate) use self::admin::MT_ADMIN_SYSTEM_ENTRIES;
/// Crate-visible `MT_ADMIN_SYSTEM_SURFACE` component constant.
pub(crate) use self::admin::MT_ADMIN_SYSTEM_SURFACE;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_ADMIN` component constant.
pub(crate) use self::admin::TRACING_TARGET_MATERIALIZER_ADMIN;
/// Canonical tracing target for the native compliance audit materializer.
/// Crate-visible `MT_BRIDGE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::bridge::MT_BRIDGE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_BRIDGE_COMPONENT_NAME` component constant.
pub(crate) use self::bridge::MT_BRIDGE_COMPONENT_NAME;
/// Crate-visible `MT_BRIDGE_PROCESSOR_NAME` component constant.
pub(crate) use self::bridge::MT_BRIDGE_PROCESSOR_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPONENT_NAME` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_PROCESSOR_NAME` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_PROJECTION_VERSION` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
pub(crate) use self::compliance::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT` component constant.
pub(crate) use self::compliance::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
/// Canonical tracing target for the native lifecycle materializer.
/// Crate-visible `MT_FEES_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::fees::MT_FEES_ACCEPTED_FAMILIES;
/// Crate-visible `MT_FEES_COMPONENT_NAME` component constant.
pub(crate) use self::fees::MT_FEES_COMPONENT_NAME;
/// Crate-visible `MT_FEES_PROCESSOR_NAME` component constant.
pub(crate) use self::fees::MT_FEES_PROCESSOR_NAME;
/// Crate-visible `MT_FEES_PROJECTION_VERSION` component constant.
pub(crate) use self::fees::MT_FEES_PROJECTION_VERSION;
/// Crate-visible `MT_FEES_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::fees::MT_FEES_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_FEES_TOKEN_2022_FEE_ENTRIES` component constant.
pub(crate) use self::fees::MT_FEES_TOKEN_2022_FEE_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_FEES` component constant.
pub(crate) use self::fees::TRACING_TARGET_MATERIALIZER_FEES;
/// Crate-visible `MT_GOVERNANCE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::governance::MT_GOVERNANCE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_GOVERNANCE_COMPONENT_NAME` component constant.
pub(crate) use self::governance::MT_GOVERNANCE_COMPONENT_NAME;
/// Crate-visible `MT_GOVERNANCE_PROCESSOR_NAME` component constant.
pub(crate) use self::governance::MT_GOVERNANCE_PROCESSOR_NAME;
/// Crate-visible `MT_LENDING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::lending::MT_LENDING_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LENDING_COMPONENT_NAME` component constant.
pub(crate) use self::lending::MT_LENDING_COMPONENT_NAME;
/// Crate-visible `MT_LENDING_PROCESSOR_NAME` component constant.
pub(crate) use self::lending::MT_LENDING_PROCESSOR_NAME;
/// Crate-visible `MT_LIFECYCLE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_BPF_LOADER_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_COMPONENT_NAME` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_COMPONENT_NAME;
/// Crate-visible `MT_LIFECYCLE_FEATURE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_FEATURE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_FEATURE_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_FEATURE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_LOADER_V4_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_LOADER_V4_SURFACE;
/// Crate-visible `MT_LIFECYCLE_PROCESSOR_NAME` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_PROCESSOR_NAME;
/// Crate-visible `MT_LIFECYCLE_PROJECTION_VERSION` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_PROJECTION_VERSION;
/// Crate-visible `MT_LIFECYCLE_SLASHING_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SLASHING_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SLASHING_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SLASHING_SURFACE;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_SYSTEM_SURFACE;
/// Crate-visible `MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES` component constant.
pub(crate) use self::lifecycle::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_LIFECYCLE` component constant.
pub(crate) use self::lifecycle::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::metadata::MT_METADATA_SPM_ACCEPTED_FAMILIES;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::metadata::MT_METADATA_SPM_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::metadata::MT_METADATA_SPM_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
/// Crate-visible `MT_LIQUIDITY_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::liquidity::MT_LIQUIDITY_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LIQUIDITY_COMPONENT_NAME` component constant.
pub(crate) use self::liquidity::MT_LIQUIDITY_COMPONENT_NAME;
/// Crate-visible `MT_LIQUIDITY_PROCESSOR_NAME` component constant.
pub(crate) use self::liquidity::MT_LIQUIDITY_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Crate-visible `MT_METADATA_TOKEN_2022_COMPONENT_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Crate-visible `MT_METADATA_TOKEN_2022_PROCESSOR_NAME` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Crate-visible `MT_METADATA_TOKEN_2022_PROJECTION_VERSION` component constant.
pub(crate) use self::metadata::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
/// Stable processor name for SPL Token and ATA risk facts.
/// Crate-visible `TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022` component constant.
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;
/// Crate-visible `MT_NFT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::nft::MT_NFT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_NFT_COMPONENT_NAME` component constant.
pub(crate) use self::nft::MT_NFT_COMPONENT_NAME;
/// Crate-visible `MT_NFT_PROCESSOR_NAME` component constant.
pub(crate) use self::nft::MT_NFT_PROCESSOR_NAME;
/// Crate-visible `MT_ORACLE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::oracle::MT_ORACLE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ORACLE_COMPONENT_NAME` component constant.
pub(crate) use self::oracle::MT_ORACLE_COMPONENT_NAME;
/// Crate-visible `MT_ORACLE_PROCESSOR_NAME` component constant.
pub(crate) use self::oracle::MT_ORACLE_PROCESSOR_NAME;
/// Crate-visible `MT_ORDERBOOK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::orderbook::MT_ORDERBOOK_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ORDERBOOK_COMPONENT_NAME` component constant.
pub(crate) use self::orderbook::MT_ORDERBOOK_COMPONENT_NAME;
/// Crate-visible `MT_ORDERBOOK_PROCESSOR_NAME` component constant.
pub(crate) use self::orderbook::MT_ORDERBOOK_PROCESSOR_NAME;
/// Crate-visible `MT_PERPETUALS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::perpetuals::MT_PERPETUALS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_PERPETUALS_COMPONENT_NAME` component constant.
pub(crate) use self::perpetuals::MT_PERPETUALS_COMPONENT_NAME;
/// Crate-visible `MT_PERPETUALS_PROCESSOR_NAME` component constant.
pub(crate) use self::perpetuals::MT_PERPETUALS_PROCESSOR_NAME;
/// Crate-visible `MT_POOL_STATE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::pool::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_POOL_STATE_COMPONENT_NAME` component constant.
pub(crate) use self::pool::MT_POOL_STATE_COMPONENT_NAME;
/// Crate-visible `MT_POOL_STATE_PROCESSOR_NAME` component constant.
pub(crate) use self::pool::MT_POOL_STATE_PROCESSOR_NAME;
/// Crate-visible `MT_REWARDS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::rewards::MT_REWARDS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_REWARDS_COMPONENT_NAME` component constant.
pub(crate) use self::rewards::MT_REWARDS_COMPONENT_NAME;
/// Crate-visible `MT_REWARDS_PROCESSOR_NAME` component constant.
pub(crate) use self::rewards::MT_REWARDS_PROCESSOR_NAME;
/// Crate-visible `MT_RISK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::risk::MT_RISK_ACCEPTED_FAMILIES;
/// Crate-visible `MT_RISK_COMPONENT_NAME` component constant.
pub(crate) use self::risk::MT_RISK_COMPONENT_NAME;
/// Crate-visible `MT_RISK_PROCESSOR_NAME` component constant.
pub(crate) use self::risk::MT_RISK_PROCESSOR_NAME;
/// Stable projection contract version for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_PROJECTION_VERSION` component constant.
pub(crate) use self::risk::MT_RISK_PROJECTION_VERSION;
/// Canonical tracing target for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_RISK_ENTRIES` component constant.
pub(crate) use self::risk::MT_RISK_RISK_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_RISK` component constant.
pub(crate) use self::risk::TRACING_TARGET_MATERIALIZER_RISK;
/// Canonical tracing target for the native stake and vote materializer.
/// Crate-visible `MT_ROUTING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::routing::MT_ROUTING_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ROUTING_COMPONENT_NAME` component constant.
pub(crate) use self::routing::MT_ROUTING_COMPONENT_NAME;
/// Crate-visible `MT_ROUTING_PROCESSOR_NAME` component constant.
pub(crate) use self::routing::MT_ROUTING_PROCESSOR_NAME;
/// Crate-visible `MT_STAKING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::staking::MT_STAKING_ACCEPTED_FAMILIES;
/// Crate-visible `MT_STAKING_COMPONENT_NAME` component constant.
pub(crate) use self::staking::MT_STAKING_COMPONENT_NAME;
/// Crate-visible `MT_STAKING_PROCESSOR_NAME` component constant.
pub(crate) use self::staking::MT_STAKING_PROCESSOR_NAME;
/// Crate-visible `MT_STAKING_PROJECTION_VERSION` component constant.
pub(crate) use self::staking::MT_STAKING_PROJECTION_VERSION;
/// Crate-visible `MT_STAKING_STAKE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_LOCKUP_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_LOCKUP_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_SURFACE` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_SURFACE;
/// Crate-visible `MT_STAKING_STAKE_VALUE_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_STAKE_VALUE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ADMIN_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_ADMIN_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_REWARD_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_REWARD_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_STATE_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_STATE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_SURFACE` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_SURFACE;
/// Crate-visible `MT_STAKING_VOTE_VALUE_ENTRIES` component constant.
pub(crate) use self::staking::MT_STAKING_VOTE_VALUE_ENTRIES;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_STAKING` component constant.
pub(crate) use self::staking::TRACING_TARGET_MATERIALIZER_STAKING;
/// Stable processor name for Token, ATA and Token-2022 account projections.
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_COMPONENT_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_PROCESSOR_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
/// Crate-visible `MT_TOKEN_ACCOUNTS_PROJECTION_VERSION` component constant.
pub(crate) use self::token::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
/// Crate-visible `MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::token::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_METADATA_RISK_COMPONENT_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_METADATA_RISK_PROCESSOR_NAME` component constant.
pub(crate) use self::token::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;
/// Crate-visible `TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS` component constant.
pub(crate) use self::token::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
/// Maximum Memo payload accepted by transaction annotations.
/// Crate-visible `MT_TRADES_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::trades::MT_TRADES_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TRADES_COMPONENT_NAME` component constant.
pub(crate) use self::trades::MT_TRADES_COMPONENT_NAME;
/// Crate-visible `MT_TRADES_PROCESSOR_NAME` component constant.
pub(crate) use self::trades::MT_TRADES_PROCESSOR_NAME;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Stable processor name for transaction annotations.
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION` component constant.
pub(crate) use self::transaction::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for transaction annotations.
/// Crate-visible `TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS` component constant.
pub(crate) use self::transaction::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;
/// Crate-visible `MT_VAULT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::vault::MT_VAULT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_VAULT_COMPONENT_NAME` component constant.
pub(crate) use self::vault::MT_VAULT_COMPONENT_NAME;
/// Crate-visible `MT_VAULT_PROCESSOR_NAME` component constant.
pub(crate) use self::vault::MT_VAULT_PROCESSOR_NAME;
#[cfg(test)]
mod tests {
#[test]
fn reserved_materializers_expose_inactive_public_contracts() {
let materializers: &[(&dyn crate::MtApiEventMaterializer, &str)] = &[
(&crate::MtBridgeMaterializer, "kb-lib.materializer.bridge"),
(&crate::MtGovernanceMaterializer, "kb-lib.materializer.governance"),
(&crate::MtLendingMaterializer, "kb-lib.materializer.lending"),
(&crate::MtLiquidityMaterializer, "kb-lib.materializer.liquidity"),
(&crate::MtNftMaterializer, "kb-lib.materializer.nft"),
(&crate::MtOracleMaterializer, "kb-lib.materializer.oracle"),
(&crate::MtOrderbookMaterializer, "kb-lib.materializer.orderbook"),
(&crate::MtPerpetualsMaterializer, "kb-lib.materializer.perpetuals"),
(&crate::MtPoolStateMaterializer, "kb-lib.materializer.pool.state"),
(&crate::MtRewardsMaterializer, "kb-lib.materializer.rewards"),
(&crate::MtRoutingMaterializer, "kb-lib.materializer.routing"),
fn materializer_contracts() -> &'static [(
&'static dyn crate::MtMaterializer,
&'static dyn crate::MtApiEventMaterializer,
&'static str,
&'static str,
)] {
return &[
(
&crate::MtAdminMaterializer,
&crate::MtAdminMaterializer,
crate::MT_ADMIN_COMPONENT_NAME,
crate::MT_ADMIN_PROCESSOR_NAME,
),
(
&crate::MtBridgeMaterializer,
&crate::MtBridgeMaterializer,
crate::MT_BRIDGE_COMPONENT_NAME,
crate::MT_BRIDGE_PROCESSOR_NAME,
),
(
&crate::MtComplianceAuditMaterializer,
&crate::MtComplianceAuditMaterializer,
crate::MT_COMPLIANCE_AUDIT_COMPONENT_NAME,
crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME,
),
(
&crate::MtFeesMaterializer,
&crate::MtFeesMaterializer,
crate::MT_FEES_COMPONENT_NAME,
crate::MT_FEES_PROCESSOR_NAME,
),
(
&crate::MtGovernanceMaterializer,
&crate::MtGovernanceMaterializer,
crate::MT_GOVERNANCE_COMPONENT_NAME,
crate::MT_GOVERNANCE_PROCESSOR_NAME,
),
(
&crate::MtLendingMaterializer,
&crate::MtLendingMaterializer,
crate::MT_LENDING_COMPONENT_NAME,
crate::MT_LENDING_PROCESSOR_NAME,
),
(
&crate::MtLifecycleMaterializer,
&crate::MtLifecycleMaterializer,
crate::MT_LIFECYCLE_COMPONENT_NAME,
crate::MT_LIFECYCLE_PROCESSOR_NAME,
),
(
&crate::MtLiquidityMaterializer,
&crate::MtLiquidityMaterializer,
crate::MT_LIQUIDITY_COMPONENT_NAME,
crate::MT_LIQUIDITY_PROCESSOR_NAME,
),
(
&crate::MtSolanaProgramMetadataMaterializer,
&crate::MtSolanaProgramMetadataMaterializer,
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME,
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
),
(
&crate::MtNftMaterializer,
&crate::MtNftMaterializer,
crate::MT_NFT_COMPONENT_NAME,
crate::MT_NFT_PROCESSOR_NAME,
),
(
&crate::MtOracleMaterializer,
&crate::MtOracleMaterializer,
crate::MT_ORACLE_COMPONENT_NAME,
crate::MT_ORACLE_PROCESSOR_NAME,
),
(
&crate::MtOrderbookMaterializer,
&crate::MtOrderbookMaterializer,
crate::MT_ORDERBOOK_COMPONENT_NAME,
crate::MT_ORDERBOOK_PROCESSOR_NAME,
),
(
&crate::MtPerpetualsMaterializer,
&crate::MtPerpetualsMaterializer,
crate::MT_PERPETUALS_COMPONENT_NAME,
crate::MT_PERPETUALS_PROCESSOR_NAME,
),
(
&crate::MtPoolStateMaterializer,
&crate::MtPoolStateMaterializer,
crate::MT_POOL_STATE_COMPONENT_NAME,
crate::MT_POOL_STATE_PROCESSOR_NAME,
),
(
&crate::MtRewardsMaterializer,
&crate::MtRewardsMaterializer,
crate::MT_REWARDS_COMPONENT_NAME,
crate::MT_REWARDS_PROCESSOR_NAME,
),
(
&crate::MtRiskMaterializer,
&crate::MtRiskMaterializer,
crate::MT_RISK_COMPONENT_NAME,
crate::MT_RISK_PROCESSOR_NAME,
),
(
&crate::MtRoutingMaterializer,
&crate::MtRoutingMaterializer,
crate::MT_ROUTING_COMPONENT_NAME,
crate::MT_ROUTING_PROCESSOR_NAME,
),
(
&crate::MtStakingMaterializer,
&crate::MtStakingMaterializer,
crate::MT_STAKING_COMPONENT_NAME,
crate::MT_STAKING_PROCESSOR_NAME,
),
(
&crate::MtTokenAccountsMaterializer,
&crate::MtTokenAccountsMaterializer,
crate::MT_TOKEN_ACCOUNTS_COMPONENT_NAME,
crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
),
(
&crate::MtTokenMetadataRiskMaterializer,
"kb-lib.materializer.token.metadata_risk",
&crate::MtTokenMetadataRiskMaterializer,
crate::MT_TOKEN_METADATA_RISK_COMPONENT_NAME,
crate::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME,
),
(
&crate::MtTradesMaterializer,
&crate::MtTradesMaterializer,
crate::MT_TRADES_COMPONENT_NAME,
crate::MT_TRADES_PROCESSOR_NAME,
),
(
&crate::MtTransactionAnnotationMaterializer,
&crate::MtTransactionAnnotationMaterializer,
crate::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME,
crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME,
),
(
&crate::MtVaultMaterializer,
&crate::MtVaultMaterializer,
crate::MT_VAULT_COMPONENT_NAME,
crate::MT_VAULT_PROCESSOR_NAME,
),
(&crate::MtTradesMaterializer, "kb-lib.materializer.trades"),
(&crate::MtVaultMaterializer, "kb-lib.materializer.vault"),
];
for (materializer, expected_name) in materializers {
let identity = materializer.identity();
assert_eq!(identity.name, *expected_name);
assert!(!identity.version.is_empty());
assert!(materializer.accepted_families().is_empty());
}
fn state_only_materializer_contracts() -> &'static [(&'static str, &'static str, &'static str)]
{
return &[
(
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
),
(
crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
),
];
}
#[test]
fn every_materializer_uses_the_component_and_processor_name_convention() {
for (legacy, contextual, component_name, processor_name) in materializer_contracts() {
assert_eq!(legacy.materializer_name(), *component_name);
assert_eq!(contextual.identity().name, *processor_name);
assert!(component_name.starts_with("kb-lib.materializer."));
assert!(processor_name.starts_with("materializer."));
assert_eq!(
component_name.strip_prefix("kb-lib."),
std::option::Option::Some(*processor_name)
);
assert!(!legacy.materializer_version().is_empty());
assert!(!contextual.identity().version.is_empty());
}
}
#[test]
fn reserved_materializers_preserve_legacy_trait_contracts() {
let materializers: &[(&dyn crate::MtMaterializer, &str)] = &[
(&crate::MtBridgeMaterializer, "kb-lib.materializer.bridge"),
(&crate::MtGovernanceMaterializer, "kb-lib.materializer.governance"),
(&crate::MtLendingMaterializer, "kb-lib.materializer.lending"),
(&crate::MtLiquidityMaterializer, "kb-lib.materializer.liquidity"),
(&crate::MtNftMaterializer, "kb-lib.materializer.nft"),
(&crate::MtOracleMaterializer, "kb-lib.materializer.oracle"),
(&crate::MtOrderbookMaterializer, "kb-lib.materializer.orderbook"),
(&crate::MtPerpetualsMaterializer, "kb-lib.materializer.perpetuals"),
(&crate::MtPoolStateMaterializer, "kb-lib.materializer.pool.state"),
(&crate::MtRewardsMaterializer, "kb-lib.materializer.rewards"),
(&crate::MtRoutingMaterializer, "kb-lib.materializer.routing"),
(
&crate::MtTokenMetadataRiskMaterializer,
"kb-lib.materializer.token.metadata_risk",
),
(&crate::MtTradesMaterializer, "kb-lib.materializer.trades"),
(&crate::MtVaultMaterializer, "kb-lib.materializer.vault"),
fn state_only_materializers_keep_runtime_processor_and_tracing_identity_aligned() {
for (component_name, processor_name, tracing_target) in state_only_materializer_contracts()
{
assert_eq!(*tracing_target, *component_name);
assert_eq!(
component_name.strip_prefix("kb-lib."),
std::option::Option::Some(*processor_name)
);
}
}
#[test]
fn reserved_materializers_are_empty_shells() {
let materializers: &[&dyn crate::MtApiEventMaterializer] = &[
&crate::MtBridgeMaterializer,
&crate::MtGovernanceMaterializer,
&crate::MtLendingMaterializer,
&crate::MtLiquidityMaterializer,
&crate::MtNftMaterializer,
&crate::MtOracleMaterializer,
&crate::MtOrderbookMaterializer,
&crate::MtPerpetualsMaterializer,
&crate::MtPoolStateMaterializer,
&crate::MtRewardsMaterializer,
&crate::MtRoutingMaterializer,
&crate::MtTokenMetadataRiskMaterializer,
&crate::MtTradesMaterializer,
&crate::MtVaultMaterializer,
];
for (materializer, expected_name) in materializers {
assert_eq!(materializer.materializer_name(), *expected_name);
assert!(!materializer.materializer_version().is_empty());
for materializer in materializers {
assert!(materializer.accepted_families().is_empty());
}
}
}

View File

@@ -1,19 +1,55 @@
// file: kb-lib/src/materializer/admin.rs
// version: 7
// version: 8
//! `admin` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable native and SPL administration materializer.
pub use self::core::MtAdminMaterializer;
pub use self::materializer::MtAdminMaterializer;
/// Materializes one exact SPL ElGamal registry account snapshot.
pub use self::core::materializer_admin_materialize_elgamal_registry_state_snapshot;
pub use self::materializer::materializer_admin_materialize_elgamal_registry_state_snapshot;
/// Materializes exact Token-2022 administration account snapshots.
pub use self::core::materializer_admin_materialize_token_2022_state_snapshots;
pub use self::materializer::materializer_admin_materialize_token_2022_state_snapshots;
/// Canonical processor name for the native and SPL administration materializer.
/// Crate-visible `MT_ADMIN_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_ADMIN_ACCEPTED_FAMILIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES;
/// Crate-visible `MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the `admin` materializer.
pub(crate) use self::constants::MT_ADMIN_COMPONENT_NAME;
/// Crate-visible `MT_ADMIN_CONFIG_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_CONFIG_ENTRIES;
/// Crate-visible `MT_ADMIN_CONFIG_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_CONFIG_SURFACE;
/// Crate-visible `MT_ADMIN_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_LOADER_V4_ENTRIES;
/// Crate-visible `MT_ADMIN_LOADER_V4_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_LOADER_V4_SURFACE;
/// Stable processor name for the `admin` materializer.
pub(crate) use self::constants::MT_ADMIN_PROCESSOR_NAME;
/// Canonical tracing target for the native and SPL administration materializer.
/// Stable projection version for the `admin` materializer.
pub(crate) use self::constants::MT_ADMIN_PROJECTION_VERSION;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES;
/// Crate-visible `MT_ADMIN_SPL_TOKEN_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SPL_TOKEN_SURFACE;
/// Crate-visible `MT_ADMIN_SYSTEM_ENTRIES` component constant.
pub(crate) use self::constants::MT_ADMIN_SYSTEM_ENTRIES;
/// Crate-visible `MT_ADMIN_SYSTEM_SURFACE` component constant.
pub(crate) use self::constants::MT_ADMIN_SYSTEM_SURFACE;
/// Canonical tracing target for the `admin` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_ADMIN;

View File

@@ -1,9 +1,67 @@
// file: kb-lib/src/materializer/admin/constants.rs
// version: 3
// version: 5
//! Local constants for the native and SPL administration materializer.
//! Local constants for the admin materializer.
/// Canonical processor name for this component.
/// Stable runtime component name for the admin materializer.
pub(crate) const MT_ADMIN_COMPONENT_NAME: &str = "kb-lib.materializer.admin";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ADMIN_PROCESSOR_NAME: &str = "materializer.admin";
/// Canonical tracing target for this component.
/// Stable projection contract version for the admin materializer.
pub(crate) const MT_ADMIN_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the admin materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_ADMIN: &str = "kb-lib.materializer.admin";
/// Component contract for `accepted_families`.
pub(crate) const MT_ADMIN_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Admin, crate::MdEventFamily::TokenAccount];
/// Component contract for `system_surface`.
pub(crate) const MT_ADMIN_SYSTEM_SURFACE: &str = "solana.core.system";
/// Component contract for `system_entries`.
pub(crate) const MT_ADMIN_SYSTEM_ENTRIES: &[&str] = &["assign", "assign_with_seed"];
/// Component contract for `config_surface`.
pub(crate) const MT_ADMIN_CONFIG_SURFACE: &str = "solana.core.config";
/// Component contract for `config_entries`.
pub(crate) const MT_ADMIN_CONFIG_ENTRIES: &[&str] = &["store"];
/// Component contract for `bpf_loader_upgradeable_surface`.
pub(crate) const MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE: &str =
"solana.core.bpf_loader_upgradeable";
/// Component contract for `bpf_loader_upgradeable_entries`.
pub(crate) const MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES: &[&str] =
&["set_authority", "set_authority_checked"];
/// Component contract for `loader_v4_surface`.
pub(crate) const MT_ADMIN_LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
/// Component contract for `loader_v4_entries`.
pub(crate) const MT_ADMIN_LOADER_V4_ENTRIES: &[&str] = &["transfer_authority"];
/// Component contract for `spl_token_surface`.
pub(crate) const MT_ADMIN_SPL_TOKEN_SURFACE: &str = "spl.token";
/// Component contract for `spl_token_2022_surface`.
pub(crate) const MT_ADMIN_SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
/// Component contract for `spl_elgamal_registry_surface`.
pub(crate) const MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE: &str = "spl.elgamal_registry";
/// Component contract for `spl_elgamal_registry_entries`.
pub(crate) const MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES: &[&str] =
&["create_registry", "update_registry"];
/// Component contract for `spl_token_authority_entries`.
pub(crate) const MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES: &[&str] = &[
"set_authority",
"initialize_permanent_delegate",
"initialize_interest_bearing_mint",
"update_interest_bearing_rate",
];
/// Component contract for `spl_token_multisig_entries`.
pub(crate) const MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES: &[&str] =
&["initialize_multisig", "initialize_multisig2"];
/// Component contract for `spl_token_2022_extension_admin_entries`.
pub(crate) const MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES: &[&str] = &[
"initialize_transfer_hook",
"update_transfer_hook_program_id",
"initialize_metadata_pointer",
"update_metadata_pointer",
"initialize_group_pointer",
"update_group_pointer",
"initialize_group_member_pointer",
"update_group_member_pointer",
"initialize_scaled_ui_amount",
"update_scaled_ui_amount_multiplier",
];

View File

@@ -1,42 +1,8 @@
// file: kb-lib/src/materializer/admin/core.rs
// version: 20
// file: kb-lib/src/materializer/admin/materializer.rs
// version: 22
//! Stable administrative projections derived from exact committed observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Admin, crate::MdEventFamily::TokenAccount];
const SYSTEM_SURFACE: &str = "solana.core.system";
const SYSTEM_ENTRIES: &[&str] = &["assign", "assign_with_seed"];
const CONFIG_SURFACE: &str = "solana.core.config";
const CONFIG_ENTRIES: &[&str] = &["store"];
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
const BPF_LOADER_UPGRADEABLE_ENTRIES: &[&str] = &["set_authority", "set_authority_checked"];
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
const LOADER_V4_ENTRIES: &[&str] = &["transfer_authority"];
const SPL_TOKEN_SURFACE: &str = "spl.token";
const SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
const SPL_ELGAMAL_REGISTRY_SURFACE: &str = "spl.elgamal_registry";
const SPL_ELGAMAL_REGISTRY_ENTRIES: &[&str] = &["create_registry", "update_registry"];
const SPL_TOKEN_AUTHORITY_ENTRIES: &[&str] = &[
"set_authority",
"initialize_permanent_delegate",
"initialize_interest_bearing_mint",
"update_interest_bearing_rate",
];
const SPL_TOKEN_MULTISIG_ENTRIES: &[&str] = &["initialize_multisig", "initialize_multisig2"];
const SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES: &[&str] = &[
"initialize_transfer_hook",
"update_transfer_hook_program_id",
"initialize_metadata_pointer",
"update_metadata_pointer",
"initialize_group_pointer",
"update_group_pointer",
"initialize_group_member_pointer",
"update_group_member_pointer",
"initialize_scaled_ui_amount",
"update_scaled_ui_amount_multiplier",
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
SystemAccountOwnership,
@@ -91,7 +57,7 @@ pub struct MtAdminMaterializer;
impl crate::MtMaterializer for crate::MtAdminMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.admin";
return crate::MT_ADMIN_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -99,7 +65,7 @@ impl crate::MtMaterializer for crate::MtAdminMaterializer {
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return ACCEPTED_FAMILIES.contains(&event.event_family)
return crate::MT_ADMIN_ACCEPTED_FAMILIES.contains(&event.event_family)
&& program_matches_surface(event)
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
}
@@ -118,17 +84,17 @@ impl crate::MtMaterializer for crate::MtAdminMaterializer {
impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.admin".to_string(),
name: crate::MT_ADMIN_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_ADMIN_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return ACCEPTED_FAMILIES.contains(&observation.event.event_family)
return crate::MT_ADMIN_ACCEPTED_FAMILIES.contains(&observation.event.event_family)
&& program_matches_surface(&observation.event)
&& projection(
observation.event.surface_code.0.as_str(),
@@ -165,7 +131,7 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
if !ACCEPTED_FAMILIES.contains(&observation.event.event_family) {
if !crate::MT_ADMIN_ACCEPTED_FAMILIES.contains(&observation.event.event_family) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if !program_matches_surface(&observation.event) {
@@ -202,7 +168,7 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
family: crate::MdMaterializedEventFamily::Admin,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_admin_event",
"domain": projection.domain(),
"loaderSurface": projection.loader_surface(),
@@ -212,6 +178,10 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_ADMIN_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(
@@ -240,41 +210,51 @@ impl crate::MtApiEventMaterializer for crate::MtAdminMaterializer {
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == SYSTEM_SURFACE && contains(SYSTEM_ENTRIES, entry_code) {
if surface_code == crate::MT_ADMIN_SYSTEM_SURFACE
&& contains(crate::MT_ADMIN_SYSTEM_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SystemAccountOwnership);
}
if surface_code == CONFIG_SURFACE && contains(CONFIG_ENTRIES, entry_code) {
if surface_code == crate::MT_ADMIN_CONFIG_SURFACE
&& contains(crate::MT_ADMIN_CONFIG_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ConfigAccountUpdate);
}
if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code)
if surface_code == crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority(
BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE,
));
}
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_ENTRIES, entry_code) {
if surface_code == crate::MT_ADMIN_LOADER_V4_SURFACE
&& contains(crate::MT_ADMIN_LOADER_V4_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority(
LOADER_V4_SURFACE,
crate::MT_ADMIN_LOADER_V4_SURFACE,
));
}
if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE)
&& contains(SPL_TOKEN_AUTHORITY_ENTRIES, entry_code)
if matches!(
surface_code,
crate::MT_ADMIN_SPL_TOKEN_SURFACE | crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
) && contains(crate::MT_ADMIN_SPL_TOKEN_AUTHORITY_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplTokenAuthority);
}
if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE)
&& contains(SPL_TOKEN_MULTISIG_ENTRIES, entry_code)
if matches!(
surface_code,
crate::MT_ADMIN_SPL_TOKEN_SURFACE | crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
) && contains(crate::MT_ADMIN_SPL_TOKEN_MULTISIG_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplTokenMultisig);
}
if surface_code == SPL_TOKEN_2022_SURFACE
&& contains(SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code)
if surface_code == crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE
&& contains(crate::MT_ADMIN_SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplToken2022ExtensionAdmin);
}
if surface_code == SPL_ELGAMAL_REGISTRY_SURFACE
&& contains(SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code)
if surface_code == crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE
&& contains(crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SplElgamalRegistry);
}
@@ -282,13 +262,13 @@ fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<Proje
}
fn program_matches_surface(event: &crate::MdDecodedProtocolEvent) -> bool {
if event.surface_code.0 == SPL_TOKEN_SURFACE {
if event.surface_code.0 == crate::MT_ADMIN_SPL_TOKEN_SURFACE {
return event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID;
}
if event.surface_code.0 == SPL_TOKEN_2022_SURFACE {
if event.surface_code.0 == crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE {
return event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID;
}
if event.surface_code.0 == SPL_ELGAMAL_REGISTRY_SURFACE {
if event.surface_code.0 == crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE {
return event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID;
}
return true;
@@ -537,7 +517,7 @@ mod tests {
fn system_assign_creates_owner_projection() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::SYSTEM_SURFACE,
crate::MT_ADMIN_SYSTEM_SURFACE,
"assign",
false,
serde_json::json!([account("assigned_account", "account111",)]),
@@ -554,7 +534,7 @@ mod tests {
fn seeded_assign_preserves_base_seed_and_owner() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::SYSTEM_SURFACE,
crate::MT_ADMIN_SYSTEM_SURFACE,
"assign_with_seed",
false,
serde_json::json!([
@@ -573,7 +553,7 @@ mod tests {
fn config_store_preserves_bounded_opaque_audit_fields() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::CONFIG_SURFACE,
crate::MT_ADMIN_CONFIG_SURFACE,
"store",
false,
serde_json::json!([
@@ -600,13 +580,17 @@ mod tests {
fn loader_authority_variants_create_exact_admin_outputs() {
let materializer = crate::MtAdminMaterializer;
for (surface, entry, target_role) in [
(super::BPF_LOADER_UPGRADEABLE_SURFACE, "set_authority", "authority_target"),
(
super::BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE,
"set_authority",
"authority_target",
),
(
crate::MT_ADMIN_BPF_LOADER_UPGRADEABLE_SURFACE,
"set_authority_checked",
"authority_target",
),
(super::LOADER_V4_SURFACE, "transfer_authority", "program_account"),
(crate::MT_ADMIN_LOADER_V4_SURFACE, "transfer_authority", "program_account"),
] {
let observation = observation(
surface,
@@ -629,7 +613,7 @@ mod tests {
#[test]
fn classic_token_authority_and_multisig_create_distinct_admin_outputs() {
let mut authority = observation(
super::SPL_TOKEN_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_SURFACE,
"set_authority",
false,
serde_json::json!([
@@ -649,7 +633,7 @@ mod tests {
);
let mut multisig = observation(
super::SPL_TOKEN_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_SURFACE,
"initialize_multisig2",
false,
serde_json::json!([
@@ -678,7 +662,7 @@ mod tests {
#[test]
fn token_2022_authority_change_uses_the_admin_owner_once() {
let mut observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
"set_authority",
false,
serde_json::json!([
@@ -703,7 +687,7 @@ mod tests {
fn failed_admin_mutation_is_refused() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::SYSTEM_SURFACE,
crate::MT_ADMIN_SYSTEM_SURFACE,
"assign",
true,
serde_json::json!([account("assigned_account", "account111",)]),
@@ -754,7 +738,7 @@ mod tests {
fn output_serialization_is_deterministic() {
let materializer = crate::MtAdminMaterializer;
let observation = observation(
super::CONFIG_SURFACE,
crate::MT_ADMIN_CONFIG_SURFACE,
"store",
false,
serde_json::json!([account("config_account", "config111",)]),
@@ -788,7 +772,7 @@ mod tests {
("update_registry", 0_i64, 2_u64, "elgamal_registry_key_updated"),
] {
let mut observation = observation(
super::SPL_ELGAMAL_REGISTRY_SURFACE,
crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE,
operation,
false,
serde_json::json!([
@@ -830,7 +814,7 @@ mod tests {
fn elgamal_registry_foreign_program_and_failed_transaction_are_refused() {
let materializer = crate::MtAdminMaterializer;
let mut foreign = observation(
super::SPL_ELGAMAL_REGISTRY_SURFACE,
crate::MT_ADMIN_SPL_ELGAMAL_REGISTRY_SURFACE,
"create_registry",
false,
serde_json::json!([]),
@@ -858,7 +842,7 @@ mod tests {
("update_group_member_pointer", "memberAddress", "member111"),
] {
let mut observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
operation,
false,
serde_json::json!([{"position":0,"accountKey":"mint111"}]),
@@ -881,7 +865,7 @@ mod tests {
#[test]
fn token_2022_extension_admin_rejects_foreign_program_and_failed_transaction() {
let mut observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_ADMIN_SPL_TOKEN_2022_SURFACE,
"initialize_metadata_pointer",
false,
serde_json::json!([]),
@@ -937,7 +921,7 @@ pub fn materializer_admin_materialize_token_2022_state_snapshots(
output_key: output_key.clone(),
family: crate::MdMaterializedEventFamily::Admin,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
"projectionSemantics": "authoritative_bounded_admin_state_snapshot",
"domain": "token_2022_extension_admin_state",
"idempotenceKey": output_key,
@@ -1072,7 +1056,7 @@ pub fn materializer_admin_materialize_elgamal_registry_state_snapshot(
output_key: format!("spl_elgamal_registry_state:{registry_account_key}:{slot}"),
family: crate::MdMaterializedEventFamily::Admin,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_ADMIN_PROJECTION_VERSION,
"domain": "spl_elgamal_registry_account_state",
"projectionSemantics": "authoritative_exact_account_snapshot",
"idempotenceKey": format!("spl_elgamal_registry:{registry_account_key}:{slot}"),

View File

@@ -1,12 +1,12 @@
// file: kb-lib/src/materializer/api/contracts.rs
// version: 7
// version: 8
//! Backend-neutral decoded observation materialization contracts.
/// Stable identity of one decoded event materializer.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct MtApiMaterializerIdentity {
/// Stable lower snake case processor name.
/// Stable dotted lower-snake-case processor name without the runtime crate prefix.
pub name: std::string::String,
/// Semantic or deterministic implementation version.
pub version: std::string::String,
@@ -177,7 +177,7 @@ impl MtApiMaterializerExecutionResult {
/// Stable decoded observation materializer contract used by the common pipeline.
pub trait MtApiEventMaterializer: std::marker::Send + std::marker::Sync {
/// Returns the stable materializer identity.
/// Returns the stable persisted processor identity for this materializer.
fn identity(&self) -> MtApiMaterializerIdentity;
/// Returns every decoded event family explicitly accepted by this materializer.
fn accepted_families(&self) -> &'static [crate::MdEventFamily];

View File

@@ -1,11 +1,11 @@
// file: kb-lib/src/materializer/api/materializer.rs
// version: 2
// version: 3
//! Materializer API contract used by business materializer crates.
//! Legacy materializer API contract used by business materializer modules.
/// Common contract implemented by all business materializer crates.
/// Common compatibility contract implemented by all business materializer modules.
pub trait MtMaterializer {
/// Returns the stable materializer name.
/// Returns the stable runtime component name with the `kb-lib.materializer.` prefix.
fn materializer_name(&self) -> &'static str;
/// Returns the materializer version.

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/bridge.rs
// version: 3
// version: 4
//! `bridge` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved bridge materializer.
pub use self::core::MtBridgeMaterializer;
pub use self::materializer::MtBridgeMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_BRIDGE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `bridge` materializer.
pub(crate) use self::constants::MT_BRIDGE_COMPONENT_NAME;
/// Stable processor name for the `bridge` materializer.
pub(crate) use self::constants::MT_BRIDGE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/bridge/constants.rs
// version: 1
//! Local constants for the bridge materializer.
/// Stable runtime component name for the bridge materializer.
pub(crate) const MT_BRIDGE_COMPONENT_NAME: &str = "kb-lib.materializer.bridge";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_BRIDGE_PROCESSOR_NAME: &str = "materializer.bridge";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_BRIDGE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/bridge/core.rs
// version: 3
// file: kb-lib/src/materializer/bridge/materializer.rs
// version: 4
//! Reserved bridge materializer.
@@ -9,7 +9,7 @@ pub struct MtBridgeMaterializer;
impl crate::MtMaterializer for crate::MtBridgeMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.bridge";
return crate::MT_BRIDGE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtBridgeMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Bridge,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtBridgeMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.bridge".to_string(),
name: crate::MT_BRIDGE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_BRIDGE_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,13 +1,36 @@
// file: kb-lib/src/materializer/compliance.rs
// version: 4
// version: 5
//! `compliance` materializer family.
mod audit;
mod constants;
/// Stable native compliance audit materializer.
pub use self::audit::MtComplianceAuditMaterializer;
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the native compliance audit materializer.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Stable processor name for the native compliance audit materializer.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Stable projection version for native compliance audit outputs.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
pub(crate) use self::audit::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Canonical tracing target for the native compliance audit materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
pub(crate) use self::audit::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;

View File

@@ -1,577 +1,37 @@
// file: kb-lib/src/materializer/compliance/audit.rs
// version: 10
// version: 11
//! Stable compliance-audit projections for committed native bytecode mutations and execution profiles.
//! Native compliance-audit materializer component.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Audit];
const COMPUTE_BUDGET_SURFACE: &str = "solana.core.compute_budget";
const COMPUTE_BUDGET_ENTRIES: &[&str] = &[
"request_units_deprecated",
"request_heap_frame",
"set_compute_unit_limit",
"set_compute_unit_price",
"set_loaded_accounts_data_size_limit",
];
const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana.core.bpf_loader_deprecated";
const BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
const WRITE_ENTRIES: &[&str] = &["write"];
const LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"];
mod constants;
mod materializer;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
ProgramByteWrite(&'static str),
ProgramByteCopy,
ComputeBudgetProfile,
}
/// Stable native compliance audit materializer.
pub use self::materializer::MtComplianceAuditMaterializer;
impl ProjectionKind {
fn output_key(self, surface_code: &str, operation: &str) -> std::string::String {
return match self {
Self::ProgramByteWrite(_) | Self::ProgramByteCopy => {
format!("program_loader_code:{surface_code}:{operation}:0")
},
Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(),
};
}
fn loader_surface(self) -> &'static str {
return match self {
Self::ProgramByteWrite(surface) => surface,
Self::ProgramByteCopy => LOADER_V4_SURFACE,
Self::ComputeBudgetProfile => COMPUTE_BUDGET_SURFACE,
};
}
}
/// Stable compliance-audit materializer for native bytecode mutations and execution profiles.
#[derive(Clone, Debug, Default)]
pub struct MtComplianceAuditMaterializer;
impl crate::MtMaterializer for crate::MtComplianceAuditMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.compliance.audit";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Audit
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
if !crate::MtMaterializer::accepts_event(self, event) {
return std::result::Result::Ok(std::vec::Vec::new());
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtComplianceAuditMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.compliance.audit".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return observation.event.event_family == crate::MdEventFamily::Audit
&& projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
)
.is_some();
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let projection = match projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
accepted = false,
"ignore observation outside supported native compliance-audit projections"
);
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
if observation.event.event_family != crate::MdEventFamily::Audit {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if projection != ProjectionKind::ComputeBudgetProfile
&& (observation.transaction_failed || !observation.observation_committed)
{
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
committed = observation.observation_committed,
transaction_failed = observation.transaction_failed,
"refuse uncommitted native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_compliance_audit_refused",
"failed or uncommitted native observations cannot create committed bytecode mutation outputs",
);
}
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
if projection == ProjectionKind::ComputeBudgetProfile {
return materialize_compute_budget_profile(observation, parameters, operation);
}
let output = crate::MtApiMaterializedOutput {
output_key: projection
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionSemantics": "committed_instruction_compliance_audit_event",
"domain": "program_loader_code_mutation",
"loaderSurface": projection.loader_surface(),
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(projection, &accounts, &parameters),
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
loader_surface = projection.loader_surface(),
output_count = 1_usize,
"materialize committed native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn materialize_compute_budget_profile(
observation: &crate::DcApiDecodedObservation,
parameters: serde_json::Value,
operation: std::string::String,
) -> crate::MtApiMaterializerExecutionResult {
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
if !compute_budget_profile_emits(&profile) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_compute_budget_profile:0".to_string(),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionSemantics": "transaction_compute_budget_profile",
"domain": "transaction_compute_budget_profile",
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": !observation.transaction_failed,
"profile": profile,
"profileSource": {
"surfaceCode": observation.event.surface_code.0.clone(),
"entryCode": observation.event.event_name.0.clone(),
"eventKey": observation.event_key.clone()
},
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compute_budget_profile",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
output_count = 1_usize,
transaction_failed = observation.transaction_failed,
"materialize native Compute Budget transaction profile"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool {
return profile
.get("currentInstructionIsProfileEmitter")
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == COMPUTE_BUDGET_SURFACE && contains(COMPUTE_BUDGET_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile);
}
if contains(WRITE_ENTRIES, entry_code) {
for supported in [
BPF_LOADER_DEPRECATED_SURFACE,
BPF_LOADER_SURFACE,
BPF_LOADER_UPGRADEABLE_SURFACE,
LOADER_V4_SURFACE,
] {
if surface_code == supported {
return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported));
}
}
}
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_COPY_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::ProgramByteCopy);
}
return std::option::Option::None;
}
fn projection_details(
projection: ProjectionKind,
accounts: &serde_json::Value,
parameters: &serde_json::Value,
) -> serde_json::Value {
return match projection {
ProjectionKind::ProgramByteWrite(_) => serde_json::json!({
"targetAccount": first_account_key_for_roles(
accounts,
&["program_account", "buffer_account"],
),
"authorityAccount": first_account_key_for_roles(
accounts,
&["authority", "buffer_authority"],
),
"offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null),
"bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null),
"bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_written",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ProgramByteCopy => serde_json::json!({
"targetAccount": account_key_for_role(accounts, "program_account"),
"authorityAccount": account_key_for_role(accounts, "authority"),
"sourceProgramAccount": account_key_for_role(accounts, "source_program_account"),
"destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null),
"sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_copied",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null,
};
}
fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value {
for role in roles {
let value = account_key_for_role(accounts, role);
if !value.is_null() {
return value;
}
}
return serde_json::Value::Null;
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let account_key = accounts.as_array().and_then(|values| {
return values.iter().find_map(|value| {
if value.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return value
.get("accountKey")
.and_then(serde_json::Value::as_str)
.map(|account_key| return account_key.to_string());
});
});
return match account_key {
std::option::Option::Some(value) => serde_json::Value::String(value),
std::option::Option::None => serde_json::Value::Null,
};
}
fn contains(entries: &[&str], entry_code: &str) -> bool {
return entries.iter().any(|entry| return *entry == entry_code);
}
#[cfg(test)]
mod tests {
fn observation(
surface_code: &str,
entry_code: &str,
transaction_failed: bool,
accounts: serde_json::Value,
parameters: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("{entry_code}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId("program-id".to_string()),
protocol_code: crate::MdProtocolCode("solana.core".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
event_name: crate::MdEventName(entry_code.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}),
transaction_failed,
transaction_error: if transaction_failed {
std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]}))
} else {
std::option::Option::None
},
observation_committed: !transaction_failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn account(role: &str, account_key: &str) -> serde_json::Value {
return serde_json::json!({"role": role, "accountKey": account_key});
}
#[test]
fn loader_write_variants_create_bounded_code_audits() {
let materializer = crate::MtComplianceAuditMaterializer;
for (surface, target_role, authority_role) in [
(super::BPF_LOADER_DEPRECATED_SURFACE, "program_account", "missing"),
(super::BPF_LOADER_SURFACE, "program_account", "missing"),
(super::BPF_LOADER_UPGRADEABLE_SURFACE, "buffer_account", "buffer_authority"),
(super::LOADER_V4_SURFACE, "program_account", "authority"),
] {
let observation = observation(
surface,
"write",
false,
serde_json::json!([
account(target_role, "target111"),
account(authority_role, "authority111"),
]),
serde_json::json!({
"offset": 7,
"byteLength": 32,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111");
assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc");
assert_eq!(
result.outputs[0].payload_json["projection"]["codeBytesMaterialized"],
false
);
}
}
#[test]
fn loader_v4_copy_preserves_source_and_bounded_ranges() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::LOADER_V4_SURFACE,
"copy",
false,
serde_json::json!([
account("program_account", "target111"),
account("authority", "authority111"),
account("source_program_account", "source111"),
]),
serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(
result.outputs[0].output_key,
"program_loader_code:solana.core.loader_v4:copy:0"
);
assert_eq!(
result.outputs[0].payload_json["projection"]["sourceProgramAccount"],
"source111"
);
assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16);
}
#[test]
fn compute_budget_profile_is_emitted_only_by_profile_emitter() {
let materializer = crate::MtComplianceAuditMaterializer;
let emitter = observation(
super::COMPUTE_BUDGET_SURFACE,
"set_compute_unit_limit",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 2,
"effectiveValues": {
"set_compute_unit_limit": {
"value": 200000,
"sourceInstructionPath": "0"
}
}
}
}),
);
let emitted = crate::MtApiEventMaterializer::materialize(&materializer, &emitter);
assert_eq!(emitted.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0");
assert_eq!(emitted.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2);
let non_emitter = observation(
super::COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": false,
"profileEmitterInstructionPath": "0"
}
}),
);
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &non_emitter);
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn failed_compute_budget_profile_remains_audit_materializable() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
true,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 1
}
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false);
}
#[test]
fn failed_code_mutation_is_refused() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::LOADER_V4_SURFACE,
"write",
true,
serde_json::json!([account("program_account", "target111")]),
serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn unrelated_audit_observation_is_ignored() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
"other_surface",
"write",
false,
serde_json::json!([]),
serde_json::json!({}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn output_serialization_is_deterministic() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
super::BPF_LOADER_UPGRADEABLE_SURFACE,
"write",
false,
serde_json::json!([
account("buffer_account", "buffer111"),
account("buffer_authority", "authority111"),
]),
serde_json::json!({
"offset": 0,
"byteLength": 2,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let first = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let second = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let first_json = match serde_json::to_string(&first) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("first compliance serialization failed: {error}")
},
};
let second_json = match serde_json::to_string(&second) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("second compliance serialization failed: {error}")
},
};
assert_eq!(first_json, second_json);
}
}
/// Crate-visible `MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the native compliance audit materializer.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES;
/// Crate-visible `MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE;
/// Stable processor name for the native compliance audit materializer.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME;
/// Stable projection version for native compliance audit outputs.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION;
/// Crate-visible `MT_COMPLIANCE_AUDIT_WRITE_ENTRIES` component constant.
pub(crate) use self::constants::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES;
/// Canonical tracing target for the native compliance audit materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;

View File

@@ -0,0 +1,42 @@
// file: kb-lib/src/materializer/compliance/audit/constants.rs
// version: 4
//! Local constants for the compliance audit materializer.
/// Stable runtime component name for the compliance audit materializer.
pub(crate) const MT_COMPLIANCE_AUDIT_COMPONENT_NAME: &str = "kb-lib.materializer.compliance.audit";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_COMPLIANCE_AUDIT_PROCESSOR_NAME: &str = "materializer.compliance.audit";
/// Stable projection contract version for the compliance audit materializer.
pub(crate) const MT_COMPLIANCE_AUDIT_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the compliance audit materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT: &str =
"kb-lib.materializer.compliance.audit";
/// Component contract for `accepted_families`.
pub(crate) const MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Audit];
/// Component contract for `compute_budget_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE: &str = "solana.core.compute_budget";
/// Component contract for `compute_budget_entries`.
pub(crate) const MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES: &[&str] = &[
"request_units_deprecated",
"request_heap_frame",
"set_compute_unit_limit",
"set_compute_unit_price",
"set_loaded_accounts_data_size_limit",
];
/// Component contract for `bpf_loader_deprecated_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE: &str =
"solana.core.bpf_loader_deprecated";
/// Component contract for `bpf_loader_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
/// Component contract for `bpf_loader_upgradeable_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE: &str =
"solana.core.bpf_loader_upgradeable";
/// Component contract for `loader_v4_surface`.
pub(crate) const MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
/// Component contract for `write_entries`.
pub(crate) const MT_COMPLIANCE_AUDIT_WRITE_ENTRIES: &[&str] = &["write"];
/// Component contract for `loader_v4_copy_entries`.
pub(crate) const MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"];

View File

@@ -0,0 +1,581 @@
// file: kb-lib/src/materializer/compliance/audit/materializer.rs
// version: 12
//! Stable compliance-audit projections for committed native bytecode mutations and execution profiles.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
ProgramByteWrite(&'static str),
ProgramByteCopy,
ComputeBudgetProfile,
}
impl ProjectionKind {
fn output_key(self, surface_code: &str, operation: &str) -> std::string::String {
return match self {
Self::ProgramByteWrite(_) | Self::ProgramByteCopy => {
format!("program_loader_code:{surface_code}:{operation}:0")
},
Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(),
};
}
fn loader_surface(self) -> &'static str {
return match self {
Self::ProgramByteWrite(surface) => surface,
Self::ProgramByteCopy => crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
Self::ComputeBudgetProfile => crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
};
}
}
/// Stable compliance-audit materializer for native bytecode mutations and execution profiles.
#[derive(Clone, Debug, Default)]
pub struct MtComplianceAuditMaterializer;
impl crate::MtMaterializer for crate::MtComplianceAuditMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_COMPLIANCE_AUDIT_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Audit
&& projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some();
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
if !crate::MtMaterializer::accepts_event(self, event) {
return std::result::Result::Ok(std::vec::Vec::new());
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtComplianceAuditMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_COMPLIANCE_AUDIT_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return observation.event.event_family == crate::MdEventFamily::Audit
&& projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
)
.is_some();
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulOrFailedAudit;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let projection = match projection(
observation.event.surface_code.0.as_str(),
observation.event.event_name.0.as_str(),
) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
accepted = false,
"ignore observation outside supported native compliance-audit projections"
);
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
if observation.event.event_family != crate::MdEventFamily::Audit {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if projection != ProjectionKind::ComputeBudgetProfile
&& (observation.transaction_failed || !observation.observation_committed)
{
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
committed = observation.observation_committed,
transaction_failed = observation.transaction_failed,
"refuse uncommitted native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_compliance_audit_refused",
"failed or uncommitted native observations cannot create committed bytecode mutation outputs",
);
}
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
if projection == ProjectionKind::ComputeBudgetProfile {
return materialize_compute_budget_profile(observation, parameters, operation);
}
let output = crate::MtApiMaterializedOutput {
output_key: projection
.output_key(observation.event.surface_code.0.as_str(), operation.as_str()),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_compliance_audit_event",
"domain": "program_loader_code_mutation",
"loaderSurface": projection.loader_surface(),
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(projection, &accounts, &parameters),
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compliance_audit",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
loader_surface = projection.loader_surface(),
output_count = 1_usize,
"materialize committed native bytecode mutation observation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn materialize_compute_budget_profile(
observation: &crate::DcApiDecodedObservation,
parameters: serde_json::Value,
operation: std::string::String,
) -> crate::MtApiMaterializerExecutionResult {
let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null);
if !compute_budget_profile_emits(&profile) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_compute_budget_profile:0".to_string(),
family: crate::MdMaterializedEventFamily::ComplianceAudit,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_COMPLIANCE_AUDIT_PROJECTION_VERSION,
"projectionSemantics": "transaction_compute_budget_profile",
"domain": "transaction_compute_budget_profile",
"operation": operation,
"programId": observation.event.program_id.0.clone(),
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": !observation.transaction_failed,
"provenance": {
"processorName": crate::MT_COMPLIANCE_AUDIT_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"profile": profile,
"profileSource": {
"surfaceCode": observation.event.surface_code.0.clone(),
"entryCode": observation.event.event_name.0.clone(),
"eventKey": observation.event_key.clone()
},
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT,
action = "materialize_compute_budget_profile",
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
output_count = 1_usize,
transaction_failed = observation.transaction_failed,
"materialize native Compute Budget transaction profile"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool {
return profile
.get("currentInstructionIsProfileEmitter")
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE
&& contains(crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile);
}
if contains(crate::MT_COMPLIANCE_AUDIT_WRITE_ENTRIES, entry_code) {
for supported in [
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE,
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE,
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
] {
if surface_code == supported {
return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported));
}
}
}
if surface_code == crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE
&& contains(crate::MT_COMPLIANCE_AUDIT_LOADER_V4_COPY_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramByteCopy);
}
return std::option::Option::None;
}
fn projection_details(
projection: ProjectionKind,
accounts: &serde_json::Value,
parameters: &serde_json::Value,
) -> serde_json::Value {
return match projection {
ProjectionKind::ProgramByteWrite(_) => serde_json::json!({
"targetAccount": first_account_key_for_roles(
accounts,
&["program_account", "buffer_account"],
),
"authorityAccount": first_account_key_for_roles(
accounts,
&["authority", "buffer_authority"],
),
"offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null),
"bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null),
"bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_written",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ProgramByteCopy => serde_json::json!({
"targetAccount": account_key_for_role(accounts, "program_account"),
"authorityAccount": account_key_for_role(accounts, "authority"),
"sourceProgramAccount": account_key_for_role(accounts, "source_program_account"),
"destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null),
"sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null),
"byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null),
"stateTransition": "program_bytes_copied",
"codeBytesMaterialized": false,
"transactionFinalAccountStateCaptured": false,
}),
ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null,
};
}
fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value {
for role in roles {
let value = account_key_for_role(accounts, role);
if !value.is_null() {
return value;
}
}
return serde_json::Value::Null;
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let account_key = accounts.as_array().and_then(|values| {
return values.iter().find_map(|value| {
if value.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return value
.get("accountKey")
.and_then(serde_json::Value::as_str)
.map(|account_key| return account_key.to_string());
});
});
return match account_key {
std::option::Option::Some(value) => serde_json::Value::String(value),
std::option::Option::None => serde_json::Value::Null,
};
}
fn contains(entries: &[&str], entry_code: &str) -> bool {
return entries.iter().any(|entry| return *entry == entry_code);
}
#[cfg(test)]
mod tests {
fn observation(
surface_code: &str,
entry_code: &str,
transaction_failed: bool,
accounts: serde_json::Value,
parameters: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("{entry_code}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId("program-id".to_string()),
protocol_code: crate::MdProtocolCode("solana.core".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{entry_code}")),
event_name: crate::MdEventName(entry_code.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}),
transaction_failed,
transaction_error: if transaction_failed {
std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]}))
} else {
std::option::Option::None
},
observation_committed: !transaction_failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn account(role: &str, account_key: &str) -> serde_json::Value {
return serde_json::json!({"role": role, "accountKey": account_key});
}
#[test]
fn loader_write_variants_create_bounded_code_audits() {
let materializer = crate::MtComplianceAuditMaterializer;
for (surface, target_role, authority_role) in [
(
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_DEPRECATED_SURFACE,
"program_account",
"missing",
),
(crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_SURFACE, "program_account", "missing"),
(
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE,
"buffer_account",
"buffer_authority",
),
(crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE, "program_account", "authority"),
] {
let observation = observation(
surface,
"write",
false,
serde_json::json!([
account(target_role, "target111"),
account(authority_role, "authority111"),
]),
serde_json::json!({
"offset": 7,
"byteLength": 32,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111");
assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc");
assert_eq!(
result.outputs[0].payload_json["projection"]["codeBytesMaterialized"],
false
);
}
}
#[test]
fn loader_v4_copy_preserves_source_and_bounded_ranges() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
"copy",
false,
serde_json::json!([
account("program_account", "target111"),
account("authority", "authority111"),
account("source_program_account", "source111"),
]),
serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(
result.outputs[0].output_key,
"program_loader_code:solana.core.loader_v4:copy:0"
);
assert_eq!(
result.outputs[0].payload_json["projection"]["sourceProgramAccount"],
"source111"
);
assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16);
}
#[test]
fn compute_budget_profile_is_emitted_only_by_profile_emitter() {
let materializer = crate::MtComplianceAuditMaterializer;
let emitter = observation(
crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
"set_compute_unit_limit",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 2,
"effectiveValues": {
"set_compute_unit_limit": {
"value": 200000,
"sourceInstructionPath": "0"
}
}
}
}),
);
let emitted = crate::MtApiEventMaterializer::materialize(&materializer, &emitter);
assert_eq!(emitted.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0");
assert_eq!(emitted.outputs[0].family, crate::MdMaterializedEventFamily::ComplianceAudit);
assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2);
let non_emitter = observation(
crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
false,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": false,
"profileEmitterInstructionPath": "0"
}
}),
);
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &non_emitter);
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn failed_compute_budget_profile_remains_audit_materializable() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_COMPUTE_BUDGET_SURFACE,
"set_compute_unit_price",
true,
serde_json::json!([]),
serde_json::json!({
"transactionProfile": {
"currentInstructionIsProfileEmitter": true,
"profileEmitterInstructionPath": "0",
"computeBudgetInstructionCount": 1
}
}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false);
}
#[test]
fn failed_code_mutation_is_refused() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_LOADER_V4_SURFACE,
"write",
true,
serde_json::json!([account("program_account", "target111")]),
serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn unrelated_audit_observation_is_ignored() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
"other_surface",
"write",
false,
serde_json::json!([]),
serde_json::json!({}),
);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn output_serialization_is_deterministic() {
let materializer = crate::MtComplianceAuditMaterializer;
let observation = observation(
crate::MT_COMPLIANCE_AUDIT_BPF_LOADER_UPGRADEABLE_SURFACE,
"write",
false,
serde_json::json!([
account("buffer_account", "buffer111"),
account("buffer_authority", "authority111"),
]),
serde_json::json!({
"offset": 0,
"byteLength": 2,
"bytesSha256": "abc",
"bytesPrefixHex": "0102",
}),
);
let first = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let second = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
let first_json = match serde_json::to_string(&first) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("first compliance serialization failed: {error}")
},
};
let second_json = match serde_json::to_string(&second) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("second compliance serialization failed: {error}")
},
};
assert_eq!(first_json, second_json);
}
}

View File

@@ -1,8 +0,0 @@
// file: kb-lib/src/materializer/compliance/constants.rs
// version: 2
//! Local constants for the native compliance audit materializer.
/// Canonical tracing target for this component.
pub(crate) const TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT: &str =
"kb-lib.materializer.compliance";

View File

@@ -1,11 +1,27 @@
// file: kb-lib/src/materializer/fees.rs
// version: 4
// version: 5
//! `fees` materializer family.
mod core;
mod constants;
mod materializer;
/// Fee materializer for committed Token-2022 fee observations.
pub use self::core::MtFeesMaterializer;
pub use self::materializer::MtFeesMaterializer;
/// Materializes authoritative Token-2022 fee extension snapshots.
pub use self::core::materializer_fees_materialize_token_2022_state_snapshots;
pub use self::materializer::materializer_fees_materialize_token_2022_state_snapshots;
/// Crate-visible `MT_FEES_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_FEES_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `fees` materializer.
pub(crate) use self::constants::MT_FEES_COMPONENT_NAME;
/// Stable processor name for the `fees` materializer.
pub(crate) use self::constants::MT_FEES_PROCESSOR_NAME;
/// Stable projection version for the `fees` materializer.
pub(crate) use self::constants::MT_FEES_PROJECTION_VERSION;
/// Crate-visible `MT_FEES_SPL_TOKEN_2022_SURFACE` component constant.
pub(crate) use self::constants::MT_FEES_SPL_TOKEN_2022_SURFACE;
/// Crate-visible `MT_FEES_TOKEN_2022_FEE_ENTRIES` component constant.
pub(crate) use self::constants::MT_FEES_TOKEN_2022_FEE_ENTRIES;
/// Canonical tracing target for the `fees` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_FEES;

View File

@@ -0,0 +1,34 @@
// file: kb-lib/src/materializer/fees/constants.rs
// version: 2
//! Local constants for the fees materializer.
/// Stable runtime component name for the fees materializer.
pub(crate) const MT_FEES_COMPONENT_NAME: &str = "kb-lib.materializer.fees";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_FEES_PROCESSOR_NAME: &str = "materializer.fees";
/// Stable projection contract version for the fees materializer.
pub(crate) const MT_FEES_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the fees materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_FEES: &str = "kb-lib.materializer.fees";
/// Component contract for `accepted_families`.
pub(crate) const MT_FEES_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Fee];
/// Component contract for `spl_token_2022_surface`.
pub(crate) const MT_FEES_SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
/// Component contract for `token_2022_fee_entries`.
pub(crate) const MT_FEES_TOKEN_2022_FEE_ENTRIES: &[&str] = &[
"initialize_transfer_fee_config",
"set_transfer_fee",
"withdraw_withheld_tokens_from_mint",
"withdraw_withheld_tokens_from_accounts",
"harvest_withheld_tokens_to_mint",
"transfer_checked_with_fee",
"initialize_confidential_transfer_fee_config",
"withdraw_confidential_withheld_tokens_from_mint",
"withdraw_confidential_withheld_tokens_from_accounts",
"harvest_confidential_withheld_tokens_to_mint",
"enable_confidential_harvest_to_mint",
"disable_confidential_harvest_to_mint",
"transfer_confidential_tokens_with_fee",
];

View File

@@ -1,33 +1,15 @@
// file: kb-lib/src/materializer/fees/core.rs
// version: 5
// file: kb-lib/src/materializer/fees/materializer.rs
// version: 8
//! Stable fee projections derived from exact committed Token-2022 observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Fee];
const SPL_TOKEN_2022_SURFACE: &str = "spl.token_2022";
const TOKEN_2022_FEE_ENTRIES: &[&str] = &[
"initialize_transfer_fee_config",
"set_transfer_fee",
"withdraw_withheld_tokens_from_mint",
"withdraw_withheld_tokens_from_accounts",
"harvest_withheld_tokens_to_mint",
"transfer_checked_with_fee",
"initialize_confidential_transfer_fee_config",
"withdraw_confidential_withheld_tokens_from_mint",
"withdraw_confidential_withheld_tokens_from_accounts",
"harvest_confidential_withheld_tokens_to_mint",
"enable_confidential_harvest_to_mint",
"disable_confidential_harvest_to_mint",
"transfer_confidential_tokens_with_fee",
];
/// Fee materializer for committed Token-2022 fee instructions.
#[derive(Clone, Debug, Default)]
pub struct MtFeesMaterializer;
impl crate::MtMaterializer for crate::MtFeesMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.fees";
return crate::MT_FEES_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
@@ -46,12 +28,12 @@ impl crate::MtMaterializer for crate::MtFeesMaterializer {
impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.fees".to_string(),
name: crate::MT_FEES_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_FEES_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
@@ -67,9 +49,35 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_fee_observation",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
accepted = false,
status = "ignored",
"ignore observation outside supported Token-2022 fee projections"
);
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_fee_observation",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
committed = observation.observation_committed,
transaction_failed = observation.transaction_failed,
status = "refused",
error_code = "failed_transaction_fee_refused",
"refuse failed or uncommitted Token-2022 fee observation"
);
return crate::MtApiMaterializerExecutionResult::refused(
"failed_transaction_fee_refused",
"failed or uncommitted Token-2022 observations cannot create fee outputs",
@@ -90,7 +98,7 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
output_key: format!("token_2022_fee:{operation}:0"),
family: crate::MdMaterializedEventFamily::Fee,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_FEES_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_fee_event",
"domain": fee_domain(observation.event.event_name.0.as_str()),
"operation": operation,
@@ -99,6 +107,10 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_FEES_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"stateReadRequired": state_read_required(
@@ -107,6 +119,21 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
"confidentialValuesDecrypted": false,
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_fee_observation",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
surface_code = %observation.event.surface_code.0.as_str(),
entry_code = %observation.event.event_name.0.as_str(),
signature = %observation.event.signature.0.as_str(),
slot = observation.event.slot.0,
instruction_path = %observation.event.instruction_path.0.as_str(),
output_count = 1_usize,
status = "inserted",
"materialize committed Token-2022 fee observation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
@@ -117,9 +144,9 @@ impl crate::MtApiEventMaterializer for crate::MtFeesMaterializer {
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Fee
&& event.surface_code.0 == SPL_TOKEN_2022_SURFACE
&& event.surface_code.0 == crate::MT_FEES_SPL_TOKEN_2022_SURFACE
&& event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& contains(TOKEN_2022_FEE_ENTRIES, event.event_name.0.as_str());
&& contains(crate::MT_FEES_TOKEN_2022_FEE_ENTRIES, event.event_name.0.as_str());
}
fn fee_domain(operation: &str) -> &'static str {
@@ -195,7 +222,7 @@ pub fn materializer_fees_materialize_token_2022_state_snapshots(
),
family: crate::MdMaterializedEventFamily::Fee,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_FEES_PROJECTION_VERSION,
"projectionSemantics": "authoritative_bounded_fee_state_snapshot",
"domain": if confidential {
"token_2022_confidential_transfer_fee_state"
@@ -218,13 +245,27 @@ pub fn materializer_fees_materialize_token_2022_state_snapshots(
"confidentialValuesDecrypted": false,
"finalAccountStateCaptured": true,
"provenance": {
"processorName": "materializer.fees",
"processorName": crate::MT_FEES_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
let status = if outputs.is_empty() { "ignored" } else { "inserted" };
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_FEES,
action = "materialize_token_2022_fee_state_snapshots",
component_name = crate::MT_FEES_COMPONENT_NAME,
processor_name = crate::MT_FEES_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = %account_key,
slot,
state_kind,
output_count = outputs.len(),
status,
"materialize bounded Token-2022 fee state snapshots"
);
return std::result::Result::Ok(outputs);
}
@@ -273,7 +314,7 @@ mod tests {
#[test]
fn committed_token_2022_fee_instruction_creates_one_stable_output() {
let observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"set_transfer_fee",
false,
@@ -290,7 +331,7 @@ mod tests {
#[test]
fn failed_token_2022_fee_instruction_is_refused() {
let observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"harvest_withheld_tokens_to_mint",
true,
@@ -310,7 +351,7 @@ mod tests {
false,
);
let wrong_program = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
"set_transfer_fee",
false,
@@ -334,7 +375,7 @@ mod tests {
#[test]
fn committed_confidential_fee_instruction_is_projected_without_decryption() {
let observation = observation(
super::SPL_TOKEN_2022_SURFACE,
crate::MT_FEES_SPL_TOKEN_2022_SURFACE,
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"initialize_confidential_transfer_fee_config",
false,

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/governance.rs
// version: 3
// version: 4
//! `governance` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved governance materializer.
pub use self::core::MtGovernanceMaterializer;
pub use self::materializer::MtGovernanceMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_GOVERNANCE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `governance` materializer.
pub(crate) use self::constants::MT_GOVERNANCE_COMPONENT_NAME;
/// Stable processor name for the `governance` materializer.
pub(crate) use self::constants::MT_GOVERNANCE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/governance/constants.rs
// version: 1
//! Local constants for the governance materializer.
/// Stable runtime component name for the governance materializer.
pub(crate) const MT_GOVERNANCE_COMPONENT_NAME: &str = "kb-lib.materializer.governance";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_GOVERNANCE_PROCESSOR_NAME: &str = "materializer.governance";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_GOVERNANCE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/governance/core.rs
// version: 4
// file: kb-lib/src/materializer/governance/materializer.rs
// version: 5
//! Reserved governance materializer.
@@ -9,7 +9,7 @@ pub struct MtGovernanceMaterializer;
impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.governance";
return crate::MT_GOVERNANCE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtGovernanceMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Governance,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtGovernanceMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.governance".to_string(),
name: crate::MT_GOVERNANCE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_GOVERNANCE_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/lending.rs
// version: 3
// version: 4
//! `lending` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved lending materializer.
pub use self::core::MtLendingMaterializer;
pub use self::materializer::MtLendingMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_LENDING_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `lending` materializer.
pub(crate) use self::constants::MT_LENDING_COMPONENT_NAME;
/// Stable processor name for the `lending` materializer.
pub(crate) use self::constants::MT_LENDING_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/lending/constants.rs
// version: 1
//! Local constants for the lending materializer.
/// Stable runtime component name for the lending materializer.
pub(crate) const MT_LENDING_COMPONENT_NAME: &str = "kb-lib.materializer.lending";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_LENDING_PROCESSOR_NAME: &str = "materializer.lending";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_LENDING_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/lending/core.rs
// version: 4
// file: kb-lib/src/materializer/lending/materializer.rs
// version: 5
//! Reserved lending materializer.
@@ -9,7 +9,7 @@ pub struct MtLendingMaterializer;
impl crate::MtMaterializer for crate::MtLendingMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.lending";
return crate::MT_LENDING_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtLendingMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Lending,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtLendingMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.lending".to_string(),
name: crate::MT_LENDING_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_LENDING_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,13 +1,59 @@
// file: kb-lib/src/materializer/lifecycle.rs
// version: 4
// version: 5
//! `lifecycle` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable native lifecycle materializer.
pub use self::core::MtLifecycleMaterializer;
pub use self::materializer::MtLifecycleMaterializer;
/// Canonical tracing target for the native lifecycle materializer.
/// Crate-visible `MT_LIFECYCLE_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ACCEPTED_FAMILIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_BPF_LOADER_SURFACE;
/// Crate-visible `MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE;
/// Stable runtime component name for the `lifecycle` materializer.
pub(crate) use self::constants::MT_LIFECYCLE_COMPONENT_NAME;
/// Crate-visible `MT_LIFECYCLE_FEATURE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_FEATURE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_FEATURE_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_FEATURE_SURFACE;
/// Crate-visible `MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_LOADER_V4_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_LOADER_V4_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_LOADER_V4_SURFACE;
/// Stable processor name for the `lifecycle` materializer.
pub(crate) use self::constants::MT_LIFECYCLE_PROCESSOR_NAME;
/// Stable projection version for the `lifecycle` materializer.
pub(crate) use self::constants::MT_LIFECYCLE_PROJECTION_VERSION;
/// Crate-visible `MT_LIFECYCLE_SLASHING_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SLASHING_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SLASHING_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SLASHING_SURFACE;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_SYSTEM_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_SYSTEM_SURFACE;
/// Crate-visible `MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE;
/// Crate-visible `MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES` component constant.
pub(crate) use self::constants::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES;
/// Canonical tracing target for the `lifecycle` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_LIFECYCLE;

View File

@@ -1,7 +1,100 @@
// file: kb-lib/src/materializer/lifecycle/constants.rs
// version: 2
// version: 4
//! Local constants for the native lifecycle materializer.
//! Local constants for the lifecycle materializer.
/// Canonical tracing target for this component.
/// Stable runtime component name for the lifecycle materializer.
pub(crate) const MT_LIFECYCLE_COMPONENT_NAME: &str = "kb-lib.materializer.lifecycle";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_LIFECYCLE_PROCESSOR_NAME: &str = "materializer.lifecycle";
/// Stable projection contract version for the lifecycle materializer.
pub(crate) const MT_LIFECYCLE_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the lifecycle materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_LIFECYCLE: &str = "kb-lib.materializer.lifecycle";
/// Component contract for `accepted_families`.
pub(crate) const MT_LIFECYCLE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Audit,
];
/// Component contract for `address_lookup_table_surface`.
pub(crate) const MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE: &str =
"solana.core.address_lookup_table";
/// Component contract for `address_lookup_table_entries`.
pub(crate) const MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES: &[&str] = &[
"create_lookup_table",
"freeze_lookup_table",
"extend_lookup_table",
"deactivate_lookup_table",
"close_lookup_table",
];
/// Component contract for `bpf_loader_deprecated_surface`.
pub(crate) const MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE: &str =
"solana.core.bpf_loader_deprecated";
/// Component contract for `bpf_loader_surface`.
pub(crate) const MT_LIFECYCLE_BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
/// Component contract for `bpf_loader_upgradeable_surface`.
pub(crate) const MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE: &str =
"solana.core.bpf_loader_upgradeable";
/// Component contract for `loader_v4_surface`.
pub(crate) const MT_LIFECYCLE_LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
/// Component contract for `feature_surface`.
pub(crate) const MT_LIFECYCLE_FEATURE_SURFACE: &str = "solana.core.feature";
/// Component contract for `feature_entries`.
pub(crate) const MT_LIFECYCLE_FEATURE_ENTRIES: &[&str] = &["revoke_pending_activation"];
/// Component contract for `immutable_loader_entries`.
pub(crate) const MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES: &[&str] = &["finalize"];
/// Component contract for `upgradeable_loader_entries`.
pub(crate) const MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES: &[&str] = &[
"initialize_buffer",
"deploy_with_max_data_len",
"upgrade",
"close",
"extend_program",
];
/// Component contract for `loader_v4_entries`.
pub(crate) const MT_LIFECYCLE_LOADER_V4_ENTRIES: &[&str] =
&["set_program_length", "deploy", "retract", "finalize"];
/// Component contract for `slashing_surface`.
pub(crate) const MT_LIFECYCLE_SLASHING_SURFACE: &str = "solana.core.slashing";
/// Component contract for `slashing_entries`.
pub(crate) const MT_LIFECYCLE_SLASHING_ENTRIES: &[&str] =
&["close_violation_report", "duplicate_block_proof"];
/// Component contract for `system_surface`.
pub(crate) const MT_LIFECYCLE_SYSTEM_SURFACE: &str = "solana.core.system";
/// Component contract for `system_account_entries`.
pub(crate) const MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES: &[&str] = &[
"create_account",
"create_account_with_seed",
"allocate",
"allocate_with_seed",
"create_account_allow_prefund",
];
/// Component contract for `system_nonce_entries`.
pub(crate) const MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES: &[&str] = &[
"advance_nonce_account",
"withdraw_nonce_account",
"initialize_nonce_account",
"authorize_nonce_account",
"upgrade_nonce_account",
];
/// Component contract for `zk_elgamal_proof_surface`.
pub(crate) const MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE: &str = "solana.core.zk_elgamal_proof";
/// Component contract for `zk_elgamal_close_entries`.
pub(crate) const MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES: &[&str] = &["close_context_state"];
/// Component contract for `zk_elgamal_verify_entries`.
pub(crate) const MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES: &[&str] = &[
"verify_zero_ciphertext",
"verify_ciphertext_ciphertext_equality",
"verify_ciphertext_commitment_equality",
"verify_pubkey_validity",
"verify_percentage_with_cap",
"verify_batched_range_proof_u64",
"verify_batched_range_proof_u128",
"verify_batched_range_proof_u256",
"verify_grouped_ciphertext_2_handles_validity",
"verify_batched_grouped_ciphertext_2_handles_validity",
"verify_grouped_ciphertext_3_handles_validity",
"verify_batched_grouped_ciphertext_3_handles_validity",
];

View File

@@ -1,70 +1,8 @@
// file: kb-lib/src/materializer/lifecycle/core.rs
// version: 16
// file: kb-lib/src/materializer/lifecycle/materializer.rs
// version: 18
//! Stable lifecycle projections derived from exact decoded observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Audit,
];
const ADDRESS_LOOKUP_TABLE_SURFACE: &str = "solana.core.address_lookup_table";
const ADDRESS_LOOKUP_TABLE_ENTRIES: &[&str] = &[
"create_lookup_table",
"freeze_lookup_table",
"extend_lookup_table",
"deactivate_lookup_table",
"close_lookup_table",
];
const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana.core.bpf_loader_deprecated";
const BPF_LOADER_SURFACE: &str = "solana.core.bpf_loader";
const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana.core.bpf_loader_upgradeable";
const LOADER_V4_SURFACE: &str = "solana.core.loader_v4";
const FEATURE_SURFACE: &str = "solana.core.feature";
const FEATURE_ENTRIES: &[&str] = &["revoke_pending_activation"];
const IMMUTABLE_LOADER_ENTRIES: &[&str] = &["finalize"];
const UPGRADEABLE_LOADER_ENTRIES: &[&str] = &[
"initialize_buffer",
"deploy_with_max_data_len",
"upgrade",
"close",
"extend_program",
];
const LOADER_V4_ENTRIES: &[&str] = &["set_program_length", "deploy", "retract", "finalize"];
const SLASHING_SURFACE: &str = "solana.core.slashing";
const SLASHING_ENTRIES: &[&str] = &["close_violation_report", "duplicate_block_proof"];
const SYSTEM_SURFACE: &str = "solana.core.system";
const SYSTEM_ACCOUNT_ENTRIES: &[&str] = &[
"create_account",
"create_account_with_seed",
"allocate",
"allocate_with_seed",
"create_account_allow_prefund",
];
const SYSTEM_NONCE_ENTRIES: &[&str] = &[
"advance_nonce_account",
"withdraw_nonce_account",
"initialize_nonce_account",
"authorize_nonce_account",
"upgrade_nonce_account",
];
const ZK_ELGAMAL_PROOF_SURFACE: &str = "solana.core.zk_elgamal_proof";
const ZK_ELGAMAL_CLOSE_ENTRIES: &[&str] = &["close_context_state"];
const ZK_ELGAMAL_VERIFY_ENTRIES: &[&str] = &[
"verify_zero_ciphertext",
"verify_ciphertext_ciphertext_equality",
"verify_ciphertext_commitment_equality",
"verify_pubkey_validity",
"verify_percentage_with_cap",
"verify_batched_range_proof_u64",
"verify_batched_range_proof_u128",
"verify_batched_range_proof_u256",
"verify_grouped_ciphertext_2_handles_validity",
"verify_batched_grouped_ciphertext_2_handles_validity",
"verify_grouped_ciphertext_3_handles_validity",
"verify_batched_grouped_ciphertext_3_handles_validity",
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
AddressLookupTable,
@@ -125,7 +63,7 @@ pub struct MtLifecycleMaterializer;
impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.lifecycle";
return crate::MT_LIFECYCLE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -152,13 +90,13 @@ impl crate::MtMaterializer for crate::MtLifecycleMaterializer {
impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.lifecycle".to_string(),
name: crate::MT_LIFECYCLE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_LIFECYCLE_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -239,7 +177,7 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
output_key,
family: crate::MdMaterializedEventFamily::Lifecycle,
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_LIFECYCLE_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_lifecycle_event",
"domain": domain,
"loaderSurface": loader_surface,
@@ -249,6 +187,10 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_LIFECYCLE_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details,
@@ -263,7 +205,9 @@ impl crate::MtApiEventMaterializer for crate::MtLifecycleMaterializer {
}
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family);
return crate::MT_LIFECYCLE_ACCEPTED_FAMILIES
.iter()
.any(|accepted| return *accepted == family);
}
fn observation_projection(
@@ -275,8 +219,8 @@ fn observation_projection(
if static_projection.is_some() {
return static_projection;
}
if surface_code == ZK_ELGAMAL_PROOF_SURFACE
&& contains(ZK_ELGAMAL_VERIFY_ENTRIES, entry_code)
if surface_code == crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE
&& contains(crate::MT_LIFECYCLE_ZK_ELGAMAL_VERIFY_ENTRIES, entry_code)
&& observation
.payload_json
.get("parameters")
@@ -290,48 +234,63 @@ fn observation_projection(
}
fn static_projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == FEATURE_SURFACE && contains(FEATURE_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_FEATURE_SURFACE
&& contains(crate::MT_LIFECYCLE_FEATURE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::FeatureGate);
}
if surface_code == ADDRESS_LOOKUP_TABLE_SURFACE
&& contains(ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code)
if surface_code == crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE
&& contains(crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::AddressLookupTable);
}
if (surface_code == BPF_LOADER_DEPRECATED_SURFACE || surface_code == BPF_LOADER_SURFACE)
&& contains(IMMUTABLE_LOADER_ENTRIES, entry_code)
if (surface_code == crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE
|| surface_code == crate::MT_LIFECYCLE_BPF_LOADER_SURFACE)
&& contains(crate::MT_LIFECYCLE_IMMUTABLE_LOADER_ENTRIES, entry_code)
{
let loader_surface = if surface_code == BPF_LOADER_DEPRECATED_SURFACE {
BPF_LOADER_DEPRECATED_SURFACE
let loader_surface = if surface_code == crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE {
crate::MT_LIFECYCLE_BPF_LOADER_DEPRECATED_SURFACE
} else {
BPF_LOADER_SURFACE
crate::MT_LIFECYCLE_BPF_LOADER_SURFACE
};
return std::option::Option::Some(ProjectionKind::ProgramLoader(loader_surface));
}
if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(UPGRADEABLE_LOADER_ENTRIES, entry_code)
if surface_code == crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE
&& contains(crate::MT_LIFECYCLE_UPGRADEABLE_LOADER_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoader(
BPF_LOADER_UPGRADEABLE_SURFACE,
crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE,
));
}
if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::ProgramLoader(LOADER_V4_SURFACE));
if surface_code == crate::MT_LIFECYCLE_LOADER_V4_SURFACE
&& contains(crate::MT_LIFECYCLE_LOADER_V4_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ProgramLoader(
crate::MT_LIFECYCLE_LOADER_V4_SURFACE,
));
}
if surface_code == SLASHING_SURFACE && contains(SLASHING_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_SLASHING_SURFACE
&& contains(crate::MT_LIFECYCLE_SLASHING_ENTRIES, entry_code)
{
return if entry_code == "duplicate_block_proof" {
std::option::Option::Some(ProjectionKind::SlashingViolationReportInitialization)
} else {
std::option::Option::Some(ProjectionKind::SlashingViolationReportClosure)
};
}
if surface_code == SYSTEM_SURFACE && contains(SYSTEM_ACCOUNT_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_SYSTEM_SURFACE
&& contains(crate::MT_LIFECYCLE_SYSTEM_ACCOUNT_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::SystemAccountLifecycle);
}
if surface_code == SYSTEM_SURFACE && contains(SYSTEM_NONCE_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_SYSTEM_SURFACE
&& contains(crate::MT_LIFECYCLE_SYSTEM_NONCE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::DurableNonceAccount);
}
if surface_code == ZK_ELGAMAL_PROOF_SURFACE && contains(ZK_ELGAMAL_CLOSE_ENTRIES, entry_code) {
if surface_code == crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE
&& contains(crate::MT_LIFECYCLE_ZK_ELGAMAL_CLOSE_ENTRIES, entry_code)
{
return std::option::Option::Some(ProjectionKind::ZkProofContextClosure);
}
return std::option::Option::None;
@@ -539,7 +498,7 @@ mod tests {
transaction_failed: bool,
) -> crate::DcApiDecodedObservation {
return observation_with_payload(
super::SYSTEM_SURFACE,
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
entry_code,
family,
transaction_failed,
@@ -579,7 +538,7 @@ mod tests {
)
};
return observation_with_payload(
super::SLASHING_SURFACE,
crate::MT_LIFECYCLE_SLASHING_SURFACE,
entry_code,
family,
transaction_failed,
@@ -620,7 +579,7 @@ mod tests {
crate::MdEventFamily::Audit
};
return observation_with_payload(
super::ZK_ELGAMAL_PROOF_SURFACE,
crate::MT_LIFECYCLE_ZK_ELGAMAL_PROOF_SURFACE,
entry_code,
family,
transaction_failed,
@@ -637,7 +596,11 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "create_lookup_table", false),
&observation(
crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE,
"create_lookup_table",
false,
),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
@@ -651,7 +614,11 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::BPF_LOADER_UPGRADEABLE_SURFACE, "deploy_with_max_data_len", false),
&observation(
crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE,
"deploy_with_max_data_len",
false,
),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(
@@ -661,7 +628,7 @@ mod tests {
assert_eq!(result.outputs[0].payload_json["domain"], "program_loader");
assert_eq!(
result.outputs[0].payload_json["loaderSurface"],
super::BPF_LOADER_UPGRADEABLE_SURFACE
crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE
);
}
@@ -670,7 +637,7 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::FEATURE_SURFACE, "revoke_pending_activation", false),
&observation(crate::MT_LIFECYCLE_FEATURE_SURFACE, "revoke_pending_activation", false),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].output_key, "feature_gate:revoke_pending_activation:0");
@@ -696,7 +663,7 @@ mod tests {
),
] {
let observation = observation_with_payload(
super::SYSTEM_SURFACE,
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
operation,
crate::MdEventFamily::Lifecycle,
false,
@@ -724,7 +691,7 @@ mod tests {
fn system_account_creation_does_not_duplicate_balance_changes() {
let materializer = crate::MtLifecycleMaterializer;
let observation = observation_with_payload(
super::SYSTEM_SURFACE,
crate::MT_LIFECYCLE_SYSTEM_SURFACE,
"create_account",
crate::MdEventFamily::Lifecycle,
false,
@@ -895,7 +862,8 @@ mod tests {
fn loader_write_and_admin_events_are_not_lifecycle_projections() {
let materializer = crate::MtLifecycleMaterializer;
for entry_code in ["write", "set_authority", "transfer_authority"] {
let observation = observation(super::BPF_LOADER_UPGRADEABLE_SURFACE, entry_code, false);
let observation =
observation(crate::MT_LIFECYCLE_BPF_LOADER_UPGRADEABLE_SURFACE, entry_code, false);
assert!(!crate::MtApiEventMaterializer::accepts_observation(
&materializer,
&observation,
@@ -908,7 +876,7 @@ mod tests {
let materializer = crate::MtLifecycleMaterializer;
let result = crate::MtApiEventMaterializer::materialize(
&materializer,
&observation(super::SYSTEM_SURFACE, "transfer", false),
&observation(crate::MT_LIFECYCLE_SYSTEM_SURFACE, "transfer", false),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
@@ -934,8 +902,12 @@ mod tests {
fn failed_native_lifecycle_is_refused() {
let materializer = crate::MtLifecycleMaterializer;
let observations = [
observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "close_lookup_table", true),
observation(super::LOADER_V4_SURFACE, "deploy", true),
observation(
crate::MT_LIFECYCLE_ADDRESS_LOOKUP_TABLE_SURFACE,
"close_lookup_table",
true,
),
observation(crate::MT_LIFECYCLE_LOADER_V4_SURFACE, "deploy", true),
nonce_observation("authorize_nonce_account", crate::MdEventFamily::Admin, true),
zk_observation("verify_zero_ciphertext", true, true),
slashing_observation("duplicate_block_proof", true),

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/liquidity.rs
// version: 3
// version: 4
//! `liquidity` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved liquidity materializer.
pub use self::core::MtLiquidityMaterializer;
pub use self::materializer::MtLiquidityMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_LIQUIDITY_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `liquidity` materializer.
pub(crate) use self::constants::MT_LIQUIDITY_COMPONENT_NAME;
/// Stable processor name for the `liquidity` materializer.
pub(crate) use self::constants::MT_LIQUIDITY_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/liquidity/constants.rs
// version: 1
//! Local constants for the liquidity materializer.
/// Stable runtime component name for the liquidity materializer.
pub(crate) const MT_LIQUIDITY_COMPONENT_NAME: &str = "kb-lib.materializer.liquidity";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_LIQUIDITY_PROCESSOR_NAME: &str = "materializer.liquidity";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_LIQUIDITY_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/liquidity/core.rs
// version: 4
// file: kb-lib/src/materializer/liquidity/materializer.rs
// version: 5
//! Reserved liquidity materializer.
@@ -9,7 +9,7 @@ pub struct MtLiquidityMaterializer;
impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.liquidity";
return crate::MT_LIQUIDITY_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtLiquidityMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Liquidity,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtLiquidityMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.liquidity".to_string(),
name: crate::MT_LIQUIDITY_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_LIQUIDITY_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,31 +1,52 @@
// file: kb-lib/src/materializer/metadata.rs
// version: 5
// version: 8
//! `metadata` materializer family.
mod core;
mod metaplex_token_metadata;
mod solana_program_metadata;
mod token_2022;
/// Token-2022, Metaplex and Solana Program Metadata materializer.
pub use self::core::MtMetadataMaterializer;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::core::materializer_metadata_materialize_metaplex_account_snapshot;
pub use self::metaplex_token_metadata::materializer_metadata_materialize_metaplex_account_snapshot;
/// Materializes one canonical Metaplex Metadata account snapshot.
pub use self::core::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::core::materializer_metadata_materialize_token_2022_snapshot;
pub use self::metaplex_token_metadata::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// Compatibility materializer retaining the historical generic metadata type name.
pub use self::solana_program_metadata::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::solana_program_metadata::MtSolanaProgramMetadataMaterializer;
/// Materializes one decoded Solana Program Metadata account snapshot.
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::token_2022::materializer_metadata_materialize_token_2022_snapshot;
/// Stable runtime component name for Metaplex Token Metadata projections.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Stable processor name for Metaplex Token Metadata projections.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Stable Metaplex Token Metadata projection version.
pub(crate) use self::metaplex_token_metadata::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Metaplex Token Metadata materialization.
pub(crate) use self::metaplex_token_metadata::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_ACCEPTED_FAMILIES;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Stable runtime component name for Solana Program Metadata projections.
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_PROCESSOR_NAME;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_PROJECTION_VERSION;
pub(crate) use self::solana_program_metadata::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
/// Stable runtime component name for Token-2022 embedded metadata projections.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Stable processor name for Token-2022 embedded metadata projections.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Stable Token-2022 embedded metadata projection version.
pub(crate) use self::token_2022::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Canonical tracing target for Token-2022 embedded metadata materialization.
pub(crate) use self::token_2022::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;

View File

@@ -1,608 +0,0 @@
// file: kb-lib/src/materializer/metadata/core.rs
// version: 7
//! Stable Token-2022, Metaplex and Solana Program Metadata materializer.
/// Initial materializer implementation placeholder.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataMaterializer;
impl crate::MtMaterializer for crate::MtMetadataMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.metadata";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Metadata
&& event.program_id.0 == kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
&& event.surface_code.0 == crate::DC_METADATA_SPM_SURFACE_CODE
&& crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES
.iter()
.any(|(_, code)| return *code == event.event_name.0.as_str());
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
let materialized = crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Metadata,
};
return std::result::Result::Ok(std::vec![materialized]);
}
}
/// Materialize embedded Token-2022 metadata, group, and group-member extensions.
pub fn materializer_metadata_materialize_token_2022_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<std::vec::Vec<crate::MtApiMaterializedOutput>, String> {
if account_key.is_empty() {
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a non-empty account key".to_string(),
);
}
if state.kind != crate::DcToken2022StateKind::Mint {
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a parsed mint state".to_string(),
);
}
let mut outputs = std::vec::Vec::new();
for entry in &state.extensions {
let projection_kind = match entry.extension_name {
"token_metadata" => std::option::Option::Some("token_metadata"),
"token_group" => std::option::Option::Some("token_group"),
"token_group_member" => std::option::Option::Some("token_group_member"),
_ => std::option::Option::None,
};
let kind = match projection_kind {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
outputs.push(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_metadata_snapshot:{account_key}:{slot}:{kind}"),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": 1,
"domain": "spl_token_2022_embedded_metadata",
"projectionSemantics": "authoritative_bounded_mint_extension_snapshot",
"idempotenceKey": format!("token_2022_metadata:{account_key}:{slot}:{kind}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"metadataSource": "token_2022_embedded_tlv",
"projectionKind": kind,
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
"externalUriFetched": false,
"identityAuthority": "mint_address",
"finalAccountStateCaptured": true,
"provenance": {
"processorName": "kb-lib.materializer.metadata",
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
return std::result::Result::Ok(outputs);
}
/// Materialize one canonical Metaplex `MetadataV1` account snapshot.
///
/// Account snapshots are authoritative for current state. Instruction observations may describe
/// mutations, but must not emit a second competing metadata-state projection.
pub fn materializer_metadata_materialize_metaplex_metadata_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
return std::result::Result::Err(
"Metaplex metadata state materialization requires committed provenance".to_string(),
);
}
if snapshot.identity.materialization
!= crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState
{
return std::result::Result::Err(
"Metaplex MetadataV1 materialization requires active-state policy".to_string(),
);
}
let metadata = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(value) => value,
_ => {
return std::result::Result::Err(
"Metaplex metadata materializer received another account layout".to_string(),
);
},
};
let slot = snapshot.provenance.slot.map(|value| return value.to_string());
let write_version = snapshot.provenance.write_version.map(|value| return value.to_string());
let payload = serde_json::json!({
"projectionVersion": 1,
"domain": "metaplex_token_metadata",
"projectionSemantics": "authoritative_committed_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": metadata.account,
"mint": metadata.mint,
"updateAuthority": metadata.update_authority,
"name": metadata.name,
"symbol": metadata.symbol,
"uri": metadata.uri,
"sellerFeeBasisPoints": metadata.seller_fee_basis_points,
"creators": metadata.payload_json.get("creators").cloned().unwrap_or(serde_json::Value::Null),
"collection": metadata.payload_json.get("collection").cloned().unwrap_or(serde_json::Value::Null),
"uses": metadata.payload_json.get("uses").cloned().unwrap_or(serde_json::Value::Null),
"tokenStandard": metadata.payload_json.get("tokenStandard").cloned().unwrap_or(serde_json::Value::Null),
"programmableConfig": metadata.payload_json.get("programmableConfig").cloned().unwrap_or(serde_json::Value::Null),
"primarySaleHappened": metadata.primary_sale_happened,
"isMutable": metadata.is_mutable,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": "active",
"materializationPolicy": "active_state",
"metadataSource": "metaplex_owned_account",
"accountState": metadata.payload_json,
"externalUriFetched": false,
"stateAuthority": "owned_account_snapshot",
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": slot,
"writeVersion": write_version,
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": "kb-lib.materializer.metadata",
"processorVersion": env!("CARGO_PKG_VERSION")
}
});
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("metaplex_metadata_state:{}", metadata.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: payload,
});
}
#[cfg(test)]
mod tests_metaplex_metadata_state {
pub(super) fn snapshot(
committed: bool,
) -> crate::DcMetadataMtmCanonicalMetadataAccountSnapshot {
let metadata = crate::DcMetadataMtmMetadataAccountSnapshot {
account: "metadata111".to_string(),
bump: 1,
mint: "mint111".to_string(),
update_authority: "authority111".to_string(),
name: "Asset".to_string(),
symbol: "AST".to_string(),
uri: "https://example.invalid/asset.json".to_string(),
seller_fee_basis_points: 500,
primary_sale_happened: true,
is_mutable: false,
payload_json: serde_json::json!({
"creators": [{"address":"creator111","verified":true,"share":100}],
"collection": {"verified":true,"key":"collection111"},
"uses": {"useMethod":"Single","remaining":"0","total":"1"},
"tokenStandard": "ProgrammableNonFungible",
"programmableConfig": {"ruleSet":"rules111"}
}),
};
return crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_metadata(
metadata,
crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(42),
write_version: std::option::Option::Some(7),
signature: std::option::Option::Some("signature111".to_string()),
instruction_index: std::option::Option::Some(3),
inner_instruction_index: std::option::Option::None,
committed,
},
);
}
#[test]
fn account_snapshot_is_one_stable_authoritative_metadata_projection() {
let first =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
let second =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
assert_eq!(first, second);
assert_eq!(
first.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_metadata_state:metadata111")
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("metaplex_owned_account"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["creators"].clone()),
std::result::Result::Ok(
serde_json::json!([{"address":"creator111","verified":true,"share":100}])
)
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["programmableConfig"].clone()),
std::result::Result::Ok(serde_json::json!({"ruleSet":"rules111"}))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["instructionProjectionRole"].clone()),
std::result::Result::Ok(serde_json::json!(
"mutation_observation_only_no_duplicate_state"
))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["accountState"]["creators"][0]["share"]
.clone()),
std::result::Result::Ok(serde_json::json!(100))
);
}
#[test]
fn uncommitted_or_non_metadata_state_is_rejected() {
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(false))
.is_err()
);
let mut wrong = snapshot(true);
wrong.state = crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker111".to_string(),
bump: 1,
mint: "mint111".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV1,
marker_group: std::option::Option::Some(0),
edition: 1,
byte_index: 0,
bit_mask: 64,
edition_taken: true,
ledger: std::vec![64],
payload_json: serde_json::json!({}),
},
);
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&wrong).is_err()
);
}
}
/// Materialize any canonical Metaplex account state other than `MetadataV1`.
///
/// Every account address maps to one stable state key. Historical layouts remain materializable
/// with an explicit historical lifecycle, while execution policy remains independently disabled.
pub fn materializer_metadata_materialize_metaplex_account_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
return std::result::Result::Err(
"Metaplex account-state materialization requires committed provenance".to_string(),
);
}
let (projection_kind, fact_owner, state_json) = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(_) => {
return std::result::Result::Err(
"MetadataV1 must use the dedicated metadata-state materializer".to_string(),
);
},
crate::DcMetadataMtmCanonicalMetadataAccountState::Edition(value) => {
("edition_state", "kb-lib.materializer.metadata", value.payload_json.clone())
},
crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(value) => (
"edition_marker_state",
"kb-lib.materializer.metadata",
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenRecord(value) => (
"programmable_token_state",
"kb-lib.materializer.lifecycle",
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::DelegateRecord(value) => {
("delegate_admin_state", "kb-lib.materializer.admin", value.payload_json.clone())
},
crate::DcMetadataMtmCanonicalMetadataAccountState::AuthorityRecord(value) => {
("authority_admin_state", "kb-lib.materializer.admin", value.payload_json.clone())
},
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenOwnedEscrow(value) => (
"token_owned_escrow_state",
"kb-lib.materializer.lifecycle",
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::ReservationList(value) => (
"historical_reservation_state",
"kb-lib.materializer.metadata",
value.payload_json.clone(),
),
};
let lifecycle = match snapshot.identity.lifecycle {
crate::DcMetadataMtmCanonicalAccountLifecycle::Active => "active",
crate::DcMetadataMtmCanonicalAccountLifecycle::ActiveExperimental => "active_experimental",
crate::DcMetadataMtmCanonicalAccountLifecycle::Historical => "historical",
};
let materialization_policy = match snapshot.identity.materialization {
crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState => "active_state",
crate::DcMetadataMtmCanonicalMaterializationPolicy::HistoricalState => "historical_state",
};
let execution_policy = match snapshot.identity.execution {
crate::DcMetadataMtmCanonicalExecutionPolicy::CurrentCandidate => "current_candidate",
crate::DcMetadataMtmCanonicalExecutionPolicy::ExperimentalCandidate => {
"experimental_candidate"
},
crate::DcMetadataMtmCanonicalExecutionPolicy::NeverHistorical => "never_historical",
};
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("metaplex_account_state:{}", snapshot.identity.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": 1,
"domain": "metaplex_token_metadata",
"projectionKind": projection_kind,
"projectionSemantics": "authoritative_committed_owned_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": snapshot.identity.account,
"programId": snapshot.identity.program_id,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": lifecycle,
"materializationPolicy": materialization_policy,
"executionPolicy": execution_policy,
"factOwner": fact_owner,
"metadataSource": "metaplex_owned_account",
"accountState": state_json,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": snapshot.provenance.slot.map(|value: u64| return value.to_string()),
"writeVersion": snapshot.provenance.write_version.map(|value: u64| return value.to_string()),
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": "kb-lib.materializer.metadata",
"processorVersion": env!("CARGO_PKG_VERSION")
}
}),
});
}
#[cfg(test)]
mod tests_metaplex_account_state {
fn provenance() -> crate::DcMetadataMtmCanonicalAccountProvenance {
return crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(90),
write_version: std::option::Option::Some(2),
signature: std::option::Option::None,
instruction_index: std::option::Option::None,
inner_instruction_index: std::option::Option::None,
committed: true,
};
}
#[test]
fn historical_edition_and_reservation_are_stable_materializable_state() {
let edition = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition(
crate::DcMetadataMtmEditionAccountSnapshot {
account: "master-v1".to_string(),
bump: 1,
mint: "mint-v1".to_string(),
kind: crate::DcMetadataMtmEditionAccountKind::MasterEditionV1,
supply: std::option::Option::Some(3),
max_supply: std::option::Option::Some(10),
parent: std::option::Option::None,
edition: std::option::Option::None,
printing_mint: std::option::Option::Some("printing".to_string()),
one_time_printing_authorization_mint: std::option::Option::Some(
"authorization".to_string(),
),
payload_json: serde_json::json!({"supply":"3","maxSupply":"10"}),
},
provenance(),
);
let reservation =
crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_reservation_list(
crate::DcMetadataMtmReservationListAccountSnapshot {
account: "reservation-v2".to_string(),
bump: 2,
kind: crate::DcMetadataMtmReservationListAccountKind::V2,
master_edition: "master-v1".to_string(),
resource: "resource".to_string(),
supply_snapshot: std::option::Option::Some(3),
reservations: std::vec::Vec::new(),
total_reservation_spots: 0,
current_reservation_spots: 0,
payload_json: serde_json::json!({"reservations":[]}),
},
provenance(),
);
let first = crate::materializer_metadata_materialize_metaplex_account_snapshot(&edition);
let second =
crate::materializer_metadata_materialize_metaplex_account_snapshot(&reservation);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["lifecycle"].clone()),
std::result::Result::Ok(serde_json::json!("historical"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["executionPolicy"].clone()),
std::result::Result::Ok(serde_json::json!("never_historical"))
);
assert_eq!(
second.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_account_state:reservation-v2")
);
}
#[test]
fn programmable_delegate_authority_and_escrow_keep_unique_fact_owners() {
let token = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_token_record(
crate::DcMetadataMtmTokenRecordAccountSnapshot {
account: "token-record".to_string(),
bump: 1,
mint: "mint".to_string(),
token: "token".to_string(),
stored_bump: 1,
state: "Locked".to_string(),
rule_set_revision: std::option::Option::Some(4),
delegate: std::option::Option::Some("delegate".to_string()),
delegate_role: std::option::Option::Some("Transfer".to_string()),
locked_transfer: std::option::Option::Some("destination".to_string()),
locked: true,
payload_json: serde_json::json!({"state":"Locked"}),
},
provenance(),
);
let output = crate::materializer_metadata_materialize_metaplex_account_snapshot(&token);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["factOwner"].clone()),
std::result::Result::Ok(serde_json::json!("kb-lib.materializer.lifecycle"))
);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("programmable_token_state"))
);
}
#[test]
fn uncommitted_and_metadata_snapshots_use_explicit_boundaries() {
let mut marker = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition_marker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker".to_string(),
bump: 1,
mint: "mint".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV2,
marker_group: std::option::Option::None,
edition: 8,
byte_index: 1,
bit_mask: 128,
edition_taken: true,
ledger: std::vec![0, 128],
payload_json: serde_json::json!({"editionTaken":true}),
},
provenance(),
);
marker.provenance.committed = false;
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(&marker).is_err()
);
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(
&super::tests_metaplex_metadata_state::snapshot(true)
)
.is_err()
);
}
}
#[cfg(test)]
mod tests_token_2022_state {
#[test]
fn embedded_metadata_group_and_member_have_distinct_stable_outputs() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 19,
extension_name: "token_metadata",
value_hex: "01".to_string(),
value_fields: serde_json::json!({"name":"Token","symbol":"TOK","uri":"https://example.invalid/token.json"}),
},
crate::DcToken2022TlvEntry {
extension_type: 20,
extension_name: "token_group",
value_hex: "02".to_string(),
value_fields: serde_json::json!({"mint":"mint111","size":"1","maxSize":"10"}),
},
crate::DcToken2022TlvEntry {
extension_type: 21,
extension_name: "token_group_member",
value_hex: "03".to_string(),
value_fields: serde_json::json!({"mint":"mint111","group":"group111","memberNumber":"1"}),
},
],
};
let outputs =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(outputs.as_ref().map(|values| return values.len()), std::result::Result::Ok(3));
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("token_2022_embedded_tlv"))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["externalUriFetched"].clone()),
std::result::Result::Ok(serde_json::json!(false))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[1].payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("token_group"))
);
}
#[test]
fn snapshot_is_idempotent_filters_unrelated_extensions_and_requires_mint_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "00".to_string(),
value_fields: serde_json::json!({}),
}],
};
let first =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
let second =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(first, second);
assert_eq!(first.as_ref().map(|values| return values.len()), std::result::Result::Ok(0));
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot("", 9, &state).is_err()
);
let account_state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot(
"account111",
9,
&account_state
)
.is_err()
);
}
}

View File

@@ -0,0 +1,22 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
// version: 2
//! Metaplex Token Metadata account-state materializer.
mod account;
mod constants;
mod state;
/// Materializes one canonical non-Metadata Metaplex account snapshot.
pub use self::account::materializer_metadata_materialize_metaplex_account_snapshot;
/// Materializes one canonical Metaplex Metadata account snapshot.
pub use self::state::materializer_metadata_materialize_metaplex_metadata_snapshot;
/// Stable runtime component name for Metaplex Token Metadata projections.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME;
/// Stable processor name for Metaplex Token Metadata projections.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME;
/// Stable Metaplex Token Metadata projection version.
pub(crate) use self::constants::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Metaplex Token Metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA;

View File

@@ -0,0 +1,283 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/account.rs
// version: 5
//! Authoritative non-Metadata Metaplex account-state materialization.
/// Materialize any canonical Metaplex account state other than `MetadataV1`.
///
/// Every account address maps to one stable state key. Historical layouts remain materializable
/// with an explicit historical lifecycle, while execution policy remains independently disabled.
pub fn materializer_metadata_materialize_metaplex_account_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_account_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %snapshot.identity.account.as_str(),
committed = false,
status = "refused",
error_code = "uncommitted_metaplex_account_snapshot",
"refuse uncommitted Metaplex account snapshot"
);
return std::result::Result::Err(
"Metaplex account-state materialization requires committed provenance".to_string(),
);
}
let (projection_kind, fact_owner, state_json) = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(_) => {
return std::result::Result::Err(
"MetadataV1 must use the dedicated metadata-state materializer".to_string(),
);
},
crate::DcMetadataMtmCanonicalMetadataAccountState::Edition(value) => (
"edition_state",
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(value) => (
"edition_marker_state",
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenRecord(value) => (
"programmable_token_state",
crate::MT_LIFECYCLE_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::DelegateRecord(value) => (
"delegate_admin_state",
crate::MT_ADMIN_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::AuthorityRecord(value) => (
"authority_admin_state",
crate::MT_ADMIN_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::TokenOwnedEscrow(value) => (
"token_owned_escrow_state",
crate::MT_LIFECYCLE_COMPONENT_NAME,
value.payload_json.clone(),
),
crate::DcMetadataMtmCanonicalMetadataAccountState::ReservationList(value) => (
"historical_reservation_state",
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
value.payload_json.clone(),
),
};
let lifecycle = match snapshot.identity.lifecycle {
crate::DcMetadataMtmCanonicalAccountLifecycle::Active => "active",
crate::DcMetadataMtmCanonicalAccountLifecycle::ActiveExperimental => "active_experimental",
crate::DcMetadataMtmCanonicalAccountLifecycle::Historical => "historical",
};
let materialization_policy = match snapshot.identity.materialization {
crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState => "active_state",
crate::DcMetadataMtmCanonicalMaterializationPolicy::HistoricalState => "historical_state",
};
let execution_policy = match snapshot.identity.execution {
crate::DcMetadataMtmCanonicalExecutionPolicy::CurrentCandidate => "current_candidate",
crate::DcMetadataMtmCanonicalExecutionPolicy::ExperimentalCandidate => {
"experimental_candidate"
},
crate::DcMetadataMtmCanonicalExecutionPolicy::NeverHistorical => "never_historical",
};
let output = crate::MtApiMaterializedOutput {
output_key: format!("metaplex_account_state:{}", snapshot.identity.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION,
"domain": "metaplex_token_metadata",
"projectionKind": projection_kind,
"projectionSemantics": "authoritative_committed_owned_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": snapshot.identity.account,
"programId": snapshot.identity.program_id,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": lifecycle,
"materializationPolicy": materialization_policy,
"executionPolicy": execution_policy,
"factOwner": fact_owner,
"metadataSource": "metaplex_owned_account",
"accountState": state_json,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": snapshot.provenance.slot.map(|value: u64| return value.to_string()),
"writeVersion": snapshot.provenance.write_version.map(|value: u64| return value.to_string()),
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION")
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_account_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %snapshot.identity.account.as_str(),
projection_kind,
fact_owner,
slot = ?snapshot.provenance.slot,
status = "inserted",
"materialize committed Metaplex account snapshot"
);
return std::result::Result::Ok(output);
}
#[cfg(test)]
mod tests_metaplex_account_state {
fn metadata_snapshot() -> crate::DcMetadataMtmCanonicalMetadataAccountSnapshot {
return crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_metadata(
crate::DcMetadataMtmMetadataAccountSnapshot {
account: "metadata-boundary".to_string(),
bump: 1,
mint: "mint-boundary".to_string(),
update_authority: "authority-boundary".to_string(),
name: "Boundary".to_string(),
symbol: "BND".to_string(),
uri: "https://example.invalid/boundary.json".to_string(),
seller_fee_basis_points: 0,
primary_sale_happened: false,
is_mutable: true,
payload_json: serde_json::json!({}),
},
provenance(),
);
}
fn provenance() -> crate::DcMetadataMtmCanonicalAccountProvenance {
return crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(90),
write_version: std::option::Option::Some(2),
signature: std::option::Option::None,
instruction_index: std::option::Option::None,
inner_instruction_index: std::option::Option::None,
committed: true,
};
}
#[test]
fn historical_edition_and_reservation_are_stable_materializable_state() {
let edition = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition(
crate::DcMetadataMtmEditionAccountSnapshot {
account: "master-v1".to_string(),
bump: 1,
mint: "mint-v1".to_string(),
kind: crate::DcMetadataMtmEditionAccountKind::MasterEditionV1,
supply: std::option::Option::Some(3),
max_supply: std::option::Option::Some(10),
parent: std::option::Option::None,
edition: std::option::Option::None,
printing_mint: std::option::Option::Some("printing".to_string()),
one_time_printing_authorization_mint: std::option::Option::Some(
"authorization".to_string(),
),
payload_json: serde_json::json!({"supply":"3","maxSupply":"10"}),
},
provenance(),
);
let reservation =
crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_reservation_list(
crate::DcMetadataMtmReservationListAccountSnapshot {
account: "reservation-v2".to_string(),
bump: 2,
kind: crate::DcMetadataMtmReservationListAccountKind::V2,
master_edition: "master-v1".to_string(),
resource: "resource".to_string(),
supply_snapshot: std::option::Option::Some(3),
reservations: std::vec::Vec::new(),
total_reservation_spots: 0,
current_reservation_spots: 0,
payload_json: serde_json::json!({"reservations":[]}),
},
provenance(),
);
let first = crate::materializer_metadata_materialize_metaplex_account_snapshot(&edition);
let second =
crate::materializer_metadata_materialize_metaplex_account_snapshot(&reservation);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["lifecycle"].clone()),
std::result::Result::Ok(serde_json::json!("historical"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["executionPolicy"].clone()),
std::result::Result::Ok(serde_json::json!("never_historical"))
);
assert_eq!(
second.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_account_state:reservation-v2")
);
}
#[test]
fn programmable_delegate_authority_and_escrow_keep_unique_fact_owners() {
let token = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_token_record(
crate::DcMetadataMtmTokenRecordAccountSnapshot {
account: "token-record".to_string(),
bump: 1,
mint: "mint".to_string(),
token: "token".to_string(),
stored_bump: 1,
state: "Locked".to_string(),
rule_set_revision: std::option::Option::Some(4),
delegate: std::option::Option::Some("delegate".to_string()),
delegate_role: std::option::Option::Some("Transfer".to_string()),
locked_transfer: std::option::Option::Some("destination".to_string()),
locked: true,
payload_json: serde_json::json!({"state":"Locked"}),
},
provenance(),
);
let output = crate::materializer_metadata_materialize_metaplex_account_snapshot(&token);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["factOwner"].clone()),
std::result::Result::Ok(serde_json::json!(crate::MT_LIFECYCLE_COMPONENT_NAME))
);
assert_eq!(
output.as_ref().map(|value| return value.payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("programmable_token_state"))
);
}
#[test]
fn uncommitted_and_metadata_snapshots_use_explicit_boundaries() {
let mut marker = crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_edition_marker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker".to_string(),
bump: 1,
mint: "mint".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV2,
marker_group: std::option::Option::None,
edition: 8,
byte_index: 1,
bit_mask: 128,
edition_taken: true,
ledger: std::vec![0, 128],
payload_json: serde_json::json!({"editionTaken":true}),
},
provenance(),
);
marker.provenance.committed = false;
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(&marker).is_err()
);
assert!(
crate::materializer_metadata_materialize_metaplex_account_snapshot(
&metadata_snapshot()
)
.is_err()
);
}
}

View File

@@ -0,0 +1,16 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/constants.rs
// version: 4
//! Local constants for the metadata metaplex_token_metadata materializer.
/// Stable runtime component name for the metadata metaplex_token_metadata materializer.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME: &str =
"kb-lib.materializer.metadata.metaplex_token_metadata";
/// Stable processor name persisted by metadata projections.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME: &str =
"materializer.metadata.metaplex_token_metadata";
/// Stable projection contract version.
pub(crate) const MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the metadata metaplex_token_metadata materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA: &str =
"kb-lib.materializer.metadata.metaplex_token_metadata";

View File

@@ -0,0 +1,229 @@
// file: kb-lib/src/materializer/metadata/metaplex_token_metadata/state.rs
// version: 5
//! Authoritative Metaplex MetadataV1 state materialization.
/// Materialize one canonical Metaplex `MetadataV1` account snapshot.
///
/// Account snapshots are authoritative for current state. Instruction observations may describe
/// mutations, but must not emit a second competing metadata-state projection.
pub fn materializer_metadata_materialize_metaplex_metadata_snapshot(
snapshot: &crate::DcMetadataMtmCanonicalMetadataAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if !snapshot.provenance.committed {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_metadata_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %snapshot.identity.account.as_str(),
committed = false,
status = "refused",
error_code = "uncommitted_metaplex_metadata_snapshot",
"refuse uncommitted Metaplex metadata snapshot"
);
return std::result::Result::Err(
"Metaplex metadata state materialization requires committed provenance".to_string(),
);
}
if snapshot.identity.materialization
!= crate::DcMetadataMtmCanonicalMaterializationPolicy::ActiveState
{
return std::result::Result::Err(
"Metaplex MetadataV1 materialization requires active-state policy".to_string(),
);
}
let metadata = match &snapshot.state {
crate::DcMetadataMtmCanonicalMetadataAccountState::Metadata(value) => value,
_ => {
return std::result::Result::Err(
"Metaplex metadata materializer received another account layout".to_string(),
);
},
};
let slot = snapshot.provenance.slot.map(|value| return value.to_string());
let write_version = snapshot.provenance.write_version.map(|value| return value.to_string());
let payload = serde_json::json!({
"projectionVersion": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROJECTION_VERSION,
"domain": "metaplex_token_metadata",
"projectionSemantics": "authoritative_committed_account_snapshot",
"stableId": snapshot.identity.stable_id,
"account": metadata.account,
"mint": metadata.mint,
"updateAuthority": metadata.update_authority,
"name": metadata.name,
"symbol": metadata.symbol,
"uri": metadata.uri,
"sellerFeeBasisPoints": metadata.seller_fee_basis_points,
"creators": json_field_or_null(&metadata.payload_json, "creators"),
"collection": json_field_or_null(&metadata.payload_json, "collection"),
"uses": json_field_or_null(&metadata.payload_json, "uses"),
"tokenStandard": json_field_or_null(&metadata.payload_json, "tokenStandard"),
"programmableConfig": json_field_or_null(&metadata.payload_json, "programmableConfig"),
"primarySaleHappened": metadata.primary_sale_happened,
"isMutable": metadata.is_mutable,
"layout": snapshot.identity.layout,
"layoutDiscriminant": snapshot.identity.discriminant,
"canonicalVersion": snapshot.identity.canonical_version,
"lifecycle": "active",
"materializationPolicy": "active_state",
"metadataSource": "metaplex_owned_account",
"accountState": metadata.payload_json,
"externalUriFetched": false,
"stateAuthority": "owned_account_snapshot",
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"source": format!("{:?}", snapshot.provenance.source),
"slot": slot,
"writeVersion": write_version,
"signature": snapshot.provenance.signature,
"instructionIndex": snapshot.provenance.instruction_index,
"innerInstructionIndex": snapshot.provenance.inner_instruction_index,
"committed": snapshot.provenance.committed,
"processorName": crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION")
}
});
let output = crate::MtApiMaterializedOutput {
output_key: format!("metaplex_metadata_state:{}", metadata.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: payload,
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_METAPLEX_TOKEN_METADATA,
action = "materialize_metaplex_metadata_snapshot",
component_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_COMPONENT_NAME,
processor_name = crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account = %metadata.account.as_str(),
mint = %metadata.mint.as_str(),
slot = ?snapshot.provenance.slot,
status = "inserted",
"materialize committed Metaplex MetadataV1 snapshot"
);
return std::result::Result::Ok(output);
}
fn json_field_or_null(value: &serde_json::Value, field: &str) -> serde_json::Value {
return match value.get(field) {
std::option::Option::Some(value) => value.clone(),
std::option::Option::None => serde_json::Value::Null,
};
}
#[cfg(test)]
mod tests_metaplex_metadata_state {
fn snapshot(committed: bool) -> crate::DcMetadataMtmCanonicalMetadataAccountSnapshot {
let metadata = crate::DcMetadataMtmMetadataAccountSnapshot {
account: "metadata111".to_string(),
bump: 1,
mint: "mint111".to_string(),
update_authority: "authority111".to_string(),
name: "Asset".to_string(),
symbol: "AST".to_string(),
uri: "https://example.invalid/asset.json".to_string(),
seller_fee_basis_points: 500,
primary_sale_happened: true,
is_mutable: false,
payload_json: serde_json::json!({
"creators": [{"address":"creator111","verified":true,"share":100}],
"collection": {"verified":true,"key":"collection111"},
"uses": {"useMethod":"Single","remaining":"0","total":"1"},
"tokenStandard": "ProgrammableNonFungible",
"programmableConfig": {"ruleSet":"rules111"}
}),
};
return crate::DcMetadataMtmCanonicalMetadataAccountSnapshot::from_metadata(
metadata,
crate::DcMetadataMtmCanonicalAccountProvenance {
source: crate::DcMetadataMtmCanonicalObservationSource::Backfill,
slot: std::option::Option::Some(42),
write_version: std::option::Option::Some(7),
signature: std::option::Option::Some("signature111".to_string()),
instruction_index: std::option::Option::Some(3),
inner_instruction_index: std::option::Option::None,
committed,
},
);
}
#[test]
fn account_snapshot_is_one_stable_authoritative_metadata_projection() {
let first =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
let second =
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(true));
assert_eq!(first, second);
assert_eq!(
first.as_ref().map(|value| return value.output_key.as_str()),
std::result::Result::Ok("metaplex_metadata_state:metadata111")
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("metaplex_owned_account"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["creators"].clone()),
std::result::Result::Ok(
serde_json::json!([{"address":"creator111","verified":true,"share":100}])
)
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["programmableConfig"].clone()),
std::result::Result::Ok(serde_json::json!({"ruleSet":"rules111"}))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["instructionProjectionRole"].clone()),
std::result::Result::Ok(serde_json::json!(
"mutation_observation_only_no_duplicate_state"
))
);
assert_eq!(
first.as_ref().map(|value| {
return value.payload_json["provenance"]["processorName"].clone();
}),
std::result::Result::Ok(serde_json::json!(
crate::MT_METADATA_METAPLEX_TOKEN_METADATA_PROCESSOR_NAME
))
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["accountState"]["creators"][0]["share"]
.clone()),
std::result::Result::Ok(serde_json::json!(100))
);
}
#[test]
fn uncommitted_or_non_metadata_state_is_rejected() {
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&snapshot(false))
.is_err()
);
let mut wrong = snapshot(true);
wrong.state = crate::DcMetadataMtmCanonicalMetadataAccountState::EditionMarker(
crate::DcMetadataMtmEditionMarkerAccountSnapshot {
account: "marker111".to_string(),
bump: 1,
mint: "mint111".to_string(),
kind: crate::DcMetadataMtmEditionMarkerAccountKind::EditionMarkerV1,
marker_group: std::option::Option::Some(0),
edition: 1,
byte_index: 0,
bit_mask: 64,
edition_taken: true,
ledger: std::vec![64],
payload_json: serde_json::json!({}),
},
);
assert!(
crate::materializer_metadata_materialize_metaplex_metadata_snapshot(&wrong).is_err()
);
}
}

View File

@@ -1,12 +1,16 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata.rs
// version: 1
// version: 2
//! Solana Program Metadata materialization surface.
mod constants;
mod instruction;
mod materializer;
mod state;
/// Compatibility materializer retaining the historical generic metadata type name.
pub use self::materializer::MtMetadataMaterializer;
/// Stable Solana Program Metadata instruction materializer.
pub use self::materializer::MtSolanaProgramMetadataMaterializer;
/// Materializes one decoded Solana Program Metadata account snapshot.
pub use self::state::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
@@ -15,10 +19,12 @@ pub use self::state::materializer_metadata_materialize_solana_program_metadata_b
pub use self::state::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) use self::constants::MT_METADATA_SPM_ACCEPTED_FAMILIES;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
/// Stable runtime component name for Solana Program Metadata projections.
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
/// Stable processor name for Solana Program Metadata projections.
pub(crate) use self::constants::MT_METADATA_SPM_PROCESSOR_NAME;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME;
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) use self::constants::MT_METADATA_SPM_PROJECTION_VERSION;
pub(crate) use self::constants::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION;
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;

View File

@@ -1,16 +1,18 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/constants.rs
// version: 1
// version: 4
//! Solana Program Metadata materializer constants.
//! Local constants for the Solana Program Metadata materializer.
/// Event families accepted by the Solana Program Metadata instruction materializer.
pub(crate) const MT_METADATA_SPM_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Metadata];
/// Stable processor name for Solana Program Metadata projections.
pub(crate) const MT_METADATA_SPM_PROCESSOR_NAME: &str =
/// Stable runtime component name for Solana Program Metadata materialization.
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME: &str =
"kb-lib.materializer.metadata.solana_program_metadata";
/// Stable processor name persisted by Solana Program Metadata projections.
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME: &str =
"materializer.metadata.solana_program_metadata";
/// Stable projection schema version for Solana Program Metadata outputs.
pub(crate) const MT_METADATA_SPM_PROJECTION_VERSION: u16 = 1;
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION: u32 = 1;
pub(crate) const MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Metadata];
/// Canonical tracing target for Solana Program Metadata materialization.
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA: &str =
"kb-lib.materializer.metadata.solana_program_metadata";

View File

@@ -1,18 +1,128 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/instruction.rs
// version: 1
// file: kb-lib/src/materializer/metadata/solana_program_metadata/materializer.rs
// version: 4
//! Committed Solana Program Metadata instruction projections.
/// Stable Solana Program Metadata instruction materializer.
#[derive(Clone, Debug, Default)]
pub struct MtSolanaProgramMetadataMaterializer;
/// Compatibility materializer retaining the historical generic metadata type name.
#[derive(Clone, Debug, Default)]
pub struct MtMetadataMaterializer;
impl crate::MtMaterializer for crate::MtSolanaProgramMetadataMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Metadata
&& event.program_id.0 == kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
&& event.surface_code.0 == crate::DC_METADATA_SPM_SURFACE_CODE
&& crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES
.iter()
.any(|(_, code)| return *code == event.event_name.0.as_str());
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
if !crate::MtMaterializer::accepts_event(self, event) {
return std::result::Result::Ok(std::vec::Vec::new());
}
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Metadata,
}]);
}
}
impl crate::MtMaterializer for crate::MtMetadataMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return crate::MtMaterializer::accepts_event(
&crate::MtSolanaProgramMetadataMaterializer,
event,
);
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return crate::MtMaterializer::materialize_event(
&crate::MtSolanaProgramMetadataMaterializer,
event,
);
}
}
impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiEventMaterializer::identity(
&crate::MtSolanaProgramMetadataMaterializer,
);
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MtApiEventMaterializer::accepted_families(
&crate::MtSolanaProgramMetadataMaterializer,
);
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return crate::MtApiEventMaterializer::accepts_observation(
&crate::MtSolanaProgramMetadataMaterializer,
observation,
);
}
fn transaction_policy(
&self,
family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiEventMaterializer::transaction_policy(
&crate::MtSolanaProgramMetadataMaterializer,
family,
);
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiEventMaterializer::materialize(
&crate::MtSolanaProgramMetadataMaterializer,
observation,
);
}
}
impl crate::MtApiEventMaterializer for crate::MtSolanaProgramMetadataMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_METADATA_SPM_PROCESSOR_NAME.to_string(),
name: crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_METADATA_SPM_ACCEPTED_FAMILIES;
return crate::MT_METADATA_SOLANA_PROGRAM_METADATA_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -55,7 +165,7 @@ impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "committed_instruction_mutation",
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
@@ -73,7 +183,7 @@ impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
"externalContentFetched": false,
"externalAccountRead": false,
"provenance": {
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"decoderProof": observation.proof.clone()
}
@@ -141,18 +251,18 @@ fn instruction_projection(operation: &str, payload: &serde_json::Value) -> serde
}
fn state_transition(operation: &str) -> &'static str {
match operation {
"write" => return "buffer_content_written",
"initialize" => return "metadata_account_initialized",
"set_authority" => return "account_authority_updated",
"set_data" => return "metadata_data_configuration_updated",
"set_immutable" => return "metadata_account_marked_immutable",
"trim" => return "account_capacity_trimmed",
"close" => return "account_closed",
"allocate" => return "buffer_account_allocated",
"extend" => return "account_capacity_extended",
_ => return "unknown",
}
return match operation {
"write" => "buffer_content_written",
"initialize" => "metadata_account_initialized",
"set_authority" => "account_authority_updated",
"set_data" => "metadata_data_configuration_updated",
"set_immutable" => "metadata_account_marked_immutable",
"trim" => "account_capacity_trimmed",
"close" => "account_closed",
"allocate" => "buffer_account_allocated",
"extend" => "account_capacity_extended",
_ => "unknown",
};
}
fn target_account(operation: &str, accounts: &serde_json::Value) -> serde_json::Value {
@@ -194,10 +304,6 @@ fn account_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Val
#[cfg(test)]
mod tests {
const MATRIX_JSON: &str = include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json"
);
fn observation(operation: &str, committed: bool) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("solana_program_metadata:{operation}:0"),
@@ -249,7 +355,9 @@ mod tests {
#[test]
fn every_stable_instruction_has_one_materialized_mutation_fact() {
let materializer = crate::MtMetadataMaterializer;
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let identity = crate::MtApiEventMaterializer::identity(&materializer);
assert_eq!(identity.name, crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME);
for (_, operation) in crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES.iter().copied() {
let value = observation(operation, true);
assert!(crate::MtApiEventMaterializer::accepts_observation(&materializer, &value));
@@ -259,12 +367,16 @@ mod tests {
assert_eq!(result.outputs[0].payload_json["operation"], *operation);
assert_eq!(result.outputs[0].payload_json["projection"]["deprecated"], false);
assert_eq!(result.outputs[0].payload_json["projection"]["replaced"], false);
assert_eq!(
result.outputs[0].payload_json["provenance"]["processorName"],
crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME
);
}
}
#[test]
fn failed_instruction_is_refused_and_foreign_surface_is_ignored() {
let materializer = crate::MtMetadataMaterializer;
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let failed = observation("set_data", false);
let failure = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
assert_eq!(failure.status, crate::MtApiMaterializerOutcomeStatus::Refused);
@@ -277,7 +389,9 @@ mod tests {
#[test]
fn materialization_matrix_matches_two_account_states_and_nine_instructions() {
let parsed = serde_json::from_str::<serde_json::Value>(MATRIX_JSON);
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json"
));
assert!(parsed.is_ok());
let matrix = match parsed {
std::result::Result::Ok(value) => value,
@@ -310,7 +424,7 @@ mod tests {
#[test]
fn trim_keeps_prestable_alias_as_provenance_only() {
let materializer = crate::MtMetadataMaterializer;
let materializer = crate::MtSolanaProgramMetadataMaterializer;
let value = observation("trim", true);
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
assert_eq!(

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata/solana_program_metadata/state.rs
// version: 2
// version: 4
//! Authoritative Solana Program Metadata account-state projections.
@@ -11,20 +11,18 @@ pub fn materializer_metadata_materialize_solana_program_metadata_account_snapsho
slot: u64,
snapshot: &crate::DcMetadataSpmAccountSnapshot,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
match snapshot {
return match snapshot {
crate::DcMetadataSpmAccountSnapshot::Buffer(value) => {
return crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
slot,
value,
);
crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
slot, value,
)
},
crate::DcMetadataSpmAccountSnapshot::Metadata(value) => {
return crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
slot,
value,
);
crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
slot, value,
)
},
}
};
}
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
@@ -65,7 +63,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_buffer_snapshot
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "buffer_state",
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
@@ -85,7 +83,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_buffer_snapshot
"externalAccountRead": false,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_solana_program_metadata_account_decoder"
}
@@ -152,7 +150,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
"projectionVersion": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROJECTION_VERSION,
"domain": "solana_program_metadata",
"projectionKind": "metadata_state",
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
@@ -179,7 +177,7 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
"externalAccountRead": false,
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
"provenance": {
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
"processorName": crate::MT_METADATA_SOLANA_PROGRAM_METADATA_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_solana_program_metadata_account_decoder"
}
@@ -188,32 +186,32 @@ pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapsh
}
fn data_projection(data: &crate::DcMetadataSpmData) -> serde_json::Value {
match data {
return match data {
crate::DcMetadataSpmData::Direct(value) => {
let mut projection = bytes_projection(value.as_slice());
projection["kind"] = serde_json::json!("direct");
return projection;
projection
},
crate::DcMetadataSpmData::Url(value) => {
return serde_json::json!({
serde_json::json!({
"kind": "url",
"value": value,
"lengthBytes": value.len(),
"sha256": hash(value.as_bytes()),
"fetched": false
});
})
},
crate::DcMetadataSpmData::External(value) => {
return serde_json::json!({
serde_json::json!({
"kind": "external",
"address": value.address.to_string(),
"offset": value.offset,
"length": value.length.map(std::num::NonZeroU32::get),
"wireLengthBytes": crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
"accountRead": false
});
})
},
}
};
}
fn bytes_projection(bytes: &[u8]) -> serde_json::Value {

View File

@@ -0,0 +1,19 @@
// file: kb-lib/src/materializer/metadata/token_2022.rs
// version: 2
//! Embedded SPL Token-2022 metadata materialization surface.
mod constants;
mod state;
/// Materializes Token-2022 embedded metadata and group snapshots.
pub use self::state::materializer_metadata_materialize_token_2022_snapshot;
/// Stable runtime component name for Token-2022 embedded metadata projections.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_COMPONENT_NAME;
/// Stable processor name for Token-2022 embedded metadata projections.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_PROCESSOR_NAME;
/// Stable Token-2022 embedded metadata projection version.
pub(crate) use self::constants::MT_METADATA_TOKEN_2022_PROJECTION_VERSION;
/// Canonical tracing target for Token-2022 embedded metadata materialization.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022;

View File

@@ -0,0 +1,15 @@
// file: kb-lib/src/materializer/metadata/token_2022/constants.rs
// version: 3
//! Local constants for the metadata token_2022 materializer.
/// Stable runtime component name for the metadata token_2022 materializer.
pub(crate) const MT_METADATA_TOKEN_2022_COMPONENT_NAME: &str =
"kb-lib.materializer.metadata.token_2022";
/// Stable processor name persisted by metadata projections.
pub(crate) const MT_METADATA_TOKEN_2022_PROCESSOR_NAME: &str = "materializer.metadata.token_2022";
/// Stable projection contract version.
pub(crate) const MT_METADATA_TOKEN_2022_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the metadata token_2022 materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022: &str =
"kb-lib.materializer.metadata.token_2022";

View File

@@ -0,0 +1,210 @@
// file: kb-lib/src/materializer/metadata/token_2022/state.rs
// version: 4
//! Authoritative embedded SPL Token-2022 metadata state materialization.
/// Materialize embedded Token-2022 metadata, group, and group-member extensions.
pub fn materializer_metadata_materialize_token_2022_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<std::vec::Vec<crate::MtApiMaterializedOutput>, String> {
if account_key.is_empty() {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
action = "materialize_token_2022_metadata_snapshot",
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = "",
slot,
status = "refused",
error_code = "empty_account_key",
"refuse Token-2022 metadata snapshot without account identity"
);
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a non-empty account key".to_string(),
);
}
if state.kind != crate::DcToken2022StateKind::Mint {
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
action = "materialize_token_2022_metadata_snapshot",
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = %account_key,
slot,
state_kind = ?state.kind,
status = "refused",
error_code = "non_mint_state",
"refuse Token-2022 metadata snapshot for non-mint state"
);
return std::result::Result::Err(
"Token-2022 metadata snapshot requires a parsed mint state".to_string(),
);
}
let mut outputs = std::vec::Vec::new();
for entry in &state.extensions {
let projection_kind = match entry.extension_name {
"token_metadata" => std::option::Option::Some("token_metadata"),
"token_group" => std::option::Option::Some("token_group"),
"token_group_member" => std::option::Option::Some("token_group_member"),
_ => std::option::Option::None,
};
let kind = match projection_kind {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
outputs.push(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_metadata_snapshot:{account_key}:{slot}:{kind}"),
family: crate::MdMaterializedEventFamily::Metadata,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_METADATA_TOKEN_2022_PROJECTION_VERSION,
"domain": "spl_token_2022_embedded_metadata",
"projectionSemantics": "authoritative_bounded_mint_extension_snapshot",
"idempotenceKey": format!("token_2022_metadata:{account_key}:{slot}:{kind}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"metadataSource": "token_2022_embedded_tlv",
"projectionKind": kind,
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
"externalUriFetched": false,
"identityAuthority": "mint_address",
"finalAccountStateCaptured": true,
"provenance": {
"processorName": crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
let status = if outputs.is_empty() { "ignored" } else { "inserted" };
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_TOKEN_2022,
action = "materialize_token_2022_metadata_snapshot",
component_name = crate::MT_METADATA_TOKEN_2022_COMPONENT_NAME,
processor_name = crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME,
processor_version = env!("CARGO_PKG_VERSION"),
account_key = %account_key,
slot,
state_kind = "mint",
output_count = outputs.len(),
status,
"materialize embedded Token-2022 metadata state snapshots"
);
return std::result::Result::Ok(outputs);
}
#[cfg(test)]
mod tests_token_2022_state {
#[test]
fn embedded_metadata_group_and_member_have_distinct_stable_outputs() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"1","decimals":0}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 19,
extension_name: "token_metadata",
value_hex: "01".to_string(),
value_fields: serde_json::json!({"name":"Token","symbol":"TOK","uri":"https://example.invalid/token.json"}),
},
crate::DcToken2022TlvEntry {
extension_type: 20,
extension_name: "token_group",
value_hex: "02".to_string(),
value_fields: serde_json::json!({"mint":"mint111","size":"1","maxSize":"10"}),
},
crate::DcToken2022TlvEntry {
extension_type: 21,
extension_name: "token_group_member",
value_hex: "03".to_string(),
value_fields: serde_json::json!({"mint":"mint111","group":"group111","memberNumber":"1"}),
},
],
};
let outputs =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(outputs.as_ref().map(|values| return values.len()), std::result::Result::Ok(3));
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("token_2022_embedded_tlv"))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["externalUriFetched"].clone()),
std::result::Result::Ok(serde_json::json!(false))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[1].payload_json["projectionKind"].clone()),
std::result::Result::Ok(serde_json::json!("token_group"))
);
assert_eq!(
outputs.as_ref().map(|values| {
return values[0].payload_json["provenance"]["processorName"].clone();
}),
std::result::Result::Ok(serde_json::json!(
crate::MT_METADATA_TOKEN_2022_PROCESSOR_NAME
))
);
assert_eq!(
outputs
.as_ref()
.map(|values| return values[0].payload_json["projectionVersion"].clone()),
std::result::Result::Ok(serde_json::json!(2))
);
}
#[test]
fn snapshot_is_idempotent_filters_unrelated_extensions_and_requires_mint_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "00".to_string(),
value_fields: serde_json::json!({}),
}],
};
let first =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
let second =
crate::materializer_metadata_materialize_token_2022_snapshot("mint111", 9, &state);
assert_eq!(first, second);
assert_eq!(first.as_ref().map(|values| return values.len()), std::result::Result::Ok(0));
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot("", 9, &state).is_err()
);
let account_state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
assert!(
crate::materializer_metadata_materialize_token_2022_snapshot(
"account111",
9,
&account_state
)
.is_err()
);
}
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/nft.rs
// version: 3
// version: 4
//! `nft` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved NFT materializer.
pub use self::core::MtNftMaterializer;
pub use self::materializer::MtNftMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_NFT_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `nft` materializer.
pub(crate) use self::constants::MT_NFT_COMPONENT_NAME;
/// Stable processor name for the `nft` materializer.
pub(crate) use self::constants::MT_NFT_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/nft/constants.rs
// version: 1
//! Local constants for the nft materializer.
/// Stable runtime component name for the nft materializer.
pub(crate) const MT_NFT_COMPONENT_NAME: &str = "kb-lib.materializer.nft";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_NFT_PROCESSOR_NAME: &str = "materializer.nft";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_NFT_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/nft/core.rs
// version: 4
// file: kb-lib/src/materializer/nft/materializer.rs
// version: 5
//! Reserved NFT materializer.
@@ -9,7 +9,7 @@ pub struct MtNftMaterializer;
impl crate::MtMaterializer for crate::MtNftMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.nft";
return crate::MT_NFT_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtNftMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Nft,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtNftMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.nft".to_string(),
name: crate::MT_NFT_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_NFT_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/oracle.rs
// version: 3
// version: 4
//! `oracle` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved oracle materializer.
pub use self::core::MtOracleMaterializer;
pub use self::materializer::MtOracleMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_ORACLE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `oracle` materializer.
pub(crate) use self::constants::MT_ORACLE_COMPONENT_NAME;
/// Stable processor name for the `oracle` materializer.
pub(crate) use self::constants::MT_ORACLE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/oracle/constants.rs
// version: 1
//! Local constants for the oracle materializer.
/// Stable runtime component name for the oracle materializer.
pub(crate) const MT_ORACLE_COMPONENT_NAME: &str = "kb-lib.materializer.oracle";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ORACLE_PROCESSOR_NAME: &str = "materializer.oracle";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_ORACLE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/oracle/core.rs
// version: 4
// file: kb-lib/src/materializer/oracle/materializer.rs
// version: 5
//! Reserved oracle materializer.
@@ -9,7 +9,7 @@ pub struct MtOracleMaterializer;
impl crate::MtMaterializer for crate::MtOracleMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.oracle";
return crate::MT_ORACLE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtOracleMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Oracle,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtOracleMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.oracle".to_string(),
name: crate::MT_ORACLE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_ORACLE_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/orderbook.rs
// version: 3
// version: 4
//! `orderbook` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved orderbook materializer.
pub use self::core::MtOrderbookMaterializer;
pub use self::materializer::MtOrderbookMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_ORDERBOOK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `orderbook` materializer.
pub(crate) use self::constants::MT_ORDERBOOK_COMPONENT_NAME;
/// Stable processor name for the `orderbook` materializer.
pub(crate) use self::constants::MT_ORDERBOOK_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/orderbook/constants.rs
// version: 1
//! Local constants for the orderbook materializer.
/// Stable runtime component name for the orderbook materializer.
pub(crate) const MT_ORDERBOOK_COMPONENT_NAME: &str = "kb-lib.materializer.orderbook";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ORDERBOOK_PROCESSOR_NAME: &str = "materializer.orderbook";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_ORDERBOOK_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/orderbook/core.rs
// version: 4
// file: kb-lib/src/materializer/orderbook/materializer.rs
// version: 5
//! Reserved orderbook materializer.
@@ -9,7 +9,7 @@ pub struct MtOrderbookMaterializer;
impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.orderbook";
return crate::MT_ORDERBOOK_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtOrderbookMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Orderbook,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtOrderbookMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.orderbook".to_string(),
name: crate::MT_ORDERBOOK_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_ORDERBOOK_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/perpetuals.rs
// version: 3
// version: 4
//! `perpetuals` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved perpetuals materializer.
pub use self::core::MtPerpetualsMaterializer;
pub use self::materializer::MtPerpetualsMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_PERPETUALS_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `perpetuals` materializer.
pub(crate) use self::constants::MT_PERPETUALS_COMPONENT_NAME;
/// Stable processor name for the `perpetuals` materializer.
pub(crate) use self::constants::MT_PERPETUALS_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/perpetuals/constants.rs
// version: 1
//! Local constants for the perpetuals materializer.
/// Stable runtime component name for the perpetuals materializer.
pub(crate) const MT_PERPETUALS_COMPONENT_NAME: &str = "kb-lib.materializer.perpetuals";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_PERPETUALS_PROCESSOR_NAME: &str = "materializer.perpetuals";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_PERPETUALS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/perpetuals/core.rs
// version: 3
// file: kb-lib/src/materializer/perpetuals/materializer.rs
// version: 4
//! Reserved perpetuals materializer.
@@ -9,7 +9,7 @@ pub struct MtPerpetualsMaterializer;
impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.perpetuals";
return crate::MT_PERPETUALS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtPerpetualsMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Perpetuals,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtPerpetualsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.perpetuals".to_string(),
name: crate::MT_PERPETUALS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_PERPETUALS_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/pool.rs
// version: 3
// version: 4
//! `pool` materializer family.
@@ -7,3 +7,10 @@ mod state;
/// Reserved pool-state materializer.
pub use self::state::MtPoolStateMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::state::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved pool-state materializer.
pub(crate) use self::state::MT_POOL_STATE_COMPONENT_NAME;
/// Stable processor name for the reserved pool-state materializer.
pub(crate) use self::state::MT_POOL_STATE_PROCESSOR_NAME;

View File

@@ -1,61 +1,17 @@
// file: kb-lib/src/materializer/pool/state.rs
// version: 4
// version: 5
//! Reserved pool-state materializer.
//! Pool-state materializer component facade.
/// Reserved materializer for pool-state observations.
#[derive(Clone, Debug, Default)]
pub struct MtPoolStateMaterializer;
mod constants;
mod materializer;
impl crate::MtMaterializer for crate::MtPoolStateMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.pool.state";
}
/// Reserved pool-state materializer.
pub use self::materializer::MtPoolStateMaterializer;
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::PoolState,
}]);
}
}
impl crate::MtApiEventMaterializer for crate::MtPoolStateMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.pool.state".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_POOL_STATE_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved pool-state materializer.
pub(crate) use self::constants::MT_POOL_STATE_COMPONENT_NAME;
/// Stable processor name for the reserved pool-state materializer.
pub(crate) use self::constants::MT_POOL_STATE_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/pool/state/constants.rs
// version: 1
//! Local constants for the pool state materializer.
/// Stable runtime component name for the pool state materializer.
pub(crate) const MT_POOL_STATE_COMPONENT_NAME: &str = "kb-lib.materializer.pool.state";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_POOL_STATE_PROCESSOR_NAME: &str = "materializer.pool.state";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_POOL_STATE_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -0,0 +1,56 @@
// file: kb-lib/src/materializer/pool/state/materializer.rs
// version: 5
//! Reserved pool-state materializer.
/// Reserved materializer for pool-state observations.
#[derive(Clone, Debug, Default)]
pub struct MtPoolStateMaterializer;
impl crate::MtMaterializer for crate::MtPoolStateMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_POOL_STATE_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtPoolStateMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_POOL_STATE_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_POOL_STATE_ACCEPTED_FAMILIES;
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/rewards.rs
// version: 3
// version: 4
//! `rewards` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved rewards materializer.
pub use self::core::MtRewardsMaterializer;
pub use self::materializer::MtRewardsMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_REWARDS_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `rewards` materializer.
pub(crate) use self::constants::MT_REWARDS_COMPONENT_NAME;
/// Stable processor name for the `rewards` materializer.
pub(crate) use self::constants::MT_REWARDS_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/rewards/constants.rs
// version: 1
//! Local constants for the rewards materializer.
/// Stable runtime component name for the rewards materializer.
pub(crate) const MT_REWARDS_COMPONENT_NAME: &str = "kb-lib.materializer.rewards";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_REWARDS_PROCESSOR_NAME: &str = "materializer.rewards";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_REWARDS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,15 +1,15 @@
// file: kb-lib/src/materializer/rewards/core.rs
// version: 4
// file: kb-lib/src/materializer/rewards/materializer.rs
// version: 5
//! Reserved rewards materializer.
/// Reserved materializer for reward observations.
/// Reserved materializer for rewards observations.
#[derive(Clone, Debug, Default)]
pub struct MtRewardsMaterializer;
impl crate::MtMaterializer for crate::MtRewardsMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.rewards";
return crate::MT_REWARDS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtRewardsMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Reward,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtRewardsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.rewards".to_string(),
name: crate::MT_REWARDS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_REWARDS_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,17 +1,23 @@
// file: kb-lib/src/materializer/risk.rs
// version: 3
// version: 4
//! `risk` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable SPL Token and ATA risk materializer.
pub use self::core::MtRiskMaterializer;
pub use self::materializer::MtRiskMaterializer;
/// Stable processor name for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_RISK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `risk` materializer.
pub(crate) use self::constants::MT_RISK_COMPONENT_NAME;
/// Stable processor name for the `risk` materializer.
pub(crate) use self::constants::MT_RISK_PROCESSOR_NAME;
/// Stable projection contract version for SPL Token and ATA risk facts.
/// Stable projection version for the `risk` materializer.
pub(crate) use self::constants::MT_RISK_PROJECTION_VERSION;
/// Canonical tracing target for SPL Token and ATA risk facts.
/// Crate-visible `MT_RISK_RISK_ENTRIES` component constant.
pub(crate) use self::constants::MT_RISK_RISK_ENTRIES;
/// Canonical tracing target for the `risk` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_RISK;

View File

@@ -1,11 +1,48 @@
// file: kb-lib/src/materializer/risk/constants.rs
// version: 3
// version: 5
//! Local constants for fact-only risk projections.
//! Local constants for the risk materializer.
/// Stable runtime component name for the risk materializer.
pub(crate) const MT_RISK_COMPONENT_NAME: &str = "kb-lib.materializer.risk";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_RISK_PROCESSOR_NAME: &str = "materializer.risk";
/// Stable projection payload contract version.
/// Stable projection contract version for the risk materializer.
pub(crate) const MT_RISK_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for this component.
/// Canonical tracing target for the risk materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_RISK: &str = "kb-lib.materializer.risk";
/// Component contract for `accepted_families`.
pub(crate) const MT_RISK_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Audit,
];
/// Component contract for `risk_entries`.
pub(crate) const MT_RISK_RISK_ENTRIES: &[&str] = &[
"approve",
"approve_checked",
"revoke",
"freeze_account",
"thaw_account",
"set_authority",
"initialize_multisig",
"initialize_multisig2",
"initialize_default_account_state",
"update_default_account_state",
"enable_required_transfer_memos",
"disable_required_transfer_memos",
"enable_cpi_guard",
"disable_cpi_guard",
"initialize_non_transferable_mint",
"initialize_pausable_config",
"pause",
"resume",
"initialize_permissioned_burn",
"enable_confidential_credits",
"disable_confidential_credits",
"enable_non_confidential_credits",
"disable_non_confidential_credits",
];

View File

@@ -1,48 +1,15 @@
// file: kb-lib/src/materializer/risk/core.rs
// version: 6
// file: kb-lib/src/materializer/risk/materializer.rs
// version: 8
//! Stable fact-only risk projections for committed SPL Token and ATA observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Audit,
];
const RISK_ENTRIES: &[&str] = &[
"approve",
"approve_checked",
"revoke",
"freeze_account",
"thaw_account",
"set_authority",
"initialize_multisig",
"initialize_multisig2",
"initialize_default_account_state",
"update_default_account_state",
"enable_required_transfer_memos",
"disable_required_transfer_memos",
"enable_cpi_guard",
"disable_cpi_guard",
"initialize_non_transferable_mint",
"initialize_pausable_config",
"pause",
"resume",
"initialize_permissioned_burn",
"enable_confidential_credits",
"disable_confidential_credits",
"enable_non_confidential_credits",
"disable_non_confidential_credits",
];
/// Stable fact-only classic SPL Token risk materializer.
#[derive(Clone, Debug, Default)]
pub struct MtRiskMaterializer;
impl crate::MtMaterializer for crate::MtRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.risk";
return crate::MT_RISK_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
@@ -66,7 +33,7 @@ impl crate::MtApiEventMaterializer for crate::MtRiskMaterializer {
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_RISK_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
@@ -171,8 +138,9 @@ fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
&& event.surface_code.0 == "spl.token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == "spl.token_2022");
let token_risk = token_program && RISK_ENTRIES.contains(&event.event_name.0.as_str());
return ACCEPTED_FAMILIES.contains(&event.event_family)
let token_risk =
token_program && crate::MT_RISK_RISK_ENTRIES.contains(&event.event_name.0.as_str());
return crate::MT_RISK_ACCEPTED_FAMILIES.contains(&event.event_family)
&& (token_risk || is_ata_recovery(event));
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/routing.rs
// version: 3
// version: 4
//! `routing` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved routing materializer.
pub use self::core::MtRoutingMaterializer;
pub use self::materializer::MtRoutingMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_ROUTING_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `routing` materializer.
pub(crate) use self::constants::MT_ROUTING_COMPONENT_NAME;
/// Stable processor name for the `routing` materializer.
pub(crate) use self::constants::MT_ROUTING_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/routing/constants.rs
// version: 1
//! Local constants for the routing materializer.
/// Stable runtime component name for the routing materializer.
pub(crate) const MT_ROUTING_COMPONENT_NAME: &str = "kb-lib.materializer.routing";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_ROUTING_PROCESSOR_NAME: &str = "materializer.routing";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_ROUTING_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/routing/core.rs
// version: 4
// file: kb-lib/src/materializer/routing/materializer.rs
// version: 5
//! Reserved routing materializer.
@@ -9,7 +9,7 @@ pub struct MtRoutingMaterializer;
impl crate::MtMaterializer for crate::MtRoutingMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.routing";
return crate::MT_ROUTING_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtRoutingMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Routing,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtRoutingMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.routing".to_string(),
name: crate::MT_ROUTING_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_ROUTING_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,13 +1,45 @@
// file: kb-lib/src/materializer/staking.rs
// version: 4
// version: 5
//! `staking` materializer family.
mod constants;
mod core;
mod materializer;
/// Stable native stake and vote materializer.
pub use self::core::MtStakingMaterializer;
pub use self::materializer::MtStakingMaterializer;
/// Canonical tracing target for the native stake and vote materializer.
/// Crate-visible `MT_STAKING_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_STAKING_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `staking` materializer.
pub(crate) use self::constants::MT_STAKING_COMPONENT_NAME;
/// Stable processor name for the `staking` materializer.
pub(crate) use self::constants::MT_STAKING_PROCESSOR_NAME;
/// Stable projection version for the `staking` materializer.
pub(crate) use self::constants::MT_STAKING_PROJECTION_VERSION;
/// Crate-visible `MT_STAKING_STAKE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_LOCKUP_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_LOCKUP_ENTRIES;
/// Crate-visible `MT_STAKING_STAKE_SURFACE` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_SURFACE;
/// Crate-visible `MT_STAKING_STAKE_VALUE_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_STAKE_VALUE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ACCOUNT_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_ACCOUNT_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_ADMIN_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_ADMIN_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_AUTHORITY_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_AUTHORITY_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_REWARD_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_REWARD_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_STATE_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_STATE_ENTRIES;
/// Crate-visible `MT_STAKING_VOTE_SURFACE` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_SURFACE;
/// Crate-visible `MT_STAKING_VOTE_VALUE_ENTRIES` component constant.
pub(crate) use self::constants::MT_STAKING_VOTE_VALUE_ENTRIES;
/// Canonical tracing target for the `staking` materializer.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_STAKING;

View File

@@ -1,7 +1,77 @@
// file: kb-lib/src/materializer/staking/constants.rs
// version: 2
// version: 4
//! Local constants for the native stake and vote materializer.
//! Local constants for the staking materializer.
/// Canonical tracing target for this component.
/// Stable runtime component name for the staking materializer.
pub(crate) const MT_STAKING_COMPONENT_NAME: &str = "kb-lib.materializer.staking";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_STAKING_PROCESSOR_NAME: &str = "materializer.staking";
/// Stable projection contract version for the staking materializer.
pub(crate) const MT_STAKING_PROJECTION_VERSION: u32 = 2;
/// Canonical tracing target for the staking materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_STAKING: &str = "kb-lib.materializer.staking";
/// Component contract for `accepted_families`.
pub(crate) const MT_STAKING_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Staking,
crate::MdEventFamily::Reward,
];
/// Component contract for `stake_surface`.
pub(crate) const MT_STAKING_STAKE_SURFACE: &str = "solana.core.stake";
/// Component contract for `vote_surface`.
pub(crate) const MT_STAKING_VOTE_SURFACE: &str = "solana.core.vote";
/// Component contract for `stake_account_entries`.
pub(crate) const MT_STAKING_STAKE_ACCOUNT_ENTRIES: &[&str] = &[
"initialize",
"initialize_checked",
"delegate_stake",
"deactivate",
"deactivate_delinquent",
];
/// Component contract for `stake_value_entries`.
pub(crate) const MT_STAKING_STAKE_VALUE_ENTRIES: &[&str] =
&["split", "withdraw", "merge", "move_stake", "move_lamports"];
/// Component contract for `stake_authority_entries`.
pub(crate) const MT_STAKING_STAKE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_with_seed",
"authorize_checked",
"authorize_checked_with_seed",
];
/// Component contract for `stake_lockup_entries`.
pub(crate) const MT_STAKING_STAKE_LOCKUP_ENTRIES: &[&str] = &["set_lockup", "set_lockup_checked"];
/// Component contract for `vote_account_entries`.
pub(crate) const MT_STAKING_VOTE_ACCOUNT_ENTRIES: &[&str] =
&["initialize_account", "initialize_account_v2"];
/// Component contract for `vote_authority_entries`.
pub(crate) const MT_STAKING_VOTE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_checked",
"authorize_with_seed",
"authorize_checked_with_seed",
];
/// Component contract for `vote_admin_entries`.
pub(crate) const MT_STAKING_VOTE_ADMIN_ENTRIES: &[&str] = &[
"update_validator_identity",
"update_commission",
"update_commission_collector",
"update_commission_bps",
];
/// Component contract for `vote_state_entries`.
pub(crate) const MT_STAKING_VOTE_STATE_ENTRIES: &[&str] = &[
"vote",
"vote_switch",
"update_vote_state",
"update_vote_state_switch",
"compact_update_vote_state",
"compact_update_vote_state_switch",
"tower_sync",
"tower_sync_switch",
];
/// Component contract for `vote_value_entries`.
pub(crate) const MT_STAKING_VOTE_VALUE_ENTRIES: &[&str] = &["withdraw"];
/// Component contract for `vote_reward_entries`.
pub(crate) const MT_STAKING_VOTE_REWARD_ENTRIES: &[&str] = &["deposit_delegator_rewards"];

View File

@@ -1,57 +1,8 @@
// file: kb-lib/src/materializer/staking/core.rs
// version: 10
// file: kb-lib/src/materializer/staking/materializer.rs
// version: 12
//! Instruction-level staking projections for native Stake and Vote observations.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::Lifecycle,
crate::MdEventFamily::Admin,
crate::MdEventFamily::Staking,
crate::MdEventFamily::Reward,
];
const STAKE_SURFACE: &str = "solana.core.stake";
const VOTE_SURFACE: &str = "solana.core.vote";
const STAKE_ACCOUNT_ENTRIES: &[&str] = &[
"initialize",
"initialize_checked",
"delegate_stake",
"deactivate",
"deactivate_delinquent",
];
const STAKE_VALUE_ENTRIES: &[&str] = &["split", "withdraw", "merge", "move_stake", "move_lamports"];
const STAKE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_with_seed",
"authorize_checked",
"authorize_checked_with_seed",
];
const STAKE_LOCKUP_ENTRIES: &[&str] = &["set_lockup", "set_lockup_checked"];
const VOTE_ACCOUNT_ENTRIES: &[&str] = &["initialize_account", "initialize_account_v2"];
const VOTE_AUTHORITY_ENTRIES: &[&str] = &[
"authorize",
"authorize_checked",
"authorize_with_seed",
"authorize_checked_with_seed",
];
const VOTE_ADMIN_ENTRIES: &[&str] = &[
"update_validator_identity",
"update_commission",
"update_commission_collector",
"update_commission_bps",
];
const VOTE_STATE_ENTRIES: &[&str] = &[
"vote",
"vote_switch",
"update_vote_state",
"update_vote_state_switch",
"compact_update_vote_state",
"compact_update_vote_state_switch",
"tower_sync",
"tower_sync_switch",
];
const VOTE_VALUE_ENTRIES: &[&str] = &["withdraw"];
const VOTE_REWARD_ENTRIES: &[&str] = &["deposit_delegator_rewards"];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ProjectionKind {
StakeAccount,
@@ -100,7 +51,7 @@ pub struct MtStakingMaterializer;
impl crate::MtMaterializer for crate::MtStakingMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.staking";
return crate::MT_STAKING_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -126,13 +77,13 @@ impl crate::MtMaterializer for crate::MtStakingMaterializer {
impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "materializer.staking".to_string(),
name: crate::MT_STAKING_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
return crate::MT_STAKING_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
@@ -205,7 +156,7 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
output_key: projection.output_key(operation.as_str()),
family: projection.family(),
payload_json: serde_json::json!({
"projectionVersion": 1,
"projectionVersion": crate::MT_STAKING_PROJECTION_VERSION,
"projectionSemantics": "committed_instruction_staking_event",
"domain": projection.domain(),
"operation": operation,
@@ -214,6 +165,10 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"transactionSucceeded": true,
"provenance": {
"processorName": crate::MT_STAKING_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
},
"accounts": accounts,
"parameters": parameters,
"projection": projection_details(
@@ -242,52 +197,54 @@ impl crate::MtApiEventMaterializer for crate::MtStakingMaterializer {
}
fn family_is_accepted(family: crate::MdEventFamily) -> bool {
return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family);
return crate::MT_STAKING_ACCEPTED_FAMILIES
.iter()
.any(|accepted| return *accepted == family);
}
fn projection(surface_code: &str, entry_code: &str) -> std::option::Option<ProjectionKind> {
if surface_code == STAKE_SURFACE {
if surface_code == crate::MT_STAKING_STAKE_SURFACE {
return stake_projection(entry_code);
}
if surface_code == VOTE_SURFACE {
if surface_code == crate::MT_STAKING_VOTE_SURFACE {
return vote_projection(entry_code);
}
return std::option::Option::None;
}
fn stake_projection(entry_code: &str) -> std::option::Option<ProjectionKind> {
if contains(STAKE_ACCOUNT_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_ACCOUNT_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeAccount);
}
if contains(STAKE_VALUE_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_VALUE_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeValue);
}
if contains(STAKE_AUTHORITY_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_AUTHORITY_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeAuthority);
}
if contains(STAKE_LOCKUP_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_STAKE_LOCKUP_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::StakeLockup);
}
return std::option::Option::None;
}
fn vote_projection(entry_code: &str) -> std::option::Option<ProjectionKind> {
if contains(VOTE_ACCOUNT_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_ACCOUNT_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteAccount);
}
if contains(VOTE_AUTHORITY_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_AUTHORITY_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteAuthority);
}
if contains(VOTE_ADMIN_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_ADMIN_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteAdmin);
}
if contains(VOTE_STATE_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_STATE_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteState);
}
if contains(VOTE_VALUE_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_VALUE_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteValue);
}
if contains(VOTE_REWARD_ENTRIES, entry_code) {
if contains(crate::MT_STAKING_VOTE_REWARD_ENTRIES, entry_code) {
return std::option::Option::Some(ProjectionKind::VoteReward);
}
return std::option::Option::None;
@@ -637,7 +594,7 @@ mod tests {
fn stake_initialize_creates_instructional_staking_projection() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"initialize",
crate::MdEventFamily::Lifecycle,
false,
@@ -659,7 +616,7 @@ mod tests {
fn stake_authority_and_lockup_events_are_projected() {
let materializer = crate::MtStakingMaterializer;
let authority = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"authorize_with_seed",
crate::MdEventFamily::Admin,
false,
@@ -672,7 +629,7 @@ mod tests {
}),
);
let lockup = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"set_lockup_checked",
crate::MdEventFamily::Admin,
false,
@@ -701,7 +658,7 @@ mod tests {
fn stake_value_projection_does_not_duplicate_lamport_changes() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"withdraw",
crate::MdEventFamily::Staking,
false,
@@ -724,7 +681,7 @@ mod tests {
fn vote_account_and_authority_events_are_projected() {
let materializer = crate::MtStakingMaterializer;
let initialize = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"initialize_account_v2",
crate::MdEventFamily::Lifecycle,
false,
@@ -739,7 +696,7 @@ mod tests {
}),
);
let authorize = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"authorize_checked",
crate::MdEventFamily::Admin,
false,
@@ -769,7 +726,7 @@ mod tests {
fn vote_state_and_reward_outputs_are_distinguished() {
let materializer = crate::MtStakingMaterializer;
let vote_state = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"tower_sync_switch",
crate::MdEventFamily::Staking,
false,
@@ -780,7 +737,7 @@ mod tests {
serde_json::json!({"root": 10, "hash": "hash111", "switchProofHash": "switch111"}),
);
let reward = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"deposit_delegator_rewards",
crate::MdEventFamily::Reward,
false,
@@ -805,7 +762,7 @@ mod tests {
fn failed_staking_mutation_is_refused() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"delegate_stake",
crate::MdEventFamily::Staking,
true,
@@ -829,7 +786,7 @@ mod tests {
serde_json::json!({}),
);
let redelegate = observation(
super::STAKE_SURFACE,
crate::MT_STAKING_STAKE_SURFACE,
"redelegate",
crate::MdEventFamily::Staking,
false,
@@ -848,7 +805,7 @@ mod tests {
fn output_serialization_is_deterministic() {
let materializer = crate::MtStakingMaterializer;
let observation = observation(
super::VOTE_SURFACE,
crate::MT_STAKING_VOTE_SURFACE,
"update_commission_bps",
crate::MdEventFamily::Admin,
false,

View File

@@ -1,10 +1,9 @@
// file: kb-lib/src/materializer/token.rs
// version: 6
// version: 7
//! `token` materializer family.
mod accounts;
mod constants;
mod metadata_risk;
/// Stable Token, ATA and Token-2022 account materializer.
@@ -14,9 +13,23 @@ pub use self::accounts::materializer_token_materialize_token_2022_state_snapshot
/// Reserved token-metadata risk materializer.
pub use self::metadata_risk::MtTokenMetadataRiskMaterializer;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES` component constant.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Stable runtime component name for Token, ATA and Token-2022 account projections.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES` component constant.
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Stable processor name for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
pub(crate) use self::accounts::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
pub(crate) use self::accounts::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::metadata_risk::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved token-metadata risk materializer.
pub(crate) use self::metadata_risk::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Stable processor name for the reserved token-metadata risk materializer.
pub(crate) use self::metadata_risk::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;

View File

@@ -1,842 +1,27 @@
// file: kb-lib/src/materializer/token/accounts.rs
// version: 7
// version: 8
//! Stable committed SPL Token mutation and ATA lifecycle projections.
//! Token, ATA and Token-2022 account materializer component.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::TokenBurn,
crate::MdEventFamily::Lifecycle,
];
const MUTATION_ENTRIES: &[&str] = &[
"initialize_mint",
"initialize_mint2",
"initialize_account",
"initialize_account2",
"initialize_account3",
"transfer",
"transfer_checked",
"approve",
"approve_checked",
"revoke",
"mint_to",
"mint_to_checked",
"burn",
"burn_checked",
"close_account",
"freeze_account",
"thaw_account",
"sync_native",
"initialize_immutable_owner",
"withdraw_excess_lamports",
"unwrap_lamports",
"initialize_non_transferable_mint",
"reallocate",
];
const ATA_LIFECYCLE_ENTRIES: &[(&str, &str)] = &[
("create", "ata_created"),
("create_idempotent", "ata_created_or_reused_idempotently"),
("recover_nested", "nested_ata_recovered"),
];
mod constants;
mod materializer;
/// Stable committed classic SPL Token account and mint mutation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTokenAccountsMaterializer;
/// Stable Token, ATA and Token-2022 account materializer.
pub use self::materializer::MtTokenAccountsMaterializer;
/// Materializes one bounded Token-2022 account state snapshot.
pub use self::materializer::materializer_token_materialize_token_2022_state_snapshot;
impl crate::MtMaterializer for crate::MtTokenAccountsMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.token.accounts";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenAccountsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_token_account_projection_refused",
"failed or uncommitted SPL Token or ATA observations cannot create mutable outputs",
);
}
if is_ata_event(&observation.event) {
return materialize_ata(observation);
}
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let authority = observation
.payload_json
.get("authority")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
let output = crate::MtApiMaterializedOutput {
output_key: format!("token_account_mutation:{operation}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_account_mutation",
"projectionSemantics": "committed_instruction_mutation_not_final_account_snapshot",
"idempotenceKey": format!(
"spl_token:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
operation
),
"operation": operation,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"mint": account_key_for_role(&accounts, "mint"),
"amountRaw": parameters.get("amountRaw").cloned().unwrap_or(serde_json::Value::Null),
"decimals": parameters.get("decimals").cloned().unwrap_or(serde_json::Value::Null),
"parameters": parameters,
"accounts": accounts,
"authority": authority,
"committed": true,
"transactionFinalAccountStateCaptured": false,
"coreBalanceChangesDuplicated": false,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_token_account_mutation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation = %observation.event.event_name.0,
"materialize committed classic SPL Token mutation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == "spl.token_2022");
let token_mutation = token_program && MUTATION_ENTRIES.contains(&event.event_name.0.as_str());
let ata = is_ata_event(event);
return ACCEPTED_FAMILIES.contains(&event.event_family) && (token_mutation || ata);
}
fn is_ata_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.associated_token_account"
&& event.event_family == crate::MdEventFamily::Lifecycle
&& ATA_LIFECYCLE_ENTRIES
.iter()
.any(|(entry, _)| return *entry == event.event_name.0);
}
fn materialize_ata(
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let operation = observation.event.event_name.0.as_str();
let addresses = match observation.payload_json.get("addresses") {
std::option::Option::Some(value) if value.is_object() => value,
_ => {
return crate::MtApiMaterializerExecutionResult::refused(
"ata_addresses_missing",
"committed ATA lifecycle projection requires the decoded address contract",
);
},
};
let validation = validate_ata_addresses(operation, addresses);
if let std::result::Result::Err((code, message)) = validation {
return crate::MtApiMaterializerExecutionResult::refused(code, message);
}
let lifecycle_kind =
match ATA_LIFECYCLE_ENTRIES.iter().find(|(entry, _)| return *entry == operation) {
std::option::Option::Some((_, value)) => *value,
std::option::Option::None => {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
let associated_token_account = if operation == "recover_nested" {
observed_address(addresses, "nestedAssociatedTokenAccount")
} else {
observed_address(addresses, "associatedTokenAccount")
};
let token_program = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.cloned()
.unwrap_or(serde_json::Value::Null);
let wallet = addresses.get("walletOwner").cloned().unwrap_or(serde_json::Value::Null);
let mint = if operation == "recover_nested" {
addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null)
} else {
addresses.get("mint").cloned().unwrap_or(serde_json::Value::Null)
};
let output = crate::MtApiMaterializedOutput {
output_key: format!("ata_lifecycle:{lifecycle_kind}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_associated_token_account_lifecycle",
"projectionSemantics": "committed_associated_address_lifecycle_not_token_state_snapshot",
"idempotenceKey": format!(
"ata:{}:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
lifecycle_kind,
associated_token_account.as_str().unwrap_or("")
),
"operation": operation,
"lifecycleKind": lifecycle_kind,
"associatedTokenAccount": associated_token_account,
"walletOwner": wallet,
"mint": mint,
"tokenProgramId": token_program,
"fundingAccount": addresses.get("fundingAccount").cloned().unwrap_or(serde_json::Value::Null),
"ownerMint": addresses.get("ownerMint").cloned().unwrap_or(serde_json::Value::Null),
"nestedMint": addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null),
"ownerAssociatedTokenAccount": observed_address(addresses, "ownerAssociatedTokenAccount"),
"nestedAssociatedTokenAccount": observed_address(addresses, "nestedAssociatedTokenAccount"),
"walletNestedMintAssociatedTokenAccount": observed_address(addresses, "walletNestedMintAssociatedTokenAccount"),
"idempotentOutcome": if operation == "create_idempotent" {
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
} else {
"not_applicable"
},
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"committed": true,
"transactionFinalAccountStateCaptured": false,
"tokenBalanceReconstructed": false,
"splTokenCpiMutationsDuplicated": false,
"ownership": {
"ataParent": "associated_address_lifecycle_only",
"splTokenCpi": "initialize_transfer_and_close_mutations"
},
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_ata_lifecycle",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation,
lifecycle_kind,
"materialize committed ATA lifecycle without duplicating SPL Token CPI mutations"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn validate_ata_addresses(
operation: &str,
addresses: &serde_json::Value,
) -> std::result::Result<(), (&'static str, &'static str)> {
let token_supported = addresses
.get("tokenProgram")
.and_then(|value| return value.get("supported"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let token_program_id = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.and_then(serde_json::Value::as_str);
let token_program_exact = matches!(
token_program_id,
std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID)
| std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID)
);
if !token_supported || !token_program_exact {
return std::result::Result::Err((
"ata_token_program_not_supported",
"ATA lifecycle projection requires classic SPL Token or Token-2022",
));
}
let fields: &[&str] = if operation == "recover_nested" {
&[
"ownerAssociatedTokenAccount",
"nestedAssociatedTokenAccount",
"walletNestedMintAssociatedTokenAccount",
]
} else {
&["associatedTokenAccount"]
};
for field in fields {
let valid = addresses
.get(*field)
.and_then(|value| return value.get("valid"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let observed = observed_address(addresses, field);
if !valid || observed.as_str().is_none_or(|value| return value.is_empty()) {
return std::result::Result::Err((
"ata_derived_address_invalid",
"ATA lifecycle projection requires every canonical address validation to pass",
));
}
}
let identity_fields: &[&str] = if operation == "recover_nested" {
&["walletOwner", "ownerMint", "nestedMint"]
} else {
&["walletOwner", "mint"]
};
if identity_fields.iter().any(|field| {
return addresses
.get(*field)
.and_then(serde_json::Value::as_str)
.is_none_or(|value| return value.is_empty());
}) {
return std::result::Result::Err((
"ata_identity_field_missing",
"ATA lifecycle projection requires wallet and mint identities",
));
}
return std::result::Result::Ok(());
}
fn observed_address(addresses: &serde_json::Value, field: &str) -> serde_json::Value {
return addresses
.get(field)
.and_then(|value| return value.get("observed"))
.cloned()
.unwrap_or(serde_json::Value::Null);
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let value = accounts.as_array().and_then(|rows| {
return rows.iter().find_map(|row| {
if row.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return row.get("accountKey").and_then(serde_json::Value::as_str);
});
});
return match value {
std::option::Option::Some(account_key) => {
serde_json::Value::String(account_key.to_string())
},
std::option::Option::None => serde_json::Value::Null,
};
}
/// Materialize one already parsed Token-2022 account snapshot without inferring hidden state.
pub fn materializer_token_materialize_token_2022_state_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if account_key.is_empty() {
return std::result::Result::Err(
"Token-2022 state snapshot requires a non-empty account key".to_string(),
);
}
let kind = match state.kind {
crate::DcToken2022StateKind::Mint => "mint",
crate::DcToken2022StateKind::Account => "account",
crate::DcToken2022StateKind::Multisig => "multisig",
};
let extensions = state
.extensions
.iter()
.map(|entry| {
return serde_json::json!({
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
});
})
.collect::<std::vec::Vec<_>>();
let extension_names = state
.extensions
.iter()
.map(|entry| return entry.extension_name)
.collect::<std::vec::Vec<_>>();
let contains_confidential_state = state.extensions.iter().any(|entry| {
return entry.extension_name.starts_with("confidential_")
|| entry.extension_name == "confidential_mint_burn";
});
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_state_snapshot:{account_key}:{slot}"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_2022_account_state",
"projectionSemantics": "authoritative_bounded_account_snapshot",
"idempotenceKey": format!("token_2022_state:{account_key}:{slot}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"stateKind": kind,
"baseFields": state.base_fields,
"baseHex": state.base_hex,
"accountType": state.account_type,
"extensionCount": state.extensions.len(),
"extensionNames": extension_names,
"extensions": extensions,
"containsConfidentialState": contains_confidential_state,
"confidentialValuesDecrypted": false,
"finalAccountStateCaptured": true,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
#[cfg(test)]
mod tests {
fn observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("token:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()),
protocol_code: crate::MdProtocolCode("spl.token".to_string()),
surface_code: crate::MdSurfaceCode("spl.token".to_string()),
event_code: crate::MdEventCode(format!("spl.token.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: family,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"parameters": {"amountRaw":"18446744073709551615","decimals":9},
"accounts": [{"role":"mint","accountKey":"mint111"}],
"authority": {"form":"single"}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"error":"failed"}))
} else {
std::option::Option::None
},
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn token_2022_observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
let mut observation = observation(entry, family, failed);
observation.event.program_id =
crate::MdProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
observation.event.protocol_code = crate::MdProtocolCode("spl.token_2022".to_string());
observation.event.surface_code = crate::MdSurfaceCode("spl.token_2022".to_string());
observation.event.event_code = crate::MdEventCode(format!("spl.token_2022.{entry}"));
observation.event_key = format!("token_2022:{entry}:0");
return observation;
}
fn ata_observation(entry: &str, failed: bool, valid: bool) -> crate::DcApiDecodedObservation {
let addresses = if entry == "recover_nested" {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_PROGRAM_ID,
"kind": "spl_token_classic",
"supported": true
},
"ownerAssociatedTokenAccount": {"expected":"ownerAta","observed":"ownerAta","valid":valid},
"nestedAssociatedTokenAccount": {"expected":"nestedAta","observed":"nestedAta","valid":valid},
"walletNestedMintAssociatedTokenAccount": {"expected":"destinationAta","observed":"destinationAta","valid":valid},
"walletOwner": "wallet111",
"ownerMint": "ownerMint111",
"nestedMint": "nestedMint111"
})
} else {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"kind": "token_2022",
"supported": true
},
"associatedTokenAccount": {"expected":"ata111","observed":"ata111","valid":valid},
"fundingAccount": "payer111",
"walletOwner": "wallet111",
"mint": "mint111"
})
};
return crate::DcApiDecodedObservation {
event_key: format!("ata:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode("spl.associated_token_account".to_string()),
surface_code: crate::MdSurfaceCode("spl.associated_token_account".to_string()),
event_code: crate::MdEventCode(format!("spl.associated_token_account.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: crate::MdEventFamily::Lifecycle,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"addresses":addresses}),
transaction_failed: failed,
transaction_error: std::option::Option::None,
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
#[test]
fn committed_checked_transfer_preserves_exact_amount_mint_and_path() {
let observation =
observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["amountRaw"], "18446744073709551615");
assert_eq!(result.outputs[0].payload_json["mint"], "mint111");
assert_eq!(result.outputs[0].payload_json["instructionPath"], "1/0");
assert_eq!(result.outputs[0].payload_json["transactionFinalAccountStateCaptured"], false);
}
#[test]
fn committed_token_2022_transfer_has_the_same_single_projection_owner() {
let observation =
token_2022_observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["operation"], "transfer_checked");
assert_eq!(
result.outputs[0].payload_json["programId"],
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
);
}
#[test]
fn failed_mutation_is_refused() {
let observation = observation("burn", crate::MdEventFamily::TokenBurn, true);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn batch_parent_admin_and_conversion_events_are_not_duplicated() {
for (entry, family) in [
("batch", crate::MdEventFamily::Audit),
("set_authority", crate::MdEventFamily::Admin),
("amount_to_ui_amount", crate::MdEventFamily::Audit),
] {
let observation = observation(entry, family, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
}
#[test]
fn every_ata_variant_has_one_exact_lifecycle_projection() {
for (entry, expected_kind, expected_ata, expected_token_program) in [
("create", "ata_created", "ata111", kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID),
(
"create_idempotent",
"ata_created_or_reused_idempotently",
"ata111",
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
),
(
"recover_nested",
"nested_ata_recovered",
"nestedAta",
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation(entry, false, true),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["lifecycleKind"], expected_kind);
assert_eq!(result.outputs[0].payload_json["associatedTokenAccount"], expected_ata);
assert_eq!(result.outputs[0].payload_json["tokenProgramId"], expected_token_program);
assert_eq!(result.outputs[0].payload_json["tokenBalanceReconstructed"], false);
assert_eq!(result.outputs[0].payload_json["splTokenCpiMutationsDuplicated"], false);
}
}
#[test]
fn idempotent_parent_does_not_invent_created_or_reused_branch() {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("create_idempotent", false, true),
);
assert_eq!(
result.outputs[0].payload_json["idempotentOutcome"],
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
);
assert!(result.outputs[0].payload_json.get("reused").is_none());
assert!(result.outputs[0].payload_json.get("created").is_none());
}
#[test]
fn uncommitted_or_invalid_ata_lifecycle_is_refused() {
for observation in [
ata_observation("create", true, true),
ata_observation("recover_nested", false, false),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
}
#[test]
fn ata_parent_and_classic_token_cpi_children_own_distinct_facts() {
let parent = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("recover_nested", false, true),
);
let transfer = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("transfer", crate::MdEventFamily::TokenAccount, false),
);
let close = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("close_account", crate::MdEventFamily::TokenAccount, false),
);
assert_eq!(parent.outputs[0].payload_json["lifecycleKind"], "nested_ata_recovered");
assert_eq!(
parent.outputs[0].payload_json["ownership"]["ataParent"],
"associated_address_lifecycle_only"
);
assert!(parent.outputs[0].payload_json.get("amountRaw").is_none());
assert_eq!(transfer.outputs[0].payload_json["operation"], "transfer");
assert_eq!(close.outputs[0].payload_json["operation"], "close_account");
assert_eq!(transfer.outputs[0].payload_json["domain"], "spl_token_account_mutation");
assert_eq!(close.outputs[0].payload_json["domain"], "spl_token_account_mutation");
}
#[test]
fn ata_matrix_and_compiled_token_account_projection_ownership_are_equal() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../test-fixtures/contract-matrices/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"),
};
let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix instructions are absent"),
};
for (entry, fact) in super::ATA_LIFECYCLE_ENTRIES {
let row = match instructions.iter().find(|row| {
return row.get("name").and_then(serde_json::Value::as_str)
== std::option::Option::Some(*entry);
}) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix entry {entry} is absent"),
};
let expected = format!("token_accounts:{fact}");
assert!(row["authorizedProjections"].as_array().is_some_and(|values| {
return values.iter().any(|value| return value == expected.as_str());
}));
}
let owner = &matrix["materializationContract"]["projectionOwners"][0];
let matrix_facts = match owner["facts"].as_array() {
std::option::Option::Some(values) => values
.iter()
.filter_map(|value| return value.as_str())
.collect::<std::vec::Vec<_>>(),
std::option::Option::None => panic!("ATA token-account owner facts are absent"),
};
let compiled_facts = super::ATA_LIFECYCLE_ENTRIES
.iter()
.map(|(_, fact)| return *fact)
.collect::<std::vec::Vec<_>>();
assert_eq!(matrix_facts, compiled_facts);
}
#[test]
fn second_ata_materialization_is_byte_for_byte_idempotent() {
let observation = ata_observation("create_idempotent", false, true);
let first = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
let second = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(first, second);
assert_eq!(first.outputs[0].output_key, second.outputs[0].output_key);
assert_eq!(
first.outputs[0].payload_json["idempotenceKey"],
second.outputs[0].payload_json["idempotenceKey"]
);
}
#[test]
fn token_2022_state_snapshot_preserves_base_and_ordered_extensions() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"42","decimals":9,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "11".repeat(108),
value_fields: serde_json::json!({"withheldAmount":"7"}),
},
crate::DcToken2022TlvEntry {
extension_type: 4,
extension_name: "confidential_transfer_mint",
value_hex: "22".repeat(65),
value_fields: serde_json::json!({"autoApproveNewAccounts":true}),
},
],
};
let output = match crate::materializer_token_materialize_token_2022_state_snapshot(
"mint111", 99, &state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("snapshot failed: {error}"),
};
assert_eq!(output.payload_json["stateKind"], "mint");
assert_eq!(output.payload_json["extensionCount"], 2);
assert_eq!(output.payload_json["extensionNames"][0], "transfer_fee_config");
assert_eq!(output.payload_json["extensionNames"][1], "confidential_transfer_mint");
assert_eq!(output.payload_json["containsConfidentialState"], true);
assert_eq!(output.payload_json["confidentialValuesDecrypted"], false);
assert_eq!(output.payload_json["baseFields"]["supply"], "42");
}
#[test]
fn token_2022_state_snapshot_is_idempotent_and_rejects_missing_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({"mint":"mint111","owner":"wallet111","amount":"5"}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
let first = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
let second = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
assert_eq!(first, second);
assert!(
crate::materializer_token_materialize_token_2022_state_snapshot("", 7, &state).is_err()
);
}
}
/// Crate-visible `MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
/// Crate-visible `MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES` component constant.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES;
/// Stable runtime component name for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
/// Crate-visible `MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES` component constant.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES;
/// Stable processor name for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME;
/// Stable projection contract version for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION;
/// Canonical tracing target for Token, ATA and Token-2022 account projections.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS;

View File

@@ -0,0 +1,54 @@
// file: kb-lib/src/materializer/token/accounts/constants.rs
// version: 4
//! Local constants for the token accounts materializer.
/// Stable runtime component name for the token accounts materializer.
pub(crate) const MT_TOKEN_ACCOUNTS_COMPONENT_NAME: &str = "kb-lib.materializer.token.accounts";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TOKEN_ACCOUNTS_PROCESSOR_NAME: &str = "materializer.token.accounts";
/// Stable projection contract version for the token accounts materializer.
pub(crate) const MT_TOKEN_ACCOUNTS_PROJECTION_VERSION: u32 = 3;
/// Canonical tracing target for the token accounts materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS: &str =
"kb-lib.materializer.token.accounts";
/// Component contract for `accepted_families`.
pub(crate) const MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[
crate::MdEventFamily::TokenAccount,
crate::MdEventFamily::TokenMint,
crate::MdEventFamily::TokenBurn,
crate::MdEventFamily::Lifecycle,
];
/// Component contract for `mutation_entries`.
pub(crate) const MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES: &[&str] = &[
"initialize_mint",
"initialize_mint2",
"initialize_account",
"initialize_account2",
"initialize_account3",
"transfer",
"transfer_checked",
"approve",
"approve_checked",
"revoke",
"mint_to",
"mint_to_checked",
"burn",
"burn_checked",
"close_account",
"freeze_account",
"thaw_account",
"sync_native",
"initialize_immutable_owner",
"withdraw_excess_lamports",
"unwrap_lamports",
"initialize_non_transferable_mint",
"reallocate",
];
/// Component contract for `ata_lifecycle_entries`.
pub(crate) const MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES: &[(&str, &str)] = &[
("create", "ata_created"),
("create_idempotent", "ata_created_or_reused_idempotently"),
("recover_nested", "nested_ata_recovered"),
];

View File

@@ -0,0 +1,809 @@
// file: kb-lib/src/materializer/token/accounts/materializer.rs
// version: 9
//! Stable committed SPL Token mutation and ATA lifecycle projections.
/// Stable committed classic SPL Token account and mint mutation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTokenAccountsMaterializer;
impl crate::MtMaterializer for crate::MtTokenAccountsMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_TOKEN_ACCOUNTS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenAccountsMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_token_account_projection_refused",
"failed or uncommitted SPL Token or ATA observations cannot create mutable outputs",
);
}
if is_ata_event(&observation.event) {
return materialize_ata(observation);
}
let parameters = observation
.payload_json
.get("parameters")
.cloned()
.unwrap_or(serde_json::Value::Null);
let accounts = observation
.payload_json
.get("accounts")
.cloned()
.unwrap_or(serde_json::Value::Null);
let authority = observation
.payload_json
.get("authority")
.cloned()
.unwrap_or(serde_json::Value::Null);
let operation = observation.event.event_name.0.clone();
let output = crate::MtApiMaterializedOutput {
output_key: format!("token_account_mutation:{operation}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_account_mutation",
"projectionSemantics": "committed_instruction_mutation_not_final_account_snapshot",
"idempotenceKey": format!(
"spl_token:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
operation
),
"operation": operation,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"mint": account_key_for_role(&accounts, "mint"),
"amountRaw": parameters.get("amountRaw").cloned().unwrap_or(serde_json::Value::Null),
"decimals": parameters.get("decimals").cloned().unwrap_or(serde_json::Value::Null),
"parameters": parameters,
"accounts": accounts,
"authority": authority,
"committed": true,
"transactionFinalAccountStateCaptured": false,
"coreBalanceChangesDuplicated": false,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_token_account_mutation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation = %observation.event.event_name.0,
"materialize committed classic SPL Token mutation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.token")
|| (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
&& event.surface_code.0 == "spl.token_2022");
let token_mutation = token_program
&& crate::MT_TOKEN_ACCOUNTS_MUTATION_ENTRIES.contains(&event.event_name.0.as_str());
let ata = is_ata_event(event);
return crate::MT_TOKEN_ACCOUNTS_ACCEPTED_FAMILIES.contains(&event.event_family)
&& (token_mutation || ata);
}
fn is_ata_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID
&& event.surface_code.0 == "spl.associated_token_account"
&& event.event_family == crate::MdEventFamily::Lifecycle
&& crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES
.iter()
.any(|(entry, _)| return *entry == event.event_name.0);
}
fn materialize_ata(
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
let operation = observation.event.event_name.0.as_str();
let addresses = match observation.payload_json.get("addresses") {
std::option::Option::Some(value) if value.is_object() => value,
_ => {
return crate::MtApiMaterializerExecutionResult::refused(
"ata_addresses_missing",
"committed ATA lifecycle projection requires the decoded address contract",
);
},
};
let validation = validate_ata_addresses(operation, addresses);
if let std::result::Result::Err((code, message)) = validation {
return crate::MtApiMaterializerExecutionResult::refused(code, message);
}
let lifecycle_kind = match crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES
.iter()
.find(|(entry, _)| return *entry == operation)
{
std::option::Option::Some((_, value)) => *value,
std::option::Option::None => {
return crate::MtApiMaterializerExecutionResult::ignored();
},
};
let associated_token_account = if operation == "recover_nested" {
observed_address(addresses, "nestedAssociatedTokenAccount")
} else {
observed_address(addresses, "associatedTokenAccount")
};
let token_program = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.cloned()
.unwrap_or(serde_json::Value::Null);
let wallet = addresses.get("walletOwner").cloned().unwrap_or(serde_json::Value::Null);
let mint = if operation == "recover_nested" {
addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null)
} else {
addresses.get("mint").cloned().unwrap_or(serde_json::Value::Null)
};
let output = crate::MtApiMaterializedOutput {
output_key: format!("ata_lifecycle:{lifecycle_kind}:0"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_associated_token_account_lifecycle",
"projectionSemantics": "committed_associated_address_lifecycle_not_token_state_snapshot",
"idempotenceKey": format!(
"ata:{}:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
lifecycle_kind,
associated_token_account.as_str().unwrap_or("")
),
"operation": operation,
"lifecycleKind": lifecycle_kind,
"associatedTokenAccount": associated_token_account,
"walletOwner": wallet,
"mint": mint,
"tokenProgramId": token_program,
"fundingAccount": addresses.get("fundingAccount").cloned().unwrap_or(serde_json::Value::Null),
"ownerMint": addresses.get("ownerMint").cloned().unwrap_or(serde_json::Value::Null),
"nestedMint": addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null),
"ownerAssociatedTokenAccount": observed_address(addresses, "ownerAssociatedTokenAccount"),
"nestedAssociatedTokenAccount": observed_address(addresses, "nestedAssociatedTokenAccount"),
"walletNestedMintAssociatedTokenAccount": observed_address(addresses, "walletNestedMintAssociatedTokenAccount"),
"idempotentOutcome": if operation == "create_idempotent" {
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
} else {
"not_applicable"
},
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"programId": observation.event.program_id.0.clone(),
"committed": true,
"transactionFinalAccountStateCaptured": false,
"tokenBalanceReconstructed": false,
"splTokenCpiMutationsDuplicated": false,
"ownership": {
"ataParent": "associated_address_lifecycle_only",
"splTokenCpi": "initialize_transfer_and_close_mutations"
},
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS,
action = "materialize_ata_lifecycle",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
operation,
lifecycle_kind,
"materialize committed ATA lifecycle without duplicating SPL Token CPI mutations"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
fn validate_ata_addresses(
operation: &str,
addresses: &serde_json::Value,
) -> std::result::Result<(), (&'static str, &'static str)> {
let token_supported = addresses
.get("tokenProgram")
.and_then(|value| return value.get("supported"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let token_program_id = addresses
.get("tokenProgram")
.and_then(|value| return value.get("programId"))
.and_then(serde_json::Value::as_str);
let token_program_exact = matches!(
token_program_id,
std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID)
| std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID)
);
if !token_supported || !token_program_exact {
return std::result::Result::Err((
"ata_token_program_not_supported",
"ATA lifecycle projection requires classic SPL Token or Token-2022",
));
}
let fields: &[&str] = if operation == "recover_nested" {
&[
"ownerAssociatedTokenAccount",
"nestedAssociatedTokenAccount",
"walletNestedMintAssociatedTokenAccount",
]
} else {
&["associatedTokenAccount"]
};
for field in fields {
let valid = addresses
.get(*field)
.and_then(|value| return value.get("valid"))
.and_then(serde_json::Value::as_bool)
== std::option::Option::Some(true);
let observed = observed_address(addresses, field);
if !valid || observed.as_str().is_none_or(|value| return value.is_empty()) {
return std::result::Result::Err((
"ata_derived_address_invalid",
"ATA lifecycle projection requires every canonical address validation to pass",
));
}
}
let identity_fields: &[&str] = if operation == "recover_nested" {
&["walletOwner", "ownerMint", "nestedMint"]
} else {
&["walletOwner", "mint"]
};
if identity_fields.iter().any(|field| {
return addresses
.get(*field)
.and_then(serde_json::Value::as_str)
.is_none_or(|value| return value.is_empty());
}) {
return std::result::Result::Err((
"ata_identity_field_missing",
"ATA lifecycle projection requires wallet and mint identities",
));
}
return std::result::Result::Ok(());
}
fn observed_address(addresses: &serde_json::Value, field: &str) -> serde_json::Value {
return addresses
.get(field)
.and_then(|value| return value.get("observed"))
.cloned()
.unwrap_or(serde_json::Value::Null);
}
fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
let value = accounts.as_array().and_then(|rows| {
return rows.iter().find_map(|row| {
if row.get("role").and_then(serde_json::Value::as_str)
!= std::option::Option::Some(role)
{
return std::option::Option::None;
}
return row.get("accountKey").and_then(serde_json::Value::as_str);
});
});
return match value {
std::option::Option::Some(account_key) => {
serde_json::Value::String(account_key.to_string())
},
std::option::Option::None => serde_json::Value::Null,
};
}
/// Materialize one already parsed Token-2022 account snapshot without inferring hidden state.
pub fn materializer_token_materialize_token_2022_state_snapshot(
account_key: &str,
slot: u64,
state: &crate::DcToken2022State,
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
if account_key.is_empty() {
return std::result::Result::Err(
"Token-2022 state snapshot requires a non-empty account key".to_string(),
);
}
let kind = match state.kind {
crate::DcToken2022StateKind::Mint => "mint",
crate::DcToken2022StateKind::Account => "account",
crate::DcToken2022StateKind::Multisig => "multisig",
};
let extensions = state
.extensions
.iter()
.map(|entry| {
return serde_json::json!({
"extensionType": entry.extension_type,
"extensionName": entry.extension_name,
"valueHex": entry.value_hex,
"valueFields": entry.value_fields,
});
})
.collect::<std::vec::Vec<_>>();
let extension_names = state
.extensions
.iter()
.map(|entry| return entry.extension_name)
.collect::<std::vec::Vec<_>>();
let contains_confidential_state = state.extensions.iter().any(|entry| {
return entry.extension_name.starts_with("confidential_")
|| entry.extension_name == "confidential_mint_burn";
});
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
output_key: format!("token_2022_state_snapshot:{account_key}:{slot}"),
family: crate::MdMaterializedEventFamily::TokenAccount,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TOKEN_ACCOUNTS_PROJECTION_VERSION,
"domain": "spl_token_2022_account_state",
"projectionSemantics": "authoritative_bounded_account_snapshot",
"idempotenceKey": format!("token_2022_state:{account_key}:{slot}"),
"accountKey": account_key,
"slot": slot,
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"stateKind": kind,
"baseFields": state.base_fields,
"baseHex": state.base_hex,
"accountType": state.account_type,
"extensionCount": state.extensions.len(),
"extensionNames": extension_names,
"extensions": extensions,
"containsConfidentialState": contains_confidential_state,
"confidentialValuesDecrypted": false,
"finalAccountStateCaptured": true,
"provenance": {
"processorName": crate::MT_TOKEN_ACCOUNTS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"source": "bounded_token_2022_state_parser"
}
}),
});
}
#[cfg(test)]
mod tests {
fn observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: format!("token:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()),
protocol_code: crate::MdProtocolCode("spl.token".to_string()),
surface_code: crate::MdSurfaceCode("spl.token".to_string()),
event_code: crate::MdEventCode(format!("spl.token.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: family,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"parameters": {"amountRaw":"18446744073709551615","decimals":9},
"accounts": [{"role":"mint","accountKey":"mint111"}],
"authority": {"form":"single"}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"error":"failed"}))
} else {
std::option::Option::None
},
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
fn token_2022_observation(
entry: &str,
family: crate::MdEventFamily,
failed: bool,
) -> crate::DcApiDecodedObservation {
let mut observation = observation(entry, family, failed);
observation.event.program_id =
crate::MdProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
observation.event.protocol_code = crate::MdProtocolCode("spl.token_2022".to_string());
observation.event.surface_code = crate::MdSurfaceCode("spl.token_2022".to_string());
observation.event.event_code = crate::MdEventCode(format!("spl.token_2022.{entry}"));
observation.event_key = format!("token_2022:{entry}:0");
return observation;
}
fn ata_observation(entry: &str, failed: bool, valid: bool) -> crate::DcApiDecodedObservation {
let addresses = if entry == "recover_nested" {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_PROGRAM_ID,
"kind": "spl_token_classic",
"supported": true
},
"ownerAssociatedTokenAccount": {"expected":"ownerAta","observed":"ownerAta","valid":valid},
"nestedAssociatedTokenAccount": {"expected":"nestedAta","observed":"nestedAta","valid":valid},
"walletNestedMintAssociatedTokenAccount": {"expected":"destinationAta","observed":"destinationAta","valid":valid},
"walletOwner": "wallet111",
"ownerMint": "ownerMint111",
"nestedMint": "nestedMint111"
})
} else {
serde_json::json!({
"tokenProgram": {
"programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
"kind": "token_2022",
"supported": true
},
"associatedTokenAccount": {"expected":"ata111","observed":"ata111","valid":valid},
"fundingAccount": "payer111",
"walletOwner": "wallet111",
"mint": "mint111"
})
};
return crate::DcApiDecodedObservation {
event_key: format!("ata:{entry}:0"),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("0".to_string()),
program_id: crate::MdProgramId(
kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(),
),
protocol_code: crate::MdProtocolCode("spl.associated_token_account".to_string()),
surface_code: crate::MdSurfaceCode("spl.associated_token_account".to_string()),
event_code: crate::MdEventCode(format!("spl.associated_token_account.{entry}")),
event_name: crate::MdEventName(entry.to_string()),
event_family: crate::MdEventFamily::Lifecycle,
source_kind: crate::MdEventSourceKind::Instruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({"addresses":addresses}),
transaction_failed: failed,
transaction_error: std::option::Option::None,
observation_committed: !failed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["fixture".to_string()],
},
};
}
#[test]
fn committed_checked_transfer_preserves_exact_amount_mint_and_path() {
let observation =
observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs[0].payload_json["amountRaw"], "18446744073709551615");
assert_eq!(result.outputs[0].payload_json["mint"], "mint111");
assert_eq!(result.outputs[0].payload_json["instructionPath"], "1/0");
assert_eq!(result.outputs[0].payload_json["transactionFinalAccountStateCaptured"], false);
}
#[test]
fn committed_token_2022_transfer_has_the_same_single_projection_owner() {
let observation =
token_2022_observation("transfer_checked", crate::MdEventFamily::TokenAccount, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["operation"], "transfer_checked");
assert_eq!(
result.outputs[0].payload_json["programId"],
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID
);
}
#[test]
fn failed_mutation_is_refused() {
let observation = observation("burn", crate::MdEventFamily::TokenBurn, true);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn batch_parent_admin_and_conversion_events_are_not_duplicated() {
for (entry, family) in [
("batch", crate::MdEventFamily::Audit),
("set_authority", crate::MdEventFamily::Admin),
("amount_to_ui_amount", crate::MdEventFamily::Audit),
] {
let observation = observation(entry, family, false);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
}
#[test]
fn every_ata_variant_has_one_exact_lifecycle_projection() {
for (entry, expected_kind, expected_ata, expected_token_program) in [
("create", "ata_created", "ata111", kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID),
(
"create_idempotent",
"ata_created_or_reused_idempotently",
"ata111",
kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,
),
(
"recover_nested",
"nested_ata_recovered",
"nestedAta",
kb_program_ids::SPL_TOKEN_PROGRAM_ID,
),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation(entry, false, true),
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(result.outputs.len(), 1);
assert_eq!(result.outputs[0].payload_json["lifecycleKind"], expected_kind);
assert_eq!(result.outputs[0].payload_json["associatedTokenAccount"], expected_ata);
assert_eq!(result.outputs[0].payload_json["tokenProgramId"], expected_token_program);
assert_eq!(result.outputs[0].payload_json["tokenBalanceReconstructed"], false);
assert_eq!(result.outputs[0].payload_json["splTokenCpiMutationsDuplicated"], false);
}
}
#[test]
fn idempotent_parent_does_not_invent_created_or_reused_branch() {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("create_idempotent", false, true),
);
assert_eq!(
result.outputs[0].payload_json["idempotentOutcome"],
"created_or_reused_not_distinguishable_from_parent_instruction_alone"
);
assert!(result.outputs[0].payload_json.get("reused").is_none());
assert!(result.outputs[0].payload_json.get("created").is_none());
}
#[test]
fn uncommitted_or_invalid_ata_lifecycle_is_refused() {
for observation in [
ata_observation("create", true, true),
ata_observation("recover_nested", false, false),
] {
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
}
#[test]
fn ata_parent_and_classic_token_cpi_children_own_distinct_facts() {
let parent = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&ata_observation("recover_nested", false, true),
);
let transfer = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("transfer", crate::MdEventFamily::TokenAccount, false),
);
let close = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation("close_account", crate::MdEventFamily::TokenAccount, false),
);
assert_eq!(parent.outputs[0].payload_json["lifecycleKind"], "nested_ata_recovered");
assert_eq!(
parent.outputs[0].payload_json["ownership"]["ataParent"],
"associated_address_lifecycle_only"
);
assert!(parent.outputs[0].payload_json.get("amountRaw").is_none());
assert_eq!(transfer.outputs[0].payload_json["operation"], "transfer");
assert_eq!(close.outputs[0].payload_json["operation"], "close_account");
assert_eq!(transfer.outputs[0].payload_json["domain"], "spl_token_account_mutation");
assert_eq!(close.outputs[0].payload_json["domain"], "spl_token_account_mutation");
}
#[test]
fn ata_matrix_and_compiled_token_account_projection_ownership_are_equal() {
let matrix: serde_json::Value = match serde_json::from_str(include_str!(
"../../../../../test-fixtures/contract-matrices/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json"
)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"),
};
let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix instructions are absent"),
};
for (entry, fact) in crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES {
let row = match instructions.iter().find(|row| {
return row.get("name").and_then(serde_json::Value::as_str)
== std::option::Option::Some(*entry);
}) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("ATA matrix entry {entry} is absent"),
};
let expected = format!("token_accounts:{fact}");
assert!(row["authorizedProjections"].as_array().is_some_and(|values| {
return values.iter().any(|value| return value == expected.as_str());
}));
}
let owner = &matrix["materializationContract"]["projectionOwners"][0];
let matrix_facts = match owner["facts"].as_array() {
std::option::Option::Some(values) => values
.iter()
.filter_map(|value| return value.as_str())
.collect::<std::vec::Vec<_>>(),
std::option::Option::None => panic!("ATA token-account owner facts are absent"),
};
let compiled_facts = crate::MT_TOKEN_ACCOUNTS_ATA_LIFECYCLE_ENTRIES
.iter()
.map(|(_, fact)| return *fact)
.collect::<std::vec::Vec<_>>();
assert_eq!(matrix_facts, compiled_facts);
}
#[test]
fn second_ata_materialization_is_byte_for_byte_idempotent() {
let observation = ata_observation("create_idempotent", false, true);
let first = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
let second = crate::MtApiEventMaterializer::materialize(
&crate::MtTokenAccountsMaterializer,
&observation,
);
assert_eq!(first, second);
assert_eq!(first.outputs[0].output_key, second.outputs[0].output_key);
assert_eq!(
first.outputs[0].payload_json["idempotenceKey"],
second.outputs[0].payload_json["idempotenceKey"]
);
}
#[test]
fn token_2022_state_snapshot_preserves_base_and_ordered_extensions() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Mint,
base_fields: serde_json::json!({"supply":"42","decimals":9,"initialized":true}),
base_hex: "00".repeat(82),
account_type: std::option::Option::Some(1),
extensions: std::vec![
crate::DcToken2022TlvEntry {
extension_type: 1,
extension_name: "transfer_fee_config",
value_hex: "11".repeat(108),
value_fields: serde_json::json!({"withheldAmount":"7"}),
},
crate::DcToken2022TlvEntry {
extension_type: 4,
extension_name: "confidential_transfer_mint",
value_hex: "22".repeat(65),
value_fields: serde_json::json!({"autoApproveNewAccounts":true}),
},
],
};
let output = match crate::materializer_token_materialize_token_2022_state_snapshot(
"mint111", 99, &state,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("snapshot failed: {error}"),
};
assert_eq!(output.payload_json["stateKind"], "mint");
assert_eq!(output.payload_json["extensionCount"], 2);
assert_eq!(output.payload_json["extensionNames"][0], "transfer_fee_config");
assert_eq!(output.payload_json["extensionNames"][1], "confidential_transfer_mint");
assert_eq!(output.payload_json["containsConfidentialState"], true);
assert_eq!(output.payload_json["confidentialValuesDecrypted"], false);
assert_eq!(output.payload_json["baseFields"]["supply"], "42");
}
#[test]
fn token_2022_state_snapshot_is_idempotent_and_rejects_missing_identity() {
let state = crate::DcToken2022State {
kind: crate::DcToken2022StateKind::Account,
base_fields: serde_json::json!({"mint":"mint111","owner":"wallet111","amount":"5"}),
base_hex: "00".repeat(165),
account_type: std::option::Option::None,
extensions: std::vec::Vec::new(),
};
let first = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
let second = crate::materializer_token_materialize_token_2022_state_snapshot(
"account111",
7,
&state,
);
assert_eq!(first, second);
assert!(
crate::materializer_token_materialize_token_2022_state_snapshot("", 7, &state).is_err()
);
}
}

View File

@@ -1,11 +0,0 @@
// file: kb-lib/src/materializer/token/constants.rs
// version: 3
//! Local constants for classic SPL Token account projections.
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TOKEN_ACCOUNTS_PROCESSOR_NAME: &str = "materializer.token.accounts";
/// Stable projection payload contract version.
pub(crate) const MT_TOKEN_ACCOUNTS_PROJECTION_VERSION: u32 = 3;
/// Canonical tracing target for this component.
pub(crate) const TRACING_TARGET_MATERIALIZER_TOKEN_ACCOUNTS: &str = "kb-lib.materializer.token";

View File

@@ -1,61 +1,17 @@
// file: kb-lib/src/materializer/token/metadata_risk.rs
// version: 4
// version: 5
//! Reserved token-metadata risk materializer.
//! Token-metadata risk materializer component facade.
/// Reserved materializer for token-metadata risk observations.
#[derive(Clone, Debug, Default)]
pub struct MtTokenMetadataRiskMaterializer;
mod constants;
mod materializer;
impl crate::MtMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.token.metadata_risk";
}
/// Reserved token-metadata risk materializer.
pub use self::materializer::MtTokenMetadataRiskMaterializer;
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::TokenMetadataRisk,
}]);
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.token.metadata_risk".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
/// Stable runtime component name for the reserved token-metadata risk materializer.
pub(crate) use self::constants::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
/// Stable processor name for the reserved token-metadata risk materializer.
pub(crate) use self::constants::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME;

View File

@@ -0,0 +1,12 @@
// file: kb-lib/src/materializer/token/metadata_risk/constants.rs
// version: 2
//! Local constants for the token metadata_risk materializer.
/// Stable runtime component name for the token metadata_risk materializer.
pub(crate) const MT_TOKEN_METADATA_RISK_COMPONENT_NAME: &str =
"kb-lib.materializer.token.metadata_risk";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TOKEN_METADATA_RISK_PROCESSOR_NAME: &str = "materializer.token.metadata_risk";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -0,0 +1,56 @@
// file: kb-lib/src/materializer/token/metadata_risk/materializer.rs
// version: 5
//! Reserved token-metadata risk materializer.
/// Reserved materializer for token-metadata risk observations.
#[derive(Clone, Debug, Default)]
pub struct MtTokenMetadataRiskMaterializer;
impl crate::MtMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_TOKEN_METADATA_RISK_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
return false;
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTokenMetadataRiskMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TOKEN_METADATA_RISK_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_TOKEN_METADATA_RISK_ACCEPTED_FAMILIES;
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
_observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult::ignored();
}
}

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/trades.rs
// version: 3
// version: 4
//! `trades` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved trades materializer.
pub use self::core::MtTradesMaterializer;
pub use self::materializer::MtTradesMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_TRADES_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `trades` materializer.
pub(crate) use self::constants::MT_TRADES_COMPONENT_NAME;
/// Stable processor name for the `trades` materializer.
pub(crate) use self::constants::MT_TRADES_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/trades/constants.rs
// version: 1
//! Local constants for the trades materializer.
/// Stable runtime component name for the trades materializer.
pub(crate) const MT_TRADES_COMPONENT_NAME: &str = "kb-lib.materializer.trades";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TRADES_PROCESSOR_NAME: &str = "materializer.trades";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_TRADES_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

View File

@@ -1,15 +1,15 @@
// file: kb-lib/src/materializer/trades/core.rs
// version: 4
// file: kb-lib/src/materializer/trades/materializer.rs
// version: 5
//! Reserved trades materializer.
/// Reserved materializer for trade observations.
/// Reserved materializer for trades observations.
#[derive(Clone, Debug, Default)]
pub struct MtTradesMaterializer;
impl crate::MtMaterializer for crate::MtTradesMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.trades";
return crate::MT_TRADES_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
@@ -22,27 +22,22 @@ impl crate::MtMaterializer for crate::MtTradesMaterializer {
fn materialize_event(
&self,
event: &crate::MdDecodedProtocolEvent,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec![crate::MdMaterializedEvent {
signature: event.signature.clone(),
slot: event.slot,
decoded_family: event.event_family,
materialized_family: crate::MdMaterializedEventFamily::Trade,
}]);
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTradesMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: "kb-lib.materializer.trades".to_string(),
name: crate::MT_TRADES_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return &[];
return crate::MT_TRADES_ACCEPTED_FAMILIES;
}
fn transaction_policy(

View File

@@ -1,19 +1,24 @@
// file: kb-lib/src/materializer/transaction.rs
// version: 3
// version: 4
//! `transaction` materializer family.
mod annotations;
mod constants;
/// Stable committed Memo transaction annotation materializer.
pub use self::annotations::MtTransactionAnnotationMaterializer;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Stable runtime component name for transaction annotations.
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Maximum Memo payload accepted by transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES` component constant.
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Stable processor name for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
pub(crate) use self::annotations::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for transaction annotations.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;
pub(crate) use self::annotations::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;

View File

@@ -1,611 +1,25 @@
// file: kb-lib/src/materializer/transaction/annotations.rs
// version: 5
// version: 6
//! Exact committed SPL Memo transaction annotation projection.
//! Committed Memo transaction annotation materializer component.
const ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[crate::MdEventFamily::Audit];
const MEMO_ENTRY_NAMES: &[&str] = &["add_memo", "memo_intent", "invalid_memo_attempt"];
mod constants;
mod materializer;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum MemoGeneration {
V1,
V3,
V4,
}
/// Stable committed Memo transaction annotation materializer.
pub use self::materializer::MtTransactionAnnotationMaterializer;
impl MemoGeneration {
fn from_event(event: &crate::MdDecodedProtocolEvent) -> std::option::Option<Self> {
if event.program_id.0 == kb_program_ids::SPL_MEMO_V1_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v1"
{
return std::option::Option::Some(Self::V1);
}
if event.program_id.0 == kb_program_ids::SPL_MEMO_V3_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v3"
{
return std::option::Option::Some(Self::V3);
}
if event.program_id.0 == kb_program_ids::SPL_MEMO_V4_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v4"
{
return std::option::Option::Some(Self::V4);
}
return std::option::Option::None;
}
fn code(self) -> &'static str {
return match self {
Self::V1 => "v1",
Self::V3 => "v3",
Self::V4 => "v4",
};
}
fn signer_status(self) -> &'static str {
return match self {
Self::V1 => "not_applicable",
Self::V3 | Self::V4 => "valid",
};
}
}
struct MemoProjection {
generation: MemoGeneration,
memo_text: std::string::String,
payload_length_bytes: u64,
payload_sha256: std::string::String,
required_signers: std::vec::Vec<std::string::String>,
observed_signers: std::vec::Vec<std::string::String>,
verified_signers: std::vec::Vec<std::string::String>,
signer_validation_status: std::string::String,
}
/// Stable transaction annotation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTransactionAnnotationMaterializer;
impl crate::MtMaterializer for crate::MtTransactionAnnotationMaterializer {
fn materializer_name(&self) -> &'static str {
return "kb-lib.materializer.transaction.annotations";
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTransactionAnnotationMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_transaction_annotation_refused",
"failed or uncommitted Memo observations cannot create transaction annotations",
);
}
if observation.event.event_name.0 != "add_memo" {
return crate::MtApiMaterializerExecutionResult::refused(
"invalid_transaction_annotation_source_refused",
"only a committed add_memo observation can create a transaction annotation",
);
}
let projection_result = parse_projection(observation);
let projection = match projection_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => {
return failed("memo_annotation_payload_invalid", message);
},
};
let idempotence_key = format!(
"spl_memo:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
projection.payload_sha256
);
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_annotation:spl_memo:0".to_string(),
family: crate::MdMaterializedEventFamily::TransactionAnnotation,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION,
"domain": "transaction_annotation",
"annotationKind": "spl.memo",
"idempotenceKey": idempotence_key,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"sourceKind": match observation.event.source_kind {
crate::MdEventSourceKind::InnerInstruction => "inner_instruction",
_ => "instruction"
},
"generation": projection.generation.code(),
"programId": observation.event.program_id.0.clone(),
"text": projection.memo_text,
"payloadLengthBytes": projection.payload_length_bytes,
"payloadSha256": projection.payload_sha256,
"signers": {
"validationStatus": projection.signer_validation_status,
"required": projection.required_signers,
"observed": projection.observed_signers,
"verified": projection.verified_signers
},
"committed": true,
"provenance": {
"processorName": crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceProtocol": observation.event.protocol_code.0.clone(),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS,
action = "materialize_transaction_annotation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
program_id = %observation.event.program_id.0,
generation = projection.generation.code(),
payload_length_bytes = projection.payload_length_bytes,
output_count = 1_usize,
"materialize committed SPL Memo transaction annotation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Audit
&& event.protocol_code.0 == "spl.memo"
&& (event.source_kind == crate::MdEventSourceKind::Instruction
|| event.source_kind == crate::MdEventSourceKind::InnerInstruction)
&& MemoGeneration::from_event(event).is_some()
&& MEMO_ENTRY_NAMES.contains(&event.event_name.0.as_str());
}
fn parse_projection(
observation: &crate::DcApiDecodedObservation,
) -> std::result::Result<MemoProjection, std::string::String> {
let generation = match MemoGeneration::from_event(&observation.event) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"Memo generation and Program ID do not match".to_string(),
);
},
};
let payload = &observation.payload_json;
if payload.get("committed").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
{
return std::result::Result::Err("Memo payload is not marked committed".to_string());
}
if payload.get("transactionSucceeded").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
|| payload.get("payloadComplete").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
|| string_field(payload, "runtimeValidation")
!= std::option::Option::Some("proven_by_successful_transaction")
|| payload
.get("utf8Validation")
.and_then(|value| return string_field(value, "status"))
!= std::option::Option::Some("valid")
{
return std::result::Result::Err(
"Memo successful runtime validation proof is incomplete".to_string(),
);
}
if string_field(payload, "generation") != std::option::Option::Some(generation.code()) {
return std::result::Result::Err(
"Memo payload generation does not match its surface".to_string(),
);
}
if string_field(payload, "programId")
!= std::option::Option::Some(observation.event.program_id.0.as_str())
{
return std::result::Result::Err(
"Memo payload Program ID does not match its event".to_string(),
);
}
if string_field(payload, "instructionPath")
!= std::option::Option::Some(observation.event.instruction_path.0.as_str())
{
return std::result::Result::Err(
"Memo payload instruction path does not match its event".to_string(),
);
}
let memo_text = match string_field(payload, "memoText") {
std::option::Option::Some(value) => value.to_string(),
std::option::Option::None => {
return std::result::Result::Err("committed Memo has no UTF-8 text".to_string());
},
};
let payload_length_bytes =
match payload.get("payloadLengthBytes").and_then(serde_json::Value::as_u64) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err("committed Memo has no byte length".to_string());
},
};
let measured_length = match u64::try_from(memo_text.len()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"Memo text length conversion failed: {error}"
));
},
};
if measured_length != payload_length_bytes {
return std::result::Result::Err(
"Memo byte length does not match its UTF-8 text".to_string(),
);
}
if payload_length_bytes > crate::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES {
return std::result::Result::Err(
"Memo byte length exceeds the projection limit".to_string(),
);
}
let payload_sha256 = match string_field(payload, "payloadSha256") {
std::option::Option::Some(value) if is_sha256(value) => value.to_string(),
_ => return std::result::Result::Err("Memo payload SHA-256 is invalid".to_string()),
};
let signer_validation = match payload.get("signerValidation") {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err("Memo signer validation is absent".to_string());
},
};
let signer_validation_status = match string_field(signer_validation, "status") {
std::option::Option::Some(value) if value == generation.signer_status() => {
value.to_string()
},
_ => {
return std::result::Result::Err(
"Memo signer validation status is inconsistent".to_string(),
);
},
};
let missing_signers = match string_array(signer_validation, "missingRequiredSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
if !missing_signers.is_empty() {
return std::result::Result::Err("committed Memo reports missing signers".to_string());
}
let required_signers = match string_array(signer_validation, "orderedRequiredSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
let observed_signers = match string_array(signer_validation, "orderedObservedSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
let verified_signers = if generation == MemoGeneration::V1 {
if !required_signers.is_empty() {
return std::result::Result::Err("Memo v1 cannot require signers".to_string());
}
std::vec::Vec::new()
} else {
if required_signers != observed_signers {
return std::result::Result::Err(
"committed Memo required and observed signers differ".to_string(),
);
}
observed_signers.clone()
};
return std::result::Result::Ok(MemoProjection {
generation,
memo_text,
payload_length_bytes,
payload_sha256,
required_signers,
observed_signers,
verified_signers,
signer_validation_status,
});
}
fn string_field<'a>(value: &'a serde_json::Value, key: &str) -> std::option::Option<&'a str> {
return value.get(key).and_then(serde_json::Value::as_str);
}
fn string_array(
value: &serde_json::Value,
key: &str,
) -> std::result::Result<std::vec::Vec<std::string::String>, std::string::String> {
let values = match value.get(key).and_then(serde_json::Value::as_array) {
std::option::Option::Some(values) => values,
std::option::Option::None => {
return std::result::Result::Err(format!("Memo signer field {key} is not an array"));
},
};
let mut output = std::vec::Vec::with_capacity(values.len());
for item in values {
let text = match item.as_str() {
std::option::Option::Some(text) if !text.trim().is_empty() => text,
_ => {
return std::result::Result::Err(format!(
"Memo signer field {key} contains an invalid pubkey"
));
},
};
output.push(text.to_string());
}
return std::result::Result::Ok(output);
}
fn is_sha256(value: &str) -> bool {
return value.len() == 64
&& value
.as_bytes()
.iter()
.all(|byte| return byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase());
}
fn failed(code: &str, message: std::string::String) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Failed,
outputs: std::vec::Vec::new(),
diagnostics: std::vec![crate::MtApiMaterializerDiagnostic {
code: code.to_string(),
message,
retriable: false,
}],
};
}
#[cfg(test)]
mod tests {
fn observation(
program_id: &str,
surface_code: &str,
generation: &str,
event_name: &str,
failed: bool,
committed: bool,
required_signers: serde_json::Value,
observed_signers: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: "memo:0".to_string(),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(program_id.to_string()),
protocol_code: crate::MdProtocolCode("spl.memo".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{event_name}")),
event_name: crate::MdEventName(event_name.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"generation": generation,
"programId": program_id,
"instructionPath": "1/0",
"transactionSucceeded": !failed,
"committed": committed,
"memoText": "memo",
"payloadLengthBytes": 4,
"payloadSha256": "1111111111111111111111111111111111111111111111111111111111111111",
"payloadComplete": true,
"utf8Validation": {"status":"valid","invalidFromByte":null},
"signerValidation": {
"status": if generation == "v1" { "not_applicable" } else { "valid" },
"orderedRequiredSigners": required_signers,
"orderedObservedSigners": observed_signers,
"missingRequiredSigners": []
},
"runtimeValidation": if failed {
"not_proven_transaction_failed"
} else {
"proven_by_successful_transaction"
}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]}))
} else {
std::option::Option::None
},
observation_committed: committed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["memo".to_string()],
},
};
}
fn v4() -> crate::DcApiDecodedObservation {
return observation(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl.memo.v4",
"v4",
"add_memo",
false,
true,
serde_json::json!(["signer", "signer"]),
serde_json::json!(["signer", "signer"]),
);
}
#[test]
fn committed_v4_memo_becomes_deterministic_transaction_annotation() {
let materializer = crate::MtTransactionAnnotationMaterializer;
let first = crate::MtApiEventMaterializer::materialize(&materializer, &v4());
let second = crate::MtApiEventMaterializer::materialize(&materializer, &v4());
assert_eq!(first, second);
assert_eq!(first.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(first.outputs.len(), 1);
assert_eq!(
first.outputs[0].family,
crate::MdMaterializedEventFamily::TransactionAnnotation
);
assert_eq!(
first.outputs[0].payload_json["signers"]["verified"],
serde_json::json!(["signer", "signer"])
);
assert_eq!(first.outputs[0].payload_json["committed"], true);
assert!(first.validate().is_ok());
}
#[test]
fn exact_v1_v3_and_v4_surfaces_are_accepted() {
for (program_id, surface, generation) in [
(kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, "spl.memo.v1", "v1"),
(kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, "spl.memo.v3", "v3"),
(kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, "spl.memo.v4", "v4"),
] {
let observation = observation(
program_id,
surface,
generation,
"add_memo",
false,
true,
serde_json::json!([]),
serde_json::json!([]),
);
assert!(crate::MtApiEventMaterializer::accepts_observation(
&crate::MtTransactionAnnotationMaterializer,
&observation,
));
}
}
#[test]
fn v1_observed_accounts_are_not_claimed_as_runtime_verified() {
let observation = observation(
kb_program_ids::SPL_MEMO_V1_PROGRAM_ID,
"spl.memo.v1",
"v1",
"add_memo",
false,
true,
serde_json::json!([]),
serde_json::json!(["observed_only"]),
);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.outputs[0].payload_json["signers"]["verified"], serde_json::json!([]));
assert_eq!(
result.outputs[0].payload_json["signers"]["observed"],
serde_json::json!(["observed_only"])
);
}
#[test]
fn failed_or_uncommitted_memo_is_refused_without_output() {
let observation = observation(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl.memo.v4",
"v4",
"memo_intent",
true,
false,
serde_json::json!([]),
serde_json::json!([]),
);
let materializer = crate::MtTransactionAnnotationMaterializer;
let policy =
crate::materializer_api_validate_materialization_policy(&materializer, &observation);
assert!(policy.is_err());
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn malformed_committed_payload_fails_closed() {
let mut observation = v4();
observation.payload_json["payloadSha256"] = serde_json::Value::String("bad".to_string());
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Failed);
assert!(result.outputs.is_empty());
assert_eq!(result.diagnostics[0].code, "memo_annotation_payload_invalid");
}
#[test]
fn foreign_audit_observation_is_not_accepted() {
let mut observation = v4();
observation.event.protocol_code = crate::MdProtocolCode("foreign".to_string());
assert!(!crate::MtApiEventMaterializer::accepts_observation(
&crate::MtTransactionAnnotationMaterializer,
&observation,
));
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn machine_readable_matrix_declares_transaction_annotation_policy() {
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../test-fixtures/contract-matrices/SPL_MEMO_MATRIX.json"
));
let matrix = match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("memo matrix is invalid JSON: {error}"),
};
assert_eq!(matrix["matrixVersion"], 4);
assert_eq!(matrix["materializationContract"]["crate"], "kb-lib");
assert_eq!(
matrix["materializationContract"]["transactionPolicy"],
"SuccessfulCommittedOnly"
);
assert_eq!(matrix["materializationContract"]["failedOrUncommittedOutput"], false);
}
}
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES` component constant.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
/// Stable runtime component name for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
/// Maximum Memo payload accepted by transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES;
/// Crate-visible `MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES` component constant.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES;
/// Stable processor name for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME;
/// Stable projection contract version for transaction annotations.
pub(crate) use self::constants::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION;
/// Canonical tracing target for transaction annotations.
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS;

View File

@@ -0,0 +1,25 @@
// file: kb-lib/src/materializer/transaction/annotations/constants.rs
// version: 5
//! Local constants for the transaction annotations materializer.
/// Stable runtime component name for the transaction annotations materializer.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME: &str =
"kb-lib.materializer.transaction.annotations";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME: &str =
"materializer.transaction.annotations";
/// Maximum Memo payload accepted by transaction annotations.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES: u64 = 566;
/// Stable projection contract version for the transaction annotations materializer.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION: u32 = 1;
/// Canonical tracing target for the transaction annotations materializer.
pub(crate) const TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS: &str =
"kb-lib.materializer.transaction.annotations";
/// Component contract for `accepted_families`.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
&[crate::MdEventFamily::Audit];
/// Component contract for `memo_entry_names`.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES: &[&str] =
&["add_memo", "memo_intent", "invalid_memo_attempt"];

View File

@@ -0,0 +1,609 @@
// file: kb-lib/src/materializer/transaction/annotations/materializer.rs
// version: 7
//! Exact committed SPL Memo transaction annotation projection.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum MemoGeneration {
V1,
V3,
V4,
}
impl MemoGeneration {
fn from_event(event: &crate::MdDecodedProtocolEvent) -> std::option::Option<Self> {
if event.program_id.0 == kb_program_ids::SPL_MEMO_V1_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v1"
{
return std::option::Option::Some(Self::V1);
}
if event.program_id.0 == kb_program_ids::SPL_MEMO_V3_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v3"
{
return std::option::Option::Some(Self::V3);
}
if event.program_id.0 == kb_program_ids::SPL_MEMO_V4_PROGRAM_ID
&& event.surface_code.0 == "spl.memo.v4"
{
return std::option::Option::Some(Self::V4);
}
return std::option::Option::None;
}
fn code(self) -> &'static str {
return match self {
Self::V1 => "v1",
Self::V3 => "v3",
Self::V4 => "v4",
};
}
fn signer_status(self) -> &'static str {
return match self {
Self::V1 => "not_applicable",
Self::V3 | Self::V4 => "valid",
};
}
}
struct MemoProjection {
generation: MemoGeneration,
memo_text: std::string::String,
payload_length_bytes: u64,
payload_sha256: std::string::String,
required_signers: std::vec::Vec<std::string::String>,
observed_signers: std::vec::Vec<std::string::String>,
verified_signers: std::vec::Vec<std::string::String>,
signer_validation_status: std::string::String,
}
/// Stable transaction annotation materializer.
#[derive(Clone, Debug, Default)]
pub struct MtTransactionAnnotationMaterializer;
impl crate::MtMaterializer for crate::MtTransactionAnnotationMaterializer {
fn materializer_name(&self) -> &'static str {
return crate::MT_TRANSACTION_ANNOTATIONS_COMPONENT_NAME;
}
fn materializer_version(&self) -> &'static str {
return env!("CARGO_PKG_VERSION");
}
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
return accepts_event(event);
}
fn materialize_event(
&self,
_event: &crate::MdDecodedProtocolEvent,
) -> kb_core::Result<std::vec::Vec<crate::MdMaterializedEvent>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
impl crate::MtApiEventMaterializer for crate::MtTransactionAnnotationMaterializer {
fn identity(&self) -> crate::MtApiMaterializerIdentity {
return crate::MtApiMaterializerIdentity {
name: crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
};
}
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
return crate::MT_TRANSACTION_ANNOTATIONS_ACCEPTED_FAMILIES;
}
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
return accepts_event(&observation.event);
}
fn transaction_policy(
&self,
_family: crate::MdEventFamily,
) -> crate::MtApiMaterializationTransactionPolicy {
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
}
fn materialize(
&self,
observation: &crate::DcApiDecodedObservation,
) -> crate::MtApiMaterializerExecutionResult {
if !accepts_event(&observation.event) {
return crate::MtApiMaterializerExecutionResult::ignored();
}
if observation.transaction_failed || !observation.observation_committed {
return crate::MtApiMaterializerExecutionResult::refused(
"uncommitted_transaction_annotation_refused",
"failed or uncommitted Memo observations cannot create transaction annotations",
);
}
if observation.event.event_name.0 != "add_memo" {
return crate::MtApiMaterializerExecutionResult::refused(
"invalid_transaction_annotation_source_refused",
"only a committed add_memo observation can create a transaction annotation",
);
}
let projection_result = parse_projection(observation);
let projection = match projection_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => {
return failed("memo_annotation_payload_invalid", message);
},
};
let idempotence_key = format!(
"spl_memo:{}:{}:{}:{}",
observation.event.signature.0,
observation.event.instruction_path.0,
observation.event.program_id.0,
projection.payload_sha256
);
let output = crate::MtApiMaterializedOutput {
output_key: "transaction_annotation:spl_memo:0".to_string(),
family: crate::MdMaterializedEventFamily::TransactionAnnotation,
payload_json: serde_json::json!({
"projectionVersion": crate::MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION,
"domain": "transaction_annotation",
"annotationKind": "spl.memo",
"idempotenceKey": idempotence_key,
"signature": observation.event.signature.0.clone(),
"slot": observation.event.slot.0,
"instructionPath": observation.event.instruction_path.0.clone(),
"sourceKind": match observation.event.source_kind {
crate::MdEventSourceKind::InnerInstruction => "inner_instruction",
_ => "instruction"
},
"generation": projection.generation.code(),
"programId": observation.event.program_id.0.clone(),
"text": projection.memo_text,
"payloadLengthBytes": projection.payload_length_bytes,
"payloadSha256": projection.payload_sha256,
"signers": {
"validationStatus": projection.signer_validation_status,
"required": projection.required_signers,
"observed": projection.observed_signers,
"verified": projection.verified_signers
},
"committed": true,
"provenance": {
"processorName": crate::MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME,
"processorVersion": env!("CARGO_PKG_VERSION"),
"sourceProtocol": observation.event.protocol_code.0.clone(),
"sourceSurface": observation.event.surface_code.0.clone(),
"sourceEventCode": observation.event.event_code.0.clone(),
"sourceEventKey": observation.event_key.clone()
}
}),
};
tracing::debug!(
target: crate::TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS,
action = "materialize_transaction_annotation",
signature = %observation.event.signature.0,
instruction_path = %observation.event.instruction_path.0,
program_id = %observation.event.program_id.0,
generation = projection.generation.code(),
payload_length_bytes = projection.payload_length_bytes,
output_count = 1_usize,
"materialize committed SPL Memo transaction annotation"
);
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
outputs: std::vec![output],
diagnostics: std::vec::Vec::new(),
};
}
}
fn accepts_event(event: &crate::MdDecodedProtocolEvent) -> bool {
return event.event_family == crate::MdEventFamily::Audit
&& event.protocol_code.0 == "spl.memo"
&& (event.source_kind == crate::MdEventSourceKind::Instruction
|| event.source_kind == crate::MdEventSourceKind::InnerInstruction)
&& MemoGeneration::from_event(event).is_some()
&& crate::MT_TRANSACTION_ANNOTATIONS_MEMO_ENTRY_NAMES
.contains(&event.event_name.0.as_str());
}
fn parse_projection(
observation: &crate::DcApiDecodedObservation,
) -> std::result::Result<MemoProjection, std::string::String> {
let generation = match MemoGeneration::from_event(&observation.event) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"Memo generation and Program ID do not match".to_string(),
);
},
};
let payload = &observation.payload_json;
if payload.get("committed").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
{
return std::result::Result::Err("Memo payload is not marked committed".to_string());
}
if payload.get("transactionSucceeded").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
|| payload.get("payloadComplete").and_then(serde_json::Value::as_bool)
!= std::option::Option::Some(true)
|| string_field(payload, "runtimeValidation")
!= std::option::Option::Some("proven_by_successful_transaction")
|| payload
.get("utf8Validation")
.and_then(|value| return string_field(value, "status"))
!= std::option::Option::Some("valid")
{
return std::result::Result::Err(
"Memo successful runtime validation proof is incomplete".to_string(),
);
}
if string_field(payload, "generation") != std::option::Option::Some(generation.code()) {
return std::result::Result::Err(
"Memo payload generation does not match its surface".to_string(),
);
}
if string_field(payload, "programId")
!= std::option::Option::Some(observation.event.program_id.0.as_str())
{
return std::result::Result::Err(
"Memo payload Program ID does not match its event".to_string(),
);
}
if string_field(payload, "instructionPath")
!= std::option::Option::Some(observation.event.instruction_path.0.as_str())
{
return std::result::Result::Err(
"Memo payload instruction path does not match its event".to_string(),
);
}
let memo_text = match string_field(payload, "memoText") {
std::option::Option::Some(value) => value.to_string(),
std::option::Option::None => {
return std::result::Result::Err("committed Memo has no UTF-8 text".to_string());
},
};
let payload_length_bytes =
match payload.get("payloadLengthBytes").and_then(serde_json::Value::as_u64) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err("committed Memo has no byte length".to_string());
},
};
let measured_length = match u64::try_from(memo_text.len()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"Memo text length conversion failed: {error}"
));
},
};
if measured_length != payload_length_bytes {
return std::result::Result::Err(
"Memo byte length does not match its UTF-8 text".to_string(),
);
}
if payload_length_bytes > crate::MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES {
return std::result::Result::Err(
"Memo byte length exceeds the projection limit".to_string(),
);
}
let payload_sha256 = match string_field(payload, "payloadSha256") {
std::option::Option::Some(value) if is_sha256(value) => value.to_string(),
_ => return std::result::Result::Err("Memo payload SHA-256 is invalid".to_string()),
};
let signer_validation = match payload.get("signerValidation") {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err("Memo signer validation is absent".to_string());
},
};
let signer_validation_status = match string_field(signer_validation, "status") {
std::option::Option::Some(value) if value == generation.signer_status() => {
value.to_string()
},
_ => {
return std::result::Result::Err(
"Memo signer validation status is inconsistent".to_string(),
);
},
};
let missing_signers = match string_array(signer_validation, "missingRequiredSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
if !missing_signers.is_empty() {
return std::result::Result::Err("committed Memo reports missing signers".to_string());
}
let required_signers = match string_array(signer_validation, "orderedRequiredSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
let observed_signers = match string_array(signer_validation, "orderedObservedSigners") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(message) => return std::result::Result::Err(message),
};
let verified_signers = if generation == MemoGeneration::V1 {
if !required_signers.is_empty() {
return std::result::Result::Err("Memo v1 cannot require signers".to_string());
}
std::vec::Vec::new()
} else {
if required_signers != observed_signers {
return std::result::Result::Err(
"committed Memo required and observed signers differ".to_string(),
);
}
observed_signers.clone()
};
return std::result::Result::Ok(MemoProjection {
generation,
memo_text,
payload_length_bytes,
payload_sha256,
required_signers,
observed_signers,
verified_signers,
signer_validation_status,
});
}
fn string_field<'a>(value: &'a serde_json::Value, key: &str) -> std::option::Option<&'a str> {
return value.get(key).and_then(serde_json::Value::as_str);
}
fn string_array(
value: &serde_json::Value,
key: &str,
) -> std::result::Result<std::vec::Vec<std::string::String>, std::string::String> {
let values = match value.get(key).and_then(serde_json::Value::as_array) {
std::option::Option::Some(values) => values,
std::option::Option::None => {
return std::result::Result::Err(format!("Memo signer field {key} is not an array"));
},
};
let mut output = std::vec::Vec::with_capacity(values.len());
for item in values {
let text = match item.as_str() {
std::option::Option::Some(text) if !text.trim().is_empty() => text,
_ => {
return std::result::Result::Err(format!(
"Memo signer field {key} contains an invalid pubkey"
));
},
};
output.push(text.to_string());
}
return std::result::Result::Ok(output);
}
fn is_sha256(value: &str) -> bool {
return value.len() == 64
&& value
.as_bytes()
.iter()
.all(|byte| return byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase());
}
fn failed(code: &str, message: std::string::String) -> crate::MtApiMaterializerExecutionResult {
return crate::MtApiMaterializerExecutionResult {
status: crate::MtApiMaterializerOutcomeStatus::Failed,
outputs: std::vec::Vec::new(),
diagnostics: std::vec![crate::MtApiMaterializerDiagnostic {
code: code.to_string(),
message,
retriable: false,
}],
};
}
#[cfg(test)]
mod tests {
fn observation(
program_id: &str,
surface_code: &str,
generation: &str,
event_name: &str,
failed: bool,
committed: bool,
required_signers: serde_json::Value,
observed_signers: serde_json::Value,
) -> crate::DcApiDecodedObservation {
return crate::DcApiDecodedObservation {
event_key: "memo:0".to_string(),
event: crate::MdDecodedProtocolEvent {
signature: crate::MdSignature("signature".to_string()),
slot: crate::MdSlot(42),
instruction_path: crate::MdInstructionPath("1/0".to_string()),
program_id: crate::MdProgramId(program_id.to_string()),
protocol_code: crate::MdProtocolCode("spl.memo".to_string()),
surface_code: crate::MdSurfaceCode(surface_code.to_string()),
event_code: crate::MdEventCode(format!("{surface_code}.{event_name}")),
event_name: crate::MdEventName(event_name.to_string()),
event_family: crate::MdEventFamily::Audit,
source_kind: crate::MdEventSourceKind::InnerInstruction,
confidence: crate::MdDecoderConfidence::ManualExact,
},
payload_json: serde_json::json!({
"generation": generation,
"programId": program_id,
"instructionPath": "1/0",
"transactionSucceeded": !failed,
"committed": committed,
"memoText": "memo",
"payloadLengthBytes": 4,
"payloadSha256": "1111111111111111111111111111111111111111111111111111111111111111",
"payloadComplete": true,
"utf8Validation": {"status":"valid","invalidFromByte":null},
"signerValidation": {
"status": if generation == "v1" { "not_applicable" } else { "valid" },
"orderedRequiredSigners": required_signers,
"orderedObservedSigners": observed_signers,
"missingRequiredSigners": []
},
"runtimeValidation": if failed {
"not_proven_transaction_failed"
} else {
"proven_by_successful_transaction"
}
}),
transaction_failed: failed,
transaction_error: if failed {
std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]}))
} else {
std::option::Option::None
},
observation_committed: committed,
proof: crate::DcApiDecoderProof {
kind: crate::DcApiDecoderProofKind::Manual,
confidence: crate::MdDecoderConfidence::ManualExact,
evidence: std::vec!["memo".to_string()],
},
};
}
fn v4() -> crate::DcApiDecodedObservation {
return observation(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl.memo.v4",
"v4",
"add_memo",
false,
true,
serde_json::json!(["signer", "signer"]),
serde_json::json!(["signer", "signer"]),
);
}
#[test]
fn committed_v4_memo_becomes_deterministic_transaction_annotation() {
let materializer = crate::MtTransactionAnnotationMaterializer;
let first = crate::MtApiEventMaterializer::materialize(&materializer, &v4());
let second = crate::MtApiEventMaterializer::materialize(&materializer, &v4());
assert_eq!(first, second);
assert_eq!(first.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
assert_eq!(first.outputs.len(), 1);
assert_eq!(
first.outputs[0].family,
crate::MdMaterializedEventFamily::TransactionAnnotation
);
assert_eq!(
first.outputs[0].payload_json["signers"]["verified"],
serde_json::json!(["signer", "signer"])
);
assert_eq!(first.outputs[0].payload_json["committed"], true);
assert!(first.validate().is_ok());
}
#[test]
fn exact_v1_v3_and_v4_surfaces_are_accepted() {
for (program_id, surface, generation) in [
(kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, "spl.memo.v1", "v1"),
(kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, "spl.memo.v3", "v3"),
(kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, "spl.memo.v4", "v4"),
] {
let observation = observation(
program_id,
surface,
generation,
"add_memo",
false,
true,
serde_json::json!([]),
serde_json::json!([]),
);
assert!(crate::MtApiEventMaterializer::accepts_observation(
&crate::MtTransactionAnnotationMaterializer,
&observation,
));
}
}
#[test]
fn v1_observed_accounts_are_not_claimed_as_runtime_verified() {
let observation = observation(
kb_program_ids::SPL_MEMO_V1_PROGRAM_ID,
"spl.memo.v1",
"v1",
"add_memo",
false,
true,
serde_json::json!([]),
serde_json::json!(["observed_only"]),
);
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.outputs[0].payload_json["signers"]["verified"], serde_json::json!([]));
assert_eq!(
result.outputs[0].payload_json["signers"]["observed"],
serde_json::json!(["observed_only"])
);
}
#[test]
fn failed_or_uncommitted_memo_is_refused_without_output() {
let observation = observation(
kb_program_ids::SPL_MEMO_V4_PROGRAM_ID,
"spl.memo.v4",
"v4",
"memo_intent",
true,
false,
serde_json::json!([]),
serde_json::json!([]),
);
let materializer = crate::MtTransactionAnnotationMaterializer;
let policy =
crate::materializer_api_validate_materialization_policy(&materializer, &observation);
assert!(policy.is_err());
let result = crate::MtApiEventMaterializer::materialize(&materializer, &observation);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Refused);
assert!(result.outputs.is_empty());
}
#[test]
fn malformed_committed_payload_fails_closed() {
let mut observation = v4();
observation.payload_json["payloadSha256"] = serde_json::Value::String("bad".to_string());
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Failed);
assert!(result.outputs.is_empty());
assert_eq!(result.diagnostics[0].code, "memo_annotation_payload_invalid");
}
#[test]
fn foreign_audit_observation_is_not_accepted() {
let mut observation = v4();
observation.event.protocol_code = crate::MdProtocolCode("foreign".to_string());
assert!(!crate::MtApiEventMaterializer::accepts_observation(
&crate::MtTransactionAnnotationMaterializer,
&observation,
));
let result = crate::MtApiEventMaterializer::materialize(
&crate::MtTransactionAnnotationMaterializer,
&observation,
);
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
}
#[test]
fn machine_readable_matrix_declares_transaction_annotation_policy() {
let parsed = serde_json::from_str::<serde_json::Value>(include_str!(
"../../../../../test-fixtures/contract-matrices/SPL_MEMO_MATRIX.json"
));
let matrix = match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("memo matrix is invalid JSON: {error}"),
};
assert_eq!(matrix["matrixVersion"], 4);
assert_eq!(matrix["materializationContract"]["crate"], "kb-lib");
assert_eq!(
matrix["materializationContract"]["transactionPolicy"],
"SuccessfulCommittedOnly"
);
assert_eq!(matrix["materializationContract"]["failedOrUncommittedOutput"], false);
}
}

View File

@@ -1,15 +0,0 @@
// file: kb-lib/src/materializer/transaction/constants.rs
// version: 3
//! Local constants for the transaction annotation materializer.
/// Maximum Memo payload accepted from the decoded contract.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_MAX_MEMO_PAYLOAD_BYTES: u64 = 4_096;
/// Stable materializer processor name.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_PROCESSOR_NAME: &str =
"materializer.transaction.annotations";
/// Stable projection contract version.
pub(crate) const MT_TRANSACTION_ANNOTATIONS_PROJECTION_VERSION: u32 = 1;
/// Canonical tracing target for this component.
pub(crate) const TRACING_TARGET_MATERIALIZER_TRANSACTION_ANNOTATIONS: &str =
"kb-lib.materializer.transaction";

View File

@@ -1,9 +1,17 @@
// file: kb-lib/src/materializer/vault.rs
// version: 3
// version: 4
//! `vault` materializer family.
mod core;
mod constants;
mod materializer;
/// Reserved vault materializer.
pub use self::core::MtVaultMaterializer;
pub use self::materializer::MtVaultMaterializer;
/// Empty accepted-family boundary for the reserved component.
pub(crate) use self::constants::MT_VAULT_ACCEPTED_FAMILIES;
/// Stable runtime component name for the `vault` materializer.
pub(crate) use self::constants::MT_VAULT_COMPONENT_NAME;
/// Stable processor name for the `vault` materializer.
pub(crate) use self::constants::MT_VAULT_PROCESSOR_NAME;

View File

@@ -0,0 +1,11 @@
// file: kb-lib/src/materializer/vault/constants.rs
// version: 1
//! Local constants for the vault materializer.
/// Stable runtime component name for the vault materializer.
pub(crate) const MT_VAULT_COMPONENT_NAME: &str = "kb-lib.materializer.vault";
/// Stable processor name persisted by the replay ledger.
pub(crate) const MT_VAULT_PROCESSOR_NAME: &str = "materializer.vault";
/// Empty family boundary retained while the materializer is reserved.
pub(crate) const MT_VAULT_ACCEPTED_FAMILIES: &[crate::MdEventFamily] = &[];

Some files were not shown because too many files have changed in this diff Show More