github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/state/events_test.go (about) 1 package state 2 3 import ( 4 bin "encoding/binary" 5 "fmt" 6 "testing" 7 8 "github.com/hyperledger/burrow/binary" 9 "github.com/hyperledger/burrow/config/source" 10 "github.com/hyperledger/burrow/crypto" 11 "github.com/hyperledger/burrow/execution/exec" 12 "github.com/hyperledger/burrow/storage" 13 "github.com/hyperledger/burrow/txs" 14 "github.com/hyperledger/burrow/txs/payload" 15 "github.com/stretchr/testify/require" 16 dbm "github.com/tendermint/tm-db" 17 ) 18 19 func TestWriteState_AddBlock(t *testing.T) { 20 s := NewState(dbm.NewMemDB()) 21 height := uint64(100) 22 numTxs := uint64(5) 23 events := uint64(10) 24 addBlock(t, s, height, numTxs, events) 25 26 txIndex := uint64(0) 27 eventIndex := uint64(0) 28 err := s.IterateStreamEvents(&height, &height, storage.AscendingSort, func(ev *exec.StreamEvent) error { 29 switch { 30 case ev.BeginTx != nil: 31 eventIndex = 0 32 case ev.Event != nil: 33 require.Equal(t, mkEvent(height, txIndex, eventIndex).Header.TxHash.String(), 34 ev.Event.Header.TxHash.String(), "event TxHash mismatch at tx #%d event #%d", 35 txIndex, eventIndex) 36 eventIndex++ 37 case ev.EndTx != nil: 38 txIndex++ 39 } 40 return nil 41 }) 42 require.NoError(t, err) 43 require.Equal(t, numTxs, txIndex, "should have observed all txs") 44 // non-increasing events 45 _, _, err = s.Update(func(ws Updatable) error { 46 return nil 47 }) 48 require.NoError(t, err) 49 50 txExecutions, err := s.TxsAtHeight(height) 51 require.NoError(t, err) 52 require.NotNil(t, txExecutions) 53 require.Equal(t, numTxs, uint64(len(txExecutions))) 54 } 55 56 func TestNestedTxs(t *testing.T) { 57 s := NewState(dbm.NewMemDB()) 58 height := uint64(2) 59 numTxs := uint64(4) 60 events := uint64(2) 61 nesting := uint64(3) 62 block := mkBlock(height, numTxs, events) 63 txes := block.TxExecutions 64 // Deeply nest transactions inside block 65 for i := uint64(0); i < nesting; i++ { 66 var next []*exec.TxExecution 67 for _, txe := range txes { 68 next = append(next, nestTxs(txe, height, events+i, numTxs+i)...) 69 } 70 txes = next 71 } 72 _, _, err := s.Update(func(ws Updatable) error { 73 return ws.AddBlock(block) 74 }) 75 require.NoError(t, err) 76 txes, err = s.TxsAtHeight(height) 77 require.NoError(t, err) 78 txsCount := deepCountTxs(block.TxExecutions) 79 require.Equal(t, txsCount, deepCountTxs(txes)) 80 // There is a geometric-arithmetic sum here... but empiricism FTW 81 require.Equal(t, 580, txsCount) 82 } 83 84 func TestReadState_TxByHash(t *testing.T) { 85 s := NewState(dbm.NewMemDB()) 86 maxHeight := uint64(3) 87 numTxs := uint64(4) 88 events := uint64(2) 89 for height := uint64(0); height < maxHeight; height++ { 90 block := mkBlock(height, numTxs, events) 91 _, _, err := s.Update(func(ws Updatable) error { 92 return ws.AddBlock(block) 93 }) 94 require.NoError(t, err) 95 } 96 97 hashSet := make(map[string]bool) 98 for height := uint64(0); height < maxHeight; height++ { 99 for txIndex := uint64(0); txIndex < numTxs; txIndex++ { 100 // Find this tx 101 tx := mkTxExecution(height, txIndex, events) 102 txHash := tx.TxHash.String() 103 // Check we have no duplicates (indicates problem with how we are generating hashes for these tests 104 require.False(t, hashSet[txHash], "should be no duplicate tx hashes") 105 hashSet[txHash] = true 106 // Try and pull the Tx by its hash 107 txOut, err := s.TxByHash(tx.TxHash) 108 require.NoError(t, err) 109 require.NotNil(t, txOut, "should retrieve non-nil transaction by TxHash %v", tx.TxHash) 110 // Make sure we get the same tx 111 require.Equal(t, txHash, txOut.TxHash.String(), "TxHash does not match as string") 112 require.Equal(t, source.JSONString(tx), source.JSONString(txOut)) 113 } 114 } 115 } 116 117 func TestLastBlockStored(t *testing.T) { 118 s := NewState(dbm.NewMemDB()) 119 // Add first block 120 addBlock(t, s, uint64(1), 2, 3) 121 lastStoredHeight, err := s.LastStoredHeight() 122 require.NoError(t, err) 123 require.Equal(t, lastStoredHeight, uint64(1)) 124 125 // Add empty block 126 addBlock(t, s, uint64(2), 0, 0) 127 lastStoredHeight, err = s.LastStoredHeight() 128 require.NoError(t, err) 129 // Same last stored height 130 require.Equal(t, lastStoredHeight, uint64(1)) 131 132 // Add non-empty block 133 addBlock(t, s, uint64(3), 1, 0) 134 lastStoredHeight, err = s.LastStoredHeight() 135 require.NoError(t, err) 136 // Same last stored height 137 require.Equal(t, lastStoredHeight, uint64(3)) 138 } 139 140 func BenchmarkAddBlockAndIterator(b *testing.B) { 141 s := NewState(dbm.NewMemDB()) 142 numTxs := uint64(5) 143 events := uint64(10) 144 for height := uint64(0); height < 2000; height++ { 145 block := mkBlock(height, numTxs, events) 146 _, _, err := s.Update(func(ws Updatable) error { 147 return ws.AddBlock(block) 148 }) 149 require.NoError(b, err) 150 } 151 err := s.IterateStreamEvents(nil, nil, storage.AscendingSort, func(ev *exec.StreamEvent) error { 152 return nil 153 }) 154 require.NoError(b, err) 155 } 156 157 func addBlock(t testing.TB, s *State, height, numTxs, events uint64) { 158 block := mkBlock(height, numTxs, events) 159 _, _, err := s.Update(func(ws Updatable) error { 160 return ws.AddBlock(block) 161 }) 162 require.NoError(t, err) 163 } 164 165 func deepCountTxs(txes []*exec.TxExecution) int { 166 sum := len(txes) 167 for _, txe := range txes { 168 sum += deepCountTxs(txe.TxExecutions) 169 } 170 return sum 171 } 172 173 func nestTxs(txe *exec.TxExecution, height, events, numTxs uint64) []*exec.TxExecution { 174 txes := make([]*exec.TxExecution, numTxs) 175 for i := uint64(0); i < numTxs; i++ { 176 txes[i] = mkTxExecution(height, i, events) 177 txe.TxExecutions = append(txe.TxExecutions, txes[i]) 178 } 179 return txes 180 } 181 182 func mkBlock(height, numTxs, events uint64) *exec.BlockExecution { 183 be := &exec.BlockExecution{ 184 Height: height, 185 } 186 for ti := uint64(0); ti < numTxs; ti++ { 187 txe := mkTxExecution(height, ti, events) 188 be.TxExecutions = append(be.TxExecutions, txe) 189 } 190 return be 191 } 192 193 func mkTxExecution(height, txIndex, events uint64) *exec.TxExecution { 194 hash := make([]byte, 32) 195 bin.BigEndian.PutUint64(hash[:8], height) 196 bin.BigEndian.PutUint64(hash[8:16], txIndex) 197 bin.BigEndian.PutUint64(hash[16:24], events) 198 txEnv := txs.Enclose("ChainTheFirst", mkTx()) 199 txe := &exec.TxExecution{ 200 TxHeader: &exec.TxHeader{ 201 TxHash: hash, 202 Height: height, 203 Index: txIndex, 204 }, 205 Envelope: txEnv, 206 Receipt: txEnv.Tx.GenerateReceipt(), 207 } 208 for e := uint64(0); e < events; e++ { 209 txe.Events = append(txe.Events, mkEvent(height, txIndex, e)) 210 } 211 return txe 212 } 213 214 func mkTx() payload.Payload { 215 return &payload.CallTx{ 216 Input: &payload.TxInput{ 217 Address: crypto.Address{1, 2, 3}, 218 Amount: 12345, 219 Sequence: 67890, 220 }, 221 GasLimit: 111, 222 Fee: 222, 223 Data: []byte("data1"), 224 } 225 } 226 227 func mkEvent(height, tx, index uint64) *exec.Event { 228 return &exec.Event{ 229 Header: &exec.Header{ 230 Height: height, 231 Index: index, 232 TxHash: crypto.Keccak256([]byte(fmt.Sprintf("txhash%v%v%v", height, tx, index))), 233 EventID: fmt.Sprintf("eventID: %v%v%v", height, tx, index), 234 }, 235 Log: &exec.LogEvent{ 236 Address: crypto.Address{byte(height), byte(index)}, 237 Topics: []binary.Word256{{1, 2, 3}}, 238 }, 239 } 240 }