354 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			354 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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/ethereum/go-ethereum/cmd/utils"
 | 
						|
	"github.com/ethereum/go-ethereum/swarm/api"
 | 
						|
	swarm "github.com/ethereum/go-ethereum/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
 | 
						|
}
 |