diff --git a/background_worker/jobs/clear_block_transactions_map.go b/background_worker/jobs/clear_block_transactions_map.go index 078201cf0..5c9c4160d 100644 --- a/background_worker/jobs/clear_block_transactions_map.go +++ b/background_worker/jobs/clear_block_transactions_map.go @@ -9,9 +9,10 @@ import ( _ "github.com/lib/pq" ) -func (c ClearJob) ClearBlockTransactionsMap(params ClearRecrodsParams) error { +func (c ClearJob) ClearBlockTransactionsMap(params ClearRecordsParams) error { Log(INFO, "Connecting to database ...") conn, err := sqlx.Open(params.Scheme(), params.String()) + if err != nil { Log(ERROR, fmt.Sprintf("unable to create connection %s", err)) return err diff --git a/background_worker/jobs/clear_block_transactions_map_test.go b/background_worker/jobs/clear_block_transactions_map_test.go index 258699760..cfbe1fd2f 100644 --- a/background_worker/jobs/clear_block_transactions_map_test.go +++ b/background_worker/jobs/clear_block_transactions_map_test.go @@ -16,11 +16,11 @@ import ( ) type ClearBlockTransactionsMapSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *ClearBlockTransactionsMapSuite) Test() { - params := ClearRecrodsParams{ + params := ClearRecordsParams{ DBConnectionParams: DefaultParams, RecordRetentionDays: 10, } diff --git a/background_worker/jobs/clear_blocks.go b/background_worker/jobs/clear_blocks.go index 4353eda52..61790e645 100644 --- a/background_worker/jobs/clear_blocks.go +++ b/background_worker/jobs/clear_blocks.go @@ -14,7 +14,7 @@ const ( numericalDateHourLayout = "2006010215" ) -type ClearRecrodsParams struct { +type ClearRecordsParams struct { dbconn.DBConnectionParams RecordRetentionDays int } @@ -41,7 +41,7 @@ func NewClearJob(opts ...func(job *ClearJob)) *ClearJob { return c } -func (c ClearJob) ClearBlocks(params ClearRecrodsParams) error { +func (c ClearJob) ClearBlocks(params ClearRecordsParams) error { Log(INFO, "Connecting to database ...") conn, err := sqlx.Open(params.Scheme(), params.String()) diff --git a/background_worker/jobs/clear_blocks_test.go b/background_worker/jobs/clear_blocks_test.go index 0e3921f53..1fcd07812 100644 --- a/background_worker/jobs/clear_blocks_test.go +++ b/background_worker/jobs/clear_blocks_test.go @@ -16,11 +16,11 @@ import ( ) type ClearJobSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *ClearJobSuite) Test() { - params := ClearRecrodsParams{ + params := ClearRecordsParams{ DBConnectionParams: DefaultParams, RecordRetentionDays: 10, } diff --git a/background_worker/jobs/clear_metamorph.go b/background_worker/jobs/clear_metamorph.go new file mode 100644 index 000000000..7f3bfedaf --- /dev/null +++ b/background_worker/jobs/clear_metamorph.go @@ -0,0 +1,43 @@ +package jobs + +import ( + "fmt" + + "github.com/jmoiron/sqlx" +) + +func runDelete(table string, params ClearRecordsParams) error { + Log(INFO, "Connecting to database ...") + conn, err := sqlx.Open(params.Scheme(), params.String()) + if err != nil { + Log(ERROR, fmt.Sprintf("unable to create connection %s", err)) + return err + } + interval := fmt.Sprintf("%d days", params.RecordRetentionDays) + + stmt, err := conn.Preparex(fmt.Sprintf("DELETE FROM %s WHERE inserted_at <= (CURRENT_DATE - $1::interval)", table)) + if err != nil { + Log(ERROR, fmt.Sprintf("unable to prepare statement %s", err)) + return err + } + + res, err := stmt.Exec(interval) + if err != nil { + Log(ERROR, "unable to delete block rows") + return err + } + rows, _ := res.RowsAffected() + Log(INFO, fmt.Sprintf("Successfully deleted %d rows", rows)) + return nil +} + +func ClearMetamorph(params ClearRecordsParams) error { + if err := runDelete("metamorph.blocks", params); err != nil { + return err + } + if err := runDelete("metamorph.transactions", params); err != nil { + return err + } + + return nil +} diff --git a/background_worker/jobs/clear_metamorph_test.go b/background_worker/jobs/clear_metamorph_test.go new file mode 100644 index 000000000..bfe40dbb0 --- /dev/null +++ b/background_worker/jobs/clear_metamorph_test.go @@ -0,0 +1,71 @@ +package jobs + +import ( + "testing" + "time" + + . "github.com/bitcoin-sv/arc/database_testing" + "github.com/bitcoin-sv/arc/metamorph/store" + "github.com/jmoiron/sqlx" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type ClearMetamorphSuite struct { + MetamorphDBTestSuite +} + +func (s *ClearMetamorphSuite) Test() { + + for i := 0; i < 5; i++ { + blk := GetTestMMBlock() + blk.InsertedAt = time.Now().Add(-20 * 24 * time.Hour) + s.InsertBlock(blk) + } + + for i := 0; i < 5; i++ { + blk := GetTestMMBlock() + blk.InsertedAt = time.Now().Add(-1 * 24 * time.Hour) + s.InsertBlock(blk) + } + + for i := 0; i < 5; i++ { + tx := GetTestMMTransaction() + tx.InsertedAt = time.Now().Add(-20 * 24 * time.Hour) + + s.InsertTransaction(tx) + } + + for i := 0; i < 5; i++ { + tx := GetTestMMTransaction() + tx.InsertedAt = time.Now().Add(-1 * 24 * time.Hour) + + s.InsertTransaction(tx) + } + + err := ClearMetamorph(ClearRecordsParams{ + DBConnectionParams: DefaultMMParams, + RecordRetentionDays: 14, + }) + + require.NoError(s.T(), err) + + db, err := sqlx.Open("postgres", DefaultMMParams.String()) + require.NoError(s.T(), err) + + var blks []store.Block + require.NoError(s.T(), db.Select(&blks, "SELECT * from metamorph.blocks")) + + assert.Len(s.T(), blks, 5) + + var stx []store.Transaction + require.NoError(s.T(), db.Select(&stx, "SELECT * from metamorph.transactions")) + assert.Len(s.T(), stx, 5) + +} + +func TestRunClearMM(t *testing.T) { + s := new(ClearMetamorphSuite) + suite.Run(t, s) +} diff --git a/background_worker/jobs/clear_transactions.go b/background_worker/jobs/clear_transactions.go index 022144952..03fa7d475 100644 --- a/background_worker/jobs/clear_transactions.go +++ b/background_worker/jobs/clear_transactions.go @@ -8,7 +8,7 @@ import ( _ "github.com/lib/pq" ) -func (c ClearJob) ClearTransactions(params ClearRecrodsParams) error { +func (c ClearJob) ClearTransactions(params ClearRecordsParams) error { Log(INFO, "Connecting to database ...") conn, err := sqlx.Open(params.Scheme(), params.String()) diff --git a/background_worker/jobs/clear_transactions_test.go b/background_worker/jobs/clear_transactions_test.go index 5c41ca681..6a2706104 100644 --- a/background_worker/jobs/clear_transactions_test.go +++ b/background_worker/jobs/clear_transactions_test.go @@ -16,11 +16,11 @@ import ( ) type ClearTransactionsSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *ClearTransactionsSuite) Test() { - params := ClearRecrodsParams{ + params := ClearRecordsParams{ DBConnectionParams: DefaultParams, RecordRetentionDays: 10, } diff --git a/background_worker/scheduler.go b/background_worker/scheduler.go index bdc76fa36..9d874031d 100644 --- a/background_worker/scheduler.go +++ b/background_worker/scheduler.go @@ -9,10 +9,10 @@ import ( type ARCScheduler struct { Scheduler *gocron.Scheduler IntervalInHours int - Params jobs.ClearRecrodsParams + Params jobs.ClearRecordsParams } -func (sched *ARCScheduler) RunJob(table string, job func(params jobs.ClearRecrodsParams) error) { +func (sched *ARCScheduler) RunJob(table string, job func(params jobs.ClearRecordsParams) error) { _, err := sched.Scheduler.Every(sched.IntervalInHours).Hours().Do(func() { jobs.Log(jobs.INFO, fmt.Sprintf("Clearing expired %s...", table)) err := job(sched.Params) diff --git a/blocktx/store/sql/get_block_for_height_test.go b/blocktx/store/sql/get_block_for_height_test.go index a5c30db12..56b30fa2b 100644 --- a/blocktx/store/sql/get_block_for_height_test.go +++ b/blocktx/store/sql/get_block_for_height_test.go @@ -13,7 +13,7 @@ import ( ) type GetBlockByHeightTestSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetBlockByHeightTestSuite) Test() { diff --git a/blocktx/store/sql/get_block_gaps_test.go b/blocktx/store/sql/get_block_gaps_test.go index 5f854c23e..d289a2ec4 100644 --- a/blocktx/store/sql/get_block_gaps_test.go +++ b/blocktx/store/sql/get_block_gaps_test.go @@ -14,7 +14,7 @@ import ( ) type GetBlockGapTestSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetBlockGapTestSuite) Test() { diff --git a/blocktx/store/sql/get_block_test.go b/blocktx/store/sql/get_block_test.go index 4fff6b282..353f8ec59 100644 --- a/blocktx/store/sql/get_block_test.go +++ b/blocktx/store/sql/get_block_test.go @@ -14,7 +14,7 @@ import ( ) type GetBlockTestSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetBlockTestSuite) Test() { diff --git a/blocktx/store/sql/get_block_transactions_test.go b/blocktx/store/sql/get_block_transactions_test.go index 50d76d38b..c8d32594f 100644 --- a/blocktx/store/sql/get_block_transactions_test.go +++ b/blocktx/store/sql/get_block_transactions_test.go @@ -15,7 +15,7 @@ import ( ) type GetBlockTransactionsSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetBlockTransactionsSuite) Test() { diff --git a/blocktx/store/sql/get_last_processed_block_test.go b/blocktx/store/sql/get_last_processed_block_test.go index 3fd649e19..562f0a5de 100644 --- a/blocktx/store/sql/get_last_processed_block_test.go +++ b/blocktx/store/sql/get_last_processed_block_test.go @@ -14,7 +14,7 @@ import ( ) type GetLastProcessedBlockSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetLastProcessedBlockSuite) Test() { diff --git a/blocktx/store/sql/get_mined_transaction_for_block_test.go b/blocktx/store/sql/get_mined_transaction_for_block_test.go index bc49575ac..df68ea156 100644 --- a/blocktx/store/sql/get_mined_transaction_for_block_test.go +++ b/blocktx/store/sql/get_mined_transaction_for_block_test.go @@ -13,7 +13,7 @@ import ( ) type MinedTransactionForBlockSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *MinedTransactionForBlockSuite) Test() { diff --git a/blocktx/store/sql/get_transaction_block_test.go b/blocktx/store/sql/get_transaction_block_test.go index 0261ef7a4..3ddaf9685 100644 --- a/blocktx/store/sql/get_transaction_block_test.go +++ b/blocktx/store/sql/get_transaction_block_test.go @@ -13,7 +13,7 @@ import ( ) type GetTransactionBlockSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetTransactionBlockSuite) Test() { diff --git a/blocktx/store/sql/get_transaction_blocks_test.go b/blocktx/store/sql/get_transaction_blocks_test.go index 83132ac17..d81c52c71 100644 --- a/blocktx/store/sql/get_transaction_blocks_test.go +++ b/blocktx/store/sql/get_transaction_blocks_test.go @@ -15,7 +15,7 @@ import ( ) type GetTransactionBlocksSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetTransactionBlocksSuite) Test() { diff --git a/blocktx/store/sql/get_transaction_merkle_path_test.go b/blocktx/store/sql/get_transaction_merkle_path_test.go index 35af8a205..140985c39 100644 --- a/blocktx/store/sql/get_transaction_merkle_path_test.go +++ b/blocktx/store/sql/get_transaction_merkle_path_test.go @@ -13,7 +13,7 @@ import ( ) type GetTransactionMerklePathSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *GetTransactionMerklePathSuite) Test() { diff --git a/blocktx/store/sql/insert_block_test.go b/blocktx/store/sql/insert_block_test.go index 38fc27596..6cf776d6b 100644 --- a/blocktx/store/sql/insert_block_test.go +++ b/blocktx/store/sql/insert_block_test.go @@ -14,7 +14,7 @@ import ( ) type InsertBlockSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *InsertBlockSuite) Test() { diff --git a/blocktx/store/sql/insert_block_transactions_test.go b/blocktx/store/sql/insert_block_transactions_test.go index 3fc14feff..5e7b20d97 100644 --- a/blocktx/store/sql/insert_block_transactions_test.go +++ b/blocktx/store/sql/insert_block_transactions_test.go @@ -14,7 +14,7 @@ import ( ) type InsertBlockTransactionsSuite struct { - DatabaseTestSuite + BlockTXDBTestSuite } type Tx struct { diff --git a/blocktx/store/sql/orphan_height_test.go b/blocktx/store/sql/orphan_height_test.go index 054087d78..ccba73238 100644 --- a/blocktx/store/sql/orphan_height_test.go +++ b/blocktx/store/sql/orphan_height_test.go @@ -13,7 +13,7 @@ import ( ) type OrphanHeightSutie struct { - DatabaseTestSuite + BlockTXDBTestSuite } func (s *OrphanHeightSutie) Test() { diff --git a/cmd/background_worker.go b/cmd/background_worker.go index 8416df189..7cf78d197 100644 --- a/cmd/background_worker.go +++ b/cmd/background_worker.go @@ -57,7 +57,7 @@ func StartBackGroundWorker(logger *slog.Logger) (func(), error) { return nil, err } - params := jobs.ClearRecrodsParams{ + params := jobs.ClearRecordsParams{ DBConnectionParams: dbconn.New( dbHost, dbPort, diff --git a/cmd/background_worker/main.go b/cmd/background_worker/main.go index eb0be1c8f..16e639199 100644 --- a/cmd/background_worker/main.go +++ b/cmd/background_worker/main.go @@ -27,7 +27,7 @@ func main() { } } - params := jobs.ClearRecrodsParams{ + params := jobs.ClearRecordsParams{ DBConnectionParams: dbconn.New( viper.GetString("cleanBlocks.host"), viper.GetInt("cleanBlocks.port"), diff --git a/database/migrations/metamorph/postgres/000001_create_transactions.down.sql b/database/migrations/metamorph/postgres/000001_create_transactions.down.sql new file mode 100644 index 000000000..693c84add --- /dev/null +++ b/database/migrations/metamorph/postgres/000001_create_transactions.down.sql @@ -0,0 +1 @@ +DROP TABLE metamorph.transactions; diff --git a/database/migrations/metamorph/postgres/000001_create_transactions.up.sql b/database/migrations/metamorph/postgres/000001_create_transactions.up.sql new file mode 100644 index 000000000..29108f7c5 --- /dev/null +++ b/database/migrations/metamorph/postgres/000001_create_transactions.up.sql @@ -0,0 +1,20 @@ +CREATE SCHEMA metamorph; +CREATE TABLE metamorph.transactions ( + hash BYTEA PRIMARY KEY, + stored_at TIMESTAMPTZ, + announced_at TIMESTAMPTZ, + mined_at TIMESTAMPTZ, + status INTEGER, + block_height BIGINT, + block_hash BYTEA, + callback_url TEXT, + callback_token TEXT, + merkle_proof TEXT, + reject_reason TEXT, + raw_tx BYTEA, + locked_by TEXT, + inserted_at_num INTEGER DEFAULT TO_NUMBER(TO_CHAR((NOW()) AT TIME ZONE 'UTC', 'yyyymmddhh24'), '9999999999') NOT NULL +); + +CREATE INDEX ix_metamorph_transactions_locked_by ON metamorph.transactions (locked_by); +CREATE INDEX ix_metamorph_transactions_inserted_at_num ON metamorph.transactions (inserted_at_num); diff --git a/database/migrations/metamorph/postgres/000002_create_blocks.down.sql b/database/migrations/metamorph/postgres/000002_create_blocks.down.sql new file mode 100644 index 000000000..06d8b13d6 --- /dev/null +++ b/database/migrations/metamorph/postgres/000002_create_blocks.down.sql @@ -0,0 +1 @@ +DROP TABLE metamorph.blocks; diff --git a/database/migrations/metamorph/postgres/000002_create_blocks.up.sql b/database/migrations/metamorph/postgres/000002_create_blocks.up.sql new file mode 100644 index 000000000..4cc1cc0eb --- /dev/null +++ b/database/migrations/metamorph/postgres/000002_create_blocks.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE metamorph.blocks ( + hash BYTEA PRIMARY KEY, + processed_at TIMESTAMPTZ, + inserted_at_num INTEGER DEFAULT TO_NUMBER(TO_CHAR((NOW()) AT TIME ZONE 'UTC', 'yyyymmddhh24'), '9999999999') NOT NULL +); + +CREATE INDEX ix_metamorph_blocks_inserted_at_num ON metamorph.blocks (inserted_at_num); diff --git a/database/migrations/metamorph/postgres/000003_create_functions.down.sql b/database/migrations/metamorph/postgres/000003_create_functions.down.sql new file mode 100644 index 000000000..22f8397b5 --- /dev/null +++ b/database/migrations/metamorph/postgres/000003_create_functions.down.sql @@ -0,0 +1,2 @@ +DROP FUNCTION reverse_bytes_iter(bytes bytea, length int, midpoint int, index int); +DROP FUNCTION reverse_bytes(bytes bytea); diff --git a/database/migrations/metamorph/postgres/000003_create_functions.up.sql b/database/migrations/metamorph/postgres/000003_create_functions.up.sql new file mode 100644 index 000000000..a49eec2fb --- /dev/null +++ b/database/migrations/metamorph/postgres/000003_create_functions.up.sql @@ -0,0 +1,16 @@ +CREATE OR REPLACE FUNCTION reverse_bytes_iter(bytes bytea, length int, midpoint int, index int) + RETURNS bytea AS + $$ +SELECT CASE WHEN index >= midpoint THEN bytes ELSE + reverse_bytes_iter( + set_byte( + set_byte(bytes, index, get_byte(bytes, length-index)), + length-index, get_byte(bytes, index) + ), + length, midpoint, index + 1 + ) + END; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE +OR REPLACE FUNCTION reverse_bytes(bytes bytea) RETURNS bytea AS 'SELECT reverse_bytes_iter(bytes, octet_length(bytes)-1, octet_length(bytes)/2, 0)' LANGUAGE SQL IMMUTABLE; diff --git a/database/migrations/metamorph/postgres/000004_create_timestamps.down.sql b/database/migrations/metamorph/postgres/000004_create_timestamps.down.sql new file mode 100644 index 000000000..d3725bd2d --- /dev/null +++ b/database/migrations/metamorph/postgres/000004_create_timestamps.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE metamorph.blocks DROP COLUMN inserted_at; +ALTER TABLE metamorph.transactions DROP COLUMN inserted_at; diff --git a/database/migrations/metamorph/postgres/000004_create_timestamps.up.sql b/database/migrations/metamorph/postgres/000004_create_timestamps.up.sql new file mode 100644 index 000000000..4af3ecf85 --- /dev/null +++ b/database/migrations/metamorph/postgres/000004_create_timestamps.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE metamorph.transactions ADD COLUMN inserted_at TIMESTAMPTZ; +ALTER TABLE metamorph.blocks ADD COLUMN inserted_at TIMESTAMPTZ; \ No newline at end of file diff --git a/database_testing/database_test_suite.go b/database_testing/blocktx_db_test_suite.go similarity index 85% rename from database_testing/database_test_suite.go rename to database_testing/blocktx_db_test_suite.go index 956f3a7fc..c8966a49e 100644 --- a/database_testing/database_test_suite.go +++ b/database_testing/blocktx_db_test_suite.go @@ -66,17 +66,17 @@ var DefaultParams = dbconn.New( "disable", ) -// DatabaseTestSuite test helper suite to +// BlockTXDBTestSuite test helper suite to // 1. create database -// 2. run database/migrations +// 2. run database/postgres // 3. use in test scenario // 4. tear down when tests are finished -type DatabaseTestSuite struct { +type BlockTXDBTestSuite struct { suite.Suite Connection *sqlx.Conn } -func (s *DatabaseTestSuite) SetupSuite() { +func (s *BlockTXDBTestSuite) SetupSuite() { _, callerFilePath, _, _ := runtime.Caller(0) testDir := filepath.Dir(callerFilePath) @@ -93,24 +93,15 @@ func (s *DatabaseTestSuite) SetupSuite() { } } -func (s *DatabaseTestSuite) SetupTest() { +func (s *BlockTXDBTestSuite) SetupTest() { s.truncateTables() } -func (s *DatabaseTestSuite) truncateTables() { - db, err := sqlx.Open("postgres", DefaultParams.String()) - require.NoError(s.T(), err) - - db.MustExec("truncate table blocks;") - db.MustExec("truncate table transactions;") - db.MustExec("truncate table block_transactions_map;") -} - -func (s *DatabaseTestSuite) Conn() *sqlx.Conn { +func (s *BlockTXDBTestSuite) Conn() *sqlx.Conn { return s.Connection } -func (s *DatabaseTestSuite) InsertBlock(block *store.Block) { +func (s *BlockTXDBTestSuite) InsertBlock(block *store.Block) { db, err := sqlx.Open("postgres", DefaultParams.String()) require.NoError(s.T(), err) @@ -140,7 +131,7 @@ func (s *DatabaseTestSuite) InsertBlock(block *store.Block) { } -func (s *DatabaseTestSuite) InsertTransaction(tx *store.Transaction) { +func (s *BlockTXDBTestSuite) InsertTransaction(tx *store.Transaction) { db, err := sqlx.Open("postgres", DefaultParams.String()) require.NoError(s.T(), err) q := `INSERT INTO transactions( @@ -159,7 +150,7 @@ func (s *DatabaseTestSuite) InsertTransaction(tx *store.Transaction) { require.NoError(s.T(), err, fmt.Sprintf("tx %+v", tx)) } -func (s *DatabaseTestSuite) InsertBlockTransactionMap(btx *store.BlockTransactionMap) { +func (s *BlockTXDBTestSuite) InsertBlockTransactionMap(btx *store.BlockTransactionMap) { db, err := sqlx.Open("postgres", DefaultParams.String()) require.NoError(s.T(), err) @@ -177,6 +168,15 @@ func (s *DatabaseTestSuite) InsertBlockTransactionMap(btx *store.BlockTransactio } // TearDownTest clear all the tables -func (s *DatabaseTestSuite) TearDownTest() { +func (s *BlockTXDBTestSuite) TearDownTest() { s.truncateTables() } + +func (s *BlockTXDBTestSuite) truncateTables() { + db, err := sqlx.Open("postgres", DefaultParams.String()) + require.NoError(s.T(), err) + + db.MustExec("truncate table blocks;") + db.MustExec("truncate table transactions;") + db.MustExec("truncate table block_transactions_map;") +} diff --git a/database_testing/metamorph_db_test_suite.go b/database_testing/metamorph_db_test_suite.go new file mode 100644 index 000000000..13cd09a0c --- /dev/null +++ b/database_testing/metamorph_db_test_suite.go @@ -0,0 +1,154 @@ +package database_testing + +import ( + "errors" + "fmt" + "path/filepath" + "runtime" + "time" + + "github.com/bitcoin-sv/arc/dbconn" + "github.com/bitcoin-sv/arc/metamorph/store" + "github.com/golang-migrate/migrate/v4" + _ "github.com/golang-migrate/migrate/v4/database/postgres" + _ "github.com/golang-migrate/migrate/v4/source/file" + "github.com/jmoiron/sqlx" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +func GetTestMMBlock() *store.Block { + return &store.Block{ + Hash: GetRandomBytes(), + ProcessedAt: time.Now().UTC(), + InsertedAt: time.Now().UTC(), + } +} + +func GetTestMMTransaction() *store.Transaction { + return &store.Transaction{ + Hash: GetRandomBytes(), + StoredAt: time.Date(2023, 10, 4, 22, 0, 0, 0, time.UTC), + AnnouncedAt: time.Date(2023, 10, 5, 12, 0, 0, 0, time.UTC), + MinedAt: time.Date(2023, 10, 7, 10, 0, 0, 0, time.UTC), + Status: 1, + BlockHeight: int64(12345), + BlockHash: []byte{0x11, 0x22, 0x33, 0x44}, + CallbackURL: "https://example.com/callback", + CallbackToken: "1234567890abcdef", + MerkleProof: "4d1f934bd7a5223b95656220d39c64b3a66b0f770137f6611052381806e90275", + RawTX: []byte("01020304"), // example raw transaction data + LockedBy: "0x1234567890abcdef", + InsertedAt: time.Now().UTC(), + } +} + +var DefaultMMParams = dbconn.New( + "localhost", + 5432, + "arcuser", + "arcpass", + "metamorph_test", + "postgres", + "disable", +) + +type MetamorphDBTestSuite struct { + suite.Suite + Connection *sqlx.Conn +} + +func (s *MetamorphDBTestSuite) SetupSuite() { + _, callerFilePath, _, _ := runtime.Caller(0) + + testDir := filepath.Dir(callerFilePath) + + path := "file://" + testDir + "/../database/migrations/metamorph/postgres" + m, err := migrate.New(path, DefaultMMParams.String()) + require.NoError(s.T(), err) + s.T().Log("applied migrations") + if err := m.Up(); err != nil { + if !errors.Is(err, migrate.ErrNoChange) { + require.NoError(s.T(), err) + } + } +} + +func (s *MetamorphDBTestSuite) SetupTest() { + s.truncateTables() +} + +func (s *MetamorphDBTestSuite) truncateTables() { + s.T().Log("truncating tables") + db, err := sqlx.Open("postgres", DefaultMMParams.String()) + require.NoError(s.T(), err) + + db.MustExec("truncate table metamorph.blocks;") + db.MustExec("truncate table metamorph.transactions;") +} + +func (s *MetamorphDBTestSuite) Conn() *sqlx.Conn { + return s.Connection +} + +func (s *MetamorphDBTestSuite) InsertBlock(block *store.Block) { + db, err := sqlx.Open("postgres", DefaultMMParams.String()) + require.NoError(s.T(), err) + + q := `INSERT INTO metamorph.blocks( + hash, + processed_at, + inserted_at) + VALUES( + :hash, + :processed_at, + :inserted_at + );` + + _, err = db.NamedExec(q, + block) + require.NoError(s.T(), err) + +} + +func (s *MetamorphDBTestSuite) InsertTransaction(tx *store.Transaction) { + db, err := sqlx.Open("postgres", DefaultMMParams.String()) + require.NoError(s.T(), err) + q := `INSERT INTO metamorph.transactions (hash, + stored_at, + announced_at, + mined_at, + status, + block_height, + block_hash, + callback_url, + callback_token, + merkle_proof, + reject_reason, + raw_tx, + locked_by, + inserted_at) VALUES ( + :hash, + :stored_at, + :announced_at, + :mined_at, + :status, + :block_height, + :block_hash, + :callback_url, + :callback_token, + :merkle_proof, + :reject_reason, + :raw_tx, + :locked_by, + :inserted_at);` + + _, err = db.NamedExec(q, tx) + + require.NoError(s.T(), err, fmt.Sprintf("tx %+v", tx)) +} + +// TearDownTest clear all the tables +func (s *MetamorphDBTestSuite) TearDownTest() { + s.truncateTables() +} diff --git a/metamorph/store/model.go b/metamorph/store/model.go new file mode 100644 index 000000000..354f4907b --- /dev/null +++ b/metamorph/store/model.go @@ -0,0 +1,28 @@ +package store + +import "time" + +type Block struct { + Hash string `db:"hash"` + ProcessedAt time.Time `db:"processed_at"` + InsertedAt time.Time `db:"inserted_at"` + InsertedAtNum int64 `db:"inserted_at_num"` +} + +type Transaction struct { + Hash string `db:"hash"` + StoredAt time.Time `db:"stored_at"` + AnnouncedAt time.Time `db:"announced_at"` + MinedAt time.Time `db:"mined_at"` + Status int `db:"status"` + BlockHeight int64 `db:"block_height"` + BlockHash []byte `db:"block_hash"` + CallbackURL string `db:"callback_url"` + CallbackToken string `db:"callback_token"` + MerkleProof string `db:"merkle_proof"` + RejectReason string `db:"reject_reason"` + RawTX []byte `db:"raw_tx"` + LockedBy string `db:"locked_by"` + InsertedAt time.Time `db:"inserted_at"` + InsertedAtNum int64 `db:"inserted_at_num"` +}