v0.3.10-pre.006
This commit is contained in:
@@ -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()),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user