@@ -13,6 +13,9 @@ use breez_sdk_spark::{
1313 Contact , DepositInfo , ListContactsRequest , Payment , PaymentDetails , PaymentMetadata ,
1414 SetLnurlMetadataItem , StorageError , StorageListPaymentsRequest , UpdateDepositPayload ,
1515} ;
16+ use ldk_node:: bitcoin:: hashes:: Hash ;
17+ use ldk_node:: bitcoin:: hashes:: sha256:: Hash as Sha256 ;
18+ use ldk_node:: bitcoin:: hex:: DisplayHex ;
1619use ldk_node:: lightning:: util:: persist:: KVSTORE_NAMESPACE_KEY_MAX_LEN ;
1720use ldk_node:: lightning:: util:: persist:: KVStore ;
1821
@@ -43,9 +46,28 @@ fn sanitize_key(key: String) -> String {
4346 }
4447}
4548
49+ fn sync_record_key ( parts : & [ & str ] ) -> String {
50+ let key = parts. iter ( ) . fold ( String :: new ( ) , |mut key, part| {
51+ key. push_str ( & part. len ( ) . to_string ( ) ) ;
52+ key. push ( '_' ) ;
53+ key. push_str ( part) ;
54+ key
55+ } ) ;
56+
57+ if key. len ( ) > KVSTORE_NAMESPACE_KEY_MAX_LEN {
58+ format ! ( "h_{}" , Sha256 :: hash( key. as_bytes( ) ) . to_byte_array( ) . as_hex( ) )
59+ } else {
60+ key
61+ }
62+ }
63+
4664/// Create a KV key from a RecordId.
4765fn record_id_key ( id : & RecordId ) -> String {
48- sanitize_key ( format ! ( "{}_{}" , id. r#type, id. data_id) )
66+ sync_record_key ( & [ & id. r#type , & id. data_id ] )
67+ }
68+
69+ fn incoming_record_key ( record : & Record ) -> String {
70+ sync_record_key ( & [ & record. id . r#type , & record. id . data_id , & record. revision . to_string ( ) ] )
4971}
5072
5173/// Serialize a Record to JSON bytes.
@@ -664,10 +686,7 @@ impl breez_sdk_spark::Storage for SparkStore {
664686
665687 async fn insert_incoming_records ( & self , records : Vec < Record > ) -> Result < ( ) , StorageError > {
666688 for record in records {
667- let key = sanitize_key ( format ! (
668- "{}_{}_{}" ,
669- record. id. r#type, record. id. data_id, record. revision
670- ) ) ;
689+ let key = incoming_record_key ( & record) ;
671690 let data = record_to_bytes ( & record) ?;
672691
673692 KVStore :: write (
@@ -684,8 +703,7 @@ impl breez_sdk_spark::Storage for SparkStore {
684703 }
685704
686705 async fn delete_incoming_record ( & self , record : Record ) -> Result < ( ) , StorageError > {
687- let key =
688- sanitize_key ( format ! ( "{}_{}_{}" , record. id. r#type, record. id. data_id, record. revision) ) ;
706+ let key = incoming_record_key ( & record) ;
689707 let _ = KVStore :: remove (
690708 self . 0 . as_ref ( ) ,
691709 SPARK_PRIMARY_NAMESPACE ,
@@ -779,3 +797,42 @@ impl breez_sdk_spark::Storage for SparkStore {
779797 Ok ( ( ) )
780798 }
781799}
800+
801+ #[ cfg( test) ]
802+ mod tests {
803+ use super :: * ;
804+
805+ fn record ( record_type : & str , data_id : & str , revision : u64 ) -> Record {
806+ Record {
807+ id : RecordId :: new ( record_type. to_string ( ) , data_id. to_string ( ) ) ,
808+ schema_version : "1" . to_string ( ) ,
809+ data : HashMap :: new ( ) ,
810+ revision,
811+ }
812+ }
813+
814+ #[ test]
815+ fn record_id_key_distinguishes_underscore_in_type_vs_data_id ( ) {
816+ let a = RecordId :: new ( "foo" . to_string ( ) , "bar_baz" . to_string ( ) ) ;
817+ let b = RecordId :: new ( "foo_bar" . to_string ( ) , "baz" . to_string ( ) ) ;
818+
819+ assert_ne ! ( record_id_key( & a) , record_id_key( & b) ) ;
820+ }
821+
822+ #[ test]
823+ fn incoming_record_key_distinguishes_underscore_in_type_vs_data_id ( ) {
824+ let a = record ( "foo" , "bar_baz" , 7 ) ;
825+ let b = record ( "foo_bar" , "baz" , 7 ) ;
826+
827+ assert_ne ! ( incoming_record_key( & a) , incoming_record_key( & b) ) ;
828+ }
829+
830+ #[ test]
831+ fn oversized_keys_use_bounded_hash_key ( ) {
832+ let id = RecordId :: new ( "a" . repeat ( KVSTORE_NAMESPACE_KEY_MAX_LEN ) , "b" . to_string ( ) ) ;
833+ let key = record_id_key ( & id) ;
834+
835+ assert ! ( key. len( ) <= KVSTORE_NAMESPACE_KEY_MAX_LEN ) ;
836+ assert ! ( key. chars( ) . all( |c| c. is_ascii_alphanumeric( ) || c == '_' || c == '-' ) ) ;
837+ }
838+ }
0 commit comments