v0.3.10-pre.006

This commit is contained in:
2026-09-07 18:58:48 +02:00
parent d4f4237723
commit f7c57f21c5
17 changed files with 1748 additions and 61 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-raw-transaction-lib/src/signature.rs
// version: 2
// version: 3
use base64::Engine; // rust-rules: trait-import
@@ -68,7 +68,14 @@ pub fn extract_raw_transaction_signature_from_binary_base64(value: &str) -> ksp_
if base64::engine::general_purpose::STANDARD.encode(decoded.as_slice()) != value {
return std::result::Result::Err(crate::signature_error());
}
let prefix = decode_signature_count(decoded.as_slice());
if decoded.first() == std::option::Option::Some(&0x81) {
return extract_v1_signature(decoded.as_slice());
}
return extract_legacy_or_v0_signature(decoded.as_slice());
}
fn extract_legacy_or_v0_signature(decoded: &[u8]) -> ksp_core_lib::Result<ksp_store_api::RawTransactionSignature> {
let prefix = decode_signature_count(decoded);
let (signature_count, prefix_len) = match prefix {
std::result::Result::Ok(prefix) => prefix,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -87,11 +94,151 @@ pub fn extract_raw_transaction_signature_from_binary_base64(value: &str) -> ksp_
if message_offset >= decoded.len() {
return std::result::Result::Err(crate::signature_error());
}
let first_end = match prefix_len.checked_add(64) {
return copy_signature(decoded, prefix_len);
}
fn extract_v1_signature(decoded: &[u8]) -> ksp_core_lib::Result<ksp_store_api::RawTransactionSignature> {
if decoded.len() > 4_096 || decoded.len() < 42 {
return std::result::Result::Err(crate::signature_error());
}
let required_signatures = match decoded.get(1) {
std::option::Option::Some(value) => usize::from(*value),
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let readonly_signed = match decoded.get(2) {
std::option::Option::Some(value) => usize::from(*value),
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let readonly_unsigned = match decoded.get(3) {
std::option::Option::Some(value) => usize::from(*value),
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
if required_signatures == 0 || required_signatures > 12 || readonly_signed >= required_signatures {
return std::result::Result::Err(crate::signature_error());
}
let mask_bytes = match decoded.get(4..8) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let mask = u32::from_le_bytes([mask_bytes[0], mask_bytes[1], mask_bytes[2], mask_bytes[3]]);
if mask & !0x1f != 0 || matches!(mask & 0b11, 1 | 2) {
return std::result::Result::Err(crate::signature_error());
}
let instruction_count = match decoded.get(40) {
std::option::Option::Some(value) => usize::from(*value),
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let address_count = match decoded.get(41) {
std::option::Option::Some(value) => usize::from(*value),
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
if instruction_count > 64 || address_count > 64 || address_count < required_signatures + readonly_unsigned {
return std::result::Result::Err(crate::signature_error());
}
let addresses_len = match address_count.checked_mul(32) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let mut offset = match 42_usize.checked_add(addresses_len) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let config_len = config_value_length(mask);
offset = match offset.checked_add(config_len) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let headers_len = match instruction_count.checked_mul(4) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let headers_end = match offset.checked_add(headers_len) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
if headers_end > decoded.len() {
return std::result::Result::Err(crate::signature_error());
}
let mut payload_len = 0_usize;
for index in 0..instruction_count {
let header_offset = offset + (index * 4);
let program_index = usize::from(decoded[header_offset]);
let account_index_count = usize::from(decoded[header_offset + 1]);
let data_len = u16::from_le_bytes([decoded[header_offset + 2], decoded[header_offset + 3]]);
if program_index >= address_count {
return std::result::Result::Err(crate::signature_error());
}
payload_len = match payload_len.checked_add(account_index_count).and_then(|value| return value.checked_add(usize::from(data_len))) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
}
let payload_end = match headers_end.checked_add(payload_len) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let payload = match decoded.get(headers_end..payload_end) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let mut payload_offset = 0_usize;
for index in 0..instruction_count {
let header_offset = offset + (index * 4);
let account_index_count = usize::from(decoded[header_offset + 1]);
let data_len = usize::from(u16::from_le_bytes([decoded[header_offset + 2], decoded[header_offset + 3]]));
let account_end = match payload_offset.checked_add(account_index_count) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let account_indexes = match payload.get(payload_offset..account_end) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
if account_indexes.iter().any(|value| return usize::from(*value) >= address_count) {
return std::result::Result::Err(crate::signature_error());
}
payload_offset = match account_end.checked_add(data_len) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
}
let signatures_len = match required_signatures.checked_mul(64) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let expected_end = match payload_end.checked_add(signatures_len) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
if expected_end != decoded.len() {
return std::result::Result::Err(crate::signature_error());
}
return copy_signature(decoded, payload_end);
}
fn config_value_length(mask: u32) -> usize {
let mut length = 0_usize;
if mask & 0b11 == 0b11 {
length += 8;
}
if mask & (1_u32 << 2) != 0 {
length += 4;
}
if mask & (1_u32 << 3) != 0 {
length += 4;
}
if mask & (1_u32 << 4) != 0 {
length += 4;
}
return length;
}
fn copy_signature(decoded: &[u8], offset: usize) -> ksp_core_lib::Result<ksp_store_api::RawTransactionSignature> {
let first_end = match offset.checked_add(64) {
std::option::Option::Some(end) => end,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};
let first = match decoded.get(prefix_len..first_end) {
let first = match decoded.get(offset..first_end) {
std::option::Option::Some(first) => first,
std::option::Option::None => return std::result::Result::Err(crate::signature_error()),
};