/vendor/github.com/vacp2p/mvds/persistenceutil/sqlite.go

https://github.com/status-im/status-go · Go · 124 lines · 85 code · 24 blank · 15 comment · 23 complexity · 4d257e489080dfca4621b988ffb26b00 MD5 · raw file

  1. package persistenceutil
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "os"
  6. "github.com/pkg/errors"
  7. "github.com/status-im/migrate/v4"
  8. "github.com/status-im/migrate/v4/database/sqlcipher"
  9. bindata "github.com/status-im/migrate/v4/source/go_bindata"
  10. )
  11. // The reduced number of kdf iterations (for performance reasons) which is
  12. // currently used for derivation of the database key
  13. // https://github.com/status-im/status-go/pull/1343
  14. // https://notes.status.im/i8Y_l7ccTiOYq09HVgoFwA
  15. const reducedKdfIterationsNumber = 3200
  16. // MigrationConfig is a struct that allows to define bindata migrations.
  17. type MigrationConfig struct {
  18. AssetNames []string
  19. AssetGetter func(name string) ([]byte, error)
  20. }
  21. // Migrate migrates a provided sqldb
  22. func Migrate(db *sql.DB) error {
  23. assetNames, assetGetter, err := prepareMigrations(DefaultMigrations)
  24. if err != nil {
  25. return err
  26. }
  27. return ApplyMigrations(db, assetNames, assetGetter)
  28. }
  29. // Open opens or initializes a new database for a given file path.
  30. // MigrationConfig is optional but if provided migrations are applied automatically.
  31. func Open(path, key string, mc ...MigrationConfig) (*sql.DB, error) {
  32. return open(path, key, reducedKdfIterationsNumber, mc)
  33. }
  34. // OpenWithIter allows to open a new database with a custom number of kdf iterations.
  35. // Higher kdf iterations number makes it slower to open the database.
  36. func OpenWithIter(path, key string, kdfIter int, mc ...MigrationConfig) (*sql.DB, error) {
  37. return open(path, key, kdfIter, mc)
  38. }
  39. func open(path string, key string, kdfIter int, configs []MigrationConfig) (*sql.DB, error) {
  40. _, err := os.OpenFile(path, os.O_CREATE, 0644)
  41. if err != nil {
  42. return nil, err
  43. }
  44. db, err := sql.Open("sqlite3", path)
  45. if err != nil {
  46. return nil, err
  47. }
  48. keyString := fmt.Sprintf("PRAGMA key = '%s'", key)
  49. // Disable concurrent access as not supported by the driver
  50. db.SetMaxOpenConns(1)
  51. if _, err = db.Exec("PRAGMA foreign_keys=ON"); err != nil {
  52. return nil, err
  53. }
  54. if _, err = db.Exec(keyString); err != nil {
  55. return nil, err
  56. }
  57. kdfString := fmt.Sprintf("PRAGMA kdf_iter = '%d'", kdfIter)
  58. if _, err = db.Exec(kdfString); err != nil {
  59. return nil, err
  60. }
  61. // Apply all provided migrations.
  62. for _, mc := range configs {
  63. if err := ApplyMigrations(db, mc.AssetNames, mc.AssetGetter); err != nil {
  64. return nil, err
  65. }
  66. }
  67. return db, nil
  68. }
  69. // ApplyMigrations allows to apply bindata migrations on the current *sql.DB.
  70. // `assetNames` is a list of assets with migrations and `assetGetter` is responsible
  71. // for returning the content of the asset with a given name.
  72. func ApplyMigrations(db *sql.DB, assetNames []string, assetGetter func(name string) ([]byte, error)) error {
  73. resources := bindata.Resource(
  74. assetNames,
  75. assetGetter,
  76. )
  77. source, err := bindata.WithInstance(resources)
  78. if err != nil {
  79. return errors.Wrap(err, "failed to create migration source")
  80. }
  81. driver, err := sqlcipher.WithInstance(db, &sqlcipher.Config{
  82. MigrationsTable: "mvds_" + sqlcipher.DefaultMigrationsTable,
  83. })
  84. if err != nil {
  85. return errors.Wrap(err, "failed to create driver")
  86. }
  87. m, err := migrate.NewWithInstance(
  88. "go-bindata",
  89. source,
  90. "sqlcipher",
  91. driver,
  92. )
  93. if err != nil {
  94. return errors.Wrap(err, "failed to create migration instance")
  95. }
  96. if err = m.Up(); err != migrate.ErrNoChange {
  97. return errors.Wrap(err, "failed to migrate")
  98. }
  99. return nil
  100. }