github.com/status-im/status-go@v1.1.0/protocol/ens/verifier.go (about)

     1  package ens
     2  
     3  import (
     4  	"database/sql"
     5  	"time"
     6  
     7  	"go.uber.org/zap"
     8  
     9  	gethcommon "github.com/ethereum/go-ethereum/common"
    10  	"github.com/status-im/status-go/eth-node/types"
    11  	enstypes "github.com/status-im/status-go/eth-node/types/ens"
    12  	"github.com/status-im/status-go/protocol/common"
    13  )
    14  
    15  type Verifier struct {
    16  	node            types.Node
    17  	online          bool
    18  	persistence     *Persistence
    19  	logger          *zap.Logger
    20  	timesource      common.TimeSource
    21  	subscriptions   []chan []*VerificationRecord
    22  	rpcEndpoint     string
    23  	contractAddress string
    24  	quit            chan struct{}
    25  }
    26  
    27  func New(node types.Node, logger *zap.Logger, timesource common.TimeSource, db *sql.DB, rpcEndpoint, contractAddress string) *Verifier {
    28  	persistence := NewPersistence(db)
    29  	return &Verifier{
    30  		node:            node,
    31  		logger:          logger,
    32  		persistence:     persistence,
    33  		timesource:      timesource,
    34  		rpcEndpoint:     rpcEndpoint,
    35  		contractAddress: contractAddress,
    36  		quit:            make(chan struct{}),
    37  	}
    38  }
    39  
    40  func (v *Verifier) Start() error {
    41  	go v.verifyLoop()
    42  	return nil
    43  }
    44  
    45  func (v *Verifier) Stop() error {
    46  	close(v.quit)
    47  
    48  	return nil
    49  }
    50  
    51  // ENSVerified adds an already verified entry to the ens table
    52  func (v *Verifier) ENSVerified(pk, ensName string, clock uint64) error {
    53  
    54  	// Add returns nil if no record was available
    55  	oldRecord, err := v.Add(pk, ensName, clock)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	var record *VerificationRecord
    61  
    62  	if oldRecord != nil {
    63  		record = oldRecord
    64  	} else {
    65  		record = &VerificationRecord{PublicKey: pk, Name: ensName, Clock: clock}
    66  	}
    67  
    68  	record.VerifiedAt = clock
    69  	record.Verified = true
    70  	records := []*VerificationRecord{record}
    71  	err = v.persistence.UpdateRecords(records)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	v.publish(records)
    76  	return nil
    77  }
    78  
    79  func (v *Verifier) GetVerifiedRecord(pk string) (*VerificationRecord, error) {
    80  	return v.persistence.GetVerifiedRecord(pk)
    81  }
    82  
    83  func (v *Verifier) Add(pk, ensName string, clock uint64) (*VerificationRecord, error) {
    84  	record := VerificationRecord{PublicKey: pk, Name: ensName, Clock: clock}
    85  	return v.persistence.AddRecord(record)
    86  }
    87  
    88  func (v *Verifier) SetOnline(online bool) {
    89  	v.online = online
    90  }
    91  
    92  func (v *Verifier) verifyLoop() {
    93  
    94  	ticker := time.NewTicker(30 * time.Second)
    95  	for {
    96  		select {
    97  
    98  		case <-v.quit:
    99  			ticker.Stop()
   100  			return
   101  		case <-ticker.C:
   102  			if !v.online || v.rpcEndpoint == "" || v.contractAddress == "" {
   103  				continue
   104  			}
   105  			err := v.verify(v.rpcEndpoint, v.contractAddress)
   106  			if err != nil {
   107  				v.logger.Error("verify loop failed", zap.Error(err))
   108  			}
   109  
   110  		}
   111  	}
   112  }
   113  
   114  func (v *Verifier) Subscribe() chan []*VerificationRecord {
   115  	c := make(chan []*VerificationRecord)
   116  	v.subscriptions = append(v.subscriptions, c)
   117  	return c
   118  }
   119  
   120  func (v *Verifier) publish(records []*VerificationRecord) {
   121  	v.logger.Info("publishing records", zap.Any("records", records))
   122  	// Publish on channels, drop if buffer is full
   123  	for _, s := range v.subscriptions {
   124  		select {
   125  		case s <- records:
   126  		default:
   127  			v.logger.Warn("ens subscription channel full, dropping message")
   128  		}
   129  	}
   130  
   131  }
   132  
   133  func (v *Verifier) ReverseResolve(address gethcommon.Address) (string, error) {
   134  	verifier := v.node.NewENSVerifier(v.logger)
   135  	return verifier.ReverseResolve(address, v.rpcEndpoint)
   136  }
   137  
   138  // Verify verifies that a registered ENS name matches the expected public key
   139  func (v *Verifier) verify(rpcEndpoint, contractAddress string) error {
   140  	v.logger.Debug("verifying ENS Names", zap.String("endpoint", rpcEndpoint))
   141  	verifier := v.node.NewENSVerifier(v.logger)
   142  
   143  	var ensDetails []enstypes.ENSDetails
   144  
   145  	// Now in seconds
   146  	now := v.timesource.GetCurrentTime() / 1000
   147  	ensToBeVerified, err := v.persistence.GetENSToBeVerified(now)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	recordsMap := make(map[string]*VerificationRecord)
   153  
   154  	for _, r := range ensToBeVerified {
   155  		recordsMap[r.PublicKey] = r
   156  		ensDetails = append(ensDetails, enstypes.ENSDetails{
   157  			PublicKeyString: r.PublicKey[2:],
   158  			Name:            r.Name,
   159  		})
   160  		v.logger.Debug("verifying ens name", zap.Any("record", r))
   161  	}
   162  
   163  	ensResponse, err := verifier.CheckBatch(ensDetails, rpcEndpoint, contractAddress)
   164  	if err != nil {
   165  		v.logger.Error("failed to check batch", zap.Error(err))
   166  		return err
   167  	}
   168  
   169  	var records []*VerificationRecord
   170  
   171  	for _, details := range ensResponse {
   172  		pk := "0x" + details.PublicKeyString
   173  		record := recordsMap[pk]
   174  
   175  		if details.Error == nil {
   176  			record.Verified = details.Verified
   177  			if !record.Verified {
   178  				record.VerificationRetries++
   179  			}
   180  		} else {
   181  			v.logger.Warn("Failed to resolve ens name",
   182  				zap.String("name", details.Name),
   183  				zap.String("publicKey", details.PublicKeyString),
   184  				zap.Error(details.Error),
   185  			)
   186  			record.VerificationRetries++
   187  		}
   188  		record.VerifiedAt = now
   189  		record.CalculateNextRetry()
   190  
   191  		records = append(records, record)
   192  	}
   193  
   194  	err = v.persistence.UpdateRecords(records)
   195  	if err != nil {
   196  
   197  		v.logger.Error("failed to update records", zap.Error(err))
   198  		return err
   199  	}
   200  
   201  	v.publish(records)
   202  
   203  	return nil
   204  }