| 1 | package commands |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "io" |
| 6 | |
| 7 | cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" |
| 8 | |
| 9 | humanize "github.com/dustin/go-humanize" |
| 10 | bitswap "github.com/ipfs/boxo/bitswap" |
| 11 | "github.com/ipfs/boxo/bitswap/server" |
| 12 | cidutil "github.com/ipfs/go-cidutil" |
| 13 | cmds "github.com/ipfs/go-ipfs-cmds" |
| 14 | peer "github.com/libp2p/go-libp2p/core/peer" |
| 15 | ) |
| 16 | |
| 17 | var BitswapCmd = &cmds.Command{ |
| 18 | Helptext: cmds.HelpText{ |
| 19 | Tagline: "Interact with the bitswap agent.", |
| 20 | ShortDescription: ``, |
| 21 | }, |
| 22 | |
| 23 | Subcommands: map[string]*cmds.Command{ |
| 24 | "stat": bitswapStatCmd, |
| 25 | "wantlist": showWantlistCmd, |
| 26 | "ledger": ledgerCmd, |
| 27 | "reprovide": deprecatedBitswapReprovideCmd, |
| 28 | }, |
| 29 | } |
| 30 | |
| 31 | const ( |
| 32 | peerOptionName = "peer" |
| 33 | ) |
| 34 | |
| 35 | var deprecatedBitswapReprovideCmd = &cmds.Command{ |
| 36 | Status: cmds.Deprecated, |
| 37 | Helptext: cmds.HelpText{ |
| 38 | Tagline: "Deprecated command to announce to bitswap. Use 'ipfs routing reprovide' instead.", |
| 39 | ShortDescription: ` |
| 40 | 'ipfs bitswap reprovide' is a legacy plumbing command used to announce to DHT. |
| 41 | Deprecated, use modern 'ipfs routing reprovide' instead.`, |
| 42 | }, |
| 43 | Run: reprovideRoutingCmd.Run, // alias to routing reprovide to not break existing users |
| 44 | } |
| 45 | |
| 46 | var showWantlistCmd = &cmds.Command{ |
| 47 | Helptext: cmds.HelpText{ |
| 48 | Tagline: "Show blocks currently on the wantlist.", |
| 49 | ShortDescription: ` |
| 50 | Print out all blocks currently on the bitswap wantlist for the local peer.`, |
| 51 | }, |
| 52 | Options: []cmds.Option{ |
| 53 | cmds.StringOption(peerOptionName, "p", "Specify which peer to show wantlist for. Default: self."), |
| 54 | }, |
| 55 | Type: KeyList{}, |
| 56 | Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { |
| 57 | nd, err := cmdenv.GetNode(env) |
| 58 | if err != nil { |
| 59 | return err |
| 60 | } |
| 61 | |
| 62 | if !nd.IsOnline { |
| 63 | return ErrNotOnline |
| 64 | } |
| 65 | |
| 66 | bs := nd.Bitswap |
| 67 | |
| 68 | pstr, found := req.Options[peerOptionName].(string) |
| 69 | if found { |
| 70 | pid, err := peer.Decode(pstr) |
| 71 | if err != nil { |
| 72 | return err |
| 73 | } |
| 74 | if pid != nd.Identity { |
| 75 | return cmds.EmitOnce(res, &KeyList{bs.WantlistForPeer(pid)}) |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | return cmds.EmitOnce(res, &KeyList{bs.GetWantlist()}) |
| 80 | }, |
| 81 | Encoders: cmds.EncoderMap{ |
| 82 | cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *KeyList) error { |
| 83 | enc, err := cmdenv.GetCidEncoder(req) |
| 84 | if err != nil { |
| 85 | return err |
| 86 | } |
| 87 | // sort the keys first |
| 88 | cidutil.Sort(out.Keys) |
| 89 | for _, key := range out.Keys { |
| 90 | fmt.Fprintln(w, enc.Encode(key)) |
| 91 | } |
| 92 | return nil |
| 93 | }), |
| 94 | }, |
| 95 | } |
| 96 | |
| 97 | const ( |
| 98 | bitswapVerboseOptionName = "verbose" |
| 99 | bitswapHumanOptionName = "human" |
| 100 | ) |
| 101 | |
| 102 | var bitswapStatCmd = &cmds.Command{ |
| 103 | Helptext: cmds.HelpText{ |
| 104 | Tagline: "Show some diagnostic information on the bitswap agent.", |
| 105 | ShortDescription: ``, |
| 106 | }, |
| 107 | Options: []cmds.Option{ |
| 108 | cmds.BoolOption(bitswapVerboseOptionName, "v", "Print extra information"), |
| 109 | cmds.BoolOption(bitswapHumanOptionName, "Print sizes in human readable format (e.g., 1K 234M 2G)"), |
| 110 | }, |
| 111 | Type: bitswap.Stat{}, |
| 112 | Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { |
| 113 | nd, err := cmdenv.GetNode(env) |
| 114 | if err != nil { |
| 115 | return err |
| 116 | } |
| 117 | |
| 118 | if !nd.IsOnline { |
| 119 | return cmds.Errorf(cmds.ErrClient, "unable to run offline: %s", ErrNotOnline) |
| 120 | } |
| 121 | |
| 122 | st, err := nd.Bitswap.Stat() |
| 123 | if err != nil { |
| 124 | return err |
| 125 | } |
| 126 | |
| 127 | return cmds.EmitOnce(res, st) |
| 128 | }, |
| 129 | Encoders: cmds.EncoderMap{ |
| 130 | cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, s *bitswap.Stat) error { |
| 131 | enc, err := cmdenv.GetCidEncoder(req) |
| 132 | if err != nil { |
| 133 | return err |
| 134 | } |
| 135 | verbose, _ := req.Options[bitswapVerboseOptionName].(bool) |
| 136 | human, _ := req.Options[bitswapHumanOptionName].(bool) |
| 137 | |
| 138 | fmt.Fprintln(w, "bitswap status") |
| 139 | fmt.Fprintf(w, "\tblocks received: %d\n", s.BlocksReceived) |
| 140 | fmt.Fprintf(w, "\tblocks sent: %d\n", s.BlocksSent) |
| 141 | if human { |
| 142 | fmt.Fprintf(w, "\tdata received: %s\n", humanize.Bytes(s.DataReceived)) |
| 143 | fmt.Fprintf(w, "\tdata sent: %s\n", humanize.Bytes(s.DataSent)) |
| 144 | } else { |
| 145 | fmt.Fprintf(w, "\tdata received: %d\n", s.DataReceived) |
| 146 | fmt.Fprintf(w, "\tdata sent: %d\n", s.DataSent) |
| 147 | } |
| 148 | fmt.Fprintf(w, "\tdup blocks received: %d\n", s.DupBlksReceived) |
| 149 | if human { |
| 150 | fmt.Fprintf(w, "\tdup data received: %s\n", humanize.Bytes(s.DupDataReceived)) |
| 151 | } else { |
| 152 | fmt.Fprintf(w, "\tdup data received: %d\n", s.DupDataReceived) |
| 153 | } |
| 154 | fmt.Fprintf(w, "\twantlist [%d keys]\n", len(s.Wantlist)) |
| 155 | for _, k := range s.Wantlist { |
| 156 | fmt.Fprintf(w, "\t\t%s\n", enc.Encode(k)) |
| 157 | } |
| 158 | |
| 159 | fmt.Fprintf(w, "\tpartners [%d]\n", len(s.Peers)) |
| 160 | if verbose { |
| 161 | for _, p := range s.Peers { |
| 162 | fmt.Fprintf(w, "\t\t%s\n", p) |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | return nil |
| 167 | }), |
| 168 | }, |
| 169 | } |
| 170 | |
| 171 | var ledgerCmd = &cmds.Command{ |
| 172 | Helptext: cmds.HelpText{ |
| 173 | Tagline: "Show the current ledger for a peer.", |
| 174 | ShortDescription: ` |
| 175 | The Bitswap decision engine tracks the number of bytes exchanged between IPFS |
| 176 | nodes, and stores this information as a collection of ledgers. This command |
| 177 | prints the ledger associated with a given peer. |
| 178 | `, |
| 179 | }, |
| 180 | Arguments: []cmds.Argument{ |
| 181 | cmds.StringArg("peer", true, false, "The PeerID (B58) of the ledger to inspect."), |
| 182 | }, |
| 183 | Type: server.Receipt{}, |
| 184 | Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { |
| 185 | nd, err := cmdenv.GetNode(env) |
| 186 | if err != nil { |
| 187 | return err |
| 188 | } |
| 189 | |
| 190 | if !nd.IsOnline { |
| 191 | return ErrNotOnline |
| 192 | } |
| 193 | |
| 194 | partner, err := peer.Decode(req.Arguments[0]) |
| 195 | if err != nil { |
| 196 | return err |
| 197 | } |
| 198 | |
| 199 | return cmds.EmitOnce(res, nd.Bitswap.LedgerForPeer(partner)) |
| 200 | }, |
| 201 | Encoders: cmds.EncoderMap{ |
| 202 | cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *server.Receipt) error { |
| 203 | fmt.Fprintf(w, "Ledger for %s\n"+ |
| 204 | "Debt ratio:\t%f\n"+ |
| 205 | "Exchanges:\t%d\n"+ |
| 206 | "Bytes sent:\t%d\n"+ |
| 207 | "Bytes received:\t%d\n\n", |
| 208 | out.Peer, out.Value, out.Exchanged, |
| 209 | out.Sent, out.Recv) |
| 210 | return nil |
| 211 | }), |
| 212 | }, |
| 213 | } |