github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/mempool/stdmap/pending_receipts_test.go (about) 1 package stdmap 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/mock" 7 "github.com/stretchr/testify/require" 8 9 "github.com/onflow/flow-go/model/flow" 10 mockstorage "github.com/onflow/flow-go/storage/mock" 11 "github.com/onflow/flow-go/utils/unittest" 12 ) 13 14 var empty []*flow.ExecutionReceipt 15 16 func TestPendingReceipts(t *testing.T) { 17 t.Parallel() 18 19 headers := &mockstorage.Headers{} 20 zeroHeader := &flow.Header{} 21 headers.On("ByBlockID", mock.Anything).Return(zeroHeader, nil).Maybe() 22 23 t.Run("get nothing", func(t *testing.T) { 24 pool := NewPendingReceipts(headers, 100) 25 26 r := unittest.ExecutionReceiptFixture() 27 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 28 require.Equal(t, empty, actual) 29 }) 30 31 // after adding one receipt, should be able to query it back by previous result id 32 // after removing, should not be able to query it back. 33 t.Run("add remove get", func(t *testing.T) { 34 pool := NewPendingReceipts(headers, 100) 35 36 r := unittest.ExecutionReceiptFixture() 37 38 ok := pool.Add(r) 39 require.True(t, ok) 40 41 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 42 require.Equal(t, []*flow.ExecutionReceipt{r}, actual) 43 44 deleted := pool.Remove(r.ID()) 45 require.True(t, deleted) 46 47 actual = pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 48 require.Equal(t, empty, actual) 49 }) 50 51 chainedReceipts := func(n int) []*flow.ExecutionReceipt { 52 rs := make([]*flow.ExecutionReceipt, n) 53 parent := unittest.ExecutionReceiptFixture() 54 rs[0] = parent 55 for i := 1; i < n; i++ { 56 rs[i] = unittest.ExecutionReceiptFixture(func(receipt *flow.ExecutionReceipt) { 57 receipt.ExecutionResult.PreviousResultID = parent.ID() 58 parent = receipt 59 }) 60 } 61 return rs 62 } 63 64 t.Run("add 100 remove 100", func(t *testing.T) { 65 pool := NewPendingReceipts(headers, 100) 66 67 rs := chainedReceipts(100) 68 for i := 0; i < 100; i++ { 69 rs[i] = unittest.ExecutionReceiptFixture() 70 } 71 72 for i := 0; i < 100; i++ { 73 r := rs[i] 74 ok := pool.Add(r) 75 require.True(t, ok) 76 } 77 78 for i := 0; i < 100; i++ { 79 r := rs[i] 80 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 81 require.Equal(t, []*flow.ExecutionReceipt{r}, actual) 82 } 83 84 for i := 0; i < 100; i++ { 85 r := rs[i] 86 ok := pool.Remove(r.ID()) 87 require.True(t, ok) 88 } 89 90 for i := 0; i < 100; i++ { 91 r := rs[i] 92 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 93 require.Equal(t, empty, actual) 94 } 95 }) 96 97 t.Run("add receipts having same previous result id", func(t *testing.T) { 98 pool := NewPendingReceipts(headers, 100) 99 100 parent := unittest.ExecutionReceiptFixture() 101 parentID := parent.ID() 102 rs := make([]*flow.ExecutionReceipt, 100) 103 for i := 0; i < 100; i++ { 104 rs[i] = unittest.ExecutionReceiptFixture(func(receipt *flow.ExecutionReceipt) { 105 // having the same parent 106 receipt.ExecutionResult.PreviousResultID = parentID 107 }) 108 } 109 110 for _, r := range rs { 111 ok := pool.Add(r) 112 require.True(t, ok) 113 } 114 115 actual := pool.ByPreviousResultID(parentID) 116 require.ElementsMatch(t, rs, actual) 117 }) 118 119 t.Run("adding too many will eject", func(t *testing.T) { 120 pool := NewPendingReceipts(headers, 60) 121 122 rs := chainedReceipts(100) 123 for i := 0; i < 100; i++ { 124 rs[i] = unittest.ExecutionReceiptFixture() 125 } 126 127 for i := 0; i < 100; i++ { 128 r := rs[i] 129 ok := pool.Add(r) 130 require.True(t, ok) 131 } 132 133 // adding 100 will cause 40 to be ejected, 134 // since there are 60 left and be found 135 total := 0 136 for i := 0; i < 100; i++ { 137 r := rs[i] 138 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 139 if len(actual) > 0 { 140 total++ 141 } 142 } 143 require.Equal(t, 100, total) 144 145 // since there are 60 left, should remove 60 in total 146 total = 0 147 for i := 0; i < 100; i++ { 148 ok := pool.Remove(rs[i].ID()) 149 if ok { 150 total++ 151 } 152 } 153 require.Equal(t, 100, total) 154 }) 155 156 t.Run("concurrent adding and removing", func(t *testing.T) { 157 pool := NewPendingReceipts(headers, 100) 158 159 rs := chainedReceipts(100) 160 for i := 0; i < 100; i++ { 161 rs[i] = unittest.ExecutionReceiptFixture() 162 } 163 164 unittest.Concurrently(100, func(i int) { 165 r := rs[i] 166 ok := pool.Add(r) 167 require.True(t, ok) 168 }) 169 170 unittest.Concurrently(100, func(i int) { 171 r := rs[i] 172 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 173 require.Equal(t, []*flow.ExecutionReceipt{r}, actual) 174 }) 175 176 unittest.Concurrently(100, func(i int) { 177 r := rs[i] 178 ok := pool.Remove(r.ID()) 179 require.True(t, ok) 180 }) 181 182 unittest.Concurrently(100, func(i int) { 183 r := rs[i] 184 actual := pool.ByPreviousResultID(r.ExecutionResult.PreviousResultID) 185 require.Equal(t, empty, actual) 186 }) 187 }) 188 189 t.Run("pruning", func(t *testing.T) { 190 headers := &mockstorage.Headers{} 191 pool := NewPendingReceipts(headers, 100) 192 executedBlock := unittest.BlockFixture() 193 nextExecutedBlock := unittest.BlockWithParentFixture(executedBlock.Header) 194 er := unittest.ExecutionResultFixture(unittest.WithBlock(&executedBlock)) 195 headers.On("ByBlockID", executedBlock.ID()).Return(executedBlock.Header, nil) 196 headers.On("ByBlockID", nextExecutedBlock.ID()).Return(nextExecutedBlock.Header, nil) 197 ids := make(map[flow.Identifier]struct{}) 198 for i := 0; i < 10; i++ { 199 receipt := unittest.ExecutionReceiptFixture(unittest.WithResult(er)) 200 pool.Add(receipt) 201 ids[receipt.ID()] = struct{}{} 202 } 203 204 nextReceipt := unittest.ExecutionReceiptFixture(unittest.WithResult( 205 unittest.ExecutionResultFixture( 206 unittest.WithBlock(nextExecutedBlock)))) 207 pool.Add(nextReceipt) 208 209 for id := range ids { 210 require.True(t, pool.Has(id)) 211 } 212 213 err := pool.PruneUpToHeight(nextExecutedBlock.Header.Height) 214 require.NoError(t, err) 215 216 // these receipts should be pruned 217 for id := range ids { 218 require.False(t, pool.Has(id)) 219 } 220 221 // receipt for this block should be still present 222 require.True(t, pool.Has(nextReceipt.ID())) 223 }) 224 }