github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/p2p/ipld/write_test.go (about) 1 package ipld 2 3 import ( 4 "context" 5 mrand "math/rand" 6 "testing" 7 "time" 8 9 mdutils "github.com/ipfs/go-merkledag/test" 10 "github.com/lazyledger/nmt" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 abci "github.com/lazyledger/lazyledger-core/abci/types" 15 "github.com/lazyledger/lazyledger-core/crypto/tmhash" 16 "github.com/lazyledger/lazyledger-core/ipfs" 17 "github.com/lazyledger/lazyledger-core/ipfs/plugin" 18 "github.com/lazyledger/lazyledger-core/libs/log" 19 tmproto "github.com/lazyledger/lazyledger-core/proto/tendermint/types" 20 "github.com/lazyledger/lazyledger-core/types" 21 "github.com/lazyledger/lazyledger-core/types/consts" 22 ) 23 24 func TestPutBlock(t *testing.T) { 25 logger := log.TestingLogger() 26 dag := mdutils.Mock() 27 croute := ipfs.MockRouting() 28 29 maxOriginalSquareSize := consts.MaxSquareSize / 2 30 maxShareCount := maxOriginalSquareSize * maxOriginalSquareSize 31 32 testCases := []struct { 33 name string 34 blockData types.Data 35 expectErr bool 36 errString string 37 }{ 38 {"no leaves", generateRandomMsgOnlyData(0), false, ""}, 39 {"single leaf", generateRandomMsgOnlyData(1), false, ""}, 40 {"16 leaves", generateRandomMsgOnlyData(16), false, ""}, 41 {"max square size", generateRandomMsgOnlyData(maxShareCount), false, ""}, 42 } 43 ctx := context.Background() 44 for _, tc := range testCases { 45 tc := tc 46 47 block := &types.Block{Data: tc.blockData} 48 49 t.Run(tc.name, func(t *testing.T) { 50 err := PutBlock(ctx, dag, block, croute, logger) 51 if tc.expectErr { 52 require.Error(t, err) 53 require.Contains(t, err.Error(), tc.errString) 54 return 55 } 56 57 require.NoError(t, err) 58 59 timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) 60 defer cancel() 61 62 block.Hash() 63 for _, rowRoot := range block.DataAvailabilityHeader.RowsRoots.Bytes() { 64 // recreate the cids using only the computed roots 65 cid, err := plugin.CidFromNamespacedSha256(rowRoot) 66 if err != nil { 67 t.Error(err) 68 } 69 70 // retrieve the data from IPFS 71 _, err = dag.Get(timeoutCtx, cid) 72 if err != nil { 73 t.Errorf("Root not found: %s", cid.String()) 74 } 75 } 76 }) 77 } 78 } 79 80 type preprocessingApp struct { 81 abci.BaseApplication 82 } 83 84 func (app *preprocessingApp) PreprocessTxs( 85 req abci.RequestPreprocessTxs) abci.ResponsePreprocessTxs { 86 time.Sleep(time.Second * 2) 87 randTxs := generateRandTxs(64, 256) 88 randMsgs := generateRandNamespacedRawData(128, nmt.DefaultNamespaceIDLen, 256) 89 randMessages := toMessageSlice(randMsgs) 90 return abci.ResponsePreprocessTxs{ 91 Txs: append(req.Txs, randTxs...), 92 Messages: &tmproto.Messages{MessagesList: randMessages}, 93 } 94 } 95 func generateRandTxs(num int, size int) [][]byte { 96 randMsgs := generateRandNamespacedRawData(num, nmt.DefaultNamespaceIDLen, size) 97 for _, msg := range randMsgs { 98 copy(msg[:nmt.DefaultNamespaceIDLen], consts.TxNamespaceID) 99 } 100 return randMsgs 101 } 102 103 func toMessageSlice(msgs [][]byte) []*tmproto.Message { 104 res := make([]*tmproto.Message, len(msgs)) 105 for i := 0; i < len(msgs); i++ { 106 res[i] = &tmproto.Message{NamespaceId: msgs[i][:nmt.DefaultNamespaceIDLen], Data: msgs[i][nmt.DefaultNamespaceIDLen:]} 107 } 108 return res 109 } 110 111 func TestDataAvailabilityHeaderRewriteBug(t *testing.T) { 112 logger := log.TestingLogger() 113 dag := mdutils.Mock() 114 croute := ipfs.MockRouting() 115 116 txs := types.Txs{} 117 l := len(txs) 118 bzs := make([][]byte, l) 119 for i := 0; i < l; i++ { 120 bzs[i] = txs[i] 121 } 122 app := &preprocessingApp{} 123 124 // See state.CreateProposalBlock to understand why we do this here: 125 processedBlockTxs := app.PreprocessTxs(abci.RequestPreprocessTxs{Txs: bzs}) 126 ppt := processedBlockTxs.GetTxs() 127 128 pbmessages := processedBlockTxs.GetMessages() 129 130 lp := len(ppt) 131 processedTxs := make(types.Txs, lp) 132 if lp > 0 { 133 for i := 0; i < l; i++ { 134 processedTxs[i] = ppt[i] 135 } 136 } 137 138 messages := types.MessagesFromProto(pbmessages) 139 lastID := makeBlockIDRandom() 140 h := int64(3) 141 142 voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) 143 commit, err := types.MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) 144 assert.NoError(t, err) 145 block := types.MakeBlock(1, processedTxs, nil, nil, messages, commit) 146 block.Hash() 147 148 hash1 := block.DataAvailabilityHeader.Hash() 149 150 ctx := context.TODO() 151 err = PutBlock(ctx, dag, block, croute, logger) 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 block.Hash() 157 hash2 := block.DataAvailabilityHeader.Hash() 158 assert.Equal(t, hash1, hash2) 159 160 } 161 162 func generateRandomMsgOnlyData(msgCount int) types.Data { 163 out := make([]types.Message, msgCount) 164 for i, msg := range generateRandNamespacedRawData(msgCount, consts.NamespaceSize, consts.MsgShareSize-2) { 165 out[i] = types.Message{NamespaceID: msg[:consts.NamespaceSize], Data: msg[consts.NamespaceSize:]} 166 } 167 return types.Data{ 168 Messages: types.Messages{MessagesList: out}, 169 } 170 } 171 172 func makeBlockIDRandom() types.BlockID { 173 var ( 174 blockHash = make([]byte, tmhash.Size) 175 partSetHash = make([]byte, tmhash.Size) 176 ) 177 mrand.Read(blockHash) 178 mrand.Read(partSetHash) 179 return types.BlockID{ 180 Hash: blockHash, 181 PartSetHeader: types.PartSetHeader{ 182 Total: 123, 183 Hash: partSetHash, 184 }, 185 } 186 } 187 188 func randVoteSet( 189 height int64, 190 round int32, 191 signedMsgType tmproto.SignedMsgType, 192 numValidators int, 193 votingPower int64, 194 ) (*types.VoteSet, *types.ValidatorSet, []types.PrivValidator) { 195 valSet, privValidators := types.RandValidatorSet(numValidators, votingPower) 196 return types.NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators 197 }