github.com/loomnetwork/gamechain@v0.0.0-20200406110549-36c47eb97a92/tools/gamechain-logger/cmd/runner.go (about)

     1  package cmd
     2  
     3  import (
     4  	"log"
     5  	"strings"
     6  	"time"
     7  
     8  	raven "github.com/getsentry/raven-go"
     9  	"github.com/jinzhu/gorm"
    10  	"github.com/loomnetwork/gamechain/battleground"
    11  	"github.com/loomnetwork/go-loom/client"
    12  	"github.com/loomnetwork/go-loom/plugin/types"
    13  	"github.com/loomnetwork/gamechain/tools/gamechain-logger/models"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  type Config struct {
    18  	ChainID           string
    19  	ReadURI           string
    20  	WriteURI          string
    21  	ReconnectInterval time.Duration
    22  	PollInterval      time.Duration
    23  	BlockInterval     int
    24  	ContractName      string
    25  }
    26  
    27  type Runner struct {
    28  	db              *gorm.DB
    29  	stopC           chan struct{}
    30  	errC            chan error
    31  	cfg             *Config
    32  	dappchainClient *client.DAppChainRPCClient
    33  }
    34  
    35  func NewRunner(db *gorm.DB, config *Config) *Runner {
    36  	return &Runner{
    37  		db:              db,
    38  		cfg:             config,
    39  		stopC:           make(chan struct{}),
    40  		errC:            make(chan error),
    41  		dappchainClient: client.NewDAppChainRPCClient(config.ChainID, config.WriteURI, config.ReadURI),
    42  	}
    43  }
    44  
    45  // Start runs the loop to watch topic. It's a blocking call.
    46  func (r *Runner) Start() {
    47  	for {
    48  		err := r.watchTopic()
    49  		if err == nil {
    50  			break
    51  		}
    52  		log.Printf("error: %v", err)
    53  		raven.CaptureErrorAndWait(err, map[string]string{})
    54  		// delay before connecting again
    55  		time.Sleep(r.cfg.ReconnectInterval)
    56  	}
    57  }
    58  
    59  func (r *Runner) Stop() {
    60  	close(r.stopC)
    61  }
    62  
    63  func (r *Runner) Error() chan error {
    64  	return r.errC
    65  }
    66  
    67  func (r *Runner) watchTopic() error {
    68  	ticker := time.NewTicker(r.cfg.PollInterval)
    69  	for {
    70  		select {
    71  		case <-ticker.C:
    72  			height := models.ZbHeightCheck{}
    73  			err := r.db.Where(&models.ZbHeightCheck{Key: 1}).
    74  				Attrs(models.ZbHeightCheck{Key: 1}).
    75  				FirstOrCreate(&height).
    76  				Error
    77  			if err != nil {
    78  				return err
    79  			}
    80  
    81  			fromBlock := height.LastBlockHeight + 1
    82  			toBlock := fromBlock + uint64(r.cfg.BlockInterval) - 1
    83  
    84  			lastBlockHeight, err := r.dappchainClient.GetBlockHeight()
    85  			if err != nil {
    86  				return err
    87  			}
    88  
    89  			if toBlock > lastBlockHeight {
    90  				continue
    91  			}
    92  			result, err := r.dappchainClient.GetContractEvents(fromBlock, toBlock, r.cfg.ContractName)
    93  			if err != nil {
    94  				return err
    95  			}
    96  
    97  			tx := r.db.Begin()
    98  			if err := r.batchProcessEvents(tx, result.Events); err != nil {
    99  				tx.Rollback()
   100  				return err
   101  			}
   102  
   103  			height.LastBlockHeight = result.ToBlock
   104  			err = tx.Model(&models.ZbHeightCheck{}).
   105  				Update(height).
   106  				Error
   107  			if err != nil {
   108  				tx.Rollback()
   109  				return err
   110  			}
   111  
   112  			tx.Commit()
   113  		case <-r.stopC:
   114  			ticker.Stop()
   115  			return nil
   116  		}
   117  
   118  	}
   119  }
   120  
   121  func (r *Runner) batchProcessEvents(db *gorm.DB, events []*types.EventData) error {
   122  	if len(events) == 0 {
   123  		return nil
   124  	}
   125  	for _, event := range events {
   126  		for _, topic := range event.Topics {
   127  			var topicHandler TopicHandler
   128  			switch topic {
   129  			case battleground.TopicFindMatchEvent:
   130  				topicHandler = FindMatchHandler
   131  			case battleground.TopicAcceptMatchEvent:
   132  				topicHandler = AcceptMatchHandler
   133  			case battleground.TopicCreateDeckEvent:
   134  				topicHandler = CreateDeckHandler
   135  			case battleground.TopicEditDeckEvent:
   136  				topicHandler = EditDeckHandler
   137  			case battleground.TopicDeleteDeckEvent:
   138  				topicHandler = DeleteDeckHandler
   139  			default:
   140  				if strings.HasPrefix(topic, "match:") {
   141  					topicHandler = MatchHandler
   142  				}
   143  			}
   144  
   145  			if topicHandler != nil {
   146  				err := topicHandler(event, db)
   147  				if err != nil {
   148  					err = errors.Wrapf(err, "error calling topic handler")
   149  					log.Printf("error: %s from event: %+v", err, event)
   150  					return err
   151  				}
   152  			}
   153  		}
   154  	}
   155  
   156  	return nil
   157  }