diff --git a/code/go/0chain.net/blobber/logging.go b/code/go/0chain.net/blobber/logging.go index 339e8807c..98d38e38e 100644 --- a/code/go/0chain.net/blobber/logging.go +++ b/code/go/0chain.net/blobber/logging.go @@ -5,7 +5,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/zcncore" ) func setupLogging() { diff --git a/code/go/0chain.net/blobber/settings.go b/code/go/0chain.net/blobber/settings.go index eb90322b2..72f631f16 100644 --- a/code/go/0chain.net/blobber/settings.go +++ b/code/go/0chain.net/blobber/settings.go @@ -9,7 +9,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/core/transaction" ) type storageScCB struct { @@ -58,20 +58,24 @@ func (ssc *storageScCB) OnInfoAvailable(op int, status int, info string, errStr } func setStorageScConfigFromChain() error { - cb := &storageScCB{ - done: make(chan struct{}), + conf, err := transaction.GetConfig("storage_sc_config") + if err != nil { + return err } - err := zcncore.GetStorageSCConfig(cb) + + maxChallengeCompletionRoundsString := conf.Fields["max_challenge_completion_rounds"] + maxChallengeCompletionRoundsInt, err := strconv.ParseInt(maxChallengeCompletionRoundsString, 10, 64) if err != nil { return err } - <-cb.done - if cb.err != nil { + maxFileSizeString := conf.Fields["max_file_size"] + maxFileSizeInt64, err := strconv.ParseInt(maxFileSizeString, 10, 64) + if err != nil { return err } - config.StorageSCConfig.ChallengeCompletionTime = cb.cct - config.StorageSCConfig.MaxFileSize = cb.mfs + config.StorageSCConfig.ChallengeCompletionTime = maxChallengeCompletionRoundsInt + config.StorageSCConfig.MaxFileSize = maxFileSizeInt64 return nil } diff --git a/code/go/0chain.net/blobber/zcn.go b/code/go/0chain.net/blobber/zcn.go index 8417a81ea..87d95b979 100644 --- a/code/go/0chain.net/blobber/zcn.go +++ b/code/go/0chain.net/blobber/zcn.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "time" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/handler" @@ -10,10 +12,9 @@ import ( handleCommon "github.com/0chain/blobber/code/go/0chain.net/core/common/handler" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" - "github.com/0chain/gosdk/zboxcore/sdk" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/zcncore" "go.uber.org/zap" - "time" ) func registerOnChain() error { @@ -82,16 +83,28 @@ func setupServerChain() error { serverChain := chain.NewChainFromConfig() chain.SetServerChain(serverChain) - if err := zcncore.InitZCNSDK(serverChain.BlockWorker, config.Configuration.SignatureScheme); err != nil { + //options := []int{ + // 0, + // 10, // MinConfirmation + // 20, // MinSubmit + // 3, // ConfirmationChainLength + // 3, // SharderConsensous + // 1, // QuerySleepTime + // 0, // VerifyOptimistic + //} + + err := client.InitSDK("{}", serverChain.BlockWorker, config.Configuration.ChainID, config.Configuration.SignatureScheme, 0, false) + if err != nil { return err } - if err := zcncore.SetWalletInfo(node.Self.GetWalletString(), false); err != nil { + + err = zcncore.SetGeneralWalletInfo(node.Self.GetWalletString(), config.Configuration.SignatureScheme) + if err != nil { return err } - if err := sdk.InitStorageSDK(node.Self.GetWalletString(), serverChain.BlockWorker, config.Configuration.ChainID, config.Configuration.SignatureScheme, - nil, 0); err != nil { - return err + if node.Self.GetWallet().IsSplit { + zcncore.RegisterZauthServer(serverChain.ZauthServer) } fmt.Print(" [OK]\n") diff --git a/code/go/0chain.net/blobbercore/allocation/allocationchange.go b/code/go/0chain.net/blobbercore/allocation/allocationchange.go index 87669bb3d..b1395caa4 100644 --- a/code/go/0chain.net/blobbercore/allocation/allocationchange.go +++ b/code/go/0chain.net/blobbercore/allocation/allocationchange.go @@ -13,12 +13,11 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "golang.org/x/sync/errgroup" "go.uber.org/zap" "gorm.io/gorm" - "gorm.io/gorm/clause" ) const ( @@ -32,8 +31,8 @@ const ( type AllocationChangeProcessor interface { CommitToFileStore(ctx context.Context, mut *sync.Mutex) error DeleteTempFile() error - ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, allocationRoot string, - ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) + ApplyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error GetPath() []string Marshal() (string, error) Unmarshal(string) error @@ -74,6 +73,7 @@ type AllocationChange struct { Input string `gorm:"column:input"` FilePath string `gorm:"-"` LookupHash string `gorm:"column:lookup_hash;size:64"` + AllocationID string `gorm:"-" json:"-"` datastore.ModelWithTS } @@ -230,8 +230,8 @@ func (cc *AllocationChangeCollector) ComputeProperties() { switch change.Operation { case constants.FileOperationInsert: acp = new(UploadFileChanger) - case constants.FileOperationUpdate: - acp = new(UpdateFileChanger) + // case constants.FileOperationUpdate: + // acp = new(UpdateFileChanger) case constants.FileOperationDelete: acp = new(DeleteFileChange) case constants.FileOperationRename: @@ -254,29 +254,36 @@ func (cc *AllocationChangeCollector) ComputeProperties() { } } -func (cc *AllocationChangeCollector) ApplyChanges(ctx context.Context, allocationRoot, prevAllocationRoot string, - ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) { - rootRef, err := cc.GetRootRef(ctx) - if err != nil { - return rootRef, err - } - if rootRef.Hash != prevAllocationRoot { - return rootRef, common.NewError("invalid_prev_root", "Invalid prev root") - } +func (cc *AllocationChangeCollector) ApplyChanges(ctx context.Context, + ts common.Timestamp, allocationVersion int64) error { + now := time.Now() + collector := reference.NewCollector(len(cc.Changes)) + timeoutctx, cancel := context.WithTimeout(ctx, time.Second*60) + defer cancel() + eg, egCtx := errgroup.WithContext(timeoutctx) + eg.SetLimit(10) for idx, change := range cc.Changes { - changeProcessor := cc.AllocationChanges[idx] - _, err := changeProcessor.ApplyChange(ctx, rootRef, change, allocationRoot, ts, fileIDMeta) - if err != nil { - return rootRef, err + select { + case <-egCtx.Done(): + return egCtx.Err() + default: + changeIndex := idx + eg.Go(func() error { + change.AllocationID = cc.AllocationID + changeProcessor := cc.AllocationChanges[changeIndex] + return changeProcessor.ApplyChange(ctx, ts, allocationVersion, collector) + }) } } - collector := reference.NewCollector(len(cc.Changes)) - _, err = rootRef.CalculateHash(ctx, true, collector) + err := eg.Wait() if err != nil { - return rootRef, err + return err } - err = collector.Finalize(ctx) - return rootRef, err + elapsedApplyChanges := time.Since(now) + err = collector.Finalize(ctx, cc.AllocationID, allocationVersion) + elapsedFinalize := time.Since(now) - elapsedApplyChanges + logging.Logger.Info("ApplyChanges", zap.String("allocation_id", cc.AllocationID), zap.Duration("apply_changes", elapsedApplyChanges), zap.Duration("finalize", elapsedFinalize), zap.Int("changes", len(cc.Changes))) + return err } func (a *AllocationChangeCollector) CommitToFileStore(ctx context.Context) error { @@ -304,154 +311,107 @@ func (a *AllocationChangeCollector) DeleteChanges(ctx context.Context) { } type Result struct { - Id string - ValidationRoot string - PrevValidationRoot string - ThumbnailHash string - PrevThumbnailHash string - FilestoreVersion int + LookupHash string } // TODO: Need to speed up this function -func (a *AllocationChangeCollector) MoveToFilestore(ctx context.Context) error { +func (a *AllocationChangeCollector) MoveToFilestore(ctx context.Context, allocationVersion int64) error { logging.Logger.Info("Move to filestore", zap.String("allocation_id", a.AllocationID)) - err := deleteFromFileStore(ctx, a.AllocationID) + var ( + refs []*reference.Ref + useRefCache bool + deletedRefs []*reference.Ref + ) + refCache := reference.GetRefCache(a.AllocationID) + defer reference.DeleteRefCache(a.AllocationID) + if refCache != nil && refCache.AllocationVersion == allocationVersion { + useRefCache = true + refs = refCache.CreatedRefs + deletedRefs = refCache.DeletedRefs + } else if refCache != nil && refCache.AllocationVersion != allocationVersion { + logging.Logger.Error("Ref cache is not valid", zap.String("allocation_id", a.AllocationID), zap.String("ref_cache_version", fmt.Sprintf("%d", refCache.AllocationVersion)), zap.String("allocation_version", fmt.Sprintf("%d", allocationVersion))) + } else { + logging.Logger.Error("Ref cache is nil", zap.String("allocation_id", a.AllocationID)) + } + err := deleteFromFileStore(a.AllocationID, deletedRefs, useRefCache) if err != nil { return err } - var refs []*Result - limitCh := make(chan struct{}, 10) - wg := &sync.WaitGroup{} - err = datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + limitCh := make(chan struct{}, 12) + wg := &sync.WaitGroup{} + if !useRefCache { tx := datastore.GetStore().GetTransaction(ctx) - err := tx.Model(&reference.Ref{}).Clauses(clause.Locking{Strength: "NO KEY UPDATE"}).Select("id", "validation_root", "thumbnail_hash", "prev_validation_root", "prev_thumbnail_hash", "filestore_version").Where("allocation_id=? AND is_precommit=? AND type=?", a.AllocationID, true, reference.FILE). - FindInBatches(&refs, 50, func(tx *gorm.DB, batch int) error { - - for _, ref := range refs { - - var count int64 - if ref.PrevValidationRoot != "" { - tx.Model(&reference.Ref{}). - Where("allocation_id=? AND validation_root=?", a.AllocationID, ref.PrevValidationRoot). - Count(&count) - } - - limitCh <- struct{}{} - wg.Add(1) - - go func(ref *Result) { - defer func() { - <-limitCh - wg.Done() - }() - - if count == 0 && ref.PrevValidationRoot != "" { - err := filestore.GetFileStore().DeleteFromFilestore(a.AllocationID, ref.PrevValidationRoot, ref.FilestoreVersion) - if err != nil { - logging.Logger.Error(fmt.Sprintf("Error while deleting file: %s", err.Error()), - zap.String("validation_root", ref.ValidationRoot)) - } - } - err := filestore.GetFileStore().MoveToFilestore(a.AllocationID, ref.ValidationRoot, ref.FilestoreVersion) - if err != nil { - logging.Logger.Error(fmt.Sprintf("Error while moving file: %s", err.Error()), - zap.String("validation_root", ref.ValidationRoot)) - } - - if ref.ThumbnailHash != "" && ref.ThumbnailHash != ref.PrevThumbnailHash { - if ref.PrevThumbnailHash != "" { - err := filestore.GetFileStore().DeleteFromFilestore(a.AllocationID, ref.PrevThumbnailHash, ref.FilestoreVersion) - if err != nil { - logging.Logger.Error(fmt.Sprintf("Error while deleting thumbnail file: %s", err.Error()), - zap.String("thumbnail_hash", ref.ThumbnailHash)) - } - } - err := filestore.GetFileStore().MoveToFilestore(a.AllocationID, ref.ThumbnailHash, ref.FilestoreVersion) - if err != nil { - logging.Logger.Error(fmt.Sprintf("Error while moving thumbnail file: %s", err.Error()), - zap.String("thumbnail_hash", ref.ThumbnailHash)) - } - } - - }(ref) - } - - return nil - }).Error - - wg.Wait() - + err = tx.Model(&reference.Ref{}).Select("lookup_hash").Where("allocation_id=? AND allocation_version=? AND type=?", a.AllocationID, allocationVersion, reference.FILE).Find(&refs).Error if err != nil { - logging.Logger.Error("Error while moving to filestore", zap.Error(err)) + logging.Logger.Error("Error while moving files to filestore", zap.Error(err)) return err } + } - return tx.Exec("UPDATE reference_objects SET is_precommit=?, prev_validation_root=validation_root, prev_thumbnail_hash=thumbnail_hash WHERE allocation_id=? AND is_precommit=? AND deleted_at is NULL", false, a.AllocationID, true).Error - }) - return err + for _, ref := range refs { + + limitCh <- struct{}{} + wg.Add(1) + refLookupHash := ref.LookupHash + go func() { + defer func() { + <-limitCh + wg.Done() + }() + err := filestore.GetFileStore().MoveToFilestore(a.AllocationID, refLookupHash, filestore.VERSION) + if err != nil { + logging.Logger.Error(fmt.Sprintf("Error while moving file: %s", err.Error())) + } + + }() + } + + wg.Wait() + return nil } -func deleteFromFileStore(ctx context.Context, allocationID string) error { - limitCh := make(chan struct{}, 10) +func deleteFromFileStore(allocationID string, deletedRefs []*reference.Ref, useRefCache bool) error { + limitCh := make(chan struct{}, 12) wg := &sync.WaitGroup{} - var results []Result + var results []*reference.Ref + if useRefCache { + results = deletedRefs + } return datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { db := datastore.GetStore().GetTransaction(ctx) + if !useRefCache { + err := db.Model(&reference.Ref{}).Unscoped().Select("lookup_hash"). + Where("allocation_id=? AND type=? AND deleted_at is not NULL", allocationID, reference.FILE). + Find(&results).Error + if err != nil && err != gorm.ErrRecordNotFound { + logging.Logger.Error("DeleteFromFileStore", zap.Error(err)) + return err + } + } - err := db.Model(&reference.Ref{}).Unscoped().Select("id", "validation_root", "thumbnail_hash", "filestore_version"). - Where("allocation_id=? AND is_precommit=? AND type=? AND deleted_at is not NULL", allocationID, true, reference.FILE). - FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { - - for _, res := range results { - var count int64 - tx.Model(&reference.Ref{}). - Where("allocation_id=? AND validation_root=?", allocationID, res.ValidationRoot). - Count(&count) - - if count != 0 && res.ThumbnailHash == "" { - continue - } - - limitCh <- struct{}{} - wg.Add(1) - - go func(res Result, count int64) { - defer func() { - <-limitCh - wg.Done() - }() - - if count == 0 { - err := filestore.GetFileStore().DeleteFromFilestore(allocationID, res.ValidationRoot, - res.FilestoreVersion) - if err != nil { - logging.Logger.Error(fmt.Sprintf("Error while deleting file: %s", err.Error()), - zap.String("validation_root", res.ValidationRoot)) - } - } - - if res.ThumbnailHash != "" { - err := filestore.GetFileStore().DeleteFromFilestore(allocationID, res.ThumbnailHash, res.FilestoreVersion) - if err != nil { - logging.Logger.Error(fmt.Sprintf("Error while deleting thumbnail: %s", err.Error()), - zap.String("thumbnail", res.ThumbnailHash)) - } - } - - }(res, count) - + for _, res := range results { + limitCh <- struct{}{} + wg.Add(1) + resLookupHash := res.LookupHash + go func() { + defer func() { + <-limitCh + wg.Done() + }() + + err := filestore.GetFileStore().DeleteFromFilestore(allocationID, resLookupHash, + filestore.VERSION) + if err != nil { + logging.Logger.Error(fmt.Sprintf("Error while deleting file: %s", err.Error()), + zap.String("lookup_hash", res.LookupHash)) } - return nil - }).Error + }() - wg.Wait() - if err != nil && err != gorm.ErrRecordNotFound { - logging.Logger.Error("DeleteFromFileStore", zap.Error(err)) - return err } + wg.Wait() return db.Model(&reference.Ref{}).Unscoped(). Delete(&reference.Ref{}, diff --git a/code/go/0chain.net/blobbercore/allocation/common_test.go b/code/go/0chain.net/blobbercore/allocation/common_test.go index 372cbaee0..9cb6429fb 100644 --- a/code/go/0chain.net/blobbercore/allocation/common_test.go +++ b/code/go/0chain.net/blobbercore/allocation/common_test.go @@ -21,11 +21,9 @@ func (mfs *MockFileStore) WriteFile(allocID, connID string, b := bytes.NewBuffer(make([]byte, 0)) n, _ := io.Copy(b, infile) return &filestore.FileOutputData{ - Name: fileData.Name, - Path: fileData.Path, - FixedMerkleRoot: "", - ValidationRoot: fileData.ValidationRoot, - Size: n, + Name: fileData.Name, + Path: fileData.Path, + Size: n, }, nil } diff --git a/code/go/0chain.net/blobbercore/allocation/connection.go b/code/go/0chain.net/blobbercore/allocation/connection.go index ef7eac62f..47e418712 100644 --- a/code/go/0chain.net/blobbercore/allocation/connection.go +++ b/code/go/0chain.net/blobbercore/allocation/connection.go @@ -68,10 +68,10 @@ func SaveFileChanger(connectionID string, fileChanger *BaseFileChanger) error { return common.NewError("connection_not_found", "connection not found") } connectionObj.lock.Lock() - if connectionObj.changes[fileChanger.PathHash] == nil { + if connectionObj.changes[fileChanger.LookupHash] == nil { return common.NewError("connection_change_not_found", "connection change not found") } - connectionObj.changes[fileChanger.PathHash].baseChanger = fileChanger + connectionObj.changes[fileChanger.LookupHash].baseChanger = fileChanger connectionObj.lock.Unlock() return nil } @@ -190,7 +190,7 @@ func SaveFileChange(ctx context.Context, connectionID, pathHash, fileName string if err != nil { return saveChange, err } - hasher := filestore.GetNewCommitHasher(contentSize) + hasher := filestore.NewCommitHasher(contentSize) change.hasher = hasher change.seqPQ = seqpriorityqueue.NewSeqPriorityQueue(contentSize) go hasher.Start(connectionObj.ctx, connectionID, connectionObj.AllocationID, fileName, pathHash, change.seqPQ) @@ -211,6 +211,12 @@ func SaveFileChange(ctx context.Context, connectionID, pathHash, fileName string DataBytes: dataWritten, }, contentSize) if addSize != 0 { + //check if reference exists and get the size + existingSize, err := reference.GetObjectSizeByLookupHash(ctx, pathHash) + if err != nil { + return saveChange, err + } + addSize -= existingSize UpdateConnectionObjSize(connectionID, addSize) } } else { @@ -240,6 +246,7 @@ func GetHasher(connectionID, pathHash string) *filestore.CommitHasher { // DeleteConnectionObjEntry remove the connectionID entry from map // If the given connectionID is not present, then it is no-op. func DeleteConnectionObjEntry(connectionID string) { + logging.Logger.Info("DeleteConnectionObjEntry", zap.String("connection_id", connectionID)) connectionObjMutex.Lock() connectionObj, ok := connectionProcessor[connectionID] if ok { @@ -256,6 +263,7 @@ func cleanConnectionObj() { for connectionID, connectionObj := range connectionProcessor { diff := time.Since(connectionObj.UpdatedAt) if diff >= ConnectionObjTimeout { + logging.Logger.Info("cleanConnectionObj", zap.String("connection_id", connectionID), zap.Duration("diff", diff)) // Stop the context and hash worker connectionObj.cnclCtx() for _, change := range connectionObj.changes { diff --git a/code/go/0chain.net/blobbercore/allocation/copyfilechange.go b/code/go/0chain.net/blobbercore/allocation/copyfilechange.go index 9f849df4e..f119bf33a 100644 --- a/code/go/0chain.net/blobbercore/allocation/copyfilechange.go +++ b/code/go/0chain.net/blobbercore/allocation/copyfilechange.go @@ -2,16 +2,17 @@ package allocation import ( "context" + "database/sql" "encoding/json" - "fmt" "path/filepath" "strings" "sync" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" - + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/encryption" ) type CopyFileChange struct { @@ -19,122 +20,77 @@ type CopyFileChange struct { AllocationID string `json:"allocation_id"` SrcPath string `json:"path"` DestPath string `json:"dest_path"` + Type string `json:"type"` + CustomMeta string `json:"custom_meta"` } func (rf *CopyFileChange) DeleteTempFile() error { return nil } -func (rf *CopyFileChange) ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) { - - totalRefs, err := reference.CountRefs(ctx, rf.AllocationID) - if err != nil { - return nil, err - } - - if int64(config.Configuration.MaxAllocationDirFiles) <= totalRefs { - return nil, common.NewErrorf("max_alloc_dir_files_reached", - "maximum files and directories already reached: %v", err) - } - - srcRef, err := rootRef.GetSrcPath(rf.SrcPath) - if err != nil { - return nil, err - } +func (rf *CopyFileChange) ApplyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { + srcLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.SrcPath) + destLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.DestPath) - rootRef.UpdatedAt = ts - rootRef.HashToBeComputed = true + var ( + srcRef *reference.Ref + err error + ) - dirRef := rootRef - fields, err := common.GetPathFields(rf.DestPath) - if err != nil { - return nil, err - } - - for i := 0; i < len(fields); i++ { - found := false - for _, child := range dirRef.Children { - if child.Name == fields[i] { - if child.Type == reference.DIRECTORY { - child.HashToBeComputed = true - dirRef = child - dirRef.UpdatedAt = ts - found = true - } else { - return nil, common.NewError("invalid_path", - fmt.Sprintf("%s is of file type", child.Path)) - } - } + err = datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + srcRef, err = reference.GetReferenceByLookupHash(ctx, rf.AllocationID, srcLookUpHash) + if err != nil { + return err } - - if len(dirRef.Children) >= config.Configuration.MaxObjectsInDir { - return nil, common.NewErrorf("max_objects_in_dir_reached", - "maximum objects in directory %s reached: %v", dirRef.Path, config.Configuration.MaxObjectsInDir) + exist, err := reference.IsRefExist(ctx, rf.AllocationID, rf.DestPath) + if err != nil { + return err + } + if exist { + return common.NewError("invalid_reference_path", "file already exists") } - if !found { - newRef := reference.NewDirectoryRef() - newRef.AllocationID = rf.AllocationID - newRef.Path = filepath.Join("/", strings.Join(fields[:i+1], "/")) - fileID, ok := fileIDMeta[newRef.Path] - if !ok || fileID == "" { - return nil, common.NewError("invalid_parameter", - fmt.Sprintf("file path %s has no entry in file ID meta", newRef.Path)) + rf.Type = srcRef.Type + if srcRef.Type == reference.DIRECTORY { + isEmpty, err := reference.IsDirectoryEmpty(ctx, srcRef.ID) + if err != nil { + return err + } + if !isEmpty { + return common.NewError("invalid_reference_path", "directory is not empty") } - newRef.FileID = fileID - newRef.ParentPath = filepath.Join("/", strings.Join(fields[:i], "/")) - newRef.Name = fields[i] - newRef.HashToBeComputed = true - newRef.CreatedAt = ts - newRef.UpdatedAt = ts - dirRef.AddChild(newRef) - dirRef = newRef } + return nil + }, &sql.TxOptions{ + ReadOnly: true, + }) + if err != nil { + return err } - _, err = rf.processCopyRefs(ctx, srcRef, dirRef, allocationRoot, ts, fileIDMeta) + parentDir, err := reference.Mkdir(ctx, rf.AllocationID, filepath.Dir(rf.DestPath), allocationVersion, ts, collector) if err != nil { - return nil, err + return err } - return rootRef, err -} - -func (rf *CopyFileChange) processCopyRefs( - ctx context.Context, srcRef, destRef *reference.Ref, - allocationRoot string, ts common.Timestamp, fileIDMeta map[string]string, -) ( - fileRefs []*reference.Ref, err error, -) { - - newRef := *srcRef - newRef.ID = 0 - newRef.Path = filepath.Join(destRef.Path, srcRef.Name) - fileID, ok := fileIDMeta[newRef.Path] - if !ok || fileID == "" { - return nil, common.NewError("invalid_parameter", - fmt.Sprintf("file path %s has no entry in fileID meta", newRef.Path)) - } - newRef.FileID = fileID - newRef.ParentPath = destRef.Path - newRef.CreatedAt = ts - newRef.UpdatedAt = ts - newRef.HashToBeComputed = true - destRef.AddChild(&newRef) - if newRef.Type == reference.DIRECTORY { - for _, childRef := range srcRef.Children { - fRefs, err := rf.processCopyRefs(ctx, childRef, &newRef, allocationRoot, ts, fileIDMeta) - if err != nil { - return nil, err - } - fileRefs = append(fileRefs, fRefs...) - } - } else { - fileRefs = append(fileRefs, &newRef) + srcRef.ID = 0 + srcRef.ParentID = &parentDir.ID + srcRef.Path = rf.DestPath + srcRef.LookupHash = destLookUpHash + srcRef.CreatedAt = ts + srcRef.UpdatedAt = ts + srcRef.ParentPath = filepath.Dir(rf.DestPath) + srcRef.Name = filepath.Base(rf.DestPath) + srcRef.PathLevel = len(strings.Split(strings.TrimRight(rf.DestPath, "/"), "/")) + srcRef.FileMetaHash = encryption.FastHash(srcRef.GetFileHashData()) + if rf.CustomMeta != "" { + srcRef.CustomMeta = rf.CustomMeta } + srcRef.AllocationVersion = allocationVersion + collector.CreateRefRecord(srcRef) - return + return nil } func (rf *CopyFileChange) Marshal() (string, error) { @@ -151,7 +107,12 @@ func (rf *CopyFileChange) Unmarshal(input string) error { } func (rf *CopyFileChange) CommitToFileStore(ctx context.Context, mut *sync.Mutex) error { - return nil + if rf.Type == reference.DIRECTORY { + return nil + } + srcLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.SrcPath) + destLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.DestPath) + return filestore.GetFileStore().CopyFile(rf.AllocationID, srcLookUpHash, destLookUpHash) } func (rf *CopyFileChange) GetPath() []string { diff --git a/code/go/0chain.net/blobbercore/allocation/copyfilechange_test.go b/code/go/0chain.net/blobbercore/allocation/copyfilechange_test.go index f0ab47414..121cad230 100644 --- a/code/go/0chain.net/blobbercore/allocation/copyfilechange_test.go +++ b/code/go/0chain.net/blobbercore/allocation/copyfilechange_test.go @@ -15,9 +15,9 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/constants" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/constants" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" mocket "github.com/selvatico/go-mocket" "go.uber.org/zap" "google.golang.org/grpc/metadata" @@ -38,7 +38,7 @@ func TestBlobberCore_CopyFile(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() testCases := []struct { name string diff --git a/code/go/0chain.net/blobbercore/allocation/dao.go b/code/go/0chain.net/blobbercore/allocation/dao.go index a62005e4f..6f67b1943 100644 --- a/code/go/0chain.net/blobbercore/allocation/dao.go +++ b/code/go/0chain.net/blobbercore/allocation/dao.go @@ -5,7 +5,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/errors" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "gorm.io/gorm" ) diff --git a/code/go/0chain.net/blobbercore/allocation/deletefilechange.go b/code/go/0chain.net/blobbercore/allocation/deletefilechange.go index 4caf0d1e3..18c4c7a31 100644 --- a/code/go/0chain.net/blobbercore/allocation/deletefilechange.go +++ b/code/go/0chain.net/blobbercore/allocation/deletefilechange.go @@ -3,7 +3,6 @@ package allocation import ( "context" "encoding/json" - "path/filepath" "sync" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" @@ -20,18 +19,15 @@ type DeleteFileChange struct { Name string `json:"name"` Path string `json:"path"` Size int64 `json:"size"` - Hash string `json:"hash"` + LookupHash string `json:"lookup_hash"` + Type string `json:"type"` } -func (nf *DeleteFileChange) ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, _ map[string]string) (*reference.Ref, error) { - - err := reference.DeleteObject(ctx, rootRef, nf.AllocationID, filepath.Clean(nf.Path), ts) - if err != nil { - return nil, err - } - - return nil, nil +func (nf *DeleteFileChange) ApplyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { + collector.LockTransaction() + defer collector.UnlockTransaction() + return reference.DeleteObject(ctx, nf.AllocationID, nf.LookupHash, nf.Type, ts, allocationVersion, collector) } func (nf *DeleteFileChange) Marshal() (string, error) { diff --git a/code/go/0chain.net/blobbercore/allocation/deletefilechange_test.go b/code/go/0chain.net/blobbercore/allocation/deletefilechange_test.go index b94dc9d7b..7d8ba36f8 100644 --- a/code/go/0chain.net/blobbercore/allocation/deletefilechange_test.go +++ b/code/go/0chain.net/blobbercore/allocation/deletefilechange_test.go @@ -13,8 +13,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" mocket "github.com/selvatico/go-mocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,7 +32,7 @@ func TestBlobberCore_DeleteFile(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() testCases := []struct { name string context metadata.MD diff --git a/code/go/0chain.net/blobbercore/allocation/entity.go b/code/go/0chain.net/blobbercore/allocation/entity.go index 98e7db8c7..fdb19dadf 100644 --- a/code/go/0chain.net/blobbercore/allocation/entity.go +++ b/code/go/0chain.net/blobbercore/allocation/entity.go @@ -44,23 +44,26 @@ type Allocation struct { Tx string `gorm:"column:tx;size:64;not null;unique;index:idx_unique_allocations_tx,unique"` TotalSize int64 `gorm:"column:size;not null;default:0"` UsedSize int64 `gorm:"column:used_size;not null;default:0"` + PrevUsedSize int64 `gorm:"column:prev_used_size;not null;default:0"` OwnerID string `gorm:"column:owner_id;size:64;not null"` OwnerPublicKey string `gorm:"column:owner_public_key;size:512;not null"` RepairerID string `gorm:"column:repairer_id;size:64;not null"` Expiration common.Timestamp `gorm:"column:expiration_date;not null"` // AllocationRoot allcation_root of last write_marker - AllocationRoot string `gorm:"column:allocation_root;size:64;not null;default:''"` - FileMetaRoot string `gorm:"column:file_meta_root;size:64;not null;default:''"` - BlobberSize int64 `gorm:"column:blobber_size;not null;default:0"` - BlobberSizeUsed int64 `gorm:"column:blobber_size_used;not null;default:0"` - LatestRedeemedWM string `gorm:"column:latest_redeemed_write_marker;size:64"` - LastRedeemedSeq int64 `gorm:"column:last_redeemed_sequence;default:0"` - IsRedeemRequired bool `gorm:"column:is_redeem_required"` - TimeUnit time.Duration `gorm:"column:time_unit;not null;default:172800000000000"` - StartTime common.Timestamp `gorm:"column:start_time;not null"` + AllocationRoot string `gorm:"column:allocation_root;size:64;not null;default:''"` + FileMetaRoot string `gorm:"column:file_meta_root;size:64;not null;default:''"` + BlobberSize int64 `gorm:"column:blobber_size;not null;default:0"` + BlobberSizeUsed int64 `gorm:"column:blobber_size_used;not null;default:0"` + PrevBlobberSizeUsed int64 `gorm:"column:prev_blobber_size_used;not null;default:0"` + LatestRedeemedWM string `gorm:"column:latest_redeemed_write_marker;size:64"` + LastRedeemedSeq int64 `gorm:"column:last_redeemed_sequence;default:0"` + IsRedeemRequired bool `gorm:"column:is_redeem_required"` + TimeUnit time.Duration `gorm:"column:time_unit;not null;default:172800000000000"` + StartTime common.Timestamp `gorm:"column:start_time;not null"` // Ending and cleaning - CleanedUp bool `gorm:"column:cleaned_up;not null;default:false"` - Finalized bool `gorm:"column:finalized;not null;default:false"` + CleanedUp bool `gorm:"column:cleaned_up;not null;default:false"` + Finalized bool `gorm:"column:finalized;not null;default:false"` + AllocationVersion int64 `gorm:"column:allocation_version;not null;default:0"` // FileOptions to define file restrictions on an allocation for third-parties // default 00000000 for all crud operations suggesting only owner has the below listed abilities. diff --git a/code/go/0chain.net/blobbercore/allocation/file_changer_base.go b/code/go/0chain.net/blobbercore/allocation/file_changer_base.go index 9c93a5bb2..f89725e14 100644 --- a/code/go/0chain.net/blobbercore/allocation/file_changer_base.go +++ b/code/go/0chain.net/blobbercore/allocation/file_changer_base.go @@ -8,10 +8,11 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/blobber/code/go/0chain.net/core/encryption" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" + "go.uber.org/zap" ) -// swagger:model BaseFileChanger +// swagger:model BaseFileChanger // BaseFileChanger base file change processor type BaseFileChanger struct { //client side: unmarshal them from 'updateMeta'/'uploadMeta' @@ -34,16 +35,12 @@ type BaseFileChanger struct { //client side: MimeType string `json:"mimetype,omitempty"` //client side: - //client side: - FixedMerkleRoot string `json:"fixed_merkle_root,omitempty"` //server side: update them by ChangeProcessor - AllocationID string `json:"allocation_id"` - //client side: - ValidationRootSignature string `json:"validation_root_signature,omitempty"` - //client side: - ValidationRoot string `json:"validation_root,omitempty"` - Size int64 `json:"size"` + AllocationID string `json:"allocation_id"` + DataHash string `json:"data_hash"` + DataHashSignature string `json:"data_hash_signature"` + Size int64 `json:"size"` //server side: ThumbnailHash string `json:"thumbnail_content_hash,omitempty"` ThumbnailSize int64 `json:"thumbnail_size"` @@ -60,16 +57,15 @@ type BaseFileChanger struct { ChunkEndIndex int `json:"chunk_end_index,omitempty"` // end index of chunks. all chunks MUST be uploaded one by one because of CompactMerkleTree ChunkHash string `json:"chunk_hash,omitempty"` UploadOffset int64 `json:"upload_offset,omitempty"` // It is next position that new incoming chunk should be append to - PathHash string `json:"-"` // hash of path + CanUpdate bool `json:"can_update"` // can file be updated or not + LookupHash string `json:"-"` // hash of allocationID+path } // swagger:model UploadResult type UploadResult struct { - Filename string `json:"filename"` - Size int64 `json:"size"` - Hash string `json:"hash"` - ValidationRoot string `json:"validation_root"` - FixedMerkleRoot string `json:"fixed_merkle_root"` + Filename string `json:"filename"` + Size int64 `json:"size"` + Hash string `json:"hash"` // UploadLength indicates the size of the entire upload in bytes. The value MUST be a non-negative integer. UploadLength int64 `json:"upload_length"` @@ -108,7 +104,8 @@ func (fc *BaseFileChanger) DeleteTempFile() error { fileInputData := &filestore.FileInputData{} fileInputData.Name = fc.Filename fileInputData.Path = fc.Path - fileInputData.ValidationRoot = fc.ValidationRoot + fileInputData.DataHash = fc.DataHash + fileInputData.LookupHash = fc.LookupHash err := filestore.GetFileStore().DeleteTempFile(fc.AllocationID, fc.ConnectionID, fileInputData) if fc.ThumbnailSize > 0 { fileInputData := &filestore.FileInputData{} @@ -122,11 +119,15 @@ func (fc *BaseFileChanger) DeleteTempFile() error { func (fc *BaseFileChanger) CommitToFileStore(ctx context.Context, mut *sync.Mutex) error { + if fc.LookupHash == "" { + fc.LookupHash = reference.GetReferenceLookup(fc.AllocationID, fc.Path) + } if fc.ThumbnailSize > 0 { fileInputData := &filestore.FileInputData{} fileInputData.Name = fc.ThumbnailFilename fileInputData.Path = fc.Path fileInputData.ThumbnailHash = fc.ThumbnailHash + fileInputData.LookupHash = fc.LookupHash fileInputData.ChunkSize = fc.ChunkSize fileInputData.IsThumbnail = true _, err := filestore.GetFileStore().CommitWrite(fc.AllocationID, fc.ConnectionID, fileInputData) @@ -137,13 +138,14 @@ func (fc *BaseFileChanger) CommitToFileStore(ctx context.Context, mut *sync.Mute fileInputData := &filestore.FileInputData{} fileInputData.Name = fc.Filename fileInputData.Path = fc.Path - fileInputData.ValidationRoot = fc.ValidationRoot - fileInputData.FixedMerkleRoot = fc.FixedMerkleRoot + fileInputData.DataHash = fc.DataHash + fileInputData.LookupHash = fc.LookupHash fileInputData.ChunkSize = fc.ChunkSize fileInputData.Size = fc.Size - fileInputData.Hasher = GetHasher(fc.ConnectionID, encryption.Hash(fc.Path)) + fileInputData.Hasher = GetHasher(fc.ConnectionID, fc.LookupHash) if fileInputData.Hasher == nil { - return common.NewError("invalid_parameters", "Invalid parameters. Error getting hasher for commit.") + logging.Logger.Error("CommitToFileStore: Error getting hasher", zap.String("connection_id", fc.ConnectionID), zap.String("lookup_hash", fc.LookupHash)) + return common.NewError("file_store_error", "Error getting hasher") } _, err := filestore.GetFileStore().CommitWrite(fc.AllocationID, fc.ConnectionID, fileInputData) if err != nil { diff --git a/code/go/0chain.net/blobbercore/allocation/file_changer_update.go b/code/go/0chain.net/blobbercore/allocation/file_changer_update.go index e1e1282db..b8faf2677 100644 --- a/code/go/0chain.net/blobbercore/allocation/file_changer_update.go +++ b/code/go/0chain.net/blobbercore/allocation/file_changer_update.go @@ -74,19 +74,14 @@ func (nf *UpdateFileChanger) ApplyChange(ctx context.Context, rootRef *reference fileRef.HashToBeComputed = true nf.deleteHash = make(map[string]int) - if fileRef.ValidationRoot != "" && fileRef.ValidationRoot != nf.ValidationRoot { - nf.deleteHash[fileRef.ValidationRoot] = fileRef.FilestoreVersion - } - fileRef.ActualFileHash = nf.ActualHash fileRef.ActualFileHashSignature = nf.ActualFileHashSignature fileRef.ActualFileSize = nf.ActualSize fileRef.MimeType = nf.MimeType - fileRef.ValidationRootSignature = nf.ValidationRootSignature - fileRef.ValidationRoot = nf.ValidationRoot fileRef.CustomMeta = nf.CustomMeta - fileRef.FixedMerkleRoot = nf.FixedMerkleRoot - fileRef.AllocationRoot = allocationRoot + fileRef.DataHash = nf.DataHash + fileRef.DataHashSignature = nf.DataHashSignature + fileRef.LookupHash = nf.LookupHash fileRef.Size = nf.Size fileRef.ThumbnailHash = nf.ThumbnailHash fileRef.ThumbnailSize = nf.ThumbnailSize @@ -95,7 +90,6 @@ func (nf *UpdateFileChanger) ApplyChange(ctx context.Context, rootRef *reference fileRef.EncryptedKey = nf.EncryptedKey fileRef.EncryptedKeyPoint = nf.EncryptedKeyPoint fileRef.ChunkSize = nf.ChunkSize - fileRef.IsPrecommit = true fileRef.FilestoreVersion = filestore.VERSION return rootRef, nil diff --git a/code/go/0chain.net/blobbercore/allocation/file_changer_upload.go b/code/go/0chain.net/blobbercore/allocation/file_changer_upload.go index 3ad14d9db..50e11b0f4 100644 --- a/code/go/0chain.net/blobbercore/allocation/file_changer_upload.go +++ b/code/go/0chain.net/blobbercore/allocation/file_changer_upload.go @@ -2,18 +2,24 @@ package allocation import ( "context" + "database/sql" "encoding/json" - "fmt" + "math" "path/filepath" "strings" + "time" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" + "go.uber.org/zap" + "gorm.io/gorm" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/util" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/encryption" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" ) // swagger:model UploadFileChanger @@ -23,93 +29,27 @@ type UploadFileChanger struct { } // ApplyChange update references, and create a new FileRef -func (nf *UploadFileChanger) applyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) { +func (nf *UploadFileChanger) applyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { - totalRefs, err := reference.CountRefs(ctx, nf.AllocationID) - if err != nil { - return nil, err - } - - if int64(config.Configuration.MaxAllocationDirFiles) <= totalRefs { - return nil, common.NewErrorf("max_alloc_dir_files_reached", - "maximum files and directories already reached: %v", err) - } - - fields, err := common.GetPathFields(filepath.Dir(nf.Path)) - if err != nil { - return nil, err - } - if rootRef.CreatedAt == 0 { - rootRef.CreatedAt = ts - } - - rootRef.UpdatedAt = ts - rootRef.HashToBeComputed = true - - dirRef := rootRef - for i := 0; i < len(fields); i++ { - found := false - for _, child := range dirRef.Children { - if child.Name == fields[i] { - if child.Type != reference.DIRECTORY { - return nil, common.NewError("invalid_reference_path", "Reference path has invalid ref type") - } - dirRef = child - dirRef.UpdatedAt = ts - dirRef.HashToBeComputed = true - found = true - } - } - - if len(dirRef.Children) >= config.Configuration.MaxObjectsInDir { - return nil, common.NewErrorf("max_objects_in_dir_reached", - "maximum objects in directory %s reached: %v", dirRef.Path, config.Configuration.MaxObjectsInDir) - } - - if !found { - newRef := reference.NewDirectoryRef() - newRef.AllocationID = dirRef.AllocationID - newRef.Path = "/" + strings.Join(fields[:i+1], "/") - fileID, ok := fileIDMeta[newRef.Path] - if !ok || fileID == "" { - return nil, common.NewError("invalid_parameter", - fmt.Sprintf("file path %s has no entry in fileID meta", newRef.Path)) - } - newRef.FileID = fileID - newRef.ParentPath = "/" + strings.Join(fields[:i], "/") - newRef.Name = fields[i] - newRef.CreatedAt = ts - newRef.UpdatedAt = ts - newRef.HashToBeComputed = true - - dirRef.AddChild(newRef) - dirRef = newRef - } - } - - for _, child := range dirRef.Children { - if child.Name == nf.Filename { - return nil, common.NewError("duplicate_file", "File already exists") - } + if nf.AllocationID == "" { + return common.NewError("invalid_parameter", "allocation_id is required") } - + now := time.Now() + parentPath := filepath.Dir(nf.Path) + nf.LookupHash = reference.GetReferenceLookup(nf.AllocationID, nf.Path) newFile := &reference.Ref{ ActualFileHash: nf.ActualHash, ActualFileHashSignature: nf.ActualFileHashSignature, ActualFileSize: nf.ActualSize, - AllocationID: dirRef.AllocationID, - ValidationRoot: nf.ValidationRoot, - ValidationRootSignature: nf.ValidationRootSignature, + AllocationID: nf.AllocationID, CustomMeta: nf.CustomMeta, - FixedMerkleRoot: nf.FixedMerkleRoot, Name: nf.Filename, Path: nf.Path, - ParentPath: dirRef.Path, + ParentPath: parentPath, Type: reference.FILE, Size: nf.Size, MimeType: nf.MimeType, - AllocationRoot: allocationRoot, ThumbnailHash: nf.ThumbnailHash, ThumbnailSize: nf.ThumbnailSize, ActualThumbnailHash: nf.ActualThumbnailHash, @@ -119,21 +59,63 @@ func (nf *UploadFileChanger) applyChange(ctx context.Context, rootRef *reference ChunkSize: nf.ChunkSize, CreatedAt: ts, UpdatedAt: ts, - HashToBeComputed: true, - IsPrecommit: true, + LookupHash: nf.LookupHash, + DataHash: nf.DataHash, + DataHashSignature: nf.DataHashSignature, + PathLevel: len(strings.Split(strings.TrimRight(nf.Path, "/"), "/")), + NumBlocks: int64(math.Ceil(float64(nf.Size*1.0) / float64(nf.ChunkSize))), FilestoreVersion: filestore.VERSION, + AllocationVersion: allocationVersion, + NumUpdates: 1, } - - fileID, ok := fileIDMeta[newFile.Path] - if !ok || fileID == "" { - return nil, common.NewError("invalid_parameter", - fmt.Sprintf("file path %s has no entry in fileID meta", newFile.Path)) + newFile.FileMetaHash = encryption.FastHash(newFile.GetFileMetaHashData()) + elapsedNewFile := time.Since(now) + // find if ref exists + var refResult struct { + ID int64 + Type string + NumUpdates int64 `gorm:"column:num_of_updates" json:"num_of_updates"` } - newFile.FileID = fileID - dirRef.AddChild(newFile) + err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + tx := datastore.GetStore().GetTransaction(ctx) + return tx.Model(&reference.Ref{}).Select("id", "type", "num_of_updates").Where("lookup_hash = ?", newFile.LookupHash).Take(&refResult).Error + }, &sql.TxOptions{ + ReadOnly: true, + }) + if err != nil && err != gorm.ErrRecordNotFound { + return err + } - return rootRef, nil + if refResult.ID > 0 { + if !nf.CanUpdate { + return common.NewError("prohibited_allocation_file_options", "Cannot update data in this allocation.") + } + if refResult.Type != reference.FILE { + return common.NewError("invalid_reference_path", "Directory already exists with the same path") + } + deleteRecord := &reference.Ref{ + ID: refResult.ID, + LookupHash: newFile.LookupHash, + Type: refResult.Type, + } + collector.DeleteRefRecord(deleteRecord) + newFile.NumUpdates = refResult.NumUpdates + 1 + } + elapsedNewFileRecord := time.Since(now) - elapsedNewFile + // get parent id + parent := filepath.Dir(nf.Path) + // create or get parent directory + parentRef, err := reference.Mkdir(ctx, nf.AllocationID, parent, allocationVersion, ts, collector) + if err != nil { + return err + } + elapsedMkdir := time.Since(now) - elapsedNewFileRecord - elapsedNewFile + newFile.ParentID = &parentRef.ID + collector.CreateRefRecord(newFile) + elapsedCreateRefRecord := time.Since(now) - elapsedMkdir - elapsedNewFileRecord - elapsedNewFile + logging.Logger.Info("UploadFileChanger", zap.Duration("elapsedNewFile", elapsedNewFile), zap.Duration("elapsedNewFileRecord", elapsedNewFileRecord), zap.Duration("elapsedMkdir", elapsedMkdir), zap.Duration("elapsedCreateRefRecord", elapsedCreateRefRecord), zap.Duration("elapsedTotal", time.Since(now))) + return err } // Marshal marshal and change to persistent to postgres diff --git a/code/go/0chain.net/blobbercore/allocation/file_changer_upload_main.go b/code/go/0chain.net/blobbercore/allocation/file_changer_upload_main.go index 5a41a40aa..7cb2fea21 100644 --- a/code/go/0chain.net/blobbercore/allocation/file_changer_upload_main.go +++ b/code/go/0chain.net/blobbercore/allocation/file_changer_upload_main.go @@ -5,11 +5,12 @@ package allocation import ( "context" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" ) -func (nf *UploadFileChanger) ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) { - return nf.applyChange(ctx, rootRef, change, allocationRoot, ts, fileIDMeta) +func (nf *UploadFileChanger) ApplyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { + return nf.applyChange(ctx, ts, allocationVersion, collector) } diff --git a/code/go/0chain.net/blobbercore/allocation/file_changer_upload_test.go b/code/go/0chain.net/blobbercore/allocation/file_changer_upload_test.go index 4ea4e3bce..f1f8964c1 100644 --- a/code/go/0chain.net/blobbercore/allocation/file_changer_upload_test.go +++ b/code/go/0chain.net/blobbercore/allocation/file_changer_upload_test.go @@ -17,9 +17,9 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/constants" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/constants" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" mocket "github.com/selvatico/go-mocket" "go.uber.org/zap" "google.golang.org/grpc/metadata" @@ -39,7 +39,7 @@ func TestBlobberCore_FileChangerUpload(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() testCases := []struct { name string @@ -106,7 +106,7 @@ func TestBlobberCore_FileChangerUpload(t *testing.T) { changes: make(map[string]*ConnectionChange), } connectionProcessor["connection_id"].changes[pathHash] = &ConnectionChange{ - hasher: filestore.GetNewCommitHasher(2310), + hasher: filestore.NewCommitHasher(2310), } change := &UploadFileChanger{ BaseFileChanger: BaseFileChanger{ diff --git a/code/go/0chain.net/blobbercore/allocation/movefilechange.go b/code/go/0chain.net/blobbercore/allocation/movefilechange.go index 7d3aa9c3e..52d587029 100644 --- a/code/go/0chain.net/blobbercore/allocation/movefilechange.go +++ b/code/go/0chain.net/blobbercore/allocation/movefilechange.go @@ -2,15 +2,17 @@ package allocation import ( "context" + "database/sql" "encoding/json" - "fmt" "path/filepath" "strings" "sync" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/encryption" ) type MoveFileChange struct { @@ -18,140 +20,74 @@ type MoveFileChange struct { AllocationID string `json:"allocation_id"` SrcPath string `json:"path"` DestPath string `json:"dest_path"` + Type string `json:"type"` } func (rf *MoveFileChange) DeleteTempFile() error { return nil } -func (rf *MoveFileChange) ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) { - - srcRef, err := rootRef.GetSrcPath(rf.SrcPath) - if err != nil { - return nil, err - } - - rootRef.UpdatedAt = ts - rootRef.HashToBeComputed = true - - srcParentPath, srcFileName := filepath.Split(rf.SrcPath) - srcFields, err := common.GetPathFields(srcParentPath) - if err != nil { - return nil, err - } - dirRef := rootRef - for i := 0; i < len(srcFields); i++ { - found := false - for _, child := range dirRef.Children { - if child.Name == srcFields[i] { - dirRef = child - found = true - dirRef.HashToBeComputed = true - break - } +func (rf *MoveFileChange) ApplyChange(cctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { + var err error + srcLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.SrcPath) + destLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.DestPath) + var srcRef *reference.Ref + err = datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + srcRef, err = reference.GetReferenceByLookupHash(ctx, rf.AllocationID, srcLookUpHash) + if err != nil { + return err } - if !found { - return nil, common.NewError("invalid_reference_path", - fmt.Sprintf("path %s does not exist", strings.Join(srcFields[:i+1], "/"))) + exist, err := reference.IsRefExist(ctx, rf.AllocationID, rf.DestPath) + if err != nil { + return err } - } - - var removed bool - for i, child := range dirRef.Children { - if child.Name == srcFileName { - dirRef.RemoveChild(i) - removed = true - break + if exist { + return common.NewError("invalid_reference_path", "file already exists") } - } - if !removed { - return nil, common.NewError("incomplete_move", - "move operation rejected as it cannot be completed") - } - - dirRef = rootRef - fields, err := common.GetPathFields(rf.DestPath) - if err != nil { - return nil, err - } - - for i := 0; i < len(fields); i++ { - found := false - for _, child := range dirRef.Children { - if child.Name == fields[i] { - if child.Type == reference.DIRECTORY { - child.HashToBeComputed = true - dirRef = child - dirRef.UpdatedAt = ts - found = true - } else { - return nil, common.NewError("invalid_path", - fmt.Sprintf("%s is of file type", child.Path)) - } + if srcRef.Type == reference.DIRECTORY { + isEmpty, err := reference.IsDirectoryEmpty(cctx, srcRef.ID) + if err != nil { + return err } - } - - if len(dirRef.Children) >= config.Configuration.MaxObjectsInDir { - return nil, common.NewErrorf("max_objects_in_dir_reached", - "maximum objects in directory %s reached: %v", dirRef.Path, config.Configuration.MaxObjectsInDir) - } - - if !found { - newRef := reference.NewDirectoryRef() - newRef.AllocationID = rf.AllocationID - newRef.Path = filepath.Join("/", strings.Join(fields[:i+1], "/")) - newRef.ParentPath = filepath.Join("/", strings.Join(fields[:i], "/")) - newRef.Name = fields[i] - newRef.HashToBeComputed = true - newRef.CreatedAt = ts - newRef.UpdatedAt = ts - fileID, ok := fileIDMeta[newRef.Path] - if !ok || fileID == "" { - return nil, common.NewError("invalid_parameter", - fmt.Sprintf("file path %s has no entry in fileID meta", newRef.Path)) + if !isEmpty { + return common.NewError("invalid_reference_path", "directory is not empty") } - newRef.FileID = fileID - dirRef.AddChild(newRef) - dirRef = newRef } + return nil + }, &sql.TxOptions{ + ReadOnly: true, + }) + if err != nil { + return err } - fileRefs := rf.processMoveRefs(ctx, srcRef, dirRef, allocationRoot, ts, true) - - for _, fileRef := range fileRefs { - fileRef.IsPrecommit = true + rf.Type = srcRef.Type + parentDir, err := reference.Mkdir(cctx, rf.AllocationID, filepath.Dir(rf.DestPath), allocationVersion, ts, collector) + if err != nil { + return err } - return rootRef, nil -} -func (rf *MoveFileChange) processMoveRefs( - ctx context.Context, srcRef, destRef *reference.Ref, - allocationRoot string, ts common.Timestamp, toAdd bool) (fileRefs []*reference.Ref) { - if srcRef.Type == reference.DIRECTORY { - srcRef.Path = filepath.Join(destRef.Path, srcRef.Name) - srcRef.ParentPath = destRef.Path - srcRef.UpdatedAt = ts - srcRef.HashToBeComputed = true - if toAdd { - destRef.AddChild(srcRef) - } - - for _, childRef := range srcRef.Children { - fileRefs = append(fileRefs, rf.processMoveRefs(ctx, childRef, srcRef, allocationRoot, ts, false)...) - } - } else if srcRef.Type == reference.FILE { - srcRef.ParentPath = destRef.Path - srcRef.Path = filepath.Join(destRef.Path, srcRef.Name) - srcRef.UpdatedAt = ts - srcRef.HashToBeComputed = true - if toAdd { - destRef.AddChild(srcRef) - } - fileRefs = append(fileRefs, srcRef) + deleteRef := &reference.Ref{ + ID: srcRef.ID, + LookupHash: srcLookUpHash, + Type: srcRef.Type, } + collector.DeleteRefRecord(deleteRef) + + srcRef.ID = 0 + srcRef.ParentID = &parentDir.ID + srcRef.Path = rf.DestPath + srcRef.ParentPath = filepath.Dir(rf.DestPath) + srcRef.Name = filepath.Base(rf.DestPath) + srcRef.LookupHash = destLookUpHash + srcRef.CreatedAt = ts + srcRef.UpdatedAt = ts + srcRef.PathLevel = len(strings.Split(strings.TrimRight(rf.DestPath, "/"), "/")) + srcRef.FileMetaHash = encryption.FastHash(srcRef.GetFileHashData()) + srcRef.AllocationVersion = allocationVersion + collector.CreateRefRecord(srcRef) - return - + return nil } func (rf *MoveFileChange) Marshal() (string, error) { @@ -168,7 +104,12 @@ func (rf *MoveFileChange) Unmarshal(input string) error { } func (rf *MoveFileChange) CommitToFileStore(ctx context.Context, mut *sync.Mutex) error { - return nil + if rf.Type == reference.DIRECTORY { + return nil + } + srcLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.SrcPath) + destLookUpHash := reference.GetReferenceLookup(rf.AllocationID, rf.DestPath) + return filestore.GetFileStore().CopyFile(rf.AllocationID, srcLookUpHash, destLookUpHash) } func (rf *MoveFileChange) GetPath() []string { diff --git a/code/go/0chain.net/blobbercore/allocation/movefilechange_test.go b/code/go/0chain.net/blobbercore/allocation/movefilechange_test.go index befa83566..0e89ec582 100644 --- a/code/go/0chain.net/blobbercore/allocation/movefilechange_test.go +++ b/code/go/0chain.net/blobbercore/allocation/movefilechange_test.go @@ -16,8 +16,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" "go.uber.org/zap" "google.golang.org/grpc/metadata" ) @@ -37,7 +37,7 @@ func TestBlobberCore_MoveFile(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() testCases := []struct { name string diff --git a/code/go/0chain.net/blobbercore/allocation/multiop_test.go b/code/go/0chain.net/blobbercore/allocation/multiop_test.go index 917cd9030..8cde60fc5 100644 --- a/code/go/0chain.net/blobbercore/allocation/multiop_test.go +++ b/code/go/0chain.net/blobbercore/allocation/multiop_test.go @@ -13,8 +13,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" mocket "github.com/selvatico/go-mocket" "github.com/stretchr/testify/require" ) @@ -30,7 +30,7 @@ func TestMultiOp(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() datastore.MocketTheStore(t, true) ctx := datastore.GetStore().CreateTransaction(context.TODO()) setupDbMock() diff --git a/code/go/0chain.net/blobbercore/allocation/newdirchange.go b/code/go/0chain.net/blobbercore/allocation/newdirchange.go index 48f434b71..e5bf4051e 100644 --- a/code/go/0chain.net/blobbercore/allocation/newdirchange.go +++ b/code/go/0chain.net/blobbercore/allocation/newdirchange.go @@ -3,16 +3,17 @@ package allocation import ( "context" "encoding/json" - "fmt" "path/filepath" "strings" "sync" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/util" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/encryption" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" + "go.uber.org/zap" + "gorm.io/gorm" ) type NewDir struct { @@ -20,72 +21,58 @@ type NewDir struct { Path string `json:"filepath" validation:"required"` AllocationID string `json:"allocation_id"` CustomMeta string `json:"custom_meta,omitempty"` + MimeType string `json:"mimetype,omitempty"` } -func (nf *NewDir) ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, fileIDMeta map[string]string) (*reference.Ref, error) { - - totalRefs, err := reference.CountRefs(ctx, nf.AllocationID) - if err != nil { - return nil, err - } - - if int64(config.Configuration.MaxAllocationDirFiles) <= totalRefs { - return nil, common.NewErrorf("max_alloc_dir_files_reached", - "maximum files and directories already reached: %v", err) - } - - err = nf.Unmarshal(change.Input) - if err != nil { - return nil, err - } - - if rootRef.CreatedAt == 0 { - rootRef.CreatedAt = ts - } - rootRef.UpdatedAt = ts - rootRef.HashToBeComputed = true - fields, err := common.GetPathFields(nf.Path) - if err != nil { - return nil, err +func (nf *NewDir) ApplyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { + parentPath := filepath.Dir(nf.Path) + parentPathLookup := reference.GetReferenceLookup(nf.AllocationID, parentPath) + parentRef, err := reference.GetFullReferenceByLookupHashWithNewTransaction(parentPathLookup) + if err != nil && err != gorm.ErrRecordNotFound { + return err } - dirRef := rootRef - for i := 0; i < len(fields); i++ { - found := false - for _, child := range dirRef.Children { - if child.Name == fields[i] { - dirRef = child - dirRef.HashToBeComputed = true - dirRef.UpdatedAt = ts - found = true - break - } + if parentRef == nil || parentRef.ID == 0 { + _, err = reference.Mkdir(ctx, nf.AllocationID, nf.Path, allocationVersion, ts, collector) + } else { + collector.LockTransaction() + defer collector.UnlockTransaction() + dirLookupHash := reference.GetReferenceLookup(nf.AllocationID, nf.Path) + dRef, err := reference.GetLimitedRefFieldsByLookupHash(ctx, nf.AllocationID, dirLookupHash, []string{"id"}) + if err != nil && err != gorm.ErrRecordNotFound { + logging.Logger.Error("ApplyChange:Newdir", zap.Error(err)) + return err } - - if !found { - newRef := reference.NewDirectoryRef() - newRef.AllocationID = nf.AllocationID - newRef.Path = filepath.Join("/", strings.Join(fields[:i+1], "/")) - newRef.PathLevel = len(fields) + 1 - newRef.ParentPath = filepath.Dir(newRef.Path) - newRef.Name = fields[i] - newRef.LookupHash = reference.GetReferenceLookup(nf.AllocationID, newRef.Path) - newRef.CreatedAt = ts - newRef.UpdatedAt = ts - newRef.HashToBeComputed = true - fileID, ok := fileIDMeta[newRef.Path] - if !ok || fileID == "" { - return nil, common.NewError("invalid_parameter", - fmt.Sprintf("file path %s has no entry in fileID meta", newRef.Path)) - } - newRef.FileID = fileID + err = nil + // already exists + if dRef != nil && dRef.ID != 0 { + return nil + } + parentIDRef := &parentRef.ID + newRef := reference.NewDirectoryRef() + newRef.AllocationID = nf.AllocationID + newRef.Path = nf.Path + if newRef.Path != "/" { + newRef.ParentPath = parentPath + } + newRef.Name = filepath.Base(nf.Path) + newRef.PathLevel = len(strings.Split(strings.TrimRight(nf.Path, "/"), "/")) + newRef.ParentID = parentIDRef + newRef.LookupHash = dirLookupHash + newRef.CreatedAt = ts + newRef.UpdatedAt = ts + newRef.FileMetaHash = encryption.FastHash(newRef.GetFileMetaHashData()) + if nf.CustomMeta != "" { newRef.CustomMeta = nf.CustomMeta - dirRef.AddChild(newRef) - dirRef = newRef } + if nf.MimeType != "" { + newRef.MimeType = nf.MimeType + } + newRef.AllocationVersion = allocationVersion + collector.CreateRefRecord(newRef) } - return rootRef, nil + return err } func (nd *NewDir) Marshal() (string, error) { diff --git a/code/go/0chain.net/blobbercore/allocation/renamefilechange.go b/code/go/0chain.net/blobbercore/allocation/renamefilechange.go index fbcee3759..774027eee 100644 --- a/code/go/0chain.net/blobbercore/allocation/renamefilechange.go +++ b/code/go/0chain.net/blobbercore/allocation/renamefilechange.go @@ -6,31 +6,37 @@ import ( "path/filepath" "sync" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "go.uber.org/zap" ) type RenameFileChange struct { - ConnectionID string `json:"connection_id"` - AllocationID string `json:"allocation_id"` - Path string `json:"path"` - NewName string `json:"new_name"` - Name string `json:"name"` - Type string `json:"type"` + ConnectionID string `json:"connection_id"` + AllocationID string `json:"allocation_id"` + Path string `json:"path"` + NewName string `json:"new_name"` + Name string `json:"name"` + Type string `json:"type"` + CustomMeta string `json:"custom_meta"` + MimeType string `json:"mimetype"` + newLookupHash string `json:"-"` } func (rf *RenameFileChange) DeleteTempFile() error { return nil } -func (rf *RenameFileChange) applyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, _ map[string]string) (*reference.Ref, error) { - +func (rf *RenameFileChange) applyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { + collector.LockTransaction() + defer collector.UnlockTransaction() if rf.Path == "/" { - return nil, common.NewError("invalid_operation", "cannot rename root path") + return common.NewError("invalid_operation", "cannot rename root path") } newPath := filepath.Join(filepath.Dir(rf.Path), rf.NewName) @@ -40,79 +46,45 @@ func (rf *RenameFileChange) applyChange(ctx context.Context, rootRef *reference. } if isFilePresent { - return nil, common.NewError("invalid_reference_path", "file already exists") - } - - affectedRef, err := rootRef.GetSrcPath(rf.Path) - if err != nil { - return nil, err - } - affectedRef.HashToBeComputed = true - affectedRef.Name = rf.NewName - affectedRef.Path = newPath - affectedRef.UpdatedAt = ts - if affectedRef.Type == reference.FILE { - affectedRef.IsPrecommit = true - } else { - rf.processChildren(ctx, affectedRef, ts) + return common.NewError("invalid_reference_path", "file already exists") } - - parentPath := filepath.Dir(rf.Path) - fields, err := common.GetPathFields(parentPath) + oldFileLookupHash := reference.GetReferenceLookup(rf.AllocationID, rf.Path) + ref, err := reference.GetReferenceByLookupHash(ctx, rf.AllocationID, oldFileLookupHash) if err != nil { - return nil, err + return common.NewError("invalid_reference_path", err.Error()) } - - rootRef.UpdatedAt = ts - rootRef.HashToBeComputed = true - dirRef := rootRef - - for i := 0; i < len(fields); i++ { - found := false - for _, child := range dirRef.Children { - if child.Name == fields[i] { - dirRef = child - dirRef.UpdatedAt = ts - dirRef.HashToBeComputed = true - found = true - break - } + if ref.Type == reference.DIRECTORY { + isEmpty, err := reference.IsDirectoryEmpty(ctx, ref.ID) + if err != nil { + return common.NewError("invalid_reference_path", err.Error()) } - - if !found { - return nil, common.NewError("invalid_reference_path", "Invalid reference path from the blobber") + if !isEmpty { + return common.NewError("invalid_reference_path", "Directory is not empty") } } - - found := false - for i, child := range dirRef.Children { - if child.Path == rf.Path { - dirRef.RemoveChild(i) - dirRef.AddChild(affectedRef) - found = true - break - } + rf.Type = ref.Type + deleteRef := &reference.Ref{ + ID: ref.ID, + LookupHash: oldFileLookupHash, + Type: ref.Type, } - if !found { - return nil, common.NewError("file_not_found", "File to rename not found in blobber") + collector.DeleteRefRecord(deleteRef) + ref.Name = rf.NewName + ref.Path = newPath + ref.ID = 0 + ref.LookupHash = reference.GetReferenceLookup(rf.AllocationID, newPath) + ref.UpdatedAt = ts + ref.FileMetaHash = encryption.Hash(ref.GetFileMetaHashData()) + if rf.CustomMeta != "" { + ref.CustomMeta = rf.CustomMeta } - - return rootRef, nil -} - -func (rf *RenameFileChange) processChildren(ctx context.Context, curRef *reference.Ref, ts common.Timestamp) { - for _, childRef := range curRef.Children { - childRef.UpdatedAt = ts - childRef.HashToBeComputed = true - newPath := filepath.Join(curRef.Path, childRef.Name) - childRef.UpdatePath(newPath, curRef.Path) - if childRef.Type == reference.FILE { - childRef.IsPrecommit = true - } - if childRef.Type == reference.DIRECTORY { - rf.processChildren(ctx, childRef, ts) - } + if rf.MimeType != "" { + ref.MimeType = rf.MimeType } + ref.AllocationVersion = allocationVersion + collector.CreateRefRecord(ref) + rf.newLookupHash = ref.LookupHash + return nil } func (rf *RenameFileChange) Marshal() (string, error) { @@ -129,7 +101,18 @@ func (rf *RenameFileChange) Unmarshal(input string) error { } func (rf *RenameFileChange) CommitToFileStore(ctx context.Context, mut *sync.Mutex) error { - return nil + if rf.Type == reference.DIRECTORY { + return nil + } + if rf.newLookupHash == "" { + return common.NewError("invalid_reference_path", "new lookup hash is empty") + } + oldFileLookupHash := reference.GetReferenceLookup(rf.AllocationID, rf.Path) + err := filestore.GetFileStore().CopyFile(rf.AllocationID, oldFileLookupHash, rf.newLookupHash) + if err != nil { + logging.Logger.Error("CommitToFileStore: CopyFile", zap.Error(err)) + } + return err } func (rf *RenameFileChange) GetPath() []string { diff --git a/code/go/0chain.net/blobbercore/allocation/renamefilechange_main.go b/code/go/0chain.net/blobbercore/allocation/renamefilechange_main.go index 9c773cab7..424ffe7ab 100644 --- a/code/go/0chain.net/blobbercore/allocation/renamefilechange_main.go +++ b/code/go/0chain.net/blobbercore/allocation/renamefilechange_main.go @@ -10,8 +10,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" ) -func (rf *RenameFileChange) ApplyChange(ctx context.Context, rootRef *reference.Ref, change *AllocationChange, - allocationRoot string, ts common.Timestamp, _ map[string]string) (*reference.Ref, error) { +func (rf *RenameFileChange) ApplyChange(ctx context.Context, + ts common.Timestamp, allocationVersion int64, collector reference.QueryCollector) error { - return rf.applyChange(ctx, rootRef, change, allocationRoot, ts, nil) + return rf.applyChange(ctx, ts, allocationVersion, collector) } diff --git a/code/go/0chain.net/blobbercore/allocation/renamefilechange_test.go b/code/go/0chain.net/blobbercore/allocation/renamefilechange_test.go index c895c7003..66b44da29 100644 --- a/code/go/0chain.net/blobbercore/allocation/renamefilechange_test.go +++ b/code/go/0chain.net/blobbercore/allocation/renamefilechange_test.go @@ -17,10 +17,11 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/config" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" - zencryption "github.com/0chain/gosdk/zboxcore/encryption" - "github.com/0chain/gosdk/zcncore" + coreNetwork "github.com/0chain/gosdk/core/conf" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" + zencryption "github.com/0chain/gosdk_common/zboxcore/encryption" + "github.com/0chain/gosdk_common/zcncore" "github.com/DATA-DOG/go-sqlmock" mocket "github.com/selvatico/go-mocket" "github.com/stretchr/testify/require" @@ -55,7 +56,7 @@ func setup(t *testing.T) { if err != nil { t.Fatal(err) } - if err := zcncore.SetWalletInfo(string(wBlob), true); err != nil { + if err := zcncore.SetWalletInfo(string(wBlob), "bls0chain", true); err != nil { t.Fatal(err) } @@ -66,10 +67,10 @@ func setup(t *testing.T) { }, ), ) - server := httptest.NewServer( + _ = httptest.NewServer( http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - n := zcncore.Network{Miners: []string{"miner 1"}, Sharders: []string{sharderServ.URL}} + n := coreNetwork.Network{Miners: []string{"miner 1"}, Sharders: []string{sharderServ.URL}} blob, err := json.Marshal(n) if err != nil { t.Fatal(err) @@ -81,10 +82,6 @@ func setup(t *testing.T) { }, ), ) - - if err := zcncore.InitZCNSDK(server.URL, "ed25519"); err != nil { - t.Fatal(err) - } } func setupMockForFileManagerInit(mock sqlmock.Sqlmock) { mock.ExpectBegin() @@ -151,7 +148,7 @@ func TestBlobberCore_RenameFile(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() testCases := []struct { name string diff --git a/code/go/0chain.net/blobbercore/allocation/rollback.go b/code/go/0chain.net/blobbercore/allocation/rollback.go index fe25adaac..d3070ad96 100644 --- a/code/go/0chain.net/blobbercore/allocation/rollback.go +++ b/code/go/0chain.net/blobbercore/allocation/rollback.go @@ -8,23 +8,23 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" ) -func ApplyRollback(ctx context.Context, allocationID string) error { +func ApplyRollback(ctx context.Context, allocationID string, allocationVersion int64) error { db := datastore.GetStore().GetTransaction(ctx) - // delete all is_precommit rows + // delete all current allocation version rows err := db.Model(&reference.Ref{}).Unscoped(). Delete(&reference.Ref{}, - "allocation_id=? AND is_precommit=? AND deleted_at IS NULL", - allocationID, true).Error + "allocation_id=? AND allocation_version=? AND deleted_at IS NULL", + allocationID, allocationVersion).Error if err != nil { return err } // err = db.Exec("UPDATE file_stats SET deleted_at=NULL WHERE ref_id IN (SELECT id FROM reference_objects WHERE allocation_id=? AND deleted_at IS NOT NULL)", allocationID).Error // revive soft deleted ref rows - err = db.Exec("UPDATE reference_objects SET deleted_at=NULL,is_precommit=? WHERE allocation_id=? AND deleted_at IS NOT NULL", false, allocationID).Error + err = db.Exec("UPDATE reference_objects SET deleted_at=NULL WHERE allocation_id=? AND deleted_at IS NOT NULL", allocationID).Error return err } diff --git a/code/go/0chain.net/blobbercore/allocation/updatefilechange_test.go b/code/go/0chain.net/blobbercore/allocation/updatefilechange_test.go index da41fcc62..07c789d78 100644 --- a/code/go/0chain.net/blobbercore/allocation/updatefilechange_test.go +++ b/code/go/0chain.net/blobbercore/allocation/updatefilechange_test.go @@ -12,8 +12,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" mocket "github.com/selvatico/go-mocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -36,7 +36,7 @@ func TestBlobberCore_UpdateFile(t *testing.T) { ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerID = client.Id() testCases := []struct { name string @@ -344,7 +344,7 @@ func TestBlobberCore_UpdateFile(t *testing.T) { changes: make(map[string]*ConnectionChange), } connectionProcessor["connection_id"].changes[pathHash] = &ConnectionChange{ - hasher: filestore.GetNewCommitHasher(2310), + hasher: filestore.NewCommitHasher(2310), } change := &UpdateFileChanger{ BaseFileChanger: BaseFileChanger{ diff --git a/code/go/0chain.net/blobbercore/allocation/workers.go b/code/go/0chain.net/blobbercore/allocation/workers.go index b5475b81d..97f916630 100644 --- a/code/go/0chain.net/blobbercore/allocation/workers.go +++ b/code/go/0chain.net/blobbercore/allocation/workers.go @@ -12,6 +12,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" + coreTxn "github.com/0chain/gosdk_common/core/transaction" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" @@ -306,20 +308,15 @@ type finalizeRequest struct { } func sendFinalizeAllocation(allocationID string) { - var tx, err = transaction.NewTransactionEntity() - if err != nil { - logging.Logger.Error("creating new transaction entity", zap.Error(err)) - return - } - var request finalizeRequest request.AllocationID = allocationID - err = tx.ExecuteSmartContract( + _, _, _, _, err := coreTxn.SmartContractTxn( transaction.STORAGE_CONTRACT_ADDRESS, - transaction.FINALIZE_ALLOCATION, - request, - 0) + coreTxn.SmartContractTxnData{ + Name: transaction.FINALIZE_ALLOCATION, + InputArgs: request, + }, true) if err != nil { logging.Logger.Error("sending finalize allocation", zap.Error(err)) return diff --git a/code/go/0chain.net/blobbercore/blobberhttp/response.go b/code/go/0chain.net/blobbercore/blobberhttp/response.go index c462fdb23..fb64be0da 100644 --- a/code/go/0chain.net/blobbercore/blobberhttp/response.go +++ b/code/go/0chain.net/blobbercore/blobberhttp/response.go @@ -35,7 +35,6 @@ type RefResult struct { OffsetPath string `json:"offset_path,omitempty"` //used for pagination; index for path is created in database OffsetDate common.Timestamp `json:"offset_date,omitempty"` //used for pagination; idex for updated_at is created in database Refs *[]reference.PaginatedRef `json:"refs"` - LatestWM *writemarker.WriteMarker `json:"latest_write_marker"` } // swagger:model RecentRefResult @@ -50,9 +49,9 @@ type ObjectPathResult struct { // swagger:model ListResult type ListResult struct { - AllocationRoot string `json:"allocation_root"` - Meta map[string]interface{} `json:"meta_data"` - Entities []map[string]interface{} `json:"list"` + AllocationVersion int64 `json:"allocation_version"` + Meta map[string]interface{} `json:"meta_data"` + Entities []map[string]interface{} `json:"list"` } // swagger:model DownloadResponse @@ -70,3 +69,7 @@ type LatestWriteMarkerResult struct { PrevWM *writemarker.WriteMarker `json:"prev_write_marker"` Version string `json:"version"` } + +type LatestVersionMarkerResult struct { + VersionMarker *writemarker.VersionMarker `json:"version_marker"` +} diff --git a/code/go/0chain.net/blobbercore/challenge/challenge.go b/code/go/0chain.net/blobbercore/challenge/challenge.go index e8f37cee7..5fb55d152 100644 --- a/code/go/0chain.net/blobbercore/challenge/challenge.go +++ b/code/go/0chain.net/blobbercore/challenge/challenge.go @@ -9,6 +9,7 @@ import ( "time" "github.com/0chain/blobber/code/go/0chain.net/core/node" + coreTxn "github.com/0chain/gosdk_common/core/transaction" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" "github.com/0chain/blobber/code/go/0chain.net/core/common" @@ -162,7 +163,7 @@ func validateOnValidators(ctx context.Context, c *ChallengeEntity) error { return nil } -func (c *ChallengeEntity) getCommitTransaction(ctx context.Context) (*transaction.Transaction, error) { +func (c *ChallengeEntity) getCommitTransaction(ctx context.Context) (*coreTxn.Transaction, error) { createdTime := common.ToTime(c.CreatedAt) logging.Logger.Info("[challenge]verify: ", @@ -186,13 +187,6 @@ func (c *ChallengeEntity) getCommitTransaction(ctx context.Context) (*transactio return nil, nil } - txn, err := transaction.NewTransactionEntity() - if err != nil { - logging.Logger.Error("[challenge]createTxn", zap.Error(err)) - c.CancelChallenge(ctx, err) - return nil, nil - } - sn := &ChallengeResponse{} sn.ChallengeID = c.ChallengeID for _, vt := range c.ValidationTickets { @@ -201,14 +195,17 @@ func (c *ChallengeEntity) getCommitTransaction(ctx context.Context) (*transactio } } - err = txn.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, transaction.CHALLENGE_RESPONSE, sn, 0) + _, _, _, txn, err := coreTxn.SmartContractTxn(transaction.STORAGE_CONTRACT_ADDRESS, coreTxn.SmartContractTxnData{ + Name: transaction.CHALLENGE_RESPONSE, + InputArgs: sn, + }, false) if err != nil { logging.Logger.Info("Failed submitting challenge to the mining network", zap.String("err:", err.Error())) c.CancelChallenge(ctx, err) return nil, nil } - err = UpdateChallengeTimingTxnSubmission(c.ChallengeID, txn.CreationDate) + err = UpdateChallengeTimingTxnSubmission(c.ChallengeID, common.Timestamp(txn.CreationDate)) if err != nil { logging.Logger.Error("[challengetiming]txn_submission", zap.Any("challenge_id", c.ChallengeID), diff --git a/code/go/0chain.net/blobbercore/challenge/protocol.go b/code/go/0chain.net/blobbercore/challenge/protocol.go index 55b8940c0..8d669d2e9 100644 --- a/code/go/0chain.net/blobbercore/challenge/protocol.go +++ b/code/go/0chain.net/blobbercore/challenge/protocol.go @@ -14,13 +14,13 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/writemarker" - "github.com/0chain/blobber/code/go/0chain.net/core/chain" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/lock" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" "github.com/0chain/blobber/code/go/0chain.net/core/util" - sdkUtil "github.com/0chain/gosdk/core/util" + coretxn "github.com/0chain/gosdk_common/core/transaction" + sdkUtil "github.com/0chain/gosdk_common/core/util" "github.com/remeh/sizedwaitgroup" "gorm.io/gorm" @@ -109,9 +109,6 @@ func (cr *ChallengeEntity) LoadValidationTickets(ctx context.Context) error { blockNum := int64(0) var objectPath *reference.ObjectPath if rootRef != nil { - if rootRef.Hash != allocationObj.AllocationRoot { - logging.Logger.Error("root_mismatch", zap.Any("allocation_root", allocationObj.AllocationRoot), zap.Any("latest_write_marker", wms[len(wms)-1].WM.AllocationRoot), zap.Any("root_ref_hash", rootRef.Hash)) - } if rootRef.NumBlocks > 0 { r := rand.New(rand.NewSource(cr.RandomNumber)) blockNum = r.Int63n(rootRef.NumBlocks) @@ -350,11 +347,11 @@ func (cr *ChallengeEntity) LoadValidationTickets(ctx context.Context) error { return nil } -func (cr *ChallengeEntity) VerifyChallengeTransaction(ctx context.Context, txn *transaction.Transaction) error { +func (cr *ChallengeEntity) VerifyChallengeTransaction(ctx context.Context, txn *coretxn.Transaction) error { if len(cr.LastCommitTxnIDs) > 0 { for _, lastTxn := range cr.LastCommitTxnIDs { logging.Logger.Info("[challenge]commit: Verifying the transaction : " + lastTxn) - t, err := transaction.VerifyTransaction(lastTxn, chain.GetServerChain()) + t, err := coretxn.VerifyTransaction(lastTxn) if err == nil { cr.SaveChallengeResult(ctx, t, false) return nil @@ -365,11 +362,11 @@ func (cr *ChallengeEntity) VerifyChallengeTransaction(ctx context.Context, txn * logging.Logger.Info("Verifying challenge response to blockchain.", zap.String("txn", txn.Hash), zap.String("challenge_id", cr.ChallengeID)) var ( - t *transaction.Transaction + t *coretxn.Transaction err error ) for i := 0; i < 3; i++ { - t, err = transaction.VerifyTransactionWithNonce(txn.Hash, txn.GetTransaction().GetTransactionNonce()) + t, err = coretxn.VerifyTransaction(txn.Hash) if err == nil { break } @@ -406,7 +403,7 @@ func IsEntityNotFoundError(err error) bool { return strings.Contains(err.Error(), EntityNotFound) } -func (cr *ChallengeEntity) SaveChallengeResult(ctx context.Context, t *transaction.Transaction, toAdd bool) { +func (cr *ChallengeEntity) SaveChallengeResult(ctx context.Context, t *coretxn.Transaction, toAdd bool) { cr.statusMutex.Lock() cr.Status = Committed cr.statusMutex.Unlock() diff --git a/code/go/0chain.net/blobbercore/challenge/worker.go b/code/go/0chain.net/blobbercore/challenge/worker.go index e6fbe14e2..c4fb76c7c 100644 --- a/code/go/0chain.net/blobbercore/challenge/worker.go +++ b/code/go/0chain.net/blobbercore/challenge/worker.go @@ -2,15 +2,15 @@ package challenge import ( "context" + "encoding/json" "sync" "time" - "github.com/0chain/gosdk/zcncore" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/blobber/code/go/0chain.net/core/transaction" + "github.com/0chain/gosdk_common/core/screstapi" + coreTxn "github.com/0chain/gosdk_common/core/transaction" "github.com/emirpasic/gods/maps/treemap" "go.uber.org/zap" "golang.org/x/sync/semaphore" @@ -54,8 +54,8 @@ const batchSize = 5 // SetupWorkers start challenge workers func SetupWorkers(ctx context.Context) { - go startPullWorker(ctx) - go startWorkers(ctx) + // go startPullWorker(ctx) + // go startWorkers(ctx) } func startPullWorker(ctx context.Context) { @@ -95,7 +95,15 @@ func getRoundWorker(ctx context.Context) { } func setRound() { - currentRound, _ := zcncore.GetRoundFromSharders() + res, err := screstapi.MakeSCRestAPICall("", "/v1/current-round", nil, "") + if err != nil { + logging.Logger.Error("getRoundWorker", zap.Error(err)) + } + var currentRound int64 + err = json.Unmarshal(res, ¤tRound) + if err != nil { + logging.Logger.Error("getRoundWorker", zap.Error(err)) + } if roundInfo.LastRoundDiff == 0 { roundInfo.LastRoundDiff = 1000 @@ -183,7 +191,7 @@ func commitOnChainWorker(ctx context.Context) { for _, challenge := range challenges { chall := challenge var ( - txn *transaction.Transaction + txn *coreTxn.Transaction err error ) _ = datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { diff --git a/code/go/0chain.net/blobbercore/config/config.go b/code/go/0chain.net/blobbercore/config/config.go index 7e9d2dccc..73c9e3bdf 100644 --- a/code/go/0chain.net/blobbercore/config/config.go +++ b/code/go/0chain.net/blobbercore/config/config.go @@ -32,9 +32,10 @@ func SetupDefaultConfig() { viper.SetDefault("rate_limiters.block_limit_request", 500) viper.SetDefault("rate_limiters.block_limit_monthly", 31250000) viper.SetDefault("rate_limiters.upload_limit_monthly", 31250000) - viper.SetDefault("rate_limiters.commit_limit_monthly", 30000) + viper.SetDefault("rate_limiters.commit_limit_monthly", 1000000000) viper.SetDefault("rate_limiters.commit_limit_daily", 1600) viper.SetDefault("rate_limiters.commit_zero_limit_daily", 400) + viper.SetDefault("rate_limiters.max_connection_changes", 100) viper.SetDefault("healthcheck.frequency", "60s") @@ -121,6 +122,7 @@ type Config struct { CommitLimitDaily int64 CommitZeroLimitDaily int64 ChallengeCleanupGap int64 + MaxConnectionChanges int HealthCheckWorkerFreq time.Duration @@ -292,7 +294,6 @@ func ReadConfig(deploymentMode int) { } else if Configuration.MinConfirmation > 100 { Configuration.MinConfirmation = 100 } - Configuration.BlockLimitDaily = viper.GetInt64("rate_limiters.block_limit_daily") Configuration.BlockLimitRequest = viper.GetInt64("rate_limiters.block_limit_request") Configuration.BlockLimitMonthly = viper.GetInt64("rate_limiters.block_limit_monthly") @@ -300,6 +301,7 @@ func ReadConfig(deploymentMode int) { Configuration.CommitLimitMonthly = viper.GetInt64("rate_limiters.commit_limit_monthly") Configuration.CommitLimitDaily = viper.GetInt64("rate_limiters.commit_limit_daily") Configuration.CommitZeroLimitDaily = viper.GetInt64("rate_limiters.commit_zero_limit_daily") + Configuration.MaxConnectionChanges = viper.GetInt("rate_limiters.max_connection_changes") Configuration.IsEnterprise = viper.GetBool("is_enterprise") } diff --git a/code/go/0chain.net/blobbercore/config/settings.go b/code/go/0chain.net/blobbercore/config/settings.go index f12f26cca..7fd0e9e30 100644 --- a/code/go/0chain.net/blobbercore/config/settings.go +++ b/code/go/0chain.net/blobbercore/config/settings.go @@ -8,8 +8,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/errors" - "github.com/0chain/gosdk/constants" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk/zboxcore/sdk" + "github.com/0chain/gosdk_common/constants" "go.uber.org/zap" "gorm.io/gorm" ) @@ -102,7 +102,7 @@ func Update(ctx context.Context, db *gorm.DB) error { } // ReloadFromChain load and refresh latest settings from blockchain -func ReloadFromChain(ctx context.Context, db *gorm.DB) (*zcncore.Blobber, error) { +func ReloadFromChain(ctx context.Context, db *gorm.DB) (*sdk.Blobber, error) { if db == nil { return nil, errors.Throw(constants.ErrInvalidParameter, "db") } @@ -115,7 +115,7 @@ func ReloadFromChain(ctx context.Context, db *gorm.DB) (*zcncore.Blobber, error) } Configuration.Capacity = int64(b.Capacity) - Configuration.NumDelegates = *b.StakePoolSettings.NumDelegates + Configuration.NumDelegates = b.StakePoolSettings.NumDelegates if token, err := b.Terms.ReadPrice.ToToken(); err != nil { return nil, err @@ -129,6 +129,6 @@ func ReloadFromChain(ctx context.Context, db *gorm.DB) (*zcncore.Blobber, error) Configuration.WritePrice = token } - Configuration.ServiceCharge = *b.StakePoolSettings.ServiceCharge + Configuration.ServiceCharge = b.StakePoolSettings.ServiceCharge return b, Update(ctx, db) } diff --git a/code/go/0chain.net/blobbercore/convert/convert.go b/code/go/0chain.net/blobbercore/convert/convert.go index 8f3ca5c41..1c6ef1bb3 100644 --- a/code/go/0chain.net/blobbercore/convert/convert.go +++ b/code/go/0chain.net/blobbercore/convert/convert.go @@ -280,13 +280,9 @@ func convertFileRefToFileMetaDataGRPC(fileref *reference.Ref) *blobbergrpc.FileM LookupHash: fileref.LookupHash, Name: fileref.Name, Path: fileref.Path, - Hash: fileref.Hash, NumBlocks: fileref.NumBlocks, - PathHash: fileref.PathHash, CustomMeta: fileref.CustomMeta, - ValidationRoot: fileref.ValidationRoot, Size: fileref.Size, - FixedMerkleRoot: fileref.FixedMerkleRoot, ActualFileSize: fileref.ActualFileSize, ActualFileHash: fileref.ActualFileHash, MimeType: fileref.MimeType, @@ -306,9 +302,7 @@ func convertDirRefToDirMetaDataGRPC(dirref *reference.Ref) *blobbergrpc.DirMetaD LookupHash: dirref.LookupHash, Name: dirref.Name, Path: dirref.Path, - Hash: dirref.Hash, NumBlocks: dirref.NumBlocks, - PathHash: dirref.PathHash, Size: dirref.Size, CreatedAt: int64(dirref.CreatedAt), UpdatedAt: int64(dirref.UpdatedAt), @@ -336,13 +330,9 @@ func convertFileMetaDataGRPCToFileRef(metaData *blobbergrpc.FileMetaData) *refer LookupHash: metaData.LookupHash, Name: metaData.Name, Path: metaData.Path, - Hash: metaData.Hash, NumBlocks: metaData.NumBlocks, - PathHash: metaData.PathHash, CustomMeta: metaData.CustomMeta, - ValidationRoot: metaData.ValidationRoot, Size: metaData.Size, - FixedMerkleRoot: metaData.FixedMerkleRoot, ActualFileSize: metaData.ActualFileSize, ActualFileHash: metaData.ActualFileHash, MimeType: metaData.MimeType, @@ -362,9 +352,7 @@ func convertDirMetaDataGRPCToDirRef(dirref *blobbergrpc.DirMetaData) *reference. LookupHash: dirref.LookupHash, Name: dirref.Name, Path: dirref.Path, - Hash: dirref.Hash, NumBlocks: dirref.NumBlocks, - PathHash: dirref.PathHash, Size: dirref.Size, CreatedAt: common.Timestamp(dirref.CreatedAt), UpdatedAt: common.Timestamp(dirref.UpdatedAt), diff --git a/code/go/0chain.net/blobbercore/convert/response_creator.go b/code/go/0chain.net/blobbercore/convert/response_creator.go index 828bd2bcb..7e8dbbfdc 100644 --- a/code/go/0chain.net/blobbercore/convert/response_creator.go +++ b/code/go/0chain.net/blobbercore/convert/response_creator.go @@ -62,7 +62,6 @@ func ListEntitesResponseCreator(r interface{}) *blobbergrpc.ListEntitiesResponse } resp.MetaData = FileRefToFileRefGRPC(reference.ListingDataToRef(httpResp.Meta)) - resp.AllocationRoot = httpResp.AllocationRoot return &resp } @@ -157,12 +156,10 @@ func CopyObjectResponseCreator(r interface{}) *blobbergrpc.CopyObjectResponse { httpResp, _ := r.(*allocation.UploadResult) return &blobbergrpc.CopyObjectResponse{ - Filename: httpResp.Filename, - Size: httpResp.Size, - ValidationRoot: httpResp.ValidationRoot, - FixedMerkleRoot: httpResp.FixedMerkleRoot, - UploadLength: httpResp.UploadLength, - UploadOffset: httpResp.UploadOffset, + Filename: httpResp.Filename, + Size: httpResp.Size, + UploadLength: httpResp.UploadLength, + UploadOffset: httpResp.UploadOffset, } } @@ -173,12 +170,10 @@ func RenameObjectResponseCreator(r interface{}) *blobbergrpc.RenameObjectRespons httpResp, _ := r.(*allocation.UploadResult) return &blobbergrpc.RenameObjectResponse{ - Filename: httpResp.Filename, - Size: httpResp.Size, - ValidationRoot: httpResp.ValidationRoot, - FixedMerkleRoot: httpResp.FixedMerkleRoot, - UploadLength: httpResp.UploadLength, - UploadOffset: httpResp.UploadOffset, + Filename: httpResp.Filename, + Size: httpResp.Size, + UploadLength: httpResp.UploadLength, + UploadOffset: httpResp.UploadOffset, } } @@ -211,11 +206,9 @@ func UploadFileResponseCreator(r interface{}) *blobbergrpc.UploadFileResponse { httpResp, _ := r.(*allocation.UploadResult) return &blobbergrpc.UploadFileResponse{ - Filename: httpResp.Filename, - Size: httpResp.Size, - ValidationRoot: httpResp.ValidationRoot, - FixedMerkleRoot: httpResp.FixedMerkleRoot, - UploadLength: httpResp.UploadLength, - UploadOffset: httpResp.UploadOffset, + Filename: httpResp.Filename, + Size: httpResp.Size, + UploadLength: httpResp.UploadLength, + UploadOffset: httpResp.UploadOffset, } } diff --git a/code/go/0chain.net/blobbercore/convert/response_handler.go b/code/go/0chain.net/blobbercore/convert/response_handler.go index 3092c6b6d..8fb3374df 100644 --- a/code/go/0chain.net/blobbercore/convert/response_handler.go +++ b/code/go/0chain.net/blobbercore/convert/response_handler.go @@ -42,9 +42,8 @@ func ListEntitesResponseHandler(resp *blobbergrpc.ListEntitiesResponse) *blobber } return &blobberhttp.ListResult{ - AllocationRoot: resp.AllocationRoot, - Meta: FileRefGRPCToFileRef(resp.MetaData).GetListingData(ctx), - Entities: entities, + Meta: FileRefGRPCToFileRef(resp.MetaData).GetListingData(ctx), + Entities: entities, } } @@ -119,11 +118,9 @@ func GetCommitMetaTxnHandlerResponse(response *blobbergrpc.CommitMetaTxnResponse func CopyObjectResponseHandler(copyObjectResponse *blobbergrpc.CopyObjectResponse) *allocation.UploadResult { return &allocation.UploadResult{ - Filename: copyObjectResponse.Filename, - Size: copyObjectResponse.Size, - ValidationRoot: copyObjectResponse.ValidationRoot, - FixedMerkleRoot: copyObjectResponse.FixedMerkleRoot, - UploadLength: copyObjectResponse.UploadLength, - UploadOffset: copyObjectResponse.UploadOffset, + Filename: copyObjectResponse.Filename, + Size: copyObjectResponse.Size, + UploadLength: copyObjectResponse.UploadLength, + UploadOffset: copyObjectResponse.UploadOffset, } } diff --git a/code/go/0chain.net/blobbercore/datastore/mocket.go b/code/go/0chain.net/blobbercore/datastore/mocket.go index b560b0841..fb9b3d4cc 100644 --- a/code/go/0chain.net/blobbercore/datastore/mocket.go +++ b/code/go/0chain.net/blobbercore/datastore/mocket.go @@ -72,7 +72,7 @@ func (store *Mocket) Close() { } } -func (store *Mocket) CreateTransaction(ctx context.Context,opts ...*sql.TxOptions) context.Context { +func (store *Mocket) CreateTransaction(ctx context.Context, opts ...*sql.TxOptions) context.Context { db := store.db.Begin() return context.WithValue(ctx, ContextKeyTransaction, EnhanceDB(db)) } @@ -86,7 +86,7 @@ func (store *Mocket) GetTransaction(ctx context.Context) *EnhancedDB { return nil } -func (store *Mocket) WithNewTransaction(f func(ctx context.Context) error) error { +func (store *Mocket) WithNewTransaction(f func(ctx context.Context) error, opts ...*sql.TxOptions) error { ctx := store.CreateTransaction(context.TODO()) defer ctx.Done() diff --git a/code/go/0chain.net/blobbercore/datastore/postgres.go b/code/go/0chain.net/blobbercore/datastore/postgres.go index 241701dfe..47e46623b 100644 --- a/code/go/0chain.net/blobbercore/datastore/postgres.go +++ b/code/go/0chain.net/blobbercore/datastore/postgres.go @@ -11,6 +11,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/logging" "gorm.io/driver/postgres" "gorm.io/gorm" + "moul.io/zapgorm2" ) // postgresStore store implementation for postgres @@ -46,6 +47,11 @@ func (p *postgresStore) GetPgDB() (*gorm.DB, error) { } func (store *postgresStore) Open() error { + gormLogger := zapgorm2.New(logging.Logger) + gormLogger.SlowThreshold = 100 * time.Millisecond + gormLogger.IgnoreRecordNotFoundError = true + gormLogger.SkipCallerLookup = true + gormLogger.SetAsDefault() db, err := gorm.Open(postgres.Open(fmt.Sprintf( "host=%v port=%v user=%v dbname=%v password=%v sslmode=disable", config.Configuration.DBHost, config.Configuration.DBPort, @@ -53,6 +59,7 @@ func (store *postgresStore) Open() error { config.Configuration.DBPassword)), &gorm.Config{ SkipDefaultTransaction: true, // https://gorm.io/docs/performance.html#Disable-Default-Transaction PrepareStmt: true, //https://gorm.io/docs/performance.html#Caches-Prepared-Statement + Logger: gormLogger, }) if err != nil { return common.NewErrorf("db_open_error", "Error opening the DB connection: %v", err) @@ -99,7 +106,7 @@ func (store *postgresStore) GetTransaction(ctx context.Context) *EnhancedDB { return nil } -func (store *postgresStore) WithNewTransaction(f func(ctx context.Context) error) error { +func (store *postgresStore) WithNewTransaction(f func(ctx context.Context) error, opts ...*sql.TxOptions) error { timeoutctx, cancel := context.WithTimeout(context.TODO(), 45*time.Second) defer cancel() ctx := store.CreateTransaction(timeoutctx) diff --git a/code/go/0chain.net/blobbercore/datastore/sqlmock.go b/code/go/0chain.net/blobbercore/datastore/sqlmock.go index a7bcdeac5..354cc69e5 100644 --- a/code/go/0chain.net/blobbercore/datastore/sqlmock.go +++ b/code/go/0chain.net/blobbercore/datastore/sqlmock.go @@ -81,7 +81,7 @@ func (store *Sqlmock) GetTransaction(ctx context.Context) *EnhancedDB { return nil } -func (store *Sqlmock) WithNewTransaction(f func(ctx context.Context) error) error { +func (store *Sqlmock) WithNewTransaction(f func(ctx context.Context) error, opts ...*sql.TxOptions) error { ctx := store.CreateTransaction(context.TODO()) defer ctx.Done() diff --git a/code/go/0chain.net/blobbercore/datastore/store.go b/code/go/0chain.net/blobbercore/datastore/store.go index a097be572..bedc36896 100644 --- a/code/go/0chain.net/blobbercore/datastore/store.go +++ b/code/go/0chain.net/blobbercore/datastore/store.go @@ -45,7 +45,7 @@ type Store interface { CreateTransaction(ctx context.Context, opts ...*sql.TxOptions) context.Context // GetTransaction get transaction from context GetTransaction(ctx context.Context) *EnhancedDB - WithNewTransaction(f func(ctx context.Context) error) error + WithNewTransaction(f func(ctx context.Context) error, opts ...*sql.TxOptions) error WithTransaction(ctx context.Context, f func(ctx context.Context) error) error // Get db connection with user that creates roles and databases. Its dialactor does not contain database name GetPgDB() (*gorm.DB, error) diff --git a/code/go/0chain.net/blobbercore/filestore/state.go b/code/go/0chain.net/blobbercore/filestore/state.go index b098a34c8..f68b556f1 100644 --- a/code/go/0chain.net/blobbercore/filestore/state.go +++ b/code/go/0chain.net/blobbercore/filestore/state.go @@ -57,8 +57,6 @@ func (fs *FileStore) initMap() error { return errors.New("could not get db client") } - limitCh := make(chan struct{}, 50) - wg := &sync.WaitGroup{} var dbAllocations []*dbAllocation err := db.Model(&dbAllocation{}).FindInBatches(&dbAllocations, 1000, func(tx *gorm.DB, batch int) error { @@ -78,18 +76,12 @@ func (fs *FileStore) initMap() error { if err != nil { return err } - - limitCh <- struct{}{} - wg.Add(1) - go fs.getTemporaryStorageDetails(ctx, &a, dbAlloc.ID, limitCh, wg) - } fs.setAllocations(allocsMap) return nil }).Error - wg.Wait() return err }) return err diff --git a/code/go/0chain.net/blobbercore/filestore/storage.go b/code/go/0chain.net/blobbercore/filestore/storage.go index 63bd60e8a..083f3d3e3 100644 --- a/code/go/0chain.net/blobbercore/filestore/storage.go +++ b/code/go/0chain.net/blobbercore/filestore/storage.go @@ -45,7 +45,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/util" + "github.com/0chain/gosdk_common/core/util" "go.uber.org/zap" "golang.org/x/crypto/sha3" "golang.org/x/sys/unix" @@ -57,10 +57,15 @@ const ( MerkleChunkSize = 64 ChunkSize = 64 * KB BufferSize = 80 * ChunkSize + ThumbnailSuffix = "_thumbnail" ) func (fs *FileStore) WriteFile(allocID, conID string, fileData *FileInputData, infile multipart.File) (*FileOutputData, error) { - tempFilePath := fs.getTempPathForFile(allocID, fileData.Name, fileData.FilePathHash, conID) + fileHash := fileData.LookupHash + if fileData.IsThumbnail { + fileHash = fileData.LookupHash + ThumbnailSuffix + } + tempFilePath := fs.getTempPathForFile(allocID, fileData.Name, fileHash, conID) var ( initialSize int64 ) @@ -157,6 +162,17 @@ func (fs *FileStore) MoveToFilestore(allocID, hash string, version int) error { } _ = os.Rename(preCommitPath, fPath) + + // Check if thumbnail exists + thumbPath := fs.getPreCommitPathForFile(allocID, hash+ThumbnailSuffix, version) + if _, err := os.Stat(thumbPath); err == nil { + thumbFilePath, err := fs.GetPathForFile(allocID, hash+ThumbnailSuffix, version) + if err != nil { + return common.NewError("get_file_path_error", err.Error()) + } + _ = os.Rename(thumbPath, thumbFilePath) + } + return nil } @@ -179,6 +195,14 @@ func (fs *FileStore) DeleteFromFilestore(allocID, hash string, version int) erro } fs.incrDecrAllocFileSizeAndNumber(allocID, -stat.Size(), -1) + thumbPath, err := fs.GetPathForFile(allocID, hash+ThumbnailSuffix, version) + if err != nil { + return common.NewError("get_file_path_error", err.Error()) + } + if _, err := os.Stat(thumbPath); err == nil { + os.Remove(thumbPath) //nolint:errcheck + } + return nil } @@ -193,15 +217,13 @@ func (fs *FileStore) DeletePreCommitDir(allocID string) error { } func (fs *FileStore) CommitWrite(allocID, conID string, fileData *FileInputData) (_ bool, err error) { - - logging.Logger.Info("Committing write", zap.String("allocation_id", allocID), zap.Any("file_data", fileData)) - filePathHash := encryption.Hash(fileData.Path) - tempFilePath := fs.getTempPathForFile(allocID, fileData.Name, filePathHash, conID) - - fileHash := fileData.ValidationRoot + now := time.Now() + logging.Logger.Debug("Committing write", zap.String("allocation_id", allocID), zap.Any("file_data", fileData)) + fileHash := fileData.LookupHash if fileData.IsThumbnail { - fileHash = fileData.ThumbnailHash + fileHash = fileData.LookupHash + ThumbnailSuffix } + tempFilePath := fs.getTempPathForFile(allocID, fileData.Name, fileHash, conID) preCommitPath := fs.getPreCommitPathForFile(allocID, fileHash, VERSION) @@ -255,7 +277,7 @@ func (fs *FileStore) CommitWrite(allocID, conID string, fileData *FileInputData) return true, nil } - key := getKey(allocID, fileData.ValidationRoot) + key := getKey(allocID, fileData.LookupHash) l, _ := contentHashMapLock.GetLock(key) l.Lock() defer func() { @@ -270,41 +292,18 @@ func (fs *FileStore) CommitWrite(allocID, conID string, fileData *FileInputData) } fileSize := rStat.Size() - now := time.Now() ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() - err = fileData.Hasher.Wait(ctx, conID, allocID, fileData.Name, filePathHash) + err = fileData.Hasher.Wait(ctx) if err != nil { return false, common.NewError("hasher_wait_error", err.Error()) } - elapsedWait := time.Since(now) - _, err = r.Seek(fileSize, io.SeekStart) - if err != nil { - return false, common.NewError("seek_error", err.Error()) - } - fmtRootBytes, err := fileData.Hasher.fmt.CalculateRootAndStoreNodes(r) - if err != nil { - return false, common.NewError("fmt_hash_calculation_error", err.Error()) + md5Hash := fileData.Hasher.GetMd5Hash() + if md5Hash != fileData.DataHash { + return false, common.NewError("hash_mismatch", + fmt.Sprintf("calculated hash does not match with expected hash. Expected %s, got %s.", + fileData.DataHash, md5Hash)) } - - validationRootBytes, err := fileData.Hasher.vt.CalculateRootAndStoreNodes(r, fileSize) - if err != nil { - return false, common.NewError("validation_hash_calculation_error", err.Error()) - } - fmtRoot := hex.EncodeToString(fmtRootBytes) - validationRoot := hex.EncodeToString(validationRootBytes) - elapsedRoot := time.Since(now) - elapsedWait - if fmtRoot != fileData.FixedMerkleRoot { - err = common.NewError("fixed_merkle_root_mismatch", - fmt.Sprintf("Expected %s got %s", fileData.FixedMerkleRoot, fmtRoot)) - return false, err - } - if validationRoot != fileData.ValidationRoot { - err = common.NewError("validation_root_mismatch", - "calculated validation root does not match with client's validation root") - return false, err - } - err = os.Rename(tempFilePath, preCommitPath) if err != nil { return false, common.NewError("write_error", err.Error()) @@ -321,10 +320,81 @@ func (fs *FileStore) CommitWrite(allocID, conID string, fileData *FileInputData) // 5. Move: It is Copy + Delete. Delete will not delete file if ref exists in database. i.e. copy would create // ref that refers to this file therefore it will be skipped fs.incrDecrAllocFileSizeAndNumber(allocID, fileSize, 1) - logging.Logger.Info("Committing write done", zap.String("file_path", fileData.Path), zap.Duration("elapsed_wait", elapsedWait), zap.Duration("elapsed_root", elapsedRoot), zap.Duration("elapsed_total", time.Since(now))) + logging.Logger.Info("Committing write done", zap.String("file_path", fileData.Path), zap.String("lookup_hash", fileData.LookupHash), zap.Duration("elapsed_total", time.Since(now))) return true, nil } +func (fs *FileStore) CopyFile(allocationID, oldFileLookupHash, newFileLookupHash string) error { + if oldFileLookupHash == newFileLookupHash { + return nil + } + var err error + oldObjectPath, err := fs.GetPathForFile(allocationID, oldFileLookupHash, VERSION) + if err != nil { + return common.NewError("get_file_path_error", err.Error()) + } + oldFile, err := os.Open(oldObjectPath) + if err != nil { + return common.NewError("file_open_error", err.Error()) + } + defer oldFile.Close() + stat, err := oldFile.Stat() + if err != nil { + return common.NewError("file_stat_error", err.Error()) + } + size := stat.Size() + + newObjectPath := fs.getPreCommitPathForFile(allocationID, newFileLookupHash, VERSION) + err = createDirs(filepath.Dir(newObjectPath)) + if err != nil { + return common.NewError("blob_object_precommit_dir_creation_error", err.Error()) + } + newFile, err := os.Create(newObjectPath) + if err != nil { + return common.NewError("file_create_error", err.Error()) + } + defer func() { + newFile.Close() + if err != nil { + os.Remove(newObjectPath) //nolint:errcheck + } + }() + bufSize := BufferSize + if size < int64(bufSize) { + bufSize = int(size) + } + copyBuf := make([]byte, bufSize) + _, err = io.CopyBuffer(newFile, oldFile, copyBuf) + if err != nil { + return common.NewError("file_copy_error", err.Error()) + } + // copy thumbnail if exists + oldThumbPath := fs.getPreCommitPathForFile(allocationID, oldFileLookupHash+ThumbnailSuffix, VERSION) + if _, err := os.Stat(oldThumbPath); err == nil { + newThumbPath := fs.getPreCommitPathForFile(allocationID, newFileLookupHash+ThumbnailSuffix, VERSION) + oldThumbFile, err := os.Open(oldThumbPath) + if err != nil { + return common.NewError("file_open_error", err.Error()) + } + defer oldThumbFile.Close() + newThumbFile, err := os.Create(newThumbPath) + if err != nil { + return common.NewError("file_create_error", err.Error()) + } + defer func() { + newThumbFile.Close() + if err != nil { + os.Remove(newThumbPath) //nolint:errcheck + } + }() + _, err = io.Copy(newThumbFile, oldThumbFile) + if err != nil { + return common.NewError("file_copy_error", err.Error()) + } + } + return nil +} + func (fs *FileStore) GetFilePathSize(allocID, filehash, thumbHash string, version int) (int64, int64, error) { filePath, err := fs.GetPathForFile(allocID, filehash, version) @@ -435,6 +505,7 @@ func (fs *FileStore) DeleteAllocation(allocID string) { func (fs *FileStore) GetFileThumbnail(readBlockIn *ReadBlockInput) (*FileDownloadResponse, error) { var fileObjectPath string var err error + readBlockIn.Hash += ThumbnailSuffix startBlock := readBlockIn.StartBlockNum if startBlock < 0 { return nil, common.NewError("invalid_block_number", "Invalid block number. Start block number cannot be negative") @@ -515,7 +586,6 @@ func (fs *FileStore) GetFileBlock(readBlockIn *ReadBlockInput) (*FileDownloadRes } startBlock := readBlockIn.StartBlockNum - endBlock := readBlockIn.StartBlockNum + readBlockIn.NumBlocks - 1 if startBlock < 0 { return nil, common.NewError("invalid_block_number", "Invalid block number. Start block number cannot be negative") @@ -558,26 +628,6 @@ func (fs *FileStore) GetFileBlock(readBlockIn *ReadBlockInput) (*FileDownloadRes nodesSize := getNodesSize(filesize, util.MaxMerkleLeavesSize) vmp := &FileDownloadResponse{} - if readBlockIn.VerifyDownload { - vpOffset := int64(FMTSize) - if readBlockIn.FilestoreVersion == 1 { - vpOffset += readBlockIn.FileSize - } - vp := validationTreeProof{ - dataSize: readBlockIn.FileSize, - offset: vpOffset, - } - - logging.Logger.Debug("calling GetMerkleProofOfMultipleIndexes", zap.Any("readBlockIn", readBlockIn), zap.Any("vmp", vmp)) - nodes, indexes, err := vp.GetMerkleProofOfMultipleIndexes(file, nodesSize, startBlock, endBlock) - if err != nil { - return nil, common.NewError("get_merkle_proof_error", err.Error()) - } - - vmp.Nodes = nodes - vmp.Indexes = indexes - } - logging.Logger.Info("filestore_version", zap.Int("version", readBlockIn.FilestoreVersion)) fileOffset := int64(startBlock) * ChunkSize if readBlockIn.FilestoreVersion == 1 { _, err = file.Seek(fileOffset, io.SeekStart) @@ -796,7 +846,7 @@ func (fs *FileStore) getAllocDir(allocID string) string { } func (fs *FileStore) GetPathForFile(allocID, hash string, version int) (string, error) { - if len(allocID) != 64 || len(hash) != 64 { + if len(allocID) != 64 { return "", errors.New("length of allocationID/hash must be 64") } var versionStr string @@ -830,7 +880,7 @@ func (fs *FileStore) getPreCommitDir(allocationID string) string { func (fs *FileStore) getTempPathForFile(allocId, fileName, pathHash, connectionID string) string { fileName = sanitizeFileName(fileName) - return filepath.Join(fs.getAllocTempDir(allocId), fileName+"."+pathHash+"."+connectionID) + return filepath.Join(fs.getAllocTempDir(allocId), getPartialPath(pathHash, getDirLevelsForFiles())+"."+connectionID) } func (fs *FileStore) getPreCommitPathForFile(allocID, hash string, version int) string { diff --git a/code/go/0chain.net/blobbercore/filestore/store.go b/code/go/0chain.net/blobbercore/filestore/store.go index 228e8de05..3e39ae39d 100644 --- a/code/go/0chain.net/blobbercore/filestore/store.go +++ b/code/go/0chain.net/blobbercore/filestore/store.go @@ -10,12 +10,11 @@ const ( ) type FileInputData struct { - Name string - Path string - ValidationRoot string - FixedMerkleRoot string - ThumbnailHash string - + Name string + Path string + DataHash string + ThumbnailHash string + LookupHash string // ChunkSize chunk size ChunkSize int64 //UploadLength indicates the size of the entire upload in bytes. The value MUST be a non-negative integer. @@ -23,19 +22,16 @@ type FileInputData struct { //Upload-Offset indicates a byte offset within a resource. The value MUST be a non-negative integer. UploadOffset int64 //IsFinal the request is final chunk - IsFinal bool - IsThumbnail bool - FilePathHash string - Size int64 - Hasher *CommitHasher + IsFinal bool + IsThumbnail bool + Size int64 + Hasher *CommitHasher } type FileOutputData struct { - Name string - Path string - ValidationRoot string - FixedMerkleRoot string - ThumbnailHash string + Name string + Path string + ThumbnailHash string // Size written size/chunk size Size int64 // ChunkUploaded the chunk is uploaded or not. @@ -57,6 +53,7 @@ type FileStorer interface { DeleteFromFilestore(allocID, hash string, version int) error DeletePreCommitDir(allocID string) error DeleteAllocation(allocID string) + CopyFile(allocationID, oldFileLookupHash, newFileLookupHash string) error // GetFileBlock Get blocks of file starting from blockNum upto numBlocks. blockNum can't be less than 1. GetFileBlock(readBlockIn *ReadBlockInput) (*FileDownloadResponse, error) GetBlocksMerkleTreeForChallenge(cri *ChallengeReadBlockInput) (*ChallengeResponse, error) diff --git a/code/go/0chain.net/blobbercore/filestore/store_test.go b/code/go/0chain.net/blobbercore/filestore/store_test.go index ad8a74dc1..21e4da514 100644 --- a/code/go/0chain.net/blobbercore/filestore/store_test.go +++ b/code/go/0chain.net/blobbercore/filestore/store_test.go @@ -21,7 +21,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/seqpriorityqueue" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/util" + "github.com/0chain/gosdk_common/core/util" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -251,16 +251,14 @@ func TestStoreStorageWriteAndCommit(t *testing.T) { validationRoot, fixedMerkleRoot, err := generateRandomData(fPath, int64(size)) require.Nil(t, err) pathHash := encryption.Hash(test.remotePath) - hasher := GetNewCommitHasher(0) + hasher := NewCommitHasher(0) fid := &FileInputData{ - Name: test.fileName, - Path: test.remotePath, - ValidationRoot: validationRoot, - FixedMerkleRoot: fixedMerkleRoot, - ChunkSize: 64 * KB, - FilePathHash: pathHash, - Hasher: hasher, - Size: int64(size), + Name: test.fileName, + Path: test.remotePath, + ChunkSize: 64 * KB, + FilePathHash: pathHash, + Hasher: hasher, + Size: int64(size), } f, err := os.Open(fPath) @@ -338,7 +336,7 @@ func TestDeletePreCommitDir(t *testing.T) { validationRoot, fixedMerkleRoot, err := generateRandomData(fPath, int64(size)) require.Nil(t, err) pathHash := encryption.Hash(remotePath) - hasher := GetNewCommitHasher(int64(size)) + hasher := NewCommitHasher(int64(size)) fid := &FileInputData{ Name: fileName, Path: remotePath, @@ -383,7 +381,7 @@ func TestDeletePreCommitDir(t *testing.T) { fid.ValidationRoot = validationRoot fid.FixedMerkleRoot = fixedMerkleRoot - hasher = GetNewCommitHasher(int64(size)) + hasher = NewCommitHasher(int64(size)) fid.Hasher = hasher // Write file to temp location @@ -446,7 +444,7 @@ func TestStorageUploadUpdate(t *testing.T) { validationRoot, fixedMerkleRoot, err := generateRandomData(fPath, int64(size)) require.Nil(t, err) pathHash := encryption.Hash(remotePath) - hasher := GetNewCommitHasher(int64(size)) + hasher := NewCommitHasher(int64(size)) fid := &FileInputData{ Name: fileName, Path: remotePath, @@ -834,7 +832,7 @@ func TestValidationRoot(t *testing.T) { fs, cleanUp := setupStorage(t) defer cleanUp() fPath := filepath.Join(fs.mp, randString(10)+".txt") - cH := GetNewCommitHasher(size) + cH := NewCommitHasher(size) _, err := cH.Write(thumbnailBytes) require.Nil(t, err) @@ -897,7 +895,7 @@ func generateRandomData(fPath string, size int64) (string, string, error) { } defer f.Close() - cH := GetNewCommitHasher(size) + cH := NewCommitHasher(size) _, err = cH.Write(p) if err != nil { return "", "", err @@ -938,7 +936,7 @@ func generateRandomDataAndStoreNodes(fPath string, size int64) (string, string, } defer f.Close() - cH := GetNewCommitHasher(size) + cH := NewCommitHasher(size) _, err = cH.Write(p) if err != nil { return "", "", err diff --git a/code/go/0chain.net/blobbercore/filestore/tree_validation.go b/code/go/0chain.net/blobbercore/filestore/tree_validation.go index 75856d485..b56a742ff 100644 --- a/code/go/0chain.net/blobbercore/filestore/tree_validation.go +++ b/code/go/0chain.net/blobbercore/filestore/tree_validation.go @@ -5,9 +5,11 @@ package filestore import ( "context" + "crypto/md5" "encoding/hex" "errors" "fmt" + "hash" "io" "math" "os" @@ -15,7 +17,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/seqpriorityqueue" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/util" + "github.com/0chain/gosdk_common/core/util" "github.com/minio/sha256-simd" "go.uber.org/zap" ) @@ -406,16 +408,24 @@ func getNewValidationTree(dataSize int64) *validationTree { type CommitHasher struct { fmt *fixedMerkleTree vt *validationTree + md5hasher hash.Hash isInitialized bool doneChan chan struct{} hashErr error dataSize int64 } -func GetNewCommitHasher(dataSize int64) *CommitHasher { +var ( + md5Pool = &sync.Pool{ + New: func() interface{} { + return md5.New() + }, + } +) + +func NewCommitHasher(dataSize int64) *CommitHasher { c := new(CommitHasher) - c.fmt = getNewFixedMerkleTree() - c.vt = getNewValidationTree(dataSize) + c.md5hasher = md5Pool.Get().(hash.Hash) c.isInitialized = true c.doneChan = make(chan struct{}) c.dataSize = dataSize @@ -434,7 +444,7 @@ func (c *CommitHasher) Start(ctx context.Context, connID, allocID, fileName, fil defer f.Close() var toFinalize bool var totalWritten int64 - + logging.Logger.Info("hasher_start", zap.String("fileHash", filePathHash), zap.String("fileName", fileName), zap.String("tempFilePath", tempFilePath)) for { select { case <-ctx.Done(): @@ -456,7 +466,6 @@ func (c *CommitHasher) Start(ctx context.Context, connID, allocID, fileName, fil } else if pq.DataBytes == 0 { continue } - logging.Logger.Info("hasher_pop", zap.Int64("offset", pq.Offset), zap.Int64("dataBytes", pq.DataBytes), zap.Any("toFinalize", toFinalize), zap.Int64("dataSize", c.dataSize), zap.String("filename", fileName), zap.Int64("totalWritten", totalWritten)) bufSize := 2 * BufferSize if pq.DataBytes < int64(bufSize) { bufSize = int(pq.DataBytes) @@ -474,7 +483,7 @@ func (c *CommitHasher) Start(ctx context.Context, connID, allocID, fileName, fil pq.DataBytes -= int64(n) pq.Offset += int64(n) totalWritten += int64(n) - _, err = c.Write(buf[:n]) + _, err = c.md5hasher.Write(buf[:n]) if err != nil { logging.Logger.Error("hasher_write", zap.Error(err), zap.Int("n", n), zap.Int64("offset", pq.Offset), zap.Int64("dataBytes", pq.DataBytes), zap.Int64("dataSize", c.dataSize), zap.String("filename", fileName), zap.Int64("totalWritten", totalWritten)) c.hashErr = err @@ -483,13 +492,12 @@ func (c *CommitHasher) Start(ctx context.Context, connID, allocID, fileName, fil } buf = nil if toFinalize { - c.hashErr = c.Finalize() return } } } -func (c *CommitHasher) Wait(ctx context.Context, connID, allocID, fileName, filePathHash string) error { +func (c *CommitHasher) Wait(ctx context.Context) error { select { case <-c.doneChan: return c.hashErr @@ -568,3 +576,10 @@ func (c *CommitHasher) GetFixedMerkleRoot() string { func (c *CommitHasher) GetValidationMerkleRoot() string { return hex.EncodeToString(c.vt.GetValidationRoot()) } + +func (c *CommitHasher) GetMd5Hash() string { + hash := hex.EncodeToString(c.md5hasher.Sum(nil)) + c.md5hasher.Reset() + md5Pool.Put(c.md5hasher) + return hash +} diff --git a/code/go/0chain.net/blobbercore/filestore/tree_validation_bench_test.go b/code/go/0chain.net/blobbercore/filestore/tree_validation_bench_test.go index 4d12fcfff..17c218d0b 100644 --- a/code/go/0chain.net/blobbercore/filestore/tree_validation_bench_test.go +++ b/code/go/0chain.net/blobbercore/filestore/tree_validation_bench_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/0chain/gosdk/core/util" + "github.com/0chain/gosdk_common/core/util" "github.com/minio/sha256-simd" ) diff --git a/code/go/0chain.net/blobbercore/filestore/tree_validation_test.go b/code/go/0chain.net/blobbercore/filestore/tree_validation_test.go index 5f10ac11c..7f16119e5 100644 --- a/code/go/0chain.net/blobbercore/filestore/tree_validation_test.go +++ b/code/go/0chain.net/blobbercore/filestore/tree_validation_test.go @@ -11,7 +11,7 @@ import ( "testing" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" - "github.com/0chain/gosdk/core/util" + "github.com/0chain/gosdk_common/core/util" "github.com/stretchr/testify/require" ) diff --git a/code/go/0chain.net/blobbercore/handler/auth_ticket.go b/code/go/0chain.net/blobbercore/handler/auth_ticket.go index 89032880c..faa119c08 100644 --- a/code/go/0chain.net/blobbercore/handler/auth_ticket.go +++ b/code/go/0chain.net/blobbercore/handler/auth_ticket.go @@ -2,9 +2,14 @@ package handler import ( "context" + "fmt" + "net/http" + + "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/common/core/common" - "net/http" + "github.com/0chain/gosdk_common/core/encryption" + "go.uber.org/zap" ) // swagger:model AuthTicketResponse @@ -19,28 +24,38 @@ type AuthTicketResponse struct { // // parameters: // -// +name: Zbox-Signature -// in: header -// type: string -// description: Digital signature to verify that the sender is 0box service. -// +name: client_id -// type: string -// in: query -// description: Client ID is used as a payload to the token generated. The token represents a signed version of this string by the blobber's private key. +// +name: Zbox-Signature +// in: header +// type: string +// description: Digital signature to verify that the sender is 0box service. +// +name: client_id +// type: string +// in: query +// description: Client ID is used as a payload to the token generated. The token represents a signed version of this string by the blobber's private key. // // responses: -// 200: AuthTicketResponse +// +// 200: AuthTicketResponse func GenerateAuthTicket(ctx context.Context, r *http.Request) (interface{}, error) { + clientID := r.URL.Query().Get("client_id") if clientID == "" { return nil, common.NewError("missing_client_id", "client_id is required") } - signature, err := node.Self.Sign(clientID) + round := r.URL.Query().Get("round") + if round == "" { + return nil, common.NewError("missing_round", "round is required") + } + + payload := encryption.Hash(fmt.Sprintf("%s_%s", clientID, round)) + + logging.Logger.Info("GenerateAuthTicket", zap.String("payload", payload), zap.String("client_id", clientID), zap.String("round", round)) + + signature, err := node.Self.Sign(payload) if err != nil { return nil, common.NewError("signature_failed", "signature failed") } - return &AuthTicketResponse{ AuthTicket: signature, }, nil diff --git a/code/go/0chain.net/blobbercore/handler/chunk_encoder.go b/code/go/0chain.net/blobbercore/handler/chunk_encoder.go index 6665b9dac..97e0d8f7d 100644 --- a/code/go/0chain.net/blobbercore/handler/chunk_encoder.go +++ b/code/go/0chain.net/blobbercore/handler/chunk_encoder.go @@ -4,7 +4,7 @@ import ( "bytes" "errors" - zencryption "github.com/0chain/gosdk/zboxcore/encryption" + zencryption "github.com/0chain/gosdk_common/zboxcore/encryption" ) // ChunkEncoder encode/decode chunk data diff --git a/code/go/0chain.net/blobbercore/handler/client_quota.go b/code/go/0chain.net/blobbercore/handler/client_quota.go index cee45a830..4335192d4 100644 --- a/code/go/0chain.net/blobbercore/handler/client_quota.go +++ b/code/go/0chain.net/blobbercore/handler/client_quota.go @@ -8,6 +8,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" + "go.uber.org/zap" "gorm.io/gorm" ) @@ -84,6 +86,7 @@ func AddWriteMarkerCount(clientID string, zeroSizeWM bool) { cs.TotalZeroWM++ } if cs.TotalZeroWM > config.Configuration.CommitZeroLimitDaily || cs.TotalWM > config.Configuration.CommitLimitDaily { + logging.Logger.Info("Client blacklisted", zap.String("client_id", clientID), zap.Int64("total_write_marker", cs.TotalWM), zap.Int64("total_zero_write_marker", cs.TotalZeroWM), zap.Int64("commit_limit_daily", config.Configuration.CommitLimitDaily), zap.Int64("commit_zero_limit_daily", config.Configuration.CommitZeroLimitDaily)) SetBlacklist(clientID) } } @@ -136,6 +139,7 @@ func saveClientStats() { } func startBlackListWorker(ctx context.Context) { + logging.Logger.Info("Starting black list worker", zap.Int64("upload_limit", config.Configuration.UploadLimitMonthly), zap.Int64("download_limit", config.Configuration.BlockLimitMonthly), zap.Int64("commit_limit", config.Configuration.CommitLimitMonthly), zap.Int64("commit_zero_limit", config.Configuration.CommitZeroLimitDaily), zap.Int64("commit_limit_daily", config.Configuration.CommitLimitDaily)) BlackListWorkerTime := 24 * time.Hour if config.Development() { BlackListWorkerTime = 10 * time.Second diff --git a/code/go/0chain.net/blobbercore/handler/file_command_delete.go b/code/go/0chain.net/blobbercore/handler/file_command_delete.go index 38497b283..2fcdebc96 100644 --- a/code/go/0chain.net/blobbercore/handler/file_command_delete.go +++ b/code/go/0chain.net/blobbercore/handler/file_command_delete.go @@ -4,8 +4,9 @@ import ( "context" "errors" "net/http" + "path/filepath" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "gorm.io/gorm" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" @@ -41,6 +42,10 @@ func (cmd *DeleteFileCommand) IsValidated(ctx context.Context, req *http.Request return common.NewError("invalid_parameters", "Invalid path") } + if filepath.Clean(path) != path { + return common.NewError("invalid_parameters", "Invalid path") + } + cmd.path = path connectionID, ok := common.GetField(req, "connection_id") @@ -50,13 +55,23 @@ func (cmd *DeleteFileCommand) IsValidated(ctx context.Context, req *http.Request cmd.connectionID = connectionID var err error lookUpHash := reference.GetReferenceLookup(allocationObj.ID, path) - cmd.existingFileRef, err = reference.GetLimitedRefFieldsByLookupHashWith(ctx, allocationObj.ID, lookUpHash, []string{"path", "name", "size", "hash", "fixed_merkle_root"}) + cmd.existingFileRef, err = reference.GetLimitedRefFieldsByLookupHashWith(ctx, allocationObj.ID, lookUpHash, []string{"path", "name", "type", "id", "size"}) if err != nil { if errors.Is(gorm.ErrRecordNotFound, err) { return common.ErrFileWasDeleted } return common.NewError("bad_db_operation", err.Error()) } + if cmd.existingFileRef.Type == reference.DIRECTORY { + // check if directory is empty + empty, err := reference.IsDirectoryEmpty(ctx, cmd.existingFileRef.ID) + if err != nil { + return common.NewError("bad_db_operation", err.Error()) + } + if !empty { + return common.NewError("invalid_operation", "Directory is not empty") + } + } cmd.existingFileRef.LookupHash = lookUpHash return nil } @@ -82,12 +97,10 @@ func (cmd *DeleteFileCommand) ProcessContent(_ context.Context, allocationObj *a connectionID := cmd.connectionID cmd.changeProcessor = &allocation.DeleteFileChange{ConnectionID: connectionID, AllocationID: allocationObj.ID, Name: cmd.existingFileRef.Name, - Hash: cmd.existingFileRef.Hash, Path: cmd.existingFileRef.Path, Size: deleteSize} + LookupHash: cmd.existingFileRef.LookupHash, Path: cmd.existingFileRef.Path, Size: deleteSize, Type: cmd.existingFileRef.Type} result := allocation.UploadResult{} result.Filename = cmd.existingFileRef.Name - result.ValidationRoot = cmd.existingFileRef.ValidationRoot - result.FixedMerkleRoot = cmd.existingFileRef.FixedMerkleRoot result.Size = cmd.existingFileRef.Size result.UpdateChange = true diff --git a/code/go/0chain.net/blobbercore/handler/file_command_update.go b/code/go/0chain.net/blobbercore/handler/file_command_update.go index 1b1daf5d4..1bfe4df13 100644 --- a/code/go/0chain.net/blobbercore/handler/file_command_update.go +++ b/code/go/0chain.net/blobbercore/handler/file_command_update.go @@ -14,9 +14,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/blobber/code/go/0chain.net/core/encryption" - sdkConst "github.com/0chain/gosdk/constants" - "github.com/0chain/gosdk/zboxcore/fileref" + sdkConst "github.com/0chain/gosdk_common/constants" + "github.com/0chain/gosdk_common/zboxcore/fileref" ) const ( @@ -72,19 +71,19 @@ func (cmd *UpdateFileCommand) IsValidated(ctx context.Context, req *http.Request return common.NewError("invalid_connection", "Invalid connection id") } - cmd.fileChanger.PathHash = encryption.Hash(cmd.fileChanger.Path) + cmd.fileChanger.LookupHash = reference.GetReferenceLookup(allocationObj.ID, cmd.fileChanger.Path) if cmd.fileChanger.ChunkSize <= 0 { cmd.fileChanger.ChunkSize = fileref.CHUNK_SIZE } - cmd.existingFileRef = allocation.GetExistingRef(cmd.fileChanger.ConnectionID, cmd.fileChanger.PathHash) + cmd.existingFileRef = allocation.GetExistingRef(cmd.fileChanger.ConnectionID, cmd.fileChanger.LookupHash) if cmd.existingFileRef == nil { cmd.existingFileRef, _ = reference.GetReference(ctx, allocationObj.ID, cmd.fileChanger.Path) if cmd.existingFileRef == nil { return common.NewError("invalid_file_update", "File at path does not exist for update") } - allocation.SaveExistingRef(cmd.fileChanger.ConnectionID, cmd.fileChanger.PathHash, cmd.existingFileRef) //nolint:errcheck + allocation.SaveExistingRef(cmd.fileChanger.ConnectionID, cmd.fileChanger.LookupHash, cmd.existingFileRef) //nolint:errcheck } thumbFile, thumbHeader, _ := req.FormFile(UploadThumbnailFile) @@ -112,7 +111,7 @@ func (cmd *UpdateFileCommand) ProcessContent(ctx context.Context, allocationObj result.Filename = cmd.fileChanger.Filename defer cmd.contentFile.Close() - filePathHash := cmd.fileChanger.PathHash + filePathHash := cmd.fileChanger.LookupHash connID := cmd.fileChanger.ConnectionID fileInputData := &filestore.FileInputData{ @@ -120,7 +119,7 @@ func (cmd *UpdateFileCommand) ProcessContent(ctx context.Context, allocationObj Path: cmd.fileChanger.Path, UploadOffset: cmd.fileChanger.UploadOffset, IsFinal: cmd.fileChanger.IsFinal, - FilePathHash: filePathHash, + LookupHash: filePathHash, Size: cmd.fileChanger.Size, } fileOutputData, err := filestore.GetFileStore().WriteFile(allocationObj.ID, connID, fileInputData, cmd.contentFile) @@ -128,8 +127,6 @@ func (cmd *UpdateFileCommand) ProcessContent(ctx context.Context, allocationObj return result, common.NewError("upload_error", "Failed to upload the file. "+err.Error()) } - result.ValidationRoot = fileOutputData.ValidationRoot - result.FixedMerkleRoot = fileOutputData.FixedMerkleRoot result.Size = fileOutputData.Size cmd.fileChanger.AllocationID = allocationObj.ID @@ -156,7 +153,7 @@ func (cmd *UpdateFileCommand) ProcessContent(ctx context.Context, allocationObj } } - saveChange, err := allocation.SaveFileChange(ctx, connID, cmd.fileChanger.PathHash, cmd.fileChanger.Filename, cmd, cmd.fileChanger.IsFinal, cmd.fileChanger.Size, cmd.fileChanger.UploadOffset, fileOutputData.Size, cmd.fileChanger.Size-cmd.existingFileRef.Size) + saveChange, err := allocation.SaveFileChange(ctx, connID, cmd.fileChanger.LookupHash, cmd.fileChanger.Filename, cmd, cmd.fileChanger.IsFinal, cmd.fileChanger.Size, cmd.fileChanger.UploadOffset, fileOutputData.Size, cmd.fileChanger.Size-cmd.existingFileRef.Size) if err != nil { return result, err } @@ -182,7 +179,7 @@ func (cmd *UpdateFileCommand) ProcessThumbnail(allocationObj *allocation.Allocat connectionID := cmd.fileChanger.ConnectionID if cmd.thumbHeader != nil { defer cmd.thumbFile.Close() - thumbInputData := &filestore.FileInputData{Name: cmd.thumbHeader.Filename, Path: cmd.fileChanger.Path, IsThumbnail: true, FilePathHash: cmd.fileChanger.PathHash} + thumbInputData := &filestore.FileInputData{Name: cmd.thumbHeader.Filename, Path: cmd.fileChanger.Path, IsThumbnail: true, LookupHash: cmd.fileChanger.LookupHash} thumbOutputData, err := filestore.GetFileStore().WriteFile(allocationObj.ID, connectionID, thumbInputData, cmd.thumbFile) if err != nil { return common.NewError("upload_error", "Failed to upload the thumbnail. "+err.Error()) @@ -196,7 +193,7 @@ func (cmd *UpdateFileCommand) ProcessThumbnail(allocationObj *allocation.Allocat } func (cmd *UpdateFileCommand) reloadChange() { - changer := allocation.GetFileChanger(cmd.fileChanger.ConnectionID, cmd.fileChanger.PathHash) + changer := allocation.GetFileChanger(cmd.fileChanger.ConnectionID, cmd.fileChanger.LookupHash) if changer != nil && changer.ThumbnailHash != "" { cmd.fileChanger.ThumbnailFilename = changer.ThumbnailFilename cmd.fileChanger.ThumbnailSize = changer.ThumbnailSize diff --git a/code/go/0chain.net/blobbercore/handler/file_command_upload.go b/code/go/0chain.net/blobbercore/handler/file_command_upload.go index 88fea77f9..4dbd00544 100644 --- a/code/go/0chain.net/blobbercore/handler/file_command_upload.go +++ b/code/go/0chain.net/blobbercore/handler/file_command_upload.go @@ -17,10 +17,9 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/constants" - "github.com/0chain/gosdk/zboxcore/fileref" + "github.com/0chain/gosdk_common/constants" + "github.com/0chain/gosdk_common/zboxcore/fileref" ) const ( @@ -77,25 +76,15 @@ func (cmd *UploadFileCommand) IsValidated(ctx context.Context, req *http.Request return common.NewError("invalid_path", fmt.Sprintf("%v is not absolute path", fileChanger.Path)) } + if filepath.Clean(fileChanger.Path) != fileChanger.Path { + return common.NewError("invalid_path", fmt.Sprintf("%v is not a clean path", fileChanger.Path)) + } + if fileChanger.ConnectionID == "" { return common.NewError("invalid_connection", "Invalid connection id") } - fileChanger.PathHash = encryption.Hash(fileChanger.Path) - - if fileChanger.UploadOffset == 0 { - isExist, err := reference.IsRefExist(ctx, allocationObj.ID, fileChanger.Path) - - if err != nil { - logging.Logger.Error(err.Error()) - return common.NewError("database_error", "Got db error while getting ref") - } - - if isExist { - msg := fmt.Sprintf("File at path :%s: already exists", fileChanger.Path) - return common.NewError("duplicate_file", msg) - } - } + fileChanger.LookupHash = reference.GetReferenceLookup(allocationObj.ID, fileChanger.Path) thumbFile, thumbHeader, _ := req.FormFile(UploadThumbnailFile) if thumbHeader != nil { @@ -111,6 +100,9 @@ func (cmd *UploadFileCommand) IsValidated(ctx context.Context, req *http.Request if fileChanger.ChunkSize <= 0 { fileChanger.ChunkSize = fileref.CHUNK_SIZE } + if allocationObj.CanUpdate() { + fileChanger.CanUpdate = true + } origfile, _, err := req.FormFile(UploadFile) if err != nil { @@ -135,7 +127,7 @@ func (cmd *UploadFileCommand) ProcessContent(ctx context.Context, allocationObj ChunkSize: cmd.fileChanger.ChunkSize, UploadOffset: cmd.fileChanger.UploadOffset, IsFinal: cmd.fileChanger.IsFinal, - FilePathHash: cmd.fileChanger.PathHash, + LookupHash: cmd.fileChanger.LookupHash, Size: cmd.fileChanger.Size, } fileOutputData, err := filestore.GetFileStore().WriteFile(allocationObj.ID, connectionID, fileInputData, cmd.contentFile) @@ -144,10 +136,8 @@ func (cmd *UploadFileCommand) ProcessContent(ctx context.Context, allocationObj return result, common.NewError("upload_error", "Failed to write file. "+err.Error()) } result.Filename = cmd.fileChanger.Filename - result.ValidationRoot = fileOutputData.ValidationRoot result.Size = fileOutputData.Size - allocationSize := allocation.GetConnectionObjSize(connectionID) + cmd.fileChanger.Size cmd.fileChanger.AllocationID = allocationObj.ID cmd.allocationChange = &allocation.AllocationChange{} @@ -171,7 +161,7 @@ func (cmd *UploadFileCommand) ProcessContent(ctx context.Context, allocationObj } } - saveChange, err := allocation.SaveFileChange(ctx, connectionID, cmd.fileChanger.PathHash, cmd.fileChanger.Filename, cmd, cmd.fileChanger.IsFinal, cmd.fileChanger.Size, cmd.fileChanger.UploadOffset, fileOutputData.Size, cmd.fileChanger.Size) + saveChange, err := allocation.SaveFileChange(ctx, connectionID, cmd.fileChanger.LookupHash, cmd.fileChanger.Filename, cmd, cmd.fileChanger.IsFinal, cmd.fileChanger.Size, cmd.fileChanger.UploadOffset, fileOutputData.Size, cmd.fileChanger.Size) if err != nil { logging.Logger.Error("UploadFileCommand.ProcessContent", zap.Error(err)) return result, err @@ -185,6 +175,7 @@ func (cmd *UploadFileCommand) ProcessContent(ctx context.Context, allocationObj return result, err } } + allocationSize := allocation.GetConnectionObjSize(connectionID) if allocationObj.BlobberSizeUsed+allocationSize > allocationObj.BlobberSize { return result, common.NewError("max_allocation_size", "Max size reached for the allocation with this blobber") @@ -195,12 +186,12 @@ func (cmd *UploadFileCommand) ProcessContent(ctx context.Context, allocationObj // ProcessThumbnail flush thumbnail file to FileStorage if it has. func (cmd *UploadFileCommand) ProcessThumbnail(allocationObj *allocation.Allocation) error { - logging.Logger.Info("ProcessThumbnail: ", zap.String("allocationID: ", cmd.fileChanger.AllocationID)) + connectionID := cmd.fileChanger.ConnectionID if cmd.thumbHeader != nil { defer cmd.thumbFile.Close() - thumbInputData := &filestore.FileInputData{Name: cmd.thumbHeader.Filename, Path: cmd.fileChanger.Path, IsThumbnail: true, FilePathHash: cmd.fileChanger.PathHash} + thumbInputData := &filestore.FileInputData{Name: cmd.thumbHeader.Filename, Path: cmd.fileChanger.Path, IsThumbnail: true, LookupHash: cmd.fileChanger.LookupHash} thumbOutputData, err := filestore.GetFileStore().WriteFile(allocationObj.ID, connectionID, thumbInputData, cmd.thumbFile) if err != nil { return common.NewError("upload_error", "Failed to upload the thumbnail. "+err.Error()) @@ -214,7 +205,7 @@ func (cmd *UploadFileCommand) ProcessThumbnail(allocationObj *allocation.Allocat } func (cmd *UploadFileCommand) reloadChange() { - changer := allocation.GetFileChanger(cmd.fileChanger.ConnectionID, cmd.fileChanger.PathHash) + changer := allocation.GetFileChanger(cmd.fileChanger.ConnectionID, cmd.fileChanger.LookupHash) if changer != nil && changer.ThumbnailHash != "" { cmd.fileChanger.ThumbnailFilename = changer.ThumbnailFilename cmd.fileChanger.ThumbnailSize = changer.ThumbnailSize @@ -234,6 +225,7 @@ func (cmd *UploadFileCommand) AddChange(ctx context.Context) error { connectionInput, _ := cmd.fileChanger.Marshal() cmd.allocationChange.LookupHash = reference.GetReferenceLookup(cmd.fileChanger.AllocationID, cmd.fileChanger.Path) cmd.allocationChange.Input = connectionInput + logging.Logger.Info("AddChange: ", zap.String("connectionID", cmd.allocationChange.ConnectionID), zap.String("lookupHash", cmd.allocationChange.LookupHash)) return cmd.allocationChange.Create(ctx) } diff --git a/code/go/0chain.net/blobbercore/handler/handler.go b/code/go/0chain.net/blobbercore/handler/handler.go index cc110b268..9f478547e 100644 --- a/code/go/0chain.net/blobbercore/handler/handler.go +++ b/code/go/0chain.net/blobbercore/handler/handler.go @@ -14,14 +14,14 @@ import ( "strings" "time" - "github.com/0chain/gosdk/core/zcncrypto" + "github.com/0chain/gosdk_common/core/zcncrypto" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" "github.com/go-openapi/runtime/middleware" - "github.com/0chain/gosdk/constants" - "github.com/0chain/gosdk/zboxcore/fileref" + "github.com/0chain/gosdk_common/constants" + "github.com/0chain/gosdk_common/zboxcore/fileref" "github.com/didip/tollbooth/v6/limiter" "github.com/gorilla/mux" "github.com/spf13/viper" @@ -285,7 +285,6 @@ func WithReadOnlyConnection(handler common.JSONResponderF) common.JSONResponderF defer func() { tx.Rollback() }() - res, err := handler(ctx, r) return res, err } @@ -312,21 +311,21 @@ func Authenticate0Box(handler common.ReqRespHandlerf) common.ReqRespHandlerf { signature := r.Header.Get("Zbox-Signature") if signature == "" { w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Invalid signature")) // nolint + w.Write([]byte("Invalid signature " + signature)) // nolint return } signatureScheme := zcncrypto.NewSignatureScheme(config.Configuration.SignatureScheme) if err := signatureScheme.SetPublicKey(common.PublicKey0box); err != nil { w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Invalid signature")) // nolint + w.Write([]byte("Invalid signature 2")) // nolint return } success, err := signatureScheme.Verify(signature, hex.EncodeToString([]byte(common.PublicKey0box))) if err != nil || !success { w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Invalid signature")) // nolint + w.Write([]byte("Invalid signature 3" + common.PublicKey0box)) // nolint return } diff --git a/code/go/0chain.net/blobbercore/handler/handler_common.go b/code/go/0chain.net/blobbercore/handler/handler_common.go index ebd6a9b64..340a82378 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_common.go +++ b/code/go/0chain.net/blobbercore/handler/handler_common.go @@ -8,14 +8,12 @@ import ( "time" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/writemarker" "github.com/0chain/blobber/code/go/0chain.net/core/build" "github.com/0chain/blobber/code/go/0chain.net/core/chain" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/lock" "github.com/0chain/blobber/code/go/0chain.net/core/node" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/core/client" "go.uber.org/zap" . "github.com/0chain/blobber/code/go/0chain.net/core/logging" @@ -79,16 +77,19 @@ func HomepageHandler(w http.ResponseWriter, r *http.Request) { ) fmt.Fprintf(w, "
Miners ...\n") - network := zcncore.GetNetwork() - for _, miner := range network.Miners { - fmt.Fprintf(w, "%v\n", miner) - } - fmt.Fprintf(w, "
\n") - fmt.Fprintf(w, "
Sharders ...\n") - for _, sharder := range network.Sharders { - fmt.Fprintf(w, "%v\n", sharder) + network, err := client.GetNetwork(context.Background()) + if err == nil { + fmt.Fprintf(w, "
Miners ...\n") + for _, miner := range network.Miners { + fmt.Fprintf(w, "%v\n", miner) + } + fmt.Fprintf(w, "
\n") + fmt.Fprintf(w, "
Sharders ...\n") + for _, sharder := range network.Sharders { + fmt.Fprintf(w, "%v\n", sharder) + } + fmt.Fprintf(w, "
\n") } - fmt.Fprintf(w, "
\n") fmt.Fprintf(w, "
") fmt.Fprintf(w, "
Running since %v (Total elapsed time: %v)
\n", StartTime.Format(common.DateTimeFormat), time.Since(StartTime)) fmt.Fprintf(w, "
") @@ -127,10 +128,6 @@ func WithStatusConnectionForWM(handler common.StatusCodeResponderF) common.Statu mutex := lock.GetMutex(allocation.Allocation{}.TableName(), allocationID) Logger.Info("Locking allocation", zap.String("allocation_id", allocationID)) - wmSet := writemarker.SetCommittingMarker(allocationID, true) - if !wmSet { - return nil, http.StatusBadRequest, common.NewError("pending_markers", "Committing marker set failed") - } mutex.Lock() defer mutex.Unlock() ctx = GetMetaDataStore().CreateTransaction(ctx) @@ -144,7 +141,6 @@ func WithStatusConnectionForWM(handler common.StatusCodeResponderF) common.Statu if rollErr != nil { Logger.Error("couldn't rollback", zap.Error(err)) } - writemarker.SetCommittingMarker(allocationID, false) } }() @@ -160,14 +156,6 @@ func WithStatusConnectionForWM(handler common.StatusCodeResponderF) common.Statu } Logger.Info("commit_success", zap.String("allocation_id", allocationID), zap.Any("response", resp)) - - if blobberRes, ok := resp.(*blobberhttp.CommitResult); ok { - // Save the write marker data - writemarker.SaveMarkerData(allocationID, blobberRes.WriteMarker.WM.Timestamp, blobberRes.WriteMarker.WM.ChainLength) - } else { - Logger.Error("Invalid response type for commit handler") - return resp, http.StatusInternalServerError, common.NewError("invalid_response_type", "Invalid response type for commit handler") - } return } } diff --git a/code/go/0chain.net/blobbercore/handler/handler_download_test.go b/code/go/0chain.net/blobbercore/handler/handler_download_test.go index 70a0e5dfe..e02abab31 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_download_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_download_test.go @@ -14,11 +14,11 @@ import ( "testing" "time" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" - zencryption "github.com/0chain/gosdk/zboxcore/encryption" - "github.com/0chain/gosdk/zboxcore/fileref" - "github.com/0chain/gosdk/zboxcore/zboxutil" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" + zencryption "github.com/0chain/gosdk_common/zboxcore/encryption" + "github.com/0chain/gosdk_common/zboxcore/fileref" + "github.com/0chain/gosdk_common/zboxcore/zboxutil" "github.com/DATA-DOG/go-sqlmock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -60,11 +60,8 @@ func TestHandlers_Download(t *testing.T) { clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` - require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) - clients := client.GetClients() - - ownerClient, guestClient := clients[0], clients[1] - + ownerClient, _ := client.PopulateClient(clientJson, "bls0chain") + guestClient, _ := client.PopulateClient(guestClientJson, "bls0chain") ownerScheme, err := getEncryptionScheme(ownerClient.Mnemonic) if err != nil { t.Fatal(err) diff --git a/code/go/0chain.net/blobbercore/handler/handler_middlewares.go b/code/go/0chain.net/blobbercore/handler/handler_middlewares.go index 424b4c99f..bd073d88e 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_middlewares.go +++ b/code/go/0chain.net/blobbercore/handler/handler_middlewares.go @@ -41,7 +41,7 @@ func UseRecovery(h http.Handler) http.Handler { defer func() { if err := recover(); err != nil { escapedUrl := sanitizeString(r.URL.String()) - logging.Logger.Error("[recover]http", zap.String("url", escapedUrl), zap.Any("err", err)) + logging.Logger.Error("[recover]http", zap.String("url", escapedUrl), zap.Any("err", err), zap.Stack("recover_stack")) } }() diff --git a/code/go/0chain.net/blobbercore/handler/handler_objecttree_test.go b/code/go/0chain.net/blobbercore/handler/handler_objecttree_test.go index 79ea68676..3217712c5 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_objecttree_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_objecttree_test.go @@ -4,6 +4,7 @@ package handler import ( + "encoding/json" "net/http" "net/http/httptest" "os" @@ -11,8 +12,8 @@ import ( "testing" "time" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" "github.com/DATA-DOG/go-sqlmock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -42,18 +43,18 @@ func TestHandlers_ObjectTree(t *testing.T) { setup(t) clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` - guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` - require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) - clients := client.GetClients() - - ownerClient := clients[0] + ownerClient := zcncrypto.Wallet{} + err := json.Unmarshal([]byte(clientJson), &ownerClient) + require.NoError(t, err) + client.SetWallet(ownerClient) + client.SetSignatureScheme("bls0chain") router, handlers := setupObjectTreeHandlers() sch := zcncrypto.NewSignatureScheme("bls0chain") //sch.Mnemonic = "expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe" - _, err := sch.RecoverKeys("expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe") + _, err = sch.RecoverKeys("expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe") if err != nil { t.Fatal(err) } diff --git a/code/go/0chain.net/blobbercore/handler/handler_refpath_test.go b/code/go/0chain.net/blobbercore/handler/handler_refpath_test.go index 04b47d2a5..5926bfcdf 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_refpath_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_refpath_test.go @@ -13,12 +13,11 @@ import ( "testing" "time" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" "github.com/DATA-DOG/go-sqlmock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" @@ -44,12 +43,8 @@ func TestHandlers_ReferencePath(t *testing.T) { setup(t) clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` - guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` - require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) - clients := client.GetClients() - - ownerClient := clients[0] + ownerClient, _ := client.PopulateClient(clientJson, "bls0chain") router, handlers := setupReferencePathHandlers() diff --git a/code/go/0chain.net/blobbercore/handler/handler_share_test.go b/code/go/0chain.net/blobbercore/handler/handler_share_test.go index 9a288997a..cb5fe690d 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_share_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_share_test.go @@ -15,9 +15,9 @@ import ( "testing" "time" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" - "github.com/0chain/gosdk/zboxcore/fileref" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" + "github.com/0chain/gosdk_common/zboxcore/fileref" "github.com/DATA-DOG/go-sqlmock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -61,13 +61,8 @@ func TestHandlers_Share(t *testing.T) { setup(t) clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` - guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` - - require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) - clients := client.GetClients() - - ownerClient := clients[0] + ownerClient, _ := client.PopulateClient(clientJson, "bls0chain") router, handlers := setupShareHandlers() sch := zcncrypto.NewSignatureScheme("bls0chain") diff --git a/code/go/0chain.net/blobbercore/handler/handler_test.go b/code/go/0chain.net/blobbercore/handler/handler_test.go index a7ed52633..ffba186ce 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_test.go @@ -18,13 +18,12 @@ import ( "github.com/DATA-DOG/go-sqlmock" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zboxcore/client" - "github.com/0chain/gosdk/zboxcore/fileref" - "github.com/0chain/gosdk/zboxcore/marker" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" + "github.com/0chain/gosdk_common/zboxcore/fileref" + "github.com/0chain/gosdk_common/zboxcore/marker" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" @@ -49,7 +48,7 @@ func resetMockFileBlock() { mockFileBlock = []byte("mock") } -func signHash(client *client.Client, hash string) (string, error) { +func signHash(client zcncrypto.Wallet, hash string) (string, error) { retSignature := "" for _, kv := range client.Keys { ss := zcncrypto.NewSignatureScheme("bls0chain") @@ -155,7 +154,7 @@ func isEndpointAllowGetReq(name string) bool { } } -func GetAuthTicketForEncryptedFile(ownerClient *client.Client, allocationID, remotePath, fileHash, clientID, encPublicKey string) (string, error) { +func GetAuthTicketForEncryptedFile(ownerClient zcncrypto.Wallet, allocationID, remotePath, fileHash, clientID, encPublicKey string) (string, error) { at := &marker.AuthTicket{} at.AllocationID = allocationID at.OwnerID = ownerClient.ClientID @@ -190,13 +189,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { setup(t) clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` - guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` - - require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) - clients := client.GetClients() - - ownerClient := clients[0] - + ownerClient, _ := client.PopulateClient(clientJson, "bls0chain") router, handlers := setupTestHandlers() sch := zcncrypto.NewSignatureScheme("bls0chain") diff --git a/code/go/0chain.net/blobbercore/handler/health.go b/code/go/0chain.net/blobbercore/handler/health.go index e4da97bf7..b0e406c44 100644 --- a/code/go/0chain.net/blobbercore/handler/health.go +++ b/code/go/0chain.net/blobbercore/handler/health.go @@ -7,6 +7,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" + coreTxn "github.com/0chain/gosdk_common/core/transaction" "go.uber.org/zap" ) @@ -29,48 +30,41 @@ func getBlobberHealthCheckError() error { return err } -func BlobberHealthCheck() (*transaction.Transaction, error) { +func BlobberHealthCheck() (string, error) { if config.Configuration.Capacity == 0 { setBlobberHealthCheckError(ErrBlobberHasRemoved) - return nil, ErrBlobberHasRemoved + return "", ErrBlobberHasRemoved } - txn, err := transaction.NewTransactionEntity() - if err != nil { - setBlobberHealthCheckError(err) - return nil, err - } - - err = txn.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, - transaction.BLOBBER_HEALTH_CHECK, common.Now(), 0) - if err != nil { + _, _, _, txn, err := coreTxn.SmartContractTxn(transaction.STORAGE_CONTRACT_ADDRESS, coreTxn.SmartContractTxnData{ + Name: transaction.BLOBBER_HEALTH_CHECK, + InputArgs: common.Now(), + }, true) + if err != nil || txn == nil { logging.Logger.Error("Failed to health check blobber on the blockchain", zap.Error(err)) setBlobberHealthCheckError(err) - return nil, err + return "", err } setBlobberHealthCheckError(nil) - return txn, nil + return txn.Hash, nil } -func ValidatorHealthCheck() (*transaction.Transaction, error) { - - txn, err := transaction.NewTransactionEntity() - - if err != nil { - - return nil, err - } +func ValidatorHealthCheck() (string, error) { + _, _, _, txn, err := coreTxn.SmartContractTxn(transaction.STORAGE_CONTRACT_ADDRESS, coreTxn.SmartContractTxnData{ + Name: transaction.VALIDATOR_HEALTH_CHECK, + InputArgs: common.Now(), + }, true) - if err = txn.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, transaction.VALIDATOR_HEALTH_CHECK, common.Now(), 0); err != nil { + if err != nil || txn == nil { logging.Logger.Error("Failed to health check validator on the blockchain", zap.Error(err)) - return nil, err + return "", err } - return txn, err + return txn.Hash, err } diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index 968eaa2a1..b73e45ea4 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -16,7 +16,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" @@ -88,14 +88,6 @@ func readPreRedeem( return } -func checkPendingMarkers(ctx context.Context, allocationID string) error { - pending := writemarker.CheckProcessingMarker(allocationID) - if pending { - return common.NewError("pending_markers", "previous marker is still pending to be redeemed") - } - return nil -} - func writePreRedeem(ctx context.Context, alloc *allocation.Allocation, writeMarker *writemarker.WriteMarker, payerID string) (err error) { // check out read pool tokens if read_price > 0 var ( @@ -462,22 +454,17 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (i return nil, common.NewErrorf("download_file", "BlockNum or NumBlocks is too large to convert to int") } - fromPreCommit := false + fromPreCommit := alloc.AllocationVersion == fileref.AllocationVersion if downloadMode == DownloadContentThumb { - - if fileref.IsPrecommit { - fromPreCommit = fileref.ThumbnailHash != fileref.PrevThumbnailHash - } - rbi := &filestore.ReadBlockInput{ AllocationID: alloc.ID, FileSize: fileref.ThumbnailSize, - Hash: fileref.ThumbnailHash, + Hash: fileref.LookupHash, StartBlockNum: int(dr.BlockNum), NumBlocks: int(dr.NumBlocks), IsThumbnail: true, IsPrecommit: fromPreCommit, - FilestoreVersion: fileref.FilestoreVersion, + FilestoreVersion: filestore.VERSION, } logging.Logger.Info("calling GetFileBlock for thumb", zap.Any("rbi", rbi)) @@ -486,20 +473,15 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (i return nil, common.NewErrorf("download_file", "couldn't get thumbnail block: %v", err) } } else { - - if fileref.IsPrecommit { - fromPreCommit = fileref.ValidationRoot != fileref.PrevValidationRoot - } - rbi := &filestore.ReadBlockInput{ AllocationID: alloc.ID, FileSize: fileref.Size, - Hash: fileref.ValidationRoot, + Hash: fileref.LookupHash, StartBlockNum: int(dr.BlockNum), NumBlocks: int(dr.NumBlocks), VerifyDownload: dr.VerifyDownload, IsPrecommit: fromPreCommit, - FilestoreVersion: fileref.FilestoreVersion, + FilestoreVersion: filestore.VERSION, } logging.Logger.Info("calling GetFileBlock", zap.Any("rbi", rbi)) fileDownloadResponse, err = filestore.GetFileStore().GetFileBlock(rbi) @@ -588,7 +570,6 @@ func (fsh *StorageHandler) CreateConnection(ctx context.Context, r *http.Request } func (fsh *StorageHandler) CommitWrite(ctx context.Context, r *http.Request) (*blobberhttp.CommitResult, error) { - var prevChainHash string startTime := time.Now() if r.Method == "GET" { return nil, common.NewError("invalid_method", "Invalid method used for the upload URL. Use POST instead") @@ -629,12 +610,6 @@ func (fsh *StorageHandler) CommitWrite(ctx context.Context, r *http.Request) (*b elapsedGetLock := time.Since(startTime) - elapsedAllocation - err = checkPendingMarkers(ctx, allocationObj.ID) - if err != nil { - Logger.Error("Error checking pending markers", zap.Error(err)) - return nil, common.NewError("pending_markers", "previous marker is still pending to be redeemed") - } - connectionObj, err := allocation.GetAllocationChanges(ctx, connectionID, allocationID, clientID) if err != nil { // might be good to check if blobber already has stored writemarker @@ -642,12 +617,12 @@ func (fsh *StorageHandler) CommitWrite(ctx context.Context, r *http.Request) (*b "Invalid connection id. Connection id was not found: %v", err) } if len(connectionObj.Changes) == 0 { - if connectionObj.Status == allocation.NewConnection { - return nil, common.NewError("invalid_parameters", - "Invalid connection id. Connection not found.") - } - return nil, common.NewError("invalid_parameters", - "Invalid connection id. Connection does not have any changes.") + logging.Logger.Info("commit_write_empty", zap.String("connection_id", connectionID)) + } + + if len(connectionObj.Changes) > config.Configuration.MaxConnectionChanges { + return nil, common.NewError("max_connection_changes", + "Max connection changes reached. A connection can only have "+fmt.Sprintf("%v", config.Configuration.MaxConnectionChanges)+" changes") } elapsedGetConnObj := time.Since(startTime) - elapsedAllocation - elapsedGetLock @@ -656,184 +631,87 @@ func (fsh *StorageHandler) CommitWrite(ctx context.Context, r *http.Request) (*b return nil, common.NewError("invalid_operation", "Operation needs to be performed by the owner of the allocation") } - writeMarkerString := r.FormValue("write_marker") - if writeMarkerString == "" { - return nil, common.NewError("invalid_parameters", "Invalid write marker passed") - } - writeMarker := writemarker.WriteMarker{} - err = json.Unmarshal([]byte(writeMarkerString), &writeMarker) - if err != nil { - return nil, common.NewErrorf("invalid_parameters", - "Invalid parameters. Error parsing the writemarker for commit: %v", - err) + if allocationObj.BlobberSizeUsed+connectionObj.Size > allocationObj.BlobberSize { + return nil, common.NewError("max_allocation_size", + "Max size reached for the allocation with this blobber") } var result blobberhttp.CommitResult - var latestWriteMarkerEntity *writemarker.WriteMarkerEntity - if allocationObj.AllocationRoot == "" { - latestWriteMarkerEntity = nil - } else { - latestWriteMarkerEntity, err = writemarker.GetWriteMarkerEntity(ctx, - allocationObj.AllocationRoot) - if err != nil { - return nil, common.NewErrorf("latest_write_marker_read_error", - "Error reading the latest write marker for allocation: %v", err) - } - if latestWriteMarkerEntity.Status == writemarker.Failed { - return nil, common.NewError("latest_write_marker_failed", - "Latest write marker is in failed state") - } - - if latestWriteMarkerEntity.Status != writemarker.Committed { - writeMarker.ChainLength = latestWriteMarkerEntity.WM.ChainLength - } - prevChainHash = latestWriteMarkerEntity.WM.ChainHash - } - - writemarkerEntity := &writemarker.WriteMarkerEntity{} - writemarkerEntity.WM = writeMarker - writemarkerEntity.WM.ChainLength += 1 - if writemarkerEntity.WM.ChainLength > config.Configuration.MaxChainLength { - return nil, common.NewError("chain_length_exceeded", "Chain length exceeded") - } - - err = writemarkerEntity.VerifyMarker(ctx, allocationObj, connectionObj, latestWriteMarkerEntity) - if err != nil { - result.AllocationRoot = allocationObj.AllocationRoot - result.ErrorMessage = "Verification of write marker failed: " + err.Error() - result.Success = false - if latestWriteMarkerEntity != nil { - result.WriteMarker = latestWriteMarkerEntity - } - Logger.Error("verify_writemarker_failed", zap.Error(err)) - return &result, common.NewError("write_marker_verification_failed", result.ErrorMessage) - } - - elapsedVerifyWM := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedGetConnObj - - var clientIDForWriteRedeem = writeMarker.ClientID - - if err := writePreRedeem(ctx, allocationObj, &writeMarker, clientIDForWriteRedeem); err != nil { - return nil, err + versionMarkerStr := r.FormValue("version_marker") + if versionMarkerStr == "" { + return nil, common.NewError("invalid_parameters", "Invalid version marker passed") } - - elapsedWritePreRedeem := time.Since(startTime) - elapsedAllocation - elapsedGetLock - - elapsedGetConnObj - elapsedVerifyWM - - fileIDMetaStr := r.FormValue("file_id_meta") - if fileIDMetaStr == "" { - return nil, common.NewError("invalid_parameters", "Invalid file ID meta passed") - } - fileIDMeta := make(map[string]string, 0) - err = json.Unmarshal([]byte(fileIDMetaStr), &fileIDMeta) - if err != nil { - return nil, common.NewError("unmarshall_error", - fmt.Sprintf("Error while unmarshalling file ID meta data: %s", err.Error())) - } - - // Move preCommitDir to finalDir - err = connectionObj.MoveToFilestore(ctx) + versionMarker := writemarker.VersionMarker{} + err = json.Unmarshal([]byte(versionMarkerStr), &versionMarker) if err != nil { - return nil, common.NewError("move_to_filestore_error", fmt.Sprintf("Error while moving to filestore: %s", err.Error())) + return nil, common.NewError("unmarshall_error", fmt.Sprintf("Error while unmarshalling version marker: %s", err.Error())) } - elapsedMoveToFilestore := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedGetConnObj - elapsedVerifyWM - elapsedWritePreRedeem - - rootRef, err := connectionObj.ApplyChanges( - ctx, writeMarker.AllocationRoot, writeMarker.PreviousAllocationRoot, writeMarker.Timestamp, fileIDMeta) + err = versionMarker.Verify(allocationID, allocationObj.OwnerPublicKey) if err != nil { - Logger.Error("Error applying changes", zap.Error(err)) return nil, err } - if !rootRef.IsPrecommit { - return nil, common.NewError("no_root_change", "No change in root ref") - } - connectionObj.Size = rootRef.Size - allocationObj.BlobberSizeUsed - - if writemarkerEntity.WM.Size != connectionObj.Size { - return nil, common.NewError("write_marker_validation_failed", fmt.Sprintf("Write Marker size %v does not match the connection size %v", writemarkerEntity.WM.Size, connectionObj.Size)) - } - - if allocationObj.BlobberSizeUsed+connectionObj.Size > allocationObj.BlobberSize { - return nil, common.NewError("max_allocation_size", - "Max size reached for the allocation with this blobber") - } - if latestWriteMarkerEntity != nil && latestWriteMarkerEntity.WM.ChainSize+connectionObj.Size != writeMarker.ChainSize { - return nil, common.NewErrorf("invalid_chain_size", - "Invalid chain size. expected:%v got %v", latestWriteMarkerEntity.WM.ChainSize+connectionObj.Size, writeMarker.ChainSize) - } else if latestWriteMarkerEntity == nil && connectionObj.Size != writeMarker.ChainSize { - return nil, common.NewErrorf("invalid_chain_size", - "Invalid chain size. expected:%v got %v", connectionObj.Size, writeMarker.ChainSize) - } - - elapsedApplyChanges := time.Since(startTime) - elapsedAllocation - elapsedGetLock - - elapsedGetConnObj - elapsedVerifyWM - elapsedWritePreRedeem - - allocationRoot := rootRef.Hash - fileMetaRoot := rootRef.FileMetaHash - if allocationRoot != writeMarker.AllocationRoot { - result.AllocationRoot = allocationObj.AllocationRoot - if latestWriteMarkerEntity != nil { - result.WriteMarker = latestWriteMarkerEntity + // Move preCommitDir to finalDir + if allocationObj.IsRedeemRequired { + err = connectionObj.MoveToFilestore(ctx, allocationObj.AllocationVersion) + if err != nil { + return nil, common.NewError("move_to_filestore_error", fmt.Sprintf("Error while moving to filestore: %s", err.Error())) } - result.Success = false - result.ErrorMessage = "Allocation root in the write marker does not match the calculated allocation root." + - " Expected hash: " + allocationRoot - return &result, common.NewError("allocation_root_mismatch", result.ErrorMessage) - } - - chainHash := writemarker.CalculateChainHash(prevChainHash, allocationRoot) - if chainHash != writeMarker.ChainHash { - return nil, common.NewError("chain_hash_mismatch", "Chain hash in the write marker does not match the calculated chain hash") } - if fileMetaRoot != writeMarker.FileMetaRoot { - // result.AllocationRoot = allocationObj.AllocationRoot - if latestWriteMarkerEntity != nil { - result.WriteMarker = latestWriteMarkerEntity + elapsedMoveToFilestore := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedGetConnObj + if len(connectionObj.Changes) > 0 { + err = connectionObj.ApplyChanges( + ctx, common.Now(), versionMarker.Version) + if err != nil { + Logger.Error("Error applying changes", zap.Error(err)) + return nil, err } - result.Success = false - result.ErrorMessage = "File meta root in the write marker does not match the calculated file meta root." + - " Expected hash: " + fileMetaRoot + "; Got: " + writeMarker.FileMetaRoot - return &result, common.NewError("file_meta_root_mismatch", result.ErrorMessage) } - writemarkerEntity.ConnectionID = connectionObj.ID - writemarkerEntity.ClientPublicKey = clientKey + elapsedApplyChanges := time.Since(startTime) - elapsedAllocation - elapsedGetLock - + elapsedGetConnObj - elapsedMoveToFilestore db := datastore.GetStore().GetTransaction(ctx) - writemarkerEntity.Latest = true - if err = db.Create(writemarkerEntity).Error; err != nil { - return nil, common.NewError("write_marker_error", "Error persisting the write marker") + err = db.Create(&versionMarker).Error + if err != nil { + return nil, common.NewError("db_error", fmt.Sprintf("Error while saving version marker: %s", err.Error())) } - allocationObj.AllocationRoot = allocationRoot - allocationObj.FileMetaRoot = fileMetaRoot - allocationObj.IsRedeemRequired = true + allocationObj.PrevBlobberSizeUsed = allocationObj.BlobberSizeUsed + allocationObj.PrevUsedSize = allocationObj.UsedSize allocationObj.BlobberSizeUsed += connectionObj.Size allocationObj.UsedSize += connectionObj.Size + allocationObj.AllocationVersion = versionMarker.Version + if len(connectionObj.Changes) == 0 { + allocationObj.IsRedeemRequired = false + } else { + allocationObj.IsRedeemRequired = true + } updateMap := map[string]interface{}{ - "allocation_root": allocationRoot, - "file_meta_root": fileMetaRoot, - "used_size": allocationObj.UsedSize, - "blobber_size_used": allocationObj.BlobberSizeUsed, - "is_redeem_required": true, + "used_size": allocationObj.UsedSize, + "blobber_size_used": allocationObj.BlobberSizeUsed, + "is_redeem_required": allocationObj.IsRedeemRequired, + "allocation_version": versionMarker.Version, + "prev_used_size": allocationObj.PrevUsedSize, + "prev_blobber_size_used": allocationObj.PrevBlobberSizeUsed, } updateOption := func(a *allocation.Allocation) { - a.AllocationRoot = allocationRoot - a.FileMetaRoot = fileMetaRoot - a.IsRedeemRequired = true + a.IsRedeemRequired = allocationObj.IsRedeemRequired a.BlobberSizeUsed = allocationObj.BlobberSizeUsed a.UsedSize = allocationObj.UsedSize + a.AllocationVersion = allocationObj.AllocationVersion + a.PrevUsedSize = allocationObj.PrevUsedSize + a.PrevBlobberSizeUsed = allocationObj.PrevBlobberSizeUsed } if err = allocation.Repo.UpdateAllocation(ctx, allocationObj, updateMap, updateOption); err != nil { - return nil, common.NewError("allocation_write_error", "Error persisting the allocation object") + return nil, common.NewError("allocation_write_error", "Error persisting the allocation object "+err.Error()) } elapsedSaveAllocation := time.Since(startTime) - elapsedAllocation - elapsedGetLock - - elapsedGetConnObj - elapsedVerifyWM - elapsedWritePreRedeem - elapsedApplyChanges + elapsedGetConnObj - elapsedApplyChanges err = connectionObj.CommitToFileStore(ctx) if err != nil { @@ -841,17 +719,25 @@ func (fsh *StorageHandler) CommitWrite(ctx context.Context, r *http.Request) (*b return nil, common.NewError("file_store_error", "Error committing to file store. "+err.Error()) } } - elapsedCommitStore := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedGetConnObj - elapsedVerifyWM - elapsedWritePreRedeem - elapsedApplyChanges - elapsedSaveAllocation - logging.Logger.Info("commit_filestore", zap.String("allocation_id", allocationId), zap.String("allocation_root", allocationRoot)) + elapsedCommitStore := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedGetConnObj - elapsedApplyChanges - elapsedSaveAllocation + logging.Logger.Info("commit_filestore", zap.String("allocation_id", allocationId)) connectionObj.DeleteChanges(ctx) db.Model(connectionObj).Updates(allocation.AllocationChangeCollector{Status: allocation.CommittedConnection}) result.AllocationRoot = allocationObj.AllocationRoot - result.WriteMarker = writemarkerEntity result.Success = true result.ErrorMessage = "" - commitOperation := connectionObj.Changes[0].Operation - input := connectionObj.Changes[0].Input + var ( + commitOperation string + input string + ) + if len(connectionObj.Changes) > 0 { + commitOperation = connectionObj.Changes[0].Operation + input = connectionObj.Changes[0].Input + } else { + commitOperation = "[commit]empty" + input = "[commit]empty" + } //Delete connection object and its changes @@ -861,13 +747,10 @@ func (fsh *StorageHandler) CommitWrite(ctx context.Context, r *http.Request) (*b Logger.Info("[commit]"+commitOperation, zap.String("alloc_id", allocationID), - zap.String("allocation_root", writeMarker.AllocationRoot), zap.String("input", input), zap.Duration("get_alloc", elapsedAllocation), zap.Duration("get-lock", elapsedGetLock), zap.Duration("get-conn-obj", elapsedGetConnObj), - zap.Duration("verify-wm", elapsedVerifyWM), - zap.Duration("write-pre-redeem", elapsedWritePreRedeem), zap.Duration("move-to-filestore", elapsedMoveToFilestore), zap.Duration("apply-changes", elapsedApplyChanges), zap.Duration("save-allocation", elapsedSaveAllocation), @@ -906,7 +789,10 @@ func (fsh *StorageHandler) RenameObject(ctx context.Context, r *http.Request) (i if new_name == "" { return nil, common.NewError("invalid_parameters", "Invalid name") } - + if filepath.Base(new_name) != new_name { + logging.Logger.Error("invalid_parameters", zap.String("new_name", new_name), zap.String("base", filepath.Base(new_name))) + return nil, common.NewError("invalid_parameters", "Invalid name") + } pathHash, err := pathHashFromReq(r, allocationID) if err != nil { return nil, err @@ -926,7 +812,7 @@ func (fsh *StorageHandler) RenameObject(ctx context.Context, r *http.Request) (i return nil, common.NewError("meta_error", "Error reading metadata for connection") } - objectRef, err := reference.GetLimitedRefFieldsByLookupHash(ctx, allocationID, pathHash, []string{"id", "name", "path", "hash", "size", "validation_root", "fixed_merkle_root", "type"}) + objectRef, err := reference.GetLimitedRefFieldsByLookupHash(ctx, allocationID, pathHash, []string{"id", "name", "path", "size", "type"}) if err != nil { return nil, common.NewError("invalid_parameters", "Invalid file path. "+err.Error()) @@ -936,6 +822,16 @@ func (fsh *StorageHandler) RenameObject(ctx context.Context, r *http.Request) (i return nil, common.NewError("invalid_operation", "cannot rename root path") } + if objectRef.Type != reference.FILE { + isEmpty, err := reference.IsDirectoryEmpty(ctx, objectRef.ID) + if err != nil { + return nil, common.NewError("invalid_operation", "Error checking if directory is empty") + } + if !isEmpty { + return nil, common.NewError("invalid_operation", "Directory is not empty") + } + } + allocationChange := &allocation.AllocationChange{} allocationChange.ConnectionID = connectionObj.ID allocationChange.Size = 0 @@ -954,9 +850,6 @@ func (fsh *StorageHandler) RenameObject(ctx context.Context, r *http.Request) (i result := &allocation.UploadResult{} result.Filename = new_name - result.Hash = objectRef.Hash - result.ValidationRoot = objectRef.ValidationRoot - result.FixedMerkleRoot = objectRef.FixedMerkleRoot result.Size = objectRef.Size return result, nil @@ -993,7 +886,7 @@ func (fsh *StorageHandler) CopyObject(ctx context.Context, r *http.Request) (int if destPath == "" { return nil, common.NewError("invalid_parameters", "Invalid destination for operation") } - + destPath = filepath.Clean(destPath) pathHash, err := pathHashFromReq(r, allocationID) if err != nil { return nil, err @@ -1013,7 +906,7 @@ func (fsh *StorageHandler) CopyObject(ctx context.Context, r *http.Request) (int return nil, common.NewError("meta_error", "Error reading metadata for connection") } - objectRef, err := reference.GetLimitedRefFieldsByLookupHash(ctx, allocationID, pathHash, []string{"id", "name", "path", "hash", "size", "validation_root", "fixed_merkle_root"}) + objectRef, err := reference.GetLimitedRefFieldsByLookupHash(ctx, allocationID, pathHash, []string{"id", "name", "path", "size", "type"}) if err != nil { return nil, common.NewError("invalid_parameters", "Invalid file path. "+err.Error()) @@ -1021,7 +914,17 @@ func (fsh *StorageHandler) CopyObject(ctx context.Context, r *http.Request) (int if objectRef.ParentPath == destPath || objectRef.Path == destPath { return nil, common.NewError("invalid_parameters", "Invalid destination path. Cannot copy to the same parent directory.") } + if objectRef.Type == reference.DIRECTORY { + isEmpty, err := reference.IsDirectoryEmpty(ctx, objectRef.ID) + if err != nil { + return nil, common.NewError("invalid_operation", "Error checking if directory is empty") + } + if !isEmpty { + return nil, common.NewError("invalid_operation", "Directory is not empty") + } + } newPath := filepath.Join(destPath, objectRef.Name) + newPath = filepath.Clean(newPath) paths, err := common.GetParentPaths(newPath) if err != nil { return nil, err @@ -1052,8 +955,7 @@ func (fsh *StorageHandler) CopyObject(ctx context.Context, r *http.Request) (int allocationChange.LookupHash = pathHash allocationChange.Operation = constants.FileOperationCopy dfc := &allocation.CopyFileChange{ConnectionID: connectionObj.ID, - AllocationID: connectionObj.AllocationID, DestPath: destPath} - dfc.SrcPath = objectRef.Path + AllocationID: connectionObj.AllocationID, DestPath: newPath, Type: objectRef.Type, SrcPath: objectRef.Path} allocation.UpdateConnectionObjSize(connectionID, allocationChange.Size) connectionObj.AddChange(allocationChange, dfc) @@ -1065,9 +967,6 @@ func (fsh *StorageHandler) CopyObject(ctx context.Context, r *http.Request) (int result := &allocation.UploadResult{} result.Filename = objectRef.Name - result.Hash = objectRef.Hash - result.ValidationRoot = objectRef.ValidationRoot - result.FixedMerkleRoot = objectRef.FixedMerkleRoot result.Size = objectRef.Size return result, nil } @@ -1103,6 +1002,7 @@ func (fsh *StorageHandler) MoveObject(ctx context.Context, r *http.Request) (int if destPath == "" { return nil, common.NewError("invalid_parameters", "Invalid destination for operation") } + destPath = filepath.Clean(destPath) pathHash, err := pathHashFromReq(r, allocationID) if err != nil { @@ -1124,7 +1024,7 @@ func (fsh *StorageHandler) MoveObject(ctx context.Context, r *http.Request) (int } objectRef, err := reference.GetLimitedRefFieldsByLookupHash( - ctx, allocationID, pathHash, []string{"id", "name", "path", "hash", "size", "validation_root", "fixed_merkle_root"}) + ctx, allocationID, pathHash, []string{"id", "name", "path", "size", "type"}) if err != nil { return nil, common.NewError("invalid_parameters", "Invalid file path. "+err.Error()) @@ -1133,7 +1033,17 @@ func (fsh *StorageHandler) MoveObject(ctx context.Context, r *http.Request) (int if objectRef.ParentPath == destPath { return nil, common.NewError("invalid_parameters", "Invalid destination path. Cannot move to the same parent directory.") } + if objectRef.Type == reference.DIRECTORY { + isEmpty, err := reference.IsDirectoryEmpty(ctx, objectRef.ID) + if err != nil { + return nil, common.NewError("invalid_operation", "Error checking if directory is empty") + } + if !isEmpty { + return nil, common.NewError("invalid_operation", "Directory is not empty") + } + } newPath := filepath.Join(destPath, objectRef.Name) + newPath = filepath.Clean(newPath) paths, err := common.GetParentPaths(newPath) if err != nil { return nil, err @@ -1167,7 +1077,8 @@ func (fsh *StorageHandler) MoveObject(ctx context.Context, r *http.Request) (int ConnectionID: connectionObj.ID, AllocationID: connectionObj.AllocationID, SrcPath: objectRef.Path, - DestPath: destPath, + DestPath: newPath, + Type: objectRef.Type, } dfc.SrcPath = objectRef.Path connectionObj.AddChange(allocationChange, dfc) @@ -1180,9 +1091,6 @@ func (fsh *StorageHandler) MoveObject(ctx context.Context, r *http.Request) (int result := &allocation.UploadResult{} result.Filename = objectRef.Name - result.Hash = objectRef.Hash - result.ValidationRoot = objectRef.ValidationRoot - result.FixedMerkleRoot = objectRef.FixedMerkleRoot result.Size = objectRef.Size return result, nil } @@ -1194,7 +1102,7 @@ func (fsh *StorageHandler) DeleteFile(ctx context.Context, r *http.Request, conn return nil, common.NewError("invalid_parameters", "Invalid path") } fileRef, err := reference.GetLimitedRefFieldsByPath(ctx, connectionObj.AllocationID, path, - []string{"path", "name", "size", "hash", "validation_root", "fixed_merkle_root"}) + []string{"path", "name", "size"}) if err != nil { Logger.Error("invalid_file", zap.Error(err)) @@ -1209,7 +1117,7 @@ func (fsh *StorageHandler) DeleteFile(ctx context.Context, r *http.Request, conn allocationChange.Operation = constants.FileOperationDelete dfc := &allocation.DeleteFileChange{ConnectionID: connectionObj.ID, AllocationID: connectionObj.AllocationID, Name: fileRef.Name, - Hash: fileRef.Hash, Path: fileRef.Path, Size: deleteSize} + LookupHash: fileRef.LookupHash, Path: fileRef.Path, Size: deleteSize} allocation.UpdateConnectionObjSize(connectionObj.ID, allocationChange.Size) @@ -1217,9 +1125,7 @@ func (fsh *StorageHandler) DeleteFile(ctx context.Context, r *http.Request, conn result := &allocation.UploadResult{} result.Filename = fileRef.Name - result.Hash = fileRef.Hash - result.ValidationRoot = fileRef.ValidationRoot - result.FixedMerkleRoot = fileRef.FixedMerkleRoot + result.Hash = fileRef.LookupHash result.Size = fileRef.Size return result, nil @@ -1335,6 +1241,7 @@ func (fsh *StorageHandler) WriteFile(ctx context.Context, r *http.Request) (*all allocationID := ctx.Value(constants.ContextKeyAllocationID).(string) allocationTx := ctx.Value(constants.ContextKeyAllocation).(string) clientID := ctx.Value(constants.ContextKeyClient).(string) + logging.Logger.Info("writeFile", zap.String("allocation_id", allocationID)) connectionID, ok := common.GetField(r, "connection_id") if !ok { logging.Logger.Error("no_connection_id", zap.String("alloc_id", allocationID)) @@ -1359,7 +1266,7 @@ func (fsh *StorageHandler) WriteFile(ctx context.Context, r *http.Request) (*all if err != nil { return nil, common.NewError("invalid_parameters", "Invalid allocation id passed."+err.Error()) } - + logging.Logger.Info("writeFileAllocation") if allocationObj.OwnerID != clientID && allocationObj.RepairerID != clientID { return nil, common.NewError("invalid_operation", "Operation needs to be performed by the owner or the payer of the allocation") } @@ -1463,19 +1370,14 @@ func (fsh *StorageHandler) Rollback(ctx context.Context, r *http.Request) (*blob return nil, common.NewError("invalid_parameters", "Invalid allocation id passed."+err.Error()) } - if allocationObj.AllocationRoot == "" { - Logger.Error("Allocation root is not set", zap.String("allocation_id", allocationObj.ID)) - return nil, common.NewError("invalid_parameters", "Allocation root is not set") + if allocationObj.AllocationVersion == 0 { + Logger.Error("Allocation version is 0", zap.String("allocation_id", allocationObj.ID)) + return nil, common.NewError("invalid_parameters", "Allocation version is not set") } elapsedAllocation := time.Since(startTime) allocationID := allocationObj.ID - connectionID, ok := common.GetField(r, "connection_id") - if !ok { - return nil, common.NewError("invalid_parameters", "Invalid connection id passed") - } - elapsedGetLock := time.Since(startTime) - elapsedAllocation if clientID == "" || clientKey == "" { @@ -1486,105 +1388,55 @@ func (fsh *StorageHandler) Rollback(ctx context.Context, r *http.Request) (*blob return nil, common.NewError("invalid_operation", "Operation needs to be performed by the owner of the allocation") } - writeMarkerString := r.FormValue("write_marker") - writeMarker := writemarker.WriteMarker{} - err = json.Unmarshal([]byte(writeMarkerString), &writeMarker) + if !allocationObj.IsRedeemRequired { + return nil, common.NewError("invalid_operation", "Last commit is rollback") + } + + versionMarkerString := r.FormValue("version_marker") + versionMarker := writemarker.VersionMarker{} + err = json.Unmarshal([]byte(versionMarkerString), &versionMarker) if err != nil { return nil, common.NewErrorf("invalid_parameters", "Invalid parameters. Error parsing the writemarker for commit: %v", err) } - var result blobberhttp.CommitResult + if versionMarker.IsRepair { + return nil, common.NewError("invalid_parameters", "Invalid version marker passed. Rollback marker cannot be a repair marker") + } - var latestWriteMarkerEntity *writemarker.WriteMarkerEntity - latestWriteMarkerEntity, err = writemarker.GetWriteMarkerEntity(ctx, - allocationObj.AllocationRoot) + var result blobberhttp.CommitResult + err = versionMarker.Verify(allocationID, allocationObj.OwnerPublicKey) if err != nil { - return nil, common.NewErrorf("latest_write_marker_read_error", - "Error reading the latest write marker for allocation: %v", err) - } - if latestWriteMarkerEntity == nil { - return nil, common.NewError("latest_write_marker_not_found", - "Latest write marker not found for allocation") + return nil, common.NewError("invalid_parameters", "Invalid version marker passed: "+err.Error()) } - writemarkerEntity := &writemarker.WriteMarkerEntity{} - writemarkerEntity.WM = writeMarker + if versionMarker.Version == allocationObj.AllocationVersion { + return nil, common.NewError("invalid_parameters", "Invalid version marker passed. Version marker is same as the current version") + } - err = writemarkerEntity.VerifyRollbackMarker(ctx, allocationObj, latestWriteMarkerEntity) + currentVersionMarker, err := writemarker.GetCurrentVersion(ctx, allocationID) if err != nil { - return nil, common.NewError("write_marker_verification_failed", "Verification of the write marker failed: "+err.Error()) + return nil, common.NewError("invalid_parameters", "Error getting the current version marker") } - - if writemarkerEntity.WM.ChainLength > config.Configuration.MaxChainLength { - return nil, common.NewError("chain_length_exceeded", "Chain length exceeded") + if currentVersionMarker.IsRepair { + return nil, common.NewError("invalid_parameters", "Invalid version marker passed. Allocation is in repair mode") } - - elapsedVerifyWM := time.Since(startTime) - elapsedAllocation - elapsedGetLock - - var clientIDForWriteRedeem = writeMarker.ClientID - - if err := writePreRedeem(ctx, allocationObj, &writeMarker, clientIDForWriteRedeem); err != nil { - return nil, err + if versionMarker.Version != currentVersionMarker.Version-1 { + return nil, common.NewError("invalid_parameters", "Invalid version marker passed. Version marker is not the previous version") } - elapsedWritePreRedeem := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedVerifyWM + elapsedWritePreRedeem := time.Since(startTime) - elapsedAllocation - elapsedGetLock timeoutCtx, cancel := context.WithTimeout(ctx, 45*time.Second) defer cancel() c := datastore.GetStore().CreateTransaction(timeoutCtx) txn := datastore.GetStore().GetTransaction(c) - err = allocation.ApplyRollback(c, allocationID) + err = allocation.ApplyRollback(c, allocationID, allocationObj.AllocationVersion) if err != nil { txn.Rollback() return nil, common.NewError("allocation_rollback_error", "Error applying the rollback for allocation: "+err.Error()) } - elapsedApplyRollback := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedVerifyWM - elapsedWritePreRedeem - - //get allocation root and ref - rootRef, err := reference.GetLimitedRefFieldsByPath(c, allocationID, "/", []string{"hash", "file_meta_hash", "is_precommit"}) - if err != nil && err != gorm.ErrRecordNotFound { - txn.Rollback() - return nil, common.NewError("root_ref_read_error", "Error reading the root reference: "+err.Error()) - } - if err == gorm.ErrRecordNotFound { - rootRef = &reference.Ref{} - } - - Logger.Info("rollback_root_ref", zap.Any("root_ref", rootRef)) - allocationRoot := rootRef.Hash - fileMetaRoot := rootRef.FileMetaHash - - if allocationRoot != writeMarker.AllocationRoot { - result.AllocationRoot = allocationObj.AllocationRoot - result.WriteMarker = latestWriteMarkerEntity - result.Success = false - result.ErrorMessage = "Allocation root in the write marker does not match the calculated allocation root." + - " Expected hash: " + allocationRoot - txn.Rollback() - return &result, common.NewError("allocation_root_mismatch", result.ErrorMessage) - } - - chainHash := writemarker.CalculateChainHash(latestWriteMarkerEntity.WM.ChainHash, allocationRoot) - if chainHash != writeMarker.ChainHash { - txn.Rollback() - return nil, common.NewError("chain_hash_mismatch", "Chain hash in the write marker does not match the calculated chain hash") - } - - if fileMetaRoot != writeMarker.FileMetaRoot { - if latestWriteMarkerEntity != nil { - result.WriteMarker = latestWriteMarkerEntity - } - result.Success = false - result.ErrorMessage = "File meta root in the write marker does not match the calculated file meta root." + - " Expected hash: " + fileMetaRoot + "; Got: " + writeMarker.FileMetaRoot - txn.Rollback() - return &result, common.NewError("file_meta_root_mismatch", result.ErrorMessage) - } - - writemarkerEntity.ConnectionID = connectionID - writemarkerEntity.ClientPublicKey = clientKey - Logger.Info("rollback_writemarker", zap.Any("writemarker", writemarkerEntity.WM)) + elapsedApplyRollback := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedWritePreRedeem alloc, err := allocation.Repo.GetByIdAndLock(c, allocationID) Logger.Info("[rollback]Lock Allocation", zap.Bool("is_redeem_required", alloc.IsRedeemRequired), zap.String("allocation_root", alloc.AllocationRoot), zap.String("latest_wm_redeemed", alloc.LatestRedeemedWM)) @@ -1593,28 +1445,25 @@ func (fsh *StorageHandler) Rollback(ctx context.Context, r *http.Request) (*blob return &result, common.NewError("allocation_read_error", "Error reading the allocation object") } - alloc.BlobberSizeUsed -= latestWriteMarkerEntity.WM.Size - alloc.UsedSize -= latestWriteMarkerEntity.WM.Size - alloc.AllocationRoot = allocationRoot - alloc.FileMetaRoot = fileMetaRoot - alloc.IsRedeemRequired = true + alloc.BlobberSizeUsed = alloc.PrevBlobberSizeUsed + alloc.UsedSize = alloc.PrevUsedSize + alloc.IsRedeemRequired = false + alloc.AllocationVersion = versionMarker.Version updateMap := map[string]interface{}{ "blobber_size_used": alloc.BlobberSizeUsed, "used_size": alloc.UsedSize, - "allocation_root": alloc.AllocationRoot, - "file_meta_root": alloc.FileMetaRoot, - "is_redeem_required": true, + "is_redeem_required": false, + "allocation_version": versionMarker.Version, } updateOption := func(a *allocation.Allocation) { a.BlobberSizeUsed = alloc.BlobberSizeUsed a.UsedSize = alloc.UsedSize - a.AllocationRoot = alloc.AllocationRoot + a.AllocationVersion = alloc.AllocationVersion a.FileMetaRoot = alloc.FileMetaRoot a.IsRedeemRequired = alloc.IsRedeemRequired } - writemarkerEntity.Latest = true - err = txn.Create(writemarkerEntity).Error + err = txn.Create(&versionMarker).Error if err != nil { txn.Rollback() return &result, common.NewError("write_marker_error", "Error persisting the write marker "+err.Error()) @@ -1633,9 +1482,7 @@ func (fsh *StorageHandler) Rollback(ctx context.Context, r *http.Request) (*blob Logger.Error("Error committing the rollback for allocation", zap.Error(err)) } - elapsedCommitRollback := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedVerifyWM - elapsedWritePreRedeem - result.AllocationRoot = allocationObj.AllocationRoot - result.WriteMarker = writemarkerEntity + elapsedCommitRollback := time.Since(startTime) - elapsedAllocation - elapsedGetLock - elapsedWritePreRedeem result.Success = true result.ErrorMessage = "" commitOperation := "rollback" @@ -1644,7 +1491,6 @@ func (fsh *StorageHandler) Rollback(ctx context.Context, r *http.Request) (*blob zap.String("alloc_id", allocationID), zap.Duration("get_alloc", elapsedAllocation), zap.Duration("get-lock", elapsedGetLock), - zap.Duration("verify-wm", elapsedVerifyWM), zap.Duration("write-pre-redeem", elapsedWritePreRedeem), zap.Duration("apply-rollback", elapsedApplyRollback), zap.Duration("total", time.Since(startTime)), diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler_bench_test.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler_bench_test.go index adcb74109..0d0094022 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler_bench_test.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler_bench_test.go @@ -11,7 +11,7 @@ package handler // "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" // "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" // "github.com/0chain/blobber/code/go/0chain.net/blobbercore/mock" -// "github.com/0chain/gosdk/zboxcore/sdk" +// "github.com/0chain/gosdk_common/zboxcore/sdk" // ) // func BenchmarkUploadFileWithDisk(b *testing.B) { diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go index ccc627dc9..b1f33365b 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go @@ -16,23 +16,24 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/zcncrypto" mocket "github.com/selvatico/go-mocket" "github.com/0chain/blobber/code/go/0chain.net/core/node" - "github.com/0chain/gosdk/zboxcore/fileref" + "github.com/0chain/gosdk_common/zboxcore/fileref" - "github.com/0chain/gosdk/zboxcore/blockchain" + "github.com/0chain/gosdk_common/zboxcore/blockchain" "github.com/0chain/blobber/code/go/0chain.net/core/common" - c_common "github.com/0chain/gosdk/core/common" - "github.com/0chain/gosdk/zboxcore/marker" + c_common "github.com/0chain/gosdk_common/core/common" + "github.com/0chain/gosdk_common/zboxcore/marker" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "github.com/stretchr/testify/require" "testing" @@ -56,10 +57,8 @@ func TestDownloadFile(t *testing.T) { ) ts := time.Now().Add(time.Hour) var mockLongTimeInFuture = common.Timestamp(ts.Unix()) + common.Timestamp(time.Second*1000) - var mockClient client.Client - require.NoError(t, json.Unmarshal([]byte(mockClientWallet), &mockClient)) - var mockOwner client.Client - require.NoError(t, json.Unmarshal([]byte(mockOwnerWallet), &mockOwner)) + mockClient, _ := client.PopulateClient(mockClientWallet, "bls0chain") + mockOwner, _ := client.PopulateClient(mockOwnerWallet, "bls0chain") var ( now = c_common.Timestamp(time.Now().Unix()) ) @@ -84,7 +83,7 @@ func TestDownloadFile(t *testing.T) { isRevoked bool isFundedBlobber bool isFunded0Chain bool - payerId client.Client + payerId zcncrypto.Wallet // client input from gosdk's BlockDownloadRequest, inData blockDownloadRequest @@ -121,15 +120,17 @@ func TestDownloadFile(t *testing.T) { if p.useAuthTicket { authTicket := &marker.AuthTicket{ AllocationID: p.inData.allocationID, - ClientID: client.GetClientID(), + ClientID: client.Wallet().ClientID, Expiration: int64(time.Duration(now) + 10000*time.Second), OwnerID: mockOwner.ClientID, Timestamp: int64(common.Now()), FilePathHash: p.inData.pathHash, } - require.NoError(t, client.PopulateClient(mockOwnerWallet, "bls0chain")) + _, err := client.PopulateClient(mockOwnerWallet, "bls0chain") + require.NoError(t, err) require.NoError(t, authTicket.Sign()) - require.NoError(t, client.PopulateClient(mockClientWallet, "bls0chain")) + _, err = client.PopulateClient(mockClientWallet, "bls0chain") + require.NoError(t, err) authTicketBytes, _ := json.Marshal(authTicket) auth := base64.StdEncoding.EncodeToString(authTicketBytes) req.Header.Set("X-Auth-Token", auth) @@ -139,8 +140,8 @@ func TestDownloadFile(t *testing.T) { } } - makeMockMakeSCRestAPICall := func(t *testing.T, p parameters) func(scAddress string, relativePath string, params map[string]string) ([]byte, error) { - return func(scAddress string, relativePath string, params map[string]string) ([]byte, error) { + makeMockMakeSCRestAPICall := func(t *testing.T, p parameters) func(scAddress string, relativePath string, params map[string]string, options ...string) ([]byte, error) { + return func(scAddress string, relativePath string, params map[string]string, options ...string) ([]byte, error) { require.New(t) require.EqualValues(t, scAddress, transaction.STORAGE_CONTRACT_ADDRESS) switch relativePath { @@ -290,9 +291,9 @@ func TestDownloadFile(t *testing.T) { setupCtx := func(p parameters) context.Context { ctx := context.TODO() - ctx = context.WithValue(ctx, constants.ContextKeyClient, client.GetClientID()) + ctx = context.WithValue(ctx, constants.ContextKeyClient, client.Id()) ctx = context.WithValue(ctx, constants.ContextKeyAllocation, p.inData.allocationTx) - ctx = context.WithValue(ctx, constants.ContextKeyClientKey, client.GetClientPublicKey()) + ctx = context.WithValue(ctx, constants.ContextKeyClientKey, client.PublicKey()) ctx = datastore.GetStore().CreateTransaction(ctx) @@ -455,9 +456,11 @@ func TestDownloadFile(t *testing.T) { func(t *testing.T) { setupParams(&test.parameters) if test.parameters.isOwner { - require.NoError(t, client.PopulateClient(mockOwnerWallet, "bls0chain")) + _, err := client.PopulateClient(mockOwnerWallet, "bls0chain") + require.NoError(t, err) } else { - require.NoError(t, client.PopulateClient(mockClientWallet, "bls0chain")) + _, err := client.PopulateClient(mockClientWallet, "bls0chain") + require.NoError(t, err) } transaction.MakeSCRestAPICall = makeMockMakeSCRestAPICall(t, test.parameters) request := setupRequest(test.parameters) diff --git a/code/go/0chain.net/blobbercore/handler/protocol.go b/code/go/0chain.net/blobbercore/handler/protocol.go index 976828558..9a2141db6 100644 --- a/code/go/0chain.net/blobbercore/handler/protocol.go +++ b/code/go/0chain.net/blobbercore/handler/protocol.go @@ -3,19 +3,19 @@ package handler import ( "context" "errors" - "fmt" "sync" - "time" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/config" + "github.com/0chain/gosdk_common/core/client" + coreTxn "github.com/0chain/gosdk_common/core/transaction" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" - "github.com/0chain/blobber/code/go/0chain.net/core/util" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/zcncore" "go.uber.org/zap" ) @@ -67,6 +67,7 @@ func getStorageNode() (*transaction.StorageNode, error) { sn.StakePoolSettings.ServiceCharge = config.Configuration.ServiceCharge sn.IsEnterprise = config.Configuration.IsEnterprise + sn.StorageVersion = 1 return sn, nil } @@ -79,19 +80,13 @@ func RegisterBlobber(ctx context.Context) error { }) if err != nil { // blobber is not registered yet - txn, err := sendSmartContractBlobberAdd(ctx) - if err != nil { - logging.Logger.Error("Error when sending add request to blockchain", zap.Any("err", err)) - return err - } - - t, err := TransactionVerify(txn) + txn, err := sendSmartContractBlobberAdd() if err != nil { - logging.Logger.Error("Failed to verify blobber register transaction", zap.Any("err", err), zap.String("txn.Hash", txn.Hash)) + logging.Logger.Error("Error in add blobber", zap.Any("err", err)) return err } - logging.Logger.Info("Verified blobber register transaction", zap.String("txn_hash", t.Hash), zap.Any("txn_output", t.TransactionOutput)) + logging.Logger.Info("Verified blobber register transaction", zap.String("txn_hash", txn.Hash), zap.Any("txn_output", txn.TransactionOutput)) return nil } @@ -105,38 +100,31 @@ func RegisterBlobber(ctx context.Context) error { } func RefreshPriceOnChain(ctx context.Context) error { - txn, err := sendSmartContractBlobberAdd(ctx) + txn, err := sendSmartContractBlobberAdd() if err != nil { - return err - } - - if t, err := TransactionVerify(txn); err != nil { logging.Logger.Error("Failed to verify price refresh transaction", zap.Any("err", err), zap.String("txn.Hash", txn.Hash)) - } else { - logging.Logger.Info("Verified price refresh transaction", zap.String("txn_hash", t.Hash), zap.Any("txn_output", t.TransactionOutput)) + return err } - return err + logging.Logger.Info("Verified price refresh transaction", zap.String("txn_hash", txn.Hash), zap.Any("txn_output", txn.TransactionOutput)) + return nil } // sendSmartContractBlobberAdd Add or update blobber on blockchain -func sendSmartContractBlobberAdd(ctx context.Context) (*transaction.Transaction, error) { +func sendSmartContractBlobberAdd() (*coreTxn.Transaction, error) { // initialize storage node (ie blobber) - txn, err := transaction.NewTransactionEntity() - if err != nil { - return nil, err - } - sn, err := getStorageNode() if err != nil { return nil, err } - err = txn.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, - transaction.ADD_BLOBBER_SC_NAME, sn, 0) + _, _, _, txn, err := coreTxn.SmartContractTxn(transaction.STORAGE_CONTRACT_ADDRESS, coreTxn.SmartContractTxnData{ + Name: transaction.ADD_BLOBBER_SC_NAME, + InputArgs: sn, + }, true) if err != nil { logging.Logger.Error("Failed to set blobber on the blockchain", - zap.String("err:", err.Error())) + zap.String("err:", err.Error()), zap.Any("Txn", txn), zap.Any("ClientFee", client.TxnFee())) return nil, err } @@ -161,39 +149,20 @@ var ErrValidatorHasRemoved = errors.New("validator has been removed") // ErrValidatorNotFound it is not registered on chain var ErrValidatorNotFound = errors.New("validator is not found") -func TransactionVerify(txn *transaction.Transaction) (t *transaction.Transaction, err error) { - msg := fmt.Sprintf("Verifying transaction: max_retries: %d", util.MAX_RETRIES) - logging.Logger.Info(msg) - for i := 0; i < util.MAX_RETRIES; i++ { - time.Sleep(transaction.SLEEP_FOR_TXN_CONFIRMATION * time.Second) - if t, err = transaction.VerifyTransactionWithNonce(txn.Hash, txn.GetTransaction().GetTransactionNonce()); err == nil { - return t, nil - } - } - - return nil, errors.New("[txn]max retries exceeded with " + txn.Hash) -} - // SendHealthCheck send heartbeat to blockchain func SendHealthCheck(provider common.ProviderType) (string, error) { - var txn *transaction.Transaction + var hash string var err error switch provider { case common.ProviderTypeBlobber: - txn, err = BlobberHealthCheck() + hash, err = BlobberHealthCheck() case common.ProviderTypeValidator: - txn, err = ValidatorHealthCheck() + hash, err = ValidatorHealthCheck() default: return "", errors.New("unknown provider type") } - if err != nil { - return "", err - } - - _, err = TransactionVerify(txn) - - return txn.Hash, err + return hash, err } diff --git a/code/go/0chain.net/blobbercore/handler/storage_handler.go b/code/go/0chain.net/blobbercore/handler/storage_handler.go index ebe240393..36374892e 100644 --- a/code/go/0chain.net/blobbercore/handler/storage_handler.go +++ b/code/go/0chain.net/blobbercore/handler/storage_handler.go @@ -12,7 +12,7 @@ import ( "gorm.io/gorm" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "go.uber.org/zap" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" @@ -21,6 +21,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/writemarker" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" . "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" ) @@ -142,8 +143,15 @@ func (fsh *StorageHandler) GetFileMeta(ctx context.Context, r *http.Request) (in return nil, common.NewError("invalid_signature", "Invalid signature") } } - + if fileref.Type == reference.DIRECTORY { + fileref.IsEmpty, err = reference.IsDirectoryEmpty(ctx, fileref.ID) + if err != nil { + return nil, common.NewError("bad_db_operation", "Error checking if directory is empty. "+err.Error()) + } + } + fileref.AllocationVersion = alloc.AllocationVersion result := fileref.GetListingData(ctx) + Logger.Info("GetFileMeta", zap.Any("allocationResultVersion", result["allocation_version"]), zap.Int64("allocationVersion", alloc.AllocationVersion), zap.Any("path", result["path"])) if !isOwner && !isRepairer { var authTokenString = r.FormValue("auth_token") @@ -255,11 +263,6 @@ func (fsh *StorageHandler) GetFileStats(ctx context.Context, r *http.Request) (i if err != nil { return nil, common.NewError("bad_db_operation", "Error retrieving file stats. "+err.Error()) } - wm, _ := writemarker.GetWriteMarkerEntity(ctx, fileref.AllocationRoot) - if wm != nil && fileStats != nil { - fileStats.WriteMarkerRedeemTxn = wm.CloseTxnID - fileStats.OnChain = wm.OnChain() - } statsMap := make(map[string]interface{}) statsBytes, err := json.Marshal(fileStats) if err != nil { @@ -391,7 +394,7 @@ func (fsh *StorageHandler) ListEntities(ctx context.Context, r *http.Request) (* if !ok { var listResult blobberhttp.ListResult - listResult.AllocationRoot = allocationObj.AllocationRoot + listResult.AllocationVersion = allocationObj.AllocationVersion if fileref == nil { fileref = &reference.Ref{Type: reference.DIRECTORY, Path: path, AllocationID: allocationID} } @@ -402,6 +405,9 @@ func (fsh *StorageHandler) ListEntities(ctx context.Context, r *http.Request) (* } fileref = parent } + if path == "/" { + fileref.Size = allocationObj.BlobberSizeUsed + } listResult.Meta = fileref.GetListingData(ctx) if clientID != allocationObj.OwnerID { delete(listResult.Meta, "path") @@ -461,16 +467,22 @@ func (fsh *StorageHandler) ListEntities(ctx context.Context, r *http.Request) (* dirref = parent } else { + if fileref == nil { + fileref = &reference.Ref{Type: reference.DIRECTORY, Path: path, AllocationID: allocationID} + } r, err := reference.GetRefWithChildren(ctx, fileref, allocationID, filePath, offset, pageLimit) if err != nil { return nil, common.NewError("invalid_parameters", "Invalid path. "+err.Error()) } dirref = r + if path == "/" { + dirref.Size = allocationObj.BlobberSizeUsed + } } var result blobberhttp.ListResult - result.AllocationRoot = allocationObj.AllocationRoot + result.AllocationVersion = allocationObj.AllocationVersion result.Meta = dirref.GetListingData(ctx) if clientID != allocationObj.OwnerID { delete(result.Meta, "path") @@ -490,7 +502,7 @@ func (fsh *StorageHandler) ListEntities(ctx context.Context, r *http.Request) (* return &result, nil } -func (fsh *StorageHandler) GetLatestWriteMarker(ctx context.Context, r *http.Request) (*blobberhttp.LatestWriteMarkerResult, error) { +func (fsh *StorageHandler) GetLatestWriteMarker(ctx context.Context, r *http.Request) (*blobberhttp.LatestVersionMarkerResult, error) { clientID := ctx.Value(constants.ContextKeyClient).(string) if clientID == "" { return nil, common.NewError("invalid_operation", "Operation needs to be performed by the owner of the allocation") @@ -508,44 +520,26 @@ func (fsh *StorageHandler) GetLatestWriteMarker(ctx context.Context, r *http.Req clientSignV2 := ctx.Value(constants.ContextKeyClientSignatureHeaderV2Key).(string) valid, err := verifySignatureFromRequest(allocationTx, clientSign, clientSignV2, publicKey) if !valid || err != nil { - return nil, common.NewError("invalid_signature", "could not verify the allocation owner") - } - - var latestWM *writemarker.WriteMarkerEntity - var prevWM *writemarker.WriteMarkerEntity - if allocationObj.AllocationRoot == "" { - latestWM = nil - } else { - latestWM, err = writemarker.GetWriteMarkerEntity(ctx, allocationObj.AllocationRoot) if err != nil { - Logger.Error("[latest_write_marker]", zap.String("allocation_root", allocationObj.AllocationRoot), zap.String("allocation_id", allocationObj.ID)) - return nil, common.NewError("latest_write_marker_read_error", "Error reading the latest write marker for allocation. "+err.Error()) - } - if latestWM == nil { - Logger.Info("[latest_write_marker]", zap.String("allocation_root", allocationObj.AllocationRoot), zap.String("allocation_id", allocationObj.ID)) - return nil, common.NewError("latest_write_marker_read_error", "Latest write marker not found for allocation.") - } - if latestWM.WM.PreviousAllocationRoot != "" { - prevWM, err = writemarker.GetWriteMarkerEntity(ctx, latestWM.WM.PreviousAllocationRoot) - if err != nil { - return nil, common.NewError("latest_write_marker_read_error", "Error reading the previous write marker for allocation."+err.Error()) - } + return nil, common.NewError("invalid_signature", "could not verify the allocation owner"+err.Error()) } + return nil, common.NewError("invalid_signature", "could not verify the allocation owner"+": "+publicKey+": "+allocationId+": "+allocationTx+": "+clientSignV2) } - var result blobberhttp.LatestWriteMarkerResult - result.Version = writemarker.MARKER_VERSION - if latestWM != nil { - if latestWM.Status == writemarker.Committed { - latestWM.WM.ChainLength = 0 // start a new chain + var vm *writemarker.VersionMarker + if allocationObj.AllocationVersion != 0 { + vm, err = writemarker.GetVersionMarker(ctx, allocationId, allocationObj.AllocationVersion) + if err != nil { + return nil, common.NewError("latest_write_marker_read_error", "Error reading the latest write marker for allocation."+err.Error()) } - result.LatestWM = &latestWM.WM - } - if prevWM != nil { - result.PrevWM = &prevWM.WM + } else { + vm = &writemarker.VersionMarker{} } - return &result, nil + result := &blobberhttp.LatestVersionMarkerResult{ + VersionMarker: vm, + } + return result, nil } func (fsh *StorageHandler) GetReferencePath(ctx context.Context, r *http.Request) (*blobberhttp.ReferencePathResult, error) { @@ -626,7 +620,8 @@ func (fsh *StorageHandler) getReferencePath(ctx context.Context, r *http.Request if allocationObj.AllocationRoot == "" { latestWM = nil } else { - latestWM, err = writemarker.GetWriteMarkerEntity(ctx, rootRef.Hash) + //TODO: remove latestWM + latestWM, err = writemarker.GetWriteMarkerEntity(ctx, rootRef.FileMetaHash) if err != nil { errCh <- common.NewError("latest_write_marker_read_error", "Error reading the latest write marker for allocation."+err.Error()) return @@ -834,15 +829,16 @@ func (fsh *StorageHandler) GetRefs(ctx context.Context, r *http.Request) (*blobb return nil, common.NewError("invalid_parameters", "empty path and authtoken") } - var pathRef *reference.Ref + var pathRef *reference.PaginatedRef switch { case path != "": pathHash = reference.GetReferenceLookup(allocationID, path) fallthrough case pathHash != "": - pathRef, err = reference.GetReferenceByLookupHash(ctx, allocationID, pathHash) + pathRef, err = reference.GetPaginatedRefByLookupHash(ctx, pathHash) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { + logging.Logger.Error("GetRefs: GetPaginatedRefByLookupHash", zap.Error(err), zap.String("path", path), zap.String("pathHash", pathHash)) return nil, common.NewError("invalid_path", "") } return nil, err @@ -874,7 +870,7 @@ func (fsh *StorageHandler) GetRefs(ctx context.Context, r *http.Request) (*blobb } if pathRef == nil { - pathRef, err = reference.GetReferenceByLookupHash(ctx, allocationID, authToken.FilePathHash) + pathRef, err = reference.GetPaginatedRefByLookupHash(ctx, authToken.FilePathHash) if err != nil { return nil, fsh.convertGormError(err) } @@ -910,13 +906,16 @@ func (fsh *StorageHandler) GetRefs(ctx context.Context, r *http.Request) (*blobb pageLimit = o } } - + var offsetTime int offsetPath := r.FormValue("offsetPath") offsetDate := r.FormValue("offsetDate") updatedDate := r.FormValue("updatedDate") err = checkValidDate(offsetDate, OffsetDateLayout) if err != nil { - return nil, err + offsetTime, err = strconv.Atoi(offsetDate) + if err != nil { + return nil, err + } } err = checkValidDate(updatedDate, OffsetDateLayout) if err != nil { @@ -945,8 +944,11 @@ func (fsh *StorageHandler) GetRefs(ctx context.Context, r *http.Request) (*blobb switch { case refType == "regular": refs, totalPages, newOffsetPath, err = reference.GetRefs( - ctx, allocationID, path, offsetPath, fileType, level, pageLimit, + ctx, allocationID, path, offsetPath, fileType, level, pageLimit, offsetTime, pathRef, ) + if refs != nil { + logging.Logger.Info("GetRefs: regular", zap.Int("refs", len(*refs)), zap.Int("totalPages", totalPages), zap.String("newOffsetPath", newOffsetPath), zap.Error(err)) + } case refType == "updated": refs, totalPages, newOffsetPath, newOffsetDate, err = reference.GetUpdatedRefs( @@ -961,24 +963,11 @@ func (fsh *StorageHandler) GetRefs(ctx context.Context, r *http.Request) (*blobb if err != nil { return nil, err } - var latestWM *writemarker.WriteMarkerEntity - if allocationObj.AllocationRoot == "" { - latestWM = nil - } else { - latestWM, err = writemarker.GetWriteMarkerEntity(ctx, allocationObj.AllocationRoot) - if err != nil { - return nil, common.NewError("latest_write_marker_read_error", "Error reading the latest write marker for allocation."+err.Error()) - } - } - var refResult blobberhttp.RefResult refResult.Refs = refs refResult.TotalPages = totalPages refResult.OffsetPath = newOffsetPath refResult.OffsetDate = newOffsetDate - if latestWM != nil { - refResult.LatestWM = &latestWM.WM - } // Refs will be returned as it is and object tree will be build in client side return &refResult, nil } @@ -1000,7 +989,7 @@ func verifySignatureFromRequest(alloc, signV1, signV2, pbK string) (bool, error) hash = encryption.Hash(hashData) } if len(sign) < 64 { - return false, nil + return false, common.NewError("500", "len shorter than 64") } return encryption.Verify(pbK, sign, hash) } diff --git a/code/go/0chain.net/blobbercore/handler/tests_common_test.go b/code/go/0chain.net/blobbercore/handler/tests_common_test.go index 78fa2dcb7..461a042e9 100644 --- a/code/go/0chain.net/blobbercore/handler/tests_common_test.go +++ b/code/go/0chain.net/blobbercore/handler/tests_common_test.go @@ -15,9 +15,11 @@ import ( "strings" "testing" + coreNetwork "github.com/0chain/gosdk_common/core/conf" + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/filestore" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/core/zcncrypto" + "github.com/0chain/gosdk_common/zcncore" ) func setup(t *testing.T) { @@ -30,7 +32,7 @@ func setup(t *testing.T) { if err != nil { t.Fatal(err) } - if err := zcncore.SetWalletInfo(string(wBlob), true); err != nil { + if err := zcncore.SetWalletInfo(string(wBlob), "bls0chain", true); err != nil { t.Fatal(err) } @@ -41,10 +43,10 @@ func setup(t *testing.T) { }, ), ) - server := httptest.NewServer( + _ = httptest.NewServer( http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - n := zcncore.Network{Miners: []string{"miner 1"}, Sharders: []string{sharderServ.URL}} + n := coreNetwork.Network{Miners: []string{"miner 1"}, Sharders: []string{sharderServ.URL}} blob, err := json.Marshal(n) if err != nil { t.Fatal(err) @@ -57,9 +59,9 @@ func setup(t *testing.T) { ), ) - if err := zcncore.InitZCNSDK(server.URL, "ed25519"); err != nil { - t.Fatal(err) - } + // if err := zcncore.InitZCNSDK(server.URL, "ed25519"); err != nil { + // t.Fatal(err) + // } } type MockFileStore struct { @@ -76,11 +78,9 @@ func (mfs *MockFileStore) WriteFile(allocID, connID string, b := bytes.NewBuffer(make([]byte, 0)) n, _ := io.Copy(b, infile) return &filestore.FileOutputData{ - Name: fileData.Name, - Path: fileData.Path, - FixedMerkleRoot: "", - ValidationRoot: fileData.ValidationRoot, - Size: n, + Name: fileData.Name, + Path: fileData.Path, + Size: n, }, nil } @@ -119,6 +119,10 @@ func (mfs *MockFileStore) GetTempFilePath(allocID, connID, fileName, filePathHas return "" } +func (mfs *MockFileStore) CopyFile(allocationID, oldFileLookupHash, newFileLookupHash string) error { + return nil +} + func (mfs *MockFileStore) GetFileBlock(in *filestore.ReadBlockInput) (*filestore.FileDownloadResponse, error) { return &filestore.FileDownloadResponse{ Data: mockFileBlock, diff --git a/code/go/0chain.net/blobbercore/handler/worker.go b/code/go/0chain.net/blobbercore/handler/worker.go index b5881b4bb..b5acbe174 100644 --- a/code/go/0chain.net/blobbercore/handler/worker.go +++ b/code/go/0chain.net/blobbercore/handler/worker.go @@ -39,6 +39,10 @@ func cleanupAllocationFiles(ctx context.Context, allocationObj allocation.Alloca db := datastore.GetStore().GetTransaction(ctx) _ = filestore.GetFileStore().IterateObjects(allocationObj.ID, func(hash string, contentSize int64) { + // thumbnail suffix makes hash greater than 65 + if len(hash) > 65 { + return + } var refs []reference.Ref version := 0 if len(hash) > 64 { @@ -46,8 +50,7 @@ func cleanupAllocationFiles(ctx context.Context, allocationObj allocation.Alloca hash = hash[:64] } err := db.Table((reference.Ref{}).TableName()). - Where(reference.Ref{ValidationRoot: hash, Type: reference.FILE}). - Or(reference.Ref{ThumbnailHash: hash, Type: reference.FILE}). + Where(reference.Ref{LookupHash: hash, Type: reference.FILE}). Find(&refs).Error if err != nil { @@ -84,6 +87,10 @@ func cleanupTempFiles(ctx context.Context) { for i := 0; i < len(openConnectionsToDelete); i++ { connection := &openConnectionsToDelete[i] logging.Logger.Info("Deleting temp files for the connection", zap.Any("connection", connection.ID)) + processor := allocation.GetConnectionProcessor(connection.ID) + if processor != nil { + continue + } connection.ComputeProperties() nctx := datastore.GetStore().CreateTransaction(ctx) diff --git a/code/go/0chain.net/blobbercore/handler/zcncore.go b/code/go/0chain.net/blobbercore/handler/zcncore.go deleted file mode 100644 index 36df8290a..000000000 --- a/code/go/0chain.net/blobbercore/handler/zcncore.go +++ /dev/null @@ -1,128 +0,0 @@ -package handler - -import ( - "sync" - - "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/zcncore" -) - -type ZCNStatus struct { - wg *sync.WaitGroup - success bool - balance int64 - info string -} - -func (zcn *ZCNStatus) OnBalanceAvailable(status int, value int64, info string) { - defer zcn.wg.Done() - if status == zcncore.StatusSuccess { - zcn.success = true - } else { - zcn.success = false - } - zcn.balance = value -} - -func (zcn *ZCNStatus) OnInfoAvailable(op, status int, info, err string) { - defer zcn.wg.Done() - if status == zcncore.StatusSuccess { - zcn.success = true - } else { - zcn.success = false - } - zcn.info = info -} - -func (zcn *ZCNStatus) OnTransactionComplete(t *zcncore.Transaction, status int) { - defer zcn.wg.Done() - if status == zcncore.StatusSuccess { - zcn.success = true - } else { - zcn.success = false - } -} - -func (zcn *ZCNStatus) OnVerifyComplete(t *zcncore.Transaction, status int) { - defer zcn.wg.Done() - if status == zcncore.StatusSuccess { - zcn.success = true - } else { - zcn.success = false - } -} - -func (zcn *ZCNStatus) OnAuthComplete(t *zcncore.Transaction, status int) {} - -func CheckBalance() (float64, error) { - wg := &sync.WaitGroup{} - statusBar := &ZCNStatus{wg: wg} - wg.Add(1) - err := zcncore.GetBalance(statusBar) - if err != nil { - return 0, common.NewError("check_balance_failed", "Call to GetBalance failed with err: "+err.Error()) - } - wg.Wait() - if !statusBar.success { - return 0, nil - } - return zcncore.ConvertToToken(statusBar.balance), nil -} - -func CallFaucet() error { - wg := &sync.WaitGroup{} - statusBar := &ZCNStatus{wg: wg} - txn, err := zcncore.NewTransaction(statusBar, 0, 0) - if err != nil { - return common.NewError("call_faucet_failed", "Failed to create new transaction with err: "+err.Error()) - } - wg.Add(1) - _, err = txn.ExecuteSmartContract(zcncore.FaucetSmartContractAddress, "pour", "Blobber Registration", zcncore.ConvertToValue(0)) - if err != nil { - return common.NewError("call_faucet_failed", "Failed to execute smart contract with err: "+err.Error()) - } - wg.Wait() - if !statusBar.success { - return common.NewError("call_faucet_failed", "Failed to execute smart contract with statusBar success failed") - } - statusBar.success = false - wg.Add(1) - err = txn.Verify() - if err != nil { - return common.NewError("call_faucet_failed", "Failed to verify smart contract with err: "+err.Error()) - } - wg.Wait() - if !statusBar.success { - return common.NewError("call_faucet_failed", "Failed to verify smart contract with statusBar success failed") - } - return nil -} - -func Transfer(token float64, clientID string) error { - wg := &sync.WaitGroup{} - statusBar := &ZCNStatus{wg: wg} - txn, err := zcncore.NewTransaction(statusBar, 0, 0) - if err != nil { - return common.NewError("call_transfer_failed", "Failed to create new transaction with err: "+err.Error()) - } - wg.Add(1) - err = txn.Send(clientID, zcncore.ConvertToValue(token), "Blobber delegate transfer") - if err != nil { - return common.NewError("call_transfer_failed", "Failed to send tokens with err: "+err.Error()) - } - wg.Wait() - if !statusBar.success { - return common.NewError("call_transfer_failed", "Failed to send tokens with statusBar success failed") - } - statusBar.success = false - wg.Add(1) - err = txn.Verify() - if err != nil { - return common.NewError("call_transfer_failed", "Failed to verify send transaction with err: "+err.Error()) - } - wg.Wait() - if !statusBar.success { - return common.NewError("call_transfer_failed", "Failed to verify send transaction with statusBar success failed") - } - return nil -} diff --git a/code/go/0chain.net/blobbercore/mock/ctx.go b/code/go/0chain.net/blobbercore/mock/ctx.go index b41344c2e..ac7195abf 100644 --- a/code/go/0chain.net/blobbercore/mock/ctx.go +++ b/code/go/0chain.net/blobbercore/mock/ctx.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" ) func SetupHandlerContext(ctx context.Context, r *http.Request, allocation string) context.Context { diff --git a/code/go/0chain.net/blobbercore/mock/init.go b/code/go/0chain.net/blobbercore/mock/init.go index e1c2fa978..624cb850c 100644 --- a/code/go/0chain.net/blobbercore/mock/init.go +++ b/code/go/0chain.net/blobbercore/mock/init.go @@ -6,9 +6,9 @@ import ( "net/http" "net/http/httptest" - "github.com/0chain/gosdk/core/zcncrypto" - "github.com/0chain/gosdk/sdks" - "github.com/0chain/gosdk/sdks/blobber" + "github.com/0chain/gosdk_common/core/zcncrypto" + "github.com/0chain/gosdk_common/sdks" + "github.com/0chain/gosdk_common/sdks/blobber" ) const ( diff --git a/code/go/0chain.net/blobbercore/readmarker/protocol.go b/code/go/0chain.net/blobbercore/readmarker/protocol.go index e568f0070..51423fc89 100644 --- a/code/go/0chain.net/blobbercore/readmarker/protocol.go +++ b/code/go/0chain.net/blobbercore/readmarker/protocol.go @@ -6,12 +6,9 @@ import ( "time" "github.com/0chain/blobber/code/go/0chain.net/core/common" - zLogger "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" "gorm.io/gorm" - - "go.uber.org/zap" ) type ReadRedeem struct { @@ -41,35 +38,7 @@ func (rme *ReadMarkerEntity) PendNumBlocks() (pendNumBlocks int64, err error) { // RedeemReadMarker redeems the read marker. func (rme *ReadMarkerEntity) RedeemReadMarker(ctx context.Context) (err error) { - tx, err := transaction.NewTransactionEntity() - if err != nil { - return common.NewErrorf("redeem_read_marker", "creating transaction: %v", err) - } - - sn := &ReadRedeem{ - ReadMarker: rme.LatestRM, - } - - err = tx.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, transaction.READ_REDEEM, sn, 0) - if err != nil { - zLogger.Logger.Info("Failed submitting read redeem", zap.Error(err)) - return common.NewErrorf("redeem_read_marker", "sending transaction: %v", err) - } - - time.Sleep(transaction.SLEEP_FOR_TXN_CONFIRMATION * time.Second) - - var logHash = tx.Hash // keep transaction hash for error logs - tx, err = transaction.VerifyTransactionWithNonce(tx.Hash, tx.GetTransaction().GetTransactionNonce()) - if err != nil { - zLogger.Logger.Error("Error verifying the read redeem transaction", zap.Error(err), zap.String("txn", logHash)) - return common.NewErrorf("redeem_read_marker", "verifying transaction: %v", err) - } - - err = rme.UpdateStatus(ctx, tx.TransactionOutput, tx.Hash) - if err != nil { - return common.NewErrorf("redeem_read_marker", "updating read marker status: %v", err) - } - + // Depreciated return } diff --git a/code/go/0chain.net/blobbercore/readmarker/readmarker.go b/code/go/0chain.net/blobbercore/readmarker/readmarker.go index b2df8007a..2fce04489 100644 --- a/code/go/0chain.net/blobbercore/readmarker/readmarker.go +++ b/code/go/0chain.net/blobbercore/readmarker/readmarker.go @@ -12,7 +12,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/encryption" zLogger "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "go.uber.org/zap" "gorm.io/gorm" ) diff --git a/code/go/0chain.net/blobbercore/reference/dbCollector.go b/code/go/0chain.net/blobbercore/reference/dbCollector.go index 37c3cc808..5531bcf60 100644 --- a/code/go/0chain.net/blobbercore/reference/dbCollector.go +++ b/code/go/0chain.net/blobbercore/reference/dbCollector.go @@ -2,48 +2,131 @@ package reference import ( "context" + "sync" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" + "go.uber.org/zap" ) type QueryCollector interface { CreateRefRecord(ref *Ref) DeleteRefRecord(ref *Ref) - Finalize(ctx context.Context) error + Finalize(ctx context.Context, allocationID string, allocationVersion int64) error + AddToCache(ref *Ref) + GetFromCache(lookupHash string) *Ref + DeleteLookupRefRecord(ref *Ref) + LockTransaction() + UnlockTransaction() } type dbCollector struct { createdRefs []*Ref deletedRefs []*Ref + refCache RefCache + refMap map[string]*Ref + txnLock sync.Mutex + lock sync.Mutex } +type RefCache struct { + AllocationVersion int64 + CreatedRefs []*Ref + DeletedRefs []*Ref +} + +var ( + cacheMap = make(map[string]*RefCache) + cacheMapLock sync.RWMutex +) + func NewCollector(changes int) QueryCollector { return &dbCollector{ - createdRefs: make([]*Ref, 0, changes*4), - deletedRefs: make([]*Ref, 0, changes*4), + createdRefs: make([]*Ref, 0, changes*2), + deletedRefs: make([]*Ref, 0, changes*2), + refCache: RefCache{ + CreatedRefs: make([]*Ref, 0, changes), + DeletedRefs: make([]*Ref, 0, changes), + }, + refMap: make(map[string]*Ref), } } func (dc *dbCollector) CreateRefRecord(ref *Ref) { + dc.lock.Lock() dc.createdRefs = append(dc.createdRefs, ref) + if ref.Type == FILE { + dc.refCache.CreatedRefs = append(dc.refCache.CreatedRefs, ref) + } + dc.lock.Unlock() } func (dc *dbCollector) DeleteRefRecord(ref *Ref) { + dc.lock.Lock() dc.deletedRefs = append(dc.deletedRefs, ref) + if ref.Type == FILE { + dc.refCache.DeletedRefs = append(dc.refCache.DeletedRefs, ref) + } + dc.lock.Unlock() } -func (dc *dbCollector) Finalize(ctx context.Context) error { +func (dc *dbCollector) DeleteLookupRefRecord(ref *Ref) { + dc.refCache.DeletedRefs = append(dc.refCache.DeletedRefs, ref) +} + +func (dc *dbCollector) Finalize(ctx context.Context, allocationID string, allocationVersion int64) error { db := datastore.GetStore().GetTransaction(ctx) if len(dc.deletedRefs) > 0 { - err := db.Delete(dc.deletedRefs).Error + err := db.Delete(&(dc.deletedRefs)).Error if err != nil { return err } } - err := db.Create(dc.createdRefs).Error - if err != nil { - return err + if len(dc.createdRefs) > 0 { + err := db.Create(&(dc.createdRefs)).Error + if err != nil { + for ind, ref := range dc.createdRefs { + logging.Logger.Error("create_ref_error", zap.String("lookup_hash", ref.LookupHash), zap.String("path", ref.Path), zap.Int("index", ind), zap.Int64("allocation_version", allocationVersion)) + } + return err + } } - + dc.refCache.AllocationVersion = allocationVersion + cacheMapLock.Lock() + cacheMap[allocationID] = &(dc.refCache) + logging.Logger.Info("Finalize", zap.Int("created", len(dc.createdRefs)), zap.Int("deleted", len(dc.deletedRefs)), zap.Int64("allocation_version", cacheMap[allocationID].AllocationVersion), zap.String("allocation_id", allocationID)) + cacheMapLock.Unlock() return nil } + +func (dc *dbCollector) AddToCache(ref *Ref) { + dc.lock.Lock() + dc.refMap[ref.LookupHash] = ref + dc.lock.Unlock() +} + +func (dc *dbCollector) GetFromCache(lookupHash string) *Ref { + dc.lock.Lock() + defer dc.lock.Unlock() + return dc.refMap[lookupHash] +} + +func GetRefCache(allocationID string) *RefCache { + cacheMapLock.RLock() + defer cacheMapLock.RUnlock() + return cacheMap[allocationID] +} + +func DeleteRefCache(allocationID string) { + cacheMapLock.Lock() + cacheMap[allocationID] = nil + cacheMapLock.Unlock() +} + +func (dc *dbCollector) LockTransaction() { + dc.txnLock.Lock() +} + +func (dc *dbCollector) UnlockTransaction() { + dc.txnLock.Unlock() +} diff --git a/code/go/0chain.net/blobbercore/reference/object.go b/code/go/0chain.net/blobbercore/reference/object.go index 31d2f0f87..f0a721ed0 100644 --- a/code/go/0chain.net/blobbercore/reference/object.go +++ b/code/go/0chain.net/blobbercore/reference/object.go @@ -2,74 +2,45 @@ package reference import ( "context" - "path/filepath" + "database/sql" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" + "go.uber.org/zap" ) -func DeleteObject(ctx context.Context, rootRef *Ref, allocationID, objPath string, ts common.Timestamp) error { - likePath := objPath + "/%" - if objPath == "/" { - likePath = "/%" - } - +func DeleteObject(ctx context.Context, allocationID, lookupHash, _type string, ts common.Timestamp, allocationVersion int64, collector QueryCollector) error { db := datastore.GetStore().GetTransaction(ctx) - - err := db.Exec("UPDATE reference_objects SET is_precommit=? WHERE allocation_id=? AND path != ? AND (path=? OR path LIKE ?)", true, allocationID, "/", objPath, likePath).Error - if err != nil { - return err - } - - err = db.Delete(&Ref{}, "allocation_id=? AND path != ? AND (path=? OR path LIKE ?)", - allocationID, "/", objPath, likePath).Error - - if err != nil { - return err - } - if objPath == "/" { - rootRef.Children = nil - rootRef.HashToBeComputed = true - rootRef.childrenLoaded = true - rootRef.UpdatedAt = ts - return nil + if _type == DIRECTORY { + ref, err := GetLimitedRefFieldsByLookupHashWith(ctx, allocationID, lookupHash, []string{"id", "type"}) + if err != nil { + logging.Logger.Error("delete_object_error", zap.Error(err)) + return err + } + isEmpty, err := IsDirectoryEmpty(ctx, ref.ID) + if err != nil { + logging.Logger.Error("delete_object_error", zap.Error(err)) + return err + } + if !isEmpty { + return common.NewError("invalid_operation", "Directory is not empty") + } + _type = ref.Type } - parentPath, deleteFileName := filepath.Split(objPath) - rootRef.UpdatedAt = ts - fields, err := common.GetPathFields(parentPath) + err := db.Exec("UPDATE reference_objects SET deleted_at=? WHERE lookup_hash=?", sql.NullTime{ + Time: common.ToTime(ts), + Valid: true, + }, lookupHash).Error if err != nil { + logging.Logger.Error("delete_object_error", zap.Error(err)) return err } - - dirRef := rootRef - - for _, name := range fields { - var found bool - for _, ref := range dirRef.Children { - if ref.Name == name { - ref.HashToBeComputed = true - ref.childrenLoaded = true - ref.UpdatedAt = ts - found = true - dirRef = ref - break - } - } - - if !found { - return common.NewError("invalid_reference_path", "Reference path has invalid references") + if _type == FILE { + deletedRef := &Ref{ + LookupHash: lookupHash, } + collector.DeleteLookupRefRecord(deletedRef) } - - for i, child := range dirRef.Children { - basePath := filepath.Base(child.Path) - if basePath == deleteFileName || child.Path == objPath { - dirRef.RemoveChild(i) - break - } - } - - rootRef.HashToBeComputed = true - rootRef.childrenLoaded = true - return nil + return err } diff --git a/code/go/0chain.net/blobbercore/reference/objectpath.go b/code/go/0chain.net/blobbercore/reference/objectpath.go index d9d19fcb3..34d817b35 100644 --- a/code/go/0chain.net/blobbercore/reference/objectpath.go +++ b/code/go/0chain.net/blobbercore/reference/objectpath.go @@ -31,7 +31,7 @@ func GetObjectPath(ctx context.Context, allocationID string, blockNum int64) (*O if rootRef.NumBlocks == 0 { var retObj ObjectPath - retObj.RootHash = rootRef.Hash + // retObj.RootHash = rootRef.Hash retObj.FileBlockNum = 0 result := rootRef.GetListingData(ctx) list := make([]map[string]interface{}, len(rootRef.Children)) @@ -68,7 +68,7 @@ func GetObjectPath(ctx context.Context, allocationID string, blockNum int64) (*O break } curRef, err = GetRefWithSortedChildren(ctx, allocationID, child.Path) - if err != nil || curRef.Hash == "" { + if err != nil { return nil, common.NewError("failed_object_path", "Failed to get the object path") } curResult = list[idx] @@ -80,7 +80,6 @@ func GetObjectPath(ctx context.Context, allocationID string, blockNum int64) (*O } var retObj ObjectPath - retObj.RootHash = rootRef.Hash retObj.Meta = curRef.GetListingData(ctx) retObj.Path = result retObj.FileBlockNum = remainingBlocks diff --git a/code/go/0chain.net/blobbercore/reference/ref.go b/code/go/0chain.net/blobbercore/reference/ref.go index 6c50cf1b3..53df6c7da 100644 --- a/code/go/0chain.net/blobbercore/reference/ref.go +++ b/code/go/0chain.net/blobbercore/reference/ref.go @@ -2,7 +2,7 @@ package reference import ( "context" - "errors" + "database/sql" "fmt" "math" "path/filepath" @@ -39,41 +39,33 @@ func init() { field := refType.Field(i) dirListTag := field.Tag.Get(DIR_LIST_TAG) - if dirListTag != "" { + if dirListTag != "" && dirListTag != "is_empty" && dirListTag != "allocation_version" { dirListFields = append(dirListFields, dirListTag) } } - dirListFields = append(dirListFields, "parent_path") + dirListFields = append(dirListFields, "parent_path", "id") } type Ref struct { ID int64 `gorm:"column:id;primaryKey"` - FileID string `gorm:"column:file_id" dirlist:"file_id" filelist:"file_id"` + ParentID *int64 `gorm:"column:parent_id"` Type string `gorm:"column:type;size:1" dirlist:"type" filelist:"type"` AllocationID string `gorm:"column:allocation_id;size:64;not null;index:idx_path_alloc,priority:1;index:idx_parent_path_alloc,priority:1;index:idx_validation_alloc,priority:1" dirlist:"allocation_id" filelist:"allocation_id"` LookupHash string `gorm:"column:lookup_hash;size:64;not null;index:idx_lookup_hash" dirlist:"lookup_hash" filelist:"lookup_hash"` Name string `gorm:"column:name;size:100;not null;index:idx_name_gin" dirlist:"name" filelist:"name"` // uses GIN tsvector index for full-text search Path string `gorm:"column:path;size:1000;not null;index:idx_path_alloc,priority:2;index:path_idx;index:idx_path_gin_trgm" dirlist:"path" filelist:"path"` FileMetaHash string `gorm:"column:file_meta_hash;size:64;not null" dirlist:"file_meta_hash" filelist:"file_meta_hash"` - Hash string `gorm:"column:hash;size:64;not null" dirlist:"hash" filelist:"hash"` NumBlocks int64 `gorm:"column:num_of_blocks;not null;default:0" dirlist:"num_of_blocks" filelist:"num_of_blocks"` - PathHash string `gorm:"column:path_hash;size:64;not null" dirlist:"path_hash" filelist:"path_hash"` ParentPath string `gorm:"column:parent_path;size:999;index:idx_parent_path_alloc,priority:2"` PathLevel int `gorm:"column:level;not null;default:0"` CustomMeta string `gorm:"column:custom_meta;not null" filelist:"custom_meta" dirlist:"custom_meta"` - ValidationRoot string `gorm:"column:validation_root;size:64;not null;index:idx_validation_alloc,priority:2" filelist:"validation_root"` - PrevValidationRoot string `gorm:"column:prev_validation_root" filelist:"prev_validation_root" json:"prev_validation_root"` - ValidationRootSignature string `gorm:"column:validation_root_signature;size:64" filelist:"validation_root_signature" json:"validation_root_signature,omitempty"` Size int64 `gorm:"column:size;not null;default:0" dirlist:"size" filelist:"size"` - FixedMerkleRoot string `gorm:"column:fixed_merkle_root;size:64;not null" filelist:"fixed_merkle_root"` ActualFileSize int64 `gorm:"column:actual_file_size;not null;default:0" dirlist:"actual_file_size" filelist:"actual_file_size"` ActualFileHashSignature string `gorm:"column:actual_file_hash_signature;size:64" filelist:"actual_file_hash_signature" json:"actual_file_hash_signature,omitempty"` ActualFileHash string `gorm:"column:actual_file_hash;size:64;not null" filelist:"actual_file_hash"` MimeType string `gorm:"column:mimetype;size:255;not null" filelist:"mimetype"` - AllocationRoot string `gorm:"column:allocation_root;size:64;not null"` ThumbnailSize int64 `gorm:"column:thumbnail_size;not null;default:0" filelist:"thumbnail_size"` ThumbnailHash string `gorm:"column:thumbnail_hash;size:64;not null" filelist:"thumbnail_hash"` - PrevThumbnailHash string `gorm:"column:prev_thumbnail_hash" filelist:"prev_thumbnail_hash"` ActualThumbnailSize int64 `gorm:"column:actual_thumbnail_size;not null;default:0" filelist:"actual_thumbnail_size"` ActualThumbnailHash string `gorm:"column:actual_thumbnail_hash;size:64;not null" filelist:"actual_thumbnail_hash"` EncryptedKey string `gorm:"column:encrypted_key;size:64" filelist:"encrypted_key"` @@ -84,11 +76,14 @@ type Ref struct { UpdatedAt common.Timestamp `gorm:"column:updated_at;index:idx_updated_at,sort:desc;" dirlist:"updated_at" filelist:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"column:deleted_at"` // soft deletion - IsPrecommit bool `gorm:"column:is_precommit;not null;default:false" filelist:"is_precommit" dirlist:"is_precommit"` ChunkSize int64 `gorm:"column:chunk_size;not null;default:65536" dirlist:"chunk_size" filelist:"chunk_size"` NumUpdates int64 `gorm:"column:num_of_updates" json:"num_of_updates"` NumBlockDownloads int64 `gorm:"column:num_of_block_downloads" json:"num_of_block_downloads"` FilestoreVersion int `gorm:"column:filestore_version" json:"-"` + DataHash string `gorm:"column:data_hash" filelist:"data_hash"` + DataHashSignature string `gorm:"column:data_hash_signature" filelist:"data_hash_signature"` + AllocationVersion int64 `gorm:"allocation_version" dirlist:"allocation_version" filelist:"allocation_version"` + IsEmpty bool `gorm:"-" dirlist:"is_empty"` HashToBeComputed bool `gorm:"-"` prevID int64 `gorm:"-"` } @@ -117,34 +112,27 @@ func (Ref) TableName() string { type PaginatedRef struct { //Gorm smart select fields. ID int64 `gorm:"column:id" json:"id,omitempty"` - FileID string `gorm:"file_id" json:"file_id"` Type string `gorm:"column:type" json:"type,omitempty"` AllocationID string `gorm:"column:allocation_id" json:"allocation_id,omitempty"` LookupHash string `gorm:"column:lookup_hash" json:"lookup_hash,omitempty"` Name string `gorm:"column:name" json:"name,omitempty"` Path string `gorm:"column:path" json:"path,omitempty"` - Hash string `gorm:"column:hash" json:"hash,omitempty"` - NumBlocks int64 `gorm:"column:num_of_blocks" json:"num_blocks,omitempty"` - PathHash string `gorm:"column:path_hash" json:"path_hash,omitempty"` + NumBlocks int64 `gorm:"column:num_of_blocks" json:"num_of_blocks,omitempty"` ParentPath string `gorm:"column:parent_path" json:"parent_path,omitempty"` PathLevel int `gorm:"column:level" json:"level,omitempty"` CustomMeta string `gorm:"column:custom_meta" json:"custom_meta,omitempty"` - ValidationRootSignature string `gorm:"column:validation_root_signature" json:"validation_root_signature,omitempty"` - ValidationRoot string `gorm:"column:validation_root" json:"validation_root,omitempty"` Size int64 `gorm:"column:size" json:"size,omitempty"` - FixedMerkleRoot string `gorm:"column:fixed_merkle_root" json:"fixed_merkle_root,omitempty"` ActualFileSize int64 `gorm:"column:actual_file_size" json:"actual_file_size,omitempty"` ActualFileHashSignature string `gorm:"column:actual_file_hash_signature" json:"actual_file_hash_signature,omitempty"` ActualFileHash string `gorm:"column:actual_file_hash" json:"actual_file_hash,omitempty"` MimeType string `gorm:"column:mimetype" json:"mimetype,omitempty"` - AllocationRoot string `gorm:"column:allocation_root" json:"allocation_root,omitempty"` ThumbnailSize int64 `gorm:"column:thumbnail_size" json:"thumbnail_size,omitempty"` ThumbnailHash string `gorm:"column:thumbnail_hash" json:"thumbnail_hash,omitempty"` ActualThumbnailSize int64 `gorm:"column:actual_thumbnail_size" json:"actual_thumbnail_size,omitempty"` ActualThumbnailHash string `gorm:"column:actual_thumbnail_hash" json:"actual_thumbnail_hash,omitempty"` EncryptedKey string `gorm:"column:encrypted_key" json:"encrypted_key,omitempty"` EncryptedKeyPoint string `gorm:"column:encrypted_key_point" json:"encrypted_key_point,omitempty"` - FileMetaHash string `gorm:"column:file_meta_hash;size:64;not null" dirlist:"file_meta_hash" filelist:"file_meta_hash"` + FileMetaHash string `gorm:"column:file_meta_hash;size:64;not null" json:"file_meta_hash"` CreatedAt common.Timestamp `gorm:"column:created_at" json:"created_at,omitempty"` UpdatedAt common.Timestamp `gorm:"column:updated_at" json:"updated_at,omitempty"` @@ -158,49 +146,128 @@ func GetReferenceLookup(allocationID, path string) string { } func NewDirectoryRef() *Ref { - return &Ref{Type: DIRECTORY, IsPrecommit: true} + return &Ref{Type: DIRECTORY} } func NewFileRef() *Ref { - return &Ref{Type: FILE, IsPrecommit: true} + return &Ref{Type: FILE} } // Mkdir create dirs if they don't exits. do nothing if dir exists. last dir will be return without child -func Mkdir(ctx context.Context, allocationID, destpath string) (*Ref, error) { - var dirRef *Ref +func Mkdir(ctx context.Context, allocationID, destpath string, allocationVersion int64, ts common.Timestamp, collector QueryCollector) (*Ref, error) { + var err error db := datastore.GetStore().GetTransaction(ctx) - // cleaning path to avoid edge case issues: append '/' prefix if not added and removing suffix '/' if added - destpath = strings.TrimSuffix(filepath.Clean("/"+destpath), "/") - dirs := strings.Split(destpath, "/") - - for i := range dirs { - currentPath := filepath.Join("/", filepath.Join(dirs[:i+1]...)) - ref, err := GetReference(ctx, allocationID, currentPath) - if err == nil { - dirRef = ref - continue + if destpath != "/" { + destpath = strings.TrimSuffix(filepath.Clean("/"+destpath), "/") + } + destLookupHash := GetReferenceLookup(allocationID, destpath) + var destRef *Ref + cachedRef := collector.GetFromCache(destLookupHash) + if cachedRef != nil { + destRef = cachedRef + } else { + destRef, err = GetReferenceByLookupHashWithNewTransaction(destLookupHash) + if err != nil && err != gorm.ErrRecordNotFound { + return nil, err } + if destRef != nil { + destRef.LookupHash = destLookupHash + defer collector.AddToCache(destRef) + } + } + if destRef != nil { + if destRef.Type != DIRECTORY { + return nil, common.NewError("invalid_dir_tree", "parent path is not a directory") + } + return destRef, nil + } + fields, err := common.GetAllParentPaths(destpath) + if err != nil { + logging.Logger.Error("mkdir: failed to get all parent paths", zap.Error(err), zap.String("destpath", destpath)) + return nil, err + } + parentLookupHashes := make([]string, 0, len(fields)) + for i := 0; i < len(fields); i++ { + parentLookupHashes = append(parentLookupHashes, GetReferenceLookup(allocationID, fields[i])) + } + var parentRefs []*Ref + collector.LockTransaction() + defer collector.UnlockTransaction() + cachedRef = collector.GetFromCache(destLookupHash) + if cachedRef != nil { + if cachedRef.Type != DIRECTORY { + return nil, common.NewError("invalid_dir_tree", "parent path is not a directory") + } + return cachedRef, nil + } else { + logging.Logger.Info("noEntryFound: ", zap.String("destLookupHash", destLookupHash), zap.String("destpath", destpath)) + } - if !errors.Is(err, gorm.ErrRecordNotFound) { - // unexpected sql error - return nil, err + tx := db.Model(&Ref{}).Select("id", "path", "type") + for i := 0; i < len(fields); i++ { + tx = tx.Or(Ref{LookupHash: parentLookupHashes[i]}) + } + err = tx.Order("path").Find(&parentRefs).Error + if err != nil && err != gorm.ErrRecordNotFound { + return nil, err + } + var ( + parentID int64 + parentPath = "/" + ) + if len(parentRefs) > 0 { + parentID = parentRefs[len(parentRefs)-1].ID + parentPath = parentRefs[len(parentRefs)-1].Path + for i := 0; i < len(parentRefs); i++ { + if parentRefs[i].Type != DIRECTORY { + return nil, common.NewError("invalid_dir_tree", "parent path is not a directory") + } + if parentRefs[i].ID == 0 { + return nil, common.NewError("invalid_dir_tree", "parent path not found") + } } + } + if destpath != "/" { + fields = append(fields, destpath) + parentLookupHashes = append(parentLookupHashes, destLookupHash) + } - // dir doesn't exists , create it + for i := len(parentRefs); i < len(fields); i++ { + logging.Logger.Info("mkdir: creating directory", zap.String("path", fields[i]), zap.Int("parentID", int(parentID))) + var parentIDRef *int64 + if parentID > 0 { + parentIDRef = &parentID + } else if parentPath != "/" { + return nil, common.NewError("invalid_dir_tree", "parent path not found") + } newRef := NewDirectoryRef() newRef.AllocationID = allocationID - newRef.Path = currentPath - newRef.ParentPath = filepath.Join("/", filepath.Join(dirs[:i]...)) - newRef.Name = dirs[i] - newRef.Type = DIRECTORY + newRef.Path = fields[i] + if newRef.Path != "/" { + newRef.ParentPath = parentPath + } + newRef.Name = filepath.Base(fields[i]) newRef.PathLevel = i + 1 - newRef.LookupHash = GetReferenceLookup(allocationID, newRef.Path) + newRef.ParentID = parentIDRef + newRef.LookupHash = parentLookupHashes[i] + newRef.CreatedAt = ts + newRef.UpdatedAt = ts + newRef.FileMetaHash = encryption.FastHash(newRef.GetFileMetaHashData()) + newRef.AllocationVersion = allocationVersion err = db.Create(newRef).Error if err != nil { + logging.Logger.Error("mkdir: failed to create directory", zap.Error(err), zap.String("path", fields[i])) return nil, err } + collector.AddToCache(newRef) + parentID = newRef.ID + parentPath = newRef.Path + } - dirRef = newRef + dirRef := &Ref{ + AllocationID: allocationID, + ID: parentID, + Path: parentPath, } return dirRef, nil @@ -262,6 +329,16 @@ func GetReferenceByLookupHash(ctx context.Context, allocationID, pathHash string return ref, nil } +func GetPaginatedRefByLookupHash(ctx context.Context, pathHash string) (*PaginatedRef, error) { + ref := &PaginatedRef{} + db := datastore.GetStore().GetTransaction(ctx) + err := db.Model(&Ref{}).Where(&Ref{LookupHash: pathHash}).Take(ref).Error + if err != nil { + return nil, err + } + return ref, nil +} + func GetReferenceByLookupHashForDownload(ctx context.Context, allocationID, pathHash string) (*Ref, error) { ref := &Ref{} db := datastore.GetStore().GetTransaction(ctx) @@ -307,6 +384,22 @@ func IsRefExist(ctx context.Context, allocationID, path string) (bool, error) { return Found, nil } +func GetObjectSizeByLookupHash(ctx context.Context, lookupHash string) (int64, error) { + db := datastore.GetStore().GetTransaction(ctx) + var size int64 + err := db.Model(&Ref{}). + Select("size"). + Where("lookup_hash = ?", lookupHash). + Take(&size).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return 0, nil + } + return 0, err + } + return size, nil +} + // GetRefsTypeFromPaths Give list of paths it will return refs of respective path with only Type and Path selected in sql query func GetRefsTypeFromPaths(ctx context.Context, allocationID string, paths []string) (refs []*Ref, err error) { if len(paths) == 0 { @@ -342,7 +435,7 @@ func GetSubDirsFromPath(p string) []string { func GetRefWithChildren(ctx context.Context, parentRef *Ref, allocationID, path string, offset, pageLimit int) (*Ref, error) { var refs []*Ref t := datastore.GetStore().GetTransaction(ctx) - db := t.Where(Ref{ParentPath: path, AllocationID: allocationID}) + db := t.Where(Ref{ParentID: &parentRef.ID}) err := db.Order("path"). Offset(offset). Limit(pageLimit). @@ -415,43 +508,38 @@ func (r *Ref) GetFileMetaHashData() string { func (fr *Ref) GetFileHashData() string { return fmt.Sprintf( - "%s:%s:%s:%s:%d:%s:%s:%d:%s:%d:%s", + "%s:%s:%s:%s:%d:%d:%s:%d", fr.AllocationID, fr.Type, // don't need to add it as well fr.Name, // don't see any utility as fr.Path below has name in it fr.Path, fr.Size, - fr.ValidationRoot, - fr.FixedMerkleRoot, fr.ActualFileSize, fr.ActualFileHash, fr.ChunkSize, - fr.FileID, ) } func (r *Ref) GetHashData() string { - return fmt.Sprintf("%s:%s:%s", r.AllocationID, r.Path, r.FileID) + return fmt.Sprintf("%s:%s", r.AllocationID, r.Path) } func (fr *Ref) CalculateFileHash(ctx context.Context, saveToDB bool, collector QueryCollector) (string, error) { fr.FileMetaHash = encryption.Hash(fr.GetFileMetaHashData()) - fr.Hash = encryption.Hash(fr.GetFileHashData()) fr.NumBlocks = int64(math.Ceil(float64(fr.Size*1.0) / float64(fr.ChunkSize))) fr.PathLevel = len(strings.Split(strings.TrimRight(fr.Path, "/"), "/")) fr.LookupHash = GetReferenceLookup(fr.AllocationID, fr.Path) - fr.PathHash = fr.LookupHash var err error if saveToDB && fr.HashToBeComputed { err = fr.SaveFileRef(ctx, collector) } - return fr.Hash, err + return fr.FileMetaHash, err } func (r *Ref) CalculateDirHash(ctx context.Context, saveToDB bool, collector QueryCollector) (h string, err error) { if !r.HashToBeComputed { - h = r.Hash + h = r.FileMetaHash return } @@ -464,9 +552,7 @@ func (r *Ref) CalculateDirHash(ctx context.Context, saveToDB bool, collector Que } }() - childHashes := make([]string, l) childFileMetaHashes := make([]string, l) - childPathHashes := make([]string, l) var refNumBlocks, size, actualSize int64 for i, childRef := range r.Children { @@ -478,22 +564,18 @@ func (r *Ref) CalculateDirHash(ctx context.Context, saveToDB bool, collector Que } childFileMetaHashes[i] = childRef.FileMetaHash - childHashes[i] = childRef.Hash - childPathHashes[i] = childRef.PathHash refNumBlocks += childRef.NumBlocks size += childRef.Size actualSize += childRef.ActualFileSize } r.FileMetaHash = encryption.Hash(r.Path + strings.Join(childFileMetaHashes, ":")) - r.Hash = encryption.Hash(r.GetHashData() + strings.Join(childHashes, ":")) - r.PathHash = encryption.Hash(strings.Join(childPathHashes, ":")) r.NumBlocks = refNumBlocks r.Size = size r.ActualFileSize = actualSize r.PathLevel = len(GetSubDirsFromPath(r.Path)) + 1 r.LookupHash = GetReferenceLookup(r.AllocationID, r.Path) - return r.Hash, err + return r.FileMetaHash, err } func (r *Ref) CalculateHash(ctx context.Context, saveToDB bool, collector QueryCollector) (string, error) { @@ -549,17 +631,8 @@ func (r *Ref) UpdatePath(newPath, parentPath string) { r.LookupHash = GetReferenceLookup(r.AllocationID, r.Path) } -func DeleteReference(ctx context.Context, refID int64, pathHash string) error { - if refID <= 0 { - return common.NewError("invalid_ref_id", "Invalid reference ID to delete") - } - db := datastore.GetStore().GetTransaction(ctx) - return db.Where("path_hash = ?", pathHash).Delete(&Ref{ID: refID}).Error -} - func (r *Ref) SaveFileRef(ctx context.Context, collector QueryCollector) error { r.prevID = r.ID - r.IsPrecommit = true r.NumUpdates += 1 if r.ID > 0 { deleteRef := &Ref{ID: r.ID} @@ -573,7 +646,6 @@ func (r *Ref) SaveFileRef(ctx context.Context, collector QueryCollector) error { func (r *Ref) SaveDirRef(ctx context.Context, collector QueryCollector) error { r.prevID = r.ID - r.IsPrecommit = true r.NumUpdates += 1 if r.ID > 0 { deleteRef := &Ref{ID: r.ID} @@ -675,3 +747,48 @@ func UpdateCustomMeta(ctx context.Context, ref *Ref, customMeta string) error { db := datastore.GetStore().GetTransaction(ctx) return db.Exec("UPDATE reference_objects SET custom_meta = ? WHERE id = ?", customMeta, ref.ID).Error } + +func IsDirectoryEmpty(ctx context.Context, id int64) (bool, error) { + db := datastore.GetStore().GetTransaction(ctx) + var ref Ref + err := db.Model(&Ref{}).Select("id").Where("parent_id = ?", &id).Take(&ref).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return true, nil + } + return false, err + } + if ref.ID > 0 { + return false, nil + } + + return true, nil +} + +func GetReferenceByLookupHashWithNewTransaction(lookupHash string) (*Ref, error) { + var ref *Ref + err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + txn := datastore.GetStore().GetTransaction(ctx) + return txn.Model(&Ref{}).Select("id", "type").Where("lookup_hash = ?", lookupHash).Take(&ref).Error + }, &sql.TxOptions{ + ReadOnly: true, + }) + if err != nil { + return nil, err + } + return ref, nil +} + +func GetFullReferenceByLookupHashWithNewTransaction(lookupHash string) (*Ref, error) { + var ref *Ref + err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + txn := datastore.GetStore().GetTransaction(ctx) + return txn.Model(&Ref{}).Where("lookup_hash = ?", lookupHash).Take(&ref).Error + }, &sql.TxOptions{ + ReadOnly: true, + }) + if err != nil { + return nil, err + } + return ref, nil +} diff --git a/code/go/0chain.net/blobbercore/reference/referencepath.go b/code/go/0chain.net/blobbercore/reference/referencepath.go index 458fe9b24..f2bfc7830 100644 --- a/code/go/0chain.net/blobbercore/reference/referencepath.go +++ b/code/go/0chain.net/blobbercore/reference/referencepath.go @@ -238,7 +238,7 @@ func GetObjectTree(ctx context.Context, allocationID, path string) (*Ref, error) // Might need to consider covering index for efficient search https://blog.crunchydata.com/blog/why-covering-indexes-are-incredibly-helpful // To retrieve refs efficiently form pagination index is created in postgresql on path column so it can be used to paginate refs // very easily and effectively; Same case for offsetDate. -func GetRefs(ctx context.Context, allocationID, path, offsetPath, _type string, level, pageLimit int) (refs *[]PaginatedRef, totalPages int, newOffsetPath string, err error) { +func GetRefs(ctx context.Context, allocationID, path, offsetPath, _type string, level, pageLimit, offsetTime int, parentRef *PaginatedRef) (refs *[]PaginatedRef, totalPages int, newOffsetPath string, err error) { var ( pRefs = make([]PaginatedRef, 0, pageLimit/4) dbError error @@ -246,22 +246,40 @@ func GetRefs(ctx context.Context, allocationID, path, offsetPath, _type string, ) path = filepath.Clean(path) tx := datastore.GetStore().GetTransaction(ctx) - pathLevel := len(strings.Split(strings.TrimSuffix(path, "/"), "/")) + 1 - if pathLevel == level { - dbQuery = tx.Model(&Ref{}).Where("allocation_id = ? AND parent_path = ? and level = ?", allocationID, path, level) + pathLevel := len(strings.Split(strings.TrimSuffix(path, "/"), "/")) + logging.Logger.Info("getRefs: CheckSingleRef", zap.Int("pathLevel", pathLevel), zap.Int("level", level), zap.String("path", path), zap.String("offsetPath", offsetPath), zap.String("type", _type), zap.Int("pageLimit", pageLimit)) + if (pageLimit == 1 && offsetPath == "" && (pathLevel == level || level == 0) && _type != FILE) || (parentRef != nil && parentRef.Type == FILE) { + pRefs = append(pRefs, *parentRef) + refs = &pRefs + newOffsetPath = parentRef.Path + return + } + + if pathLevel+1 == level { + dbQuery = tx.Model(&Ref{}).Where("parent_id = ?", parentRef.ID) if _type != "" { dbQuery = dbQuery.Where("type = ?", _type) } dbQuery = dbQuery.Where("path > ?", offsetPath) + if offsetTime != 0 { + dbQuery = dbQuery.Where("created_at < ?", offsetTime) + } dbQuery = dbQuery.Order("path") } else { - dbQuery = tx.Model(&Ref{}).Where("allocation_id = ? AND (path=? OR path LIKE ?)", allocationID, path, path+"%") + listPath := path + if path != "/" { + listPath = path + "/" + } + dbQuery = tx.Model(&Ref{}).Where("allocation_id = ? AND path LIKE ?", allocationID, listPath+"%") if _type != "" { dbQuery = dbQuery.Where("type = ?", _type) } if level != 0 { dbQuery = dbQuery.Where("level = ?", level) } + if offsetTime != 0 { + dbQuery = dbQuery.Where("created_at < ?", offsetTime) + } dbQuery = dbQuery.Where("path > ?", offsetPath) diff --git a/code/go/0chain.net/blobbercore/util/json.go b/code/go/0chain.net/blobbercore/util/json.go index dbfce39ac..39afbdbc1 100644 --- a/code/go/0chain.net/blobbercore/util/json.go +++ b/code/go/0chain.net/blobbercore/util/json.go @@ -5,14 +5,15 @@ import ( "reflect" "strings" - "github.com/0chain/gosdk/zboxcore/fileref" + "github.com/0chain/gosdk_common/zboxcore/fileref" ) // Validate unmarshalled data with tag-based rules // Example: -// struct { -// Name string `json:"name" validation:"required"` -// } +// +// struct { +// Name string `json:"name" validation:"required"` +// } func UnmarshalValidation(v interface{}) error { fields := reflect.ValueOf(v).Elem() diff --git a/code/go/0chain.net/blobbercore/writemarker/mutex.go b/code/go/0chain.net/blobbercore/writemarker/mutex.go index 4bf3663dd..006dd33ff 100644 --- a/code/go/0chain.net/blobbercore/writemarker/mutex.go +++ b/code/go/0chain.net/blobbercore/writemarker/mutex.go @@ -9,7 +9,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/errors" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "go.uber.org/zap" ) diff --git a/code/go/0chain.net/blobbercore/writemarker/protocol.go b/code/go/0chain.net/blobbercore/writemarker/protocol.go index c33c30d07..79c16b5bf 100644 --- a/code/go/0chain.net/blobbercore/writemarker/protocol.go +++ b/code/go/0chain.net/blobbercore/writemarker/protocol.go @@ -3,17 +3,15 @@ package writemarker import ( "context" "fmt" - "time" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" - "github.com/0chain/blobber/code/go/0chain.net/core/chain" "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/encryption" . "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" - "github.com/0chain/blobber/code/go/0chain.net/core/transaction" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" + "github.com/0chain/gosdk_common/core/transaction" "go.uber.org/zap" ) @@ -25,7 +23,19 @@ type CommitConnection struct { ChainData []byte `json:"chain_data"` } -const timeGap = 180 +const ( + ADD_BLOBBER_SC_NAME = "add_blobber" + UPDATE_BLOBBER_SC_NAME = "update_blobber_settings" + ADD_VALIDATOR_SC_NAME = "add_validator" + CLOSE_CONNECTION_SC_NAME = "commit_connection" + READ_REDEEM = "read_redeem" + CHALLENGE_RESPONSE = "challenge_response" + BLOBBER_HEALTH_CHECK = "blobber_health_check" + FINALIZE_ALLOCATION = "finalize_allocation" + VALIDATOR_HEALTH_CHECK = "validator_health_check" + STORAGE_CONTRACT_ADDRESS = "6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712d7" + timeGap = 180 +) // VerifyMarker verify WriteMarker's hash and check allocation_root if it is unique func (wme *WriteMarkerEntity) VerifyMarker(ctx context.Context, dbAllocation *allocation.Allocation, co *allocation.AllocationChangeCollector, latestWM *WriteMarkerEntity) error { @@ -113,7 +123,7 @@ func (wme *WriteMarkerEntity) VerifyMarker(ctx context.Context, dbAllocation *al func (wme *WriteMarkerEntity) redeemMarker(ctx context.Context, startSeq int64) error { if len(wme.CloseTxnID) > 0 { - t, err := transaction.VerifyTransaction(wme.CloseTxnID, chain.GetServerChain()) + t, err := transaction.VerifyTransaction(wme.CloseTxnID) if err == nil { wme.Status = Committed wme.StatusMessage = t.TransactionOutput @@ -123,19 +133,14 @@ func (wme *WriteMarkerEntity) redeemMarker(ctx context.Context, startSeq int64) } } - txn, err := transaction.NewTransactionEntity() - if err != nil { - wme.StatusMessage = "Error creating transaction entity. " + err.Error() - if err := wme.UpdateStatus(ctx, Failed, "Error creating transaction entity. "+err.Error(), "", startSeq, wme.Sequence); err != nil { - Logger.Error("WriteMarkerEntity_UpdateStatus", zap.Error(err)) - } - return err - } - + var out, hash string + var nonce int64 + txn := &transaction.Transaction{} sn := &CommitConnection{} sn.AllocationRoot = wme.WM.AllocationRoot sn.PrevAllocationRoot = wme.WM.PreviousAllocationRoot sn.WriteMarker = &wme.WM + var err error err = datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { sn.ChainData, err = GetMarkersForChain(ctx, wme.WM.AllocationID, startSeq, wme.Sequence-1) return err @@ -148,10 +153,9 @@ func (wme *WriteMarkerEntity) redeemMarker(ctx context.Context, startSeq int64) return err } - if sn.AllocationRoot == sn.PrevAllocationRoot { + if sn.AllocationRoot == sn.PrevAllocationRoot && wme.WM.Version != MARKER_VERSION { // get nonce of prev WM - var prevWM *WriteMarkerEntity - prevWM, err = GetPreviousWM(ctx, sn.AllocationRoot, wme.WM.Timestamp) + _, err = GetPreviousWM(ctx, sn.AllocationRoot, wme.WM.Timestamp) if err != nil { wme.StatusMessage = "Error getting previous write marker. " + err.Error() if err := wme.UpdateStatus(ctx, Failed, "Error getting previous write marker. "+err.Error(), "", startSeq, wme.Sequence); err != nil { @@ -159,10 +163,13 @@ func (wme *WriteMarkerEntity) redeemMarker(ctx context.Context, startSeq int64) } return err } - err = txn.ExecuteRollbackWM(transaction.STORAGE_CONTRACT_ADDRESS, transaction.CLOSE_CONNECTION_SC_NAME, sn, 0, prevWM.CloseTxnNonce) - } else { - err = txn.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, transaction.CLOSE_CONNECTION_SC_NAME, sn, 0) + } + + hash, out, nonce, txn, err = transaction.SmartContractTxn(STORAGE_CONTRACT_ADDRESS, transaction.SmartContractTxnData{ + Name: CLOSE_CONNECTION_SC_NAME, + InputArgs: sn, + }, true) if err != nil { Logger.Error("Failed during sending close connection to the miner. ", zap.String("err:", err.Error())) wme.Status = Failed @@ -173,23 +180,11 @@ func (wme *WriteMarkerEntity) redeemMarker(ctx context.Context, startSeq int64) return err } - time.Sleep(transaction.SLEEP_FOR_TXN_CONFIRMATION * time.Second) - t, err := transaction.VerifyTransactionWithNonce(txn.Hash, txn.GetTransaction().GetTransactionNonce()) wme.CloseTxnID = txn.Hash - wme.CloseTxnNonce = txn.GetTransaction().GetTransactionNonce() - if err != nil { - Logger.Error("Error verifying the close connection transaction", zap.String("err:", err.Error()), zap.String("txn", txn.Hash)) - wme.Status = Failed - wme.StatusMessage = "Error verifying the close connection transaction." + err.Error() - // TODO Is this single try? - if err := wme.UpdateStatus(ctx, Failed, "Error verifying the close connection transaction."+err.Error(), txn.Hash, startSeq, wme.Sequence); err != nil { - Logger.Error("WriteMarkerEntity_UpdateStatus", zap.Error(err)) - } - return err - } + wme.CloseTxnNonce = nonce wme.Status = Committed - wme.StatusMessage = t.TransactionOutput - err = wme.UpdateStatus(ctx, Committed, t.TransactionOutput, t.Hash, startSeq, wme.Sequence) + wme.StatusMessage = out + err = wme.UpdateStatus(ctx, Committed, out, hash, startSeq, wme.Sequence) return err } diff --git a/code/go/0chain.net/blobbercore/writemarker/version_marker.go b/code/go/0chain.net/blobbercore/writemarker/version_marker.go new file mode 100644 index 000000000..757c7723c --- /dev/null +++ b/code/go/0chain.net/blobbercore/writemarker/version_marker.go @@ -0,0 +1,69 @@ +package writemarker + +import ( + "context" + "fmt" + + "github.com/0chain/blobber/code/go/0chain.net/blobbercore/datastore" + "github.com/0chain/blobber/code/go/0chain.net/core/common" + "github.com/0chain/blobber/code/go/0chain.net/core/encryption" + "github.com/0chain/blobber/code/go/0chain.net/core/logging" + "go.uber.org/zap" +) + +type VersionMarker struct { + ID int64 `gorm:"column:id;primaryKey"` + ClientID string `gorm:"client_id" json:"client_id"` + BlobberID string `gorm:"blobber_id" json:"blobber_id"` + AllocationID string `gorm:"allocation_id" json:"allocation_id"` + Version int64 `gorm:"version" json:"version"` + Timestamp int64 `gorm:"timestamp" json:"timestamp"` + Signature string `gorm:"signature" json:"signature"` + IsRepair bool `gorm:"is_repair" json:"is_repair"` + RepairVersion int64 `gorm:"repair_version" json:"repair_version"` + RepairOffset string `gorm:"repair_offset" json:"repair_offset"` +} + +func (VersionMarker) TableName() string { + return "version_markers" +} + +func GetCurrentVersion(ctx context.Context, allocationID string) (*VersionMarker, error) { + db := datastore.GetStore().GetTransaction(ctx) + var vm VersionMarker + err := db.Where("allocation_id = ?", allocationID).Order("id DESC").Take(&vm).Error + return &vm, err +} + +func GetVersionMarker(ctx context.Context, allocationID string, version int64) (*VersionMarker, error) { + db := datastore.GetStore().GetTransaction(ctx) + var vm VersionMarker + err := db.Where("allocation_id = ? and version = ?", allocationID, version).Order("id DESC").Take(&vm).Error + return &vm, err +} + +func (vm *VersionMarker) Verify(allocationID, clientPubKey string) error { + if vm.AllocationID != allocationID { + return common.NewError("version_marker_validation_failed", "Invalid allocation id") + } + + if vm.Signature == "" { + return common.NewError("version_marker_validation_failed", "Signature is missing") + } + + hashData := vm.GetHashData() + signatureHash := encryption.Hash(hashData) + sigOK, err := encryption.Verify(clientPubKey, vm.Signature, signatureHash) + if err != nil { + return common.NewError("version_marker_validation_failed", "Error during verifying signature. "+err.Error()) + } + if !sigOK { + logging.Logger.Error("write_marker_sig_error", zap.Any("vm", vm)) + return common.NewError("version_marker_validation_failed", "Version marker signature is not valid") + } + return nil +} + +func (vm *VersionMarker) GetHashData() string { + return fmt.Sprintf("%s:%s:%s:%d:%d", vm.AllocationID, vm.ClientID, vm.BlobberID, vm.Version, vm.Timestamp) +} diff --git a/code/go/0chain.net/blobbercore/writemarker/worker.go b/code/go/0chain.net/blobbercore/writemarker/worker.go index 7d5a3f924..7801a7c82 100644 --- a/code/go/0chain.net/blobbercore/writemarker/worker.go +++ b/code/go/0chain.net/blobbercore/writemarker/worker.go @@ -98,19 +98,19 @@ func deleteMarkerData(allocationID string) { // ) func SetupWorkers(ctx context.Context) { - var res []allocation.Res + // var res []allocation.Res - err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { - res = allocation.Repo.GetAllocationIds(ctx) - return nil - }) - if err != nil && err != gorm.ErrRecordNotFound { - logging.Logger.Error("error_getting_allocations_worker", - zap.Any("error", err)) - } + // err := datastore.GetStore().WithNewTransaction(func(ctx context.Context) error { + // res = allocation.Repo.GetAllocationIds(ctx) + // return nil + // }) + // if err != nil && err != gorm.ErrRecordNotFound { + // logging.Logger.Error("error_getting_allocations_worker", + // zap.Any("error", err)) + // } - startRedeem(ctx, res) - go startCollector(ctx) + // startRedeem(ctx, res) + // go startCollector(ctx) // go startCleanupWorker(ctx) } diff --git a/code/go/0chain.net/blobbercore/zcn/query.go b/code/go/0chain.net/blobbercore/zcn/query.go index 7b122ca77..1f00f1eb6 100644 --- a/code/go/0chain.net/blobbercore/zcn/query.go +++ b/code/go/0chain.net/blobbercore/zcn/query.go @@ -6,30 +6,23 @@ import ( "fmt" "sync" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk/zboxcore/sdk" + "github.com/0chain/gosdk_common/zcncore" ) var ErrBlobberNotFound = errors.New("blobber is not found on chain") // GetBlobber try to get blobber info from chain. -func GetBlobber(blobberID string) (*zcncore.Blobber, error) { - cb := &getBlobberCallback{} - cb.wg.Add(1) - if err := zcncore.GetBlobber(blobberID, cb); err != nil { - cb.wg.Done() +func GetBlobber(blobberID string) (*sdk.Blobber, error) { + var ( + blobber *sdk.Blobber + err error + ) + if blobber, err = sdk.GetBlobber(blobberID); err != nil { return nil, err } - cb.wg.Wait() - if cb.Error != nil { - return nil, cb.Error - } - - if cb.Blobber == nil { - return nil, ErrBlobberNotFound - } - - return cb.Blobber, nil + return blobber, nil } diff --git a/code/go/0chain.net/core/chain/entity.go b/code/go/0chain.net/core/chain/entity.go index 7360a4f93..4c82c3d13 100644 --- a/code/go/0chain.net/core/chain/entity.go +++ b/code/go/0chain.net/core/chain/entity.go @@ -23,13 +23,13 @@ func GetServerChain() *Chain { /*Chain - data structure that holds the chain data*/ type Chain struct { - ID string - Version string - CreationDate common.Timestamp - OwnerID string - ParentChainID string - BlockWorker string - + ID string + Version string + CreationDate common.Timestamp + OwnerID string + ParentChainID string + BlockWorker string + ZauthServer string GenesisBlockHash string } @@ -44,12 +44,13 @@ func (c *Chain) Validate(ctx context.Context) error { return nil } -//NewChainFromConfig - create a new chain from config +// NewChainFromConfig - create a new chain from config func NewChainFromConfig() *Chain { chain := Provider() chain.ID = common.ToKey(config.Configuration.ChainID) chain.OwnerID = viper.GetString("server_chain.owner") chain.BlockWorker = viper.GetString("block_worker") + chain.ZauthServer = viper.GetString("zauth_server") return chain } diff --git a/code/go/0chain.net/core/common/utils.go b/code/go/0chain.net/core/common/utils.go index d3ee7a82d..7aa8cdd2b 100644 --- a/code/go/0chain.net/core/common/utils.go +++ b/code/go/0chain.net/core/common/utils.go @@ -47,6 +47,29 @@ func GetParentPaths(fPath string) ([]string, error) { return paths[2:], nil } +func GetAllParentPaths(fPath string) ([]string, error) { + if fPath == "" { + return nil, nil + } + if fPath == "/" { + return []string{"/"}, nil + } + + fPath = filepath.Clean(fPath) + if !filepath.IsAbs(fPath) { + return nil, NewError("invalid_path", fmt.Sprintf("%v is not absolute path", fPath)) + } + splittedPaths := strings.Split(fPath, "/") + var paths []string + for i := 0; i < len(splittedPaths); i++ { + subPath := strings.Join(splittedPaths[0:i], "/") + paths = append(paths, subPath) + } + returnPaths := []string{"/"} + returnPaths = append(returnPaths, paths[2:]...) + return returnPaths, nil +} + // GetPathFields will return slice of fields of path. // For path /a/b/c/d/e/f.txt it will return [a, b, c, d, e, f.txt],nil func GetPathFields(p string) ([]string, error) { diff --git a/code/go/0chain.net/core/encryption/hash.go b/code/go/0chain.net/core/encryption/hash.go index 53f649343..b6859cb1b 100644 --- a/code/go/0chain.net/core/encryption/hash.go +++ b/code/go/0chain.net/core/encryption/hash.go @@ -3,6 +3,8 @@ package encryption import ( "crypto/sha1" "encoding/hex" + "hash" + "sync" "github.com/minio/sha256-simd" "golang.org/x/crypto/sha3" @@ -10,6 +12,12 @@ import ( const HASH_LENGTH = 32 +var sha3Pool = sync.Pool{ + New: func() interface{} { + return sha3.New256() + }, +} + type HashBytes [HASH_LENGTH]byte /*Hash - hash the given data and return the hash as hex string */ @@ -30,9 +38,12 @@ func RawHash(data interface{}) []byte { default: panic("unknown type") } - hash := sha3.New256() + hash := sha3Pool.Get().(hash.Hash) hash.Write(databuf) - return hash.Sum(nil) + res := hash.Sum(nil) + hash.Reset() + sha3Pool.Put(hash) + return res } func ShaHash(data interface{}) []byte { diff --git a/code/go/0chain.net/core/encryption/keys.go b/code/go/0chain.net/core/encryption/keys.go index bb652f1bf..58327656c 100644 --- a/code/go/0chain.net/core/encryption/keys.go +++ b/code/go/0chain.net/core/encryption/keys.go @@ -9,12 +9,14 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/config" . "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/core/zcncrypto" + "github.com/0chain/gosdk_common/core/zcncrypto" "github.com/herumi/bls-go-binary/bls" ) -/*ReadKeys - reads a publicKey and a privateKey from a Reader. -They are assumed to be in two separate lines one followed by the other*/ +/* +ReadKeys - reads a publicKey and a privateKey from a Reader. +They are assumed to be in two separate lines one followed by the other +*/ func ReadKeys(reader io.Reader) (publicKey, privateKey, publicIp, port string) { scanner := bufio.NewScanner(reader) scanner.Scan() @@ -45,13 +47,14 @@ func Verify(publicKey, signature, hash string) (bool, error) { } // If input is normal herumi/bls public key, it returns it immmediately. -// So this is completely backward compatible with herumi/bls. +// +// So this is completely backward compatible with herumi/bls. +// // If input is MIRACL public key, convert it to herumi/bls public key. // // This is an example of the raw public key we expect from MIRACL var miraclExamplePK = `0418a02c6bd223ae0dfda1d2f9a3c81726ab436ce5e9d17c531ff0a385a13a0b491bdfed3a85690775ee35c61678957aaba7b1a1899438829f1dc94248d87ed36817f6dfafec19bfa87bf791a4d694f43fec227ae6f5a867490e30328cac05eaff039ac7dfc3364e851ebd2631ea6f1685609fc66d50223cc696cb59ff2fee47ac` -// // This is an example of the same MIRACL public key serialized with ToString(). // pk ([1bdfed3a85690775ee35c61678957aaba7b1a1899438829f1dc94248d87ed368,18a02c6bd223ae0dfda1d2f9a3c81726ab436ce5e9d17c531ff0a385a13a0b49],[039ac7dfc3364e851ebd2631ea6f1685609fc66d50223cc696cb59ff2fee47ac,17f6dfafec19bfa87bf791a4d694f43fec227ae6f5a867490e30328cac05eaff]) func MiraclToHerumiPK(pk string) string { diff --git a/code/go/0chain.net/core/encryption/keys_test.go b/code/go/0chain.net/core/encryption/keys_test.go index b0a71afc3..1b411a0e3 100644 --- a/code/go/0chain.net/core/encryption/keys_test.go +++ b/code/go/0chain.net/core/encryption/keys_test.go @@ -3,8 +3,8 @@ package encryption import ( "testing" - "github.com/0chain/gosdk/core/sys" - "github.com/0chain/gosdk/zboxcore/client" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/core/sys" "github.com/herumi/bls-go-binary/bls" "github.com/stretchr/testify/require" ) @@ -12,7 +12,8 @@ import ( func TestSignatureVerify(t *testing.T) { allocationId := "4f928c7857fabb5737347c42204eea919a4777f893f35724f563b932f64e2367" walletConfig := "{\"client_id\":\"9a566aa4f8e8c342fed97c8928040a21f21b8f574e5782c28568635ba9c75a85\",\"client_key\":\"40cd10039913ceabacf05a7c60e1ad69bb2964987bc50f77495e514dc451f907c3d8ebcdab20eedde9c8f39b9a1d66609a637352f318552fb69d4b3672516d1a\",\"keys\":[{\"public_key\":\"40cd10039913ceabacf05a7c60e1ad69bb2964987bc50f77495e514dc451f907c3d8ebcdab20eedde9c8f39b9a1d66609a637352f318552fb69d4b3672516d1a\",\"private_key\":\"a3a88aad5d89cec28c6e37c2925560ce160ac14d2cdcf4a4654b2bb358fe7514\"}],\"mnemonics\":\"inside february piece turkey offer merry select combine tissue wave wet shift room afraid december gown mean brick speak grant gain become toy clown\",\"version\":\"1.0\",\"date_created\":\"2021-05-21 17:32:29.484657 +0545 +0545 m=+0.072791323\"}" - require.NoError(t, client.PopulateClient(walletConfig, "bls0chain")) + _, err := client.PopulateClient(walletConfig, "bls0chain") + require.NoError(t, err) sig, serr := client.Sign(allocationId) require.Nil(t, serr) require.NotNil(t, sig) diff --git a/code/go/0chain.net/core/node/context.go b/code/go/0chain.net/core/node/context.go index 083851263..d5d0bb97c 100644 --- a/code/go/0chain.net/core/node/context.go +++ b/code/go/0chain.net/core/node/context.go @@ -3,7 +3,7 @@ package node import ( "context" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" ) const SELF_NODE constants.ContextKey = "SELF_NODE" diff --git a/code/go/0chain.net/core/node/self_node.go b/code/go/0chain.net/core/node/self_node.go index c58692f59..047980d91 100644 --- a/code/go/0chain.net/core/node/self_node.go +++ b/code/go/0chain.net/core/node/self_node.go @@ -4,9 +4,10 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/config" - "github.com/0chain/gosdk/core/zcncrypto" + "github.com/0chain/gosdk_common/core/zcncrypto" "golang.org/x/crypto/sha3" ) diff --git a/code/go/0chain.net/core/transaction/entity.go b/code/go/0chain.net/core/transaction/entity.go index 6a41ff980..c0c80e7d6 100644 --- a/code/go/0chain.net/core/transaction/entity.go +++ b/code/go/0chain.net/core/transaction/entity.go @@ -1,20 +1,10 @@ package transaction import ( - "encoding/json" "sync" "time" - "github.com/0chain/gosdk/core/transaction" - - "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "go.uber.org/zap" - - "github.com/0chain/gosdk/zcncore" - - "github.com/0chain/blobber/code/go/0chain.net/core/chain" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/blobber/code/go/0chain.net/core/node" ) var ( @@ -22,25 +12,6 @@ var ( last50TransactionsMutex sync.Mutex ) -// Transaction entity that encapsulates the transaction related data and meta data -type Transaction struct { - Hash string `json:"hash,omitempty"` - Version string `json:"version,omitempty"` - ClientID string `json:"client_id,omitempty"` - PublicKey string `json:"public_key,omitempty"` - ToClientID string `json:"to_client_id,omitempty"` - ChainID string `json:"chain_id,omitempty"` - TransactionData string `json:"transaction_data,omitempty"` - Value int64 `json:"transaction_value,omitempty"` - Signature string `json:"signature,omitempty"` - CreationDate common.Timestamp `json:"creation_date,omitempty"` - TransactionType int `json:"transaction_type,omitempty"` - TransactionOutput string `json:"transaction_output,omitempty"` - OutputHash string `json:"txn_output_hash"` - zcntxn zcncore.TransactionScheme - wg *sync.WaitGroup -} - type SmartContractTxnData struct { Name string `json:"name"` InputArgs interface{} `json:"input"` @@ -73,6 +44,7 @@ type StorageNode struct { PublicKey string `json:"-"` StakePoolSettings StakePoolSettings `json:"stake_pool_settings"` IsEnterprise bool `json:"is_enterprise"` + StorageVersion int `json:"storage_version"` } type BlobberAllocation struct { @@ -81,19 +53,20 @@ type BlobberAllocation struct { } type StorageAllocation struct { - ID string `json:"id"` - Tx string `json:"tx"` - OwnerPublicKey string `json:"owner_public_key"` - OwnerID string `json:"owner_id"` - Size int64 `json:"size"` - UsedSize int64 `json:"used_size"` - Expiration common.Timestamp `json:"expiration_date"` - BlobberDetails []*BlobberAllocation `json:"blobber_details"` - Finalized bool `json:"finalized"` - TimeUnit time.Duration `json:"time_unit"` - WritePool uint64 `json:"write_pool"` - FileOptions uint16 `json:"file_options"` - StartTime common.Timestamp `json:"start_time"` + ID string `json:"id"` + Tx string `json:"tx"` + OwnerPublicKey string `json:"owner_public_key"` + OwnerID string `json:"owner_id"` + Size int64 `json:"size"` + UsedSize int64 `json:"used_size"` + Expiration common.Timestamp `json:"expiration_date"` + BlobberDetails []*BlobberAllocation `json:"blobber_details"` + Finalized bool `json:"finalized"` + TimeUnit time.Duration `json:"time_unit"` + WritePool uint64 `json:"write_pool"` + FileOptions uint16 `json:"file_options"` + StartTime common.Timestamp `json:"start_time"` + OwnerSigningPublicKey string `json:"owner_signing_public_key"` DataShards int64 `json:"data_shards"` ParityShards int64 `json:"parity_shards"` @@ -120,216 +93,6 @@ const ( const STORAGE_CONTRACT_ADDRESS = "6dba10422e368813802877a85039d3985d96760ed844092319743fb3a76712d7" -func NewTransactionEntity() (*Transaction, error) { - txn := &Transaction{} - txn.Version = "1.0" - txn.ClientID = node.Self.ID - txn.CreationDate = common.Now() - txn.ChainID = chain.GetServerChain().ID - txn.PublicKey = node.Self.PublicKey - txn.wg = &sync.WaitGroup{} - zcntxn, err := zcncore.NewTransaction(txn, 0, 0) - if err != nil { - return nil, err - } - txn.zcntxn = zcntxn - return txn, nil -} - -func (t *Transaction) GetTransaction() zcncore.TransactionScheme { - return t.zcntxn -} - -func (t *Transaction) ExecuteSmartContract(address, methodName string, input interface{}, val uint64) error { - t.wg.Add(1) - - sn := transaction.SmartContractTxnData{Name: methodName, InputArgs: input} - snBytes, err := json.Marshal(sn) - if err != nil { - return err - } - - updateLast50Transactions(string(snBytes)) - - nonce := monitor.getNextUnusedNonce() - if err := t.zcntxn.SetTransactionNonce(nonce); err != nil { - logging.Logger.Error("Failed to set nonce.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", nonce), - zap.Any("error", err)) - } - - logging.Logger.Info("Transaction nonce set.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", nonce)) - - _, err = t.zcntxn.ExecuteSmartContract(address, methodName, input, uint64(val)) - if err != nil { - t.wg.Done() - logging.Logger.Error("Failed to execute SC.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", err)) - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - return err - } - - t.wg.Wait() - - t.Hash = t.zcntxn.GetTransactionHash() - if len(t.zcntxn.GetTransactionError()) > 0 { - logging.Logger.Error("Failed to submit SC.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", t.zcntxn.GetTransactionError())) - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - return common.NewError("transaction_send_error", t.zcntxn.GetTransactionError()) - } - return nil -} - -func (t *Transaction) ExecuteRollbackWM(address, methodName string, input interface{}, val uint64, prevNonce int64) error { - t.wg.Add(1) - - sn := transaction.SmartContractTxnData{Name: methodName, InputArgs: input} - snBytes, err := json.Marshal(sn) - if err != nil { - return err - } - - updateLast50Transactions(string(snBytes)) - - nonce := monitor.getNextUnusedNonce() - if nonce < prevNonce { - t.wg.Done() - logging.Logger.Error("Failed to set nonce as prevNonce is greater", - zap.Any("nonce", nonce), - zap.Any("prevNonce", prevNonce), - ) - monitor.recordFailedNonce(nonce) - return common.NewError("transaction_send_error", "Failed to set nonce as prevNonce is greater") - } - if err := t.zcntxn.SetTransactionNonce(nonce); err != nil { - logging.Logger.Error("Failed to set nonce.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", nonce), - zap.Any("error", err)) - } - - logging.Logger.Info("Transaction nonce set.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", nonce)) - - _, err = t.zcntxn.ExecuteSmartContract(address, methodName, input, uint64(val)) - if err != nil { - t.wg.Done() - logging.Logger.Error("Failed to execute SC.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", err)) - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - return err - } - - t.wg.Wait() - - t.Hash = t.zcntxn.GetTransactionHash() - if len(t.zcntxn.GetTransactionError()) > 0 { - logging.Logger.Error("Failed to submit SC.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", t.zcntxn.GetTransactionError())) - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - return common.NewError("transaction_send_error", t.zcntxn.GetTransactionError()) - } - return nil -} - -func (t *Transaction) Verify() error { - if err := t.zcntxn.SetTransactionHash(t.Hash); err != nil { - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - logging.Logger.Error("Failed to set txn hash.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", err)) - return err - } - t.wg.Add(1) - err := t.zcntxn.Verify() - if err != nil { - t.wg.Done() - logging.Logger.Error("Failed to start txn verification.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", err)) - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - return err - } - t.wg.Wait() - if len(t.zcntxn.GetVerifyError()) > 0 { - logging.Logger.Error("Failed to verify txn.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce()), - zap.Any("error", t.zcntxn.GetVerifyError()), - zap.Any("verify_output", t.zcntxn.GetVerifyOutput())) - monitor.recordFailedNonce(t.zcntxn.GetTransactionNonce()) - return common.NewError("transaction_verify_error", t.zcntxn.GetVerifyError()) - } else { - logging.Logger.Info("Successful txn verification.", - zap.Any("hash", t.zcntxn.GetTransactionHash()), - zap.Any("nonce", t.zcntxn.GetTransactionNonce())) - monitor.recordSuccess(t.zcntxn.GetTransactionNonce()) - } - - output := t.zcntxn.GetVerifyOutput() - - var objmap map[string]json.RawMessage - err = json.Unmarshal([]byte(output), &objmap) - if err != nil { - // it is a plain error message from blockchain. The format is `error_code: error message`. eg verify_challenge: could not find challenge, value not present - // so it is impossible to decode as map[string]json.RawMessage. - return common.NewError("transaction_verify_error", string(output)) - } - - err = json.Unmarshal(objmap["txn"], t) - if err != nil { - var confirmation map[string]json.RawMessage - err = json.Unmarshal(objmap["confirmation"], &confirmation) - if err != nil { - return common.NewError("transaction_verify_error", "Error unmarshaling verify output->confirmation: "+string(output)+" "+err.Error()) - } - err = json.Unmarshal(confirmation["txn"], t) - if err != nil { - return common.NewError("transaction_verify_error", "Error unmarshaling verify output->confirmation->txn: "+string(output)+" "+err.Error()) - } - } - return nil -} - -// func (t *Transaction) ComputeHashAndSign() error { -// hashdata := fmt.Sprintf("%v:%v:%v:%v:%v", t.CreationDate, t.ClientID, -// t.ToClientID, t.Value, encryption.Hash(t.TransactionData)) -// t.Hash = encryption.Hash(hashdata) -// var err error -// t.Signature, err = node.Self.Sign(t.Hash) -// if err != nil { -// return err -// } -// return nil -// } - -func (t *Transaction) OnTransactionComplete(zcntxn *zcncore.Transaction, status int) { - t.wg.Done() -} - -func (t *Transaction) OnVerifyComplete(zcntxn *zcncore.Transaction, status int) { - t.wg.Done() -} - -func (t *Transaction) OnAuthComplete(zcntxn *zcncore.Transaction, status int) { - -} - func updateLast50Transactions(data string) { last50TransactionsMutex.Lock() defer last50TransactionsMutex.Unlock() diff --git a/code/go/0chain.net/core/transaction/http.go b/code/go/0chain.net/core/transaction/http.go index 52feee60c..f38f0bd06 100644 --- a/code/go/0chain.net/core/transaction/http.go +++ b/code/go/0chain.net/core/transaction/http.go @@ -1,9 +1,8 @@ package transaction import ( - "github.com/0chain/blobber/code/go/0chain.net/core/chain" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/zboxcore/zboxutil" + "github.com/0chain/gosdk_common/core/screstapi" ) const TXN_SUBMIT_URL = "v1/transaction/put" @@ -16,39 +15,8 @@ const ( ) var ErrNoTxnDetail = common.NewError("missing_transaction_detail", "No transaction detail was found on any of the sharders") -var MakeSCRestAPICall func(scAddress string, relativePath string, params map[string]string) ([]byte, error) = MakeSCRestAPICallNoHandler +var MakeSCRestAPICall func(scAddress string, relativePath string, params map[string]string, options ...string) ([]byte, error) = MakeSCRestAPICallNoHandler -func MakeSCRestAPICallNoHandler(address string, path string, params map[string]string) ([]byte, error) { - return zboxutil.MakeSCRestAPICall(address, path, params, nil) -} - -func VerifyTransaction(txnHash string, chain *chain.Chain) (*Transaction, error) { - txn, err := NewTransactionEntity() - if err != nil { - return nil, err - } - - txn.Hash = txnHash - err = txn.Verify() - if err != nil { - return nil, err - } - return txn, nil -} - -// VerifyTransactionWithNonce verifies a transaction with known nonce. -func VerifyTransactionWithNonce(txnHash string, nonce int64) (*Transaction, error) { - txn, err := NewTransactionEntity() - if err != nil { - return nil, err - } - - txn.Hash = txnHash - _ = txn.zcntxn.SetTransactionNonce(nonce) - - err = txn.Verify() - if err != nil { - return nil, err - } - return txn, nil +func MakeSCRestAPICallNoHandler(address string, path string, params map[string]string, options ...string) ([]byte, error) { + return screstapi.MakeSCRestAPICall(address, path, params, options...) } diff --git a/code/go/0chain.net/core/transaction/nonce.go b/code/go/0chain.net/core/transaction/nonce.go index 44013c7a6..403031e27 100644 --- a/code/go/0chain.net/core/transaction/nonce.go +++ b/code/go/0chain.net/core/transaction/nonce.go @@ -5,7 +5,8 @@ import ( "time" "github.com/0chain/blobber/code/go/0chain.net/core/logging" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/zcncore" "go.uber.org/zap" ) @@ -88,23 +89,7 @@ func (m *nonceMonitor) refreshFromBalance() { // sync lock not necessary, this is expected to be called within a synchronized function. m.shouldRefreshFromBalance = false - cb := &getNonceCallBack{waitCh: make(chan struct{})} - if err := zcncore.GetNonce(cb); err != nil { - return - } - - <-cb.waitCh - - newNonce := int64(0) - if cb.hasError { - logging.Logger.Info("Couldn't get nonce from remote, use 0") - newNonce = int64(0) - } else { - logging.Logger.Info("Got nonce from balance.", zap.Any("nonce", cb.nonce), zap.Any("highestSuccess", m.highestSuccess)) - newNonce = cb.nonce - } - - m.highestSuccess = newNonce + m.highestSuccess = client.Nonce() m.failed = make(map[int64]int64) m.used = make(map[int64]time.Time) diff --git a/code/go/0chain.net/validator/main.go b/code/go/0chain.net/validator/main.go index bf5ca7f12..330387e93 100644 --- a/code/go/0chain.net/validator/main.go +++ b/code/go/0chain.net/validator/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "flag" "fmt" "log" @@ -21,12 +22,13 @@ import ( . "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" - "github.com/0chain/blobber/code/go/0chain.net/core/util" "github.com/0chain/blobber/code/go/0chain.net/validatorcore/config" "github.com/0chain/blobber/code/go/0chain.net/validatorcore/storage" + coreTxn "github.com/0chain/gosdk_common/core/transaction" "github.com/0chain/gosdk/zboxcore/sdk" - "github.com/0chain/gosdk/zcncore" + "github.com/0chain/gosdk_common/core/client" + "github.com/0chain/gosdk_common/zcncore" "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/spf13/viper" @@ -202,28 +204,26 @@ func RegisterValidator() { } for { - txn, err := storage.GetProtocolImpl().RegisterValidator(common.GetRootContext()) + sn := &transaction.StorageNode{} + sn.ID = node.Self.ID + sn.BaseURL = node.Self.GetURLBase() + sn.StakePoolSettings.DelegateWallet = config.Configuration.DelegateWallet + sn.StakePoolSettings.NumDelegates = config.Configuration.NumDelegates + sn.StakePoolSettings.ServiceCharge = config.Configuration.ServiceCharge + + hash, out, _, _, err := coreTxn.SmartContractTxn(transaction.STORAGE_CONTRACT_ADDRESS, coreTxn.SmartContractTxnData{ + Name: transaction.ADD_VALIDATOR_SC_NAME, + InputArgs: sn, + }, true) if err != nil { - Logger.Error("Error registering validator", zap.Any("err", err)) + Logger.Error("Add validator transaction could not be verified", zap.Any("err", err), zap.String("txn.Hash", hash)) continue } - time.Sleep(transaction.SLEEP_FOR_TXN_CONFIRMATION * time.Second) - txnVerified := false - verifyRetries := 0 - for verifyRetries < util.MAX_RETRIES { - time.Sleep(transaction.SLEEP_FOR_TXN_CONFIRMATION * time.Second) - t, err := transaction.VerifyTransactionWithNonce(txn.Hash, txn.GetTransaction().GetTransactionNonce()) - if err == nil { - Logger.Info("Transaction for adding validator accepted and verified", zap.String("txn_hash", t.Hash), zap.Any("txn_output", t.TransactionOutput)) - go handler.StartHealthCheck(common.GetRootContext(), common.ProviderTypeValidator) - return - } - verifyRetries++ - } - if !txnVerified { - Logger.Error("Add validator transaction could not be verified", zap.Any("err", err), zap.String("txn.Hash", txn.Hash)) - } + Logger.Info("Transaction for adding validator accepted and verified", zap.String("txn_hash", hash), zap.Any("txn_output", out)) + + go handler.StartHealthCheck(common.GetRootContext(), common.ProviderTypeValidator) + break } } @@ -232,17 +232,16 @@ func SetupValidatorOnBC(logDir string) error { var logName = logDir + "/validator.log" zcncore.SetLogFile(logName, false) zcncore.SetLogLevel(3) - if err := zcncore.InitZCNSDK(serverChain.BlockWorker, config.Configuration.SignatureScheme); err != nil { - return err - } - if err := zcncore.SetWalletInfo(node.Self.GetWalletString(), false); err != nil { + err := client.InitSDK("{}", serverChain.BlockWorker, config.Configuration.ChainID, config.Configuration.SignatureScheme, int64(0), false) + if err != nil { return err } - var blob []string - if err := sdk.InitStorageSDK(node.Self.GetWalletString(), serverChain.BlockWorker, - config.Configuration.ChainID, config.Configuration.SignatureScheme, blob, int64(0)); err != nil { + + err = zcncore.SetGeneralWalletInfo(node.Self.GetWalletString(), config.Configuration.SignatureScheme) + if err != nil { return err } + go RegisterValidator() return nil } @@ -254,7 +253,7 @@ func HomePageHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "
Working on the chain: %v
\n", mc.ID) fmt.Fprintf(w, "
I am a validator with
\n", node.Self.ID, node.Self.PublicKey, build.BuildTag) fmt.Fprintf(w, "
Miners ...\n") - network := zcncore.GetNetwork() + network, _ := client.GetNetwork(context.Background()) for _, miner := range network.Miners { fmt.Fprintf(w, "%v\n", miner) } diff --git a/code/go/0chain.net/validatorcore/storage/challenge_handler.go b/code/go/0chain.net/validatorcore/storage/challenge_handler.go index e88aaba8f..53999cf32 100644 --- a/code/go/0chain.net/validatorcore/storage/challenge_handler.go +++ b/code/go/0chain.net/validatorcore/storage/challenge_handler.go @@ -13,6 +13,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/common" "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" + "github.com/0chain/blobber/code/go/0chain.net/core/transaction" "go.uber.org/zap" "golang.org/x/crypto/sha3" @@ -41,7 +42,7 @@ func challengeHandler(ctx context.Context, r *http.Request) (interface{}, error) time.Sleep(1 * time.Second) - allocationObj, err := GetProtocolImpl().VerifyAllocationTransaction(ctx, challengeObj.AllocationID) + allocationObj, err := requestAllocation(challengeObj.AllocationID) if err != nil { logging.Logger.Error("Error verifying the allocation from BC", zap.String("allocation_id", challengeObj.AllocationID), zap.Error(err)) return nil, common.NewError("invalid_parameters", "Allocation could not be verified. "+err.Error()) @@ -58,6 +59,33 @@ func challengeHandler(ctx context.Context, r *http.Request) (interface{}, error) return ValidValidationTicket(challengeObj, challengeRequest.ChallengeID, challengeHash) } +func requestAllocation(allocID string) (allocation *Allocation, err error) { + var b []byte + b, err = transaction.MakeSCRestAPICall( + transaction.STORAGE_CONTRACT_ADDRESS, + "/allocation", + map[string]string{"allocation": allocID}) + if err != nil { + return + } + sa := new(transaction.StorageAllocation) + err = json.Unmarshal(b, sa) + if err != nil { + return + } + allocation = &Allocation{ + ID: sa.ID, + DataShards: sa.DataShards, + ParityShards: sa.ParityShards, + Size: sa.Size, + Expiration: sa.Expiration, + Owner: sa.OwnerID, + OwnerPublicKey: sa.OwnerPublicKey, + OwnerSigningPublicKey: sa.OwnerSigningPublicKey, + } + return +} + func NewChallengeRequest(r *http.Request) (*ChallengeRequest, string, error) { if r.Method == "GET" { return nil, "", common.NewError("invalid_method", "Invalid method used for the upload URL. Use multi-part form POST instead") diff --git a/code/go/0chain.net/validatorcore/storage/context.go b/code/go/0chain.net/validatorcore/storage/context.go index 86a2acb5d..7b8449ea0 100644 --- a/code/go/0chain.net/validatorcore/storage/context.go +++ b/code/go/0chain.net/validatorcore/storage/context.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" ) func SetupContext(handler common.JSONResponderF) common.JSONResponderF { diff --git a/code/go/0chain.net/validatorcore/storage/handler_integration_tests.go b/code/go/0chain.net/validatorcore/storage/handler_integration_tests.go index 6fb11006b..a4fa31ad7 100644 --- a/code/go/0chain.net/validatorcore/storage/handler_integration_tests.go +++ b/code/go/0chain.net/validatorcore/storage/handler_integration_tests.go @@ -8,7 +8,7 @@ import ( "net/http" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/gosdk/constants" + "github.com/0chain/gosdk_common/constants" "github.com/gorilla/mux" ) diff --git a/code/go/0chain.net/validatorcore/storage/models.go b/code/go/0chain.net/validatorcore/storage/models.go index ba47ef2f8..00746d2c9 100644 --- a/code/go/0chain.net/validatorcore/storage/models.go +++ b/code/go/0chain.net/validatorcore/storage/models.go @@ -11,7 +11,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/blobber/code/go/0chain.net/validatorcore/storage/writemarker" - "github.com/0chain/gosdk/core/util" + "github.com/0chain/gosdk_common/core/util" "github.com/mitchellh/mapstructure" @@ -263,14 +263,15 @@ func (op *ObjectPath) Verify(allocationID string, challengeRand int64) error { } type Allocation struct { - ID string `json:"id"` - DataShards int `json:"data_shards"` - ParityShards int `json:"parity_shards"` - Size int64 `json:"size"` - UsedSize int64 `json:"used_size"` - Expiration common.Timestamp `json:"expiration_date"` - Owner string `json:"owner_id"` - OwnerPublicKey string `json:"owner_public_key"` + ID string `json:"id"` + DataShards int64 `json:"data_shards"` + ParityShards int64 `json:"parity_shards"` + Size int64 `json:"size"` + UsedSize int64 `json:"used_size"` + Expiration common.Timestamp `json:"expiration_date"` + Owner string `json:"owner_id"` + OwnerPublicKey string `json:"owner_public_key"` + OwnerSigningPublicKey string `json:"owner_signing_public_key"` } type ChallengeProof struct { diff --git a/code/go/0chain.net/validatorcore/storage/models_test.go b/code/go/0chain.net/validatorcore/storage/models_test.go index 221053cdb..eef445acb 100644 --- a/code/go/0chain.net/validatorcore/storage/models_test.go +++ b/code/go/0chain.net/validatorcore/storage/models_test.go @@ -10,8 +10,8 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/blobber/code/go/0chain.net/validatorcore/storage" - "github.com/0chain/gosdk/core/util" - "github.com/0chain/gosdk/core/zcncrypto" + "github.com/0chain/gosdk_common/core/util" + "github.com/0chain/gosdk_common/core/zcncrypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" diff --git a/code/go/0chain.net/validatorcore/storage/protocol.go b/code/go/0chain.net/validatorcore/storage/protocol.go index 521d3ee01..1871a008a 100644 --- a/code/go/0chain.net/validatorcore/storage/protocol.go +++ b/code/go/0chain.net/validatorcore/storage/protocol.go @@ -4,17 +4,13 @@ import ( "context" "encoding/json" "sync" - "time" "github.com/0chain/blobber/code/go/0chain.net/core/chain" "github.com/0chain/blobber/code/go/0chain.net/core/common" - "github.com/0chain/blobber/code/go/0chain.net/core/logging" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/blobber/code/go/0chain.net/core/transaction" - "github.com/0chain/blobber/code/go/0chain.net/validatorcore/config" - "github.com/0chain/gosdk/constants" - "go.uber.org/zap" + "github.com/0chain/gosdk_common/constants" ) const CHUNK_SIZE = 64 * 1024 @@ -67,19 +63,6 @@ func GetProtocolImpl() *ValidatorProtocolImpl { // return txn.Hash, nil // } -func (sp *ValidatorProtocolImpl) VerifyAllocationTransaction(ctx context.Context, allocationID string) (*Allocation, error) { - t, err := transaction.VerifyTransaction(allocationID, sp.ServerChain) - if err != nil { - return nil, common.NewError("invalid_allocation", "Invalid Allocation id. Allocation not found in blockchain. "+err.Error()) - } - var allocationObj Allocation - err = json.Unmarshal([]byte(t.TransactionOutput), &allocationObj) - if err != nil { - return nil, common.NewError("transaction_output_decode_error", "Error decoding the allocation transaction output."+err.Error()) - } - return &allocationObj, nil -} - func (sp *ValidatorProtocolImpl) VerifyChallengeTransaction(ctx context.Context, challengeRequest *ChallengeRequest) (*Challenge, error) { blobberID := ctx.Value(constants.ContextKeyClient).(string) if blobberID == "" { @@ -125,28 +108,3 @@ func (wb *WalletCallback) OnWalletCreateComplete(status int, wallet, err string) wb.err = err wb.wg.Done() } - -func (sp *ValidatorProtocolImpl) RegisterValidator(ctx context.Context) (*transaction.Transaction, error) { - time.Sleep(transaction.SLEEP_FOR_TXN_CONFIRMATION * time.Second) - - txn, err := transaction.NewTransactionEntity() - if err != nil { - return nil, err - } - - sn := &transaction.StorageNode{} - sn.ID = node.Self.ID - sn.BaseURL = node.Self.GetURLBase() - sn.StakePoolSettings.DelegateWallet = config.Configuration.DelegateWallet - sn.StakePoolSettings.NumDelegates = config.Configuration.NumDelegates - sn.StakePoolSettings.ServiceCharge = config.Configuration.ServiceCharge - - logging.Logger.Info("Adding validator to the blockchain.") - err = txn.ExecuteSmartContract(transaction.STORAGE_CONTRACT_ADDRESS, transaction.ADD_VALIDATOR_SC_NAME, sn, 0) - if err != nil { - logging.Logger.Info("Failed during registering validator to the mining network", zap.String("err:", err.Error())) - return nil, err - } - - return txn, nil -} diff --git a/code/go/0chain.net/validatorcore/storage/writemarker/entity_test.go b/code/go/0chain.net/validatorcore/storage/writemarker/entity_test.go index 7f02f46a1..86a4ef3bf 100644 --- a/code/go/0chain.net/validatorcore/storage/writemarker/entity_test.go +++ b/code/go/0chain.net/validatorcore/storage/writemarker/entity_test.go @@ -9,7 +9,7 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/encryption" "github.com/0chain/blobber/code/go/0chain.net/validatorcore/storage/writemarker" - "github.com/0chain/gosdk/core/zcncrypto" + "github.com/0chain/gosdk_common/core/zcncrypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/config/0chain_blobber.yaml b/config/0chain_blobber.yaml index 55b5bcf37..758bab823 100755 --- a/config/0chain_blobber.yaml +++ b/config/0chain_blobber.yaml @@ -76,11 +76,13 @@ rate_limiters: # Max upload limit in a month for a client. Default is 2000GB(the value needs to be in blocks which is data/64KB) upload_limit_monthly: 31250000 # Max commit limit in a month for a client. Default is 30000 - commit_limit_monthly: 30000 + commit_limit_monthly: 1000000000 # Max commit limit in a day for a client. Default is 1600 commit_limit_daily: 1600 # Max commit limit with size zero or less in a day for a client. Default is 400 commit_zero_limit_daily: 400 + # Max connection changes in a batch. Default is 100 + max_connection_changes: 100 server_chain: id: "0afc093ffb509f059c55478bc1a60351cef7b4e9c008a53a6cc8241ca8617dfe" diff --git a/docker.local/base.Dockerfile b/docker.local/base.Dockerfile index ae00982f2..f5d3dc6bf 100644 --- a/docker.local/base.Dockerfile +++ b/docker.local/base.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine3.18 as blobber_base +FROM golang:1.22-alpine3.18 as blobber_base LABEL zchain="blobber" diff --git a/go.mod b/go.mod index e747651f4..93195d1f6 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/0chain/blobber -go 1.21 +go 1.22.0 + +toolchain go1.23.1 require ( github.com/0chain/errors v1.0.3 - github.com/0chain/gosdk v1.17.0-RC6 + github.com/0chain/gosdk v1.19.6 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/didip/tollbooth/v6 v6.1.2 github.com/go-openapi/runtime v0.26.0 @@ -19,26 +21,27 @@ require ( github.com/remeh/sizedwaitgroup v1.0.0 github.com/selvatico/go-mocket v1.0.7 github.com/spf13/viper v1.16.0 - github.com/stretchr/testify v1.8.4 - go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.17.0 - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 + github.com/stretchr/testify v1.9.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.21.0 + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 golang.org/x/time v0.3.0 // indirect google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect google.golang.org/grpc v1.56.2 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 - google.golang.org/protobuf v1.31.0 + google.golang.org/protobuf v1.33.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/datatypes v1.2.0 gorm.io/driver/postgres v1.5.2 gorm.io/driver/sqlite v1.5.2 - gorm.io/gorm v1.25.5 + gorm.io/gorm v1.25.11 ) require ( + github.com/0chain/gosdk_common v0.0.0-20250318142402-b7bd1ebd4f66 github.com/lithammer/shortuuid/v3 v3.0.7 - golang.org/x/sync v0.5.0 + golang.org/x/sync v0.7.0 google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc ) @@ -53,6 +56,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.26.1 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.5 github.com/pressly/goose/v3 v3.13.4 + moul.io/zapgorm2 v1.3.0 ) require ( @@ -68,15 +72,17 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect github.com/aws/smithy-go v1.19.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/hack-pad/go-webworkers v0.1.0 // indirect github.com/hack-pad/safejs v0.1.1 // indirect - github.com/hitenjain14/fasthttp v0.0.0-20240527123209-06019e79bff9 // indirect + github.com/hitenjain14/fasthttp v0.0.0-20240916135632-f9303a91736c // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect + github.com/x448/float16 v0.8.4 // indirect ) require ( - github.com/0chain/common v0.0.6-0.20230127095721-8df4d1d72565 + github.com/0chain/common v1.18.3 github.com/Luzifer/go-openssl/v3 v3.1.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/btcsuite/btcd v0.23.4 // indirect @@ -148,9 +154,8 @@ require ( go.dedis.ch/fixbuf v1.0.3 // indirect go.dedis.ch/kyber/v3 v3.1.0 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.10.0 // indirect - golang.org/x/text v0.14.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/text v0.16.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect diff --git a/go.sum b/go.sum index a4f5c3b19..c052bf306 100644 --- a/go.sum +++ b/go.sum @@ -36,12 +36,14 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/0chain/common v0.0.6-0.20230127095721-8df4d1d72565 h1:z+DtCR8mBsjPnEsT2XtRu4X7GfBiMnz9dYvWYs9V0B4= -github.com/0chain/common v0.0.6-0.20230127095721-8df4d1d72565/go.mod h1:UyDC8Qyl5z9lGkCnf9RHJPMektnFX8XtCJZHXCCVj8E= +github.com/0chain/common v1.18.3 h1:42dYOv2KyMTSanuS67iDtfv+ErbSRqR8NJ3MG72MwaI= +github.com/0chain/common v1.18.3/go.mod h1:Lapu2Tj7z5Sm4r+X141e7vsz4NDODTEypeElYAP3iSw= github.com/0chain/errors v1.0.3 h1:QQZPFxTfnMcRdt32DXbzRQIfGWmBsKoEdszKQDb0rRM= github.com/0chain/errors v1.0.3/go.mod h1:xymD6nVgrbgttWwkpSCfLLEJbFO6iHGQwk/yeSuYkIc= -github.com/0chain/gosdk v1.17.0-RC6 h1:NB4pfu0VbVWfQQEfnFqn5inaiQuyhSaUW7VwgH3LW8U= -github.com/0chain/gosdk v1.17.0-RC6/go.mod h1:y7Ucdmv40VltqulZnncMNjNQ4piX5Dta5ujNmPmXnxg= +github.com/0chain/gosdk v1.19.6 h1:GfNdBBC0uQqaooT4Vh6B7FP1m76lyTagugizwfYYleQ= +github.com/0chain/gosdk v1.19.6/go.mod h1:8unFy9Dx2YyPKMYPDGR3MFhUEymbAfQcRDm9bobVLGw= +github.com/0chain/gosdk_common v0.0.0-20250318142402-b7bd1ebd4f66 h1:lDzI8ZW/mdqfT0ySRnNke2c5KoqVh+FNXY8536yn0DA= +github.com/0chain/gosdk_common v0.0.0-20250318142402-b7bd1ebd4f66/go.mod h1:+FBmkG4JocGmD+8gyE4kyst15p+3xm3Nf+nrg4cu5V4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= @@ -107,7 +109,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNIC github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU= github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -148,7 +149,6 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -163,14 +163,18 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= -github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= -github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -230,12 +234,14 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= -github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -485,8 +491,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/herumi/bls-go-binary v1.31.0 h1:L1goQ2tMtGgpXCg5AwHAdJQpLs/pfnWWEc3Wog6OhmI= github.com/herumi/bls-go-binary v1.31.0/go.mod h1:O4Vp1AfR4raRGwFeQpr9X/PQtncEicMoOe6BQt1oX0Y= -github.com/hitenjain14/fasthttp v0.0.0-20240527123209-06019e79bff9 h1:Z6Mu2JCsW2hbqx91L0HNPRPQ10RyAFvPocQHlrRo1Jk= -github.com/hitenjain14/fasthttp v0.0.0-20240527123209-06019e79bff9/go.mod h1:RZMcXy7u4S+E97IXYTe7WHZ3+mCYOh4vys8PkIGZeXk= +github.com/hitenjain14/fasthttp v0.0.0-20240916135632-f9303a91736c h1:lDSIbcLu5TdT+uwb4wPzZgo1pQvKjP/tArL5QKjDJdI= +github.com/hitenjain14/fasthttp v0.0.0-20240916135632-f9303a91736c/go.mod h1:RZMcXy7u4S+E97IXYTe7WHZ3+mCYOh4vys8PkIGZeXk= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c h1:DZfsyhDK1hnSS5lH8l+JggqzEleHteTYfutAiVlSUM8= @@ -783,8 +789,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -794,8 +801,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -827,6 +834,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -840,6 +849,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= @@ -870,23 +880,24 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -904,8 +915,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -917,8 +928,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -942,8 +953,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -987,11 +999,12 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1013,8 +1026,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1073,17 +1086,19 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1094,8 +1109,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1164,8 +1179,9 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1281,8 +1297,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1330,9 +1346,10 @@ gorm.io/driver/sqlite v1.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc= gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= +gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= -gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1361,6 +1378,8 @@ modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk= +moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/goose/migrations/1698861371_full_db_snapshot.sql b/goose/migrations/1698861371_full_db_snapshot.sql index 950dd0e6a..9496a21b4 100644 --- a/goose/migrations/1698861371_full_db_snapshot.sql +++ b/goose/migrations/1698861371_full_db_snapshot.sql @@ -103,7 +103,10 @@ CREATE TABLE allocations ( cleaned_up boolean DEFAULT false NOT NULL, finalized boolean DEFAULT false NOT NULL, file_options integer DEFAULT 63 NOT NULL, - start_time bigint NOT NULL + start_time bigint NOT NULL, + allocation_version bigint DEFAULT 0 NOT NULL, + prev_used_size bigint DEFAULT 0 NOT NULL, + prev_blobber_size_used bigint DEFAULT 0 NOT NULL ); @@ -329,7 +332,6 @@ ALTER TABLE read_pools OWNER TO blobber_user; CREATE TABLE reference_objects ( id bigint NOT NULL, - file_id text, type character varying(1), allocation_id character varying(64) NOT NULL, lookup_hash character varying(64) NOT NULL, @@ -337,25 +339,17 @@ CREATE TABLE reference_objects ( thumbnail_filename text, path character varying(1000) NOT NULL COLLATE pg_catalog."POSIX", file_meta_hash character varying(64) NOT NULL, - hash character varying(64) NOT NULL, num_of_blocks bigint DEFAULT 0 NOT NULL, - path_hash character varying(64) NOT NULL, parent_path character varying(999), level bigint DEFAULT 0 NOT NULL, custom_meta text NOT NULL, - validation_root character varying(64) NOT NULL, - prev_validation_root text, - validation_root_signature character varying(64), size bigint DEFAULT 0 NOT NULL, - fixed_merkle_root character varying(64) NOT NULL, actual_file_size bigint DEFAULT 0 NOT NULL, actual_file_hash_signature character varying(64), actual_file_hash character varying(64) NOT NULL, mimetype character varying(255) NOT NULL, - allocation_root character varying(64) NOT NULL, thumbnail_size bigint DEFAULT 0 NOT NULL, thumbnail_hash character varying(64) NOT NULL, - prev_thumbnail_hash text, actual_thumbnail_size bigint DEFAULT 0 NOT NULL, actual_thumbnail_hash character varying(64) NOT NULL, encrypted_key character varying(64), @@ -363,10 +357,13 @@ CREATE TABLE reference_objects ( created_at bigint, updated_at bigint, deleted_at timestamp with time zone, - is_precommit boolean DEFAULT false NOT NULL, chunk_size bigint DEFAULT 65536 NOT NULL, num_of_updates bigint, - num_of_block_downloads bigint + num_of_block_downloads bigint, + data_hash character varying(64), + data_hash_signature character varying(64), + parent_id bigint DEFAULT NULL, + allocation_version bigint DEFAULT 0 NOT NULL ); @@ -575,8 +572,6 @@ ALTER TABLE ONLY marketplace_share_info ALTER COLUMN id SET DEFAULT nextval('mar ALTER TABLE ONLY reference_objects ALTER COLUMN id SET DEFAULT nextval('reference_objects_id_seq'::regclass); -ALTER TABLE ONLY reference_objects ADD CONSTRAINT path_commit UNIQUE(lookup_hash,is_precommit); - -- -- Name: terms id; Type: DEFAULT; Schema: public; Owner: blobber_user -- @@ -759,7 +754,7 @@ CREATE INDEX idx_created_at ON reference_objects USING btree (created_at DESC); -- Name: idx_lookup_hash; Type: INDEX; Schema: public; Owner: blobber_user -- -CREATE INDEX idx_lookup_hash ON reference_objects USING btree (lookup_hash); +CREATE UNIQUE INDEX idx_lookup_hash_deleted ON reference_objects USING btree (lookup_hash,(deleted_at IS NULL)) INCLUDE(id,type,num_of_updates); -- @@ -787,14 +782,20 @@ CREATE INDEX idx_name_gin ON reference_objects USING gin (to_tsvector('english': -- Name: idx_parent_path_alloc; Type: INDEX; Schema: public; Owner: blobber_user -- -CREATE INDEX idx_parent_path_alloc ON reference_objects USING btree (allocation_id, parent_path); +-- CREATE INDEX idx_parent_path_alloc ON reference_objects USING btree (allocation_id, parent_path) WHERE deleted_at IS NULL; + +-- +-- Name: idx_parent_id; Type: INDEX; Schema: public; Owner: blobber_user +-- + +CREATE INDEX idx_parent_id ON reference_objects USING btree (parent_id); -- -- Name: idx_path_alloc; Type: INDEX; Schema: public; Owner: blobber_user -- -CREATE INDEX idx_path_alloc ON reference_objects USING btree (allocation_id, path); +CREATE INDEX idx_path_alloc ON reference_objects USING btree (allocation_id, path) WHERE deleted_at IS NULL; -- @@ -839,13 +840,6 @@ CREATE INDEX idx_updated_at ON reference_objects USING btree (updated_at DESC); CREATE INDEX idx_write_pools_cab ON write_pools USING btree (allocation_id); --- --- Name: path_idx; Type: INDEX; Schema: public; Owner: blobber_user --- - -CREATE INDEX path_idx ON reference_objects USING btree (path); - - -- -- Name: allocation_changes fk_allocation_connections_changes; Type: FK CONSTRAINT; Schema: public; Owner: blobber_user -- @@ -855,18 +849,18 @@ ALTER TABLE ONLY allocation_changes -- - -- Name: connection_id_index; Type: INDEX; Schema: public; Owner: blobber_user - -- + -- Name: fk_reference_objects; TYPE FK CONSTRAINT; Schema: public; Owner: blobber_user + -- -CREATE INDEX connection_id_index ON allocation_changes USING btree (connection_id); + ALTER TABLE ONLY reference_objects + ADD CONSTRAINT fk_reference_objects FOREIGN KEY (parent_id) REFERENCES reference_objects(id) ON DELETE CASCADE; --- --- Name: file_stats fk_file_stats_ref; Type: FK CONSTRAINT; Schema: public; Owner: blobber_user --- + -- + -- Name: connection_id_index; Type: INDEX; Schema: public; Owner: blobber_user + -- -ALTER TABLE ONLY file_stats - ADD CONSTRAINT fk_file_stats_ref FOREIGN KEY (ref_id) REFERENCES reference_objects(id) ON DELETE CASCADE; +CREATE INDEX connection_id_index ON allocation_changes USING btree (connection_id); -- diff --git a/goose/migrations/1717416291_change_lookuphash.sql b/goose/migrations/1717416291_change_lookuphash.sql index e2d8e71dd..f55ac0827 100644 --- a/goose/migrations/1717416291_change_lookuphash.sql +++ b/goose/migrations/1717416291_change_lookuphash.sql @@ -1,6 +1,6 @@ -- +goose Up -- +goose StatementBegin -ALTER TABLE allocation_changes ADD COLUMN lookup_hash character varying(64); +ALTER TABLE allocation_changes ADD COLUMN lookup_hash character varying(64); -- CREATE UNIQUE INDEX idx_allocation_changes_lookup_hash ON allocation_changes USING HASH(lookup_hash,connection_id); -- +goose StatementEnd \ No newline at end of file diff --git a/goose/migrations/1718188301_change_idx.sql b/goose/migrations/1718188301_change_idx.sql index 564e37891..14e6c3ba0 100644 --- a/goose/migrations/1718188301_change_idx.sql +++ b/goose/migrations/1718188301_change_idx.sql @@ -1,5 +1,10 @@ -- +goose Up -- +goose StatementBegin -CREATE INDEX idx_allocation_changes_lookup_hash ON allocation_changes USING HASH(lookup_hash); + -- + -- Name: connection_id_lookup_hash; Type: UNIQUE CONSTRAINT; Schema: public; Owner: blobber_user + -- + +ALTER TABLE ONLY allocation_changes ADD CONSTRAINT connection_id_lookup_hash UNIQUE(connection_id,lookup_hash); + -- +goose StatementEnd \ No newline at end of file diff --git a/goose/migrations/1718391849_ref_index.sql b/goose/migrations/1718391849_ref_index.sql index 4380bc232..6890115ea 100644 --- a/goose/migrations/1718391849_ref_index.sql +++ b/goose/migrations/1718391849_ref_index.sql @@ -1,4 +1,10 @@ -- +goose Up -- +goose StatementBegin -DROP INDEX idx_created_at,idx_updated_at,idx_lookup_hash,idx_path_gin_trgm,idx_name_gin,idx_allocation_changes_lookup_hash; +DROP INDEX idx_created_at,idx_updated_at,idx_path_gin_trgm,idx_name_gin; + +CREATE INDEX idx_is_allocation_version_deleted_at on reference_objects(allocation_id,allocation_version) WHERE type='f' AND deleted_at IS NULL; + +CREATE INDEX idx_is_deleted on reference_objects(allocation_id) WHERE deleted_at IS NOT NULL; + +CREATE INDEX idx_path_alloc_level ON reference_objects USING btree (allocation_id,level,type,path) WHERE deleted_at IS NULL; -- +goose StatementEnd \ No newline at end of file diff --git a/goose/migrations/1721021811_version_marker.sql b/goose/migrations/1721021811_version_marker.sql new file mode 100644 index 000000000..dcf563a9a --- /dev/null +++ b/goose/migrations/1721021811_version_marker.sql @@ -0,0 +1,61 @@ +-- +goose Up +-- +goose StatementBegin + +CREATE TABLE version_markers( + id bigint NOT NULL, + allocation_id character varying(64) NOT NULL, + blobber_id character varying(64) NOT NULL, + client_id character varying(64) NOT NULL, + "version" bigint NOT NULL, + "timestamp" bigint NOT NULL, + signature character varying(64), + is_repair boolean NOT NULL DEFAULT false, + repair_version bigint, + repair_offset character varying(1000) +); + +ALTER TABLE version_markers OWNER TO blobber_user; + +-- +-- Name: version_markers_id_seq; Type: SEQUENCE; Schema: public; Owner: blobber_user +-- + +CREATE SEQUENCE version_markers_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE version_markers_id_seq OWNER TO blobber_user; + + +-- +-- Name: version_markers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: blobber_user +-- + +ALTER SEQUENCE version_markers_id_seq OWNED BY version_markers.id; + + +-- +-- Name: version_markers version_markers_pkey; Type: CONSTRAINT; Schema: public; Owner: blobber_user +-- + +ALTER TABLE ONLY version_markers + ADD CONSTRAINT version_markers_pkey PRIMARY KEY (id); + + +-- +-- Name: version_markers id; Type: DEFAULT; Schema: public; Owner: blobber_user +-- + +ALTER TABLE ONLY version_markers ALTER COLUMN id SET DEFAULT nextval('version_markers_id_seq'::regclass); + + +-- +-- Name: version_markers_allocation_id_idx; Type: INDEX; Schema: public; Owner: blobber_user +-- + +CREATE INDEX version_markers_allocation_id_idx ON version_markers USING btree (allocation_id,version); + +-- +goose StatementEnd