-
Notifications
You must be signed in to change notification settings - Fork 467
/
Copy pathmain.go
338 lines (276 loc) · 10.4 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
package commands
import (
"context"
"fmt"
"github.com/filecoin-project/go-filecoin/types"
"net"
"net/url"
"os"
"path/filepath"
"syscall"
ma "gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr"
"gx/ipfs/QmVmDhyTTUcQXFD1rRQ64fGLMSAoaQvNH3hwuaCFAPq2hy/errors"
"gx/ipfs/QmZcLBXKaFe8ND5YHPkJRAwmhJGrVsi1JqDZNyJ4nRK5Mj/go-multiaddr-net"
"gx/ipfs/Qma6uuSyjkecGhMFFLfzyJDPyoDtNJSHJNweDccZhaWkgU/go-ipfs-cmds"
"gx/ipfs/Qma6uuSyjkecGhMFFLfzyJDPyoDtNJSHJNweDccZhaWkgU/go-ipfs-cmds/cli"
cmdhttp "gx/ipfs/Qma6uuSyjkecGhMFFLfzyJDPyoDtNJSHJNweDccZhaWkgU/go-ipfs-cmds/http"
"gx/ipfs/QmdcULN1WCzgoQmcCaUAmEhwcxHYsDrbZ2LvRJKCL8dMrK/go-homedir"
"gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
"github.com/filecoin-project/go-filecoin/api/impl"
"github.com/filecoin-project/go-filecoin/repo"
)
const (
// OptionAPI is the name of the option for specifying the api port.
OptionAPI = "cmdapiaddr"
// OptionRepoDir is the name of the option for specifying the directory of the repo.
OptionRepoDir = "repodir"
// APIPrefix is the prefix for the http version of the api.
APIPrefix = "/api"
// OfflineMode tells us if we should try to connect this Filecoin node to the network
OfflineMode = "offline"
// ELStdout tells the daemon to write event logs to stdout.
ELStdout = "elstdout"
// AutoSealIntervalSeconds configures the daemon to check for and seal any staged sectors on an interval.
AutoSealIntervalSeconds = "auto-seal-interval-seconds"
// SwarmAddress is the multiaddr for this Filecoin node
SwarmAddress = "swarmlisten"
// SwarmPublicRelayAddress is a public address that the filecoin node
// will listen on if it is operating as a relay. We use this to specify
// the public ip:port of a relay node that is sitting behind a static
// NAT mapping.
SwarmPublicRelayAddress = "swarmrelaypublic"
// BlockTime is the duration string of the block time the daemon will
// run with. TODO: this should eventually be more explicitly grouped
// with testing as we won't be able to set blocktime in production.
BlockTime = "block-time"
// PeerKeyFile is the path of file containing key to use for new nodes libp2p identity
PeerKeyFile = "peerkeyfile"
// WithMiner when set, creates a custom genesis block with a pre generated miner account, requires to run the daemon using dev mode (--dev)
WithMiner = "with-miner"
// DefaultAddress when set, sets the daemons's default address to the provided address
DefaultAddress = "default-address"
// GenesisFile is the path of file containing archive of genesis block DAG data
GenesisFile = "genesisfile"
// ClusterTest populates config bootstrap addrs with the dns multiaddrs of the test cluster and other test cluster specific bootstrap parameters
ClusterTest = "cluster-test"
// ClusterNightly populates config bootstrap addrs with the dns multiaddrs of the nightly cluster and other nightly cluster specific bootstrap parameters
ClusterNightly = "cluster-nightly"
// IsRelay when set causes the the daemon to provide libp2p relay
// services allowing other filecoin nodes behind NATs to talk directly.
IsRelay = "is-relay"
)
// command object for the local cli
var rootCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "A decentralized storage network",
Subcommands: `
START RUNNING FILECOIN
go-filecoin init - Initialize a filecoin repo
go-filecoin config <key> [<value>] - Get and set filecoin config values
go-filecoin daemon - Start a long-running daemon process
go-filecoin wallet - Manage your filecoin wallets
go-filecoin address - Interact with addresses
STORE AND RETRIEVE DATA
go-filecoin client - Make deals, store data, retrieve data
go-filecoin retrieval-client - Manage retrieval client operations
MINE
go-filecoin miner - Manage a single miner actor
go-filecoin mining - Manage all mining operations for a node
VIEW DATA STRUCTURES
go-filecoin chain - Inspect the filecoin blockchain
go-filecoin dag - Interact with IPLD DAG objects
go-filecoin show - Get human-readable representations of filecoin objects
NETWORK COMMANDS
go-filecoin bootstrap - Interact with bootstrap addresses
go-filecoin id - Show info about the network peers
go-filecoin ping <peer ID>... - Send echo request packets to p2p network members
go-filecoin swarm - Interact with the swarm
ACTOR COMMANDS
go-filecoin actor - Interact with actors. Actors are built-in smart contracts.
go-filecoin paych - Payment channel operations
MESSAGE COMMANDS
go-filecoin message - Manage messages
go-filecoin mpool - View the mempool of outstanding messages
TOOL COMMANDS
go-filecoin log - Interact with the daemon event log output.
go-filecoin version - Show go-filecoin version information
`,
},
Options: []cmdkit.Option{
cmdkit.StringOption(OptionAPI, "set the api port to use"),
cmdkit.StringOption(OptionRepoDir, "set the directory of the repo, defaults to ~/.filecoin"),
cmds.OptionEncodingType,
cmdkit.BoolOption("help", "Show the full command help text."),
cmdkit.BoolOption("h", "Show a short version of the command help text."),
},
Subcommands: make(map[string]*cmds.Command),
}
// command object for the daemon
var rootCmdDaemon = &cmds.Command{
Subcommands: make(map[string]*cmds.Command),
}
// all top level commands, not available to daemon
var rootSubcmdsLocal = map[string]*cmds.Command{
"daemon": daemonCmd,
"init": initCmd,
}
// all top level commands, available on daemon. set during init() to avoid configuration loops.
var rootSubcmdsDaemon = map[string]*cmds.Command{
"actor": actorCmd,
"address": addrsCmd,
"bootstrap": bootstrapCmd,
"chain": chainCmd,
"config": configCmd,
"client": clientCmd,
"dag": dagCmd,
"id": idCmd,
"log": logCmd,
"message": msgCmd,
"miner": minerCmd,
"mining": miningCmd,
"mpool": mpoolCmd,
"paych": paymentChannelCmd,
"ping": pingCmd,
"retrieval-client": retrievalClientCmd,
"show": showCmd,
"swarm": swarmCmd,
"version": versionCmd,
"wallet": walletCmd,
}
func init() {
for k, v := range rootSubcmdsLocal {
rootCmd.Subcommands[k] = v
}
for k, v := range rootSubcmdsDaemon {
rootCmd.Subcommands[k] = v
rootCmdDaemon.Subcommands[k] = v
}
}
// Run processes the arguments and stdin
func Run(args []string, stdin, stdout, stderr *os.File) (int, error) {
err := cli.Run(context.Background(), rootCmd, args, stdin, stdout, stderr, buildEnv, makeExecutor)
if err == nil {
return 0, nil
}
if exerr, ok := err.(cli.ExitError); ok {
return int(exerr), nil
}
return 1, err
}
func buildEnv(ctx context.Context, req *cmds.Request) (cmds.Environment, error) {
return &Env{ctx: ctx, api: impl.New(nil)}, nil
}
type executor struct {
api string
exec cmds.Executor
}
func (e *executor) Execute(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
if e.api == "" {
return e.exec.Execute(req, re, env)
}
client := cmdhttp.NewClient(e.api, cmdhttp.ClientWithAPIPrefix(APIPrefix))
res, err := client.Send(req)
if err != nil {
if isConnectionRefused(err) {
return cmdkit.Errorf(cmdkit.ErrFatal, "Connection Refused. Is the daemon running?")
}
return cmdkit.Errorf(cmdkit.ErrFatal, err.Error())
}
// copy received result into cli emitter
err = cmds.Copy(re, res)
if err != nil {
return cmdkit.Errorf(cmdkit.ErrFatal|cmdkit.ErrNormal, err.Error())
}
return nil
}
func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
isDaemonRequired := requiresDaemon(req)
var api string
if isDaemonRequired {
var err error
api, err = getAPIAddress(req)
if err != nil {
return nil, errors.Wrap(err, "failed to get API address")
}
}
if api == "" && isDaemonRequired {
return nil, ErrMissingDaemon
}
return &executor{
api: api,
exec: cmds.NewExecutor(rootCmd),
}, nil
}
func getAPIAddress(req *cmds.Request) (string, error) {
var out string
// second highest precedence is env vars.
if envapi := os.Getenv("FIL_API"); envapi != "" {
out = envapi
}
// first highest precedence is cmd flag.
if apiAddress, ok := req.Options[OptionAPI].(string); ok && apiAddress != "" {
out = apiAddress
}
// we will read the api file if no other option is given.
if len(out) == 0 {
apiFilePath, err := homedir.Expand(filepath.Join(filepath.Clean(getRepoDir(req)), repo.APIFile))
if err != nil {
return "", errors.Wrap(err, "failed to read api file")
}
out, err = repo.APIAddrFromFile(apiFilePath)
if err != nil {
return "", err
}
}
maddr, err := ma.NewMultiaddr(out)
if err != nil {
return "", err
}
_, host, err := manet.DialArgs(maddr)
if err != nil {
return "", err
}
return host, nil
}
func requiresDaemon(req *cmds.Request) bool {
if req.Command == daemonCmd {
return false
}
if req.Command == initCmd {
return false
}
return true
}
func isConnectionRefused(err error) bool {
urlErr, ok := err.(*url.Error)
if !ok {
return false
}
opErr, ok := urlErr.Err.(*net.OpError)
if !ok {
return false
}
syscallErr, ok := opErr.Err.(*os.SyscallError)
if !ok {
return false
}
return syscallErr.Err == syscall.ECONNREFUSED
}
var priceOption = cmdkit.StringOption("price", "Price (FIL e.g. 0.00013) to pay for each GasUnits consumed mining this message")
var limitOption = cmdkit.Uint64Option("limit", "Maximum number of GasUnits this message is allowed to consume")
func parseGasOptions(req *cmds.Request) (gasPrice types.AttoFIL, gasLimit types.GasUnits, error error) {
priceOption := req.Options["price"]
if priceOption == nil {
return types.AttoFIL{}, types.NewGasUnits(0), errors.New("price option is required")
}
price, ok := types.NewAttoFILFromFILString(priceOption.(string))
if !ok {
return types.AttoFIL{}, types.NewGasUnits(0), errors.New("invalid gas price (specify FIL as a decimal number)")
}
gasLimitInt, ok := req.Options["limit"].(uint64)
if !ok {
msg := fmt.Sprintf("invalid gas limit: %s", req.Options["limit"].(string))
return types.AttoFIL{}, types.NewGasUnits(0), errors.New(msg)
}
return *price, types.NewGasUnits(gasLimitInt), nil
}