func (d *DcrtimeStore) version(w http.ResponseWriter, r *http.Request) {
	var versions []uint
	var prefixes []string
	vs, _ := parseAndValidateAPIVersions(d.cfg.APIVersions)
	for _, v := range vs {
		switch v {
		case v1.APIVersion:
			versions = append(versions, v1.APIVersion)
			prefixes = append(prefixes, v1.RoutePrefix)
		case v2.APIVersion:
			versions = append(versions, v2.APIVersion)
			prefixes = append(prefixes, v2.RoutePrefix)
		}
	}
	versionReply := v2.VersionReply{
		Versions:      versions,
		RoutePrefixes: prefixes,
	}
	via := r.RemoteAddr
	xff := r.Header.Get(forward)
	if xff != "" {
		via = fmt.Sprintf("%v via %v", xff, r.RemoteAddr)
	}
	log.Infof("%v Version %v", r.URL.Path, via)
	util.RespondWithJSON(w, http.StatusOK, versionReply)
}
version returns the supported API versions running on the server.
Handles /version
package main
import (
	"bytes"
	"context"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"time"
	v1 "github.com/decred/dcrtime/api/v1"
	v2 "github.com/decred/dcrtime/api/v2"
	"github.com/decred/dcrtime/dcrtimed/backend"
	"github.com/decred/dcrtime/dcrtimed/backend/filesystem"
	"github.com/decred/dcrtime/util"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)
const (
	fStr = "20060102.150405"
	forward = "X-Forwarded-For"
)
var (
	interruptSignals = []os.Signal{os.Interrupt}
)
type DcrtimeStore struct {
	backend    backend.Backend
	cfg        *config
	router     *mux.Router
	ctx        context.Context
	httpClient *http.Client
	apiTokens  map[string]struct{}
}
func (d *DcrtimeStore) sendToBackend(ctx context.Context, w http.ResponseWriter,
	method, route, contentType, remoteAddr string, body *bytes.Reader) {
	storeHost := fmt.Sprintf("https://%s%s", d.cfg.StoreHost, route)
	req, err := http.NewRequestWithContext(ctx, method, storeHost, body)
	if err != nil {
		log.Errorf("Error generating new http request: %v", err)
		util.RespondWithError(w, http.StatusServiceUnavailable,
			"Server failed to generate a new http request")
		return
	}
	req.Header.Set("Content-Type", contentType)
	req.Header.Set(forward, remoteAddr)
	resp, err := d.httpClient.Do(req)
	if err != nil {
		log.Errorf("Error posting to storehost: %v", err)
		util.RespondWithError(w, http.StatusServiceUnavailable,
			"Server busy, please try again later.")
		return
	}
	defer resp.Body.Close()
	bodyBuf := new(bytes.Buffer)
	_, err = bodyBuf.ReadFrom(resp.Body)
	if err != nil {
		log.Errorf("ReadFrom failed: %v", err)
		util.RespondWithError(w, http.StatusServiceUnavailable,
			"Server busy, please try again later.")
		return
	}
	if resp.StatusCode != http.StatusOK {
		if resp.StatusCode == http.StatusUnauthorized {
			util.RespondWithCopy(w, http.StatusUnauthorized, "application/json",
				bodyBuf.Bytes())
			return
		}
		e, err := getError(resp.Body)
		if err != nil {
			log.Errorf("Bad status posting to %v: %v", storeHost,
				resp.Status)
		} else {
			log.Errorf("Bad status posting to %v: %v\n%v",
				storeHost, resp.Status, e)
		}
		util.RespondWithError(w, http.StatusInternalServerError,
			bodyBuf.String())
		return
	}
	err = util.RespondWithCopy(w, resp.StatusCode,
		resp.Header.Get("Content-Type"), bodyBuf.Bytes())
	if err != nil {
		log.Errorf("Error responding to client: %v", err)
	}
}
func (d *DcrtimeStore) proxyStatusV1(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Unable to read request")
		return
	}
	var s v1.Status
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&s); err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Invalid request payload")
		return
	}
	d.sendToBackend(r.Context(), w, r.Method, v1.StatusRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader(b))
	log.Infof("%v Status %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyTimestampV1(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Unable to read request")
		return
	}
	var t v1.Timestamp
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&t); err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Invalid request payload")
		return
	}
	d.sendToBackend(r.Context(), w, r.Method, v1.TimestampRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader(b))
	for _, v := range t.Digests {
		log.Infof("%v Timestamp %v: %v", r.URL.Path, r.RemoteAddr, v)
	}
}
func (d *DcrtimeStore) proxyVerifyV1(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Unable to read request")
		return
	}
	var v v1.Verify
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&v); err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Invalid request payload")
		return
	}
	d.sendToBackend(r.Context(), w, r.Method, v1.VerifyRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader(b))
	log.Infof("%v Verify %v: Timestamps %v Digests %v",
		r.URL.Path, r.RemoteAddr, len(v.Timestamps), len(v.Digests))
}
func (d *DcrtimeStore) proxyWalletBalanceV1(w http.ResponseWriter, r *http.Request) {
	apiToken := r.URL.Query().Get("apitoken")
	route := v1.WalletBalanceRoute + "?apitoken=" + apiToken
	d.sendToBackend(r.Context(), w, r.Method, route, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader([]byte{}))
	log.Infof("%v WalletBalance %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyLastAnchorV1(w http.ResponseWriter, r *http.Request) {
	d.sendToBackend(r.Context(), w, r.Method, v1.LastAnchorRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader([]byte{}))
	log.Infof("%v LastAnchor %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyStatusV2(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Unable to read request")
		return
	}
	var s v2.Status
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&s); err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Invalid request payload")
		return
	}
	d.sendToBackend(r.Context(), w, r.Method, v2.StatusRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader(b))
	log.Infof("%v Status %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyTimestampV2(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	dig := r.Form.Get("digest")
	route := v2.TimestampRoute + "?digest=" + dig
	r.Body.Close()
	d.sendToBackend(r.Context(), w, http.MethodGet, route, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader([]byte{}))
	log.Infof("%v Timestamp %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyVerifyV2(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	dig := r.Form.Get("digest")
	route := v2.VerifyRoute + "?digest=" + dig
	r.Body.Close()
	d.sendToBackend(r.Context(), w, r.Method, route, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader([]byte{}))
	log.Infof("%v Verify %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyTimestampBatchV2(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Unable to read request")
		return
	}
	var t v2.TimestampBatch
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&t); err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Invalid request payload")
		return
	}
	d.sendToBackend(r.Context(), w, r.Method, v2.TimestampBatchRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader(b))
	for _, v := range t.Digests {
		log.Infof("Timestamp %v: %v", r.RemoteAddr, v)
	}
	log.Infof("%v TimestampBatch %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyVerifyBatchV2(w http.ResponseWriter, r *http.Request) {
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Unable to read request")
		return
	}
	var v v2.VerifyBatch
	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&v); err != nil {
		util.RespondWithError(w, http.StatusBadRequest,
			"Invalid request payload")
		return
	}
	d.sendToBackend(r.Context(), w, r.Method, v2.VerifyBatchRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader(b))
	log.Infof("%v VerifyBatch %v: Timestamps %v Digests %v",
		r.URL.Path, r.RemoteAddr, len(v.Timestamps), len(v.Digests))
}
func (d *DcrtimeStore) proxyWalletBalanceV2(w http.ResponseWriter, r *http.Request) {
	apiToken := r.URL.Query().Get("apitoken")
	route := v2.WalletBalanceRoute + "?apitoken=" + apiToken
	d.sendToBackend(r.Context(), w, r.Method, route, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader([]byte{}))
	log.Infof("%v WalletBalance %v", r.URL.Path, r.RemoteAddr)
}
func (d *DcrtimeStore) proxyLastAnchorV2(w http.ResponseWriter, r *http.Request) {
	d.sendToBackend(r.Context(), w, r.Method, v2.LastAnchorRoute, r.Header.Get("Content-Type"),
		r.RemoteAddr, bytes.NewReader([]byte{}))
	log.Infof("%v LastAnchor %v", r.URL.Path, r.RemoteAddr)
}