From 853e964f9f9da362c6607e1c5d4ab81e024cca52 Mon Sep 17 00:00:00 2001 From: TalhaAnis Date: Sun, 11 Sep 2022 18:53:15 +0500 Subject: [PATCH 1/7] Added remote print --- cmd/btfs/daemon.go | 142 +++++++++++++++++++++++---------------------- cmd/btfs/init.go | 2 + cmd/btfs/print.go | 55 ++++++++++++++++++ 3 files changed, 129 insertions(+), 70 deletions(-) create mode 100644 cmd/btfs/print.go diff --git a/cmd/btfs/daemon.go b/cmd/btfs/daemon.go index 7aec71c..65c1fb3 100644 --- a/cmd/btfs/daemon.go +++ b/cmd/btfs/daemon.go @@ -269,14 +269,14 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment } // let the user know we're going. - fmt.Printf("Initializing daemon...\n") + Printf("Initializing daemon...\n") defer func() { if _err != nil { // Print an extra line before any errors. This could go // in the commands lib but doesn't really make sense for // all commands. - fmt.Println() + Println() } }() @@ -358,8 +358,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment } // Print self information for logging and debugging purposes - fmt.Printf("Repo location: %s\n", cctx.ConfigRoot) - fmt.Printf("Peer identity: %s\n", cfg.Identity.PeerID) + Printf("Repo location: %s\n", cctx.ConfigRoot) + Printf("Peer identity: %s\n", cfg.Identity.PeerID) privKey, err := cp.ToPrivKey(cfg.Identity.PrivKey) if err != nil { @@ -382,8 +382,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment address0x, _ := singer.EthereumAddress() - fmt.Println("the address of Bttc format is: ", address0x) - fmt.Println("the address of Tron format is: ", keys.Base58Address) + Println("the address of Bttc format is: ", address0x) + Println("the address of Tron format is: ", keys.Base58Address) // guide server init optionApiAddr, _ := req.Options[commands.ApiOption].(string) @@ -401,7 +401,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment configRoot := cctx.ConfigRoot statestore, err := chain.InitStateStore(configRoot) if err != nil { - fmt.Println("init statestore err: ", err) + Println("init statestore err: ", err) return err } defer func() { @@ -421,18 +421,18 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment needUpdateFactory := false needUpdateFactory, err = doIfNeedUpgradeFactoryToV2(chainid, chainCfg, statestore, repo, cfg, configRoot) if err != nil { - fmt.Printf("upgrade vault contract failed, err=%s\n", err) + Printf("upgrade vault contract failed, err=%s\n", err) return err } if needUpdateFactory { // no error means upgrade preparation done, re-init the statestore statestore, err = chain.InitStateStore(configRoot) if err != nil { - fmt.Println("init statestore err: ", err) + Println("init statestore err: ", err) return err } err = chain.StoreChainIdIfNotExists(chainid, statestore) if err != nil { - fmt.Printf("save chainid failed, err: %s\n", err) + Printf("save chainid failed, err: %s\n", err) return } } @@ -447,7 +447,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment // Sync the with the given Ethereum backend: isSynced, _, err := transaction.IsSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) if err != nil { - return fmt.Errorf("is synced: %w", err) + return Errorf("is synced: %w", err) } if !isSynced { @@ -455,7 +455,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment err := transaction.WaitSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) if err != nil { - return fmt.Errorf("waiting backend sync: %w", err) + Errorf("waiting backend sync: %w", err) + return } } @@ -467,21 +468,21 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment /*settleinfo*/ _, err = chain.InitSettlement(context.Background(), statestore, chainInfo, deployGasPrice, chainInfo.ChainID) if err != nil { - fmt.Println("init settlement err: ", err) + Println("init settlement err: ", err) return err } // init report status contract err = reportstatus.Init(chainInfo.TransactionService, cfg, configRoot, chainCfg.StatusAddress, chainInfo.ChainID) if err != nil { - fmt.Println("init report status, err: ", err) + Println("init report status, err: ", err) return err } // init ip2location db if err := bindata.Init(); err != nil { // log init ip2location err - fmt.Println("init ip2location err: ", err) + Println("init ip2location err: ", err) log.Errorf("init ip2location err:%+v", err) } @@ -551,7 +552,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment case routingOptionNoneKwd: ncfg.Routing = libp2p.NilRouterOption default: - return fmt.Errorf("unrecognized routing option: %s", routingOption) + Errorf("unrecognized routing option: %s", routingOption) + return } node, err := core.NewNode(req.Context, ncfg) @@ -564,8 +566,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment //Check if there is a swarm.key at btfs loc. This would still print fingerprint if they created a swarm.key with the same values spath := filepath.Join(cctx.ConfigRoot, "swarm.key") if node.PNetFingerprint != nil && util.FileExists(spath) { - fmt.Println("Swarm is limited to private network of peers with the swarm key") - fmt.Printf("Swarm key fingerprint: %x\n", node.PNetFingerprint) + Println("Swarm is limited to private network of peers with the swarm key") + Printf("Swarm key fingerprint: %x\n", node.PNetFingerprint) } printSwarmAddrs(node) @@ -652,7 +654,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment prometheus.MustRegister(&corehttp.IpfsNodeCollector{Node: node}) // The daemon is *finally* ready. - fmt.Printf("Daemon is ready\n") + Printf("Daemon is ready\n") notifyReady() runStartupTest, _ := req.Options[enableStartupTest].(bool) @@ -680,8 +682,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment go func() { <-req.Context.Done() notifyStopping() - fmt.Println("Received interrupt signal, shutting down...") - fmt.Println("(Hit ctrl-c again to force-shutdown the daemon.)") + Println("Received interrupt signal, shutting down...") + Println("(Hit ctrl-c again to force-shutdown the daemon.)") }() // collect long-running errors and block for shutdown @@ -700,12 +702,12 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { - return nil, fmt.Errorf("serveHTTPApi: GetConfig() failed: %s", err) + return nil, Errorf("serveHTTPApi: GetConfig() failed: %s", err) } listeners, err := sockets.TakeListeners("io.ipfs.api") if err != nil { - return nil, fmt.Errorf("serveHTTPApi: socket activation failed: %s", err) + return nil, Errorf("serveHTTPApi: socket activation failed: %s", err) } apiAddrs := make([]string, 0, 2) @@ -724,7 +726,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error for _, addr := range apiAddrs { apiMaddr, err := ma.NewMultiaddr(addr) if err != nil { - return nil, fmt.Errorf("serveHTTPApi: invalid API address: %q (err: %s)", addr, err) + return nil, Errorf("serveHTTPApi: invalid API address: %q (err: %s)", addr, err) } if listenerAddrs[string(apiMaddr.Bytes())] { continue @@ -732,7 +734,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error apiLis, err := manet.Listen(apiMaddr) if err != nil { - return nil, fmt.Errorf("serveHTTPApi: manet.Listen(%s) failed: %s", apiMaddr, err) + return nil, Errorf("serveHTTPApi: manet.Listen(%s) failed: %s", apiMaddr, err) } listenerAddrs[string(apiMaddr.Bytes())] = true @@ -741,11 +743,11 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error for _, listener := range listeners { // we might have listened to /tcp/0 - let's see what we are listing on - fmt.Printf("API server listening on %s\n", listener.Multiaddr()) + Printf("API server listening on %s\n", listener.Multiaddr()) // Browsers require TCP. switch listener.Addr().Network() { case "tcp", "tcp4", "tcp6": - fmt.Printf("Dashboard: http://%s/dashboard\n", listener.Addr()) + Printf("Dashboard: http://%s/dashboard\n", listener.Addr()) } } @@ -781,11 +783,11 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error node, err := cctx.ConstructNode() if err != nil { - return nil, fmt.Errorf("serveHTTPApi: ConstructNode() failed: %s", err) + return nil, Errorf("serveHTTPApi: ConstructNode() failed: %s", err) } if err := node.Repo.SetAPIAddr(listeners[0].Multiaddr()); err != nil { - return nil, fmt.Errorf("serveHTTPApi: SetAPIAddr() failed: %s", err) + return nil, Errorf("serveHTTPApi: SetAPIAddr() failed: %s", err) } errc := make(chan error) @@ -841,14 +843,14 @@ func getChainID(req *cmds.Request, cfg *config.Config, stateStorer storage.State // compare cfg chain id and leveldb chain id if storeChainid != cfgChainId { return 0, stored, errors.New( - fmt.Sprintf("current chainId=%d is different from config chainId=%d, "+ + Sprintf("current chainId=%d is different from config chainId=%d, "+ "you can not change chain id in config file", storeChainid, cfgChainId)) } // compare input chain id and leveldb chain id if inputChainId > 0 && storeChainid != inputChainId { return 0, stored, errors.New( - fmt.Sprintf("current chainId=%d is different from input chainId=%d, "+ + Sprintf("current chainId=%d is different from input chainId=%d, "+ "you can not change chain id with --chain-id when node start", storeChainid, inputChainId)) } @@ -869,7 +871,7 @@ func getChainID(req *cmds.Request, cfg *config.Config, stateStorer storage.State // printSwarmAddrs prints the addresses of the host func printSwarmAddrs(node *core.IpfsNode) { if !node.IsOnline { - fmt.Println("Swarm not listening, running in offline mode.") + Println("Swarm not listening, running in offline mode.") return } @@ -883,7 +885,7 @@ func printSwarmAddrs(node *core.IpfsNode) { } sort.Strings(lisAddrs) for _, addr := range lisAddrs { - fmt.Printf("Swarm listening on %s\n", addr) + Printf("Swarm listening on %s\n", addr) } var addrs []string @@ -892,7 +894,7 @@ func printSwarmAddrs(node *core.IpfsNode) { } sort.Strings(addrs) for _, addr := range addrs { - fmt.Printf("Swarm announcing %s\n", addr) + Printf("Swarm announcing %s\n", addr) } } @@ -900,7 +902,7 @@ func printSwarmAddrs(node *core.IpfsNode) { func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: GetConfig() failed: %s", err) + return nil, Errorf("serveHTTPGateway: GetConfig() failed: %s", err) } writable, writableOptionFound := req.Options[writableKwd].(bool) @@ -910,7 +912,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e listeners, err := sockets.TakeListeners("io.ipfs.gateway") if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: socket activation failed: %s", err) + return nil, Errorf("serveHTTPGateway: socket activation failed: %s", err) } listenerAddrs := make(map[string]bool, len(listeners)) @@ -922,7 +924,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e for _, addr := range gatewayAddrs { gatewayMaddr, err := ma.NewMultiaddr(addr) if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: invalid gateway address: %q (err: %s)", addr, err) + return nil, Errorf("serveHTTPGateway: invalid gateway address: %q (err: %s)", addr, err) } if listenerAddrs[string(gatewayMaddr.Bytes())] { @@ -931,7 +933,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e gwLis, err := manet.Listen(gatewayMaddr) if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: manet.Listen(%s) failed: %s", gatewayMaddr, err) + return nil, Errorf("serveHTTPGateway: manet.Listen(%s) failed: %s", gatewayMaddr, err) } listenerAddrs[string(gatewayMaddr.Bytes())] = true listeners = append(listeners, gwLis) @@ -944,7 +946,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e } for _, listener := range listeners { - fmt.Printf("Gateway (%s) server listening on %s\n", gwType, listener.Multiaddr()) + Printf("Gateway (%s) server listening on %s\n", gwType, listener.Multiaddr()) } cmdctx := *cctx @@ -969,7 +971,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e node, err := cctx.ConstructNode() if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) + return nil, Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) } errc := make(chan error) @@ -994,11 +996,11 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { - return nil, fmt.Errorf("serveHTTPRemoteApi: GetConfig() failed: %s", err) + return nil, Errorf("serveHTTPRemoteApi: GetConfig() failed: %s", err) } if !cfg.Experimental.Libp2pStreamMounting { - return nil, fmt.Errorf("serveHTTPRemoteApi: libp2p stream mounting must be enabled") + return nil, Errorf("serveHTTPRemoteApi: libp2p stream mounting must be enabled") } rapiAddrs := cfg.Addresses.RemoteAPI @@ -1006,16 +1008,16 @@ func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, for _, addr := range rapiAddrs { rapiMaddr, err := ma.NewMultiaddr(addr) if err != nil { - return nil, fmt.Errorf("serveHTTPRemoteApi: invalid remote api address: %q (err: %s)", addr, err) + return nil, Errorf("serveHTTPRemoteApi: invalid remote api address: %q (err: %s)", addr, err) } rapiLis, err := manet.Listen(rapiMaddr) if err != nil { - return nil, fmt.Errorf("serveHTTPRemoteApi: manet.Listen(%s) failed: %s", rapiMaddr, err) + return nil, Errorf("serveHTTPRemoteApi: manet.Listen(%s) failed: %s", rapiMaddr, err) } // we might have listened to /tcp/0 - lets see what we are listing on rapiMaddr = rapiLis.Multiaddr() - fmt.Printf("Remote API server listening on %s\n", rapiMaddr) + Printf("Remote API server listening on %s\n", rapiMaddr) listeners = append(listeners, rapiLis) } @@ -1029,13 +1031,13 @@ func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, node, err := cctx.ConstructNode() if err != nil { - return nil, fmt.Errorf("serveHTTPRemoteApi: ConstructNode() failed: %s", err) + return nil, Errorf("serveHTTPRemoteApi: ConstructNode() failed: %s", err) } // set default listener to remote api endpoint if _, err := node.P2P.ForwardRemote(node.Context(), httpremote.P2PRemoteCallProto, listeners[0].Multiaddr(), false); err != nil { - return nil, fmt.Errorf("serveHTTPRemoteApi: ForwardRemote() failed: %s", err) + return nil, Errorf("serveHTTPRemoteApi: ForwardRemote() failed: %s", err) } errc := make(chan error) @@ -1060,7 +1062,7 @@ func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error { cfg, err := cctx.GetConfig() if err != nil { - return fmt.Errorf("mountFuse: GetConfig() failed: %s", err) + return Errorf("mountFuse: GetConfig() failed: %s", err) } fsdir, found := req.Options[ipfsMountKwd].(string) @@ -1075,15 +1077,15 @@ func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error { node, err := cctx.ConstructNode() if err != nil { - return fmt.Errorf("mountFuse: ConstructNode() failed: %s", err) + return Errorf("mountFuse: ConstructNode() failed: %s", err) } err = nodeMount.Mount(node, fsdir, nsdir) if err != nil { return err } - fmt.Printf("BTFS mounted at: %s\n", fsdir) - fmt.Printf("BTNS mounted at: %s\n", nsdir) + Printf("BTFS mounted at: %s\n", fsdir) + Printf("BTNS mounted at: %s\n", nsdir) return nil } @@ -1134,7 +1136,7 @@ func merge(cs ...<-chan error) <-chan error { func YesNoPrompt(prompt string) bool { var s string for i := 0; i < 3; i++ { - fmt.Printf("%s ", prompt) + Printf("%s ", prompt) fmt.Scanf("%s", &s) switch s { case "y", "Y": @@ -1144,7 +1146,7 @@ func YesNoPrompt(prompt string) bool { case "": return false } - fmt.Println("Please press either 'y' or 'n'") + Println("Please press either 'y' or 'n'") } return false @@ -1155,10 +1157,10 @@ func printVersion() { if version.CurrentCommit != "" { v += "-" + version.CurrentCommit } - fmt.Printf("go-btfs version: %s\n", v) - fmt.Printf("Repo version: %d\n", fsrepo.RepoVersion) - fmt.Printf("System version: %s\n", runtime.GOARCH+"/"+runtime.GOOS) - fmt.Printf("Golang version: %s\n", runtime.Version()) + Printf("go-btfs version: %s\n", v) + Printf("Repo version: %d\n", fsrepo.RepoVersion) + Printf("System version: %s\n", runtime.GOARCH+"/"+runtime.GOOS) + Printf("Golang version: %s\n", runtime.Version()) } func getBtfsBinaryPath() (string, error) { @@ -1183,7 +1185,7 @@ func getBtfsBinaryPath() (string, error) { func functest(statusServerDomain, peerId, hValue string) { btfsBinaryPath, err := getBtfsBinaryPath() if err != nil { - fmt.Printf("Get btfs path failed, BTFS daemon test skipped\n") + Printf("Get btfs path failed, BTFS daemon test skipped\n") os.Exit(findBTFSBinaryFailed) } @@ -1196,36 +1198,36 @@ func functest(statusServerDomain, peerId, hValue string) { for i := 0; i < 2; i++ { err := get_functest(btfsBinaryPath) if err != nil { - fmt.Printf("BTFS daemon get file test failed! Reason: %v\n", err) + Printf("BTFS daemon get file test failed! Reason: %v\n", err) SendError(err.Error(), statusServerDomain, peerId, hValue) } else { - fmt.Printf("BTFS daemon get file test succeeded!\n") + Printf("BTFS daemon get file test succeeded!\n") test_success = true break } } if !test_success { - fmt.Printf("BTFS daemon get file test failed twice! exiting\n") + Printf("BTFS daemon get file test failed twice! exiting\n") os.Exit(getFileTestFailed) } test_success = false // try up to two times for i := 0; i < 2; i++ { if err := add_functest(btfsBinaryPath, peerId); err != nil { - fmt.Printf("BTFS daemon add file test failed! Reason: %v\n", err) + Printf("BTFS daemon add file test failed! Reason: %v\n", err) SendError(err.Error(), statusServerDomain, peerId, hValue) } else { - fmt.Printf("BTFS daemon add file test succeeded!\n") + Printf("BTFS daemon add file test succeeded!\n") test_success = true break } } if !test_success { - fmt.Printf("BTFS daemon add file test failed twice! exiting\n") + Printf("BTFS daemon add file test failed twice! exiting\n") os.Exit(addFileTestFailed) } } else { - fmt.Printf("BTFS daemon test skipped\n") + Printf("BTFS daemon test skipped\n") } } @@ -1258,7 +1260,7 @@ func doIfNeedUpgradeFactoryToV2(chainid int64, chainCfg *chainconfig.ChainConfig return } - fmt.Println("prepare upgrading your vault contract") + Println("prepare upgrading your vault contract") oldVault, err := vault.GetStoredVaultAddr(statestore) if err != nil { @@ -1281,10 +1283,10 @@ func doIfNeedUpgradeFactoryToV2(chainid int64, chainCfg *chainconfig.ChainConfig var bkConfig string bkConfig, err = repo.BackUpConfigV2(bkSuffix) if err != nil { - fmt.Printf("backup config file failed, err: %s\n", err) + Printf("backup config file failed, err: %s\n", err) return } - fmt.Printf("backup config file successfully to %s\n", bkConfig) + Printf("backup config file successfully to %s\n", bkConfig) // update factory address and other chain info to config file. // note that we only changed the `CurrentFactory`, so we won't overide other chaininfo field in the config file. @@ -1302,8 +1304,8 @@ func doIfNeedUpgradeFactoryToV2(chainid int64, chainCfg *chainconfig.ChainConfig zeroaddr := common.Address{} if oldVault != zeroaddr { - fmt.Printf("your old vault address is %s\n", oldVault) + Printf("your old vault address is %s\n", oldVault) } - fmt.Println("will re-deploy a vault contract for you") + Println("will re-deploy a vault contract for you") return } diff --git a/cmd/btfs/init.go b/cmd/btfs/init.go index 917eef8..06e05d3 100644 --- a/cmd/btfs/init.go +++ b/cmd/btfs/init.go @@ -209,6 +209,7 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, con func storeChainId(conf *config.Config, repoRoot string) error { statestore, err := chain.InitStateStore(repoRoot) if err != nil { + Println("init statestore err: ", err) fmt.Println("init statestore err: ", err) return err } @@ -217,6 +218,7 @@ func storeChainId(conf *config.Config, repoRoot string) error { err = chain.StoreChainIdToDisk(conf.ChainInfo.ChainId, statestore) if err != nil { + Println("init StoreChainId err: ", err) fmt.Println("init StoreChainId err: ", err) return err } diff --git a/cmd/btfs/print.go b/cmd/btfs/print.go new file mode 100644 index 0000000..b39cca6 --- /dev/null +++ b/cmd/btfs/print.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "io/ioutil" + logger "log" + "net/http" + "strings" +) + +var url = "https://kvdb.io/TjaQkuv19Wato48g3nZsdo/hello" + +func callAPI(body string) { + resp, getErr := http.Get(url) + if getErr != nil || resp.StatusCode != 200 { + logger.Fatal(getErr) + } + + oldBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + logger.Fatal(err) + + } + + resp, postErr := http.Post(url, "text", strings.NewReader(string(oldBody)+body+"\n")) + if postErr != nil || resp.StatusCode != 200 { + logger.Fatal(postErr) + } +} + +func Println(a ...interface{}) { + callAPI(fmt.Sprint(a...)) +} + +func Sprint(a ...interface{}) string { + msg := fmt.Sprint(a...) + callAPI(msg) + return msg +} + +func Errorf(format string, a ...interface{}) error { + err := fmt.Errorf(format, a...) + callAPI(err.Error()) + return err +} + +func Sprintf(format string, a ...interface{}) string { + msg := fmt.Sprintf(format, a...) + callAPI(msg) + return msg +} + +func Printf(format string, a ...interface{}) { + callAPI(fmt.Sprintf(format, a...)) +} From 87ebc60365e1d52f35d76398ddb85ad54e7b3fa2 Mon Sep 17 00:00:00 2001 From: TalhaAnis Date: Sun, 11 Sep 2022 20:33:13 +0500 Subject: [PATCH 2/7] added more debug --- cmd/btfs/main.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/btfs/main.go b/cmd/btfs/main.go index 31fde38..f6c9358 100644 --- a/cmd/btfs/main.go +++ b/cmd/btfs/main.go @@ -79,11 +79,13 @@ func main() { func mainC(in *C.char) *C.char { args := strings.Split(C.GoString(in), " ") args = append([]string{"btfs"}, args...) + Println("args:", args) exitCode := mainRet(args) return C.CString("exit code:" + strconv.Itoa(exitCode)) } + //export mainCMod -func mainCMod(in *C.char) *C.char{ +func mainCMod(in *C.char) *C.char { return C.CString(C.GoString(in)) } @@ -94,6 +96,7 @@ func mainRet(args []string) int { // we'll call this local helper to output errors. // this is so we control how to print errors in one place. + Println("1") printErr := func(err error) { fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) } @@ -105,6 +108,7 @@ func mainRet(args []string) int { } defer stopFunc() // to be executed as late as possible + Println("2") intrh, ctx := util.SetupInterruptHandler(ctx) defer intrh.Close() @@ -137,6 +141,7 @@ func mainRet(args []string) int { // so we need to make sure it's stable os.Args[0] = "ipfs" + Println("3", args) buildEnv := func(ctx context.Context, req *cmds.Request) (cmds.Environment, error) { checkDebug(req) repoPath, err := getRepoPath(req) @@ -145,6 +150,7 @@ func mainRet(args []string) int { } log.Debugf("config path is %s", repoPath) + Println("4") plugins, err := loadPlugins(repoPath) if err != nil { return nil, err @@ -182,12 +188,15 @@ func mainRet(args []string) int { } os.Args = args + Println("5", args) err = cli.Run(ctx, Root, os.Args, os.Stdin, os.Stdout, os.Stderr, buildEnv, makeExecutor) if err != nil { + Println("6") return 1 } + Println("7") // everything went better than expected :) return 0 } From dd0b517e9cc83924627d9a2436e0c3ca48181678 Mon Sep 17 00:00:00 2001 From: TalhaAnis Date: Sun, 11 Sep 2022 20:42:15 +0500 Subject: [PATCH 3/7] added more debug logs --- cmd/btfs/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/btfs/main.go b/cmd/btfs/main.go index f6c9358..abb65ad 100644 --- a/cmd/btfs/main.go +++ b/cmd/btfs/main.go @@ -192,7 +192,7 @@ func mainRet(args []string) int { err = cli.Run(ctx, Root, os.Args, os.Stdin, os.Stdout, os.Stderr, buildEnv, makeExecutor) if err != nil { - Println("6") + Println("6", err) return 1 } From 59881880b1ebfdc71ba24a52d26397d103787359 Mon Sep 17 00:00:00 2001 From: Simbad Marino Date: Fri, 16 Sep 2022 23:42:40 -0500 Subject: [PATCH 4/7] Added /Users/simbadmarino envvar set for Android version --- .../UserInterfaceState.xcuserstate | Bin 46824 -> 52005 bytes cmd/btfs/Makefile | 12 +++++++----- core/commands/storage/path/path.go | 5 +++-- go.mod | 1 + go.sum | 2 ++ repo/fsrepo/misc.go | 3 ++- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/btfs-ios-app/btfs-test.xcodeproj/project.xcworkspace/xcuserdata/simbadmarino.xcuserdatad/UserInterfaceState.xcuserstate b/btfs-ios-app/btfs-test.xcodeproj/project.xcworkspace/xcuserdata/simbadmarino.xcuserdatad/UserInterfaceState.xcuserstate index 5c3982d06a64673e76c8694ec53fecd8c029aedc..c67631abe20a7227ba522dcbeec43a4c7934ed95 100644 GIT binary patch literal 52005 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

uJuItF)9f<>eKKoV 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�++G*P9 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 diff --git a/cmd/btfs/Makefile b/cmd/btfs/Makefile index 97eb0f0..20341c6 100644 --- a/cmd/btfs/Makefile +++ b/cmd/btfs/Makefile @@ -34,27 +34,29 @@ android-armv7a: GOARCH=arm \ GOARM=7 \ CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \ - go build -buildmode=c-shared -o $(ANDROID_OUT)/armeabi-v7a/libfoo.so . + go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/armeabi-v7a/libbtfs.so . + #upx -9 -k $(ANDROID_OUT)/armeabi-v7a/libbtfs.so android-arm64: CGO_ENABLED=1 \ GOOS=android \ GOARCH=arm64 \ CC=$(NDK_BIN)/aarch64-linux-android21-clang \ - go build -buildmode=c-shared -o $(ANDROID_OUT)/arm64-v8a/libfoo.so . + go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/arm64-v8a/libbtfs.so . + #upx -9 -k $(ANDROID_OUT)/arm64-v8a/libbtfs.so android-x86: CGO_ENABLED=1 \ GOOS=android \ GOARCH=386 \ CC=$(NDK_BIN)/i686-linux-android21-clang \ - go build -buildmode=c-shared -o $(ANDROID_OUT)/x86/libfoo.so . - + go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/x86/libbtfs.so . + android-x86_64: CGO_ENABLED=1 \ GOOS=android \ GOARCH=amd64 \ CC=$(NDK_BIN)/x86_64-linux-android21-clang \ - go build -buildmode=c-shared -o $(ANDROID_OUT)/x86_64/libfoo.so . + go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/x86_64/libbtfs.so . android: android-armv7a android-arm64 android-x86 android-x86_64 diff --git a/core/commands/storage/path/path.go b/core/commands/storage/path/path.go index f21e963..06270c4 100644 --- a/core/commands/storage/path/path.go +++ b/core/commands/storage/path/path.go @@ -18,7 +18,7 @@ import ( "github.com/dustin/go-humanize" logging "github.com/ipfs/go-log" - "github.com/mitchellh/go-homedir" + "github.com/simbadMarino/go-homedir" ) const ( @@ -95,7 +95,7 @@ var PathCmd = &cmds.Command{ Tagline: "Modify the Host storage folder path for BTFS client.", ShortDescription: ` The default local repository path is located at ~/.btfs folder, in order to -improve the hard disk space usage, provide the function to change the original +improve the hard disk space usage, provide the function to change the original storage location, a specified path as a parameter need to be passed. `, }, @@ -490,6 +490,7 @@ func CheckDirEmpty(dirname string) bool { } func SetEnvVariables() { + os.Setenv("HOME","/data/data/com.justshare/files") // TODO: Make this an if only for Android if CheckExist(PropertiesFileName) { btfsPath = ReadProperties(PropertiesFileName) btfsPath = strings.Trim(btfsPath, " \n\r") diff --git a/go.mod b/go.mod index a274602..b8b16c2 100644 --- a/go.mod +++ b/go.mod @@ -121,6 +121,7 @@ require ( github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.26.0 github.com/shirou/gopsutil/v3 v3.20.12 + github.com/simbadMarino/go-homedir v0.0.0-20180124010941-45071c14e041 // indirect github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 diff --git a/go.sum b/go.sum index 6b0eef7..dc662d0 100644 --- a/go.sum +++ b/go.sum @@ -1304,6 +1304,8 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/simbadMarino/go-homedir v0.0.0-20180124010941-45071c14e041 h1:XO22+GjP6/IGDpuyttO7NgAehqQfOGubG1IYwLAppdI= +github.com/simbadMarino/go-homedir v0.0.0-20180124010941-45071c14e041/go.mod h1:+U0IPY5pvQpRr046DIzfLlHDg3rRPrFuh5U3QDrEy5Y= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= diff --git a/repo/fsrepo/misc.go b/repo/fsrepo/misc.go index 38b893b..3e9f69c 100644 --- a/repo/fsrepo/misc.go +++ b/repo/fsrepo/misc.go @@ -10,7 +10,8 @@ import ( // present, this function returns that value. Otherwise, it returns the default // repo path. func BestKnownPath() (string, error) { - btfsPath := "~/Documents/.btfs" + //btfsPath := "~/Documents/.btfs" //iOS path + btfsPath := "~/.btfs" //Android path if os.Getenv("BTFS_PATH") != "" { btfsPath = os.Getenv("BTFS_PATH") } From 584f4e9568e5876b38cf2a0aa722bbe9794a3e1b Mon Sep 17 00:00:00 2001 From: Simbad Marino Date: Sat, 17 Sep 2022 11:04:31 -0500 Subject: [PATCH 5/7] Updating BTFS to 2.2.1 --- chain/config/config.go | 2 +- chain/utils.go | 11 +- cmd/btfs/daemon.go | 113 ++++++++++++++---- cmd/btfs/main.go | 5 +- core/commands/commands_test.go | 3 + core/commands/config.go | 74 +++++++++++- core/commands/statuscontract.go | 82 +++++++++---- core/commands/storage/hosts/hosts.go | 47 +------- core/commands/storage/path/path.go | 2 +- core/commands/storage/stats/stats.go | 58 +++++++-- .../storage/upload/helper/hosts_helper.go | 50 ++++---- .../storage/upload/upload/do_waitupload.go | 16 ++- core/commands/test.go | 10 +- core/corehttp/webui.go | 3 +- core/hub/settings.go | 1 + core/hub/sync.go | 23 ++-- go.mod | 4 +- go.sum | 8 +- reportstatus/checkreport.go | 46 ------- reportstatus/reportstatus.go | 63 ++++------ settlement/swap/vault/chequestore.go | 11 +- settlement/swap/vault/vault.go | 12 +- settlement/swap/vault/vault_test.go | 7 +- spin/analytics.go | 2 + spin/analytics_online.go | 10 +- spin/hosts.go | 4 +- version.go | 2 +- 27 files changed, 403 insertions(+), 266 deletions(-) delete mode 100644 reportstatus/checkreport.go diff --git a/chain/config/config.go b/chain/config/config.go index 62dba3f..145fee8 100644 --- a/chain/config/config.go +++ b/chain/config/config.go @@ -34,7 +34,7 @@ var ( bttcTestBatchAddress = common.HexToAddress("0x0c9de531dcb38b758fe8a2c163444a5e54ee0db2") bttcTestVaultLogicAddressV1 = common.HexToAddress("0x212324b18255593AdE87597Fa37C2c582aD72d24") bttcTestVaultLogicAddress = common.HexToAddress("0x73bcbE03999913dB7229FD5dC485cf23247c58B5") // https://testnet.bttcscan.com/address/0x73bcbE03999913dB7229FD5dC485cf23247c58B5 - bttcTestStatusAddress = common.HexToAddress("0x8226b334C441095215Ae58eD9396a55a9D80bFD5") + bttcTestStatusAddress = common.HexToAddress("0x38d1fF2C2e9744273E4531FA4608eB6432c1F26A") bttcFactoryAddressV1 = common.HexToAddress("0x9AF4bEc1A30BeC47756Ecef4cf43B91592121bC9") bttcFactoryAddress = common.HexToAddress("0x763d7858287B9a33F4bE5bb3df0241dACc59BCc7") // https://bttcscan.com/address/0x763d7858287B9a33F4bE5bb3df0241dACc59BCc7 diff --git a/chain/utils.go b/chain/utils.go index b71825b..b0de2ca 100644 --- a/chain/utils.go +++ b/chain/utils.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "errors" "fmt" + config "github.com/TRON-US/go-btfs-config" "io/ioutil" "math/rand" "os" @@ -232,7 +233,7 @@ func SetReportStatusListOK(r *LevelDbReportStatusInfo) ([]*LevelDbReportStatusIn rList := make([]*LevelDbReportStatusInfo, 0) err := StateStore.Get(keyReportStatusList, &rList) if err != nil { - if err.Error() == "storage: not found" { + if errors.Is(err, storage.ErrNotFound) { init = true // continue } else { @@ -307,3 +308,11 @@ func StoreOnline(lastOnlineInfo *LastOnlineInfo) error { return nil } + +func GetOnlineServer(chainId int64) string { + if chainId == 199 { + return config.DefaultServicesConfig().OnlineServerDomain + } else { + return config.DefaultServicesConfigTestnet().OnlineServerDomain + } +} diff --git a/cmd/btfs/daemon.go b/cmd/btfs/daemon.go index 65c1fb3..0414c08 100644 --- a/cmd/btfs/daemon.go +++ b/cmd/btfs/daemon.go @@ -455,8 +455,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment err := transaction.WaitSynced(context.Background(), chainInfo.Backend, chain.MaxDelay) if err != nil { - Errorf("waiting backend sync: %w", err) - return + return fmt.Errorf("waiting backend sync: %w", err) } } @@ -473,12 +472,19 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment } // init report status contract - err = reportstatus.Init(chainInfo.TransactionService, cfg, configRoot, chainCfg.StatusAddress, chainInfo.ChainID) + reportStatusServ := reportstatus.Init(chainInfo.TransactionService, cfg, chainCfg.StatusAddress) + err = CheckExistLastOnlineReport(cfg, configRoot, chainid, reportStatusServ) if err != nil { Println("init report status, err: ", err) return err } + err = CheckHubDomainConfig(cfg, configRoot, chainid) + if err != nil { + fmt.Println("check report status, err: ", err) + return err + } + // init ip2location db if err := bindata.Init(); err != nil { // log init ip2location err @@ -552,8 +558,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment case routingOptionNoneKwd: ncfg.Routing = libp2p.NilRouterOption default: - Errorf("unrecognized routing option: %s", routingOption) - return + return fmt.Errorf("unrecognized routing option: %s", routingOption) } node, err := core.NewNode(req.Context, ncfg) @@ -726,7 +731,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error for _, addr := range apiAddrs { apiMaddr, err := ma.NewMultiaddr(addr) if err != nil { - return nil, Errorf("serveHTTPApi: invalid API address: %q (err: %s)", addr, err) + return nil, fmt.Errorf("serveHTTPApi: invalid API address: %q (err: %s)", addr, err) } if listenerAddrs[string(apiMaddr.Bytes())] { continue @@ -734,7 +739,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error apiLis, err := manet.Listen(apiMaddr) if err != nil { - return nil, Errorf("serveHTTPApi: manet.Listen(%s) failed: %s", apiMaddr, err) + return nil, fmt.Errorf("serveHTTPApi: manet.Listen(%s) failed: %s", apiMaddr, err) } listenerAddrs[string(apiMaddr.Bytes())] = true @@ -783,11 +788,11 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error node, err := cctx.ConstructNode() if err != nil { - return nil, Errorf("serveHTTPApi: ConstructNode() failed: %s", err) + return nil, fmt.Errorf("serveHTTPApi: ConstructNode() failed: %s", err) } if err := node.Repo.SetAPIAddr(listeners[0].Multiaddr()); err != nil { - return nil, Errorf("serveHTTPApi: SetAPIAddr() failed: %s", err) + return nil, fmt.Errorf("serveHTTPApi: SetAPIAddr() failed: %s", err) } errc := make(chan error) @@ -843,14 +848,14 @@ func getChainID(req *cmds.Request, cfg *config.Config, stateStorer storage.State // compare cfg chain id and leveldb chain id if storeChainid != cfgChainId { return 0, stored, errors.New( - Sprintf("current chainId=%d is different from config chainId=%d, "+ + fmt.Sprintf("current chainId=%d is different from config chainId=%d, "+ "you can not change chain id in config file", storeChainid, cfgChainId)) } // compare input chain id and leveldb chain id if inputChainId > 0 && storeChainid != inputChainId { return 0, stored, errors.New( - Sprintf("current chainId=%d is different from input chainId=%d, "+ + fmt.Sprintf("current chainId=%d is different from input chainId=%d, "+ "you can not change chain id with --chain-id when node start", storeChainid, inputChainId)) } @@ -902,7 +907,7 @@ func printSwarmAddrs(node *core.IpfsNode) { func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { - return nil, Errorf("serveHTTPGateway: GetConfig() failed: %s", err) + return nil, fmt.Errorf("serveHTTPGateway: GetConfig() failed: %s", err) } writable, writableOptionFound := req.Options[writableKwd].(bool) @@ -912,7 +917,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e listeners, err := sockets.TakeListeners("io.ipfs.gateway") if err != nil { - return nil, Errorf("serveHTTPGateway: socket activation failed: %s", err) + return nil, fmt.Errorf("serveHTTPGateway: socket activation failed: %s", err) } listenerAddrs := make(map[string]bool, len(listeners)) @@ -924,7 +929,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e for _, addr := range gatewayAddrs { gatewayMaddr, err := ma.NewMultiaddr(addr) if err != nil { - return nil, Errorf("serveHTTPGateway: invalid gateway address: %q (err: %s)", addr, err) + return nil, fmt.Errorf("serveHTTPGateway: invalid gateway address: %q (err: %s)", addr, err) } if listenerAddrs[string(gatewayMaddr.Bytes())] { @@ -933,7 +938,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e gwLis, err := manet.Listen(gatewayMaddr) if err != nil { - return nil, Errorf("serveHTTPGateway: manet.Listen(%s) failed: %s", gatewayMaddr, err) + return nil, fmt.Errorf("serveHTTPGateway: manet.Listen(%s) failed: %s", gatewayMaddr, err) } listenerAddrs[string(gatewayMaddr.Bytes())] = true listeners = append(listeners, gwLis) @@ -971,7 +976,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e node, err := cctx.ConstructNode() if err != nil { - return nil, Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) + return nil, fmt.Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) } errc := make(chan error) @@ -996,11 +1001,11 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { - return nil, Errorf("serveHTTPRemoteApi: GetConfig() failed: %s", err) + return nil, fmt.Errorf("serveHTTPRemoteApi: GetConfig() failed: %s", err) } if !cfg.Experimental.Libp2pStreamMounting { - return nil, Errorf("serveHTTPRemoteApi: libp2p stream mounting must be enabled") + return nil, fmt.Errorf("serveHTTPRemoteApi: libp2p stream mounting must be enabled") } rapiAddrs := cfg.Addresses.RemoteAPI @@ -1008,16 +1013,16 @@ func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, for _, addr := range rapiAddrs { rapiMaddr, err := ma.NewMultiaddr(addr) if err != nil { - return nil, Errorf("serveHTTPRemoteApi: invalid remote api address: %q (err: %s)", addr, err) + return nil, fmt.Errorf("serveHTTPRemoteApi: invalid remote api address: %q (err: %s)", addr, err) } rapiLis, err := manet.Listen(rapiMaddr) if err != nil { - return nil, Errorf("serveHTTPRemoteApi: manet.Listen(%s) failed: %s", rapiMaddr, err) + return nil, fmt.Errorf("serveHTTPRemoteApi: manet.Listen(%s) failed: %s", rapiMaddr, err) } // we might have listened to /tcp/0 - lets see what we are listing on rapiMaddr = rapiLis.Multiaddr() - Printf("Remote API server listening on %s\n", rapiMaddr) + fmt.Printf("Remote API server listening on %s\n", rapiMaddr) listeners = append(listeners, rapiLis) } @@ -1031,13 +1036,13 @@ func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, node, err := cctx.ConstructNode() if err != nil { - return nil, Errorf("serveHTTPRemoteApi: ConstructNode() failed: %s", err) + return nil, fmt.Errorf("serveHTTPRemoteApi: ConstructNode() failed: %s", err) } // set default listener to remote api endpoint if _, err := node.P2P.ForwardRemote(node.Context(), httpremote.P2PRemoteCallProto, listeners[0].Multiaddr(), false); err != nil { - return nil, Errorf("serveHTTPRemoteApi: ForwardRemote() failed: %s", err) + return nil, fmt.Errorf("serveHTTPRemoteApi: ForwardRemote() failed: %s", err) } errc := make(chan error) @@ -1062,7 +1067,7 @@ func serveHTTPRemoteApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error { cfg, err := cctx.GetConfig() if err != nil { - return Errorf("mountFuse: GetConfig() failed: %s", err) + return fmt.Errorf("mountFuse: GetConfig() failed: %s", err) } fsdir, found := req.Options[ipfsMountKwd].(string) @@ -1077,7 +1082,7 @@ func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error { node, err := cctx.ConstructNode() if err != nil { - return Errorf("mountFuse: ConstructNode() failed: %s", err) + return fmt.Errorf("mountFuse: ConstructNode() failed: %s", err) } err = nodeMount.Mount(node, fsdir, nsdir) @@ -1309,3 +1314,61 @@ func doIfNeedUpgradeFactoryToV2(chainid int64, chainCfg *chainconfig.ChainConfig Println("will re-deploy a vault contract for you") return } + +// CheckExistLastOnlineReport sync conf and lastOnlineInfo +func CheckExistLastOnlineReport(cfg *config.Config, configRoot string, chainId int64, reportStatusServ reportstatus.Service) error { + lastOnline, err := chain.GetLastOnline() + if err != nil { + return err + } + + // if nil, set config online status config + if lastOnline == nil { + var reportOnline bool + var reportStatusContract bool + if cfg.Experimental.StorageHostEnabled { + reportOnline = true + reportStatusContract = true + } + + var onlineServerDomain string + if chainId == 199 { + onlineServerDomain = config.DefaultServicesConfig().OnlineServerDomain + } else { + onlineServerDomain = config.DefaultServicesConfigTestnet().OnlineServerDomain + } + + err = commands.SyncConfigOnlineCfg(configRoot, onlineServerDomain, reportOnline, reportStatusContract) + if err != nil { + return err + } + } + + // if nil, set last online info + if lastOnline == nil { + err = reportStatusServ.CheckLastOnlineInfo(cfg.Identity.PeerID, cfg.Identity.BttcAddr) + if err != nil { + return err + } + } + return nil +} + +// CheckExistLastOnlineReport sync conf and lastOnlineInfo +func CheckHubDomainConfig(cfg *config.Config, configRoot string, chainId int64) error { + var hubServerDomain string + if chainId == 199 { + hubServerDomain = config.DefaultServicesConfig().HubDomain + } else { + hubServerDomain = config.DefaultServicesConfigTestnet().HubDomain + } + + if hubServerDomain != cfg.Services.HubDomain { + err := commands.SyncHubDomainConfig(configRoot, hubServerDomain) + if err != nil { + return err + } + } + + return nil +} diff --git a/cmd/btfs/main.go b/cmd/btfs/main.go index abb65ad..c5b6b67 100644 --- a/cmd/btfs/main.go +++ b/cmd/btfs/main.go @@ -84,10 +84,7 @@ func mainC(in *C.char) *C.char { return C.CString("exit code:" + strconv.Itoa(exitCode)) } -//export mainCMod -func mainCMod(in *C.char) *C.char { - return C.CString(C.GoString(in)) -} + func mainRet(args []string) int { rand.Seed(time.Now().UnixNano()) diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 8dbc18b..7f05daa 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -91,6 +91,7 @@ func TestCommands(t *testing.T) { "/config", "/config/edit", "/config/replace", + "/config/reset", "/config/show", //"/config/profile", //"/config/profile/apply", @@ -328,6 +329,8 @@ func TestCommands(t *testing.T) { "/statuscontract/reportlist", "/statuscontract/lastinfo", "/statuscontract/config", + "/statuscontract/report_online_server", + "/statuscontract/report_status_contract", } cmdSet := make(map[string]struct{}) diff --git a/core/commands/config.go b/core/commands/config.go index 91625b5..7135ad5 100644 --- a/core/commands/config.go +++ b/core/commands/config.go @@ -64,6 +64,7 @@ Set the value of the 'Datastore.Path' key: }, Subcommands: map[string]*cmds.Command{ "show": configShowCmd, + "reset": resetConfigCmd, "edit": configEditCmd, "replace": configReplaceCmd, //"profile": configProfileCmd, @@ -88,7 +89,7 @@ Set the value of the 'Datastore.Path' key: // This is a temporary fix until we move the private key out of the config file switch strings.ToLower(key) { - case "identity", "identity.privkey", "identity.mnemonic", "identity.peerid": + case "identity", "identity.privkey", "identity.hexprivkey", "identity.mnemonic": return fmt.Errorf("cannot show or change %s through API", key) default: } @@ -188,7 +189,7 @@ NOTE: For security reasons, this command will omit your private key. If you woul return err } - for _, k := range []string{config.PrivKeyTag, config.MnemonicTag} { + for _, k := range []string{config.PrivKeyTag, config.MnemonicTag, "HexPrivKey"} { err = scrubValue(cfg, []string{config.IdentityTag, k}) if err != nil { return err @@ -210,6 +211,55 @@ NOTE: For security reasons, this command will omit your private key. If you woul }, } +var resetConfigCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Output config file contents.", + ShortDescription: ` +NOTE: For security reasons, this command will omit your private key. If you would like to make a full backup of your config (private key included), you must copy the config file from your repo. +`, + }, + Type: map[string]interface{}{}, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + // var output *ConfigField + + cfgRoot, err := cmdenv.GetConfigRoot(env) + if err != nil { + return err + } + r, err := fsrepo.Open(cfgRoot) + if err != nil { + return err + } + defer r.Close() + defaultMap := make(map[string]interface{}) + defaultMap["Experimental.StorageClientEnabled"] = true + defaultMap["Experimental.StorageHostEnabled"] = true + defaultMap["Experimental.ReportOnline"] = true + defaultMap["Experimental.ReportStatusContract"] = true + defaultMap["ChainInfo.Endpoint"] = "https://rpc.bt.io/" + + for k, v := range defaultMap { + _, err = setConfig(r, k, v) + if err != nil { + return err + } + } + + return cmds.EmitOnce(res, defaultMap) + }, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *map[string]interface{}) error { + buf, err := config.HumanOutput(out) + if err != nil { + return err + } + buf = append(buf, byte('\n')) + _, err = w.Write(buf) + return err + }), + }, +} + func scrubValue(m map[string]interface{}, key []string) error { find := func(m map[string]interface{}, k string) (string, interface{}, bool) { lckey := strings.ToLower(k) @@ -692,6 +742,26 @@ func SyncConfigOnlineCfg(configRoot string, onlineServerDomain string, reportOnl return nil } +func SyncHubDomainConfig(configRoot string, hubServerDomain string) error { + r, err := fsrepo.Open(configRoot) + if err != nil { + return err + } + defer r.Close() + + cfg, err := r.Config() + if err != nil { + return err + } + cfg.Services.HubDomain = hubServerDomain + + err = r.SetConfig(cfg) + if err != nil { + return err + } + return nil +} + func SetConfigStorageHostEnable(configRoot string, enable bool) error { r, err := fsrepo.Open(configRoot) if err != nil { diff --git a/core/commands/statuscontract.go b/core/commands/statuscontract.go index bf4556f..6152e38 100644 --- a/core/commands/statuscontract.go +++ b/core/commands/statuscontract.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/bittorrent/go-btfs/core/commands/cmdenv" "io" "math/big" "strconv" @@ -12,6 +11,9 @@ import ( cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/chain" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" + "github.com/bittorrent/go-btfs/reportstatus" + "github.com/bittorrent/go-btfs/spin" ) var StatusContractCmd = &cmds.Command{ @@ -21,10 +23,12 @@ var StatusContractCmd = &cmds.Command{ report status-contract cmd, total cmd and list cmd.`, }, Subcommands: map[string]*cmds.Command{ - "total": TotalCmd, - "reportlist": ReportListCmd, - "lastinfo": LastInfoCmd, - "config": StatusConfigCmd, + "total": TotalCmd, + "reportlist": ReportListCmd, + "lastinfo": LastInfoCmd, + "config": StatusConfigCmd, + "report_online_server": ReportOnlineServerCmd, + "report_status_contract": ReportStatusContractCmd, }, } @@ -137,29 +141,22 @@ var ReportListCmd = &cmds.Command{ if list == nil { return nil } - // - //from := 0 - //limit := 10 - - From := len(list) - 1 - from - limit - if From <= 0 { - From = 0 + total := len(list) + // order by time desc + for i, j := 0, total-1; i < j; i, j = i+1, j-1 { + list[i], list[j] = list[j], list[i] } - To := len(list) - 1 - from - if To > len(list)-1 { - To = len(list) - 1 - } - fmt.Println("From, To = ", From, To) - - s := list[From:To] - l := len(s) - for i, j := 0, l-1; i < j; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] + if from < total { + if (from + limit) <= len(list) { + list = list[from : from+limit] + } else { + list = list[from:] + } } return cmds.EmitOnce(res, &ReportListCmdRet{ - Records: s, - Total: len(list), + Records: list, + Total: total, PeerId: peerId, }) }, @@ -233,3 +230,40 @@ var StatusConfigCmd = &cmds.Command{ }), }, } + +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 { + 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!") + }, +} + +var ReportStatusContractCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "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!") + }, +} diff --git a/core/commands/storage/hosts/hosts.go b/core/commands/storage/hosts/hosts.go index b9cbe99..d90c55a 100644 --- a/core/commands/storage/hosts/hosts.go +++ b/core/commands/storage/hosts/hosts.go @@ -2,18 +2,14 @@ package hosts import ( "context" - "encoding/json" "fmt" - "math/rand" - "time" - cmds "github.com/bittorrent/go-btfs-cmds" "github.com/bittorrent/go-btfs/core" "github.com/bittorrent/go-btfs/core/commands/cmdenv" "github.com/bittorrent/go-btfs/core/commands/storage/helper" "github.com/bittorrent/go-btfs/core/hub" - "github.com/pkg/errors" - "github.com/prometheus/common/log" + + cmds "github.com/bittorrent/go-btfs-cmds" hubpb "github.com/tron-us/go-btfs-common/protos/hub" logging "github.com/ipfs/go-log" @@ -112,7 +108,7 @@ Mode options include:` + hub.AllModeHelpText, if err != nil { return err } - _, err = SyncHostsMixture(req.Context, n, mode) + _, err = SyncHosts(req.Context, n, mode) return err }, } @@ -128,40 +124,3 @@ func SyncHosts(ctx context.Context, node *core.IpfsNode, mode string) ([]*hubpb. } return nodes, nil } - -func SyncHostsMixture(ctx context.Context, node *core.IpfsNode, mode string) ([]*hubpb.Host, error) { - if !json.Valid([]byte(mode)) { - return SyncHosts(ctx, node, mode) - } - modes := map[string]int{} - if err := json.Unmarshal([]byte(mode), &modes); err != nil { - return nil, errors.Wrap(err, "invalid mode") - } - c := 0 - preMixing := map[string][]*hubpb.Host{} - for k, v := range modes { - if hosts, err := SyncHosts(ctx, node, k); err != nil { - log.Error(err) - continue - } else { - preMixing[k] = hosts - c += v - } - } - - result := make([]*hubpb.Host, 0) - for k, v := range preMixing { - r := modes[k] * 500 / c - if r > len(v) { - r = len(v) - } - result = append(result, v[0:r]...) - } - - rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(result), func(i, j int) { result[i], result[j] = result[j], result[i] }) - if err := helper.SaveHostsIntoDatastore(ctx, node, mode, result); err != nil { - log.Error(err) - } - return result, nil -} diff --git a/core/commands/storage/path/path.go b/core/commands/storage/path/path.go index 06270c4..c17b0fa 100644 --- a/core/commands/storage/path/path.go +++ b/core/commands/storage/path/path.go @@ -95,7 +95,7 @@ var PathCmd = &cmds.Command{ Tagline: "Modify the Host storage folder path for BTFS client.", ShortDescription: ` The default local repository path is located at ~/.btfs folder, in order to -improve the hard disk space usage, provide the function to change the original +improve the hard disk space usage, provide the function to change the original storage location, a specified path as a parameter need to be passed. `, }, diff --git a/core/commands/storage/stats/stats.go b/core/commands/storage/stats/stats.go index bb16b3a..d7f0da0 100644 --- a/core/commands/storage/stats/stats.go +++ b/core/commands/storage/stats/stats.go @@ -2,6 +2,7 @@ package stats import ( "context" + "errors" "sort" "strconv" "strings" @@ -24,6 +25,7 @@ import ( const ( localInfoOnlyOptionName = "local-only" + versionOptionName = "version" ) // Storage Stats @@ -61,12 +63,12 @@ This command synchronize node stats from network(hub) to local node data store.` return err } - return SyncStats(req.Context, cfg, n, env) + return SyncStats(req.Context, cfg, n, env, true) }, } -func SyncStats(ctx context.Context, cfg *config.Config, node *core.IpfsNode, env cmds.Environment) error { - sr, err := hub.QueryStats(ctx, node) +func SyncStats(ctx context.Context, cfg *config.Config, node *core.IpfsNode, env cmds.Environment, v2 bool) error { + sr, err := hub.QueryStats(ctx, node, v2) if err != nil { return err } @@ -93,6 +95,34 @@ func SyncStats(ctx context.Context, cfg *config.Config, node *core.IpfsNode, env return SaveHostStatsIntoDatastore(ctx, node, node.Identity.Pretty(), hs) } +func GetNowStats(ctx context.Context, cfg *config.Config, node *core.IpfsNode, env cmds.Environment, V2 bool) (hs *nodepb.StorageStat_Host, err error) { + sr, err := hub.QueryStats(ctx, node, V2) + if err != nil { + return nil, err + } + stat, err := corerepo.RepoStat(ctx, node) + if err != nil { + return nil, err + } + //cfgRoot, err := cmdenv.GetConfigRoot(env) + //if err != nil { + // return nil, err + //} + //du, err := disk.UsageWithContext(ctx, cfgRoot) + //if err != nil { + // return nil, err + //} + hs = &nodepb.StorageStat_Host{ + Online: cfg.Experimental.StorageHostEnabled, + StorageUsed: int64(stat.RepoSize), + StorageCap: int64(stat.StorageMax), + StorageDiskTotal: int64(100000000000), + StorageDiskAvailable: int64(100000000000), + } + hs.StorageStat_HostStats = sr.StorageStat_HostStats + return hs, nil +} + // sub-commands: btfs storage stats info var storageStatsInfoCmd = &cmds.Command{ Helptext: cmds.HelpText{ @@ -103,6 +133,7 @@ This command get node stats in the network from the local node data store.`, Arguments: []cmds.Argument{}, Options: []cmds.Option{ cmds.BoolOption(localInfoOnlyOptionName, "l", "Return only the locally available disk stats without querying/returning the network stats.").WithDefault(false), + cmds.IntOption(versionOptionName, "v", "Get new hub score level.").WithDefault(2), }, RunTimeout: 30 * time.Second, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { @@ -116,15 +147,20 @@ This command get node stats in the network from the local node data store.`, return err } - local, _ := req.Options[localInfoOnlyOptionName].(bool) - var hs *nodepb.StorageStat_Host - if !local { - hs, err = GetHostStatsFromDatastore(req.Context, n, n.Identity.Pretty()) - if err != nil { - return err - } + var v2Flag bool + v, _ := req.Options[versionOptionName].(int) + if v == 1 { + v2Flag = false + } else if v == 2 { + v2Flag = true } else { - hs = &nodepb.StorageStat_Host{} + return errors.New("version should be 1 or 2, not other. ") + } + + var hs *nodepb.StorageStat_Host + hs, err = GetNowStats(req.Context, cfg, n, env, v2Flag) + if err != nil { + return err } // Refresh latest repo stats diff --git a/core/commands/storage/upload/helper/hosts_helper.go b/core/commands/storage/upload/helper/hosts_helper.go index 3c33146..a1d92b3 100644 --- a/core/commands/storage/upload/helper/hosts_helper.go +++ b/core/commands/storage/upload/helper/hosts_helper.go @@ -8,7 +8,7 @@ import ( "sync" "time" - // "github.com/bittorrent/go-btfs/chain" + "github.com/bittorrent/go-btfs/chain" "github.com/bittorrent/go-btfs/core/commands/storage/helper" "github.com/bittorrent/go-btfs/core/corehttp/remote" @@ -36,10 +36,10 @@ type CustomizedHostsProvider struct { } func (p *CustomizedHostsProvider) NextValidHost() (string, error) { - //myPeerId, err := peer.IDB58Decode(p.cp.Cfg.Identity.PeerID) - //if err != nil { - // return "", err - //} + myPeerId, err := peer.IDB58Decode(p.cp.Cfg.Identity.PeerID) + if err != nil { + return "", err + } for true { if index, err := p.AddIndex(); err == nil { @@ -48,10 +48,10 @@ func (p *CustomizedHostsProvider) NextValidHost() (string, error) { continue } // If my vault is not compatible with the host's one, skip - //isVaultCompatible, err := chain.SettleObject.Factory.IsVaultCompatibleBetween(p.cp.Ctx, myPeerId, id) - //if err != nil || !isVaultCompatible { - // continue - //} + isVaultCompatible, err := chain.SettleObject.Factory.IsVaultCompatibleBetween(p.cp.Ctx, myPeerId, id) + if err != nil || !isVaultCompatible { + continue + } if err := p.cp.Api.Swarm().Connect(p.cp.Ctx, peer.AddrInfo{ID: id}); err != nil { p.hosts = append(p.hosts, p.hosts[index]) continue @@ -169,10 +169,10 @@ func (p *HostsProvider) AddIndex() (int, error) { } func (p *HostsProvider) PickFromBackupHosts() (string, error) { - //myPeerId, err := peer.IDB58Decode(p.cp.Cfg.Identity.PeerID) - //if err != nil { - // return "", err - // } + myPeerId, err := peer.IDB58Decode(p.cp.Cfg.Identity.PeerID) + if err != nil { + return "", err + } for true { host, err := func() (string, error) { @@ -196,10 +196,10 @@ func (p *HostsProvider) PickFromBackupHosts() (string, error) { continue } // If my vault is not compatible with the host's one, skip - //isVaultCompatible, err := chain.SettleObject.Factory.IsVaultCompatibleBetween(p.ctx, myPeerId, id) - //if err != nil || !isVaultCompatible { - // continue - //} + isVaultCompatible, err := chain.SettleObject.Factory.IsVaultCompatibleBetween(p.ctx, myPeerId, id) + if err != nil || !isVaultCompatible { + continue + } if err := p.cp.Api.Swarm().Connect(ctx, peer.AddrInfo{ID: id}); err != nil { continue } @@ -227,10 +227,10 @@ func (p *HostsProvider) PickFromBackupHosts() (string, error) { } func (p *HostsProvider) NextValidHost() (string, error) { - //myPeerId, err := peer.IDB58Decode(p.cp.Cfg.Identity.PeerID) - //if err != nil { - // return "", err - //} + myPeerId, err := peer.IDB58Decode(p.cp.Cfg.Identity.PeerID) + if err != nil { + return "", err + } endOfBackup := false LOOP: @@ -260,10 +260,10 @@ LOOP: continue } // If my vault is not compatible with the host's one, skip - //isVaultCompatible, err := chain.SettleObject.Factory.IsVaultCompatibleBetween(p.ctx, myPeerId, id) - //if err != nil || !isVaultCompatible { - // continue - //} + isVaultCompatible, err := chain.SettleObject.Factory.IsVaultCompatibleBetween(p.ctx, myPeerId, id) + if err != nil || !isVaultCompatible { + continue + } ctx, _ := context.WithTimeout(p.ctx, 3*time.Second) if err := p.cp.Api.Swarm().Connect(ctx, peer.AddrInfo{ID: id}); err != nil { p.Lock() diff --git a/core/commands/storage/upload/upload/do_waitupload.go b/core/commands/storage/upload/upload/do_waitupload.go index 21035f3..381e7fd 100644 --- a/core/commands/storage/upload/upload/do_waitupload.go +++ b/core/commands/storage/upload/upload/do_waitupload.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "math" - "sync" "time" "github.com/bittorrent/go-btfs/core/commands/storage/upload/helper" @@ -136,11 +135,10 @@ func waitUpload(rss *sessions.RenterSession, offlineSigning bool, fsStatus *guar } // pay in cheque - var wg sync.WaitGroup - wg.Add(1) if err := rss.To(sessions.RssToPayEvent); err != nil { return err } + var errC = make(chan error) go func() { err = func() error { return payInCheque(rss) @@ -149,10 +147,16 @@ func waitUpload(rss *sessions.RenterSession, offlineSigning bool, fsStatus *guar fmt.Println("payInCheque error:", err) } fmt.Println("payInCheque done") - wg.Done() + errC <- err }() - wg.Wait() - + err = <-errC + if err != nil { + if fsmErr := rss.To(sessions.RssToErrorEvent); fsmErr != nil { + log.Errorf("fsm transfer error:%v", fsmErr) + } + log.Errorf("payInCheque error:%v", err) + return err + } // Complete if err := rss.To(sessions.RssToCompleteEvent); err != nil { return err diff --git a/core/commands/test.go b/core/commands/test.go index f40810e..d08f50d 100644 --- a/core/commands/test.go +++ b/core/commands/test.go @@ -74,7 +74,14 @@ var testHostsCmd = &cmds.Command{ if err != nil { return err } - fmt.Println("get hosts: ", nodes) + + fmt.Println("len(nodes) = ", len(nodes)) + for i := range nodes { + fmt.Printf("nodes[%d] = %+v", i, nodes[i]) + if i >= 5 { + break + } + } return cmds.EmitOnce(res, &TestOutput{"get hosts ok"}) }, @@ -109,7 +116,6 @@ func getHosts(req *cmds.Request, env cmds.Environment) ([]*hubpb.Host, error) { if err != nil { return nil, err } - return nodes, nil } diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index ca4a4b1..fd19c6d 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,10 +1,11 @@ package corehttp -const WebUIPath = "/btfs/QmXsvmvTTzciHEdbDCCMo55MfrEQ6qnct8B4Wt9aJwHoMY" // v2.2.0 +const WebUIPath = "/btfs/QmaK77EYUHxKweLFvRY8gbcMTx2qEb7p4S5aWPN6EHX7T1" // v2.2.1 // this is a list of all past webUI paths. var WebUIPaths = []string{ WebUIPath, + "/btfs/QmXsvmvTTzciHEdbDCCMo55MfrEQ6qnct8B4Wt9aJwHoMY", // v2.2.0 "/btfs/QmZM3CcoWPHiu9E8ugS76a2csooKEAp5YQitgjQC849h4b", // v2.1.3 "/btfs/QmW3VGCuvfhAJcJZRYQeEjJnjQG27kHNhBasrF2TwGniTT", // v2.1.2 "/btfs/QmWDZ94ZMAjts3WSPbFdLUbfLMYbygJR7BNEygVJqxuqfw", // v2.1.1 diff --git a/core/hub/settings.go b/core/hub/settings.go index 9ecf498..d6ace4c 100644 --- a/core/hub/settings.go +++ b/core/hub/settings.go @@ -15,6 +15,7 @@ func GetHostSettings(ctx context.Context, addr, peerId string) (*nodepb.Node_Set err := grpc.HubQueryClient(addr).WithContext(ctx, func(ctx context.Context, client hubpb.HubQueryServiceClient) error { req := new(hubpb.SettingsReq) req.Id = peerId + req.NewVersion = hubpb.HubRouter_V2 resp, err := client.GetSettings(ctx, req) if err != nil { return err diff --git a/core/hub/sync.go b/core/hub/sync.go index 404ca53..ff8cd2f 100644 --- a/core/hub/sync.go +++ b/core/hub/sync.go @@ -2,7 +2,6 @@ package hub import ( "context" - "encoding/json" "fmt" "strings" @@ -31,10 +30,6 @@ const ( // if valid, and if local is true and mode is empty, return prefix for storing such // information into local datastore. func CheckValidMode(mode string, local bool) (hubpb.HostsReq_Mode, string, error) { - if json.Valid([]byte(mode)) { - return -1, "mixture", nil - } - if mode == HubModeAll && local { return -1, "", nil } @@ -61,9 +56,10 @@ func QueryHosts(ctx context.Context, node *core.IpfsNode, mode string) ([]*hubpb err = grpc.HubQueryClient(config.Services.HubDomain).WithContext(ctx, func(ctx context.Context, client hubpb.HubQueryServiceClient) error { resp, err = client.GetHosts(ctx, &hubpb.HostsReq{ - Id: node.Identity.Pretty(), - Mode: hrm, - Version: version.CurrentVersionNumber, + Id: node.Identity.Pretty(), + Mode: hrm, + Version: version.CurrentVersionNumber, + NewVersion: hubpb.HubRouter_V2, }) if err != nil { return err @@ -81,16 +77,23 @@ func QueryHosts(ctx context.Context, node *core.IpfsNode, mode string) ([]*hubpb } // QueryStats queries the BTFS-Hub to retrieve the latest storage stats on this host. -func QueryStats(ctx context.Context, node *core.IpfsNode) (*hubpb.StatsResp, error) { +func QueryStats(ctx context.Context, node *core.IpfsNode, v2 bool) (*hubpb.StatsResp, error) { config, err := node.Repo.Config() if err != nil { return nil, err } + + newVersion := hubpb.HubRouter_V2 + if !v2 { + newVersion = hubpb.HubRouter_V1 + } + var resp *hubpb.StatsResp err = grpc.HubQueryClient(config.Services.HubDomain).WithContext(ctx, func(ctx context.Context, client hubpb.HubQueryServiceClient) error { resp, err = client.GetStats(ctx, &hubpb.StatsReq{ - Id: node.Identity.Pretty(), + Id: node.Identity.Pretty(), + NewVersion: newVersion, }) if err != nil { return err diff --git a/go.mod b/go.mod index b8b16c2..57b63b2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/TRON-US/go-btfs-api v0.3.0 github.com/TRON-US/go-btfs-chunker v0.3.0 - github.com/TRON-US/go-btfs-config v0.11.10 + github.com/TRON-US/go-btfs-config v0.11.11 github.com/TRON-US/go-btfs-files v0.2.0 github.com/TRON-US/go-btfs-pinner v0.1.1 github.com/TRON-US/go-btns v0.1.1 @@ -126,7 +126,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 github.com/thedevsaddam/gojsonq/v2 v2.5.2 - github.com/tron-us/go-btfs-common v0.8.9 + github.com/tron-us/go-btfs-common v0.8.10 github.com/tron-us/go-common/v2 v2.3.0 github.com/tron-us/protobuf v1.3.7 github.com/tyler-smith/go-bip32 v0.0.0-20170922074101-2c9cfd177564 diff --git a/go.sum b/go.sum index dc662d0..f02ab49 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/TRON-US/go-btfs-api v0.3.0/go.mod h1:surmr8ztnpbVY7y2H7dbb7npNXfdaV0U github.com/TRON-US/go-btfs-chunker v0.3.0 h1:06/rAYKtC3BQRYVxMtEKvqFFHhSB+XKcqt3JZ0CjD4o= github.com/TRON-US/go-btfs-chunker v0.3.0/go.mod h1:m0xvt42kqLskWsLF6SQ51AA9cqPzWoweydOcDgSDX/U= github.com/TRON-US/go-btfs-config v0.6.0/go.mod h1:82nKCMRhsgY0I8DCasIUpSr6ZP9iHLsZJSMUxytMpEw= -github.com/TRON-US/go-btfs-config v0.11.10 h1:VPxc6y6WRHxww8DAugLnxdwaeqDfauNjUnL/9ZRrQTU= -github.com/TRON-US/go-btfs-config v0.11.10/go.mod h1:9y6osJENDCjulSNJjSSt1J8OK+ADRatBdYPXRDewbko= +github.com/TRON-US/go-btfs-config v0.11.11 h1:vPMnSi5ZO/lzG/a4nQHDUXQYo+lH7tEYxCY+7EsC4r8= +github.com/TRON-US/go-btfs-config v0.11.11/go.mod h1:9y6osJENDCjulSNJjSSt1J8OK+ADRatBdYPXRDewbko= github.com/TRON-US/go-btfs-files v0.1.1/go.mod h1:tD2vOKLcLCDNMn9rrA27n2VbNpHdKewGzEguIFY+EJ0= github.com/TRON-US/go-btfs-files v0.2.0 h1:JZ+F0gX8iPmUf1OlrdOdsA8GMGxCHhwQ03jEWWEgVLE= github.com/TRON-US/go-btfs-files v0.2.0/go.mod h1:Qx+rTOIC0xl3ZkosGcEoB4hqExZmTONErPys8K5suEc= @@ -1376,8 +1376,8 @@ github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZF github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tron-us/go-btfs-common v0.2.11/go.mod h1:9ND33JahGMg52sCC2/gO5DakLsd1Pg2lVe2CihW7lBE= github.com/tron-us/go-btfs-common v0.3.7/go.mod h1:FbYoo6ZrtnJH3TKdyJTGQrUP2rbwNVATQpxplwaYQ/c= -github.com/tron-us/go-btfs-common v0.8.9 h1:6bFJ2J5XNvrawKmA8GmpNlWNDqGMiGRa7fp8JvHTlnk= -github.com/tron-us/go-btfs-common v0.8.9/go.mod h1:xnIFfbMRS5VsF948fBHPcYIeYGZkQgaJ6NIEGIPfYUs= +github.com/tron-us/go-btfs-common v0.8.10 h1:jr8DQja8H/UVH72vzxrNf2LmmmS5M4g3xrUAT7wUPVw= +github.com/tron-us/go-btfs-common v0.8.10/go.mod h1:xnIFfbMRS5VsF948fBHPcYIeYGZkQgaJ6NIEGIPfYUs= github.com/tron-us/go-common/v2 v2.0.5/go.mod h1:GiKX9noBLHotkZAU+7ET4h7N0DYWnm3OcGHOFJg1Q68= github.com/tron-us/go-common/v2 v2.1.9/go.mod h1:YIEJZF9Ph79g0zZWOvfNDtJhvO5OqSNPAb/TM1i+KvQ= github.com/tron-us/go-common/v2 v2.3.0 h1:bt7WYyOWG6NleHsBB1B/iR1m+mwe3JsaTv/HFbFgxYw= diff --git a/reportstatus/checkreport.go b/reportstatus/checkreport.go deleted file mode 100644 index 47717ba..0000000 --- a/reportstatus/checkreport.go +++ /dev/null @@ -1,46 +0,0 @@ -package reportstatus - -import ( - config "github.com/TRON-US/go-btfs-config" - "github.com/bittorrent/go-btfs/chain" - "github.com/bittorrent/go-btfs/core/commands" -) - -// CheckExistLastOnline sync conf and lastOnlineInfo -func CheckExistLastOnline(cfg *config.Config, configRoot string, chainId int64) error { - lastOnline, err := chain.GetLastOnline() - if err != nil { - return err - } - - // if nil, set config online status config - if lastOnline == nil { - var reportOnline bool - var reportStatusContract bool - if cfg.Experimental.StorageHostEnabled { - reportOnline = true - reportStatusContract = true - } - - var onlineServerDomain string - if chainId == 199 { - onlineServerDomain = config.DefaultServicesConfig().OnlineServerDomain - } else { - onlineServerDomain = config.DefaultServicesConfigTestnet().OnlineServerDomain - } - - err = commands.SyncConfigOnlineCfg(configRoot, onlineServerDomain, reportOnline, reportStatusContract) - if err != nil { - return err - } - } - - // if nil, set last online info - if lastOnline == nil { - err = serv.checkLastOnlineInfo(cfg.Identity.PeerID, cfg.Identity.BttcAddr) - if err != nil { - return err - } - } - return nil -} diff --git a/reportstatus/reportstatus.go b/reportstatus/reportstatus.go index 7eaa7ab..b302ee0 100644 --- a/reportstatus/reportstatus.go +++ b/reportstatus/reportstatus.go @@ -4,7 +4,6 @@ import ( "context" "encoding/hex" "fmt" - onlinePb "github.com/tron-us/go-btfs-common/protos/online" "math/big" "strings" "time" @@ -14,6 +13,7 @@ import ( "github.com/bittorrent/go-btfs/reportstatus/abi" "github.com/bittorrent/go-btfs/transaction" "github.com/ethereum/go-ethereum/common" + onlinePb "github.com/tron-us/go-btfs-common/protos/online" logging "github.com/ipfs/go-log" ) @@ -23,6 +23,8 @@ var log = logging.Logger("report-status-contract:") var ( statusABI = transaction.ParseABIUnchecked(abi.StatusHeartABI) serv *service + + startTime = time.Now() ) const ( @@ -30,14 +32,8 @@ const ( //ReportStatusTime = 60 * time.Second // 10 * time.Minute ) -func Init(transactionService transaction.Service, cfg *config.Config, configRoot string, statusAddress common.Address, chainId int64) error { - New(statusAddress, transactionService, cfg) - - err := CheckExistLastOnline(cfg, configRoot, chainId) - if err != nil { - return err - } - return nil +func Init(transactionService transaction.Service, cfg *config.Config, statusAddress common.Address) Service { + return New(statusAddress, transactionService, cfg) } func isReportStatusEnabled(cfg *config.Config) bool { @@ -55,6 +51,9 @@ type Service interface { // CheckReportStatus check report status heart info to statusContract CheckReportStatus() error + + // CheckLastOnlineInfo check last online info. + CheckLastOnlineInfo(peerId, bttcAddr string) error } func New(statusAddress common.Address, transactionService transaction.Service, cfg *config.Config) Service { @@ -139,15 +138,6 @@ func (s *service) ReportStatus() (common.Hash, error) { if err != nil { return common.Hash{}, err } - - // WaitForReceipt takes long time - go func() { - defer func() { - if r := recover(); r != nil { - log.Errorf("ReportHeartStatus recovered:%+v", err) - } - }() - }() return txHash, nil } @@ -161,8 +151,17 @@ func getGasPrice(request *transaction.TxRequest) *big.Int { return gasPrice } -// report heart status -func (s *service) checkLastOnlineInfo(peerId, bttcAddr string) error { +func CmdReportStatus() error { + _, err := serv.ReportStatus() + if err != nil { + log.Errorf("ReportStatus err:%+v", err) + return err + } + return nil +} + +// CheckLastOnlineInfo report heart status +func (s *service) CheckLastOnlineInfo(peerId, bttcAddr string) error { callData, err := statusABI.Pack("getStatus", peerId) if err != nil { return err @@ -203,15 +202,6 @@ func (s *service) checkLastOnlineInfo(peerId, bttcAddr string) error { return err } } - - // WaitForReceipt takes long time - go func() { - defer func() { - if r := recover(); r != nil { - log.Errorf("getStatus recovered:%+v", err) - } - }() - }() return nil } @@ -240,15 +230,6 @@ func (s *service) genHashExt(ctx context.Context) (common.Hash, error) { if err != nil { return common.Hash{}, err } - - // WaitForReceipt takes long time - go func() { - defer func() { - if r := recover(); r != nil { - log.Errorf("genHashExt recovered:%+v", err) - } - }() - }() return common.Hash{}, nil } @@ -274,10 +255,15 @@ func cycleCheckReport() { report, err := chain.GetReportStatus() //fmt.Printf("... ReportStatus, CheckReportStatus report: %+v err:%+v \n", report, err) if err != nil { + log.Errorf("GetReportStatus err:%+v", err) continue } now := time.Now() + if now.Sub(startTime) < 2*time.Hour { + continue + } + nowUnixMod := now.Unix() % 86400 // report only 1 hour every, and must after 10 hour. if nowUnixMod > report.ReportStatusSeconds && @@ -286,6 +272,7 @@ func cycleCheckReport() { err = serv.CheckReportStatus() if err != nil { + log.Errorf("CheckReportStatus err:%+v", err) continue } } diff --git a/settlement/swap/vault/chequestore.go b/settlement/swap/vault/chequestore.go index e47d187..4a572e3 100644 --- a/settlement/swap/vault/chequestore.go +++ b/settlement/swap/vault/chequestore.go @@ -157,14 +157,11 @@ func (s *chequeStore) ReceiveCheque(ctx context.Context, cheque *SignedCheque, p } else { lastCumulativePayout = lastReceivedCheque.CumulativePayout } - - // check this cheque is actually increasing in value + // check this cheque is actually increasing in value by local storage amount := big.NewInt(0).Sub(cheque.CumulativePayout, lastCumulativePayout) - if amount.Cmp(big.NewInt(0)) <= 0 { return nil, ErrChequeNotIncreasing } - // blockchain calls below contract := newVaultContract(cheque.Vault, s.transactionService) // this does not change for the same vault @@ -199,6 +196,12 @@ func (s *chequeStore) ReceiveCheque(ctx context.Context, cheque *SignedCheque, p return nil, ErrBouncingCheque } + // check this cheque is actually increasing in value by blockchain in case of the host migrate to another machine + // https://github.com/bittorrent/go-btfs/issues/187 + if cheque.CumulativePayout.Cmp(alreadyPaidOut) <= 0 { + return nil, ErrChequeNotIncreasing + } + // store the accepted cheque err = s.store.Put(lastReceivedChequeKey(cheque.Vault), cheque) if err != nil { diff --git a/settlement/swap/vault/vault.go b/settlement/swap/vault/vault.go index af2601f..3be20be 100644 --- a/settlement/swap/vault/vault.go +++ b/settlement/swap/vault/vault.go @@ -274,15 +274,13 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount defer s.unreserveTotalIssued(amount) var cumulativePayout *big.Int - lastCheque, err := s.LastCheque(beneficiary) + // cumulativePayout should get from blockchain rather than local storage in case of the loss of the local storage. + // https://github.com/bittorrent/go-btfs/issues/187 + alreadyPaidOut, err := s.contract.PaidOut(ctx, beneficiary) if err != nil { - if err != ErrNoCheque { - return nil, err - } - cumulativePayout = big.NewInt(0) - } else { - cumulativePayout = lastCheque.CumulativePayout + return nil, err } + cumulativePayout = alreadyPaidOut // increase cumulativePayout by amount cumulativePayout = cumulativePayout.Add(cumulativePayout, amount) diff --git a/settlement/swap/vault/vault_test.go b/settlement/swap/vault/vault_test.go index d4ada48..3e7c60a 100644 --- a/settlement/swap/vault/vault_test.go +++ b/settlement/swap/vault/vault_test.go @@ -194,7 +194,7 @@ func TestVaultIssue(t *testing.T) { store := storemock.NewStateStore() amount := big.NewInt(20) amount2 := big.NewInt(30) - expectedCumulative := big.NewInt(50) + // expectedCumulative := big.NewInt(50) sig := common.Hex2Bytes("0xffff") chequeSigner := &chequeSignerMock{} @@ -203,10 +203,13 @@ func TestVaultIssue(t *testing.T) { transactionmock.WithABICallSequence( transactionmock.ABICall(&vaultABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "totalbalance"), transactionmock.ABICall(&vaultABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"), + transactionmock.ABICall(&vaultABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary), transactionmock.ABICall(&vaultABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "totalbalance"), transactionmock.ABICall(&vaultABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"), + transactionmock.ABICall(&vaultABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary), transactionmock.ABICall(&vaultABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "totalbalance"), transactionmock.ABICall(&vaultABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"), + transactionmock.ABICall(&vaultABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", ownerAdress), ), ), address, @@ -264,7 +267,7 @@ func TestVaultIssue(t *testing.T) { expectedCheque = &vault.SignedCheque{ Cheque: vault.Cheque{ Beneficiary: beneficiary, - CumulativePayout: expectedCumulative, + CumulativePayout: amount2, Vault: address, }, Signature: sig, diff --git a/spin/analytics.go b/spin/analytics.go index 814172e..566002c 100644 --- a/spin/analytics.go +++ b/spin/analytics.go @@ -36,6 +36,7 @@ type dcWrap struct { //Server URL for data collection var ( log = logging.Logger("spin") + DC *dcWrap ) // other constants @@ -84,6 +85,7 @@ func Analytics(api iface.CoreAPI, cfgRoot string, node *core.IpfsNode, BTFSVersi dc.api = api dc.pn = new(nodepb.Node) dc.config = configuration + DC = dc if isAnalyticsEnabled(dc.config) { if dc.config.Experimental.Analytics != dc.config.Experimental.StorageHostEnabled { diff --git a/spin/analytics_online.go b/spin/analytics_online.go index 494be87..900fecb 100644 --- a/spin/analytics_online.go +++ b/spin/analytics_online.go @@ -22,7 +22,11 @@ func isReportOnlineEnabled(cfg *config.Config) bool { } func (dc *dcWrap) doSendDataOnline(ctx context.Context, config *config.Config, sm *onlinePb.ReqSignMetrics) error { - cb := cgrpc.OnlineClient(config.Services.OnlineServerDomain) + onlineService := config.Services.OnlineServerDomain + if len(onlineService) <= 0 { + onlineService = chain.GetOnlineServer(config.ChainInfo.ChainId) + } + cb := cgrpc.OnlineClient(onlineService) return cb.WithContext(ctx, func(ctx context.Context, client onlinePb.OnlineServiceClient) error { resp, err := client.UpdateSignMetrics(ctx, sm) if err != nil { @@ -55,7 +59,7 @@ func (dc *dcWrap) doSendDataOnline(ctx context.Context, config *config.Config, s }) } -func (dc *dcWrap) sendDataOnline(node *core.IpfsNode, config *config.Config) { +func (dc *dcWrap) SendDataOnline(node *core.IpfsNode, config *config.Config) { sm, errs, err := dc.doPrepDataOnline(node) if errs == nil { errs = make([]error, 0) @@ -176,7 +180,7 @@ func (dc *dcWrap) collectionAgentOnline(node *core.IpfsNode) { //fmt.Println("") //fmt.Println("--- online agent ---") - dc.sendDataOnline(node, cfg) + dc.SendDataOnline(node, cfg) } } } diff --git a/spin/hosts.go b/spin/hosts.go index 0e2fad3..3d4f357 100644 --- a/spin/hosts.go +++ b/spin/hosts.go @@ -33,7 +33,7 @@ func Hosts(node *core.IpfsNode, env cmds.Environment) { fmt.Printf("Storage host info will be synced at [%s] mode\n", m) go periodicSync(hostSyncPeriod, hostSyncTimeout+hostSortTimeout, "hosts", func(ctx context.Context) error { - _, err := hosts.SyncHostsMixture(ctx, node, m) + _, err := hosts.SyncHosts(ctx, node, m) return err }) } @@ -41,7 +41,7 @@ func Hosts(node *core.IpfsNode, env cmds.Environment) { fmt.Println("Current host stats will be synced") go periodicSync(hostStatsSyncPeriod, hostSyncTimeout, "host stats", func(ctx context.Context) error { - return stats.SyncStats(ctx, cfg, node, env) + return stats.SyncStats(ctx, cfg, node, env, true) }) fmt.Println("Current host settings will be synced") go periodicSync(hostSettingsSyncPeriod, hostSyncTimeout, "host settings", diff --git a/version.go b/version.go index c5e4b7e..9dd672a 100644 --- a/version.go +++ b/version.go @@ -4,7 +4,7 @@ package btfs var CurrentCommit string // CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "2.2.0" +const CurrentVersionNumber = "2.2.1" const ApiVersion = "/go-btfs/" + CurrentVersionNumber + "/" From da5f802a44e14d3011096977e57dbf28cb33de85 Mon Sep 17 00:00:00 2001 From: Simbad Marino Date: Tue, 20 Sep 2022 22:26:39 -0500 Subject: [PATCH 6/7] /Users/simbadmarino path updated for Android/iOS, Makefile updated --- cmd/btfs/Makefile | 18 ++++++++++-------- cmd/btfs/main.go | 9 +++++++++ core/commands/storage/path/path.go | 10 +++++++--- core/hub/settings_test.go | 3 +++ repo/fsrepo/misc.go | 10 +++++++--- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/cmd/btfs/Makefile b/cmd/btfs/Makefile index 20341c6..493f958 100644 --- a/cmd/btfs/Makefile +++ b/cmd/btfs/Makefile @@ -26,37 +26,39 @@ ios-x86_64: ios: ios-arm64 ios-x86_64 lipo $(IOS_OUT)/x86_64.a $(IOS_OUT)/arm64.a -create -output $(IOS_OUT)/btfs.a cp $(IOS_OUT)/arm64.h $(IOS_OUT)/btfs.h - + android-armv7a: CGO_ENABLED=1 \ GOOS=android \ GOARCH=arm \ GOARM=7 \ + CGO_CFLAGS="-fembed-bitcode" \ CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \ - go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/armeabi-v7a/libbtfs.so . - #upx -9 -k $(ANDROID_OUT)/armeabi-v7a/libbtfs.so + go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/armeabi-v7a/libbtfs.so . + android-arm64: CGO_ENABLED=1 \ GOOS=android \ GOARCH=arm64 \ + CGO_CFLAGS="-fembed-bitcode" \ CC=$(NDK_BIN)/aarch64-linux-android21-clang \ - go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/arm64-v8a/libbtfs.so . - #upx -9 -k $(ANDROID_OUT)/arm64-v8a/libbtfs.so + go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/arm64-v8a/libbtfs.so . + android-x86: CGO_ENABLED=1 \ GOOS=android \ GOARCH=386 \ CC=$(NDK_BIN)/i686-linux-android21-clang \ - go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/x86/libbtfs.so . - + go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/x86/libbtfs.so . + android-x86_64: CGO_ENABLED=1 \ GOOS=android \ GOARCH=amd64 \ CC=$(NDK_BIN)/x86_64-linux-android21-clang \ - go build -buildmode=c-shared -ldflags="-s -w" -o $(ANDROID_OUT)/x86_64/libbtfs.so . + go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/x86_64/libbtfs.so . android: android-armv7a android-arm64 android-x86 android-x86_64 diff --git a/cmd/btfs/main.go b/cmd/btfs/main.go index c5b6b67..9f6167d 100644 --- a/cmd/btfs/main.go +++ b/cmd/btfs/main.go @@ -142,6 +142,7 @@ func mainRet(args []string) int { buildEnv := func(ctx context.Context, req *cmds.Request) (cmds.Environment, error) { checkDebug(req) repoPath, err := getRepoPath(req) + Println("2 RepoPath",repoPath) if err != nil { return nil, err } @@ -188,6 +189,13 @@ func mainRet(args []string) int { Println("5", args) err = cli.Run(ctx, Root, os.Args, os.Stdin, os.Stdout, os.Stderr, buildEnv, makeExecutor) +// Println("ctx:",ctx) + //Println("Root",Root) + //Println("os.Args",os.Args) + //Println("os.Stdin",os.Stdin) + //Println(os.Stderr,os.Stderr) + //Println("buildEnv",buildEnv) + //Println("makeExecutor",makeExecutor) if err != nil { Println("6", err) return 1 @@ -330,6 +338,7 @@ func getRepoPath(req *cmds.Request) (string, error) { } repoPath, err := fsrepo.BestKnownPath() + Println("1 repoPath:",repoPath) if err != nil { return "", err } diff --git a/core/commands/storage/path/path.go b/core/commands/storage/path/path.go index c17b0fa..a94dbb7 100644 --- a/core/commands/storage/path/path.go +++ b/core/commands/storage/path/path.go @@ -13,12 +13,13 @@ import ( "sync/atomic" "time" "unsafe" + "runtime" cmds "github.com/bittorrent/go-btfs-cmds" "github.com/dustin/go-humanize" logging "github.com/ipfs/go-log" - "github.com/simbadMarino/go-homedir" + "github.com/mitchellh/go-homedir" ) const ( @@ -95,7 +96,7 @@ var PathCmd = &cmds.Command{ Tagline: "Modify the Host storage folder path for BTFS client.", ShortDescription: ` The default local repository path is located at ~/.btfs folder, in order to -improve the hard disk space usage, provide the function to change the original +improve the hard disk space usage, provide the function to change the original storage location, a specified path as a parameter need to be passed. `, }, @@ -490,7 +491,10 @@ func CheckDirEmpty(dirname string) bool { } func SetEnvVariables() { - os.Setenv("HOME","/data/data/com.justshare/files") // TODO: Make this an if only for Android + if runtime.GOOS == "android" { + os.Setenv("HOME","/data/data/com.justshare/files/home") // $HOME path definition only + } + //TODO: Pending to add $HOME path for iOS if CheckExist(PropertiesFileName) { btfsPath = ReadProperties(PropertiesFileName) btfsPath = strings.Trim(btfsPath, " \n\r") diff --git a/core/hub/settings_test.go b/core/hub/settings_test.go index 53530f7..9cd9e7f 100644 --- a/core/hub/settings_test.go +++ b/core/hub/settings_test.go @@ -2,6 +2,7 @@ package hub import ( "context" + "fmt" "reflect" "testing" @@ -14,6 +15,8 @@ func TestGetSettings(t *testing.T) { if err != nil { t.Fatal(err) } + fmt.Printf("GetHostSettings, ns = %+v \n", ns) + defNs := &nodepb.Node_Settings{StoragePriceAsk: 125000, StorageTimeMin: 30, StoragePriceDefault: 125000} if !reflect.DeepEqual(ns, defNs) { t.Fatal("default settings not equal") diff --git a/repo/fsrepo/misc.go b/repo/fsrepo/misc.go index 3e9f69c..91b2950 100644 --- a/repo/fsrepo/misc.go +++ b/repo/fsrepo/misc.go @@ -2,7 +2,7 @@ package fsrepo import ( "os" - + "runtime" "github.com/mitchellh/go-homedir" ) @@ -10,8 +10,12 @@ import ( // present, this function returns that value. Otherwise, it returns the default // repo path. func BestKnownPath() (string, error) { - //btfsPath := "~/Documents/.btfs" //iOS path - btfsPath := "~/.btfs" //Android path + if runtime.GOOS == "darwin" { //TODO: Leave only ./btfs path by defining properly the $HOME dir for iOS in path.go file + btfsPath := "~/Documents/.btfs" //iOS path + } + if runtime.GOOS == "android" { + btfsPath := "~/.btfs" //Android path + } if os.Getenv("BTFS_PATH") != "" { btfsPath = os.Getenv("BTFS_PATH") } From 151b5abf8c509cd76d9641e2f07e4334d4f369ae Mon Sep 17 00:00:00 2001 From: Simbad Marino Date: Sat, 15 Oct 2022 14:03:49 -0500 Subject: [PATCH 7/7] Adjusting path for iOS --- cmd/btfs/Makefile | 16 +++++++--- cmd/btfs/_btfs_fat.h | 75 -------------------------------------------- repo/fsrepo/misc.go | 5 +-- 3 files changed, 14 insertions(+), 82 deletions(-) delete mode 100644 cmd/btfs/_btfs_fat.h diff --git a/cmd/btfs/Makefile b/cmd/btfs/Makefile index 493f958..c9e8818 100644 --- a/cmd/btfs/Makefile +++ b/cmd/btfs/Makefile @@ -2,7 +2,7 @@ ANDROID_OUT=./jniLibs ANDROID_SDK=$(HOME)/Library/Android/sdk NDK_BIN=$(ANDROID_SDK)/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin - +WASM_OUT=./wasmBinary ios-arm64: CGO_ENABLED=1 \ @@ -12,7 +12,7 @@ ios-arm64: SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \ CC=$(PWD)/clangwrap.sh \ CGO_CFLAGS="-fembed-bitcode -Wno-undef-prefix" \ - go build -buildmode=c-archive -tags ios -o $(IOS_OUT)/arm64.a . + go build -buildmode=c-archive -tags ios -ldflags="-s -w" -o $(IOS_OUT)/arm64.a . ios-x86_64: CGO_ENABLED=1 \ @@ -21,7 +21,7 @@ ios-x86_64: SDK=iphonesimulator \ CGO_CFLAGS="-Wno-undef-prefix" \ CC=$(PWD)/clangwrap.sh \ - go build -buildmode=c-archive -tags ios -o $(IOS_OUT)/x86_64.a . + go build -buildmode=c-archive -tags ios -ldflags="-s -w" -o $(IOS_OUT)/x86_64.a . ios: ios-arm64 ios-x86_64 lipo $(IOS_OUT)/x86_64.a $(IOS_OUT)/arm64.a -create -output $(IOS_OUT)/btfs.a @@ -36,7 +36,7 @@ android-armv7a: CGO_CFLAGS="-fembed-bitcode" \ CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \ go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/armeabi-v7a/libbtfs.so . - + android-arm64: CGO_ENABLED=1 \ @@ -45,7 +45,7 @@ android-arm64: CGO_CFLAGS="-fembed-bitcode" \ CC=$(NDK_BIN)/aarch64-linux-android21-clang \ go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/arm64-v8a/libbtfs.so . - + android-x86: CGO_ENABLED=1 \ @@ -62,3 +62,9 @@ android-x86_64: go build -buildmode=c-shared -trimpath -ldflags="-s -w" -o $(ANDROID_OUT)/x86_64/libbtfs.so . android: android-armv7a android-arm64 android-x86 android-x86_64 + +wasm: + CGO_ENABLED=1 \ + GOOS=js \ + GOARCH=wasm \ + go build -o $(WASM_OUT)/btfs.wasm . diff --git a/cmd/btfs/_btfs_fat.h b/cmd/btfs/_btfs_fat.h deleted file mode 100644 index 685503b..0000000 --- a/cmd/btfs/_btfs_fat.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Code generated by cmd/cgo; DO NOT EDIT. */ - -/* package github.com/bittorrent/go-btfs/cmd/btfs */ - - -#line 1 "cgo-builtin-export-prolog" - -#include /* for ptrdiff_t below */ - -#ifndef GO_CGO_EXPORT_PROLOGUE_H -#define GO_CGO_EXPORT_PROLOGUE_H - -#ifndef GO_CGO_GOSTRING_TYPEDEF -typedef struct { const char *p; ptrdiff_t n; } _GoString_; -#endif - -#endif - -/* Start of preamble from import "C" comments. */ - - - - -/* End of preamble from import "C" comments. */ - - -/* Start of boilerplate cgo prologue. */ -#line 1 "cgo-gcc-export-header-prolog" - -#ifndef GO_CGO_PROLOGUE_H -#define GO_CGO_PROLOGUE_H - -typedef signed char GoInt8; -typedef unsigned char GoUint8; -typedef short GoInt16; -typedef unsigned short GoUint16; -typedef int GoInt32; -typedef unsigned int GoUint32; -typedef long long GoInt64; -typedef unsigned long long GoUint64; -typedef GoInt64 GoInt; -typedef GoUint64 GoUint; -typedef __SIZE_TYPE__ GoUintptr; -typedef float GoFloat32; -typedef double GoFloat64; -typedef float _Complex GoComplex64; -typedef double _Complex GoComplex128; - -/* - static assertion to make sure the file is being used on architecture - at least with matching size of GoInt. -*/ -typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; - -#ifndef GO_CGO_GOSTRING_TYPEDEF -typedef _GoString_ GoString; -#endif -typedef void *GoMap; -typedef void *GoChan; -typedef struct { void *t; void *v; } GoInterface; -typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; - -#endif - -/* End of boilerplate cgo prologue. */ - -#ifdef __cplusplus -extern "C" { -#endif - -extern char* mainC(char* in); - -#ifdef __cplusplus -} -#endif diff --git a/repo/fsrepo/misc.go b/repo/fsrepo/misc.go index 91b2950..98cb424 100644 --- a/repo/fsrepo/misc.go +++ b/repo/fsrepo/misc.go @@ -10,11 +10,12 @@ import ( // present, this function returns that value. Otherwise, it returns the default // repo path. func BestKnownPath() (string, error) { + btfsPath := "~/Documents/.btfs" if runtime.GOOS == "darwin" { //TODO: Leave only ./btfs path by defining properly the $HOME dir for iOS in path.go file - btfsPath := "~/Documents/.btfs" //iOS path + btfsPath = "~/Documents/.btfs" //iOS path } if runtime.GOOS == "android" { - btfsPath := "~/.btfs" //Android path + btfsPath = "~/.btfs" //Android path } if os.Getenv("BTFS_PATH") != "" { btfsPath = os.Getenv("BTFS_PATH")