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  }