github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/block/block_test.go (about) 1 // Copyright (c) 2022 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package block 7 8 import ( 9 "encoding/hex" 10 "fmt" 11 "math/big" 12 "math/rand" 13 "testing" 14 "time" 15 16 "github.com/stretchr/testify/require" 17 "go.uber.org/zap" 18 "google.golang.org/protobuf/types/known/timestamppb" 19 20 "github.com/iotexproject/go-pkgs/hash" 21 "github.com/iotexproject/iotex-core/action" 22 "github.com/iotexproject/iotex-core/pkg/compress" 23 "github.com/iotexproject/iotex-core/pkg/log" 24 "github.com/iotexproject/iotex-core/pkg/unit" 25 "github.com/iotexproject/iotex-core/pkg/version" 26 "github.com/iotexproject/iotex-core/test/identityset" 27 "github.com/iotexproject/iotex-core/testutil" 28 "github.com/iotexproject/iotex-proto/golang/iotextypes" 29 ) 30 31 func TestMerkle(t *testing.T) { 32 require := require.New(t) 33 34 producerAddr := identityset.Address(27).String() 35 producerPubKey := identityset.PrivateKey(27).PublicKey() 36 producerPriKey := identityset.PrivateKey(27) 37 amount := uint64(50 << 22) 38 // create testing transactions 39 selp0, err := action.SignedTransfer(producerAddr, producerPriKey, 1, big.NewInt(int64(amount)), nil, 100, big.NewInt(0)) 40 require.NoError(err) 41 42 selp1, err := action.SignedTransfer(identityset.Address(28).String(), producerPriKey, 1, big.NewInt(int64(amount)), nil, 100, big.NewInt(0)) 43 require.NoError(err) 44 45 selp2, err := action.SignedTransfer(identityset.Address(29).String(), producerPriKey, 1, big.NewInt(int64(amount)), nil, 100, big.NewInt(0)) 46 require.NoError(err) 47 48 selp3, err := action.SignedTransfer(identityset.Address(30).String(), producerPriKey, 1, big.NewInt(int64(amount)), nil, 100, big.NewInt(0)) 49 require.NoError(err) 50 51 selp4, err := action.SignedTransfer(identityset.Address(32).String(), producerPriKey, 1, big.NewInt(int64(amount)), nil, 100, big.NewInt(0)) 52 require.NoError(err) 53 54 // create block using above 5 tx and verify merkle 55 actions := []*action.SealedEnvelope{selp0, selp1, selp2, selp3, selp4} 56 block := NewBlockDeprecated( 57 0, 58 0, 59 hash.ZeroHash256, 60 testutil.TimestampNow(), 61 producerPubKey, 62 actions, 63 ) 64 hash, err := block.CalculateTxRoot() 65 require.NoError(err) 66 require.Equal("eb5cb75ae199d96de7c1cd726d5e1a3dff15022ed7bdc914a3d8b346f1ef89c9", hex.EncodeToString(hash[:])) 67 68 hashes := actionHashs(block) 69 for i := range hashes { 70 h, err := actions[i].Hash() 71 require.NoError(err) 72 require.Equal(hex.EncodeToString(h[:]), hashes[i]) 73 } 74 75 t.Log("Merkle root match pass\n") 76 } 77 78 var ( 79 _pkBytes = identityset.PrivateKey(27).PublicKey().Bytes() 80 _pbBlock = iotextypes.Block{ 81 Header: &iotextypes.BlockHeader{ 82 Core: &iotextypes.BlockHeaderCore{ 83 Version: version.ProtocolVersion, 84 Height: 123456789, 85 Timestamp: timestamppb.Now(), 86 }, 87 ProducerPubkey: _pkBytes, 88 }, 89 Body: &iotextypes.BlockBody{ 90 Actions: []*iotextypes.Action{ 91 { 92 Core: &iotextypes.ActionCore{ 93 Action: &iotextypes.ActionCore_Transfer{ 94 Transfer: &iotextypes.Transfer{ 95 Amount: "100000000000000000", 96 Recipient: "alice", 97 }, 98 }, 99 Version: version.ProtocolVersion, 100 Nonce: 101, 101 ChainID: 1, 102 }, 103 SenderPubKey: _pkBytes, 104 Signature: action.ValidSig, 105 }, 106 { 107 Core: &iotextypes.ActionCore{ 108 Action: &iotextypes.ActionCore_Execution{ 109 Execution: &iotextypes.Execution{ 110 Contract: "bob", 111 Amount: "200000000000000000", 112 Data: []byte{1, 2, 3, 4}, 113 }, 114 }, 115 Version: version.ProtocolVersion, 116 Nonce: 102, 117 ChainID: 2, 118 }, 119 SenderPubKey: _pkBytes, 120 Signature: action.ValidSig, 121 }, 122 }, 123 }, 124 } 125 ) 126 127 func TestBlockCompressionSize(t *testing.T) { 128 for _, n := range []int{1, 10, 100, 1000, 10000} { 129 blk := makeBlock(t, n) 130 blkBytes, err := blk.Serialize() 131 require.NoError(t, err) 132 compressedBlkBytes, err := compress.CompGzip(blkBytes) 133 require.NoError(t, err) 134 log.L().Info( 135 "Compression result", 136 zap.Int("numActions", n), 137 zap.Int("before", len(blkBytes)), 138 zap.Int("after", len(compressedBlkBytes)), 139 ) 140 } 141 } 142 143 func BenchmarkBlockCompression(b *testing.B) { 144 for _, i := range []int{1, 10, 100, 1000, 2000} { 145 b.Run(fmt.Sprintf("numActions: %d", i), func(b *testing.B) { 146 for n := 0; n < b.N; n++ { 147 blk := makeBlock(b, i) 148 blkBytes, err := blk.Serialize() 149 require.NoError(b, err) 150 b.StartTimer() 151 _, err = compress.CompGzip(blkBytes) 152 b.StopTimer() 153 require.NoError(b, err) 154 } 155 }) 156 } 157 } 158 159 func BenchmarkBlockDecompression(b *testing.B) { 160 for _, i := range []int{1, 10, 100, 1000, 2000} { 161 b.Run(fmt.Sprintf("numActions: %d", i), func(b *testing.B) { 162 for n := 0; n < b.N; n++ { 163 blk := makeBlock(b, i) 164 blkBytes, err := blk.Serialize() 165 require.NoError(b, err) 166 blkBytes, err = compress.CompGzip(blkBytes) 167 require.NoError(b, err) 168 b.StartTimer() 169 _, err = compress.DecompGzip(blkBytes) 170 b.StopTimer() 171 require.NoError(b, err) 172 } 173 }) 174 } 175 } 176 177 func makeBlock(tb testing.TB, n int) *Block { 178 rand.Seed(time.Now().Unix()) 179 sevlps := make([]*action.SealedEnvelope, 0) 180 for j := 1; j <= n; j++ { 181 i := rand.Int() 182 tsf, err := action.NewTransfer( 183 uint64(i), 184 unit.ConvertIotxToRau(1000+int64(i)), 185 identityset.Address(i%identityset.Size()).String(), 186 nil, 187 20000+uint64(i), 188 unit.ConvertIotxToRau(1+int64(i)), 189 ) 190 require.NoError(tb, err) 191 eb := action.EnvelopeBuilder{} 192 evlp := eb. 193 SetAction(tsf). 194 SetGasLimit(tsf.GasLimit()). 195 SetGasPrice(tsf.GasPrice()). 196 SetNonce(tsf.Nonce()). 197 SetVersion(1). 198 Build() 199 sevlp, err := action.Sign(evlp, identityset.PrivateKey((i+1)%identityset.Size())) 200 require.NoError(tb, err) 201 sevlps = append(sevlps, sevlp) 202 } 203 rap := RunnableActionsBuilder{} 204 ra := rap.AddActions(sevlps...). 205 Build() 206 blk, err := NewBuilder(ra). 207 SetHeight(1). 208 SetTimestamp(time.Now()). 209 SetVersion(1). 210 SetReceiptRoot(hash.Hash256b([]byte("hello, world!"))). 211 SetDeltaStateDigest(hash.Hash256b([]byte("world, hello!"))). 212 SetPrevBlockHash(hash.Hash256b([]byte("hello, block!"))). 213 SignAndBuild(identityset.PrivateKey(0)) 214 require.NoError(tb, err) 215 return &blk 216 } 217 218 func TestVerifyBlock(t *testing.T) { 219 require := require.New(t) 220 221 tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(27), 1, big.NewInt(20), []byte{}, 100000, big.NewInt(10)) 222 require.NoError(err) 223 224 tsf2, err := action.SignedTransfer(identityset.Address(29).String(), identityset.PrivateKey(27), 1, big.NewInt(30), []byte{}, 100000, big.NewInt(10)) 225 require.NoError(err) 226 227 blkhash, err := tsf1.Hash() 228 require.NoError(err) 229 blk, err := NewTestingBuilder(). 230 SetHeight(1). 231 SetPrevBlockHash(blkhash). 232 SetTimeStamp(testutil.TimestampNow()). 233 AddActions(tsf1, tsf2). 234 SignAndBuild(identityset.PrivateKey(27)) 235 require.NoError(err) 236 t.Run("success", func(t *testing.T) { 237 require.True(blk.Header.VerifySignature()) 238 require.NoError(blk.VerifyTxRoot()) 239 }) 240 241 t.Run("wrong root hash", func(t *testing.T) { 242 blk.Actions[0], blk.Actions[1] = blk.Actions[1], blk.Actions[0] 243 require.True(blk.Header.VerifySignature()) 244 require.Error(blk.VerifyTxRoot()) 245 }) 246 } 247 248 // actionHashs returns action hashs in the block 249 func actionHashs(blk *Block) []string { 250 actHash := make([]string, len(blk.Actions)) 251 for i := range blk.Actions { 252 h, err := blk.Actions[i].Hash() 253 if err != nil { 254 log.L().Debug("Skipping action due to hash error", zap.Error(err)) 255 continue 256 } 257 actHash[i] = hex.EncodeToString(h[:]) 258 } 259 return actHash 260 }