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

     1  package service
     2  
     3  import (
     4  	"io"
     5  
     6  	"github.com/hyperledger/burrow/event/query"
     7  	"github.com/hyperledger/burrow/execution/exec"
     8  	"github.com/hyperledger/burrow/logging"
     9  	"github.com/hyperledger/burrow/logging/structure"
    10  	"github.com/hyperledger/burrow/vent/chain"
    11  	"github.com/hyperledger/burrow/vent/sqlsol"
    12  	"github.com/hyperledger/burrow/vent/types"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  func NewBlockConsumer(chainID string, projection *sqlsol.Projection, opt sqlsol.SpecOpt, getEventSpec EventSpecGetter,
    17  	eventCh chan<- types.EventData, doneCh chan struct{}, logger *logging.Logger) func(block chain.Block) error {
    18  
    19  	logger = logger.WithScope("makeBlockConsumer")
    20  
    21  	var blockHeight uint64
    22  
    23  	return func(block chain.Block) error {
    24  		if finished(doneCh) {
    25  			return io.EOF
    26  		}
    27  
    28  		// set new block number
    29  		blockHeight = block.GetHeight()
    30  		txs := block.GetTxs()
    31  
    32  		logger.TraceMsg("Block received",
    33  			"height", blockHeight,
    34  			"num_txs", len(txs))
    35  
    36  		// create a fresh new structure to store block data at this height
    37  		blockData := sqlsol.NewBlockData(blockHeight)
    38  
    39  		if opt.Enabled(sqlsol.Block) {
    40  			blkRawData, err := buildBlkData(projection.Tables, block)
    41  			if err != nil {
    42  				return errors.Wrapf(err, "Error building block raw data")
    43  			}
    44  			// set row in structure
    45  			blockData.AddRow(tables.Block, blkRawData)
    46  		}
    47  
    48  		for _, txe := range txs {
    49  			events := txe.GetEvents()
    50  			logger.TraceMsg("Getting transaction", "TxHash", txe.GetHash(), "num_events", len(events))
    51  
    52  			if opt.Enabled(sqlsol.Tx) {
    53  				txRawData, err := buildTxData(txe)
    54  				if err != nil {
    55  					return errors.Wrapf(err, "Error building tx raw data")
    56  				}
    57  				// set row in structure
    58  				blockData.AddRow(tables.Tx, txRawData)
    59  			}
    60  
    61  			// reverted transactions don't have to update event data tables
    62  			// so check that condition to filter them
    63  			if txe.GetException() == nil {
    64  				txOrigin := txe.GetOrigin()
    65  				if txOrigin == nil {
    66  					// This is an original transaction from the current chain so we build its origin from context
    67  					txOrigin = &chain.Origin{
    68  						ChainID: chainID,
    69  						Height:  block.GetHeight(),
    70  						Index:   txe.GetIndex(),
    71  					}
    72  				}
    73  
    74  				for _, event := range events {
    75  					var tagged query.Tagged = event
    76  					eventID := exec.SolidityEventID(event.GetTopics())
    77  					eventSpec, eventSpecErr := getEventSpec(eventID, event.GetAddress())
    78  					if eventSpecErr != nil {
    79  						logger.InfoMsg("could not get ABI for solidity event",
    80  							structure.ErrorKey, eventSpecErr,
    81  							"event_id", eventID,
    82  							"address", event.GetAddress())
    83  					} else {
    84  						// Since we have the event ABI we will allow matching on ABI fields
    85  						tagged = query.TagsFor(event, query.TaggedPrefix("Event", eventSpec))
    86  					}
    87  
    88  					// see which spec filter matches with the one in event data
    89  					for _, eventClass := range projection.Spec {
    90  						qry, err := eventClass.Query()
    91  
    92  						if err != nil {
    93  							return errors.Wrapf(err, "Error parsing query from filter string")
    94  						}
    95  
    96  						// there's a matching filter, add data to the rows
    97  						if qry.Matches(tagged) {
    98  							if eventSpecErr != nil {
    99  								return errors.Wrapf(eventSpecErr, "could not get ABI for solidity event matching "+
   100  									"projection filter \"%s\" with id %v at address %v",
   101  									eventClass.Filter, eventID, event.GetAddress())
   102  							}
   103  
   104  							logger.InfoMsg("Matched event", "event_id", eventID, "filter", eventClass.Filter)
   105  
   106  							// unpack, decode & build event data
   107  							eventData, err := buildEventData(projection, eventClass, event, txOrigin, eventSpec, logger)
   108  							if err != nil {
   109  								return errors.Wrapf(err, "Error building event data")
   110  							}
   111  
   112  							// set row in structure
   113  							blockData.AddRow(eventClass.TableName, eventData)
   114  						}
   115  					}
   116  				}
   117  			}
   118  		}
   119  
   120  		// upsert rows in specific SQL event tables and update block number
   121  		// store block data in SQL tables (if any)
   122  		for name, rows := range blockData.Data.Tables {
   123  			logger.InfoMsg("Upserting rows in SQL table", "height", blockHeight, "table", name, "action", "UPSERT", "rows", rows)
   124  		}
   125  
   126  		eventCh <- blockData.Data
   127  		return nil
   128  	}
   129  }