@@ -73,6 +73,8 @@ pub enum Error {
7373 DuplicateApproval = 31 ,
7474 EscrowNotReleasable = 32 ,
7575 InvalidFeePayload = 33 ,
76+ /// delivery_address string exceeds MAX_DELIVERY_ADDRESS_LENGTH.
77+ DeliveryAddressTooLong = 34 ,
7678}
7779
7880// Alias for issue/docs terminology.
@@ -556,6 +558,8 @@ pub enum DataKey {
556558 HospitalUnits ( Address ) ,
557559 /// Per-unit pending custody event index: unit_id -> String (event_id of the active Pending custody event)
558560 UnitCustodyIndex ( u64 ) ,
561+ /// Per-unit custody events list: unit_id -> Vec<String> (all event_ids ever created for this unit)
562+ UnitCustodyEvents ( u64 ) ,
559563 /// Custody trail page: (unit_id, page_number) -> Vec<String> (max 20 event IDs)
560564 UnitTrailPage ( u64 , u32 ) ,
561565 /// Custody trail metadata: unit_id -> TrailMetadata
@@ -581,9 +585,10 @@ pub use storage_lifecycle::{
581585
582586// Re-export constants for internal use
583587pub ( crate ) use constants:: {
584- HEX_HASH_LENGTH , MAX_BATCH_EXPIRY_SIZE , MAX_BATCH_SIZE , MAX_EVENTS_PER_PAGE , MAX_QUANTITY_ML ,
585- MAX_REQUEST_ML , MAX_SHELF_LIFE_DAYS , MAX_UNIT_ID_LENGTH , MIN_QUANTITY_ML , MIN_REQUEST_ML ,
586- MIN_SHELF_LIFE_DAYS , NOMINATION_EXPIRY_SECONDS , SECONDS_PER_DAY , TRANSFER_EXPIRY_SECONDS ,
588+ HEX_HASH_LENGTH , MAX_BATCH_EXPIRY_SIZE , MAX_BATCH_SIZE , MAX_DELIVERY_ADDRESS_LENGTH ,
589+ MAX_EVENTS_PER_PAGE , MAX_QUANTITY_ML , MAX_REQUEST_ML , MAX_SHELF_LIFE_DAYS , MAX_UNIT_ID_LENGTH ,
590+ MIN_QUANTITY_ML , MIN_REQUEST_ML , MIN_SHELF_LIFE_DAYS , NOMINATION_EXPIRY_SECONDS ,
591+ SECONDS_PER_DAY , TRANSFER_EXPIRY_SECONDS ,
587592} ;
588593
589594/// Pending SuperAdmin nomination entry.
@@ -1393,6 +1398,19 @@ impl HealthChainContract {
13931398 let index_key = DataKey :: UnitCustodyIndex ( unit_id) ;
13941399 env. storage ( ) . persistent ( ) . set ( & index_key, & event_id) ;
13951400
1401+ // Maintain per-unit custody events list so archive_custody_events can find all events
1402+ // for this unit in O(k) (k = events per unit) instead of scanning the full CUSTODY_EVENTS map
1403+ let unit_events_key = DataKey :: UnitCustodyEvents ( unit_id) ;
1404+ let mut unit_event_ids: Vec < String > = env
1405+ . storage ( )
1406+ . persistent ( )
1407+ . get ( & unit_events_key)
1408+ . unwrap_or ( Vec :: new ( & env) ) ;
1409+ unit_event_ids. push_back ( event_id. clone ( ) ) ;
1410+ env. storage ( )
1411+ . persistent ( )
1412+ . set ( & unit_events_key, & unit_event_ids) ;
1413+
13961414 let old_status = unit. status ;
13971415 unit. status = BloodStatus :: InTransit ;
13981416 unit. transfer_timestamp = Some ( current_time) ;
@@ -2484,6 +2502,10 @@ impl HealthChainContract {
24842502 return Err ( Error :: InvalidDeliveryAddress ) ;
24852503 }
24862504
2505+ if delivery_address. len ( ) > MAX_DELIVERY_ADDRESS_LENGTH {
2506+ return Err ( Error :: DeliveryAddressTooLong ) ;
2507+ }
2508+
24872509 let current_time = env. ledger ( ) . timestamp ( ) ;
24882510 if required_by <= current_time {
24892511 return Err ( Error :: InvalidRequiredBy ) ;
@@ -5204,6 +5226,31 @@ mod test {
52045226 ) ;
52055227 }
52065228
5229+ #[ test]
5230+ #[ should_panic( expected = "Error(Contract, #34)" ) ]
5231+ fn test_create_request_delivery_address_too_long ( ) {
5232+ let env = Env :: default ( ) ;
5233+ let ( _, _, hospital, client) = setup_contract_with_hospital ( & env) ;
5234+
5235+ env. mock_all_auths ( ) ;
5236+ let current_time = env. ledger ( ) . timestamp ( ) ;
5237+ let required_by = current_time + 3600 ;
5238+
5239+ // 201 bytes — one byte over MAX_DELIVERY_ADDRESS_LENGTH (200)
5240+ let addr_bytes = [ b'a' ; 201 ] ;
5241+ let addr_str = core:: str:: from_utf8 ( & addr_bytes) . unwrap ( ) ;
5242+ let long_addr = String :: from_str ( & env, addr_str) ;
5243+
5244+ client. create_request (
5245+ & hospital,
5246+ & BloodType :: OPositive ,
5247+ & 200 ,
5248+ & UrgencyLevel :: High ,
5249+ & required_by,
5250+ & long_addr,
5251+ ) ;
5252+ }
5253+
52075254 #[ test]
52085255 #[ should_panic( expected = "Error(Contract, #14)" ) ]
52095256 fn test_create_request_empty_delivery_address ( ) {
0 commit comments