@@ -36,14 +36,16 @@ use std::{
3636} ;
3737use thiserror:: Error ;
3838use tlog_tiles:: {
39- Hash , HashReader , LogEntry , PathElem , PendingLogEntry , Tile , TileIterator , TlogError , TlogTile ,
39+ Hash , HashReader , LogEntry , PendingLogEntry , Tile , TileIterator , TlogError , TlogTile ,
4040 TreeWithTimestamp , UnixTimestamp , HASH_SIZE ,
4141} ;
4242use tokio:: sync:: watch:: { channel, Receiver , Sender } ;
4343
4444/// The maximum tile level is 63 (<c2sp.org/static-ct-api>), so safe to use [`u8::MAX`] as
4545/// the special level for data tiles. The Go implementation uses -1.
46- const DATA_TILE_KEY : u8 = u8:: MAX ;
46+ const DATA_TILE_LEVEL_KEY : u8 = u8:: MAX ;
47+ /// Same as above, anything above 63 is fine to use as the level key.
48+ const UNHASHED_TILE_LEVEL_KEY : u8 = u8:: MAX - 1 ;
4749const CHECKPOINT_KEY : & str = "checkpoint" ;
4850const STAGING_KEY : & str = "staging" ;
4951
@@ -323,9 +325,13 @@ impl SequenceState {
323325 "unexpected extension in DO checkpoint"
324326 ) ;
325327
326- // TODO: This is guaranteed to succeed right now bc we've hardcoded the verifier types. But
327- // this won't in general. Make an error type for this
328- let timestamp = timestamp. expect ( "no verifiers with timestamped signatures were used" ) ;
328+ let timestamp = match timestamp {
329+ Some ( timestamp) => timestamp,
330+ None if L :: REQUIRE_CHECKPOINT_TIMESTAMP => {
331+ bail ! ( "no verifiers with timestamped signatures were used" )
332+ }
333+ _ => 0 ,
334+ } ;
329335
330336 // Load the checkpoint from the object storage backend, verify it, and compare it to the
331337 // DO storage checkpoint.
@@ -372,33 +378,29 @@ impl SequenceState {
372378 // Fetch the tiles on the right edge, and verify them against the checkpoint.
373379 let mut edge_tiles = HashMap :: new ( ) ;
374380 if c. size ( ) > 0 {
375- // Fetch the right-most edge tiles by reading the last leaf. TileHashReader will fetch
381+ // Fetch the right-most tree tiles by reading the last leaf. TileHashReader will fetch
376382 // and verify the right tiles as a side-effect.
377383 edge_tiles = read_edge_tiles ( object, c. size ( ) , c. hash ( ) ) . await ?;
378384
379385 // Fetch the right-most data tile.
380- let mut data_tile = edge_tiles
381- . get ( & 0 )
382- . ok_or ( anyhow ! ( "no level 0 tile found" ) ) ?
383- . clone ( ) ;
384- data_tile. tile . set_data_with_path ( PathElem :: Data ) ;
385- data_tile . b = object
386- . fetch ( & data_tile. tile . path ( ) )
386+ let ( level0_tile , level0_tile_bytes ) = {
387+ let x = edge_tiles . get ( & 0 ) . ok_or ( anyhow ! ( "no level 0 tile found" ) ) ? ;
388+ ( x . tile , & x . b )
389+ } ;
390+ let data_tile = level0_tile . with_data_path ( L :: Pending :: DATA_TILE_PATH ) ;
391+ let data_tile_bytes = object
392+ . fetch ( & data_tile. path ( ) )
387393 . await ?
388394 . ok_or ( anyhow ! ( "no data tile in object storage" ) ) ?;
389- edge_tiles. insert ( DATA_TILE_KEY , data_tile. clone ( ) ) ;
390395
391396 // Verify the data tile against the level 0 tile.
392- let start = u64:: from ( TlogTile :: FULL_WIDTH ) * data_tile. tile . level_index ( ) ;
393- for ( i, entry) in TileIterator :: < L > :: new (
394- edge_tiles. get ( & DATA_TILE_KEY ) . unwrap ( ) . b . clone ( ) ,
395- data_tile. tile . width ( ) as usize ,
396- )
397- . enumerate ( )
397+ let start = u64:: from ( TlogTile :: FULL_WIDTH ) * data_tile. level_index ( ) ;
398+ for ( i, entry) in
399+ TileIterator :: < L > :: new ( & data_tile_bytes, data_tile. width ( ) as usize ) . enumerate ( )
398400 {
399401 let got = entry?. merkle_tree_leaf ( ) ;
400- let exp = edge_tiles . get ( & 0 ) . unwrap ( ) . tile . hash_at_index (
401- & edge_tiles . get ( & 0 ) . unwrap ( ) . b ,
402+ let exp = level0_tile . hash_at_index (
403+ level0_tile_bytes ,
402404 tlog_tiles:: stored_hash_index ( 0 , start + i as u64 ) ,
403405 ) ?;
404406 if got != exp {
@@ -408,6 +410,32 @@ impl SequenceState {
408410 ) ;
409411 }
410412 }
413+
414+ // Store the data tile.
415+ edge_tiles. insert (
416+ DATA_TILE_LEVEL_KEY ,
417+ TileWithBytes {
418+ tile : data_tile,
419+ b : data_tile_bytes,
420+ } ,
421+ ) ;
422+
423+ // Fetch and store the right-most auxiliary tile, if configured.
424+ if let Some ( path_elem) = L :: Pending :: AUX_TILE_PATH {
425+ let aux_tile = level0_tile. with_data_path ( path_elem) ;
426+ let aux_tile_bytes = object
427+ . fetch ( & aux_tile. path ( ) )
428+ . await ?
429+ . ok_or ( anyhow ! ( "no auxiliary tile in object storage" ) ) ?;
430+
431+ edge_tiles. insert (
432+ UNHASHED_TILE_LEVEL_KEY ,
433+ TileWithBytes {
434+ tile : aux_tile,
435+ b : aux_tile_bytes,
436+ } ,
437+ ) ;
438+ }
411439 }
412440
413441 for tile in & edge_tiles {
@@ -601,12 +629,23 @@ async fn sequence_entries<L: LogEntry>(
601629 // Load the current partial data tile, if any.
602630 let mut tile_uploads: Vec < UploadAction > = Vec :: new ( ) ;
603631 let mut edge_tiles = sequence_state. edge_tiles . clone ( ) ;
604- let mut data_tile: Vec < u8 > = Vec :: new ( ) ;
605- if let Some ( t) = edge_tiles. get ( & DATA_TILE_KEY ) {
632+ let mut data_tile = Vec :: new ( ) ;
633+ if let Some ( t) = edge_tiles. get ( & DATA_TILE_LEVEL_KEY ) {
606634 if t. tile . width ( ) < TlogTile :: FULL_WIDTH {
607635 data_tile. clone_from ( & t. b ) ;
608636 }
609637 }
638+
639+ // Load the current partial auxiliary tile, if configured.
640+ let mut aux_tile = Vec :: new ( ) ;
641+ if L :: Pending :: AUX_TILE_PATH . is_some ( ) {
642+ if let Some ( t) = edge_tiles. get ( & UNHASHED_TILE_LEVEL_KEY ) {
643+ if t. tile . width ( ) < TlogTile :: FULL_WIDTH {
644+ aux_tile. clone_from ( & t. b ) ;
645+ }
646+ }
647+ }
648+
610649 let mut overlay = HashMap :: new ( ) ;
611650 let mut n = old_size;
612651 let mut sequenced_metadata = Vec :: with_capacity ( entries. len ( ) ) ;
@@ -618,6 +657,11 @@ async fn sequence_entries<L: LogEntry>(
618657 cache_metadata. push ( ( entry. lookup_key ( ) , metadata) ) ;
619658 sequenced_metadata. push ( ( sender, metadata) ) ;
620659
660+ // Write to the auxiliary tile, if configured.
661+ if L :: Pending :: AUX_TILE_PATH . is_some ( ) {
662+ aux_tile. extend ( entry. aux_entry ( ) ) ;
663+ }
664+
621665 let sequenced_entry = L :: new ( entry, metadata) ;
622666 let tile_leaf = sequenced_entry. to_data_tile_entry ( ) ;
623667 let merkle_tree_leaf = sequenced_entry. merkle_tree_leaf ( ) ;
@@ -649,22 +693,33 @@ async fn sequence_entries<L: LogEntry>(
649693
650694 // If the data tile is full, stage it.
651695 if n % u64:: from ( TlogTile :: FULL_WIDTH ) == 0 {
652- stage_data_tile ( n, & mut edge_tiles, & mut tile_uploads, & data_tile) ;
653696 metrics
654697 . seq_data_tile_size
655698 . with_label_values ( & [ "full" ] )
656699 . observe ( data_tile. len ( ) . as_f64 ( ) ) ;
657- data_tile. clear ( ) ;
700+ stage_data_tile :: < L > (
701+ n,
702+ & mut edge_tiles,
703+ & mut tile_uploads,
704+ std:: mem:: take ( & mut data_tile) ,
705+ std:: mem:: take ( & mut aux_tile) ,
706+ ) ;
658707 }
659708 }
660709
661710 // Stage leftover partial data tile, if any.
662711 if n != old_size && n % u64:: from ( TlogTile :: FULL_WIDTH ) != 0 {
663- stage_data_tile ( n, & mut edge_tiles, & mut tile_uploads, & data_tile) ;
664712 metrics
665713 . seq_data_tile_size
666714 . with_label_values ( & [ "partial" ] )
667715 . observe ( data_tile. len ( ) . as_f64 ( ) ) ;
716+ stage_data_tile :: < L > (
717+ n,
718+ & mut edge_tiles,
719+ & mut tile_uploads,
720+ std:: mem:: take ( & mut data_tile) ,
721+ std:: mem:: take ( & mut aux_tile) ,
722+ ) ;
668723 }
669724
670725 // Produce and stage new tree tiles.
@@ -815,28 +870,45 @@ async fn sequence_entries<L: LogEntry>(
815870 Ok ( ( ) )
816871}
817872
818- // Stage a data tile. This is used as a helper function for [`sequence_entries`].
819- fn stage_data_tile (
873+ // Stage a data tile, and if configured an auxiliary tile.
874+ // This is used as a helper function for [`sequence_entries`].
875+ fn stage_data_tile < L : LogEntry > (
820876 n : u64 ,
821877 edge_tiles : & mut HashMap < u8 , TileWithBytes > ,
822878 tile_uploads : & mut Vec < UploadAction > ,
823- data_tile : & [ u8 ] ,
879+ data_tile : Vec < u8 > ,
880+ aux_tile : Vec < u8 > ,
824881) {
825- let mut tile = TlogTile :: from_index ( tlog_tiles:: stored_hash_index ( 0 , n - 1 ) ) ;
826- tile . set_data_with_path ( PathElem :: Data ) ;
882+ let tile = TlogTile :: from_index ( tlog_tiles:: stored_hash_index ( 0 , n - 1 ) )
883+ . with_data_path ( L :: Pending :: DATA_TILE_PATH ) ;
827884 edge_tiles. insert (
828- DATA_TILE_KEY ,
885+ DATA_TILE_LEVEL_KEY ,
829886 TileWithBytes {
830887 tile,
831- b : data_tile. to_owned ( ) ,
888+ b : data_tile. clone ( ) ,
832889 } ,
833890 ) ;
834- let action = UploadAction {
891+ tile_uploads . push ( UploadAction {
835892 key : tile. path ( ) ,
836- data : data_tile. to_owned ( ) ,
893+ data : data_tile,
837894 opts : OPTS_DATA_TILE . clone ( ) ,
838- } ;
839- tile_uploads. push ( action) ;
895+ } ) ;
896+ if let Some ( path_elem) = L :: Pending :: AUX_TILE_PATH {
897+ let tile =
898+ TlogTile :: from_index ( tlog_tiles:: stored_hash_index ( 0 , n - 1 ) ) . with_data_path ( path_elem) ;
899+ edge_tiles. insert (
900+ UNHASHED_TILE_LEVEL_KEY ,
901+ TileWithBytes {
902+ tile,
903+ b : aux_tile. clone ( ) ,
904+ } ,
905+ ) ;
906+ tile_uploads. push ( UploadAction {
907+ key : tile. path ( ) ,
908+ data : aux_tile,
909+ opts : OPTS_DATA_TILE . clone ( ) ,
910+ } ) ;
911+ }
840912}
841913
842914/// Applies previously-staged uploads to the object backend where contents can be retrieved by log clients.
@@ -2224,17 +2296,22 @@ mod tests {
22242296 let leaf_hashes = read_tile_hashes ( & self . object , c. size ( ) , c. hash ( ) , & indexes)
22252297 . map_err ( |e| anyhow ! ( e) ) ?;
22262298
2227- let mut last_tile = TlogTile :: from_index ( tlog_tiles:: stored_hash_count ( c. size ( ) - 1 ) ) ;
2228- last_tile . set_data_with_path ( PathElem :: Data ) ;
2299+ let last_tile = TlogTile :: from_index ( tlog_tiles:: stored_hash_count ( c. size ( ) - 1 ) )
2300+ . with_data_path ( StaticCTPendingLogEntry :: DATA_TILE_PATH ) ;
22292301
22302302 for n in 0 ..last_tile. level_index ( ) {
22312303 let tile = if n == last_tile. level_index ( ) {
22322304 last_tile
22332305 } else {
2234- TlogTile :: new ( 0 , n, TlogTile :: FULL_WIDTH , Some ( PathElem :: Data ) )
2306+ TlogTile :: new (
2307+ 0 ,
2308+ n,
2309+ TlogTile :: FULL_WIDTH ,
2310+ Some ( StaticCTPendingLogEntry :: DATA_TILE_PATH ) ,
2311+ )
22352312 } ;
22362313 for ( i, entry) in TileIterator :: < StaticCTLogEntry > :: new (
2237- block_on ( self . object . fetch ( & tile. path ( ) ) )
2314+ & block_on ( self . object . fetch ( & tile. path ( ) ) )
22382315 . map_err ( |e| anyhow ! ( e) ) ?
22392316 . unwrap ( ) ,
22402317 tile. width ( ) as usize ,
0 commit comments