@@ -87,6 +87,284 @@ func TestUniverseFederationCRUD(t *testing.T) {
8787 require .NoError (t , err )
8888}
8989
90+ // TestFederationProofSyncLogCRUD tests that we can add, modify, and remove
91+ // proof sync log entries from the Universe DB.
92+ func TestFederationProofSyncLogCRUD (t * testing.T ) {
93+ t .Parallel ()
94+
95+ var (
96+ ctx = context .Background ()
97+ dbHandle = NewDbHandle (t )
98+ fedStore = dbHandle .UniverseFederationStore
99+ )
100+
101+ // Populate the database with a random asset, its associated proof, and
102+ // a set of servers.
103+ testAsset , testAnnotatedProof := dbHandle .AddRandomAssetProof (t )
104+ uniProof := dbHandle .AddUniProofLeaf (t , testAsset , testAnnotatedProof )
105+ uniId := universe .NewUniIDFromAsset (* testAsset )
106+
107+ servers := dbHandle .AddRandomServerAddrs (t , 3 )
108+
109+ // Designate pending sync status for all servers except the first.
110+ // Make a map set of pending sync servers.
111+ pendingSyncServers := make (map [universe.ServerAddr ]struct {})
112+ for i := range servers {
113+ server := servers [i ]
114+ if i == 0 {
115+ continue
116+ }
117+ pendingSyncServers [server ] = struct {}{}
118+ }
119+
120+ // Add log entries for the first server.
121+ syncServer := servers [0 ]
122+
123+ // Add push log entry.
124+ _ , err := fedStore .UpsertFederationProofSyncLog (
125+ ctx , uniId , uniProof .LeafKey , syncServer ,
126+ universe .SyncDirectionPush , universe .ProofSyncStatusComplete ,
127+ true ,
128+ )
129+ require .NoError (t , err )
130+
131+ // Add pull log entry.
132+ _ , err = fedStore .UpsertFederationProofSyncLog (
133+ ctx , uniId , uniProof .LeafKey , syncServer ,
134+ universe .SyncDirectionPull , universe .ProofSyncStatusComplete ,
135+ true ,
136+ )
137+ require .NoError (t , err )
138+
139+ // We've already added log entries for the first server. We will now
140+ // insert new proof sync log entries for the remaining servers.
141+ for _ , server := range servers [1 :] {
142+ _ , err := fedStore .UpsertFederationProofSyncLog (
143+ ctx , uniId , uniProof .LeafKey , server ,
144+ universe .SyncDirectionPush ,
145+ universe .ProofSyncStatusPending , false ,
146+ )
147+ require .NoError (t , err )
148+ }
149+
150+ // Retrieve all sync status pending log entries.
151+ syncDirectionPush := universe .SyncDirectionPush
152+ pendingLogEntries , err := fedStore .FetchPendingProofsSyncLog (
153+ ctx , & syncDirectionPush ,
154+ )
155+ require .NoError (t , err )
156+ require .Len (t , pendingLogEntries , 2 )
157+
158+ for i := range pendingLogEntries {
159+ entry := pendingLogEntries [i ]
160+ require .Equal (
161+ t , universe .ProofSyncStatusPending , entry .SyncStatus ,
162+ )
163+ require .Equal (
164+ t , universe .SyncDirectionPush , entry .SyncDirection ,
165+ )
166+ require .Equal (t , uniId .String (), entry .UniID .String ())
167+ require .Equal (t , int64 (0 ), entry .AttemptCounter )
168+
169+ assertProofSyncLogLeafKey (t , uniProof .LeafKey , entry .LeafKey )
170+ assertProofSyncLogLeaf (t , * uniProof .Leaf , entry .Leaf )
171+
172+ // Check for server address in pending sync server set.
173+ _ , ok := pendingSyncServers [entry .ServerAddr ]
174+ require .True (t , ok )
175+ }
176+
177+ // Retrieve all push sync status complete log entries.
178+ completePushLogEntries , err := fedStore .QueryFederationProofSyncLog (
179+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPush ,
180+ universe .ProofSyncStatusComplete ,
181+ )
182+ require .NoError (t , err )
183+
184+ // There should only be one complete push log entry.
185+ require .Len (t , completePushLogEntries , 1 )
186+
187+ // Check that the complete log entry is as expected.
188+ completePushEntry := completePushLogEntries [0 ]
189+
190+ require .Equal (t , servers [0 ], completePushEntry .ServerAddr )
191+ require .Equal (
192+ t , universe .ProofSyncStatusComplete ,
193+ completePushEntry .SyncStatus ,
194+ )
195+ require .Equal (
196+ t , universe .SyncDirectionPush , completePushEntry .SyncDirection ,
197+ )
198+ require .Equal (t , uniId .String (), completePushEntry .UniID .String ())
199+ require .Equal (t , int64 (0 ), completePushEntry .AttemptCounter )
200+
201+ assertProofSyncLogLeafKey (
202+ t , uniProof .LeafKey , completePushEntry .LeafKey ,
203+ )
204+ assertProofSyncLogLeaf (t , * uniProof .Leaf , completePushEntry .Leaf )
205+
206+ // Retrieve all pull sync status complete log entries.
207+ completePullLogEntries , err := fedStore .QueryFederationProofSyncLog (
208+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPull ,
209+ universe .ProofSyncStatusComplete ,
210+ )
211+ require .NoError (t , err )
212+
213+ // There should only be one complete push log entry.
214+ require .Len (t , completePullLogEntries , 1 )
215+
216+ // Check that the complete log entry is as expected.
217+ completePullEntry := completePullLogEntries [0 ]
218+
219+ require .Equal (t , servers [0 ], completePullEntry .ServerAddr )
220+ require .Equal (
221+ t , universe .ProofSyncStatusComplete ,
222+ completePullEntry .SyncStatus ,
223+ )
224+ require .Equal (
225+ t , universe .SyncDirectionPull , completePullEntry .SyncDirection ,
226+ )
227+ require .Equal (t , uniId .String (), completePullEntry .UniID .String ())
228+ require .Equal (t , int64 (0 ), completePullEntry .AttemptCounter )
229+
230+ assertProofSyncLogLeafKey (
231+ t , uniProof .LeafKey , completePullEntry .LeafKey ,
232+ )
233+ assertProofSyncLogLeaf (t , * uniProof .Leaf , completePullEntry .Leaf )
234+
235+ // Increment the attempt counter for one of the pending log entries.
236+ _ , err = fedStore .UpsertFederationProofSyncLog (
237+ ctx , uniId , uniProof .LeafKey , servers [1 ],
238+ universe .SyncDirectionPush , universe .ProofSyncStatusPending ,
239+ true ,
240+ )
241+ require .NoError (t , err )
242+
243+ // Check that the attempt counter was incremented as expected.
244+ pendingLogEntries , err = fedStore .QueryFederationProofSyncLog (
245+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPush ,
246+ universe .ProofSyncStatusPending ,
247+ )
248+ require .NoError (t , err )
249+ require .Len (t , pendingLogEntries , 2 )
250+
251+ for i := range pendingLogEntries {
252+ entry := pendingLogEntries [i ]
253+ if entry .ServerAddr == servers [1 ] {
254+ require .Equal (t , int64 (1 ), entry .AttemptCounter )
255+ } else {
256+ require .Equal (t , int64 (0 ), entry .AttemptCounter )
257+ }
258+ }
259+
260+ // Upsert without incrementing the attempt counter for one of the
261+ // pending log entries.
262+ _ , err = fedStore .UpsertFederationProofSyncLog (
263+ ctx , uniId , uniProof .LeafKey , servers [1 ],
264+ universe .SyncDirectionPush , universe .ProofSyncStatusPending ,
265+ false ,
266+ )
267+ require .NoError (t , err )
268+
269+ // Check that the attempt counter was not changed as expected.
270+ pendingLogEntries , err = fedStore .QueryFederationProofSyncLog (
271+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPush ,
272+ universe .ProofSyncStatusPending ,
273+ )
274+ require .NoError (t , err )
275+ require .Len (t , pendingLogEntries , 2 )
276+
277+ for i := range pendingLogEntries {
278+ entry := pendingLogEntries [i ]
279+ if entry .ServerAddr == servers [1 ] {
280+ require .Equal (t , int64 (1 ), entry .AttemptCounter )
281+ } else {
282+ require .Equal (t , int64 (0 ), entry .AttemptCounter )
283+ }
284+ }
285+
286+ // Set the sync status to complete for one of the pending log entries.
287+ _ , err = fedStore .UpsertFederationProofSyncLog (
288+ ctx , uniId , uniProof .LeafKey , servers [1 ],
289+ universe .SyncDirectionPush , universe .ProofSyncStatusComplete ,
290+ false ,
291+ )
292+ require .NoError (t , err )
293+
294+ // Check that the sync status was updated as expected.
295+ pendingLogEntries , err = fedStore .QueryFederationProofSyncLog (
296+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPush ,
297+ universe .ProofSyncStatusPending ,
298+ )
299+ require .NoError (t , err )
300+ require .Len (t , pendingLogEntries , 1 )
301+
302+ completePushLogEntries , err = fedStore .QueryFederationProofSyncLog (
303+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPush ,
304+ universe .ProofSyncStatusComplete ,
305+ )
306+ require .NoError (t , err )
307+ require .Len (t , completePushLogEntries , 2 )
308+
309+ // Delete log entries for one of the servers.
310+ err = fedStore .DeleteProofsSyncLogEntries (ctx , servers [0 ], servers [1 ])
311+ require .NoError (t , err )
312+
313+ // Only one log entry should remain and it should have sync status
314+ // pending.
315+ pendingLogEntries , err = fedStore .QueryFederationProofSyncLog (
316+ ctx , uniId , uniProof .LeafKey , universe .SyncDirectionPush ,
317+ universe .ProofSyncStatusPending ,
318+ )
319+ require .NoError (t , err )
320+ require .Len (t , pendingLogEntries , 1 )
321+
322+ // Check that the remaining log entry is as expected.
323+ pendingEntry := pendingLogEntries [0 ]
324+ require .Equal (t , servers [2 ], pendingEntry .ServerAddr )
325+ }
326+
327+ // assertProofSyncLogLeafKey asserts that a leaf key derived from a proof sync
328+ // log entry is equal to a given leaf key.
329+ func assertProofSyncLogLeafKey (t * testing.T , actualLeafKey universe.LeafKey ,
330+ logLeafKey universe.LeafKey ) {
331+
332+ // We can safely ignore the tweaked script key as it is the derivation
333+ // information for the script key. It is only ever known to the owner of
334+ // the asset and is never serialized in a proof
335+ actualLeafKey .ScriptKey .TweakedScriptKey = nil
336+ require .Equal (t , actualLeafKey , logLeafKey )
337+ }
338+
339+ // assertProofSyncLogLeaf asserts that a leaf derived from a proof sync log
340+ // entry is equal to a given universe leaf.
341+ func assertProofSyncLogLeaf (t * testing.T , actualLeaf universe.Leaf ,
342+ logLeaf universe.Leaf ) {
343+
344+ if actualLeaf .GenesisWithGroup .GroupKey != nil {
345+ // We can safely ignore the group key witness as it is the
346+ // basically just extracted from the asset and won't be relevant
347+ // when parsing the proof.
348+ actualLeaf .GenesisWithGroup .GroupKey .Witness = nil
349+
350+ // We can safely ignore the pre-tweaked group key
351+ // (GroupKey.RawKey) as it is the derivation information for the
352+ // group key. It is only ever known to the owner of the asset
353+ // and is never serialized in a proof.
354+ actualLeaf .GenesisWithGroup .GroupKey .RawKey .PubKey = nil
355+ }
356+
357+ require .Equal (t , actualLeaf .Amt , logLeaf .Amt )
358+ require .Equal (t , actualLeaf .RawProof , logLeaf .RawProof )
359+ require .Equal (t , actualLeaf .GenesisWithGroup , logLeaf .GenesisWithGroup )
360+
361+ // We compare the assets with our custom asset quality function as the
362+ // SplitCommitmentRoot field MS-SMT node types will differ. A computed
363+ // node is derived from the database data whereas the generated asset
364+ // may have a MS-SMT branch node type.
365+ actualLeaf .Asset .DeepEqual (logLeaf .Asset )
366+ }
367+
90368// TestFederationConfigDefault tests that we're able to fetch the default
91369// federation config.
92370func TestFederationConfigDefault (t * testing.T ) {
0 commit comments