github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/integration/core/kernel_test.go (about) 1 // +build integration 2 3 package core 4 5 import ( 6 "bufio" 7 "context" 8 "fmt" 9 "os" 10 "syscall" 11 "testing" 12 "time" 13 14 "github.com/hyperledger/burrow/acm" 15 "github.com/hyperledger/burrow/acm/balance" 16 "github.com/hyperledger/burrow/config" 17 "github.com/hyperledger/burrow/event" 18 "github.com/hyperledger/burrow/execution/exec" 19 "github.com/hyperledger/burrow/execution/solidity" 20 "github.com/hyperledger/burrow/genesis" 21 "github.com/hyperledger/burrow/integration" 22 "github.com/hyperledger/burrow/integration/rpctest" 23 "github.com/hyperledger/burrow/keys" 24 "github.com/hyperledger/burrow/logging/logconfig" 25 "github.com/hyperledger/burrow/logging/loggers" 26 "github.com/hyperledger/burrow/rpc/rpctransact" 27 "github.com/hyperledger/burrow/txs/payload" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/status" 32 ) 33 34 func TestKernel(t *testing.T) { 35 testKernel(t) 36 } 37 38 func TestKernelNoConsensus(t *testing.T) { 39 testKernel(t, integration.NoConsensus) 40 } 41 42 func testKernel(t *testing.T, opts ...func(*config.BurrowConfig)) { 43 t.Run(fmt.Sprintf("Group"), func(t *testing.T) { 44 t.Parallel() 45 genesisDoc, privateAccounts, privateValidators := genesis.NewDeterministicGenesis(123).GenesisDoc(1, 1) 46 require.NotNil(t, privateAccounts) 47 require.NotNil(t, privateValidators) 48 t.Run("BootThenShutdown", func(t *testing.T) { 49 conf, cleanup := integration.NewTestConfig(genesisDoc, opts...) 50 defer cleanup() 51 require.NotNil(t, privateAccounts) 52 require.NotNil(t, privateValidators) 53 assert.NoError(t, bootWaitBlocksShutdown(t, privateValidators[0], privateAccounts, conf, nil)) 54 }) 55 56 t.Run("BootShutdownResume", func(t *testing.T) { 57 testConfig, cleanup := integration.NewTestConfig(genesisDoc, opts...) 58 defer cleanup() 59 i := uint64(0) 60 // asserts we get a consecutive run of blocks 61 blockChecker := func(block *exec.BlockExecution) bool { 62 if i == 0 { 63 // We send some synchronous transactions so catch up to latest block 64 i = block.Height - 1 65 } 66 require.Equal(t, i+1, block.Height) 67 i++ 68 // stop every third block 69 if i%3 == 0 { 70 i = 0 71 return false 72 } 73 return true 74 } 75 // First run 76 err := bootWaitBlocksShutdown(t, privateValidators[0], privateAccounts, testConfig, blockChecker) 77 require.NoError(t, err) 78 // Resume and check we pick up where we left off 79 err = bootWaitBlocksShutdown(t, privateValidators[0], privateAccounts, testConfig, blockChecker) 80 require.NoError(t, err) 81 // Resuming with mismatched genesis should fail 82 genesisDoc.Salt = []byte("foo") 83 err = bootWaitBlocksShutdown(t, privateValidators[0], privateAccounts, testConfig, blockChecker) 84 assert.Error(t, err) 85 }) 86 87 t.Run("LoggingSignals", func(t *testing.T) { 88 conf, cleanup := integration.NewTestConfig(genesisDoc, opts...) 89 defer cleanup() 90 name := "capture" 91 buffer := 100 92 path := "foo.json" 93 conf.Logging = logconfig.New(). 94 Root(func(sink *logconfig.SinkConfig) *logconfig.SinkConfig { 95 return sink.SetTransform(logconfig.CaptureTransform(name, buffer, false)). 96 SetOutput(logconfig.FileOutput(path).SetFormat(loggers.JSONFormat)) 97 }) 98 i := 0 99 gap := 1 100 assert.NoError(t, bootWaitBlocksShutdown(t, privateValidators[0], privateAccounts, conf, 101 func(block *exec.BlockExecution) (cont bool) { 102 if i == gap { 103 // Send sync signal 104 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 105 } 106 if i == gap*2 { 107 // Send reload signal (shouldn't dump capture logger) 108 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 109 } 110 if i == gap*3 { 111 return false 112 } 113 i++ 114 return true 115 })) 116 f, err := os.OpenFile(path, os.O_RDONLY, 0644) 117 require.NoError(t, err) 118 n := 0 119 scanner := bufio.NewScanner(f) 120 for scanner.Scan() { 121 n++ 122 } 123 // We may spill a few writes in ring buffer 124 assert.InEpsilon(t, buffer*2, n, 10) 125 }) 126 127 }) 128 } 129 130 func bootWaitBlocksShutdown(t testing.TB, validator *acm.PrivateAccount, privateAccounts []*acm.PrivateAccount, 131 testConfig *config.BurrowConfig, blockChecker func(block *exec.BlockExecution) (cont bool)) error { 132 133 kern, err := integration.TestKernel(validator, rpctest.PrivateAccounts, testConfig) 134 if err != nil { 135 return err 136 } 137 138 kern.SetKeyClient(keys.NewLocalKeyClient(keys.NewMemoryKeyStore(privateAccounts...), kern.Logger)) 139 kern.SetKeyStore(keys.NewFilesystemKeyStore(keys.DefaultKeysDir, false)) 140 ctx := context.Background() 141 if err = kern.Boot(); err != nil { 142 return err 143 } 144 145 inputAddress := privateAccounts[0].GetAddress() 146 tcli := rpctest.NewTransactClient(t, kern.GRPCListenAddress().String()) 147 148 subID := event.GenSubID() 149 ch, err := kern.Emitter.Subscribe(ctx, subID, exec.QueryForBlockExecution(), 10) 150 if err != nil { 151 return err 152 } 153 defer kern.Emitter.UnsubscribeAll(ctx, subID) 154 155 stopCh := make(chan struct{}) 156 // Catch first error only 157 errCh := make(chan error, 1) 158 // Generate a few transactions concurrent with restarts 159 go func() { 160 pow := testConfig.GenesisDoc.Validators[0].Amount 161 for { 162 // Fire and forget - we can expect a few to fail since we are restarting kernel 163 txe, err := tcli.CallTxSync(ctx, &payload.CallTx{ 164 Input: &payload.TxInput{ 165 Address: inputAddress, 166 Amount: 2, 167 }, 168 Address: nil, 169 Data: solidity.Bytecode_StrangeLoop, 170 Fee: 2, 171 GasLimit: 10000, 172 }) 173 handleTxe(txe, err, errCh) 174 175 txe, err = tcli.BroadcastTxSync(ctx, &rpctransact.TxEnvelopeParam{ 176 Payload: &payload.Any{ 177 GovTx: payload.AlterBalanceTx(inputAddress, validator, balance.New().Power(pow)), 178 }, 179 }) 180 handleTxe(txe, err, errCh) 181 select { 182 case <-stopCh: 183 close(errCh) 184 return 185 default: 186 time.Sleep(time.Millisecond) 187 } 188 pow += 100 189 } 190 }() 191 192 cont := true 193 for cont { 194 select { 195 case <-time.After(2 * time.Second): 196 return fmt.Errorf("timed out waiting for block") 197 case msg := <-ch: 198 if blockChecker == nil { 199 cont = false 200 } else { 201 cont = blockChecker(msg.(*exec.BlockExecution)) 202 } 203 } 204 } 205 206 close(stopCh) 207 208 for err := range errCh { 209 return err 210 } 211 212 return kern.Shutdown(ctx) 213 } 214 215 func handleTxe(txe *exec.TxExecution, err error, errCh chan<- error) { 216 if err == nil { 217 err = txe.Exception.AsError() 218 } 219 if err != nil { 220 statusError := status.Convert(err) 221 // We expect the GRPC service to be unavailable when we restart 222 if statusError != nil && statusError.Code() != codes.Unavailable { 223 // Don't block - we'll just capture first error 224 select { 225 case errCh <- err: 226 default: 227 228 } 229 } 230 } 231 }