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  }