@@ -19,14 +19,17 @@ import (
1919 "encoding/json"
2020 "errors"
2121 "flag"
22+ "fmt"
2223 "log/slog"
2324 "net/http"
2425 "os"
2526 "path/filepath"
27+ "net/url"
2628
2729 fnote "github.com/transparency-dev/formats/note"
2830 "github.com/transparency-dev/tessera"
2931 "github.com/transparency-dev/tessera/cmd/mtc/mirror/internal/config"
32+ "github.com/transparency-dev/tessera/storage/posix"
3033 "github.com/transparency-dev/tessera/cmd/mtc/mirror/internal/handler"
3134 "github.com/transparency-dev/witness/omniwitness"
3235 "github.com/transparency-dev/witness/witness"
@@ -41,7 +44,8 @@ const (
4144var (
4245 listenAddr = flag .String ("listen_addr" , ":8080" , "The address to listen on for HTTP requests." )
4346 storageDir = flag .String ("storage_dir" , "" , "Directory to store mirror data." )
44- witnessSignerPath = flag .String ("witness_signer_path" , "" , "The path to the note-formatted witness signer secret key." )
47+ witnessCosignerPath = flag .String ("witness_cosigner_path" , "" , "The path to the note-formatted witness cosigner secret key." )
48+ mirrorCosignerPath = flag .String ("mirror_cosigner_path" , "" , "The path to the note-formatted mirror cosigner secret key." )
4549 mirrorConfigPath = flag .String ("config_path" , "" , "The path to the mirror configuration file." )
4650)
4751
@@ -57,6 +61,8 @@ func main() {
5761 }
5862 }()
5963
64+ mirrorCosigner := mustCreateCosigner (ctx , * mirrorCosignerPath )
65+
6066 mux := handler .NewMirrorMux ()
6167 cfg := mirrorConfigFromFlags (ctx )
6268 for _ , l := range cfg .Logs {
@@ -65,11 +71,21 @@ func main() {
6571 slog .ErrorContext (ctx , "Failed to create verifier" , slog .String ("vkey" , l .VKey ), slog .Any ("error" , err ))
6672 os .Exit (1 )
6773 }
74+
6875 origin := v .Name ()
69- if err := mux .AddTarget (origin , & fakeTarget {}); err != nil {
70- slog .ErrorContext (ctx , "Failed to add target" , slog .String ("origin" , origin ), slog .Any ("error" , err ))
76+
77+ // Create the mirror
78+ t , err := newMirrorTarget (ctx , w , origin , mirrorCosigner )
79+ if err != nil {
80+ slog .ErrorContext (ctx , "Failed to create mirror target" , slog .String ("origin" , origin ), slog .Any ("error" , err ))
81+ os .Exit (1 )
82+ }
83+ if err := mux .AddTarget (origin , t ); err != nil {
84+ slog .ErrorContext (ctx , "Failed to add target to mux" , slog .String ("origin" , origin ), slog .Any ("error" , err ))
7185 os .Exit (1 )
7286 }
87+
88+ // Ensure log is known by the witness
7389 if err := wp .AddLogs (ctx , []omniwitness.Log {
7490 {Origin : origin , VKey : l .VKey },
7591 }); err != nil {
@@ -87,6 +103,29 @@ func main() {
87103 }
88104}
89105
106+ // newMirrorTarget creates a new POSIX driver and MirrorTarget for the given origin.
107+ //
108+ // The target directory for the driver is derived from the storage directory and the origin in accordance
109+ // with the `tlog-mirror` spec, allowing the root of the storage directory to be exported directly to read-only clients.
110+ func newMirrorTarget (ctx context.Context , w * witness.Witness , origin string , mirrorSigner note.Signer ) (* tessera.MirrorTarget , error ) {
111+ targetDir := filepath .Join (* storageDir , url .PathEscape (origin ))
112+ if err := os .MkdirAll (targetDir , 0o755 ); err != nil {
113+ return nil , fmt .Errorf ("mkdir %q: %v" , targetDir , err )
114+ }
115+ d , err := posix .New (ctx , posix.Config {Path : targetDir })
116+ if err != nil {
117+ return nil , fmt .Errorf ("posix.New: %v" , err )
118+ }
119+ mOpts := tessera .NewMirrorOptions ().
120+ WithCheckpointSource (func (ctx context.Context ) ([]byte , error ) {
121+ return w .GetCheckpoint (ctx , origin )
122+ }).
123+ WithSigner (mirrorSigner )
124+ return tessera .NewMirrorTarget (ctx , d , mOpts )
125+ }
126+
127+ // mirrorConfigFromFlags returns a mirror configuration loaded from the provided flags.
128+ // Exits if the mirror configuration could not be loaded.
90129func mirrorConfigFromFlags (ctx context.Context ) config.Config {
91130 if * mirrorConfigPath == "" {
92131 slog .ErrorContext (ctx , "Mirror config path not specified" )
@@ -131,7 +170,7 @@ func witnessFromFlags(ctx context.Context) (*witness.Witness, *sqlite.Persistenc
131170
132171 w , err := witness .New (ctx , witness.Opts {
133172 Persistence : p ,
134- Signers : witnessSignerFromFlags (ctx ),
173+ Signers : witnessCosignerFromFlags (ctx ),
135174 VerifierForLog : func (ctx context.Context , origin string ) (note.Verifier , bool , error ) {
136175 log , ok , err := p .Log (ctx , origin )
137176 if err != nil || ! ok {
@@ -147,30 +186,30 @@ func witnessFromFlags(ctx context.Context) (*witness.Witness, *sqlite.Persistenc
147186 return w , p , shutdown
148187}
149188
150- // fakeTarget is a temporary mirror target impl, and will be removed in due course.
151- type fakeTarget struct {}
152-
153- func (f fakeTarget ) AddEntries (ctx context.Context , uploadStart , uploadEnd uint64 , ticket []byte , next func () (* tessera.MirrorPackage , error )) (nextIdx uint64 , curSize uint64 , newTicket []byte , cosigs []byte , err error ) {
154- slog .InfoContext (ctx , "fake target: AddEntries" , slog .Uint64 ("uploadStart" , uploadStart ), slog .Uint64 ("uploadEnd" , uploadEnd ))
155- return uploadEnd , 0 , nil , nil , nil
156- }
157-
158- func witnessSignerFromFlags (ctx context.Context ) []note.Signer {
159- if * witnessSignerPath == "" {
189+ func witnessCosignerFromFlags (ctx context.Context ) []note.Signer {
190+ if * witnessCosignerPath == "" {
160191 slog .WarnContext (ctx , "Witness cosigner not configured, add-checkpoint will not return cosigs" )
161192 return []note.Signer {}
162193 }
163- r , err := os .ReadFile (* witnessSignerPath )
194+ return []note.Signer {mustCreateCosigner (ctx , * witnessCosignerPath )}
195+ }
196+
197+ func mustCreateCosigner (ctx context.Context , path string ) note.Signer {
198+ if path == "" {
199+ slog .ErrorContext (ctx , "Cosigner key path not specified" )
200+ os .Exit (1 )
201+ }
202+ r , err := os .ReadFile (path )
164203 if err != nil {
165- slog .ErrorContext (ctx , "Failed to read witness cosigner file " , slog .String ("path" , * witnessSignerPath ), slog .Any ("error" , err ))
204+ slog .ErrorContext (ctx , "Failed to read cosigner key " , slog .String ("path" , path ), slog .Any ("error" , err ))
166205 os .Exit (1 )
167206 }
168207 s , err := fnote .NewSignerForCosignatureV1 (string (r ))
169208 if err != nil {
170- slog .ErrorContext (ctx , "Failed to create cosigner" , slog .String ("path" , * witnessSignerPath ), slog .Any ("error" , err ))
209+ slog .ErrorContext (ctx , "Failed to create cosigner" , slog .String ("path" , path ), slog .Any ("error" , err ))
171210 os .Exit (1 )
172211 }
173- return []note. Signer { s }
212+ return s
174213}
175214
176215
0 commit comments