From 183982640716658b77c35545de88912cb3453d5f Mon Sep 17 00:00:00 2001 From: Pedro Piccino Date: Sat, 11 Apr 2026 22:36:51 -0300 Subject: [PATCH] fix: handle empty String fields in to_byte_array (issue #1272) Empty String fields previously produced [0u8; 32], which caused the Poseidon hasher to fail with custom program error 1 on-chain. Now empty strings use a non-zero sentinel byte to avoid this error. Fixes #1272 --- program-libs/hasher/src/to_byte_array.rs | 9 +++++++++ program-libs/hasher/tests/to_byte_array.rs | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/program-libs/hasher/src/to_byte_array.rs b/program-libs/hasher/src/to_byte_array.rs index a8f1f4edb3..5cd2810768 100644 --- a/program-libs/hasher/src/to_byte_array.rs +++ b/program-libs/hasher/src/to_byte_array.rs @@ -180,6 +180,10 @@ impl ToByteArray for String { /// Max allowed String length is 31 bytes. /// For longer strings hash to field size or provide a custom implementation. + /// + /// Empty strings are represented with a leading 1 byte at index 0 to avoid + /// an all-zero `[0u8; 32]` representation, which can cause errors in the + /// Poseidon hasher on Solana (see issue #1272). fn to_byte_array(&self) -> Result<[u8; 32], HasherError> { let bytes = self.as_bytes(); let mut result = [0u8; 32]; @@ -187,6 +191,11 @@ impl ToByteArray for String { if byte_len > 31 { return Err(HasherError::InvalidInputLength(31, bytes.len())); } + if byte_len == 0 { + // Use a non-zero sentinel to avoid all-zero representation. + // This prevents Poseidon syscall errors on Solana (issue #1272). + result[0] = 1; + } result[32 - byte_len..].copy_from_slice(bytes); Ok(result) } diff --git a/program-libs/hasher/tests/to_byte_array.rs b/program-libs/hasher/tests/to_byte_array.rs index 71b2ad69e8..fd34be1039 100644 --- a/program-libs/hasher/tests/to_byte_array.rs +++ b/program-libs/hasher/tests/to_byte_array.rs @@ -216,11 +216,15 @@ fn test_to_byte_array_u8_arrays() { #[test] fn test_to_byte_array_string() { - // Test with empty string + // Test with empty string - should produce a non-zero representation + // to avoid Poseidon errors (issue #1272) let empty_string = "".to_string(); let result = empty_string.to_byte_array().unwrap(); - let expected = [0u8; 32]; + let mut expected = [0u8; 32]; + expected[0] = 1; // non-zero sentinel for empty strings assert_eq!(result, expected); + // Ensure it's not all zeros + assert_ne!(result, [0u8; 32]); // Test with short string let short_string = "foobar".to_string();