github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/rpc/test/helpers.go (about) 1 package rpctest 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 "time" 10 11 abci "github.com/badrootd/celestia-core/abci/types" 12 "github.com/badrootd/celestia-core/libs/log" 13 14 cfg "github.com/badrootd/celestia-core/config" 15 cmtnet "github.com/badrootd/celestia-core/libs/net" 16 nm "github.com/badrootd/celestia-core/node" 17 "github.com/badrootd/celestia-core/p2p" 18 "github.com/badrootd/celestia-core/privval" 19 "github.com/badrootd/celestia-core/proxy" 20 ctypes "github.com/badrootd/celestia-core/rpc/core/types" 21 core_grpc "github.com/badrootd/celestia-core/rpc/grpc" 22 rpcclient "github.com/badrootd/celestia-core/rpc/jsonrpc/client" 23 ) 24 25 // Options helps with specifying some parameters for our RPC testing for greater 26 // control. 27 type Options struct { 28 suppressStdout bool 29 recreateConfig bool 30 // SpecificConfig will replace the global config if not nil 31 SpecificConfig *cfg.Config 32 } 33 34 var globalConfig *cfg.Config 35 var defaultOptions = Options{ 36 suppressStdout: false, 37 recreateConfig: false, 38 } 39 40 func waitForRPC() { 41 laddr := GetConfig().RPC.ListenAddress 42 client, err := rpcclient.New(laddr) 43 if err != nil { 44 panic(err) 45 } 46 result := new(ctypes.ResultStatus) 47 for { 48 _, err := client.Call(context.Background(), "status", map[string]interface{}{}, result) 49 if err == nil { 50 return 51 } 52 53 fmt.Println("error", err) 54 time.Sleep(time.Millisecond) 55 } 56 } 57 58 func waitForGRPC() { 59 client := GetGRPCClient() 60 for { 61 _, err := client.Ping(context.Background(), &core_grpc.RequestPing{}) 62 if err == nil { 63 return 64 } 65 } 66 } 67 68 // f**ing long, but unique for each test 69 func makePathname() string { 70 // get path 71 p, err := os.Getwd() 72 if err != nil { 73 panic(err) 74 } 75 // fmt.Println(p) 76 sep := string(filepath.Separator) 77 return strings.ReplaceAll(p, sep, "_") 78 } 79 80 func randPort() int { 81 port, err := cmtnet.GetFreePort() 82 if err != nil { 83 panic(err) 84 } 85 return port 86 } 87 88 func makeAddrs() (string, string, string) { 89 return fmt.Sprintf("tcp://127.0.0.1:%d", randPort()), 90 fmt.Sprintf("tcp://127.0.0.1:%d", randPort()), 91 fmt.Sprintf("tcp://127.0.0.1:%d", randPort()) 92 } 93 94 func createConfig() *cfg.Config { 95 pathname := makePathname() 96 c := cfg.ResetTestRoot(pathname) 97 98 // and we use random ports to run in parallel 99 cmt, rpc, grpc := makeAddrs() 100 c.P2P.ListenAddress = cmt 101 c.RPC.ListenAddress = rpc 102 c.RPC.CORSAllowedOrigins = []string{"https://cometbft.com/"} 103 c.RPC.GRPCListenAddress = grpc 104 return c 105 } 106 107 // GetConfig returns a config for the test cases as a singleton 108 func GetConfig(forceCreate ...bool) *cfg.Config { 109 if globalConfig == nil || (len(forceCreate) > 0 && forceCreate[0]) { 110 globalConfig = createConfig() 111 } 112 return globalConfig 113 } 114 115 func GetGRPCClient() core_grpc.BroadcastAPIClient { 116 grpcAddr := globalConfig.RPC.GRPCListenAddress 117 return core_grpc.StartGRPCClient(grpcAddr) 118 } 119 120 // StartTendermint starts a test CometBFT server in a go routine and returns when it is initialized 121 func StartTendermint(app abci.Application, opts ...func(*Options)) *nm.Node { 122 nodeOpts := defaultOptions 123 for _, opt := range opts { 124 opt(&nodeOpts) 125 } 126 node := NewTendermint(app, &nodeOpts) 127 err := node.Start() 128 if err != nil { 129 panic(err) 130 } 131 132 // wait for rpc 133 waitForRPC() 134 waitForGRPC() 135 136 if !nodeOpts.suppressStdout { 137 fmt.Println("CometBFT running!") 138 } 139 140 return node 141 } 142 143 // StopTendermint stops a test CometBFT server, waits until it's stopped and 144 // cleans up test/config files. 145 func StopTendermint(node *nm.Node) { 146 if err := node.Stop(); err != nil { 147 node.Logger.Error("Error when trying to stop node", "err", err) 148 } 149 node.Wait() 150 os.RemoveAll(node.Config().RootDir) 151 } 152 153 // NewTendermint creates a new CometBFT server and sleeps forever 154 func NewTendermint(app abci.Application, opts *Options) *nm.Node { 155 // Create & start node 156 if opts.SpecificConfig != nil { 157 globalConfig = opts.SpecificConfig 158 } 159 config := GetConfig(opts.recreateConfig) 160 161 var logger log.Logger 162 if opts.suppressStdout { 163 logger = log.NewNopLogger() 164 } else { 165 logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) 166 logger = log.NewFilter(logger, log.AllowError()) 167 } 168 pvKeyFile := config.PrivValidatorKeyFile() 169 pvKeyStateFile := config.PrivValidatorStateFile() 170 pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) 171 papp := proxy.NewLocalClientCreator(app) 172 nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) 173 if err != nil { 174 panic(err) 175 } 176 node, err := nm.NewNode(config, pv, nodeKey, papp, 177 nm.DefaultGenesisDocProviderFunc(config), 178 nm.DefaultDBProvider, 179 nm.DefaultMetricsProvider(config.Instrumentation), 180 logger) 181 if err != nil { 182 panic(err) 183 } 184 return node 185 } 186 187 // SuppressStdout is an option that tries to make sure the RPC test CometBFT 188 // node doesn't log anything to stdout. 189 func SuppressStdout(o *Options) { 190 o.suppressStdout = true 191 } 192 193 // RecreateConfig instructs the RPC test to recreate the configuration each 194 // time, instead of treating it as a global singleton. 195 func RecreateConfig(o *Options) { 196 o.recreateConfig = true 197 }