Skip to content

Commit de6ce0c

Browse files
authored
[SOT-197] feat: add verifier (#2)
* feat: add verifier * fix: update log * doc: update README.md * fix: create one ipfs shell.NewShell() session per QmHash * fix: add output-timestamp dir for verification output
1 parent 1b01575 commit de6ce0c

File tree

7 files changed

+334
-51
lines changed

7 files changed

+334
-51
lines changed

README.md

+42-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,43 @@
11
# btfs-migration-tools
2-
tools: migrate ipfs file to btfs network
2+
> This is a command line tool to migrate ipfs file to btfs network
3+
4+
## Get the source code
5+
```bash
6+
go get -u github.com/TRON-US/btfs-migration-toolkit
7+
```
8+
9+
## Build the binary
10+
```bash
11+
go build
12+
```
13+
14+
## Usage
15+
The file migration from IPFS network to BTFS network has to be done in two phases.
16+
17+
Phase one: download a file from IPFS to local file system and upload this file to BTFS network
18+
through Soter using soter-go-sdk.
19+
20+
Phase two: verify a file has indeed been stored in BTFS network by BTT Integration Architecture.
21+
This is achieved by using the request id to query soter status record.
22+
23+
Before using this command line tool, users should configure the config.yaml file first and provide a input.csv file.
24+
### Example
25+
#### Batch upload
26+
```bash
27+
./btfs-migration-toolkit --config=config.yaml --method=batch_upload --input=input.csv
28+
```
29+
30+
#### Single upload
31+
```bash
32+
./btfs-migration-toolkit --config=config.yaml --method=single_upload --hash=Qmxxxxxx
33+
```
34+
35+
#### Batch verify
36+
```bash
37+
./btfs-migration-toolkit --config=config.yaml --method=batch_verify --input=output_hash.csv
38+
```
39+
40+
#### Single verify
41+
```bash
42+
./btfs-migration-toolkit --config=config.yaml --method=single_verify --request-id=xxxxx
43+
```

constants/constant.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,29 @@ const (
44
OkCode = 0
55
InsufficientBalanceCode = 20009
66

7-
OutputHashFileName = "output_hash.csv"
8-
OutputRetryFileName = "output_retry.csv"
7+
// files has been downloaded from IPFS network and uploaded to BTFS network through Soter
8+
// BTFS hash and request id have been return from Soter and saved to this file
9+
OutputHashFileName = "output_hash.csv"
10+
// the migration has been failed, and IPFS hash is saved in this file for retry
11+
OutputRetryFileName = "output_retry.csv"
12+
// record identified by this request id is in Pending status
13+
OutputPendingFileName = "output_P.csv"
14+
// record identified by this request id is in Failed status
15+
OutputFailFileName = "output_F.csv"
16+
// record identified by this request id is in Success status
17+
OutputSucessFileName = "output_S.csv"
18+
// this request id is invalid or verification is abort due to internal error
19+
OutputErrorFileName = "output_E.csv"
920

1021
// method
11-
BatchUpload = "batch_upload"
22+
BatchUpload = "batch_upload"
1223
SingleUpload = "single_upload"
24+
BatchVerify = "batch_verify"
25+
SingleVerify = "single_verify"
26+
27+
//
28+
Delimiter = ","
29+
StatusP = "P"
30+
StatusF = "F"
31+
StatusS = "S"
1332
)

core/load_config.go

-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ package core
22

33
import (
44
"github.com/TRON-US/btfs-migration-toolkit/conf"
5-
65
"github.com/fsnotify/fsnotify"
7-
"github.com/ipfs/go-ipfs-api"
86
"github.com/spf13/viper"
97
)
108

119
var Conf *conf.Config
12-
var Sh *shell.Shell
1310

1411
//init config file to viper
1512
func InitConfig(cfg string) error {
@@ -48,7 +45,6 @@ func getNewConfig() error {
4845
if err := viper.Unmarshal(Conf); err != nil {
4946
return err
5047
}
51-
Sh = shell.NewShell(Conf.IpfsUrl)
5248

5349
return nil
5450
}

core/soter_response.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
package core
22

3-
type SoterResponse struct {
3+
type SoterAddFileResponse struct {
44
Cid string `json:"cid"`
55
RequestId string `json:"request_id"`
66
}
7+
8+
type SoterOrderDetailsResponse struct {
9+
FileHash string `json:"file_hash"`
10+
FileSize int64 `json:"file_size"`
11+
FileName string `json:"file_name"`
12+
Fee int64 `json:"fee"`
13+
Type string `json:"type"`
14+
Description string `json:"description"`
15+
Status string `json:"status"`
16+
}

main.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package main
22

33
import (
44
"fmt"
5+
"os"
6+
57
"github.com/TRON-US/btfs-migration-toolkit/constants"
68
"github.com/TRON-US/btfs-migration-toolkit/core"
7-
"github.com/TRON-US/btfs-migration-toolkit/uploader"
8-
"os"
9+
"github.com/TRON-US/btfs-migration-toolkit/service"
910

1011
"github.com/spf13/pflag"
1112
)
@@ -15,6 +16,7 @@ var (
1516
method = pflag.StringP("method", "m", "", "choose a method to run")
1617
inputFile = pflag.StringP("input", "i", "", "input .csv file with a list of IPFS QmHash")
1718
ipfsHash = pflag.StringP("hash", "h", "", "IPFS hash to migrate")
19+
requestId = pflag.StringP("request-id", "r", "", "request id to query for")
1820
)
1921

2022
func main() {
@@ -26,16 +28,15 @@ func main() {
2628

2729
switch *method {
2830
case constants.BatchUpload:
29-
uploader.BatchUpload(*inputFile)
31+
service.BatchUpload(*inputFile)
3032
case constants.SingleUpload:
31-
uploader.SingleUpload(*ipfsHash)
33+
service.SingleUpload(*ipfsHash)
34+
case constants.BatchVerify:
35+
service.BatchVerify(*inputFile)
36+
case constants.SingleVerify:
37+
service.SingleVerify(*requestId)
3238
default:
3339
fmt.Println("unknown method")
3440
os.Exit(0)
3541
}
36-
37-
fmt.Printf("Migration complete.\n" +
38-
"Please checkout %s and %s for batch migration.\n" +
39-
"Or review screen output for single migration.\n",
40-
constants.OutputHashFileName, constants.OutputRetryFileName)
4142
}

uploader/upload.go renamed to service/uploader.go

+46-33
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
package uploader
1+
package service
22

33
import (
44
"bufio"
55
"encoding/json"
66
"fmt"
7+
"os"
8+
"strings"
9+
"sync"
10+
711
"github.com/TRON-US/btfs-migration-toolkit/constants"
812
"github.com/TRON-US/btfs-migration-toolkit/core"
913
"github.com/TRON-US/btfs-migration-toolkit/log"
1014
"github.com/TRON-US/soter-sdk-go/soter"
11-
"os"
12-
"strings"
13-
"sync"
15+
16+
"github.com/ipfs/go-ipfs-api"
1417
)
1518

1619
func BatchUpload(inputFilename string) {
17-
batchSize := core.Conf.BatchSize
18-
1920
inputHashFile, err := os.Open(inputFilename)
2021
if err != nil {
2122
log.Logger().Error(fmt.Sprintf("Failed to open file %s, reason=[%v]", inputFilename, err))
@@ -27,7 +28,7 @@ func BatchUpload(inputFilename string) {
2728
}
2829
}()
2930

30-
outputHashFile, err := os.Create(fmt.Sprintf("./%s", constants.OutputHashFileName))
31+
outputHashFile, err := os.Create(constants.OutputHashFileName)
3132
if err != nil {
3233
log.Logger().Error(fmt.Sprintf("Failed to open file %s, reason=[%v]", constants.OutputHashFileName, err))
3334
os.Exit(1)
@@ -38,10 +39,18 @@ func BatchUpload(inputFilename string) {
3839
constants.OutputHashFileName, err))
3940
}
4041
}()
41-
outputRetryFile, err := os.Create(fmt.Sprintf("./%s", constants.OutputRetryFileName))
42+
43+
outputRetryFile, err := os.Create(constants.OutputRetryFileName)
4244
if err != nil {
43-
log.Logger().Error(fmt.Sprintf("Failed to open %s, reason=[%v]", constants.OutputRetryFileName, err))
45+
log.Logger().Error(fmt.Sprintf("Failed to open file %s, reason=[%v]", constants.OutputRetryFileName, err))
46+
os.Exit(1)
4447
}
48+
defer func() {
49+
if err := outputRetryFile.Close(); err != nil {
50+
log.Logger().Error(fmt.Sprintf("Failed to close file %s, reason=[%v]",
51+
constants.OutputRetryFileName, err))
52+
}
53+
}()
4554

4655
wg := sync.WaitGroup{}
4756
scanner := bufio.NewScanner(inputHashFile)
@@ -50,44 +59,50 @@ func BatchUpload(inputFilename string) {
5059
hash := scanner.Text()
5160
wg.Add(1)
5261
counter++
53-
go func(h string, outFile *os.File, retryFile *os.File) {
62+
go func(h string, outFile, retryFile *os.File) {
5463
defer wg.Done()
5564
res, err := migrate(h)
5665
if err != nil {
57-
log.Logger().Error(fmt.Sprintf("ipfs_hash=%s, reason=[%v]", h, err))
66+
log.Logger().Error(fmt.Sprintf("[ipfs_hash=%s] Failed to migrate, reason=[%v]", h, err))
5867
// definitely failed to upload through soter; write to output_retry.csv
5968
_, err = fmt.Fprintln(retryFile, h)
6069
if err != nil {
61-
errMsg := fmt.Sprintf("Failed to write to file %s, hash=%s, reason=[%v]",
62-
constants.OutputRetryFileName, h, err)
70+
errMsg := fmt.Sprintf("[ipfs_hash=%s] Failed to write to file %s, reason=[%v]",
71+
h, constants.OutputRetryFileName, err)
6372
log.Logger().Error(errMsg)
6473
}
6574
return
6675
}
67-
log.Logger().Debug(fmt.Sprintf("[%s,%s,%s]", h, res[0], res[1]))
68-
// write <ipfs_hash, request_id, btfs_hash> to output_hash.csv
76+
// write (ipfs_hash, request_id, btfs_hash) to output_hash.csv
77+
log.Logger().Debug(fmt.Sprintf("[ipfs_hash=%s] Write to file %s, (%s,%s,%s)",
78+
h, constants.OutputHashFileName, h, res[0], res[1]))
6979
line := fmt.Sprintf("%s,%s,%s", h, res[0], res[1])
7080
_, err = fmt.Fprintln(outFile, line)
7181
if err != nil {
7282
log.Logger().Error(err.Error())
7383
}
7484
}(hash, outputHashFile, outputRetryFile)
75-
if counter % batchSize == 0 {
85+
if counter % core.Conf.BatchSize == 0 {
7686
wg.Wait()
7787
counter = 0
7888
}
7989
}
8090
// wait here because counter < batchSize and no more lines to read
8191
wg.Wait()
8292
if err := scanner.Err(); err != nil {
83-
log.Logger().Error(err.Error())
93+
errMsg := fmt.Sprintf("Failed to scan input file, reason=[%v]", err)
94+
log.Logger().Error(errMsg)
8495
}
96+
97+
fmt.Printf("\nMigration complete.\n" +
98+
"Please checkout %s and %s for batch migration.\n",
99+
constants.OutputHashFileName, constants.OutputRetryFileName)
85100
}
86101

87102
func SingleUpload(ipfsHash string) {
88103
res, err := migrate(ipfsHash)
89104
if err != nil {
90-
log.Logger().Error(err.Error())
105+
log.Logger().Error(fmt.Sprintf("[ipfs_hash=%s] Failed to migrate, reason=[%v]", ipfsHash, err))
91106
os.Exit(1)
92107
}
93108
fmt.Printf("IPFS hash: %s\n", ipfsHash)
@@ -97,12 +112,11 @@ func SingleUpload(ipfsHash string) {
97112

98113
func migrate(ipfsHash string) ([]string, error) {
99114
if !strings.HasPrefix(ipfsHash, "Qm") {
100-
errMsg := fmt.Sprintf("input with invalid IPFS hash [%s]", ipfsHash)
101-
log.Logger().Debug(errMsg)
115+
errMsg := fmt.Sprintf("[ipfs_hash=%s] Input with invalid ipfs hash", ipfsHash)
102116
return nil, fmt.Errorf(errMsg)
103117
}
104118
// download file from IPFS network to local file system
105-
log.Logger().Debug(fmt.Sprintf("downloading file from IPFS network, %s", ipfsHash))
119+
log.Logger().Debug(fmt.Sprintf("[ipfs_hash=%s] Downloading the file from IPFS network", ipfsHash))
106120
if err := downloadFromIPFS(ipfsHash); err != nil {
107121
return nil, err
108122
}
@@ -111,12 +125,13 @@ func migrate(ipfsHash string) ([]string, error) {
111125
defer func(h string) {
112126
// delete local files
113127
if err := os.Remove(fmt.Sprintf("./%s", h)); err != nil {
114-
errMsg := fmt.Sprintf("Failed to delete file %s", h)
128+
errMsg := fmt.Sprintf("[ipfs_hash=%s] Failed to delete local file", h)
115129
log.Logger().Error(errMsg)
116130
}
117131
}(ipfsHash)
118132

119133
// upload the file to BTFS through soter
134+
log.Logger().Debug(fmt.Sprintf("[ipfs_hash=%s] Uploading the file to BTFS network", ipfsHash))
120135
res, err := uploadToBTFS(ipfsHash)
121136
if err != nil {
122137
return nil, err
@@ -126,8 +141,9 @@ func migrate(ipfsHash string) ([]string, error) {
126141

127142
func downloadFromIPFS(hash string) error {
128143
// go-ipfs-api, sdk: get
129-
if err := core.Sh.Get(hash, hash); err != nil {
130-
return err
144+
sh := shell.NewShell(core.Conf.IpfsUrl)
145+
if err := sh.Get(hash, hash); err != nil {
146+
return fmt.Errorf("downloading from IPFS errors out, %v", err)
131147
}
132148

133149
return nil
@@ -138,27 +154,24 @@ func uploadToBTFS(filename string) ([]string, error) {
138154
filePath := fmt.Sprintf("./%s", filename)
139155
resp, err := sh.AddFile(core.Conf.UserAddress, filePath)
140156
if err != nil {
141-
log.Logger().Error(err.Error())
142-
return nil, err
157+
errMsg := fmt.Sprintf("failed to add file, reason=[%v]", err)
158+
return nil, fmt.Errorf(errMsg)
143159
}
144160
if resp.Code != constants.OkCode {
145-
errMsg := fmt.Sprintf("Error: code=%d, message=%s", resp.Code, resp.Message)
146-
log.Logger().Error(errMsg)
161+
errMsg := fmt.Sprintf("response code error: code=%d, message=%s", resp.Code, resp.Message)
147162
if resp.Code == constants.InsufficientBalanceCode {
148163
os.Exit(1)
149164
}
150165
return nil, fmt.Errorf(errMsg)
151166
}
152167
s, err := json.Marshal(resp.Data)
153168
if err != nil {
154-
log.Logger().Error(err.Error())
155-
return nil, err
169+
return nil, fmt.Errorf("failed to marshal response data, %v", err)
156170
}
157-
var soterResponse core.SoterResponse
171+
var soterResponse core.SoterAddFileResponse
158172
err = json.Unmarshal(s, &soterResponse)
159173
if err != nil {
160-
log.Logger().Error(err.Error())
161-
return nil, err
174+
return nil, fmt.Errorf("failed to unmarshal soter response data, %v", err)
162175
}
163176
res := [...]string{soterResponse.RequestId, soterResponse.Cid}
164177
return res[:], nil

0 commit comments

Comments
 (0)