github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/service/block_consumer_test.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hyperledger/burrow/vent/chain/burrow"
    10  
    11  	"github.com/hyperledger/burrow/crypto"
    12  	"github.com/hyperledger/burrow/execution/evm/abi"
    13  	"github.com/hyperledger/burrow/execution/exec"
    14  	"github.com/hyperledger/burrow/execution/solidity"
    15  	"github.com/hyperledger/burrow/logging"
    16  	"github.com/hyperledger/burrow/vent/chain"
    17  	"github.com/hyperledger/burrow/vent/sqlsol"
    18  	"github.com/hyperledger/burrow/vent/types"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    22  )
    23  
    24  const chainID = "TestChainID"
    25  
    26  func TestBlockConsumer(t *testing.T) {
    27  	doneCh := make(chan struct{})
    28  	eventCh := make(chan types.EventData, 100)
    29  
    30  	spec, err := abi.ReadSpec(solidity.Abi_EventEmitter)
    31  	require.NoError(t, err)
    32  
    33  	type args struct {
    34  		Direction []byte
    35  		Trueism   bool
    36  		German    string
    37  		NewDepth  *big.Int
    38  		Bignum    int8
    39  		Hash      string
    40  	}
    41  	manyTypesEventSpec := spec.EventsByName["ManyTypes"]
    42  
    43  	bignum := big.NewInt(1000)
    44  	in := args{
    45  		Direction: make([]byte, 32),
    46  		Trueism:   false,
    47  		German:    "foo",
    48  		NewDepth:  bignum,
    49  		Bignum:    100,
    50  		Hash:      "ba",
    51  	}
    52  	direction := "frogs"
    53  	copy(in.Direction, direction)
    54  	topics, data, err := abi.PackEvent(manyTypesEventSpec, in)
    55  	require.NoError(t, err)
    56  	log := &exec.LogEvent{
    57  		Address: crypto.Address{},
    58  		Data:    data,
    59  		Topics:  topics,
    60  	}
    61  
    62  	logger := logging.NewNoopLogger()
    63  	fieldMappings := []*types.EventFieldMapping{
    64  		{
    65  			Field:         "direction",
    66  			Type:          types.EventFieldTypeString,
    67  			ColumnName:    "direction",
    68  			BytesToString: true,
    69  		},
    70  	}
    71  	t.Run("Consume matching event", func(t *testing.T) {
    72  		spec, err := abi.ReadSpec(solidity.Abi_EventEmitter)
    73  		require.NoError(t, err)
    74  
    75  		longFilter := "(Log1Text = 'a' OR Log1Text = 'b' OR Log1Text = 'c' OR Log1Text = 'frogs') AND EventName = 'ManyTypes'"
    76  		require.True(t, len(longFilter) > 100)
    77  		tableName := "Events"
    78  		projection, err := sqlsol.NewProjection(types.ProjectionSpec{
    79  			{
    80  				TableName:     tableName,
    81  				Filter:        longFilter,
    82  				FieldMappings: fieldMappings,
    83  			},
    84  		})
    85  		require.NoError(t, err)
    86  		blockConsumer := NewBlockConsumer(chainID, projection, sqlsol.None, spec.GetEventAbi, eventCh, doneCh, logger)
    87  		tables, err := consumeBlock(blockConsumer, eventCh, log)
    88  		require.NoError(t, err)
    89  		rows := tables[tableName]
    90  		assert.Len(t, rows, 1)
    91  		assert.Equal(t, direction, rows[0].RowData["direction"])
    92  	})
    93  
    94  	t.Run("Consume matching event without ABI", func(t *testing.T) {
    95  		spec, err := abi.ReadSpec(solidity.Abi_EventEmitter)
    96  		require.NoError(t, err)
    97  
    98  		// Remove the ABI
    99  		delete(spec.EventsByID, manyTypesEventSpec.ID)
   100  
   101  		tableName := "Events"
   102  		// Here we are using a filter that matches - but we no longer have ABI
   103  		projection, err := sqlsol.NewProjection(types.ProjectionSpec{
   104  			{
   105  				TableName:     tableName,
   106  				Filter:        "Log1Text = 'a' OR Log1Text = 'b' OR Log1Text = 'frogs'",
   107  				FieldMappings: fieldMappings,
   108  			},
   109  		})
   110  		require.NoError(t, err)
   111  		blockConsumer := NewBlockConsumer(chainID, projection, sqlsol.None, spec.GetEventAbi, eventCh, doneCh, logger)
   112  		_, err = consumeBlock(blockConsumer, eventCh, log)
   113  		require.Error(t, err)
   114  		require.Contains(t, err.Error(), "could not find ABI")
   115  	})
   116  
   117  	t.Run("Consume non-matching event without ABI", func(t *testing.T) {
   118  		spec, err := abi.ReadSpec(solidity.Abi_EventEmitter)
   119  		require.NoError(t, err)
   120  
   121  		// Remove the ABI
   122  		delete(spec.EventsByID, manyTypesEventSpec.ID)
   123  
   124  		tableName := "Events"
   125  		// Here we are using a filter that matches - but we no longer have ABI
   126  		projection, err := sqlsol.NewProjection(types.ProjectionSpec{
   127  			{
   128  				TableName:     tableName,
   129  				Filter:        "ThisIsNotAKey = 'bar'",
   130  				FieldMappings: fieldMappings,
   131  			},
   132  		})
   133  		require.NoError(t, err)
   134  		blockConsumer := NewBlockConsumer(chainID, projection, sqlsol.None, spec.GetEventAbi, eventCh, doneCh, logger)
   135  		table, err := consumeBlock(blockConsumer, eventCh, log)
   136  		require.Len(t, table, 0, "should match no event")
   137  	})
   138  
   139  	// This is possibly 'bad' behaviour - since you may be missing an ABI - but for now it is expected. On-chain ABIs
   140  	// ought to solve this
   141  	t.Run("Consume event that doesn't match without ABI tags", func(t *testing.T) {
   142  		// This is the case where we silently fail - it would match if we had the ABI - but since we don't it doesn't
   143  		// and we just carry on
   144  		tableName := "Events"
   145  		// Here we are using a filter that matches - but we no longer have ABI
   146  		projection, err := sqlsol.NewProjection(types.ProjectionSpec{
   147  			{
   148  				TableName:     tableName,
   149  				Filter:        "EventName = 'ManyTypes'",
   150  				FieldMappings: fieldMappings,
   151  			},
   152  		})
   153  		require.NoError(t, err)
   154  
   155  		spec, err := abi.ReadSpec(solidity.Abi_EventEmitter)
   156  		require.NoError(t, err)
   157  
   158  		blockConsumer := NewBlockConsumer(chainID, projection, sqlsol.None, spec.GetEventAbi, eventCh, doneCh, logger)
   159  		table, err := consumeBlock(blockConsumer, eventCh, log)
   160  		// Check matches
   161  		require.NoError(t, err)
   162  		require.Len(t, table, 1)
   163  		require.Len(t, table[tableName], 1)
   164  		// Now Remove the ABI - should not match the event
   165  		delete(spec.EventsByID, manyTypesEventSpec.ID)
   166  		blockConsumer = NewBlockConsumer(chainID, projection, sqlsol.None, spec.GetEventAbi, eventCh, doneCh, logger)
   167  		table, err = consumeBlock(blockConsumer, eventCh, log)
   168  		require.NoError(t, err)
   169  		require.Len(t, table, 0, "should match no events")
   170  	})
   171  }
   172  
   173  const timeout = time.Second
   174  
   175  var errTimeout = fmt.Errorf("timed out after %s waiting for consumer to emit block event", timeout)
   176  
   177  func consumeBlock(blockConsumer func(block chain.Block) error, eventCh <-chan types.EventData,
   178  	logEvents ...*exec.LogEvent) (map[string]types.EventDataTable, error) {
   179  
   180  	block := &exec.BlockExecution{
   181  		Header: &tmproto.Header{},
   182  	}
   183  	for _, logEvent := range logEvents {
   184  		txe := &exec.TxExecution{
   185  			TxHeader: &exec.TxHeader{},
   186  		}
   187  		err := txe.Log(logEvent)
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		block.AppendTxs(txe)
   192  	}
   193  	err := blockConsumer(burrow.NewBurrowBlock(block))
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	select {
   198  	case <-time.After(timeout):
   199  		return nil, errTimeout
   200  	case ed := <-eventCh:
   201  		return ed.Tables, nil
   202  	}
   203  }