func (s *Service) ValidatorsByPublicKey(ctx context.Context, pubKeys []phase0.BLSPubKey) (map[phase0.BLSPubKey]*chaindb.Validator, error) {
	tx := s.tx(ctx)
	if tx == nil {
		ctx, cancel, err := s.BeginTx(ctx)
		if err != nil {
			return nil, errors.Wrap(err, "failed to begin transaction")
		}
		tx = s.tx(ctx)
		defer cancel()
	}
	sqlPubKeys := make([][]byte, len(pubKeys))
	for i := range pubKeys {
		sqlPubKeys[i] = pubKeys[i][:]
	}
	rows, err := tx.Query(ctx, `
      SELECT f_public_key
            ,f_index
            ,f_slashed
            ,f_activation_eligibility_epoch
            ,f_activation_epoch
            ,f_exit_epoch
            ,f_withdrawable_epoch
            ,f_effective_balance
      FROM t_validators
      WHERE f_public_key = ANY($1)
      ORDER BY f_index
	  `,
		sqlPubKeys,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	validators := make(map[phase0.BLSPubKey]*chaindb.Validator)
	for rows.Next() {
		validator, err := validatorFromRow(rows)
		if err != nil {
			return nil, err
		}
		validators[validator.PublicKey] = validator
	}
	return validators, nil
}
ValidatorsByPublicKey fetches all validators matching the given public keys.
This is a common starting point for external entities to query specific validators, as they should
always have the public key at a minimum, hence the return map keyed by public key.
package postgresql
import (
	"context"
	"database/sql"
	"fmt"
	"sort"
	"strings"
	"github.com/attestantio/go-eth2-client/spec/phase0"
	"github.com/jackc/pgx/v4"
	"github.com/pkg/errors"
	"github.com/wealdtech/chaind/services/chaindb"
)
var farFutureEpoch = phase0.Epoch(0xffffffffffffffff)
func (s *Service) SetValidator(ctx context.Context, validator *chaindb.Validator) error {
	tx := s.tx(ctx)
	if tx == nil {
		return ErrNoTransaction
	}
	var activationEligibilityEpoch sql.NullInt64
	var activationEpoch sql.NullInt64
	var exitEpoch sql.NullInt64
	var withdrawableEpoch sql.NullInt64
	if validator.ActivationEligibilityEpoch != farFutureEpoch {
		activationEligibilityEpoch.Valid = true
		activationEligibilityEpoch.Int64 = (int64)(validator.ActivationEligibilityEpoch)
	}
	if validator.ActivationEpoch != farFutureEpoch {
		activationEpoch.Valid = true
		activationEpoch.Int64 = (int64)(validator.ActivationEpoch)
	}
	if validator.ExitEpoch != farFutureEpoch {
		exitEpoch.Valid = true
		exitEpoch.Int64 = (int64)(validator.ExitEpoch)
	}
	if validator.WithdrawableEpoch != farFutureEpoch {
		withdrawableEpoch.Valid = true
		withdrawableEpoch.Int64 = (int64)(validator.WithdrawableEpoch)
	}
	_, err := tx.Exec(ctx, `
      INSERT INTO t_validators(f_public_key
                              ,f_index
                              ,f_slashed
                              ,f_activation_eligibility_epoch
                              ,f_activation_epoch
                              ,f_exit_epoch
                              ,f_withdrawable_epoch
                              ,f_effective_balance)
      VALUES($1,$2,$3,$4,$5,$6,$7,$8)
      ON CONFLICT (f_index) DO
      UPDATE
      SET f_public_key = excluded.f_public_key
         ,f_slashed = excluded.f_slashed
         ,f_activation_eligibility_epoch = excluded.f_activation_eligibility_epoch
         ,f_activation_epoch = excluded.f_activation_epoch
         ,f_exit_epoch = excluded.f_exit_epoch
         ,f_withdrawable_epoch = excluded.f_withdrawable_epoch
         ,f_effective_balance = excluded.f_effective_balance
		 `,
		validator.PublicKey[:],
		validator.Index,
		validator.Slashed,
		activationEligibilityEpoch,
		activationEpoch,
		exitEpoch,
		withdrawableEpoch,
		validator.EffectiveBalance,
	)
	return err
}
func (s *Service) SetValidatorBalance(ctx context.Context, balance *chaindb.ValidatorBalance) error {
	tx := s.tx(ctx)
	if tx == nil {
		return ErrNoTransaction
	}
	_, err := tx.Exec(ctx, `
      INSERT INTO t_validator_balances(f_validator_index
                                      ,f_epoch
                                      ,f_balance
                                      ,f_effective_balance)
      VALUES($1,$2,$3,$4)
      ON CONFLICT (f_validator_index, f_epoch) DO
      UPDATE
      SET f_balance = excluded.f_balance
         ,f_effective_balance = excluded.f_effective_balance
		 `,
		balance.Index,
		balance.Epoch,
		balance.Balance,
		balance.EffectiveBalance,
	)
	return err
}
func (s *Service) SetValidatorBalances(ctx context.Context, balances []*chaindb.ValidatorBalance) error {
	tx := s.tx(ctx)
	if tx == nil {
		return ErrNoTransaction
	}
	_, err := tx.CopyFrom(ctx,
		pgx.Identifier{"t_validator_balances"},
		[]string{
			"f_validator_index",
			"f_epoch",
			"f_balance",
			"f_effective_balance",
		},
		pgx.CopyFromSlice(len(balances), func(i int) ([]interface{}, error) {
			return []interface{}{
				balances[i].Index,
				balances[i].Epoch,
				balances[i].Balance,
				balances[i].EffectiveBalance,
			}, nil
		}))
	return err
}
func (s *Service) Validators(ctx context.Context) ([]*chaindb.Validator, error) {
	tx := s.tx(ctx)
	if tx == nil {
		ctx, cancel, err := s.BeginTx(ctx)
		if err != nil {
			return nil, errors.Wrap(err, "failed to begin transaction")
		}
		tx = s.tx(ctx)
		defer cancel()
	}
	rows, err := tx.Query(ctx, `
      SELECT f_public_key
            ,f_index
            ,f_slashed
            ,f_activation_eligibility_epoch
            ,f_activation_epoch
            ,f_exit_epoch
            ,f_withdrawable_epoch
            ,f_effective_balance
      FROM t_validators
      ORDER BY f_index
	  `)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	validators := make([]*chaindb.Validator, 0)
	for rows.Next() {
		validator, err := validatorFromRow(rows)
		if err != nil {
			return nil, err
		}
		validators = append(validators, validator)
	}
	return validators, nil
}