github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/cmd/swarm/manifest.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Command  MANIFEST update
    18  package main
    19  
    20  import (
    21  	"fmt"
    22  	"os"
    23  	"strings"
    24  
    25  	"github.com/ethereum/go-ethereum/cmd/utils"
    26  	"github.com/ethereum/go-ethereum/swarm/api"
    27  	swarm "github.com/ethereum/go-ethereum/swarm/api/client"
    28  	"gopkg.in/urfave/cli.v1"
    29  )
    30  
    31  var manifestCommand = cli.Command{
    32  	Name:               "manifest",
    33  	CustomHelpTemplate: helpTemplate,
    34  	Usage:              "perform operations on swarm manifests",
    35  	ArgsUsage:          "COMMAND",
    36  	Description:        "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove",
    37  	Subcommands: []cli.Command{
    38  		{
    39  			Action:             manifestAdd,
    40  			CustomHelpTemplate: helpTemplate,
    41  			Name:               "add",
    42  			Usage:              "add a new path to the manifest",
    43  			ArgsUsage:          "<MANIFEST> <path> <hash>",
    44  			Description:        "Adds a new path to the manifest",
    45  		},
    46  		{
    47  			Action:             manifestUpdate,
    48  			CustomHelpTemplate: helpTemplate,
    49  			Name:               "update",
    50  			Usage:              "update the hash for an already existing path in the manifest",
    51  			ArgsUsage:          "<MANIFEST> <path> <newhash>",
    52  			Description:        "Update the hash for an already existing path in the manifest",
    53  		},
    54  		{
    55  			Action:             manifestRemove,
    56  			CustomHelpTemplate: helpTemplate,
    57  			Name:               "remove",
    58  			Usage:              "removes a path from the manifest",
    59  			ArgsUsage:          "<MANIFEST> <path>",
    60  			Description:        "Removes a path from the manifest",
    61  		},
    62  	},
    63  }
    64  
    65  // manifestAdd adds a new entry to the manifest at the given path.
    66  // New entry hash, the last argument, must be the hash of a manifest
    67  // with only one entry, which meta-data will be added to the original manifest.
    68  // On success, this function will print new (updated) manifest's hash.
    69  func manifestAdd(ctx *cli.Context) {
    70  	args := ctx.Args()
    71  	if len(args) != 3 {
    72  		utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
    73  	}
    74  
    75  	var (
    76  		mhash = args[0]
    77  		path  = args[1]
    78  		hash  = args[2]
    79  	)
    80  
    81  	bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
    82  	client := swarm.NewClient(bzzapi)
    83  
    84  	m, _, err := client.DownloadManifest(hash)
    85  	if err != nil {
    86  		utils.Fatalf("Error downloading manifest to add: %v", err)
    87  	}
    88  	l := len(m.Entries)
    89  	if l == 0 {
    90  		utils.Fatalf("No entries in manifest %s", hash)
    91  	} else if l > 1 {
    92  		utils.Fatalf("Too many entries in manifest %s", hash)
    93  	}
    94  
    95  	newManifest := addEntryToManifest(client, mhash, path, m.Entries[0])
    96  	fmt.Println(newManifest)
    97  }
    98  
    99  // manifestUpdate replaces an existing entry of the manifest at the given path.
   100  // New entry hash, the last argument, must be the hash of a manifest
   101  // with only one entry, which meta-data will be added to the original manifest.
   102  // On success, this function will print hash of the updated manifest.
   103  func manifestUpdate(ctx *cli.Context) {
   104  	args := ctx.Args()
   105  	if len(args) != 3 {
   106  		utils.Fatalf("Need exactly three arguments <MHASH> <path> <HASH>")
   107  	}
   108  
   109  	var (
   110  		mhash = args[0]
   111  		path  = args[1]
   112  		hash  = args[2]
   113  	)
   114  
   115  	bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
   116  	client := swarm.NewClient(bzzapi)
   117  
   118  	m, _, err := client.DownloadManifest(hash)
   119  	if err != nil {
   120  		utils.Fatalf("Error downloading manifest to update: %v", err)
   121  	}
   122  	l := len(m.Entries)
   123  	if l == 0 {
   124  		utils.Fatalf("No entries in manifest %s", hash)
   125  	} else if l > 1 {
   126  		utils.Fatalf("Too many entries in manifest %s", hash)
   127  	}
   128  
   129  	newManifest, _, defaultEntryUpdated := updateEntryInManifest(client, mhash, path, m.Entries[0], true)
   130  	if defaultEntryUpdated {
   131  		// Print informational message to stderr
   132  		// allowing the user to get the new manifest hash from stdout
   133  		// without the need to parse the complete output.
   134  		fmt.Fprintln(os.Stderr, "Manifest default entry is updated, too")
   135  	}
   136  	fmt.Println(newManifest)
   137  }
   138  
   139  // manifestRemove removes an existing entry of the manifest at the given path.
   140  // On success, this function will print hash of the manifest which does not
   141  // contain the path.
   142  func manifestRemove(ctx *cli.Context) {
   143  	args := ctx.Args()
   144  	if len(args) != 2 {
   145  		utils.Fatalf("Need exactly two arguments <MHASH> <path>")
   146  	}
   147  
   148  	var (
   149  		mhash = args[0]
   150  		path  = args[1]
   151  	)
   152  
   153  	bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
   154  	client := swarm.NewClient(bzzapi)
   155  
   156  	newManifest := removeEntryFromManifest(client, mhash, path)
   157  	fmt.Println(newManifest)
   158  }
   159  
   160  func addEntryToManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry) string {
   161  	var longestPathEntry = api.ManifestEntry{}
   162  
   163  	mroot, isEncrypted, err := client.DownloadManifest(mhash)
   164  	if err != nil {
   165  		utils.Fatalf("Manifest download failed: %v", err)
   166  	}
   167  
   168  	// See if we path is in this Manifest or do we have to dig deeper
   169  	for _, e := range mroot.Entries {
   170  		if path == e.Path {
   171  			utils.Fatalf("Path %s already present, not adding anything", path)
   172  		} else {
   173  			if e.ContentType == api.ManifestType {
   174  				prfxlen := strings.HasPrefix(path, e.Path)
   175  				if prfxlen && len(path) > len(longestPathEntry.Path) {
   176  					longestPathEntry = e
   177  				}
   178  			}
   179  		}
   180  	}
   181  
   182  	if longestPathEntry.Path != "" {
   183  		// Load the child Manifest add the entry there
   184  		newPath := path[len(longestPathEntry.Path):]
   185  		newHash := addEntryToManifest(client, longestPathEntry.Hash, newPath, entry)
   186  
   187  		// Replace the hash for parent Manifests
   188  		newMRoot := &api.Manifest{}
   189  		for _, e := range mroot.Entries {
   190  			if longestPathEntry.Path == e.Path {
   191  				e.Hash = newHash
   192  			}
   193  			newMRoot.Entries = append(newMRoot.Entries, e)
   194  		}
   195  		mroot = newMRoot
   196  	} else {
   197  		// Add the entry in the leaf Manifest
   198  		entry.Path = path
   199  		mroot.Entries = append(mroot.Entries, entry)
   200  	}
   201  
   202  	newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
   203  	if err != nil {
   204  		utils.Fatalf("Manifest upload failed: %v", err)
   205  	}
   206  	return newManifestHash
   207  }
   208  
   209  // updateEntryInManifest updates an existing entry o path with a new one in the manifest with provided mhash
   210  // finding the path recursively through all nested manifests. Argument isRoot is used for default
   211  // entry update detection. If the updated entry has the same hash as the default entry, then the
   212  // default entry in root manifest will be updated too.
   213  // Returned values are the new manifest hash, hash of the entry that was replaced by the new entry and
   214  // a a bool that is true if default entry is updated.
   215  func updateEntryInManifest(client *swarm.Client, mhash, path string, entry api.ManifestEntry, isRoot bool) (newManifestHash, oldHash string, defaultEntryUpdated bool) {
   216  	var (
   217  		newEntry         = api.ManifestEntry{}
   218  		longestPathEntry = api.ManifestEntry{}
   219  	)
   220  
   221  	mroot, isEncrypted, err := client.DownloadManifest(mhash)
   222  	if err != nil {
   223  		utils.Fatalf("Manifest download failed: %v", err)
   224  	}
   225  
   226  	// See if we path is in this Manifest or do we have to dig deeper
   227  	for _, e := range mroot.Entries {
   228  		if path == e.Path {
   229  			newEntry = e
   230  			// keep the reference of the hash of the entry that should be replaced
   231  			// for default entry detection
   232  			oldHash = e.Hash
   233  		} else {
   234  			if e.ContentType == api.ManifestType {
   235  				prfxlen := strings.HasPrefix(path, e.Path)
   236  				if prfxlen && len(path) > len(longestPathEntry.Path) {
   237  					longestPathEntry = e
   238  				}
   239  			}
   240  		}
   241  	}
   242  
   243  	if longestPathEntry.Path == "" && newEntry.Path == "" {
   244  		utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
   245  	}
   246  
   247  	if longestPathEntry.Path != "" {
   248  		// Load the child Manifest add the entry there
   249  		newPath := path[len(longestPathEntry.Path):]
   250  		var newHash string
   251  		newHash, oldHash, _ = updateEntryInManifest(client, longestPathEntry.Hash, newPath, entry, false)
   252  
   253  		// Replace the hash for parent Manifests
   254  		newMRoot := &api.Manifest{}
   255  		for _, e := range mroot.Entries {
   256  			if longestPathEntry.Path == e.Path {
   257  				e.Hash = newHash
   258  			}
   259  			newMRoot.Entries = append(newMRoot.Entries, e)
   260  
   261  		}
   262  		mroot = newMRoot
   263  	}
   264  
   265  	// update the manifest if the new entry is found and
   266  	// check if default entry should be updated
   267  	if newEntry.Path != "" || isRoot {
   268  		// Replace the hash for leaf Manifest
   269  		newMRoot := &api.Manifest{}
   270  		for _, e := range mroot.Entries {
   271  			if newEntry.Path == e.Path {
   272  				entry.Path = e.Path
   273  				newMRoot.Entries = append(newMRoot.Entries, entry)
   274  			} else if isRoot && e.Path == "" && e.Hash == oldHash {
   275  				entry.Path = e.Path
   276  				newMRoot.Entries = append(newMRoot.Entries, entry)
   277  				defaultEntryUpdated = true
   278  			} else {
   279  				newMRoot.Entries = append(newMRoot.Entries, e)
   280  			}
   281  		}
   282  		mroot = newMRoot
   283  	}
   284  
   285  	newManifestHash, err = client.UploadManifest(mroot, isEncrypted)
   286  	if err != nil {
   287  		utils.Fatalf("Manifest upload failed: %v", err)
   288  	}
   289  	return newManifestHash, oldHash, defaultEntryUpdated
   290  }
   291  
   292  func removeEntryFromManifest(client *swarm.Client, mhash, path string) string {
   293  	var (
   294  		entryToRemove    = api.ManifestEntry{}
   295  		longestPathEntry = api.ManifestEntry{}
   296  	)
   297  
   298  	mroot, isEncrypted, err := client.DownloadManifest(mhash)
   299  	if err != nil {
   300  		utils.Fatalf("Manifest download failed: %v", err)
   301  	}
   302  
   303  	// See if we path is in this Manifest or do we have to dig deeper
   304  	for _, entry := range mroot.Entries {
   305  		if path == entry.Path {
   306  			entryToRemove = entry
   307  		} else {
   308  			if entry.ContentType == api.ManifestType {
   309  				prfxlen := strings.HasPrefix(path, entry.Path)
   310  				if prfxlen && len(path) > len(longestPathEntry.Path) {
   311  					longestPathEntry = entry
   312  				}
   313  			}
   314  		}
   315  	}
   316  
   317  	if longestPathEntry.Path == "" && entryToRemove.Path == "" {
   318  		utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
   319  	}
   320  
   321  	if longestPathEntry.Path != "" {
   322  		// Load the child Manifest remove the entry there
   323  		newPath := path[len(longestPathEntry.Path):]
   324  		newHash := removeEntryFromManifest(client, longestPathEntry.Hash, newPath)
   325  
   326  		// Replace the hash for parent Manifests
   327  		newMRoot := &api.Manifest{}
   328  		for _, entry := range mroot.Entries {
   329  			if longestPathEntry.Path == entry.Path {
   330  				entry.Hash = newHash
   331  			}
   332  			newMRoot.Entries = append(newMRoot.Entries, entry)
   333  		}
   334  		mroot = newMRoot
   335  	}
   336  
   337  	if entryToRemove.Path != "" {
   338  		// remove the entry in this Manifest
   339  		newMRoot := &api.Manifest{}
   340  		for _, entry := range mroot.Entries {
   341  			if entryToRemove.Path != entry.Path {
   342  				newMRoot.Entries = append(newMRoot.Entries, entry)
   343  			}
   344  		}
   345  		mroot = newMRoot
   346  	}
   347  
   348  	newManifestHash, err := client.UploadManifest(mroot, isEncrypted)
   349  	if err != nil {
   350  		utils.Fatalf("Manifest upload failed: %v", err)
   351  	}
   352  	return newManifestHash
   353  }