Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PSL-1207] implement recovery flows for cascade multi-vol registration/activation #893

Merged
merged 1 commit into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions common/storage/ticketstore/activation_attempts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ticketstore

import (
"github.com/pastelnetwork/gonode/common/types"
"time"
)

type ActivationAttemptsQueries interface {
Expand All @@ -15,14 +16,14 @@ type ActivationAttemptsQueries interface {
func (s *TicketStore) InsertActivationAttempt(attempt types.ActivationAttempt) (int64, error) {
const insertQuery = `
INSERT INTO activation_attempts (
file_id, activation_attempt_at, is_successful, error_message
) VALUES (?, ?, ?, ?)
file_id, activation_attempt_at, is_successful, error_message, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?)
RETURNING id;`

var id int64
err := s.db.QueryRow(insertQuery,
attempt.FileID, attempt.ActivationAttemptAt,
attempt.IsSuccessful, attempt.ErrorMessage).Scan(&id)
attempt.IsSuccessful, attempt.ErrorMessage, time.Now().UTC(), time.Now().UTC()).Scan(&id)
if err != nil {
return 0, err
}
Expand All @@ -34,7 +35,7 @@ func (s *TicketStore) InsertActivationAttempt(attempt types.ActivationAttempt) (
func (s *TicketStore) UpdateActivationAttempt(attempt types.ActivationAttempt) (int64, error) {
const updateQuery = `
UPDATE activation_attempts
SET activation_attempt_at = ?, is_successful = ?, error_message = ?
SET activation_attempt_at = ?, is_successful = ?, error_message = ?, updated_at = ?
WHERE id = ? AND file_id = ?
RETURNING id`

Expand All @@ -43,6 +44,7 @@ func (s *TicketStore) UpdateActivationAttempt(attempt types.ActivationAttempt) (
attempt.ActivationAttemptAt,
attempt.IsSuccessful,
attempt.ErrorMessage,
time.Now().UTC(),
attempt.ID,
attempt.FileID).Scan(&id)
if err != nil {
Expand Down
49 changes: 46 additions & 3 deletions common/storage/ticketstore/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package ticketstore

import (
"github.com/pastelnetwork/gonode/common/types"
"time"
)

type FilesQueries interface {
UpsertFile(file types.File) error
GetFileByID(fileID string) (*types.File, error)
GetFilesByBaseFileID(baseFileID string) ([]*types.File, error)
GetFileByTaskID(taskID string) (*types.File, error)
GetFilesByBaseFileIDAndConcludedCheck(baseFileID string, isConcluded bool) ([]*types.File, error)
}

// UpsertFile inserts a new file into the files table
Expand All @@ -20,8 +22,8 @@ func (s *TicketStore) UpsertFile(file types.File) error {
req_amount, is_concluded, cascade_metadata_ticket_id, uuid_key,
hash_of_original_big_file, name_of_original_big_file_with_ext,
size_of_original_big_file, data_type_of_original_big_file,
start_block, done_block
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
start_block, done_block, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(file_id)
DO UPDATE SET
upload_timestamp = COALESCE(excluded.upload_timestamp, files.upload_timestamp),
Expand Down Expand Up @@ -50,7 +52,7 @@ func (s *TicketStore) UpsertFile(file types.File) error {
file.ReqAmount, file.IsConcluded, file.CascadeMetadataTicketID, file.UUIDKey,
file.HashOfOriginalBigFile, file.NameOfOriginalBigFileWithExt,
file.SizeOfOriginalBigFile, file.DataTypeOfOriginalBigFile,
file.StartBlock, file.DoneBlock)
file.StartBlock, file.DoneBlock, time.Now().UTC(), time.Now().UTC())
if err != nil {
return err
}
Expand Down Expand Up @@ -156,3 +158,44 @@ func (s *TicketStore) GetFilesByBaseFileID(baseFileID string) ([]*types.File, er

return files, nil
}

// GetFilesByBaseFileIDAndConcludedCheck retrieves un-concluded files by base_file_id and is_concluded check from the files table.
func (s *TicketStore) GetFilesByBaseFileIDAndConcludedCheck(baseFileID string, isConcluded bool) ([]*types.File, error) {
const selectQuery = `
SELECT file_id, upload_timestamp, path, file_index, base_file_id, task_id,
reg_txid, activation_txid, req_burn_txn_amount, burn_txn_id,
req_amount, is_concluded, cascade_metadata_ticket_id, uuid_key,
hash_of_original_big_file, name_of_original_big_file_with_ext,
size_of_original_big_file, data_type_of_original_big_file,
start_block, done_block
FROM files
WHERE base_file_id = ? AND is_concluded = ?;`

rows, err := s.db.Query(selectQuery, baseFileID, isConcluded)
if err != nil {
return nil, err
}
defer rows.Close()

var files []*types.File
for rows.Next() {
var file types.File
err := rows.Scan(
&file.FileID, &file.UploadTimestamp, &file.Path, &file.FileIndex, &file.BaseFileID, &file.TaskID,
&file.RegTxid, &file.ActivationTxid, &file.ReqBurnTxnAmount, &file.BurnTxnID,
&file.ReqAmount, &file.IsConcluded, &file.CascadeMetadataTicketID, &file.UUIDKey,
&file.HashOfOriginalBigFile, &file.NameOfOriginalBigFileWithExt,
&file.SizeOfOriginalBigFile, &file.DataTypeOfOriginalBigFile,
&file.StartBlock, &file.DoneBlock)
if err != nil {
return nil, err
}
files = append(files, &file)
}

if err = rows.Err(); err != nil {
return nil, err
}

return files, nil
}
1 change: 1 addition & 0 deletions common/storage/ticketstore/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ type TicketStorageInterface interface {
FilesQueries
ActivationAttemptsQueries
RegistrationAttemptsQueries
MultiVolCascadeTicketMapQueries
}
44 changes: 44 additions & 0 deletions common/storage/ticketstore/multi_vol_cascade_ticket_txid_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ticketstore

import (
"time"

"github.com/pastelnetwork/gonode/common/types"
)

type MultiVolCascadeTicketMapQueries interface {
InsertMultiVolCascadeTicketTxIDMap(m types.MultiVolCascadeTicketTxIDMap) error
GetMultiVolCascadeTicketTxIDMap(baseFileID string) (*types.MultiVolCascadeTicketTxIDMap, error)
}

func (s *TicketStore) InsertMultiVolCascadeTicketTxIDMap(m types.MultiVolCascadeTicketTxIDMap) error {
insertSQL := `
INSERT INTO multi_vol_cascade_tickets_txid_map (base_file_id, multi_vol_cascade_ticket_txid, created_at, updated_at)
VALUES (?, ?, ?, ?)`
stmt, err := s.db.Prepare(insertSQL)
if err != nil {
return err
}
defer stmt.Close()

_, err = stmt.Exec(m.BaseFileID, m.MultiVolCascadeTicketTxid, time.Now().UTC(), time.Now().UTC())
if err != nil {
return err
}
return nil
}

func (s *TicketStore) GetMultiVolCascadeTicketTxIDMap(baseFileID string) (*types.MultiVolCascadeTicketTxIDMap, error) {
selectSQL := `
SELECT id, base_file_id, multi_vol_cascade_ticket_txid
FROM multi_vol_cascade_tickets_txid_map
WHERE base_file_id = ?`
row := s.db.QueryRow(selectSQL, baseFileID)

var ticket types.MultiVolCascadeTicketTxIDMap
err := row.Scan(&ticket.ID, &ticket.BaseFileID, &ticket.MultiVolCascadeTicketTxid)
if err != nil {
return nil, err
}
return &ticket, nil
}
12 changes: 7 additions & 5 deletions common/storage/ticketstore/registration_attempts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ticketstore

import (
"github.com/pastelnetwork/gonode/common/types"
"time"
)

type RegistrationAttemptsQueries interface {
Expand All @@ -15,14 +16,14 @@ type RegistrationAttemptsQueries interface {
func (s *TicketStore) InsertRegistrationAttempt(attempt types.RegistrationAttempt) (int64, error) {
const insertQuery = `
INSERT INTO registration_attempts (
file_id, reg_started_at, processor_sns, finished_at, is_successful, error_message
) VALUES (?, ?, ?, ?, ?, ?)
file_id, reg_started_at, processor_sns, finished_at, is_successful, error_message, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id;`

var id int64
err := s.db.QueryRow(insertQuery,
attempt.FileID, attempt.RegStartedAt, attempt.ProcessorSNS,
attempt.FinishedAt, attempt.IsSuccessful, attempt.ErrorMessage).Scan(&id)
attempt.FinishedAt, attempt.IsSuccessful, attempt.ErrorMessage, time.Now().UTC(), time.Now().UTC()).Scan(&id)
if err != nil {
return 0, err
}
Expand All @@ -38,14 +39,15 @@ func (s *TicketStore) UpdateRegistrationAttempt(attempt types.RegistrationAttemp
processor_sns = ?,
finished_at = ?,
is_successful = ?,
error_message = ?
error_message = ?,
updated_at = ?
WHERE id = ? AND file_id = ?
RETURNING id;`

var id int64
err := s.db.QueryRow(updateQuery,
attempt.RegStartedAt, attempt.ProcessorSNS, attempt.FinishedAt,
attempt.IsSuccessful, attempt.ErrorMessage,
attempt.IsSuccessful, attempt.ErrorMessage, time.Now().UTC(),
attempt.ID, attempt.FileID).Scan(&id)
if err != nil {
return 0, err
Expand Down
24 changes: 21 additions & 3 deletions common/storage/ticketstore/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ CREATE TABLE IF NOT EXISTS files (
size_of_original_big_file FLOAT,
data_type_of_original_big_file TEXT,
start_block INTEGER,
done_block INTEGER
done_block INTEGER,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
`

Expand All @@ -43,7 +45,9 @@ CREATE TABLE IF NOT EXISTS registration_attempts (
processor_sns TEXT,
finished_at DATETIME,
is_successful BOOLEAN,
error_message TEXT
error_message TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
`

Expand All @@ -53,7 +57,19 @@ CREATE TABLE IF NOT EXISTS activation_attempts (
file_id TEXT NOT NULL,
activation_attempt_at DATETIME,
is_successful BOOLEAN,
error_message TEXT
error_message TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
`

const createMultiVolCascadeTicketTxIDMap string = `
CREATE TABLE IF NOT EXISTS multi_vol_cascade_tickets_txid_map (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
base_file_id TEXT NOT NULL,
multi_vol_cascade_ticket_txid TEXT NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
`

Expand Down Expand Up @@ -94,6 +110,8 @@ func OpenTicketingDb() (TicketStorageInterface, error) {
return nil, fmt.Errorf("cannot create table(s): %w", err)
}

_, _ = db.Exec(createMultiVolCascadeTicketTxIDMap)

pragmas := []string{
"PRAGMA synchronous=NORMAL;",
"PRAGMA cache_size=-262144;",
Expand Down
6 changes: 6 additions & 0 deletions common/types/ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ func (fs Files) GetBase() *File {

return nil
}

type MultiVolCascadeTicketTxIDMap struct {
ID int64
MultiVolCascadeTicketTxid string
BaseFileID string
}
78 changes: 78 additions & 0 deletions walletnode/api/design/cascade.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,31 @@ var _ = Service("cascade", func() {
Response(StatusCreated)
})
})

Method("restore", func() {
Description("Restore the files cascade registration")
Meta("swagger:summary", "Restore the file details for registration, activation and multi-volume pastel")

Security(APIKeyAuth)

Payload(func() {
Extend(RestoreFilePayload)
})
Result(RestoreFileResult)

HTTP(func() {
POST("/restore/{base_file_id}")
Params(func() {
Param("base_file_id", String)
})

// Define error HTTP statuses.
Response("UnAuthorized", StatusUnauthorized)
Response("BadRequest", StatusBadRequest)
Response("InternalServerError", StatusInternalServerError)
Response(StatusCreated)
})
})
})

// AssetUploadPayload represents a payload for uploading asset
Expand Down Expand Up @@ -341,6 +366,45 @@ var FileRegistrationDetailPayload = Type("FileRegistrationDetailPayload", func()
Required("base_file_id")
})

// RestoreFilePayload - Payload for restore file details.
var RestoreFilePayload = Type("RestoreFilePayload", func() {
Description("Restore file details")
Attribute("base_file_id", String, func() {
Description("Base file ID")
MaxLength(8)
Example("VK7mpAqZ")
})
Attribute("app_pastelId", String, func() {
Meta("struct:field:name", "AppPastelID")
Description("App PastelID")
MinLength(86)
MaxLength(86)
Pattern(`^[a-zA-Z0-9]+$`)
Example("jXYJud3rmrR1Sk2scvR47N4E4J5Vv48uCC6se2nzHrBRdjaKj3ybPoi1Y2VVoRqi1GnQrYKjSxQAC7NBtvtEdS")
})
APIKey("api_key", "key", String, func() {
Description("Passphrase of the owner's PastelID")
Example("Basic abcdef12345")
})
Attribute("make_publicly_accessible", Boolean, func() {
Meta("struct:field:name", "MakePubliclyAccessible")
Description("To make it publicly accessible")
Example(false)
Default(false)

})
Attribute("spendable_address", String, func() {
Meta("struct:field:name", "SpendableAddress")
Description("Address to use for registration fee ")
MinLength(35)
MaxLength(35)
Pattern(`^[a-zA-Z0-9]+$`)
Example("PtiqRXn2VQwBjp1K8QXR2uW2w2oZ3Ns7N6j")
})

Required("base_file_id", "app_pastelId", "key")
})

// FileRegistrationDetailResult is registration detail result.
var FileRegistrationDetailResult = ResultType("application/vnd.cascade.registration-detail", func() {
TypeName("Registration")
Expand All @@ -350,6 +414,20 @@ var FileRegistrationDetailResult = ResultType("application/vnd.cascade.registrat
Required("files")
})

// RestoreFileResult is return type for restore file details.
var RestoreFileResult = ResultType("application/vnd.cascade.restore-files", func() {
TypeName("RestoreFile")
Attribute("total_volumes", Int, "Total volumes of selected file")
Attribute("registered_volumes", Int, "Total registered volumes")
Attribute("volumes_with_pending_registration", Int, "Total volumes with pending registration")
Attribute("volumes_registration_in_progress", Int, "Total volumes with in-progress registration")
Attribute("activated_volumes", Int, "Total volumes that are activated")
Attribute("volumes_activated_in_recovery_flow", Int, "Total volumes that are activated in restore process")

Required("total_volumes", "registered_volumes", "volumes_with_pending_registration",
"volumes_registration_in_progress", "activated_volumes", "volumes_activated_in_recovery_flow")
})

var File = Type("File", func() {
Attribute("file_id", String, "File ID")
Attribute("upload_timestamp", String, "Upload Timestamp in datetime format", func() {
Expand Down
Loading
Loading