diff --git a/chain/utils.go b/chain/utils.go index 41bb0700a..b71825b17 100644 --- a/chain/utils.go +++ b/chain/utils.go @@ -162,3 +162,148 @@ func StoreChainIdIfNotExists(chainID int64, statestore storage.StateStorer) erro return nil } + +// GetReportStatus from leveldb +var keyReportStatus = "keyReportStatus" + +type ReportStatusInfo struct { + ReportStatusSeconds int64 + LastReportTime time.Time +} + +func GetReportStatus() (*ReportStatusInfo, error) { + if StateStore == nil { + return nil, errors.New("please start btfs node, at first! ") + } + + var reportStatusInfo ReportStatusInfo + err := StateStore.Get(keyReportStatus, &reportStatusInfo) + if err != nil { + if err == storage.ErrNotFound { + reportStatusInfo = ReportStatusInfo{ReportStatusSeconds: int64(rand.Intn(100000000) % 86400), LastReportTime: time.Time{}} + err := StateStore.Put(keyReportStatus, reportStatusInfo) + if err != nil { + fmt.Println("StoreChainIdIfNotExists: init StoreChainId err: ", err) + return nil, err + } + } + return nil, err + } + return &reportStatusInfo, nil +} + +func SetReportStatusOK() (*ReportStatusInfo, error) { + var reportStatusInfo ReportStatusInfo + err := StateStore.Get(keyReportStatus, &reportStatusInfo) + if err != nil { + return nil, err + } + reportStatusInfo.LastReportTime = time.Now() + err = StateStore.Put(keyReportStatus, reportStatusInfo) + if err != nil { + return nil, err + } + //fmt.Println("... ReportStatus, SetReportStatus: ok! ") + return &reportStatusInfo, nil +} + +// GetReportStatus from leveldb +var keyReportStatusList = "keyReportStatusList" + +type LevelDbReportStatusInfo struct { + Peer string `json:"peer"` + BttcAddress string `json:"bttc_addr"` + StatusContract string `json:"status_contract"` + Nonce uint32 `json:"nonce"` + TxHash string `json:"tx_hash"` + GasSpend string `json:"gas_spend"` + ReportTime time.Time `json:"report_time"` + IncreaseNonce uint32 `json:"increase_nonce"` +} + +// SetReportStatusListOK store tx list +func SetReportStatusListOK(r *LevelDbReportStatusInfo) ([]*LevelDbReportStatusInfo, error) { + if StateStore == nil { + return nil, errors.New("please start btfs node, at first! ") + } + + init := false + + rList := make([]*LevelDbReportStatusInfo, 0) + err := StateStore.Get(keyReportStatusList, &rList) + if err != nil { + if err.Error() == "storage: not found" { + init = true + // continue + } else { + return nil, err + } + } + + if init { + r.IncreaseNonce = r.Nonce + } else { + r.IncreaseNonce = r.Nonce - rList[len(rList)-1].Nonce + } + + rList = append(rList, r) + err = StateStore.Put(keyReportStatusList, rList) + if err != nil { + return nil, err + } + //fmt.Println("... ReportStatus, SetReportStatusListOK: ok! rList = ", rList) + return rList, nil +} + +// GetReportStatusListOK store tx list +func GetReportStatusListOK() ([]*LevelDbReportStatusInfo, error) { + if StateStore == nil { + return nil, errors.New("please start btfs node, at first! ") + } + + rList := make([]*LevelDbReportStatusInfo, 0) + err := StateStore.Get(keyReportStatusList, &rList) + if err != nil { + if err.Error() == "storage: not found" { + return nil, nil + } else { + return nil, err + } + } + + return rList, nil +} + +// GetLastOnline from leveldb +var keyLastOnline = "keyLastOnline" + +type LastOnlineInfo struct { + LastSignedInfo onlinePb.SignedInfo + LastSignature string + LastTime time.Time +} + +func GetLastOnline() (*LastOnlineInfo, error) { + if StateStore == nil { + return nil, errors.New("please start btfs node, at first! ") + } + + var lastOnlineInfo LastOnlineInfo + err := StateStore.Get(keyLastOnline, &lastOnlineInfo) + if err != nil { + if err == storage.ErrNotFound { + return nil, nil + } + return nil, err + } + return &lastOnlineInfo, nil +} +func StoreOnline(lastOnlineInfo *LastOnlineInfo) error { + err := StateStore.Put(keyLastOnline, *lastOnlineInfo) + if err != nil { + fmt.Println("StoreOnline: init StoreChainId err: ", err) + return err + } + + return nil +} diff --git a/core/commands/statuscontract.go b/core/commands/statuscontract.go index e69de29bb..bf4556fd9 100644 --- a/core/commands/statuscontract.go +++ b/core/commands/statuscontract.go @@ -0,0 +1,235 @@ +package commands + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/bittorrent/go-btfs/core/commands/cmdenv" + "io" + "math/big" + "strconv" + "time" + + cmds "github.com/bittorrent/go-btfs-cmds" + "github.com/bittorrent/go-btfs/chain" +) + +var StatusContractCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "report status-contract cmd.", + ShortDescription: ` +report status-contract cmd, total cmd and list cmd.`, + }, + Subcommands: map[string]*cmds.Command{ + "total": TotalCmd, + "reportlist": ReportListCmd, + "lastinfo": LastInfoCmd, + "config": StatusConfigCmd, + }, +} + +type TotalCmdRet struct { + PeerId string `json:"peer_id"` + StatusContract string `json:"status_contract"` + TotalCount int `json:"total_count"` + TotalGasSpend string `json:"total_gas_spend"` +} + +var TotalCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "report status-contract total info, (total count, total gas spend, and contract address)", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + n, err := cmdenv.GetNode(env) + if err != nil { + return err + } + peerId := n.Identity.Pretty() + + list, err := chain.GetReportStatusListOK() + if err != nil { + return err + } + if list == nil { + return nil + } + + // get list to cal spend, from string to big.int... + totalGasSpend := new(big.Int) + for _, r := range list { + n := new(big.Int) + if len(r.GasSpend) <= 0 { + //fmt.Println("r.GasSpend is zero. ") + continue + } + //fmt.Println("r.GasSpend = ", r.GasSpend) + + n, ok := n.SetString(r.GasSpend, 10) + if !ok { + return errors.New("parse gas_spend is error. ") + } + totalGasSpend = totalGasSpend.Add(totalGasSpend, n) + } + + return cmds.EmitOnce(res, &TotalCmdRet{ + PeerId: peerId, + StatusContract: list[len(list)-1].StatusContract, + TotalCount: len(list), + TotalGasSpend: totalGasSpend.String(), + }) + }, + Type: TotalCmdRet{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *TotalCmdRet) error { + marshaled, err := json.MarshalIndent(out, "", "\t") + if err != nil { + return err + } + marshaled = append(marshaled, byte('\n')) + fmt.Fprintln(w, string(marshaled)) + return nil + }), + }, +} + +type ReportListCmdRet struct { + Records []*chain.LevelDbReportStatusInfo `json:"records"` + Total int `json:"total"` + PeerId string `json:"peer_id"` +} + +var ReportListCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "report status-contract list, and input from and limit to get its.", + }, + RunTimeout: 5 * time.Minute, + Arguments: []cmds.Argument{ + cmds.StringArg("from", true, false, "page offset"), + cmds.StringArg("limit", true, false, "page limit."), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + n, err := cmdenv.GetNode(env) + if err != nil { + return err + } + peerId := n.Identity.Pretty() + + from, err := strconv.Atoi(req.Arguments[0]) + if err != nil { + return fmt.Errorf("parse from:%v failed", req.Arguments[0]) + } + limit, err := strconv.Atoi(req.Arguments[1]) + if err != nil { + return fmt.Errorf("parse limit:%v failed", req.Arguments[1]) + } + if from < 0 { + return fmt.Errorf("invalid from: %d", from) + } + if limit < 0 { + return fmt.Errorf("invalid limit: %d", limit) + } + + list, err := chain.GetReportStatusListOK() + if err != nil { + return err + } + if list == nil { + return nil + } + // + //from := 0 + //limit := 10 + + From := len(list) - 1 - from - limit + if From <= 0 { + From = 0 + } + 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] + } + + return cmds.EmitOnce(res, &ReportListCmdRet{ + Records: s, + Total: len(list), + PeerId: peerId, + }) + }, + Type: ReportListCmdRet{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ReportListCmdRet) error { + marshaled, err := json.MarshalIndent(out, "", "\t") + if err != nil { + return err + } + marshaled = append(marshaled, byte('\n')) + fmt.Fprintln(w, string(marshaled)) + return nil + }), + }, +} + +var LastInfoCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "get reporting status-contract last info", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + last, err := chain.GetLastOnline() + if err != nil { + return err + } + if last == nil { + return errors.New("not found. ") + } + + return cmds.EmitOnce(res, last) + }, + Type: chain.LastOnlineInfo{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *chain.LastOnlineInfo) error { + marshaled, err := json.MarshalIndent(out, "", "\t") + if err != nil { + return err + } + marshaled = append(marshaled, byte('\n')) + fmt.Fprintln(w, string(marshaled)) + return nil + }), + }, +} + +var StatusConfigCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "get reporting status-contract config. ", + }, + RunTimeout: 5 * time.Minute, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + rs, err := chain.GetReportStatus() + if err != nil { + return err + } + + return cmds.EmitOnce(res, rs) + }, + Type: chain.ReportStatusInfo{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *chain.ReportStatusInfo) error { + marshaled, err := json.MarshalIndent(out, "", "\t") + if err != nil { + return err + } + marshaled = append(marshaled, byte('\n')) + fmt.Fprintln(w, string(marshaled)) + return nil + }), + }, +} diff --git a/go.mod b/go.mod index 7007b7a18..39f9340db 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.8 + github.com/TRON-US/go-btfs-config v0.11.11-pre1 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 @@ -125,7 +125,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.6 + github.com/tron-us/go-btfs-common v0.8.10-pre7 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 3c411d8d7..d7499a4a2 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.8 h1:eSuNp488lBv+dip35KKBHWDRO3Wnrwn7h7M/0e+r4ZE= -github.com/TRON-US/go-btfs-config v0.11.8/go.mod h1:9y6osJENDCjulSNJjSSt1J8OK+ADRatBdYPXRDewbko= +github.com/TRON-US/go-btfs-config v0.11.11-pre1 h1:/xBFbQcX03DE8mL5G8T71Bdk1bNu85y/Ei/XnZ0t4+g= +github.com/TRON-US/go-btfs-config v0.11.11-pre1/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= @@ -1374,8 +1374,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.6 h1:IA1qxW0WSxAcYFJM3DG0DJIm4wptE4zueNHBQmY7dv8= -github.com/tron-us/go-btfs-common v0.8.6/go.mod h1:xnIFfbMRS5VsF948fBHPcYIeYGZkQgaJ6NIEGIPfYUs= +github.com/tron-us/go-btfs-common v0.8.10-pre7 h1:06Pr1rgUPYy/aCcrQLkldb3APdoqW3f5japDXiEShVg= +github.com/tron-us/go-btfs-common v0.8.10-pre7/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/reportstatus.go b/reportstatus/reportstatus.go index e69de29bb..7eaa7abab 100644 --- a/reportstatus/reportstatus.go +++ b/reportstatus/reportstatus.go @@ -0,0 +1,293 @@ +package reportstatus + +import ( + "context" + "encoding/hex" + "fmt" + onlinePb "github.com/tron-us/go-btfs-common/protos/online" + "math/big" + "strings" + "time" + + config "github.com/TRON-US/go-btfs-config" + "github.com/bittorrent/go-btfs/chain" + "github.com/bittorrent/go-btfs/reportstatus/abi" + "github.com/bittorrent/go-btfs/transaction" + "github.com/ethereum/go-ethereum/common" + + logging "github.com/ipfs/go-log" +) + +var log = logging.Logger("report-status-contract:") + +var ( + statusABI = transaction.ParseABIUnchecked(abi.StatusHeartABI) + serv *service +) + +const ( + ReportStatusTime = 10 * time.Minute + //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 isReportStatusEnabled(cfg *config.Config) bool { + return cfg.Experimental.StorageHostEnabled || cfg.Experimental.ReportStatusContract +} + +type service struct { + statusAddress common.Address + transactionService transaction.Service +} + +type Service interface { + // ReportStatus report status heart info to statusContract + ReportStatus() (common.Hash, error) + + // CheckReportStatus check report status heart info to statusContract + CheckReportStatus() error +} + +func New(statusAddress common.Address, transactionService transaction.Service, cfg *config.Config) Service { + serv = &service{ + statusAddress: statusAddress, + transactionService: transactionService, + } + + if isReportStatusEnabled(cfg) { + go func() { + cycleCheckReport() + }() + } + return serv +} + +// ReportStatus report heart status +func (s *service) ReportStatus() (common.Hash, error) { + lastOnline, err := chain.GetLastOnline() + if err != nil { + return common.Hash{}, err + } + + if lastOnline == nil { + return common.Hash{}, nil + } + if len(lastOnline.LastSignedInfo.Peer) <= 0 { + return common.Hash{}, nil + } + + peer := lastOnline.LastSignedInfo.Peer + createTime := lastOnline.LastSignedInfo.CreatedTime + version := lastOnline.LastSignedInfo.Version + nonce := lastOnline.LastSignedInfo.Nonce + bttcAddress := common.HexToAddress(lastOnline.LastSignedInfo.BttcAddress) + signedTime := lastOnline.LastSignedInfo.SignedTime + signature, err := hex.DecodeString(strings.Replace(lastOnline.LastSignature, "0x", "", -1)) + //fmt.Printf("... ReportStatus, lastOnline = %+v \n", lastOnline) + + callData, err := statusABI.Pack("reportStatus", peer, createTime, version, nonce, bttcAddress, signedTime, signature) + if err != nil { + return common.Hash{}, err + } + + request := &transaction.TxRequest{ + To: &s.statusAddress, + Data: callData, + Value: big.NewInt(0), + Description: "Report Heart Status", + } + + txHash, err := s.transactionService.Send(context.Background(), request) + if err != nil { + return common.Hash{}, err + } + //fmt.Println("... ReportStatus, txHash, err = ", txHash, err) + + now := time.Now() + _, err = chain.SetReportStatusOK() + if err != nil { + return common.Hash{}, err + } + + stx, err := s.transactionService.WaitForReceipt(context.Background(), txHash) + if err != nil { + return common.Hash{}, err + } + gasPrice := getGasPrice(request) + gasTotal := big.NewInt(1).Mul(gasPrice, big.NewInt(int64(stx.GasUsed))) + //fmt.Println("... ReportStatus, gasPrice, stx.GasUsed, gasTotal = ", gasPrice.String(), stx.GasUsed, gasTotal.String()) + + r := &chain.LevelDbReportStatusInfo{ + Peer: peer, + BttcAddress: bttcAddress.String(), + StatusContract: s.statusAddress.String(), + Nonce: nonce, + TxHash: txHash.String(), + GasSpend: gasTotal.String(), + ReportTime: now, + } + _, err = chain.SetReportStatusListOK(r) + 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 +} + +func getGasPrice(request *transaction.TxRequest) *big.Int { + var gasPrice *big.Int + if request.GasPrice == nil { + gasPrice = big.NewInt(300000000000000) + } else { + gasPrice = request.GasPrice + } + return gasPrice +} + +// report heart status +func (s *service) checkLastOnlineInfo(peerId, bttcAddr string) error { + callData, err := statusABI.Pack("getStatus", peerId) + if err != nil { + return err + } + request := &transaction.TxRequest{ + To: &s.statusAddress, + Data: callData, + } + + result, err := s.transactionService.Call(context.Background(), request) + if err != nil { + return err + } + v, err := statusABI.Unpack("getStatus", result) + if err != nil { + return err + } + //fmt.Printf("...... getStatus - result v = %+v, err = %v \n", v, err) + + nonce := v[3].(uint32) + if nonce > 0 { + lastOnlineInfo := chain.LastOnlineInfo{ + LastSignedInfo: onlinePb.SignedInfo{ + Peer: v[0].(string), + CreatedTime: v[1].(uint32), + Version: v[2].(string), + Nonce: v[3].(uint32), + BttcAddress: bttcAddr, + SignedTime: v[4].(uint32), + }, + LastSignature: "0x" + hex.EncodeToString(v[5].([]byte)), + LastTime: time.Now(), + } + fmt.Printf("... init reset lastOnlineInfo = %+v \n", lastOnlineInfo) + + err = chain.StoreOnline(&lastOnlineInfo) + if err != nil { + return err + } + } + + // WaitForReceipt takes long time + go func() { + defer func() { + if r := recover(); r != nil { + log.Errorf("getStatus recovered:%+v", err) + } + }() + }() + return nil +} + +// report heart status +func (s *service) genHashExt(ctx context.Context) (common.Hash, error) { + peer := "1" + createTime := uint32(1) + version := "1" + num := uint32(3) + bttcAddress := "0x22df207EC3C8D18fEDeed87752C5a68E5b4f6FbD" + fmt.Println("...... genHashExt, param = ", peer, createTime, version, num, bttcAddress) + + callData, err := statusABI.Pack("genHashExt", peer, createTime, version, num, common.HexToAddress(bttcAddress)) + if err != nil { + return common.Hash{}, err + } + + request := &transaction.TxRequest{ + To: &s.statusAddress, + Data: callData, + } + + result, err := s.transactionService.Call(ctx, request) + fmt.Println("...... genHashExt - totalStatus, result, err = ", common.Bytes2Hex(result), err) + + 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 +} + +func (s *service) CheckReportStatus() error { + _, err := s.ReportStatus() + if err != nil { + log.Errorf("ReportStatus err:%+v", err) + return err + } + return nil +} + +func cycleCheckReport() { + tick := time.NewTicker(ReportStatusTime) + defer tick.Stop() + + // Force tick on immediate start + // CheckReport in the for loop + for ; true; <-tick.C { + //fmt.Println("") + //fmt.Println("... ReportStatus, CheckReportStatus ...") + + report, err := chain.GetReportStatus() + //fmt.Printf("... ReportStatus, CheckReportStatus report: %+v err:%+v \n", report, err) + if err != nil { + continue + } + + now := time.Now() + nowUnixMod := now.Unix() % 86400 + // report only 1 hour every, and must after 10 hour. + if nowUnixMod > report.ReportStatusSeconds && + nowUnixMod < report.ReportStatusSeconds+3600 && + now.Sub(report.LastReportTime) > 10*time.Hour { + + err = serv.CheckReportStatus() + if err != nil { + continue + } + } + } +}