github.com/status-im/status-go@v1.1.0/eth-node/bridge/geth/ens/ens.go (about)

     1  package ens
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/elliptic"
     7  	"encoding/hex"
     8  	"math/big"
     9  	"time"
    10  
    11  	ens "github.com/wealdtech/go-ens/v3"
    12  	"go.uber.org/zap"
    13  
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/ethclient"
    16  
    17  	"github.com/status-im/status-go/eth-node/crypto"
    18  	enstypes "github.com/status-im/status-go/eth-node/types/ens"
    19  )
    20  
    21  const (
    22  	contractQueryTimeout = 5000 * time.Millisecond
    23  )
    24  
    25  type Verifier struct {
    26  	logger *zap.Logger
    27  }
    28  
    29  // NewVerifier returns a Verifier attached to the specified logger
    30  func NewVerifier(logger *zap.Logger) *Verifier {
    31  	return &Verifier{logger: logger}
    32  }
    33  
    34  func (m *Verifier) ReverseResolve(address common.Address, rpcEndpoint string) (string, error) {
    35  	ctx, cancel := context.WithTimeout(context.Background(), contractQueryTimeout)
    36  	defer cancel()
    37  
    38  	ethClient, err := ethclient.DialContext(ctx, rpcEndpoint)
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  	return ens.ReverseResolve(ethClient, address)
    43  }
    44  
    45  func (m *Verifier) verifyENSName(ensInfo enstypes.ENSDetails, ethclient *ethclient.Client) enstypes.ENSResponse {
    46  	publicKeyStr := ensInfo.PublicKeyString
    47  	ensName := ensInfo.Name
    48  	m.logger.Info("Resolving ENS name", zap.String("name", ensName), zap.String("publicKey", publicKeyStr))
    49  	response := enstypes.ENSResponse{
    50  		Name:            ensName,
    51  		PublicKeyString: publicKeyStr,
    52  		VerifiedAt:      time.Now().Unix(),
    53  	}
    54  
    55  	expectedPubKeyBytes, err := hex.DecodeString(publicKeyStr)
    56  	if err != nil {
    57  		response.Error = err
    58  		return response
    59  	}
    60  
    61  	publicKey, err := crypto.UnmarshalPubkey(expectedPubKeyBytes)
    62  	if err != nil {
    63  		response.Error = err
    64  		return response
    65  	}
    66  
    67  	// Resolve ensName
    68  	resolver, err := ens.NewResolver(ethclient, ensName)
    69  	if err != nil {
    70  		m.logger.Error("error while creating ENS name resolver", zap.String("ensName", ensName), zap.Error(err))
    71  		response.Error = err
    72  		return response
    73  	}
    74  	x, y, err := resolver.PubKey()
    75  	if err != nil {
    76  		m.logger.Error("error while resolving public key from ENS name", zap.String("ensName", ensName), zap.Error(err))
    77  		response.Error = err
    78  		return response
    79  	}
    80  
    81  	// Assemble the bytes returned for the pubkey
    82  	pubKeyBytes := elliptic.Marshal(crypto.S256(), new(big.Int).SetBytes(x[:]), new(big.Int).SetBytes(y[:]))
    83  
    84  	response.PublicKey = publicKey
    85  	response.Verified = bytes.Equal(pubKeyBytes, expectedPubKeyBytes)
    86  	return response
    87  }
    88  
    89  // CheckBatch verifies that a registered ENS name matches the expected public key
    90  func (m *Verifier) CheckBatch(ensDetails []enstypes.ENSDetails, rpcEndpoint, contractAddress string) (map[string]enstypes.ENSResponse, error) {
    91  	ctx, cancel := context.WithTimeout(context.Background(), contractQueryTimeout)
    92  	defer cancel()
    93  
    94  	ch := make(chan enstypes.ENSResponse)
    95  	response := make(map[string]enstypes.ENSResponse)
    96  
    97  	ethclient, err := ethclient.DialContext(ctx, rpcEndpoint)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	for _, ensInfo := range ensDetails {
   103  		go func(info enstypes.ENSDetails) { ch <- m.verifyENSName(info, ethclient) }(ensInfo)
   104  	}
   105  
   106  	for range ensDetails {
   107  		r := <-ch
   108  		response[r.PublicKeyString] = r
   109  	}
   110  	close(ch)
   111  
   112  	return response, nil
   113  }