github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/node/node_test.go (about) 1 package node 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "syscall" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 "github.com/gnolang/gno/tm2/pkg/bft/abci/example/kvstore" 15 "github.com/gnolang/gno/tm2/pkg/bft/appconn" 16 cfg "github.com/gnolang/gno/tm2/pkg/bft/config" 17 mempl "github.com/gnolang/gno/tm2/pkg/bft/mempool" 18 "github.com/gnolang/gno/tm2/pkg/bft/privval" 19 "github.com/gnolang/gno/tm2/pkg/bft/proxy" 20 sm "github.com/gnolang/gno/tm2/pkg/bft/state" 21 "github.com/gnolang/gno/tm2/pkg/bft/types" 22 tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" 23 "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" 24 dbm "github.com/gnolang/gno/tm2/pkg/db" 25 "github.com/gnolang/gno/tm2/pkg/db/memdb" 26 "github.com/gnolang/gno/tm2/pkg/events" 27 "github.com/gnolang/gno/tm2/pkg/log" 28 "github.com/gnolang/gno/tm2/pkg/p2p" 29 p2pmock "github.com/gnolang/gno/tm2/pkg/p2p/mock" 30 "github.com/gnolang/gno/tm2/pkg/random" 31 ) 32 33 func TestNodeStartStop(t *testing.T) { 34 config, genesisFile := cfg.ResetTestRoot("node_node_test") 35 defer os.RemoveAll(config.RootDir) 36 37 // create & start node 38 n, err := DefaultNewNode(config, genesisFile, log.NewNoopLogger()) 39 require.NoError(t, err) 40 err = n.Start() 41 require.NoError(t, err) 42 43 t.Logf("Started node %v", n.sw.NodeInfo()) 44 45 // wait for the node to produce a block 46 blocksSub := events.SubscribeToEvent(n.EventSwitch(), "node_test", types.EventNewBlock{}) 47 require.NoError(t, err) 48 select { 49 case _, ok := <-blocksSub: 50 if !ok { 51 t.Fatal("blocksSub was cancelled") 52 } 53 case <-time.After(10 * time.Second): 54 t.Fatal("timed out waiting for the node to produce a block") 55 } 56 57 // stop the node 58 go func() { 59 n.Stop() 60 }() 61 62 select { 63 case <-n.Quit(): 64 case <-time.After(5 * time.Second): 65 pid := os.Getpid() 66 p, err := os.FindProcess(pid) 67 if err != nil { 68 panic(err) 69 } 70 err = p.Signal(syscall.SIGABRT) 71 fmt.Println(err) 72 t.Fatal("timed out waiting for shutdown") 73 } 74 } 75 76 func TestSplitAndTrimEmpty(t *testing.T) { 77 testCases := []struct { 78 s string 79 sep string 80 cutset string 81 expected []string 82 }{ 83 {"a,b,c", ",", " ", []string{"a", "b", "c"}}, 84 {" a , b , c ", ",", " ", []string{"a", "b", "c"}}, 85 {" a, b, c ", ",", " ", []string{"a", "b", "c"}}, 86 {" a, ", ",", " ", []string{"a"}}, 87 {" ", ",", " ", []string{}}, 88 } 89 90 for _, tc := range testCases { 91 assert.Equal(t, tc.expected, splitAndTrimEmpty(tc.s, tc.sep, tc.cutset), "%s", tc.s) 92 } 93 } 94 95 func TestNodeDelayedStart(t *testing.T) { 96 config, genesisFile := cfg.ResetTestRoot("node_delayed_start_test") 97 defer os.RemoveAll(config.RootDir) 98 now := tmtime.Now() 99 100 // create & start node 101 n, err := DefaultNewNode(config, genesisFile, log.NewTestingLogger(t)) 102 n.GenesisDoc().GenesisTime = now.Add(2 * time.Second) 103 require.NoError(t, err) 104 105 err = n.Start() 106 require.NoError(t, err) 107 defer n.Stop() 108 109 startTime := tmtime.Now() 110 assert.Equal(t, true, startTime.After(n.GenesisDoc().GenesisTime)) 111 } 112 113 func TestNodeReady(t *testing.T) { 114 config, genesisFile := cfg.ResetTestRoot("node_node_test") 115 defer os.RemoveAll(config.RootDir) 116 117 // Create & start node 118 n, err := DefaultNewNode(config, genesisFile, log.NewTestingLogger(t)) 119 require.NoError(t, err) 120 121 // Assert that blockstore has zero block before waiting for the first block 122 require.Equal(t, int64(0), n.BlockStore().Height()) 123 124 // Assert that first block signal is not alreay received by calling Ready 125 select { 126 case <-n.Ready(): 127 require.FailNow(t, "first block signal should not be close before starting the node") 128 default: // ok 129 } 130 131 err = n.Start() 132 require.NoError(t, err) 133 defer n.Stop() 134 135 // Wait until the node is ready or timeout 136 select { 137 case <-time.After(time.Second): 138 require.FailNow(t, "timeout while waiting for first block signal") 139 case <-n.Ready(): // ready 140 } 141 142 // Check that blockstore have at last one block 143 require.GreaterOrEqual(t, n.BlockStore().Height(), int64(1)) 144 } 145 146 func TestNodeSetAppVersion(t *testing.T) { 147 config, genesisFile := cfg.ResetTestRoot("node_app_version_test") 148 defer os.RemoveAll(config.RootDir) 149 150 // create & start node 151 n, err := DefaultNewNode(config, genesisFile, log.NewTestingLogger(t)) 152 require.NoError(t, err) 153 154 // default config uses the kvstore app 155 appVersion := kvstore.AppVersion 156 157 // check version is set in state 158 state := sm.LoadState(n.stateDB) 159 assert.Equal(t, state.AppVersion, appVersion) 160 161 // check version is set in node info 162 appVersion2, ok := n.nodeInfo.VersionSet.Get("app") 163 assert.True(t, ok) 164 assert.Equal(t, appVersion2.Version, appVersion) 165 } 166 167 func TestNodeSetPrivValTCP(t *testing.T) { 168 addr := "tcp://" + testFreeAddr(t) 169 170 config, genesisFile := cfg.ResetTestRoot("node_priv_val_tcp_test") 171 defer os.RemoveAll(config.RootDir) 172 config.BaseConfig.PrivValidatorListenAddr = addr 173 174 dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey()) 175 dialerEndpoint := privval.NewSignerDialerEndpoint( 176 log.NewTestingLogger(t), 177 dialer, 178 ) 179 privval.SignerDialerEndpointTimeoutReadWrite(100 * time.Millisecond)(dialerEndpoint) 180 181 signerServer := privval.NewSignerServer( 182 dialerEndpoint, 183 config.ChainID(), 184 types.NewMockPV(), 185 ) 186 187 go func() { 188 err := signerServer.Start() 189 if err != nil { 190 panic(err) 191 } 192 }() 193 defer signerServer.Stop() 194 195 n, err := DefaultNewNode(config, genesisFile, log.NewTestingLogger(t)) 196 require.NoError(t, err) 197 assert.IsType(t, &privval.SignerClient{}, n.PrivValidator()) 198 } 199 200 // address without a protocol must result in error 201 func TestPrivValidatorListenAddrNoProtocol(t *testing.T) { 202 addrNoPrefix := testFreeAddr(t) 203 204 config, genesisFile := cfg.ResetTestRoot("node_priv_val_tcp_test") 205 defer os.RemoveAll(config.RootDir) 206 config.BaseConfig.PrivValidatorListenAddr = addrNoPrefix 207 208 _, err := DefaultNewNode(config, genesisFile, log.NewTestingLogger(t)) 209 assert.Error(t, err) 210 } 211 212 func TestNodeSetPrivValIPC(t *testing.T) { 213 tmpfile := "/tmp/kms." + random.RandStr(6) + ".sock" 214 defer os.Remove(tmpfile) // clean up 215 216 config, genesisFile := cfg.ResetTestRoot("node_priv_val_tcp_test") 217 defer os.RemoveAll(config.RootDir) 218 config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile 219 220 dialer := privval.DialUnixFn(tmpfile) 221 dialerEndpoint := privval.NewSignerDialerEndpoint( 222 log.NewTestingLogger(t), 223 dialer, 224 ) 225 privval.SignerDialerEndpointTimeoutReadWrite(100 * time.Millisecond)(dialerEndpoint) 226 227 pvsc := privval.NewSignerServer( 228 dialerEndpoint, 229 config.ChainID(), 230 types.NewMockPV(), 231 ) 232 233 go func() { 234 err := pvsc.Start() 235 require.NoError(t, err) 236 }() 237 defer pvsc.Stop() 238 239 n, err := DefaultNewNode(config, genesisFile, log.NewTestingLogger(t)) 240 require.NoError(t, err) 241 assert.IsType(t, &privval.SignerClient{}, n.PrivValidator()) 242 } 243 244 // testFreeAddr claims a free port so we don't block on listener being ready. 245 func testFreeAddr(t *testing.T) string { 246 t.Helper() 247 248 ln, err := net.Listen("tcp", "127.0.0.1:0") 249 require.NoError(t, err) 250 defer ln.Close() 251 252 return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port) 253 } 254 255 // create a proposal block using real and full 256 // mempool pool and validate it. 257 func TestCreateProposalBlock(t *testing.T) { 258 config, _ := cfg.ResetTestRoot("node_create_proposal") 259 defer os.RemoveAll(config.RootDir) 260 cc := proxy.NewLocalClientCreator(kvstore.NewKVStoreApplication()) 261 proxyApp := appconn.NewAppConns(cc) 262 err := proxyApp.Start() 263 require.Nil(t, err) 264 defer proxyApp.Stop() 265 266 logger := log.NewTestingLogger(t) 267 268 var height int64 = 1 269 state, stateDB := state(1, height) 270 maxBlockBytes := 16384 271 state.ConsensusParams.Block.MaxBlockBytes = int64(maxBlockBytes) 272 proposerAddr, _ := state.Validators.GetByIndex(0) 273 274 // Make Mempool 275 mempool := mempl.NewCListMempool( 276 config.Mempool, 277 proxyApp.Mempool(), 278 state.LastBlockHeight, 279 state.ConsensusParams.Block.MaxTxBytes, 280 mempl.WithPreCheck(sm.TxPreCheck(state)), 281 ) 282 mempool.SetLogger(logger) 283 284 // fill the mempool with more txs 285 // than can fit in a block 286 txLength := 1000 287 for i := 0; i < maxBlockBytes/txLength; i++ { 288 tx := random.RandBytes(txLength) 289 err := mempool.CheckTx(tx, nil) 290 assert.NoError(t, err) 291 } 292 293 blockExec := sm.NewBlockExecutor( 294 stateDB, 295 logger, 296 proxyApp.Consensus(), 297 mempool, 298 ) 299 300 commit := types.NewCommit(types.BlockID{}, nil) 301 block, _ := blockExec.CreateProposalBlock( 302 height, 303 state, commit, 304 proposerAddr, 305 ) 306 307 err = blockExec.ValidateBlock(state, block) 308 assert.NoError(t, err) 309 } 310 311 func TestNodeNewNodeCustomReactors(t *testing.T) { 312 config, genesisFile := cfg.ResetTestRoot("node_new_node_custom_reactors_test") 313 defer os.RemoveAll(config.RootDir) 314 315 cr := p2pmock.NewReactor() 316 customBlockchainReactor := p2pmock.NewReactor() 317 318 nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) 319 require.NoError(t, err) 320 321 n, err := NewNode(config, 322 privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()), 323 nodeKey, 324 proxy.DefaultClientCreator(nil, config.ProxyApp, config.ABCI, config.DBDir()), 325 DefaultGenesisDocProviderFunc(genesisFile), 326 DefaultDBProvider, 327 log.NewTestingLogger(t), 328 CustomReactors(map[string]p2p.Reactor{"FOO": cr, "BLOCKCHAIN": customBlockchainReactor}), 329 ) 330 require.NoError(t, err) 331 332 err = n.Start() 333 require.NoError(t, err) 334 defer n.Stop() 335 336 assert.True(t, cr.IsRunning()) 337 assert.Equal(t, cr, n.Switch().Reactor("FOO")) 338 339 assert.True(t, customBlockchainReactor.IsRunning()) 340 assert.Equal(t, customBlockchainReactor, n.Switch().Reactor("BLOCKCHAIN")) 341 } 342 343 func state(nVals int, height int64) (sm.State, dbm.DB) { 344 vals := make([]types.GenesisValidator, nVals) 345 for i := 0; i < nVals; i++ { 346 secret := []byte(fmt.Sprintf("test%d", i)) 347 pk := ed25519.GenPrivKeyFromSecret(secret) 348 vals[i] = types.GenesisValidator{ 349 Address: pk.PubKey().Address(), 350 PubKey: pk.PubKey(), 351 Power: 1000, 352 Name: fmt.Sprintf("test%d", i), 353 } 354 } 355 s, _ := sm.MakeGenesisState(&types.GenesisDoc{ 356 ChainID: "test-chain", 357 Validators: vals, 358 AppHash: nil, 359 }) 360 361 // save validators to db for 2 heights 362 stateDB := memdb.NewMemDB() 363 sm.SaveState(stateDB, s) 364 365 for i := 1; i < int(height); i++ { 366 s.LastBlockHeight++ 367 s.LastValidators = s.Validators.Copy() 368 sm.SaveState(stateDB, s) 369 } 370 return s, stateDB 371 }