88 "os"
99 "path/filepath"
1010 "regexp"
11+ "sort"
1112 "strings"
1213 "testing"
1314
@@ -573,6 +574,10 @@ func TestMigration33(t *testing.T) {
573574
574575 // And now that we have test data inserted, we can migrate to the latest
575576 // version.
577+ // NOTE: the post-migration check was originally run at migration
578+ // version 33, but was later moved to version 48. Targeting the latest
579+ // migration, will execute post-migration check, but at version 48
580+ // instead of 33.
576581 err := db .ExecuteMigrations (TargetLatest , WithPostStepCallbacks (
577582 makePostStepCallbacks (db , postMigrationChecks ),
578583 ))
@@ -672,6 +677,115 @@ func TestMigration33(t *testing.T) {
672677 )
673678}
674679
680+ // TestMigration48ScriptKeyTypeReplay makes sure that if the script key type
681+ // backfill already ran for a user (originally at migration 33), the replay at
682+ // migration 48 is a no-op and doesn't rewrite any data.
683+ func TestMigration48ScriptKeyTypeReplay (t * testing.T ) {
684+ ctx := context .Background ()
685+
686+ db := NewTestDBWithVersion (t , 32 )
687+
688+ InsertTestdata (t , db .BaseDB , "migrations_test_00033_dummy_data.sql" )
689+
690+ // We simulate that a user previously ran migration 33, which at the
691+ // time also contained the code migration which has now been separated
692+ // to migration 48.
693+ err := db .ExecuteMigrations (TargetVersion (33 ), WithPostStepCallbacks (
694+ makePostStepCallbacks (db , map [uint ]postMigrationCheck {
695+ 33 : determineAndAssignScriptKeyType ,
696+ }),
697+ ))
698+ require .NoError (t , err )
699+
700+ const (
701+ key1 = "039c571fffcac1a1a7cd3372bd202ad8562f28e48b90f8a4eb714" +
702+ "eca062f576ee6"
703+ key2 = "029c571fffcac1a1a7cd3372bd202ad8562f28e48b90f8a4eb714" +
704+ "eca062f576ee6"
705+ key3 = "03f9cdf1ff7c9fbb0ea3c8533cd7048994f41ea20a79764469c22" +
706+ "aa18aa6696169"
707+ key4 = "027c79b9b26e463895eef5679d8558942c86c4ad2233adef01bc3" +
708+ "e6d540b3653fe"
709+ key5 = "0350aaeb166f4234650d84a2d8a130987aeaf6950206e0905401e" +
710+ "e74ff3f8d18e6"
711+ key6 = "02248bca7dbb12dcf0b490263a1d521691691aa2541842b7472c8" +
712+ "3acac0e88443b"
713+ )
714+
715+ expectedKeyTypes := map [string ]asset.ScriptKeyType {
716+ key1 : asset .ScriptKeyUnknown ,
717+ key2 : asset .ScriptKeyBip86 ,
718+ key3 : asset .ScriptKeyScriptPathExternal ,
719+ key4 : asset .ScriptKeyTombstone ,
720+ key5 : asset .ScriptKeyScriptPathChannel ,
721+ key6 : asset .ScriptKeyBurn ,
722+ }
723+
724+ // fetchTypes returns the script key types currently persisted in the
725+ // database keyed by their tweaked hex representation.
726+ fetchTypes := func () map [string ]asset.ScriptKeyType {
727+ currentTypes := make (map [string ]asset.ScriptKeyType )
728+
729+ for keyHex := range expectedKeyTypes {
730+ keyBytes , err := hex .DecodeString (keyHex )
731+ require .NoError (t , err )
732+
733+ dbKey , err := db .BaseDB .FetchScriptKeyByTweakedKey (
734+ ctx , keyBytes ,
735+ )
736+ require .NoError (t , err )
737+
738+ currentTypes [keyHex ] =
739+ extractSqlInt16 [asset.ScriptKeyType ](
740+ dbKey .ScriptKey .KeyType ,
741+ )
742+ }
743+
744+ return currentTypes
745+ }
746+
747+ // Verify that the database contains the expected script keys after the
748+ // first migration has been run.
749+ require .Equal (t , expectedKeyTypes , fetchTypes ())
750+
751+ // Now let's change the ScriptKey type for one of the entries, to an
752+ // incorrect value. When the code migration is rerun, this value should
753+ // not be changed despite being incorrect, as the replay of the code
754+ // migration won't act on values which have already been assigned.
755+ keyBytes , err := hex .DecodeString (key5 )
756+ require .NoError (t , err )
757+
758+ dbKey , err := db .BaseDB .FetchScriptKeyByTweakedKey (
759+ ctx , keyBytes ,
760+ )
761+
762+ _ , err = db .BaseDB .UpsertScriptKey (ctx , NewScriptKey {
763+ InternalKeyID : dbKey .InternalKey .KeyID ,
764+ TweakedScriptKey : dbKey .ScriptKey .TweakedScriptKey ,
765+ Tweak : dbKey .ScriptKey .Tweak ,
766+ KeyType : sqlInt16 (asset .ScriptKeyBip86 ),
767+ })
768+ require .NoError (t , err )
769+
770+ // Executing the code migration again (now at migration 48) should not
771+ // change or add any new values than the values assigned when the code
772+ // migration was run for migration version 33.
773+ err = db .ExecuteMigrations (TargetLatest , WithPostStepCallbacks (
774+ makePostStepCallbacks (db , postMigrationChecks ),
775+ ))
776+ require .NoError (t , err )
777+
778+ // As we changed the value for key5 to an incorrect value, we expect
779+ // that the db still contains that, hence not being equal to the
780+ // expectedKeyTypes.
781+ require .NotEqual (t , expectedKeyTypes , fetchTypes ())
782+
783+ // If we change the expectedKeyTypes to contain the incorrect value,
784+ // they should however be equal.
785+ expectedKeyTypes [key5 ] = asset .ScriptKeyBip86
786+ require .Equal (t , expectedKeyTypes , fetchTypes ())
787+ }
788+
675789// TestMigration37 tests that the Golang based post-migration check for the
676790// asset burn insertion works as expected.
677791func TestMigration37 (t * testing.T ) {
@@ -685,6 +799,10 @@ func TestMigration37(t *testing.T) {
685799
686800 // And now that we have test data inserted, we can migrate to the latest
687801 // version.
802+ // NOTE: the post-migration check was originally run at migration
803+ // version 37, but was later moved to version 49. Targeting the latest
804+ // migration, will execute post-migration check, but at version 49
805+ // instead of 37.
688806 err := db .ExecuteMigrations (TargetLatest , WithPostStepCallbacks (
689807 makePostStepCallbacks (db , postMigrationChecks ),
690808 ))
@@ -696,6 +814,71 @@ func TestMigration37(t *testing.T) {
696814 require .Len (t , burns , 5 )
697815}
698816
817+ // TestMigration49BurnReplay makes sure that if the asset burn code migration
818+ // already ran for a user (originally at migration 37), the replay at migration
819+ // 49 is a no-op and doesn't insert duplicate burns.
820+ func TestMigration49BurnReplay (t * testing.T ) {
821+ ctx := context .Background ()
822+
823+ db := NewTestDBWithVersion (t , 36 )
824+
825+ InsertTestdata (t , db .BaseDB , "migrations_test_00037_dummy_data.sql" )
826+
827+ // The test data inserts 3 burns into the database before the migration
828+ // is run. After the migration is run, 2 more entries will be added.
829+ burnsBefore , err := db .QueryBurns (ctx , QueryBurnsFilters {})
830+ require .NoError (t , err )
831+ require .Len (t , burnsBefore , 3 )
832+
833+ // We simulate that a user previously ran migration 37, which at the
834+ // time also contained the code migration which has now been separated
835+ // to migration 49.
836+ err = db .ExecuteMigrations (TargetVersion (37 ), WithPostStepCallbacks (
837+ makePostStepCallbacks (db , map [uint ]postMigrationCheck {
838+ 37 : insertAssetBurns ,
839+ }),
840+ ))
841+ require .NoError (t , err )
842+
843+ // Since the migration was insertAssetBurns code migration was run for
844+ // version 37, the database should now contain 5 entries.
845+ burnsAfterFirstMigration , err := db .QueryBurns (ctx , QueryBurnsFilters {})
846+ require .NoError (t , err )
847+ require .Len (t , burnsAfterFirstMigration , 5 )
848+
849+ normalizeBurns := func (burns []sqlc.QueryBurnsRow ) []string {
850+ result := make ([]string , 0 , len (burns ))
851+
852+ for _ , burn := range burns {
853+ result = append (result , fmt .Sprintf ("%x:%x:%x:%d" ,
854+ burn .AnchorTxid , burn .AssetID , burn .GroupKey ,
855+ burn .Amount ))
856+ }
857+
858+ sort .Strings (result )
859+
860+ return result
861+ }
862+
863+ // Execute the rest of the migrations, which will trigger the migration
864+ // version 49 code migration.
865+ err = db .ExecuteMigrations (TargetLatest , WithPostStepCallbacks (
866+ makePostStepCallbacks (db , postMigrationChecks ),
867+ ))
868+ require .NoError (t , err )
869+
870+ burnsAfter , err := db .QueryBurns (ctx , QueryBurnsFilters {})
871+ require .NoError (t , err )
872+
873+ // Despite that the code migration in migration version 49 was executed
874+ // once more, the asset burns persisted in the database should not have
875+ // changed.
876+ require .Equal (
877+ t , normalizeBurns (burnsAfterFirstMigration ),
878+ normalizeBurns (burnsAfter ),
879+ )
880+ }
881+
699882// TestDirtySqliteVersion tests that if a migration fails and leaves an Sqlite
700883// database backend in a dirty state, any attempts of re-executing migrations on
701884// the db (i.e. restart tapd), will fail with an error indicating that the
0 commit comments