|
| 1 | +package types |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "testing" |
| 6 | + "time" |
| 7 | + |
| 8 | + "github.com/gogo/protobuf/proto" |
| 9 | + "github.com/stretchr/testify/assert" |
| 10 | + "github.com/stretchr/testify/require" |
| 11 | + cmtrand "github.com/tendermint/tendermint/libs/rand" |
| 12 | + "github.com/tendermint/tendermint/proto/tendermint/mempool" |
| 13 | + "github.com/tendermint/tendermint/state" |
| 14 | + "github.com/tendermint/tendermint/types" |
| 15 | +) |
| 16 | + |
| 17 | +// TestTxsToParts extensive testing of the txs to parts method |
| 18 | +// that recovers the parts from mempool txs. |
| 19 | +func TestTxsToParts(t *testing.T) { |
| 20 | + cleanup, _, sm := state.SetupTestCase(t) |
| 21 | + t.Cleanup(func() { |
| 22 | + cleanup(t) |
| 23 | + }) |
| 24 | + numberOfTxs := 16 // increasing the number of transactions increases the test time exponentially |
| 25 | + |
| 26 | + tests := []struct { |
| 27 | + name string |
| 28 | + txs []types.Tx |
| 29 | + }{ |
| 30 | + { |
| 31 | + name: "txs size == types.BlockPartSizeBytes/3", |
| 32 | + txs: func() []types.Tx { |
| 33 | + txs := make([]types.Tx, 0, numberOfTxs) |
| 34 | + for i := 0; i < numberOfTxs; i++ { |
| 35 | + txs = append(txs, cmtrand.Bytes(int(types.BlockPartSizeBytes/3))) |
| 36 | + } |
| 37 | + return txs |
| 38 | + }(), |
| 39 | + }, |
| 40 | + { |
| 41 | + name: "txs size == types.BlockPartSizeBytes", |
| 42 | + txs: func() []types.Tx { |
| 43 | + txs := make([]types.Tx, 0, numberOfTxs) |
| 44 | + for i := 0; i < numberOfTxs; i++ { |
| 45 | + txs = append(txs, cmtrand.Bytes(int(types.BlockPartSizeBytes))) |
| 46 | + } |
| 47 | + return txs |
| 48 | + }(), |
| 49 | + }, |
| 50 | + { |
| 51 | + name: "txs size == types.BlockPartSizeBytes * 3", |
| 52 | + txs: func() []types.Tx { |
| 53 | + txs := make([]types.Tx, 0, numberOfTxs) |
| 54 | + for i := 0; i < numberOfTxs; i++ { |
| 55 | + txs = append(txs, cmtrand.Bytes(int(types.BlockPartSizeBytes)*3)) |
| 56 | + } |
| 57 | + return txs |
| 58 | + }(), |
| 59 | + }, |
| 60 | + } |
| 61 | + |
| 62 | + for _, test := range tests { |
| 63 | + t.Run(test.name, func(t *testing.T) { |
| 64 | + data := types.Data{Txs: test.txs} |
| 65 | + block, partSet := sm.MakeBlock(1, data, types.RandCommit(time.Now()), []types.Evidence{}, cmtrand.Bytes(20)) |
| 66 | + |
| 67 | + txsFound := make([]UnmarshalledTx, len(partSet.TxPos)) |
| 68 | + for i, pos := range partSet.TxPos { |
| 69 | + // calculate the protobuf overhead |
| 70 | + protoTxs := mempool.Txs{Txs: [][]byte{data.Txs[i]}} |
| 71 | + marshalledTx, err := proto.Marshal(&protoTxs) |
| 72 | + require.NoError(t, err) |
| 73 | + |
| 74 | + txKey, err := types.TxKeyFromBytes(block.Txs[i].Hash()) |
| 75 | + require.NoError(t, err) |
| 76 | + txsFound[i] = UnmarshalledTx{ |
| 77 | + MetaData: TxMetaData{ |
| 78 | + Start: uint32(pos.Start), |
| 79 | + End: uint32(pos.End), |
| 80 | + Hash: block.Txs[i].Hash(), |
| 81 | + }, |
| 82 | + Key: txKey, |
| 83 | + TxBytes: marshalledTx, |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + // generate all the possible combinations for the provided number of transactions |
| 88 | + txsCombinations := GenerateTxsCombinations(numberOfTxs) |
| 89 | + |
| 90 | + for _, combination := range txsCombinations { |
| 91 | + t.Run(fmt.Sprintf("%v", combination), func(t *testing.T) { |
| 92 | + combinationTxs := make([]UnmarshalledTx, 0) |
| 93 | + for index, val := range combination { |
| 94 | + if val == 1 { |
| 95 | + combinationTxs = append(combinationTxs, txsFound[index]) |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + parts := TxsToParts(combinationTxs) |
| 100 | + |
| 101 | + for _, part := range parts { |
| 102 | + expectedPart := partSet.GetPart(int(part.Index)) |
| 103 | + assert.Equal(t, expectedPart.Bytes, part.Bytes) |
| 104 | + } |
| 105 | + }) |
| 106 | + } |
| 107 | + }) |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +// GenerateTxsCombinations generates all relevant transaction placements |
| 112 | +// in a block given a number of transactions. |
| 113 | +func GenerateTxsCombinations(n int) [][]int { |
| 114 | + total := 1 << n // 2^n combinations |
| 115 | + result := make([][]int, 0) |
| 116 | + |
| 117 | + for i := 0; i < total; i++ { |
| 118 | + bitArray := make([]int, n) |
| 119 | + for j := 0; j < n; j++ { |
| 120 | + // Extract the bit at position j |
| 121 | + bitArray[j] = (i >> j) & 1 |
| 122 | + } |
| 123 | + result = append(result, bitArray) |
| 124 | + } |
| 125 | + return result |
| 126 | +} |
0 commit comments