aboutsummaryrefslogblamecommitdiffstats
path: root/cmd/swarm/manifest.go
blob: ad918b8139493f9b7d79cb220ad40d26734f51aa (plain) (tree)
1
                                         


















                                                                       
             
            
                 
 


                                                                  
                                

 

































                                                                                                                                        




                                                                               
                          

                                                                                  


             


                               

         











                                                                             

         
                                                                            
                                

 




                                                                               
                          

                                                                                  


             


                               
         
 

                                                                             
 








                                                                             
         








                                                                                                             

 



                                                                              
                          

                                                                         


             

                               

         

                                                                             
 

                                                                   

 

                                                                                                   
 
                                                                 
                       
                                                                 

         
                                                                         

                                         
                                                                                          
                        

                                                                          
                                                                                      
                                                            







                                                              
                                                                                            

                                                        
                                           


                                                            
                         
                                                                      



                                                     

                                                            

         
                                                                         
                       
                                                               

                              

 






                                                                                                                                                                        
             

                                                      

         
                                                                 
                       
                                                                 

         
                                                                         





                                                                                              
                        

                                                                          
                                                                                      
                                                            





                                                               
                                                                                               




                                                              

                                                                                                                 

                                                        
                                           


                                                            
                         
                                                                      




                                


                                                            
                                                     
                                           


                                                    
                                                                                  





                                                                                  




                                
                                                                        
                       
                                                               
         
                                                            

 
                                                                               
             

                                                      

         
                                                                 
                       
                                                                 

         



                                                                         
                        
                                                                  








                                                                                      
                                                                                                




                                                                 
                                                                                          

                                                        
                                           










                                                                          
                                           







                                                                                  
                                                                         
                       
                                                               

                              
 
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

// Command  MANIFEST update
package main

import (
    "fmt"
    "os"
    "strings"

    "github.com/dexon-foundation/dexon/cmd/utils"
    "github.com/dexon-foundation/dexon/swarm/api"
    swarm "github.com/dexon-foundation/dexon/swarm/api/client"
    "gopkg.in/urfave/cli.v1"
)

var manifestCommand = cli.Command{
    Name:               "manifest",
    CustomHelpTemplate: helpTemplate,
    Usage:              "perform operations on swarm manifests",
    ArgsUsage:          "COMMAND",
    Description:        "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove",
    Subcommands: []cli.Command{
        {
            Action:             manifestAdd,
            CustomHelpTemplate: helpTemplate,
            Name:               "add",
            Usage:              "add a new path to the manifest",
            ArgsUsage:          "<MANIFEST> <path> <hash>",
            Description:        "Adds a new path to the manifest",
        },
        {
            Action:             manifestUpdate,
            CustomHelpTemplate: helpTemplate,
            Name:               "update",
            Usage:              "update the hash for an already existing path in the manifest",
            ArgsUsage:          "<MANIFEST> <path> <newhash>",
            Description:        "Update the hash for an already existing path in the manifest",
        },
        {
            Action:             manifestRemove,
            CustomHelpTemplate: helpTemplate,
            Name:               "remove",
            Usage:              "removes a path from the manifest",
            ArgsUsage:          "<MANIFEST> <path>",
            Description:        "Removes a path from the manifest",
        },
    },
}

// manifestAdd adds a new entry to the manifest at the given path.
// New entry hash, the last argument, must be the hash of a manifest
// with only one entry, which meta-data will be added to the original manifest.
// On success, this function will print new (updated) manifest's hash.
func manifestAdd(ctx *cli.Context) {
    args := ctx.Args()
    if len(args) != 3 {
        utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
    }

    var (
        mhash = args[0]
        path  = args[1]
        hash  = args[2]
    )

    bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
    client := swarm.NewClient(bzzapi)

    m, _, err := client.DownloadManifest(hash)
    if err != nil {
        utils.Fatalf("Error downloading manifest to add: %v", err)
    }
    l := len(m.Entries)
    if l == 0 {
        utils.Fatalf("No entries in manifest %s", hash)
    } else if l > 1 {
        utils.Fatalf("Too many entries in manifest %s", hash)
    }

    newManifest := addEntryToManifest(client, mhash, path, m.Entries[0])
    fmt.Println(newManifest)
}

// manifestUpdate replaces an existing entry of the manifest at the given path.
// New entry hash, the last argument, must be the hash of a manifest
// with only one entry, which meta-data will be added to the original manifest.
// On success, this function will print hash of the updated manifest.
func manifestUpdate(ctx *cli.Context) {
    args := ctx.Args()
    if len(args) != 3 {
        utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
    }

    var (
        mhash = args[0]
        path  = args[1]
        hash  = args[2]
    )

    bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
    client := swarm.NewClient(bzzapi)

    m, _, err := client.DownloadManifest(hash)
    if err != nil {
        utils.Fatalf("Error downloading manifest to update: %v", err)
    }
    l := len(m.Entries)
    if l == 0 {
        utils.Fatalf("No entries in manifest %s", hash)
    } else if l > 1 {
        utils.Fatalf("Too many entries in manifest %s", hash)
    }

    newManifest, _, defaultEntryUpdated := updateEntryInManifest(client, mhash, path, m.Entries[0], true)
    if defaultEntryUpdated {
        // Print informational message to stderr
        // allowing the user to get the new manifest hash from stdout
        // without the need to parse the complete output.
        fmt.Fprintln(os.Stderr, "Manifest default entry is updated, too")
    }
    fmt.Println(newManifest)
}

// manifestRemove removes an existing entry of the manifest at the given path.
// On success, this function will print hash of the manifest which does not
// contain the path.
func manifestRemove(ctx *cli.Context) {
    args := ctx.Args()
    if len(args) != 2 {
        utils.Fatalf("Need exactly two arguments <MHASH> <path>")
    }

    var (
        mhash = args[0]
        path  = args[1]
    )

    bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
    client := swarm.NewClient(bzzapi)

    newManifest := removeEntryFromManifest(client, mhash, path)
    fmt.Println(newManifest)
}

func addEntryToManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry) string {
    var longestPathEntry = api.ManifestEntry{}

    mroot, isEncrypted, err := client.DownloadManifest(mhash)
    if err != nil {
        utils.Fatalf("Manifest download failed: %v", err)
    }

    // See if we path is in this Manifest or do we have to dig deeper
    for _, e := range mroot.Entries {
        if path == e.Path {
            utils.Fatalf("Path %s already present, not adding anything", path)
        } else {
            if e.ContentType == api.ManifestType {
                prfxlen := strings.HasPrefix(path, e.Path)
                if prfxlen && len(path) > len(longestPathEntry.Path) {
                    longestPathEntry = e
                }
            }
        }
    }

    if longestPathEntry.Path != "" {
        // Load the child Manifest add the entry there
        newPath := path[len(longestPathEntry.Path):]
        newHash := addEntryToManifest(client, longestPathEntry.Hash, newPath, entry)

        // Replace the hash for parent Manifests
        newMRoot := &api.Manifest{}
        for _, e := range mroot.Entries {
            if longestPathEntry.Path == e.Path {
                e.Hash = newHash
            }
            newMRoot.Entries = append(newMRoot.Entries, e)
        }
        mroot = newMRoot
    } else {
        // Add the entry in the leaf Manifest
        entry.Path = path
        mroot.Entries = append(mroot.Entries, entry)
    }

    newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
    if err != nil {
        utils.Fatalf("Manifest upload failed: %v", err)
    }
    return newManifestHash
}

// updateEntryInManifest updates an existing entry o path with a new one in the manifest with provided mhash
// finding the path recursively through all nested manifests. Argument isRoot is used for default
// entry update detection. If the updated entry has the same hash as the default entry, then the
// default entry in root manifest will be updated too.
// Returned values are the new manifest hash, hash of the entry that was replaced by the new entry and
// a a bool that is true if default entry is updated.
func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry, isRoot bool) (newManifestHash, oldHash string, defaultEntryUpdated bool) {
    var (
        newEntry         = api.ManifestEntry{}
        longestPathEntry = api.ManifestEntry{}
    )

    mroot, isEncrypted, err := client.DownloadManifest(mhash)
    if err != nil {
        utils.Fatalf("Manifest download failed: %v", err)
    }

    // See if we path is in this Manifest or do we have to dig deeper
    for _, e := range mroot.Entries {
        if path == e.Path {
            newEntry = e
            // keep the reference of the hash of the entry that should be replaced
            // for default entry detection
            oldHash = e.Hash
        } else {
            if e.ContentType == api.ManifestType {
                prfxlen := strings.HasPrefix(path, e.Path)
                if prfxlen && len(path) > len(longestPathEntry.Path) {
                    longestPathEntry = e
                }
            }
        }
    }

    if longestPathEntry.Path == "" && newEntry.Path == "" {
        utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
    }

    if longestPathEntry.Path != "" {
        // Load the child Manifest add the entry there
        newPath := path[len(longestPathEntry.Path):]
        var newHash string
        newHash, oldHash, _ = updateEntryInManifest(client, longestPathEntry.Hash, newPath, entry, false)

        // Replace the hash for parent Manifests
        newMRoot := &api.Manifest{}
        for _, e := range mroot.Entries {
            if longestPathEntry.Path == e.Path {
                e.Hash = newHash
            }
            newMRoot.Entries = append(newMRoot.Entries, e)

        }
        mroot = newMRoot
    }

    // update the manifest if the new entry is found and
    // check if default entry should be updated
    if newEntry.Path != "" || isRoot {
        // Replace the hash for leaf Manifest
        newMRoot := &api.Manifest{}
        for _, e := range mroot.Entries {
            if newEntry.Path == e.Path {
                entry.Path = e.Path
                newMRoot.Entries = append(newMRoot.Entries, entry)
            } else if isRoot && e.Path == "" && e.Hash == oldHash {
                entry.Path = e.Path
                newMRoot.Entries = append(newMRoot.Entries, entry)
                defaultEntryUpdated = true
            } else {
                newMRoot.Entries = append(newMRoot.Entries, e)
            }
        }
        mroot = newMRoot
    }

    newManifestHash, err = client.UploadManifest(mroot, isEncrypted)
    if err != nil {
        utils.Fatalf("Manifest upload failed: %v", err)
    }
    return newManifestHash, oldHash, defaultEntryUpdated
}

func removeEntryFromManifest(client *swarm.Client, mhash, path string) string {
    var (
        entryToRemove    = api.ManifestEntry{}
        longestPathEntry = api.ManifestEntry{}
    )

    mroot, isEncrypted, err := client.DownloadManifest(mhash)
    if err != nil {
        utils.Fatalf("Manifest download failed: %v", err)
    }

    // See if we path is in this Manifest or do we have to dig deeper
    for _, entry := range mroot.Entries {
        if path == entry.Path {
            entryToRemove = entry
        } else {
            if entry.ContentType == api.ManifestType {
                prfxlen := strings.HasPrefix(path, entry.Path)
                if prfxlen && len(path) > len(longestPathEntry.Path) {
                    longestPathEntry = entry
                }
            }
        }
    }

    if longestPathEntry.Path == "" && entryToRemove.Path == "" {
        utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
    }

    if longestPathEntry.Path != "" {
        // Load the child Manifest remove the entry there
        newPath := path[len(longestPathEntry.Path):]
        newHash := removeEntryFromManifest(client, longestPathEntry.Hash, newPath)

        // Replace the hash for parent Manifests
        newMRoot := &api.Manifest{}
        for _, entry := range mroot.Entries {
            if longestPathEntry.Path == entry.Path {
                entry.Hash = newHash
            }
            newMRoot.Entries = append(newMRoot.Entries, entry)
        }
        mroot = newMRoot
    }

    if entryToRemove.Path != "" {
        // remove the entry in this Manifest
        newMRoot := &api.Manifest{}
        for _, entry := range mroot.Entries {
            if entryToRemove.Path != entry.Path {
                newMRoot.Entries = append(newMRoot.Entries, entry)
            }
        }
        mroot = newMRoot
    }

    newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
    if err != nil {
        utils.Fatalf("Manifest upload failed: %v", err)
    }
    return newManifestHash
}