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 }