github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/state/events_test.go (about)

     1  package state
     2  
     3  import (
     4  	bin "encoding/binary"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/hyperledger/burrow/binary"
     9  	"github.com/hyperledger/burrow/config/source"
    10  	"github.com/hyperledger/burrow/crypto"
    11  	"github.com/hyperledger/burrow/execution/exec"
    12  	"github.com/hyperledger/burrow/storage"
    13  	"github.com/hyperledger/burrow/txs"
    14  	"github.com/hyperledger/burrow/txs/payload"
    15  	"github.com/stretchr/testify/require"
    16  	dbm "github.com/tendermint/tm-db"
    17  )
    18  
    19  func TestWriteState_AddBlock(t *testing.T) {
    20  	s := NewState(dbm.NewMemDB())
    21  	height := uint64(100)
    22  	numTxs := uint64(5)
    23  	events := uint64(10)
    24  	addBlock(t, s, height, numTxs, events)
    25  
    26  	txIndex := uint64(0)
    27  	eventIndex := uint64(0)
    28  	err := s.IterateStreamEvents(&height, &height, storage.AscendingSort, func(ev *exec.StreamEvent) error {
    29  		switch {
    30  		case ev.BeginTx != nil:
    31  			eventIndex = 0
    32  		case ev.Event != nil:
    33  			require.Equal(t, mkEvent(height, txIndex, eventIndex).Header.TxHash.String(),
    34  				ev.Event.Header.TxHash.String(), "event TxHash mismatch at tx #%d event #%d",
    35  				txIndex, eventIndex)
    36  			eventIndex++
    37  		case ev.EndTx != nil:
    38  			txIndex++
    39  		}
    40  		return nil
    41  	})
    42  	require.NoError(t, err)
    43  	require.Equal(t, numTxs, txIndex, "should have observed all txs")
    44  	// non-increasing events
    45  	_, _, err = s.Update(func(ws Updatable) error {
    46  		return nil
    47  	})
    48  	require.NoError(t, err)
    49  
    50  	txExecutions, err := s.TxsAtHeight(height)
    51  	require.NoError(t, err)
    52  	require.NotNil(t, txExecutions)
    53  	require.Equal(t, numTxs, uint64(len(txExecutions)))
    54  }
    55  
    56  func TestNestedTxs(t *testing.T) {
    57  	s := NewState(dbm.NewMemDB())
    58  	height := uint64(2)
    59  	numTxs := uint64(4)
    60  	events := uint64(2)
    61  	nesting := uint64(3)
    62  	block := mkBlock(height, numTxs, events)
    63  	txes := block.TxExecutions
    64  	// Deeply nest transactions inside block
    65  	for i := uint64(0); i < nesting; i++ {
    66  		var next []*exec.TxExecution
    67  		for _, txe := range txes {
    68  			next = append(next, nestTxs(txe, height, events+i, numTxs+i)...)
    69  		}
    70  		txes = next
    71  	}
    72  	_, _, err := s.Update(func(ws Updatable) error {
    73  		return ws.AddBlock(block)
    74  	})
    75  	require.NoError(t, err)
    76  	txes, err = s.TxsAtHeight(height)
    77  	require.NoError(t, err)
    78  	txsCount := deepCountTxs(block.TxExecutions)
    79  	require.Equal(t, txsCount, deepCountTxs(txes))
    80  	// There is a geometric-arithmetic sum here... but empiricism FTW
    81  	require.Equal(t, 580, txsCount)
    82  }
    83  
    84  func TestReadState_TxByHash(t *testing.T) {
    85  	s := NewState(dbm.NewMemDB())
    86  	maxHeight := uint64(3)
    87  	numTxs := uint64(4)
    88  	events := uint64(2)
    89  	for height := uint64(0); height < maxHeight; height++ {
    90  		block := mkBlock(height, numTxs, events)
    91  		_, _, err := s.Update(func(ws Updatable) error {
    92  			return ws.AddBlock(block)
    93  		})
    94  		require.NoError(t, err)
    95  	}
    96  
    97  	hashSet := make(map[string]bool)
    98  	for height := uint64(0); height < maxHeight; height++ {
    99  		for txIndex := uint64(0); txIndex < numTxs; txIndex++ {
   100  			// Find this tx
   101  			tx := mkTxExecution(height, txIndex, events)
   102  			txHash := tx.TxHash.String()
   103  			// Check we have no duplicates (indicates problem with how we are generating hashes for these tests
   104  			require.False(t, hashSet[txHash], "should be no duplicate tx hashes")
   105  			hashSet[txHash] = true
   106  			// Try and pull the Tx by its hash
   107  			txOut, err := s.TxByHash(tx.TxHash)
   108  			require.NoError(t, err)
   109  			require.NotNil(t, txOut, "should retrieve non-nil transaction by TxHash %v", tx.TxHash)
   110  			// Make sure we get the same tx
   111  			require.Equal(t, txHash, txOut.TxHash.String(), "TxHash does not match as string")
   112  			require.Equal(t, source.JSONString(tx), source.JSONString(txOut))
   113  		}
   114  	}
   115  }
   116  
   117  func TestLastBlockStored(t *testing.T) {
   118  	s := NewState(dbm.NewMemDB())
   119  	// Add first block
   120  	addBlock(t, s, uint64(1), 2, 3)
   121  	lastStoredHeight, err := s.LastStoredHeight()
   122  	require.NoError(t, err)
   123  	require.Equal(t, lastStoredHeight, uint64(1))
   124  
   125  	// Add empty block
   126  	addBlock(t, s, uint64(2), 0, 0)
   127  	lastStoredHeight, err = s.LastStoredHeight()
   128  	require.NoError(t, err)
   129  	// Same last stored height
   130  	require.Equal(t, lastStoredHeight, uint64(1))
   131  
   132  	// Add non-empty block
   133  	addBlock(t, s, uint64(3), 1, 0)
   134  	lastStoredHeight, err = s.LastStoredHeight()
   135  	require.NoError(t, err)
   136  	// Same last stored height
   137  	require.Equal(t, lastStoredHeight, uint64(3))
   138  }
   139  
   140  func BenchmarkAddBlockAndIterator(b *testing.B) {
   141  	s := NewState(dbm.NewMemDB())
   142  	numTxs := uint64(5)
   143  	events := uint64(10)
   144  	for height := uint64(0); height < 2000; height++ {
   145  		block := mkBlock(height, numTxs, events)
   146  		_, _, err := s.Update(func(ws Updatable) error {
   147  			return ws.AddBlock(block)
   148  		})
   149  		require.NoError(b, err)
   150  	}
   151  	err := s.IterateStreamEvents(nil, nil, storage.AscendingSort, func(ev *exec.StreamEvent) error {
   152  		return nil
   153  	})
   154  	require.NoError(b, err)
   155  }
   156  
   157  func addBlock(t testing.TB, s *State, height, numTxs, events uint64) {
   158  	block := mkBlock(height, numTxs, events)
   159  	_, _, err := s.Update(func(ws Updatable) error {
   160  		return ws.AddBlock(block)
   161  	})
   162  	require.NoError(t, err)
   163  }
   164  
   165  func deepCountTxs(txes []*exec.TxExecution) int {
   166  	sum := len(txes)
   167  	for _, txe := range txes {
   168  		sum += deepCountTxs(txe.TxExecutions)
   169  	}
   170  	return sum
   171  }
   172  
   173  func nestTxs(txe *exec.TxExecution, height, events, numTxs uint64) []*exec.TxExecution {
   174  	txes := make([]*exec.TxExecution, numTxs)
   175  	for i := uint64(0); i < numTxs; i++ {
   176  		txes[i] = mkTxExecution(height, i, events)
   177  		txe.TxExecutions = append(txe.TxExecutions, txes[i])
   178  	}
   179  	return txes
   180  }
   181  
   182  func mkBlock(height, numTxs, events uint64) *exec.BlockExecution {
   183  	be := &exec.BlockExecution{
   184  		Height: height,
   185  	}
   186  	for ti := uint64(0); ti < numTxs; ti++ {
   187  		txe := mkTxExecution(height, ti, events)
   188  		be.TxExecutions = append(be.TxExecutions, txe)
   189  	}
   190  	return be
   191  }
   192  
   193  func mkTxExecution(height, txIndex, events uint64) *exec.TxExecution {
   194  	hash := make([]byte, 32)
   195  	bin.BigEndian.PutUint64(hash[:8], height)
   196  	bin.BigEndian.PutUint64(hash[8:16], txIndex)
   197  	bin.BigEndian.PutUint64(hash[16:24], events)
   198  	txEnv := txs.Enclose("ChainTheFirst", mkTx())
   199  	txe := &exec.TxExecution{
   200  		TxHeader: &exec.TxHeader{
   201  			TxHash: hash,
   202  			Height: height,
   203  			Index:  txIndex,
   204  		},
   205  		Envelope: txEnv,
   206  		Receipt:  txEnv.Tx.GenerateReceipt(),
   207  	}
   208  	for e := uint64(0); e < events; e++ {
   209  		txe.Events = append(txe.Events, mkEvent(height, txIndex, e))
   210  	}
   211  	return txe
   212  }
   213  
   214  func mkTx() payload.Payload {
   215  	return &payload.CallTx{
   216  		Input: &payload.TxInput{
   217  			Address:  crypto.Address{1, 2, 3},
   218  			Amount:   12345,
   219  			Sequence: 67890,
   220  		},
   221  		GasLimit: 111,
   222  		Fee:      222,
   223  		Data:     []byte("data1"),
   224  	}
   225  }
   226  
   227  func mkEvent(height, tx, index uint64) *exec.Event {
   228  	return &exec.Event{
   229  		Header: &exec.Header{
   230  			Height:  height,
   231  			Index:   index,
   232  			TxHash:  crypto.Keccak256([]byte(fmt.Sprintf("txhash%v%v%v", height, tx, index))),
   233  			EventID: fmt.Sprintf("eventID: %v%v%v", height, tx, index),
   234  		},
   235  		Log: &exec.LogEvent{
   236  			Address: crypto.Address{byte(height), byte(index)},
   237  			Topics:  []binary.Word256{{1, 2, 3}},
   238  		},
   239  	}
   240  }