commands

package
v0.0.0-...-9fdd194 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 28, 2024 License: Apache-2.0, MIT Imports: 140 Imported by: 0

Documentation

Overview

Package commands implements the btfs command interface

Using github.com/bittorrent/go-btfs/commands to define the command line and HTTP APIs. This is the interface available to folks using BTFS from outside of the Go language.

Index

Constants

View Source
const (
	CheckBackoffDuration = 20 * time.Second
	CheckMaxRetries      = 3
)
View Source
const (
	ConfigOption  = "config"
	DebugOption   = "debug"
	LocalOption   = "local" // DEPRECATED: use OfflineOption
	OfflineOption = "offline"
	ApiOption     = "api"
)
View Source
const P2PProtoPrefix = "/x/"

P2PProtoPrefix is the default required prefix for protocol names

Variables

View Source
var AccessKeyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Manage S3-Compatible-API access-keys.",
		ShortDescription: "Commands for generate, update, get and list access-keys stored in this node.",
	},
	Subcommands: map[string]*cmds.Command{
		"generate": accessKeyGenerateCmd,
		"enable":   accessKeyEnableCmd,
		"disable":  accessKeyDisableCmd,
		"reset":    accessKeyResetCmd,
		"delete":   accessKeyDeleteCmd,
		"get":      accessKeyGetCmd,
		"list":     accessKeyListCmd,
	},
	NoLocal: true,
}
View Source
var ActiveReqsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List commands run on this BTFS node.",
		ShortDescription: `
Lists running and recently run commands.
`,
	},
	NoLocal: true,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctx := env.(*oldcmds.Context)
		return cmds.EmitOnce(res, ctx.ReqLog.Report())
	},
	Options: []cmds.Option{
		cmds.BoolOption(verboseOptionName, "v", "Print extra information."),
	},
	Subcommands: map[string]*cmds.Command{
		"clear":    clearInactiveCmd,
		"set-time": setRequestClearCmd,
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *[]*cmds.ReqLogEntry) error {
			verbose, _ := req.Options[verboseOptionName].(bool)

			tw := tabwriter.NewWriter(w, 4, 4, 2, ' ', 0)
			if verbose {
				fmt.Fprint(tw, "ID\t")
			}
			fmt.Fprint(tw, "Command\t")
			if verbose {
				fmt.Fprint(tw, "Arguments\tOptions\t")
			}
			fmt.Fprintln(tw, "Active\tStartTime\tRunTime")

			for _, req := range *out {
				if verbose {
					fmt.Fprintf(tw, "%d\t", req.ID)
				}
				fmt.Fprintf(tw, "%s\t", req.Command)
				if verbose {
					fmt.Fprintf(tw, "%v\t[", req.Args)
					var keys []string
					for k := range req.Options {
						keys = append(keys, k)
					}
					sort.Strings(keys)

					for _, k := range keys {
						fmt.Fprintf(tw, "%s=%v,", k, req.Options[k])
					}
					fmt.Fprintf(tw, "]\t")
				}

				var live time.Duration
				if req.Active {
					live = time.Since(req.StartTime)
				} else {
					live = req.EndTime.Sub(req.StartTime)
				}
				t := req.StartTime.Format(time.Stamp)
				fmt.Fprintf(tw, "%t\t%s\t%s\n", req.Active, t, live)
			}
			tw.Flush()

			return nil
		}),
	},
	Type: []*cmds.ReqLogEntry{},
}
View Source
var AddCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Add a file or directory to btfs.",
		ShortDescription: `
Adds contents of <path> to btfs. Use -r to add directories (recursively).
`,
		LongDescription: `
Adds contents of <path> to btfs. Use -r to add directories.
Note that directories are added recursively, to form the btfs
MerkleDAG.

If the daemon is not running, it will just add locally.
If the daemon is started later, it will be advertised after a few
seconds when the reprovider runs.

The wrap option, '-w', wraps the file (or files, if using the
recursive option) in a directory. This directory contains only
the files which have been added, and means that the file retains
its filename. For example:

  > btfs add example.jpg
  added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH example.jpg
  > btfs add example.jpg -w
  added QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH example.jpg
  added QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx

You can now refer to the added file in a gateway, like so:

  /btfs/QmaG4FuMqEBnQNn3C8XJ5bpW8kLs7zq2ZXgHptJHbKDDVx/example.jpg

The chunker option, '-s', specifies the chunking strategy that dictates
how to break files into blocks. Blocks with same content can
be deduplicated. Different chunking strategies will produce different
hashes for the same file. The default is a fixed block size of
256 * 1024 bytes, 'size-262144'. Alternatively, you can use the
Rabin fingerprint chunker for content defined chunking by specifying
rabin-[min]-[avg]-[max] (where min/avg/max refer to the desired
chunk sizes in bytes), e.g. 'rabin-262144-524288-1048576'.
Buzhash or Rabin fingerprint chunker for content defined chunking by
specifying buzhash or rabin-[min]-[avg]-[max] (where min/avg/max refer
to the desired chunk sizes in bytes), e.g. 'rabin-262144-524288-1048576'.
For replicated files intended for host storage, reed-solomon should be
used with default settings. It is also supported to customize data and
parity shards using reed-solomon-[#data]-[#parity]-[size].

The following examples use very small byte sizes to demonstrate the
properties of the different chunkers on a small file. You'll likely
want to use a 1024 times larger chunk sizes for most files.

  > btfs add --chunker=size-2048 btfs-logo.svg
  added QmafrLBfzRLV4XSH1XcaMMeaXEUhDJjmtDfsYU95TrWG87 btfs-logo.svg
  > btfs add --chunker=rabin-512-1024-2048 btfs-logo.svg
  added Qmf1hDN65tR55Ubh2RN1FPxr69xq3giVBz1KApsresY8Gn btfs-logo.svg

You can now check what blocks have been created by:

  > btfs object links QmafrLBfzRLV4XSH1XcaMMeaXEUhDJjmtDfsYU95TrWG87
  QmY6yj1GsermExDXoosVE3aSPxdMNYr6aKuw3nA8LoWPRS 2059
  Qmf7ZQeSxq2fJVJbCmgTrLLVN9tDR9Wy5k75DxQKuz5Gyt 1195
  > btfs object links Qmf1hDN65tR55Ubh2RN1FPxr69xq3giVBz1KApsresY8Gn
  QmY6yj1GsermExDXoosVE3aSPxdMNYr6aKuw3nA8LoWPRS 2059
  QmerURi9k4XzKCaaPbsK6BL5pMEjF7PGphjDvkkjDtsVf3 868
  QmQB28iwSriSUSMqG2nXDTLtdPHgWb4rebBrU7Q1j4vxPv 338

Finally, a note on hash determinism. While not guaranteed, adding the same
file/directory with the same flags will almost always result in the same output
hash. However, almost all of the flags provided by this command (other than pin,
only-hash, and progress/status related flags) will change the final hash.
`,
	},

	Arguments: []cmds.Argument{
		cmds.FileArg("path", true, true, "The path to a file to be added to btfs.").EnableRecursive().EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.OptionRecursivePath,
		cmds.OptionDerefArgs,
		cmds.OptionStdinName,
		cmds.OptionHidden,
		cmds.OptionIgnore,
		cmds.OptionIgnoreRules,
		cmds.BoolOption(quietOptionName, "q", "Write minimal output."),
		cmds.BoolOption(quieterOptionName, "Q", "Write only final hash."),
		cmds.BoolOption(silentOptionName, "Write no output."),
		cmds.BoolOption(progressOptionName, "p", "Stream progress data."),
		cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation."),
		cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk."),
		cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object."),
		cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm, size-[bytes], rabin-[min]-[avg]-[max], buzhash or reed-solomon-[#data]-[#parity]-[size]").WithDefault("size-262144"),
		cmds.BoolOption(pinOptionName, "Pin this object when adding.").WithDefault(true),
		cmds.BoolOption(rawLeavesOptionName, "Use raw blocks for leaf nodes. (experimental)"),
		cmds.BoolOption(noCopyOptionName, "Add the file using filestore. Implies raw-leaves. (experimental)"),
		cmds.BoolOption(fstoreCacheOptionName, "Check the filestore for pre-existing blocks. (experimental)"),
		cmds.IntOption(cidVersionOptionName, "CID version. Defaults to 0 unless an option that depends on CIDv1 is passed. (experimental)"),
		cmds.StringOption(hashOptionName, "Hash function to use. Implies CIDv1 if not sha2-256. (experimental)").WithDefault("sha2-256"),
		cmds.BoolOption(inlineOptionName, "Inline small blocks into CIDs. (experimental)"),
		cmds.IntOption(inlineLimitOptionName, "Maximum block size to inline. (experimental)").WithDefault(32),
		cmds.StringOption(tokenMetaOptionName, "m", "Token metadata in JSON string"),
		cmds.BoolOption(encryptName, "Encrypt the file."),
		cmds.StringOption(pubkeyName, "The public key to encrypt the file."),
		cmds.StringOption(peerIdName, "The peer id to encrypt the file."),
		cmds.IntOption(pinDurationCountOptionName, "d", "Duration for which the object is pinned in days.").WithDefault(0),
		cmds.BoolOption(uploadToBlockchainOptionName, "add file meta to blockchain").WithDefault(false),
	},
	PreRun: func(req *cmds.Request, env cmds.Environment) error {
		quiet, _ := req.Options[quietOptionName].(bool)
		quieter, _ := req.Options[quieterOptionName].(bool)
		quiet = quiet || quieter

		silent, _ := req.Options[silentOptionName].(bool)

		if quiet || silent {
			return nil
		}

		_, found := req.Options[progressOptionName].(bool)
		if !found {
			req.Options[progressOptionName] = true
		}

		return nil
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		progress, _ := req.Options[progressOptionName].(bool)
		trickle, _ := req.Options[trickleOptionName].(bool)
		wrap, _ := req.Options[wrapOptionName].(bool)
		hash, _ := req.Options[onlyHashOptionName].(bool)
		silent, _ := req.Options[silentOptionName].(bool)
		chunker, _ := req.Options[chunkerOptionName].(string)
		dopin, _ := req.Options[pinOptionName].(bool)
		rawblks, rbset := req.Options[rawLeavesOptionName].(bool)
		nocopy, _ := req.Options[noCopyOptionName].(bool)
		fscache, _ := req.Options[fstoreCacheOptionName].(bool)
		cidVer, cidVerSet := req.Options[cidVersionOptionName].(int)
		hashFunStr, _ := req.Options[hashOptionName].(string)
		inline, _ := req.Options[inlineOptionName].(bool)
		inlineLimit, _ := req.Options[inlineLimitOptionName].(int)
		tokenMetadata, _ := req.Options[tokenMetaOptionName].(string)
		encrypt, _ := req.Options[encryptName].(bool)
		pubkey, _ := req.Options[pubkeyName].(string)
		peerId, _ := req.Options[peerIdName].(string)
		pinDuration, _ := req.Options[pinDurationCountOptionName].(int)
		uploadToBlockchain, _ := req.Options[uploadToBlockchainOptionName].(bool)

		hashFunCode, ok := mh.Names[strings.ToLower(hashFunStr)]
		if !ok {
			return fmt.Errorf("unrecognized hash function: %s", strings.ToLower(hashFunStr))
		}

		enc, err := cmdenv.GetCidEncoder(req)
		if err != nil {
			return err
		}

		toadd := req.Files
		if wrap {
			toadd = files.NewSliceDirectory([]files.DirEntry{
				files.FileEntry("", req.Files),
			})
		}

		opts := []options.UnixfsAddOption{
			options.Unixfs.Hash(hashFunCode),

			options.Unixfs.Inline(inline),
			options.Unixfs.InlineLimit(inlineLimit),

			options.Unixfs.Chunker(chunker),

			options.Unixfs.Pin(dopin),
			options.Unixfs.HashOnly(hash),
			options.Unixfs.FsCache(fscache),
			options.Unixfs.Nocopy(nocopy),

			options.Unixfs.Progress(progress),
			options.Unixfs.Silent(silent),

			options.Unixfs.TokenMetadata(tokenMetadata),
			options.Unixfs.PinDuration(int64(pinDuration)),
		}

		if cidVerSet {
			opts = append(opts, options.Unixfs.CidVersion(cidVer))
		}

		if rbset {
			opts = append(opts, options.Unixfs.RawLeaves(rawblks))
		}

		if trickle {
			opts = append(opts, options.Unixfs.Layout(options.TrickleLayout))
		}

		if encrypt {
			opts = append(opts, options.Unixfs.Encrypt(encrypt))
			opts = append(opts, options.Unixfs.Pubkey(pubkey))
			opts = append(opts, options.Unixfs.PeerId(peerId))
		}

		opts = append(opts, nil)

		var added int
		addit := toadd.Entries()
		for addit.Next() {
			_, dir := addit.Node().(files.Directory)
			errCh := make(chan error, 1)
			events := make(chan interface{}, adderOutChanSize)
			opts[len(opts)-1] = options.Unixfs.Events(events)
			var pr coreifacePath.Resolved
			go func() {
				var err error
				defer close(events)
				pr, err = api.Unixfs().Add(req.Context, addit.Node(), opts...)
				errCh <- err
			}()

			for event := range events {
				output, ok := event.(*coreiface.AddEvent)
				if !ok {
					return errors.New("unknown event type")
				}

				h := ""
				if output.Path != nil {
					h = enc.Encode(output.Path.Cid())
				}

				if !dir && addit.Name() != "" {
					output.Name = addit.Name()
				} else {
					output.Name = path.Join(addit.Name(), output.Name)
				}

				if err := res.Emit(&AddEvent{
					Name:  output.Name,
					Hash:  h,
					Bytes: output.Bytes,
					Size:  output.Size,
				}); err != nil {
					return err
				}
			}

			if err := <-errCh; err != nil {
				return err
			}
			added++
			if uploadToBlockchain {
				cctx := env.(*oldcmds.Context)
				cfg, err := cctx.GetConfig()
				if err != nil {
					return err
				}
				fname := addit.Name()
				size, _ := addit.Node().Size()
				cli, err := ethclient.Dial(cfg.ChainInfo.Endpoint)
				if err != nil {
					return err
				}
				defer cli.Close()
				currChainCfg, ok := chainconfig.GetChainConfig(cfg.ChainInfo.ChainId)
				if !ok {
					return fmt.Errorf("chain %d is not supported yet", cfg.ChainInfo.ChainId)
				}
				contractAddress := currChainCfg.FileMetaAddress
				contr, err := abi.NewFileMeta(contractAddress, cli)
				if err != nil {
					return err
				}
				pkbytesOri, err := base64.StdEncoding.DecodeString(cfg.Identity.PrivKey)
				if err != nil {
					return err
				}
				privateKey, err := ethCrypto.ToECDSA(pkbytesOri[4:])
				if err != nil {
					return err
				}
				fromAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
				nonce, err := cli.PendingNonceAt(req.Context, fromAddress)
				if err != nil {
					return err
				}
				auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(cfg.ChainInfo.ChainId))
				if err != nil {
					return err
				}
				auth.Nonce = big.NewInt(int64(nonce))
				auth.Value = big.NewInt(0)
				data := abi.FileMetaFileMetaData{
					OwnerPeerId: cfg.Identity.PeerID,
					From:        common.HexToAddress(cfg.Identity.BttcAddr),
					FileName:    fname,
					FileExt:     path.Ext(fname),
					IsDir:       dir,
					FileSize:    big.NewInt(size),
				}
				tx, err := contr.AddFileMeta(auth, pr.Cid().String(), data)
				if err != nil {
					return err
				}
				fmt.Println("Write into file meta contract successfully! Transaction hash is: ", tx.Hash().Hex())
			}
		}

		if addit.Err() != nil {
			return addit.Err()
		}

		if added == 0 {
			return fmt.Errorf("expected a file argument")
		}

		return nil
	},
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			sizeChan := make(chan int64, 1)
			outChan := make(chan interface{})
			req := res.Request()

			go func() {
				op := res.Request().Options[encryptName]
				encrypt := op != nil && op.(bool)
				if encrypt {
					it := req.Files.Entries()
					var size int64 = 0
					for it.Next() {
						s, err := it.Node().Size()
						if err != nil {
							log.Warnf("error getting files size: %s", err)

							return
						}
						blockCount := s/16 + 1
						size += blockCount * 32
						sizeChan <- size
					}
				} else {
					size, err := req.Files.Size()
					if err != nil {
						log.Warnf("error getting files size: %s", err)

						return
					}
					sizeChan <- size
				}
			}()

			progressBar := func(wait chan struct{}) {
				defer close(wait)

				quiet, _ := req.Options[quietOptionName].(bool)
				quieter, _ := req.Options[quieterOptionName].(bool)
				quiet = quiet || quieter

				progress, _ := req.Options[progressOptionName].(bool)

				var bar *pb.ProgressBar
				if progress {
					bar = pb.New64(0).SetUnits(pb.U_BYTES)
					bar.ManualUpdate = true
					bar.ShowTimeLeft = false
					bar.ShowPercent = false
					bar.Output = os.Stderr
					bar.Start()
				}

				lastFile := ""
				lastHash := ""
				var totalProgress, prevFiles, lastBytes int64

			LOOP:
				for {
					select {
					case out, ok := <-outChan:
						if !ok {
							if quieter {
								fmt.Fprintln(os.Stdout, lastHash)
							}

							break LOOP
						}
						output := out.(*AddEvent)
						if len(output.Hash) > 0 {
							lastHash = output.Hash
							if quieter {
								continue
							}

							if progress {

								fmt.Fprintf(os.Stderr, "\033[2K\r")
							}
							if quiet {
								fmt.Fprintf(os.Stdout, "%s\n", output.Hash)
							} else {
								fmt.Fprintf(os.Stdout, "added %s %s\n", output.Hash, output.Name)
							}

						} else {
							if !progress {
								continue
							}

							if len(lastFile) == 0 {
								lastFile = output.Name
							}
							if output.Name != lastFile || output.Bytes < lastBytes {
								prevFiles += lastBytes
								lastFile = output.Name
							}
							lastBytes = output.Bytes
							delta := prevFiles + lastBytes - totalProgress
							totalProgress = bar.Add64(delta)
						}

						if progress {
							bar.Update()
						}
					case size := <-sizeChan:
						if progress {
							bar.Total = size
							bar.ShowPercent = true
							bar.ShowBar = true
							bar.ShowTimeLeft = true
						}
					case <-req.Context.Done():

						return
					}
				}

				if progress && bar.Total == 0 && bar.Get() != 0 {
					bar.Total = bar.Get()
					bar.ShowPercent = true
					bar.ShowBar = true
					bar.ShowTimeLeft = true
					bar.Update()
				}
			}

			if e := res.Error(); e != nil {
				close(outChan)
				return e
			}

			wait := make(chan struct{})
			go progressBar(wait)

			defer func() { <-wait }()
			defer close(outChan)

			for {
				v, err := res.Next()
				if err != nil {
					if err == io.EOF {
						return nil
					}

					return err
				}

				select {
				case outChan <- v:
				case <-req.Context.Done():
					return req.Context.Err()
				}
			}
		},
	},
	Type: AddEvent{},
}
View Source
var BackupCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Back up BTFS's data",
		LongDescription: `
This command will create a backup of the data from the current BTFS node.
`,
	},
	Arguments: []cmds.Argument{
		cmds.FileArg("file", true, false, "data to encode").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.StringOption(outputFileOption, "backup output file path"),
		cmds.StringOption(compressOption, "gz or zip").WithDefault("gz"),
		cmds.StringsOption(excludeOption, "exclude backup output file path"),
	},
	Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		if nd.IsOnline {
			return errors.New("this action must be run in offline mode, please stop your 'btfs daemon' first")
		}
		r, err := fsrepo.Open(env.(*commands.Context).ConfigRoot)
		if err != nil {
			return err
		}
		defer r.Close()

		var fileName = fmt.Sprintf("btfs_backup_%d", time.Now().Unix())

		outputName, ok := req.Options[outputFileOption].(string)
		if ok {
			fileName = outputName
		}
		btfsPath, err := fsrepo.BestKnownPath()
		if err != nil {
			return err
		}

		excludePath, _ := req.Options[excludeOption].([]string)
		for _, v := range excludePath {

			if v != "config" && v != "statestore" && v != "datastore" {
				return errors.New("-exclude only support config, statestore or datastore")
			}
		}

		excludePath = append(excludePath, "repo.lock")
		compressWay, _ := req.Options[compressOption].(string)

		if compressWay != "gz" && compressWay != "zip" {
			return errors.New("-a only support zip or gz, gz is default")
		}
		absPath, err := filepath.Abs(fileName)
		if err != nil {
			return err
		}
		if compressWay == "zip" {
			absPath += ".zip"
			err = Zip(btfsPath, absPath, excludePath)
		} else {
			absPath += ".tar.gz"
			err = Tar(btfsPath, absPath, excludePath)
		}
		if err != nil {
			return err
		}
		fmt.Printf("Backup successful! The backup path is %s\n", absPath)
		return nil
	},
}
View Source
var BitswapCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Interact with the bitswap agent.",
		ShortDescription: ``,
	},

	Subcommands: map[string]*cmds.Command{
		"stat":      bitswapStatCmd,
		"wantlist":  showWantlistCmd,
		"ledger":    ledgerCmd,
		"reprovide": reprovideCmd,
	},
}
View Source
var BlockCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with raw BTFS blocks.",
		ShortDescription: `
'btfs block' is a plumbing command used to manipulate raw BTFS blocks.
Reads from stdin or writes to stdout, and <key> is a base58 encoded
multihash.
`,
	},

	Subcommands: map[string]*cmds.Command{
		"stat": blockStatCmd,
		"get":  blockGetCmd,
		"put":  blockPutCmd,
		"rm":   blockRmCmd,
	},
}
View Source
var BootstrapCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Show or edit the list of bootstrap peers.",
		ShortDescription: `
Running 'btfs bootstrap' with no arguments will run 'btfs bootstrap list'.
` + bootstrapSecurityWarning,
	},

	Run:      bootstrapListCmd.Run,
	Encoders: bootstrapListCmd.Encoders,
	Type:     bootstrapListCmd.Type,

	Subcommands: map[string]*cmds.Command{
		"list": bootstrapListCmd,
		"add":  bootstrapAddCmd,
		"rm":   bootstrapRemoveCmd,
	},
}
View Source
var CatCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Show BTFS object data.",
		ShortDescription: "Displays the data contained by an BTFS or BTNS object(s) at the given path.",
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("btfs-path", true, true, "The path to the BTFS object(s) to be outputted.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.Int64Option(offsetOptionName, "o", "Byte offset to begin reading from."),
		cmds.Int64Option(lengthOptionName, "l", "Maximum number of bytes to read."),
		cmds.BoolOption(catMetaDisplayOptionName, "m", "Display token metadata"),
		cmds.BoolOption(decryptName, "d", "Decrypt the file."),
		cmds.StringOption(privateKeyName, "pk", "The private key to decrypt file."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		offset, _ := req.Options[offsetOptionName].(int64)
		if offset < 0 {
			return fmt.Errorf("cannot specify negative offset")
		}

		max, found := req.Options[lengthOptionName].(int64)

		if max < 0 {
			return fmt.Errorf("cannot specify negative length")
		}
		if !found {
			max = -1
		}

		meta, _ := req.Options[catMetaDisplayOptionName].(bool)

		err = req.ParseBodyArgs()
		if err != nil {
			return err
		}

		readers, length, err := cat(req.Context, api, req.Arguments, int64(offset), int64(max), meta, req.Options)
		if err != nil {
			return err
		}

		res.SetLength(length)
		reader := io.MultiReader(readers...)

		return res.Emit(reader)
	},
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			if res.Length() > 0 && res.Length() < progressBarMinSize {
				return cmds.Copy(re, res)
			}

			for {
				v, err := res.Next()
				if err != nil {
					if err == io.EOF {
						return nil
					}
					return err
				}

				switch val := v.(type) {
				case io.Reader:
					bar, reader := progressBarForReader(os.Stderr, val, int64(res.Length()))
					bar.Start()

					err = re.Emit(reader)
					if err != nil {
						return err
					}
				default:
					log.Warnf("cat postrun: received unexpected type %T", val)
				}
			}
		},
	},
}
View Source
var CidCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Convert and discover properties of CIDs",
	},
	Subcommands: map[string]*cmds.Command{
		"format": cidFmtCmd,
		"base32": base32Cmd,
		"bases":  basesCmd,
		"codecs": codecsCmd,
		"hashes": hashesCmd,
	},
	Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
}
View Source
var CommandsDaemonCmd = CommandsCmd(Root)

commandsDaemonCmd is the "btfs commands" command for daemon

View Source
var CommandsDaemonROCmd = CommandsCmd(RootRO)
View Source
var ConfigCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Get and set btfs config values.",
		ShortDescription: `
'btfs config' controls configuration variables. It works like 'git config'.
The configuration values are stored in a config file inside your btfs
repository.`,
		LongDescription: `
'btfs config' controls configuration variables. It works
much like 'git config'. The configuration values are stored in a config
file inside your BTFS repository.

Examples:

Get the value of the 'Datastore.Path' key:

  $ btfs config Datastore.Path

Set the value of the 'Datastore.Path' key:

  $ btfs config Datastore.Path ~/.btfs/datastore
`,
	},
	Subcommands: map[string]*cmds.Command{
		"show":    configShowCmd,
		"reset":   resetConfigCmd,
		"edit":    configEditCmd,
		"replace": configReplaceCmd,

		"storage-host-enable": storageHostEnableCmd,
		"sync-chain-info":     SyncChainInfoCmd,
		"sync-simple-mode":    SyncSimpleModeCmd,
		"optin":               optInCmd,
		"optout":              optOutCmd,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")."),
		cmds.StringArg("value", false, false, "The value to set the config entry to."),
	},
	Options: []cmds.Option{
		cmds.BoolOption(configBoolOptionName, "Set a boolean value."),
		cmds.BoolOption(configJSONOptionName, "Parse stringified JSON."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		args := req.Arguments
		key := args[0]

		var output *ConfigField

		switch strings.ToLower(key) {
		case "identity", "identity.privkey", "identity.hexprivkey", "identity.mnemonic":
			return fmt.Errorf("cannot show or change %s through API", key)
		default:
		}

		cfgRoot, err := cmdenv.GetConfigRoot(env)
		if err != nil {
			return err
		}
		r, err := fsrepo.Open(cfgRoot)
		if err != nil {
			return err
		}
		defer r.Close()
		if len(args) == 2 {
			value := args[1]

			if parseJSON, _ := req.Options[configJSONOptionName].(bool); parseJSON {
				var jsonVal interface{}
				if err := json.Unmarshal([]byte(value), &jsonVal); err != nil {
					err = fmt.Errorf("failed to unmarshal json. %s", err)
					return err
				}

				output, err = setConfig(r, key, jsonVal)
			} else if isbool, _ := req.Options[configBoolOptionName].(bool); isbool {
				output, err = setConfig(r, key, value == "true")
			} else {
				output, err = setConfig(r, key, value)
			}
			if err != nil {
				return err
			}
			if f, err := getConfig(r, "UI.Wallet.Initialized"); err == nil {
				if f.Value.(bool) == true {
					err := r.SetConfigKey("Identity.Mnemonic", "")
					if err != nil {
						return err
					}
				}
			}
		} else {
			output, err = getConfig(r, key)
		}

		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, output)
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ConfigField) error {
			if len(req.Arguments) == 2 {
				return nil
			}

			buf, err := config.HumanOutput(out.Value)
			if err != nil {
				return err
			}
			buf = append(buf, byte('\n'))

			_, err = w.Write(buf)
			return err
		}),
	},
	Type: ConfigField{},
}
View Source
var DNSCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Resolve DNS links.",
		ShortDescription: `
Multihashes are hard to remember, but domain names are usually easy to
remember.  To create memorable aliases for multihashes, DNS TXT
records can point to other DNS links, BTFS objects, BTNS keys, etc.
This command resolves those links to the referenced object.
`,
		LongDescription: `
Multihashes are hard to remember, but domain names are usually easy to
remember.  To create memorable aliases for multihashes, DNS TXT
records can point to other DNS links, BTFS objects, BTNS keys, etc.
This command resolves those links to the referenced object.

Note: This command can only recursively resolve DNS links,
it will fail to recursively resolve through BTNS keys etc.
For general-purpose recursive resolution, use btfs name resolve -r.

For example, with this DNS TXT record:

	> dig +short TXT _dnslink.ipfs.io
	dnslink=/btfs/QmRzTuh2Lpuz7Gr39stNr6mTFdqAghsZec1JoUnfySUzcy

The resolver will give:

	> btfs dns ipfs.io
	/btfs/QmRzTuh2Lpuz7Gr39stNr6mTFdqAghsZec1JoUnfySUzcy

The resolver can recursively resolve:

	> dig +short TXT recursive.ipfs.io
	dnslink=/ipns/ipfs.io
	> btfs dns -r recursive.ipfs.io
	/btfs/QmRzTuh2Lpuz7Gr39stNr6mTFdqAghsZec1JoUnfySUzcy
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("domain-name", true, false, "The domain-name name to resolve.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.BoolOption(dnsRecursiveOptionName, "r", "Resolve until the result is not a DNS link.").WithDefault(true),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		node, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}
		recursive, _ := req.Options[dnsRecursiveOptionName].(bool)
		name := req.Arguments[0]
		resolver := namesys.NewDNSResolver(node.DNSResolver.LookupTXT)

		var routing []nsopts.ResolveOpt
		if !recursive {
			routing = append(routing, nsopts.Depth(1))
		}

		output, err := resolver.Resolve(req.Context, name, routing...)
		if err != nil && (recursive || err != namesys.ErrResolveRecursion) {
			return err
		}
		return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: output})
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ncmd.ResolvedPath) error {
			fmt.Fprintln(w, out.Path.String())
			return nil
		}),
	},
	Type: ncmd.ResolvedPath{},
}
View Source
var DhtCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Issue commands directly through the DHT.",
		ShortDescription: ``,
	},

	Subcommands: map[string]*cmds.Command{
		"query":     queryDhtCmd,
		"findprovs": findProvidersDhtCmd,
		"findpeer":  findPeerDhtCmd,
		"get":       getValueDhtCmd,
		"put":       putValueDhtCmd,
		"provide":   provideRefDhtCmd,
	},
}
View Source
var DiagCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Generate diagnostic reports.",
	},

	Subcommands: map[string]*cmds.Command{
		"sys":  sysDiagCmd,
		"cmds": ActiveReqsCmd,
	},
}
View Source
var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded")

ErrDepthLimitExceeded indicates that the max depth has been exceeded.

View Source
var ErrNotDHT = errors.New("routing service is not a DHT")
View Source
var ErrNotOnline = errors.New("this command must be run in online mode. Try running 'btfs daemon' first")
View Source
var ErrPingSelf = errors.New("error: can't ping self")

ErrPingSelf is returned when the user attempts to ping themself.

View Source
var FileStoreCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with filestore objects.",
	},
	Subcommands: map[string]*cmds.Command{
		"ls":     lsFileStore,
		"verify": verifyFileStore,
		"dups":   dupsFileStore,
	},
}
View Source
var FilesCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with unixfs files.",
		ShortDescription: `
Files is an API for manipulating BTFS objects as if they were a unix
filesystem.

The files facility interacts with MFS (Mutable File System). MFS acts as a
single, dynamic filesystem mount. MFS has a root CID that is transparently
updated when a change happens (and can be checked with "ipfs files stat /").

All files and folders within MFS are respected and will not be cleaned up
during garbage collections. MFS is independent from the list of pinned items
("ipfs pin ls"). Calls to "ipfs pin add" and "ipfs pin rm" will add and remove
pins independently of MFS. If MFS content that was
additionally pinned is removed by calling "ipfs files rm", it will still
remain pinned.

Content added with "ipfs add" (which by default also becomes pinned), is not
added to MFS. Any content can be put into MFS with the command "ipfs files cp
/ipfs/<cid> /some/path/".


NOTE:
Most of the subcommands of 'btfs files' accept the '--flush' flag. It defaults
to true. Use caution when setting this flag to false. It will improve
performance for large numbers of file operations, but it does so at the cost
of consistency guarantees. If the daemon is unexpectedly killed before running
'btfs files flush' on the files in question, then data may be lost. This also
applies to running 'btfs repo gc' concurrently with '--flush=false'
operations.
`,
	},
	Options: []cmds.Option{
		cmds.BoolOption(filesFlushOptionName, "f", "Flush target and ancestors after write.").WithDefault(true),
	},
	Subcommands: map[string]*cmds.Command{
		"read":  filesReadCmd,
		"write": filesWriteCmd,
		"mv":    filesMvCmd,
		"cp":    filesCpCmd,
		"ls":    filesLsCmd,
		"mkdir": filesMkdirCmd,
		"stat":  filesStatCmd,
		"rm":    filesRmCmd,
		"flush": filesFlushCmd,
		"chcid": filesChcidCmd,
	},
}

FilesCmd is the 'btfs files' command

View Source
var GetCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Download BTFS objects.",
		ShortDescription: `
Stores to disk the data contained an BTFS or BTNS object(s) at the given path.

By default, the output will be stored at './<btfs-path>', but an alternate
path can be specified with '--output=<path>' or '-o=<path>'.

To output a TAR archive instead of unpacked files, use '--archive' or '-a'.

To compress the output with GZIP compression, use '--compress' or '-C'. You
may also specify the level of compression by specifying '-l=<1-9>'.

To output just the metadata of this BTFS node, use '--meta' or '-m'.

To repair missing shards of a Reed-Solomon encoded file, use '--repair-shards' or '-rs'.
If '--meta' or '-m' is enabled, this option is ignored.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("btfs-path", true, false, "The path to the BTFS object(s) to be outputted.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.StringOption(outputOptionName, "o", "The path where the output should be stored."),
		cmds.BoolOption(archiveOptionName, "a", "Output a TAR archive."),
		cmds.BoolOption(compressOptionName, "C", "Compress the output with GZIP compression."),
		cmds.IntOption(compressionLevelOptionName, "l", "The level of compression (1-9)."),
		cmds.BoolOption(getMetaDisplayOptionName, "m", "Display token metadata"),
		cmds.BoolOption(decryptName, "d", "Decrypt the file."),
		cmds.StringOption(privateKeyName, "pk", "The private key to decrypt file."),
		cmds.StringOption(repairShardsName, "rs", "Repair the list of shards. Multihashes separated by ','."),
		cmds.BoolOption(quietOptionName, "q", "Quiet mode: perform get operation without writing to anywhere. Same as using -o /dev/null."),
	},
	PreRun: func(req *cmds.Request, env cmds.Environment) error {
		_, err := cmdenv.GetCompressLevel(getCompressOptions(req))
		return err
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		btfsPath := req.Arguments[0]
		decrypt, _ := req.Options[decryptName].(bool)
		privateKey, _ := req.Options[privateKeyName].(string)
		meta, _ := req.Options[getMetaDisplayOptionName].(bool)
		repairShards, _ := req.Options[repairShardsName].(string)
		quiet, _ := req.Options[quietOptionName].(bool)
		archive, _ := req.Options[archiveOptionName].(bool)
		cmprs, cmplvl := getCompressOptions(req)

		reader, err := cmdenv.GetFile(req, res, api, btfsPath, decrypt, privateKey, meta, repairShards, quiet, archive, cmprs, cmplvl)
		if err != nil {
			return err
		}

		return res.Emit(reader)
	},
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			req := res.Request()

			v, err := res.Next()
			if err != nil {
				return err
			}

			if v == nil {
				return nil
			}

			outReader, ok := v.(io.Reader)
			if !ok {
				return e.New(e.TypeErr(outReader, v))
			}

			outPath := getOutPath(req)

			cmplvl, err := cmdenv.GetCompressLevel(getCompressOptions(req))
			if err != nil {
				return err
			}

			archive, _ := req.Options[archiveOptionName].(bool)
			gw := getWriter{
				Out:         os.Stdout,
				Err:         os.Stderr,
				Archive:     archive,
				Compression: cmplvl,
				Size:        int64(res.Length()),
			}

			return gw.Write(outReader, outPath)
		},
	},
}
View Source
var GuardCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with guard services from BTFS client.",
		ShortDescription: `
Connect with guard functions directly through this command.
The subcommands here are mostly for debugging and testing purposes.`,
	},
	Subcommands: map[string]*cmds.Command{
		"test": guardTestCmd,
	},
}
View Source
var IDCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Show btfs node id info.",
		ShortDescription: `
Prints out information about the specified peer.
If no peer is specified, prints out information for local peers.

'btfs id' supports the format option for output with the following keys:
<id> : The peers id.
<aver>: Agent version.
<pver>: Protocol version.
<pubkey>: Public key.
<addrs>: Addresses (newline delimited).

EXAMPLE:

    btfs id Qmece2RkXhsKe5CRooNisBTh4SK119KrXXGmoK6V3kb8aH -f="<addrs>\n"
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("peerid", false, false, "Peer.ID of node to look up."),
	},
	Options: []cmds.Option{
		cmds.StringOption(formatOptionName, "f", "Optional output format."),
		cmds.StringOption(idFormatOptionName, "Encoding used for peer IDs: Can either be a multibase encoded CID or a base58btc encoded multihash. Takes {b58mh|base36|k|base32|b...}.").WithDefault("b58mh"),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		keyEnc, err := ke.KeyEncoderFromString(req.Options[idFormatOptionName].(string))
		if err != nil {
			return err
		}

		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		var id peer.ID
		if len(req.Arguments) > 0 {
			var err error
			id, err = peer.Decode(req.Arguments[0])
			if err != nil {
				return fmt.Errorf("invalid peer id")
			}
		} else {
			id = n.Identity
		}

		if id == n.Identity {
			output, err := printSelf(keyEnc, n, env)
			if err != nil {
				return err
			}
			return cmds.EmitOnce(res, output)
		}

		if !n.IsOnline {
			return errors.New(offlineIdErrorMessage)
		}

		err = n.PeerHost.Connect(req.Context, peer.AddrInfo{ID: id})
		switch err {
		case nil:
		case kb.ErrLookupFailure:
			return errors.New(offlineIdErrorMessage)
		default:
			return err
		}

		output, err := printPeer(keyEnc, n.Peerstore, id, n, env)
		if err != nil {
			return err
		}
		return cmds.EmitOnce(res, output)
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *IdOutput) error {
			format, found := req.Options[formatOptionName].(string)
			if found {
				output := format
				output = strings.Replace(output, "<id>", out.ID, -1)
				output = strings.Replace(output, "<aver>", out.AgentVersion, -1)
				output = strings.Replace(output, "<pver>", out.ProtocolVersion, -1)
				output = strings.Replace(output, "<pubkey>", out.PublicKey, -1)
				output = strings.Replace(output, "<addrs>", strings.Join(out.Addresses, "\n"), -1)
				output = strings.Replace(output, "<protocols>", strings.Join(out.Protocols, "\n"), -1)
				output = strings.Replace(output, "\\n", "\n", -1)
				output = strings.Replace(output, "\\t", "\t", -1)
				fmt.Fprint(w, output)
			} else {
				marshaled, err := json.MarshalIndent(out, "", "\t")
				if err != nil {
					return err
				}
				marshaled = append(marshaled, byte('\n'))
				fmt.Fprintln(w, string(marshaled))
			}
			return nil
		}),
	},
	Type: IdOutput{},
}
View Source
var KeyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Create and list BTNS name keypairs",
		ShortDescription: `
'btfs key gen' generates a new keypair for usage with BTNS and 'btfs name
publish'.

  > btfs key gen --type=rsa --size=2048 mykey
  > btfs name publish --key=mykey QmSomeHash

'btfs key list' lists the available keys.

  > btfs key list
  self
  mykey
		`,
	},
	Subcommands: map[string]*cmds.Command{
		"gen":    keyGenCmd,
		"list":   keyListCmd,
		"rename": keyRenameCmd,
		"rm":     keyRmCmd,
	},
}
View Source
var LastInfoCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "get reporting status-contract last info",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		last, err := chain.GetLastOnline()
		if err != nil {
			return err
		}
		if last == nil {
			return errors.New("not found. ")
		}

		r := chain.LastOnlineInfoRet{
			LastTime:      last.LastTime,
			LastSignature: last.LastSignature,
			LastSignedInfo: onlinePb.SignedInfo{
				Peer:        last.LastSignedInfo.Peer,
				CreatedTime: last.LastSignedInfo.CreatedTime,
				Version:     last.LastSignedInfo.Version,
				Nonce:       last.LastSignedInfo.Nonce,
				BttcAddress: last.LastSignedInfo.BttcAddress,
				SignedTime:  last.LastSignedInfo.SignedTime,
			},
		}

		return cmds.EmitOnce(res, &r)
	},
	Type: chain.LastOnlineInfoRet{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *chain.LastOnlineInfoRet) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}
View Source
var LogCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with the daemon log output.",
		ShortDescription: `
'btfs log' contains utility commands to affect or read the logging
output of a running daemon.

There are also two environmental variables that direct the logging 
system (not just for the daemon logs, but all commands):
    IPFS_LOGGING - sets the level of verbosity of the logging.
        One of: debug, info, warn, error, dpanic, panic, fatal
    IPFS_LOGGING_FMT - sets formatting of the log output.
        One of: color, nocolor
`,
	},

	Subcommands: map[string]*cmds.Command{
		"level": logLevelCmd,
		"ls":    logLsCmd,
		"tail":  logTailCmd,
	},
}
View Source
var LsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List directory contents for Unix filesystem objects.",
		ShortDescription: `
Displays the contents of an BTFS or BTNS object(s) at the given path, with
the following format:

  <link base58 hash> <link size in bytes> <link name>

The JSON output contains type information.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("btfs-path", true, true, "The path to the BTFS object(s) to list links from.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.BoolOption(lsHeadersOptionNameTime, "v", "Print table headers (Hash, Size, Name)."),
		cmds.BoolOption(lsResolveTypeOptionName, "Resolve linked objects to find out their types.").WithDefault(true),
		cmds.BoolOption(lsSizeOptionName, "Resolve linked objects to find out their file size.").WithDefault(true),
		cmds.BoolOption(lsStreamOptionName, "s", "Enable experimental streaming of directory entries as they are traversed."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		resolveType, _ := req.Options[lsResolveTypeOptionName].(bool)
		resolveSize, _ := req.Options[lsSizeOptionName].(bool)
		stream, _ := req.Options[lsStreamOptionName].(bool)

		err = req.ParseBodyArgs()
		if err != nil {
			return err
		}
		paths := req.Arguments

		enc, err := cmdenv.GetCidEncoder(req)
		if err != nil {
			return err
		}

		var processLink func(path string, link LsLink) error
		var dirDone func(i int)

		processDir := func() (func(path string, link LsLink) error, func(i int)) {
			return func(path string, link LsLink) error {
				output := []LsObject{{
					Hash:  path,
					Links: []LsLink{link},
				}}
				return res.Emit(&LsOutput{output})
			}, func(i int) {}
		}
		done := func() error { return nil }

		if !stream {
			output := make([]LsObject, len(req.Arguments))

			processDir = func() (func(path string, link LsLink) error, func(i int)) {

				outputLinks := make([]LsLink, 0)
				return func(path string, link LsLink) error {

						outputLinks = append(outputLinks, link)
						return nil
					}, func(i int) {

						sort.Slice(outputLinks, func(i, j int) bool {
							return outputLinks[i].Name < outputLinks[j].Name
						})

						output[i] = LsObject{
							Hash:  paths[i],
							Links: outputLinks,
						}
					}
			}

			done = func() error {
				return cmds.EmitOnce(res, &LsOutput{output})
			}
		}

		for i, fpath := range paths {
			results, err := api.Unixfs().Ls(req.Context, path.New(fpath),
				options.Unixfs.ResolveChildren(resolveSize || resolveType))
			if err != nil {
				return err
			}

			processLink, dirDone = processDir()
			for link := range results {
				if link.Err != nil {
					return link.Err
				}
				var ftype unixfs_pb.Data_DataType
				switch link.Type {
				case iface.TFile:
					ftype = unixfs.TFile
				case iface.TDirectory:
					ftype = unixfs.TDirectory
				case iface.TSymlink:
					ftype = unixfs.TSymlink
				}
				lsLink := LsLink{
					Name: link.Name,
					Hash: enc.Encode(link.Cid),

					Size:   link.Size,
					Type:   ftype,
					Target: link.Target,
				}
				if err := processLink(paths[i], lsLink); err != nil {
					return err
				}
			}
			dirDone(i)
		}
		return done()
	},
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			req := res.Request()
			lastObjectHash := ""

			for {
				v, err := res.Next()
				if err != nil {
					if err == io.EOF {
						return nil
					}
					return err
				}
				out := v.(*LsOutput)
				lastObjectHash = tabularOutput(req, os.Stdout, out, lastObjectHash, false)
			}
		},
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *LsOutput) error {

			ignoreBreaks, _ := req.Options[lsStreamOptionName].(bool)
			tabularOutput(req, w, out, "", ignoreBreaks)
			return nil
		}),
	},
	Type: LsOutput{},
}
View Source
var MbaseCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Encode and decode files or stdin with multibase format",
	},
	Subcommands: map[string]*cmds.Command{
		"encode":    mbaseEncodeCmd,
		"decode":    mbaseDecodeCmd,
		"transcode": mbaseTranscodeCmd,
		"list":      basesCmd,
	},
	Extra: CreateCmdExtras(SetDoesNotUseRepo(true)),
}
View Source
var MetadataCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with metadata for BTFS files.",
		ShortDescription: `
'btfs metadata' is a command to manipulate token metadata for BTFS files
 that are stored through BTT payment.`,
	},

	Subcommands: map[string]*cmds.Command{
		"add": metadataAddCmd,
		"rm":  metadataRemoveCmd,
	},
}

MetadataCmd is the 'btfs metadata' command

View Source
var MountCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Mounts BTFS to the filesystem (read-only).",
		ShortDescription: `
Mount BTFS at a read-only mountpoint on the OS (default: /btfs and /btns).
All BTFS objects will be accessible under that directory. Note that the
root will not be listable, as it is virtual. Access known paths directly.

You may have to create /btfs and /btns before using 'btfs mount':

> sudo mkdir /btfs /btns
> sudo chown $(whoami) /btfs /btns
> btfs daemon &
> btfs mount
`,
		LongDescription: `
Mount BTFS at a read-only mountpoint on the OS. The default, /btfs and /btns,
are set in the configuration file, but can be overridden by the options.
All BTFS objects will be accessible under this directory. Note that the
root will not be listable, as it is virtual. Access known paths directly.

You may have to create /btfs and /btns before using 'btfs mount':

> sudo mkdir /btfs /btns
> sudo chown $(whoami) /btfs /btns
> btfs daemon &
> btfs mount

Example:

# setup
> mkdir foo
> echo "baz" > foo/bar
> btfs add -r foo
added QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR foo/bar
added QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC foo
> btfs ls QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC
QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR 12 bar
> btfs cat QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR
baz

# mount
> btfs daemon &
> btfs mount
BTFS mounted at: /btfs
BTNS mounted at: /btns
> cd /btfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC
> ls
bar
> cat bar
baz
> cat /btfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC/bar
baz
> cat /btfs/QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR
baz
`,
	},
	Options: []cmds.Option{
		cmds.StringOption(mountIPFSPathOptionName, "f", "The path where BTFS should be mounted."),
		cmds.StringOption(mountIPNSPathOptionName, "n", "The path where BTNS should be mounted."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		cfg, err := cmdenv.GetConfig(env)
		if err != nil {
			return err
		}

		nd, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		if !nd.IsOnline {
			return ErrNotOnline
		}

		fsdir, found := req.Options[mountIPFSPathOptionName].(string)
		if !found {
			fsdir = cfg.Mounts.IPFS
		}

		nsdir, found := req.Options[mountIPNSPathOptionName].(string)
		if !found {
			nsdir = cfg.Mounts.IPNS
		}

		err = nodeMount.Mount(nd, fsdir, nsdir)
		if err != nil {
			return err
		}

		var output config.Mounts
		output.IPFS = fsdir
		output.IPNS = nsdir
		return cmds.EmitOnce(res, &output)
	},
	Type: config.Mounts{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, mounts *config.Mounts) error {
			fmt.Fprintf(w, "BTFS mounted at: %s\n", mounts.IPFS)
			fmt.Fprintf(w, "BTNS mounted at: %s\n", mounts.IPNS)

			return nil
		}),
	},
}
View Source
var NetworkCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Get btfs network information",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		timeoutCtx, _ := context.WithTimeout(context.Background(), CheckBackoffDuration*time.Duration(CheckMaxRetries))
		_, err = chain.ChainObject.Backend.BlockNumber(timeoutCtx)
		if err != nil {
			chain.CodeBttc = chain.ConstCodeError
			chain.ErrBttc = err
		} else {
			chain.CodeBttc = chain.ConstCodeSuccess
			chain.ErrBttc = nil
		}

		ret := NetworkRet{
			CodeBttc:   chain.CodeBttc,
			ErrBttc:    switchErrToString(chain.ErrBttc),
			CodeStatus: chain.CodeStatus,
			ErrStatus:  switchErrToString(chain.ErrStatus),
		}
		return cmds.EmitOnce(res, &ret)
	},
	Type: NetworkRet{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *NetworkRet) error {
			_, err := fmt.Fprintf(w, "code bttc:\t%d\nerr bttc:\t%s\ncode status:\t%d\nerr status:\t%s\n",
				out.CodeBttc, out.ErrBttc, out.CodeStatus, out.ErrStatus)
			return err
		}),
	},
}
View Source
var P2PCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Libp2p stream mounting.",
		ShortDescription: `
Create and use tunnels to remote peers over libp2p

Note: this command is experimental and subject to change as usecases and APIs
are refined`,
	},

	Subcommands: map[string]*cmds.Command{
		"stream":    p2pStreamCmd,
		"forward":   p2pForwardCmd,
		"listen":    p2pListenCmd,
		"close":     p2pCloseCmd,
		"ls":        p2pLsCmd,
		"handshake": P2phandshakeCmd,
	},
}

P2PCmd is the 'btfs p2p' command

View Source
var P2phandshakeCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "P2p handshake.",
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("chain-id", true, false, "the specified chain."),
		cmds.StringArg("peer-id", true, false, "the id of peer who send handshake data."),
	},
	RunTimeout: 1 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctxParams, err := uh.ExtractContextParams(req, env)
		if err != nil {
			return err
		}

		requestPid, ok := remote.GetStreamRequestRemotePeerID(req, ctxParams.N)
		if !ok {
			return fmt.Errorf("fail to get peer ID from request")
		}

		chain_id, ok := new(big.Int).SetString(req.Arguments[0], 10)
		if !ok {
			return fmt.Errorf("chainid:%s cannot be parsed", req.Arguments[1])
		}

		peer_id := req.Arguments[1]

		if peer_id != requestPid.String() {
			return fmt.Errorf("the wrong peer ID: want[%s], get[%s]", requestPid.String(), peer_id)
		}

		fmt.Printf("receive handshake from peer peer_id:%v, chain_id:%v \n", peer_id, chain_id)

		output := &pb.Handshake{}
		output.Beneficiary = chain.ChainObject.OverlayAddress.Bytes()

		return cmds.EmitOnce(res, output)
	},
	Type: pb.Handshake{},
}
View Source
var PinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Pin (and unpin) objects to local storage.",
	},

	Subcommands: map[string]*cmds.Command{
		"add":    addPinCmd,
		"rm":     rmPinCmd,
		"ls":     listPinCmd,
		"verify": verifyPinCmd,
		"update": updatePinCmd,
	},
}
View Source
var PingCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Send echo request packets to BTFS hosts.",
		ShortDescription: `
'btfs ping' is a tool to test sending data to other nodes. It finds nodes
via the routing system, sends pings, waits for pongs, and prints out round-
trip latency information.
		`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("peer ID", true, true, "ID of peer to be pinged.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.IntOption(pingCountOptionName, "n", "Number of ping messages to send.").WithDefault(10),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		if !n.IsOnline {
			return ErrNotOnline
		}

		addr, pid, err := ParsePeerParam(req.Arguments[0])
		if err != nil {
			return fmt.Errorf("failed to parse peer address '%s': %s", req.Arguments[0], err)
		}

		if pid == n.Identity {
			return ErrPingSelf
		}

		if addr != nil {
			n.Peerstore.AddAddr(pid, addr, pstore.TempAddrTTL)
		}

		numPings, _ := req.Options[pingCountOptionName].(int)
		if numPings <= 0 {
			return fmt.Errorf("ping count must be greater than 0, was %d", numPings)
		}

		if len(n.Peerstore.Addrs(pid)) == 0 {

			if err := res.Emit(&PingResult{
				Text:    fmt.Sprintf("Looking up peer %s", pid.Pretty()),
				Success: true,
			}); err != nil {
				return err
			}

			ctx, cancel := context.WithTimeout(req.Context, kPingTimeout)
			p, err := n.Routing.FindPeer(ctx, pid)
			cancel()
			if err != nil {
				return fmt.Errorf("peer lookup failed: %s", err)
			}
			n.Peerstore.AddAddrs(p.ID, p.Addrs, pstore.TempAddrTTL)
		}

		if err := res.Emit(&PingResult{
			Text:    fmt.Sprintf("PING %s.", pid.Pretty()),
			Success: true,
		}); err != nil {
			return err
		}

		ctx, cancel := context.WithTimeout(req.Context, kPingTimeout*time.Duration(numPings))
		defer cancel()
		pings := ping.Ping(ctx, n.PeerHost, pid)

		var (
			count int
			total time.Duration
		)
		ticker := time.NewTicker(time.Second)
		defer ticker.Stop()

		for i := 0; i < numPings; i++ {
			r, ok := <-pings
			if !ok {
				break
			}

			if r.Error != nil {
				err = res.Emit(&PingResult{
					Success: false,
					Text:    fmt.Sprintf("Ping error: %s", r.Error),
				})
			} else {
				count++
				total += r.RTT
				err = res.Emit(&PingResult{
					Success: true,
					Time:    r.RTT,
				})
			}
			if err != nil {
				return err
			}

			select {
			case <-ticker.C:
			case <-ctx.Done():
				return ctx.Err()
			}
		}
		if count == 0 {
			return fmt.Errorf("ping failed")
		}
		averagems := total.Seconds() * 1000 / float64(count)
		return res.Emit(&PingResult{
			Success: true,
			Text:    fmt.Sprintf("Average latency: %.2fms", averagems),
		})
	},
	Type: PingResult{},
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			var (
				total time.Duration
				count int
			)

			for {
				event, err := res.Next()
				switch err {
				case nil:
				case io.EOF:
					return nil
				case context.Canceled, context.DeadlineExceeded:
					if count == 0 {
						return err
					}
					averagems := total.Seconds() * 1000 / float64(count)
					return re.Emit(&PingResult{
						Success: true,
						Text:    fmt.Sprintf("Average latency: %.2fms", averagems),
					})
				default:
					return err
				}

				pr := event.(*PingResult)
				if pr.Success && pr.Text == "" {
					total += pr.Time
					count++
				}
				err = re.Emit(event)
				if err != nil {
					return err
				}
			}
		},
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *PingResult) error {
			if len(out.Text) > 0 {
				fmt.Fprintln(w, out.Text)
			} else if out.Success {
				fmt.Fprintf(w, "Pong received: time=%.2f ms\n", out.Time.Seconds()*1000)
			} else {
				fmt.Fprintf(w, "Pong failed\n")
			}
			return nil
		}),
	},
}
View Source
var PubsubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "An experimental publish-subscribe system on btfs.",
		ShortDescription: `
btfs pubsub allows you to publish messages to a given topic, and also to
subscribe to new messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"pub":   PubsubPubCmd,
		"sub":   PubsubSubCmd,
		"ls":    PubsubLsCmd,
		"peers": PubsubPeersCmd,
	},
}
View Source
var PubsubLsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List subscribed topics by name.",
		ShortDescription: `
btfs pubsub ls lists out the names of topics you are currently subscribed to.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		l, err := api.PubSub().Ls(req.Context)
		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, stringList{l})
	},
	Type: stringList{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(stringListEncoder),
	},
}
View Source
var PubsubPeersCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List peers we are currently pubsubbing with.",
		ShortDescription: `
btfs pubsub peers with no arguments lists out the pubsub peers you are
currently connected to. If given a topic, it will list connected
peers who are subscribed to the named topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", false, false, "topic to list connected peers of"),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		var topic string
		if len(req.Arguments) == 1 {
			topic = req.Arguments[0]
		}

		peers, err := api.PubSub().Peers(req.Context, options.PubSub.Topic(topic))
		if err != nil {
			return err
		}

		list := &stringList{make([]string, 0, len(peers))}

		for _, peer := range peers {
			list.Strings = append(list.Strings, peer.Pretty())
		}
		sort.Strings(list.Strings)
		return cmds.EmitOnce(res, list)
	},
	Type: stringList{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(stringListEncoder),
	},
}
View Source
var PubsubPubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Publish a message to a given pubsub topic.",
		ShortDescription: `
btfs pubsub pub publishes a message to a specified topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", true, false, "Topic to publish to."),
		cmds.StringArg("data", true, true, "Payload of message to publish.").EnableStdin(),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		topic := req.Arguments[0]

		err = req.ParseBodyArgs()
		if err != nil {
			return err
		}

		for _, data := range req.Arguments[1:] {
			if err := api.PubSub().Publish(req.Context, topic, []byte(data)); err != nil {
				return err
			}
		}

		return nil
	},
}
View Source
var PubsubSubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Subscribe to messages on a given topic.",
		ShortDescription: `
btfs pubsub sub subscribes to messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
		LongDescription: `
btfs pubsub sub subscribes to messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.

This command outputs data in the following encodings:
  * "json"
(Specified by the "--encoding" or "--enc" flag)
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", true, false, "String name of topic to subscribe to."),
	},
	Options: []cmds.Option{
		cmds.BoolOption(pubsubDiscoverOptionName, "Deprecated option to instruct pubsub to discovery peers for the topic. Discovery is now built into pubsub."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		topic := req.Arguments[0]
		sub, err := api.PubSub().Subscribe(req.Context, topic)
		if err != nil {
			return err
		}
		defer sub.Close()

		if f, ok := res.(http.Flusher); ok {
			f.Flush()
		}

		for {
			msg, err := sub.Next(req.Context)
			if err == io.EOF || err == context.Canceled {
				return nil
			} else if err != nil {
				return err
			}

			if err := res.Emit(&pubsubMessage{
				Data:     msg.Data(),
				From:     []byte(msg.From()),
				Seqno:    msg.Seq(),
				TopicIDs: msg.Topics(),
			}); err != nil {
				return err
			}
		}
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, psm *pubsubMessage) error {
			_, err := w.Write(psm.Data)
			return err
		}),
		"ndpayload": cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, psm *pubsubMessage) error {
			psm.Data = append(psm.Data, '\n')
			_, err := w.Write(psm.Data)
			return err
		}),
		"lenpayload": cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, psm *pubsubMessage) error {
			buf := make([]byte, 8, len(psm.Data)+8)

			n := binary.PutUvarint(buf, uint64(len(psm.Data)))
			buf = append(buf[:n], psm.Data...)
			_, err := w.Write(buf)
			return err
		}),
	},
	Type: pubsubMessage{},
}
View Source
var RecoveryCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:         "Recover BTFS's data from a archived file of backup",
		LongDescription: `This command will recover data from a previously created backup file`,
	},
	Options: []cmds.Option{
		cmds.StringOption(backupPathOption, "backup output file path"),
	},
	Run: func(req *cmds.Request, resp cmds.ResponseEmitter, env cmds.Environment) error {
		backupPath, ok := req.Options[backupPathOption].(string)
		if !ok {
			return errors.New("you need to specify -r to indicate the path you want to recover")
		}
		btfsPath := env.(*commands.Context).ConfigRoot
		dstPath := filepath.Dir(btfsPath)
		if fsrepo.IsInitialized(btfsPath) {
			newPath := filepath.Join(dstPath, fmt.Sprintf(".btfs_backup_%d", time.Now().Unix()))

			err := os.Rename(btfsPath, newPath)
			if err != nil {
				return err
			}
			fmt.Println("btfs configuration file already exists!")
			fmt.Println("We have renamed it to ", newPath)
		}

		if err := UnTar(backupPath, dstPath); err != nil {
			err = UnZip(backupPath, dstPath)
			if err != nil {
				return errors.New("your file is not exists or your file format is not tar.gz or zip, please check again")
			}
		}
		fmt.Println("Recovery successful!")
		return nil
	},
}
View Source
var RefsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List links (references) from an object.",
		ShortDescription: `
Lists the hashes of all the links an BTFS or BTNS object(s) contains,
with the following format:

  <link base58 hash>

NOTE: List all references recursively by using the flag '-r'.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"local": RefsLocalCmd,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("btfs-path", true, true, "Path to the object(s) to list refs from.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.StringOption(refsFormatOptionName, "Emit edges with given format. Available tokens: <src> <dst> <linkname>.").WithDefault("<dst>"),
		cmds.BoolOption(refsEdgesOptionName, "e", "Emit edge format: `<from> -> <to>`."),
		cmds.BoolOption(refsUniqueOptionName, "u", "Omit duplicate refs from output."),
		cmds.BoolOption(refsRecursiveOptionName, "r", "Recursively list links of child nodes."),
		cmds.IntOption(refsMaxDepthOptionName, "Only for recursive refs, limits fetch and listing to the given depth").WithDefault(-1),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := req.ParseBodyArgs()
		if err != nil {
			return err
		}

		ctx := req.Context
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		enc, err := cmdenv.GetCidEncoder(req)
		if err != nil {
			return err
		}

		unique, _ := req.Options[refsUniqueOptionName].(bool)
		recursive, _ := req.Options[refsRecursiveOptionName].(bool)
		maxDepth, _ := req.Options[refsMaxDepthOptionName].(int)
		edges, _ := req.Options[refsEdgesOptionName].(bool)
		format, _ := req.Options[refsFormatOptionName].(string)

		if !recursive {
			maxDepth = 1
		}

		if edges {
			if format != "<dst>" {
				return errors.New("using format argument with edges is not allowed")
			}

			format = "<src> -> <dst>"
		}

		objs, err := objectsForPaths(ctx, api, req.Arguments)
		if err != nil {
			return err
		}

		rw := RefWriter{
			res:      res,
			DAG:      merkledag.NewSession(ctx, api.Dag()),
			Ctx:      ctx,
			Unique:   unique,
			PrintFmt: format,
			MaxDepth: maxDepth,
		}

		for _, o := range objs {
			if _, err := rw.WriteRefs(o, enc); err != nil {
				if err := res.Emit(&RefWrapper{Err: err.Error()}); err != nil {
					return err
				}
			}
		}

		return nil
	},
	Encoders: refsEncoderMap,
	Type:     RefWrapper{},
}

RefsCmd is the `btfs refs` command

View Source
var RefsLocalCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List all local references.",
		ShortDescription: `
Displays the hashes of all local objects.
`,
	},

	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		ctx := req.Context
		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		allKeys, err := n.Blockstore.AllKeysChan(ctx)
		if err != nil {
			return err
		}

		for k := range allKeys {
			err := res.Emit(&RefWrapper{Ref: k.String()})
			if err != nil {
				return err
			}
		}

		return nil
	},
	Encoders: refsEncoderMap,
	Type:     RefWrapper{},
}
View Source
var RefsROCmd = &cmds.Command{}

RefsROCmd is `btfs refs` command

View Source
var RepoCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Manipulate the BTFS repo.",
		ShortDescription: `
'btfs repo' is a plumbing command used to manipulate the repo.
`,
	},

	Subcommands: map[string]*cmds.Command{
		"stat":    repoStatCmd,
		"gc":      repoGcCmd,
		"fsck":    repoFsckCmd,
		"version": repoVersionCmd,
		"verify":  repoVerifyCmd,
	},
}
View Source
var ReportLastTimeDailyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "report status-contract total info, (total count, total gas spend, and contract address)",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		last, err := chain.GetReportOnlineLastTimeDaily()
		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, &RetReportLastTime{
			EveryDaySeconds: last.EveryDaySeconds,
			LastTime:        last.LastReportTime,
		})
	},
	Type: RetReportLastTime{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *RetReportLastTime) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}

ReportLastTimeDailyCmd (last time daily)

View Source
var ReportListCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "(old)report status-contract list, and input from and limit to get its.",
	},
	RunTimeout: 5 * time.Minute,
	Arguments: []cmds.Argument{
		cmds.StringArg("from", true, false, "page offset"),
		cmds.StringArg("limit", true, false, "page limit."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}
		peerId := n.Identity.Pretty()

		from, err := strconv.Atoi(req.Arguments[0])
		if err != nil {
			return fmt.Errorf("parse from:%v failed", req.Arguments[0])
		}
		limit, err := strconv.Atoi(req.Arguments[1])
		if err != nil {
			return fmt.Errorf("parse limit:%v failed", req.Arguments[1])
		}
		if from < 0 {
			return fmt.Errorf("invalid from: %d", from)
		}
		if limit < 0 {
			return fmt.Errorf("invalid limit: %d", limit)
		}

		list, err := chain.GetReportStatusListOK()
		if err != nil {
			return err
		}
		if list == nil {
			return nil
		}
		total := len(list)

		for i, j := 0, total-1; i < j; i, j = i+1, j-1 {
			list[i], list[j] = list[j], list[i]
		}
		if from < total {
			if (from + limit) <= len(list) {
				list = list[from : from+limit]
			} else {
				list = list[from:]
			}
		}

		return cmds.EmitOnce(res, &ReportListCmdRet{
			Records: list,
			Total:   total,
			PeerId:  peerId,
		})
	},
	Type: ReportListCmdRet{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ReportListCmdRet) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}
View Source
var ReportListDailyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "report list daily, and input from and limit to get its.",
	},
	RunTimeout: 5 * time.Minute,
	Arguments: []cmds.Argument{
		cmds.StringArg("from", true, false, "page offset"),
		cmds.StringArg("limit", true, false, "page limit."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}
		peerId := n.Identity.Pretty()

		cfg, err := cmdenv.GetConfig(env)
		if err != nil {
			return err
		}
		bttcAddr := cfg.Identity.BttcAddr

		from, err := strconv.Atoi(req.Arguments[0])
		if err != nil {
			return fmt.Errorf("parse from:%v failed", req.Arguments[0])
		}
		limit, err := strconv.Atoi(req.Arguments[1])
		if err != nil {
			return fmt.Errorf("parse limit:%v failed", req.Arguments[1])
		}
		if from < 0 {
			return fmt.Errorf("invalid from: %d", from)
		}
		if limit < 0 {
			return fmt.Errorf("invalid limit: %d", limit)
		}

		list, err := chain.GetReportOnlineListDailyOK()
		if err != nil {
			return err
		}
		if list == nil {
			return nil
		}
		total := len(list)

		for i, j := 0, total-1; i < j; i, j = i+1, j-1 {
			list[i], list[j] = list[j], list[i]
		}
		if from < total {
			if (from + limit) <= len(list) {
				list = list[from : from+limit]
			} else {
				list = list[from:]
			}
		}

		rs := make([]*chain.LastOnlineInfoRet, 0)
		for _, v := range list {
			r := chain.LastOnlineInfoRet{
				LastTime:      v.LastTime,
				LastSignature: v.LastSignature,
				LastSignedInfo: onlinePb.SignedInfo{
					Peer:        v.LastSignedInfo.Peer,
					CreatedTime: v.LastSignedInfo.CreatedTime,
					Version:     v.LastSignedInfo.Version,
					Nonce:       v.LastSignedInfo.Nonce,
					BttcAddress: v.LastSignedInfo.BttcAddress,
					SignedTime:  v.LastSignedInfo.SignedTime,
				},
			}
			rs = append(rs, &r)
		}

		return cmds.EmitOnce(res, &RetReportOnlineListDaily{
			Records:  rs,
			Total:    total,
			PeerId:   peerId,
			BttcAddr: bttcAddr,
		})
	},
	Type: RetReportOnlineListDaily{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *RetReportOnlineListDaily) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}

ReportListDailyCmd (report list daily)

View Source
var ReportOnlineDailyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "daily report online server. ",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		node, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		cfg, err := cmdenv.GetConfig(env)
		if err != nil {
			return err
		}

		msg, err := spin.DC.SendOnlineDaily(node, cfg)
		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, "daily report online server ok! "+msg)
	},
}

ReportOnlineDailyCmd (report online daily)

View Source
var ReportOnlineServerCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "report online server. ",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		node, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		cfg, err := cmdenv.GetConfig(env)
		if err != nil {
			return err
		}

		spin.DC.SendDataOnline(node, cfg)

		return cmds.EmitOnce(res, "report online server ok!")
	},
}
View Source
var ReportStatusContractCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "(old,drop it)report status-contract. ",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := reportstatus.CmdReportStatus()
		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, "report status contract ok!")
	},
}
View Source
var ResolveCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Resolve the value of names to BTFS.",
		ShortDescription: `
There are a number of mutable name protocols that can link among
themselves and into BTNS. This command accepts any of these
identifiers and resolves them to the referenced item.
`,
		LongDescription: `
There are a number of mutable name protocols that can link among
themselves and into BTNS. For example BTNS references can (currently)
point at an BTFS object, and DNS links can point at other DNS links, BTNS
entries, or BTFS objects. This command accepts any of these
identifiers and resolves them to the referenced item.

EXAMPLES

Resolve the value of your identity:

  $ btfs resolve /btns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
  /btfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj

Resolve the value of another name:

  $ btfs resolve /btns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
  /btns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Resolve the value of another name recursively:

  $ btfs resolve -r /btns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
  /btfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj

Resolve the value of an BTFS DAG path:

  $ btfs resolve /btfs/QmeZy1fGbwgVSrqbfh9fKQrAWgeyRnj7h8fsHS1oy3k99x/beep/boop
  /btfs/QmYRMjyvAiHKN9UTi8Bzt1HUspmSRD8T8DwxfSMzLgBon1

`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("name", true, false, "The name to resolve.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.BoolOption(resolveRecursiveOptionName, "r", "Resolve until the result is an BTFS name.").WithDefault(true),
		cmds.IntOption(resolveDhtRecordCountOptionName, "dhtrc", "Number of records to request for DHT resolution."),
		cmds.StringOption(resolveDhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution eg \"30s\". Pass 0 for no timeout."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		api, err := cmdenv.GetApi(env, req)
		if err != nil {
			return err
		}

		name := req.Arguments[0]
		recursive, _ := req.Options[resolveRecursiveOptionName].(bool)

		if strings.HasPrefix(name, "/btns/") && !recursive {
			rc, rcok := req.Options[resolveDhtRecordCountOptionName].(uint)
			dhtt, dhttok := req.Options[resolveDhtTimeoutOptionName].(string)
			ropts := []options.NameResolveOption{
				options.Name.ResolveOption(nsopts.Depth(1)),
			}

			if rcok {
				ropts = append(ropts, options.Name.ResolveOption(nsopts.DhtRecordCount(rc)))
			}
			if dhttok {
				d, err := time.ParseDuration(dhtt)
				if err != nil {
					return err
				}
				if d < 0 {
					return errors.New("DHT timeout value must be >= 0")
				}
				ropts = append(ropts, options.Name.ResolveOption(nsopts.DhtTimeout(d)))
			}
			p, err := api.Name().Resolve(req.Context, name, ropts...)

			if err != nil && err != ns.ErrResolveRecursion {
				return err
			}
			return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ipfspath.Path(p.String())})
		}

		var enc cidenc.Encoder
		switch {
		case !cmdenv.CidBaseDefined(req) && !strings.HasPrefix(name, "/ipns/"):

			enc, err = cmdenv.CidEncoderFromPath(name)
			if err == nil {
				break
			}

			fallthrough
		default:
			enc, err = cmdenv.GetCidEncoder(req)
			if err != nil {
				return err
			}
		}

		rp, err := api.ResolvePath(req.Context, path.New(name))
		if err != nil {
			return err
		}

		encoded := "/" + rp.Namespace() + "/" + enc.Encode(rp.Cid())
		if remainder := rp.Remainder(); remainder != "" {
			encoded += "/" + remainder
		}

		return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ipfspath.Path(encoded)})
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, rp *ncmd.ResolvedPath) error {
			fmt.Fprintln(w, rp.Path.String())
			return nil
		}),
	},
	Type: ncmd.ResolvedPath{},
}
View Source
var RmCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Remove files or directories from a local btfs node.",
		ShortDescription: `Removes all blocks under <hash> recursively from a local btfs node.`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("hash", true, true, "The hash(es) of the file(s)/directory(s) to be removed from the local btfs node.").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.BoolOption(rmForceOptionName, "f", "Forcibly remove the object, even if there are constraints (such as host-stored file).").WithDefault(false),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		force, _ := req.Options[rmForceOptionName].(bool)

		results, err := rm.RmDag(req.Context, req.Arguments, n, req, env, force)
		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, &stringList{Strings: results})
	},
	Type: stringList{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(stringListEncoder),
	},
}
View Source
var Root = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:  "Global p2p merkle-dag filesystem.",
		Synopsis: "btfs [--config=<config> | -c] [--debug | -D] [--help] [-h] [--api=<api>] [--offline] [--cid-base=<base>] [--upgrade-cidv0-in-output] [--encoding=<encoding> | --enc] [--timeout=<timeout>] <command> ...",
		Subcommands: `
BASIC COMMANDS
  init          Initialize btfs local configuration
  add <path>    Add a file to BTFS
  cat <ref>     Show BTFS object data
  get <ref>     Download BTFS objects
  ls <ref>      List links from an object
  refs <ref>    List hashes of links from an object

BTFS COMMANDS
  storage       Manage client and host storage features
  rm            Clean up locally stored files and objects

DATA STRUCTURE COMMANDS
  block         Interact with raw blocks in the datastore
  object        Interact with raw dag nodes
  files         Interact with objects as if they were a unix filesystem
  dag           Interact with IPLD documents (experimental)
  metadata      Interact with metadata for BTFS files

ASSETS COMMANDS
  bttc          BTTC service related commands, e.g. swap between BTT and WBTT
  vault         Vault service reladted commands, e.g. deposit WBTT to your vault
  cheque        Cheque service related commands, e.g. list cheques you have received
  settlement    Show cheque settlement info

ADVANCED COMMANDS
  daemon        Start a long-running daemon process
  mount         Mount an BTFS read-only mount point
  resolve       Resolve any type of name
  name          Publish and resolve BTNS names
  key           Create and list BTNS name keypairs
  dns           Resolve DNS links
  pin           Pin objects to local storage
  repo          Manipulate the BTFS repository
  stats         Various operational stats
  p2p           Libp2p stream mounting
  filestore     Manage the filestore (experimental)

NETWORK COMMANDS
  id            Show info about BTFS peers
  bootstrap     Add or remove bootstrap peers
  swarm         Manage connections to the p2p network
  dht           Query the DHT for values or peers
  ping          Measure the latency of a connection
  diag          Print diagnostics

TOOL COMMANDS
  config        Manage configuration
  version       Show btfs version information
  commands      List all available commands
  cid           Convert and discover properties of CIDs
  log           Manage and show logs of running daemon

Use 'btfs <command> --help' to learn more about each command.

btfs uses a repository in the local file system. By default, the repo is
located at ~/.btfs. To change the repo location, set the $BTFS_PATH
environment variable:

  export BTFS_PATH=/path/to/btfsrepo

EXIT STATUS

The CLI will exit with one of the following values:

0     Successful execution.
1     Failed executions.
`,
	},
	Options: []cmds.Option{
		cmds.StringOption(ConfigOption, "c", "Path to the configuration file to use."),
		cmds.BoolOption(DebugOption, "D", "Operate in debug mode."),
		cmds.BoolOption(cmds.OptLongHelp, "Show the full command help text."),
		cmds.BoolOption(cmds.OptShortHelp, "Show a short version of the command help text."),
		cmds.BoolOption(LocalOption, "L", "Run the command locally, instead of using the daemon. DEPRECATED: use --offline."),
		cmds.BoolOption(OfflineOption, "Run the command offline."),
		cmds.StringOption(ApiOption, "Use a specific API instance (defaults to /ip4/127.0.0.1/tcp/5001)"),

		cmdenv.OptionCidBase,
		cmdenv.OptionUpgradeCidV0InOutput,

		cmds.OptionEncodingType,
		cmds.OptionStreamChannels,
		cmds.OptionTimeout,
	},
}
View Source
var RootRO = &cmds.Command{}

RootRO is the readonly version of Root

View Source
var RootRemote = &cmds.Command{}

RootRemote is the remote-facing version of Root

View Source
var StatsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Query BTFS statistics.",
		ShortDescription: `'btfs stats' is a set of commands to help look at statistics
for your BTFS node.
`,
		LongDescription: `'btfs stats' is a set of commands to help look at statistics
for your BTFS node.`,
	},

	Subcommands: map[string]*cmds.Command{
		"bw":      statBwCmd,
		"repo":    repoStatCmd,
		"bitswap": bitswapStatCmd,
		"dht":     statDhtCmd,
	},
}
View Source
var StatusConfigCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "(old)get reporting status-contract config. ",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		rs, err := chain.GetReportStatus()
		if err != nil {
			return err
		}

		return cmds.EmitOnce(res, rs)
	},
	Type: chain.ReportStatusInfo{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *chain.ReportStatusInfo) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}
View Source
var StatusContractCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "report status-contract cmd.",
		ShortDescription: `
report status-contract cmd, total cmd and list cmd.`,
	},
	Subcommands: map[string]*cmds.Command{
		"total":      TotalCmd,
		"reportlist": ReportListCmd,
		"config":     StatusConfigCmd,

		"lastinfo":             LastInfoCmd,
		"report_online_server": ReportOnlineServerCmd,

		"daily_report_online_server": ReportOnlineDailyCmd,
		"daily_report_list":          ReportListDailyCmd,
		"daily_total":                TotalDailyCmd,
		"daily_last_report_time":     ReportLastTimeDailyCmd,
	},
}
View Source
var SwarmCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Interact with the swarm.",
		ShortDescription: `
'btfs swarm' is a tool to manipulate the network swarm. The swarm is the
component that opens, listens for, and maintains connections to other
btfs peers in the internet.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"addrs":      swarmAddrsCmd,
		"connect":    swarmConnectCmd,
		"disconnect": swarmDisconnectCmd,
		"filters":    swarmFiltersCmd,
		"peers":      swarmPeersCmd,
	},
}
View Source
var SyncChainInfoCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "sync chain info.",
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		node, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}

		if !node.IsDaemon {
			return errors.New("please start the node first, for synchronization")
		}

		cfgRoot, err := cmdenv.GetConfigRoot(env)
		if err != nil {
			return err
		}
		chainInfo := chain.ChainObject
		err = SyncConfigChainInfo(cfgRoot, &chainInfo)
		if err != nil {
			return err
		}

		err = chain.StoreChainIdIfNotExists(chainInfo.ChainID, chain.StateStore)
		if err != nil {
			return err
		}

		out := fmt.Sprintf("sync chain info ok. \n")
		return cmds.EmitOnce(res, &out)
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *string) error {
			_, err := w.Write([]byte(*out))
			return err
		}),
	},
}
View Source
var SyncSimpleModeCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "simple mode is true or not.",
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("value", true, false, "simple mode is true or not."),
	},
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		enable, err := strconv.ParseBool(req.Arguments[0])
		if err != nil {
			return err
		}

		cfgRoot, err := cmdenv.GetConfigRoot(env)
		if err != nil {
			return err
		}

		err = SetSimpleMode(cfgRoot, enable)
		if err != nil {
			return err
		}

		out := fmt.Sprintf("set simple mode = %v \n    please restart the node to use it!\n", enable)
		return cmds.EmitOnce(res, &out)
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *string) error {
			_, err := w.Write([]byte(*out))
			return err
		}),
	},
}
View Source
var TarCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Utility functions for tar files in btfs.",
	},

	Subcommands: map[string]*cmds.Command{
		"add": tarAddCmd,
		"cat": tarCatCmd,
	},
}
View Source
var TestCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "test it.",
		ShortDescription: `test it. get hosts or send cheque.`,
	},

	Run:      testCmd.Run,
	Encoders: testCmd.Encoders,
	Type:     testCmd.Type,

	Subcommands: map[string]*cmds.Command{
		"hosts":        testHostsCmd,
		"cheque":       testChequeCmd,
		"p2phandshake": testP2pShakeCmd,
	},
}
View Source
var TotalCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "(old)report status-contract total info, (total count, total gas spend, and contract address)",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}
		peerId := n.Identity.Pretty()

		list, err := chain.GetReportStatusListOK()
		if err != nil {
			return err
		}
		if list == nil {
			return nil
		}

		totalGasSpend := new(big.Int)
		for _, r := range list {
			n := new(big.Int)
			if len(r.GasSpend) <= 0 {

				continue
			}

			n, ok := n.SetString(r.GasSpend, 10)
			if !ok {
				return errors.New("parse gas_spend is error. ")
			}
			totalGasSpend = totalGasSpend.Add(totalGasSpend, n)
		}

		return cmds.EmitOnce(res, &TotalCmdRet{
			PeerId:         peerId,
			StatusContract: list[len(list)-1].StatusContract,
			TotalCount:     len(list),
			TotalGasSpend:  totalGasSpend.String(),
		})
	},
	Type: TotalCmdRet{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *TotalCmdRet) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}
View Source
var TotalDailyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "report status-contract total info, (total count, total gas spend, and contract address)",
	},
	RunTimeout: 5 * time.Minute,
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		err := utils.CheckSimpleMode(env)
		if err != nil {
			return err
		}

		n, err := cmdenv.GetNode(env)
		if err != nil {
			return err
		}
		peerId := n.Identity.Pretty()

		list, err := chain.GetReportOnlineListDailyOK()
		if err != nil {
			return err
		}
		if list == nil {
			return nil
		}

		return cmds.EmitOnce(res, &RetTotalDaily{
			PeerId:         peerId,
			StatusContract: chain.ChainObject.Chainconfig.StatusAddress.String(),
			TotalCount:     len(list),
		})
	},
	Type: RetTotalDaily{},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *RetTotalDaily) error {
			marshaled, err := json.MarshalIndent(out, "", "\t")
			if err != nil {
				return err
			}
			marshaled = append(marshaled, byte('\n'))
			fmt.Fprintln(w, string(marshaled))
			return nil
		}),
	},
}

TotalDailyCmd (total daily)

View Source
var VersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline:          "Show btfs version information.",
		ShortDescription: "Returns the current version of btfs and exits.",
	},
	Subcommands: map[string]*cmds.Command{
		"deps": depsVersionCommand,
	},

	Options: []cmds.Option{
		cmds.BoolOption(versionNumberOptionName, "n", "Only show the version number."),
		cmds.BoolOption(versionCommitOptionName, "Show the commit hash."),
		cmds.BoolOption(versionRepoOptionName, "Show repo version."),
		cmds.BoolOption(versionAllOptionName, "Show all version information"),
	},

	Extra: CreateCmdExtras(SetDoesNotUseRepo(true), SetDoesNotUseConfigAsInput(true)),
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		return cmds.EmitOnce(res, &VersionOutput{
			Version: version.CurrentVersionNumber,
			Commit:  version.CurrentCommit,
			Repo:    fmt.Sprint(fsrepo.RepoVersion),
			System:  runtime.GOARCH + "/" + runtime.GOOS,
			Golang:  runtime.Version(),
		})
	},
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, version *VersionOutput) error {
			all, _ := req.Options[versionAllOptionName].(bool)
			if all {
				ver := version.Version
				if version.Commit != "" {
					ver += "-" + version.Commit
				}
				out := fmt.Sprintf("go-btfs version: %s\n"+
					"Repo version: %s\nSystem version: %s\nGolang version: %s\n",
					ver, version.Repo, version.System, version.Golang)
				fmt.Fprint(w, out)
				return nil
			}

			commit, _ := req.Options[versionCommitOptionName].(bool)
			commitTxt := ""
			if commit && version.Commit != "" {
				commitTxt = "-" + version.Commit
			}

			repo, _ := req.Options[versionRepoOptionName].(bool)
			if repo {
				fmt.Fprintln(w, version.Repo)
				return nil
			}

			number, _ := req.Options[versionNumberOptionName].(bool)
			if number {
				fmt.Fprintln(w, version.Version+commitTxt)
				return nil
			}

			fmt.Fprint(w, fmt.Sprintf("btfs version %s%s\n", version.Version, commitTxt))
			return nil
		}),
	},
	Type: VersionOutput{},
}
View Source
var VersionROCmd = &cmds.Command{}

VersionROCmd is `btfs version` command (without deps).

Functions

func CommandsCmd

func CommandsCmd(root *cmds.Command) *cmds.Command

CommandsCmd takes in a root command, and returns a command that lists the subcommands in that root

func CreateCmdExtras

func CreateCmdExtras(opts ...func(e *cmds.Extra)) *cmds.Extra

func ECCDecrypt

func ECCDecrypt(ct []byte, prk ecies.PrivateKey) ([]byte, error)

func ECCEncrypt

func ECCEncrypt(pt []byte, puk ecies.PublicKey) ([]byte, error)

func ExternalBinary

func ExternalBinary(instructions string) *cmds.Command

func GetDoesNotUseConfigAsInput

func GetDoesNotUseConfigAsInput(e *cmds.Extra) (val bool, found bool)

func GetDoesNotUseRepo

func GetDoesNotUseRepo(e *cmds.Extra) (val bool, found bool)

func GetPreemptsAutoUpdate

func GetPreemptsAutoUpdate(e *cmds.Extra) (val bool, found bool)

func NotifyAndWaitIfOnRestarting

func NotifyAndWaitIfOnRestarting()

NotifyAndWaitIfOnRestaring will called by the daemon goroutine

func ParsePeerParam

func ParsePeerParam(text string) (ma.Multiaddr, peer.ID, error)

func SetConfigStorageHostEnable

func SetConfigStorageHostEnable(configRoot string, enable bool) error

func SetDoesNotUseConfigAsInput

func SetDoesNotUseConfigAsInput(val bool) func(e *cmds.Extra)

func SetDoesNotUseRepo

func SetDoesNotUseRepo(val bool) func(e *cmds.Extra)

func SetPreemptsAutoUpdate

func SetPreemptsAutoUpdate(val bool) func(e *cmds.Extra)

func SetSimpleMode

func SetSimpleMode(configRoot string, enable bool) error

func SyncConfigChainInfo

func SyncConfigChainInfo(configRoot string, chainInfo *chain.ChainInfo) error

func SyncConfigChainInfoV2

func SyncConfigChainInfoV2(configRoot string, chainid int64, endpoint string, currentFactoryAddr, priceOracleAddr common.Address) error

func SyncConfigOnlineCfg

func SyncConfigOnlineCfg(configRoot string, onlineServerDomain string, reportOnline, reportStatusContract bool) error

func SyncConfigOnlineCfgV2

func SyncConfigOnlineCfgV2(configRoot string, onlineServerDomain string, reportOnline bool) error

func SyncHubDomainConfig

func SyncHubDomainConfig(configRoot string, hubServerDomain string) error

func Tar

func Tar(src, dst string, excludePath []string) (err error)

func UnTar

func UnTar(src, dst string) (err error)

func UnZip

func UnZip(src, dst string) (err error)

func Zip

func Zip(src, dst string, excludePath []string) (err error)

Types

type AddEvent

type AddEvent struct {
	Name  string
	Hash  string `json:",omitempty"`
	Bytes int64  `json:",omitempty"`
	Size  string `json:",omitempty"`
}

type AddPinOutput

type AddPinOutput struct {
	Pins     []string
	Progress int `json:",omitempty"`
}

type BadNode

type BadNode struct {
	Cid string
	Err string
}

BadNode is used in PinVerifyRes

type BlockStat

type BlockStat struct {
	Key  string
	Size int
}

func (BlockStat) String

func (bs BlockStat) String() string

type BootstrapOutput

type BootstrapOutput struct {
	Peers []string
}

type CidFormatRes

type CidFormatRes struct {
	CidStr    string // Original Cid String passed in
	Formatted string // Formatted Result
	ErrorMsg  string // Error
}

type CodeAndName

type CodeAndName struct {
	Code int
	Name string
}

type Command

type Command struct {
	Name        string
	Subcommands []Command
	Options     []Option
	// contains filtered or unexported fields
}

type ConfigField

type ConfigField struct {
	Key   string
	Value interface{}
}

type ConfigUpdateOutput

type ConfigUpdateOutput struct {
	OldCfg map[string]interface{}
	NewCfg map[string]interface{}
}

ConfigUpdateOutput is config profile apply command's output

type Dependency

type Dependency struct {
	Path       string
	Version    string
	ReplacedBy string
	Sum        string
}

type GcResult

type GcResult struct {
	Key   cid.Cid
	Error string `json:",omitempty"`
}

GcResult is the result returned by "repo gc" command.

type IdOutput

type IdOutput struct {
	ID              string
	PublicKey       string
	Addresses       []string
	AgentVersion    string
	ProtocolVersion string
	Protocols       []string
	DaemonProcessID int
	TronAddress     string
	BttcAddress     string
	VaultAddress    string
	ChainID         int64
	SimpleMode      bool
}

type KeyList

type KeyList struct {
	Keys []cid.Cid
}

KeyList is a general type for outputting lists of keys

type KeyOutput

type KeyOutput struct {
	Name string
	Id   string
}

type KeyOutputList

type KeyOutputList struct {
	Keys []KeyOutput
}

type KeyRenameOutput

type KeyRenameOutput struct {
	Was       string
	Now       string
	Id        string
	Overwrite bool
}

KeyRenameOutput define the output type of keyRenameCmd

type LsLink struct {
	Name, Hash string
	Size       uint64
	Type       unixfs_pb.Data_DataType
	Target     string
}

LsLink contains printable data for a single ipld link in ls output

type LsObject

type LsObject struct {
	Hash  string
	Links []LsLink
}

LsObject is an element of LsOutput It can represent all or part of a directory

type LsOutput

type LsOutput struct {
	Objects []LsObject
}

LsOutput is a set of printable data for directories, it can be complete or partial

type MessageOutput

type MessageOutput struct {
	Message string
}

type MetaResult

type MetaResult struct {
	Hash string
}

type NetworkRet

type NetworkRet struct {
	CodeBttc   int    `json:"code_bttc"`
	ErrBttc    string `json:"err_bttc"`
	CodeStatus int    `json:"code_status"`
	ErrStatus  string `json:"err_status"`
}

type Option

type Option struct {
	Names []string
}

type P2PHandshakeOutput

type P2PHandshakeOutput struct {
	Beneficiary string
}

type P2PListenerInfoOutput

type P2PListenerInfoOutput struct {
	Protocol      string
	ListenAddress string
	TargetAddress string
}

P2PListenerInfoOutput is output type of ls command

type P2PLsOutput

type P2PLsOutput struct {
	Listeners []P2PListenerInfoOutput
}

P2PLsOutput is output type of ls command

type P2PStreamInfoOutput

type P2PStreamInfoOutput struct {
	HandlerID     string
	Protocol      string
	OriginAddress string
	TargetAddress string
}

P2PStreamInfoOutput is output type of streams command

type P2PStreamsOutput

type P2PStreamsOutput struct {
	Streams []P2PStreamInfoOutput
}

P2PStreamsOutput is output type of streams command

type PinLsList

type PinLsList struct {
	Keys map[string]PinLsType
}

PinLsList is a set of pins with their type

type PinLsObject

type PinLsObject struct {
	Cid  string `json:",omitempty"`
	Type string `json:",omitempty"`
}

PinLsObject contains the description of a pin

type PinLsOutputWrapper

type PinLsOutputWrapper struct {
	PinLsList
	PinLsObject
}

PinLsOutputWrapper is the output type of the pin ls command. Pin ls needs to output two different type depending on if it's streamed or not. We use this to bypass the cmds lib refusing to have interface{}

type PinLsType

type PinLsType struct {
	Type string
}

PinLsType contains the type of a pin

type PinOutput

type PinOutput struct {
	Pins []string
}

type PinStatus

type PinStatus struct {
	Ok       bool
	BadNodes []BadNode `json:",omitempty"`
}

PinStatus is part of PinVerifyRes, do not use directly

type PinVerifyRes

type PinVerifyRes struct {
	Cid string
	PinStatus
}

PinVerifyRes is the result returned for each pin checked in "pin verify"

func (PinVerifyRes) Format

func (r PinVerifyRes) Format(out io.Writer)

Format formats PinVerifyRes

type PingResult

type PingResult struct {
	Success bool
	Time    time.Duration
	Text    string
}

type RefWrapper

type RefWrapper struct {
	Ref string
	Err string
}

type RefWriter

type RefWriter struct {
	DAG ipld.NodeGetter
	Ctx context.Context

	Unique   bool
	MaxDepth int
	PrintFmt string
	// contains filtered or unexported fields
}

func (*RefWriter) WriteEdge

func (rw *RefWriter) WriteEdge(from, to cid.Cid, linkname string, enc cidenc.Encoder) error

Write one edge

func (*RefWriter) WriteRefs

func (rw *RefWriter) WriteRefs(c cid.Cid, enc cidenc.Encoder) (int, error)

WriteRefs writes refs of the given object to the underlying writer.

type RepoVersion

type RepoVersion struct {
	Version string
}

type ReportListCmdRet

type ReportListCmdRet struct {
	Records []*chain.LevelDbReportStatusInfo `json:"records"`
	Total   int                              `json:"total"`
	PeerId  string                           `json:"peer_id"`
}

type RetReportLastTime

type RetReportLastTime struct {
	EveryDaySeconds int64     `json:"every_day_seconds"`
	LastTime        time.Time `json:"last_time"`
}

type RetReportOnlineListDaily

type RetReportOnlineListDaily struct {
	Records  []*chain.LastOnlineInfoRet `json:"records"`
	Total    int                        `json:"total"`
	PeerId   string                     `json:"peer_id"`
	BttcAddr string                     `json:"bttc_addr"`
}

type RetTotalDaily

type RetTotalDaily struct {
	PeerId         string `json:"peer_id"`
	StatusContract string `json:"status_contract"`
	TotalCount     int    `json:"total_count"`
}

type TestOutput

type TestOutput struct {
	Status string
}

type TotalCmdRet

type TotalCmdRet struct {
	PeerId         string `json:"peer_id"`
	StatusContract string `json:"status_contract"`
	TotalCount     int    `json:"total_count"`
	TotalGasSpend  string `json:"total_gas_spend"`
}

type VerifyProgress

type VerifyProgress struct {
	Msg      string
	Progress int
}

type VersionOutput

type VersionOutput struct {
	Version string
	Commit  string
	Repo    string
	System  string
	Golang  string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL