func (tlc *TemporalLogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) {
	type result struct {
		roots []ct.ASN1Cert
		err   error
	}
	results := make(chan result, len(tlc.Clients))
	for _, c := range tlc.Clients {
		go func(c *LogClient) {
			var r result
			r.roots, r.err = c.GetAcceptedRoots(ctx)
			results <- r
		}(c)
	}
	var allRoots []ct.ASN1Cert
	seen := make(map[[sha256.Size]byte]bool)
	for range tlc.Clients {
		r := <-results
		if r.err != nil {
			return nil, r.err
		}
		for _, root := range r.roots {
			h := sha256.Sum256(root.Data)
			if seen[h] {
				continue
			}
			seen[h] = true
			allRoots = append(allRoots, root)
		}
	}
	return allRoots, nil
}
GetAcceptedRoots retrieves the set of acceptable root certificates for all
of the shards of a temporal log (i.e. the union).
package client
import (
	"context"
	"crypto/sha256"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/client/configpb"
	"github.com/google/certificate-transparency-go/jsonclient"
	"github.com/google/certificate-transparency-go/x509"
	"google.golang.org/protobuf/encoding/prototext"
	"google.golang.org/protobuf/proto"
)
type interval struct {
	lower *time.Time  
	upper *time.Time  
}
func TemporalLogConfigFromFile(filename string) (*configpb.TemporalLogConfig, error) {
	if len(filename) == 0 {
		return nil, errors.New("log config filename empty")
	}
	cfgBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("failed to read log config: %v", err)
	}
	var cfg configpb.TemporalLogConfig
	if txtErr := prototext.Unmarshal(cfgBytes, &cfg); txtErr != nil {
		if binErr := proto.Unmarshal(cfgBytes, &cfg); binErr != nil {
			return nil, fmt.Errorf("failed to parse TemporalLogConfig from %q as text protobuf (%v) or binary protobuf (%v)", filename, txtErr, binErr)
		}
	}
	if len(cfg.Shard) == 0 {
		return nil, errors.New("empty log config found")
	}
	return &cfg, nil
}
type AddLogClient interface {
	AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error)
	AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error)
	GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error)
}
type TemporalLogClient struct {
	Clients   []*LogClient
	intervals []interval
}
func NewTemporalLogClient(cfg *configpb.TemporalLogConfig, hc *http.Client) (*TemporalLogClient, error) {
	if len(cfg.GetShard()) == 0 {
		return nil, errors.New("empty config")
	}
	overall, err := shardInterval(cfg.Shard[0])
	if err != nil {
		return nil, fmt.Errorf("cfg.Shard[0] invalid: %v", err)
	}
	intervals := make([]interval, 0, len(cfg.Shard))
	intervals = append(intervals, overall)
	for i := 1; i < len(cfg.Shard); i++ {
		interval, err := shardInterval(cfg.Shard[i])
		if err != nil {
			return nil, fmt.Errorf("cfg.Shard[%d] invalid: %v", i, err)
		}
		if overall.upper == nil {
			return nil, fmt.Errorf("cfg.Shard[%d] extends an interval with no upper bound", i)
		}
		if interval.lower == nil {
			return nil, fmt.Errorf("cfg.Shard[%d] has no lower bound but extends an interval", i)
		}
		if !interval.lower.Equal(*overall.upper) {
			return nil, fmt.Errorf("cfg.Shard[%d] starts at %v but previous interval ended at %v", i, interval.lower, overall.upper)
		}
		overall.upper = interval.upper
		intervals = append(intervals, interval)
	}
	clients := make([]*LogClient, 0, len(cfg.Shard))
	for i, shard := range cfg.Shard {
		opts := jsonclient.Options{UserAgent: "ct-go-multilog/1.0"}
		opts.PublicKeyDER = shard.GetPublicKeyDer()
		c, err := New(shard.Uri, hc, opts)
		if err != nil {
			return nil, fmt.Errorf("failed to create client for cfg.Shard[%d]: %v", i, err)
		}
		clients = append(clients, c)
	}
	tlc := TemporalLogClient{
		Clients:   clients,
		intervals: intervals,
	}
	return &tlc, nil
}