func (c *Client) localMetaFromSnapshot(name string, m data.SnapshotFileMeta) (json.RawMessage, bool) {
	b, ok := c.localMeta[name]
	if !ok {
		return nil, false
	}
	meta, err := util.GenerateSnapshotFileMeta(bytes.NewReader(b), m.HashAlgorithms()...)
	if err != nil {
		return nil, false
	}
	err = util.SnapshotFileMetaEqual(meta, m)
	return b, err == nil
}
localMetaFromSnapshot returns localmetadata if it matches the snapshot
package client
import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"github.com/theupdateframework/go-tuf/data"
	"github.com/theupdateframework/go-tuf/util"
	"github.com/theupdateframework/go-tuf/verify"
)
const (
	defaultRootDownloadLimit      = 512000
	defaultTimestampDownloadLimit = 16384
	defaultMaxDelegations         = 32
	defaultMaxRootRotations       = 1e3
)
type LocalStore interface {
	GetMeta() (map[string]json.RawMessage, error)
	SetMeta(name string, meta json.RawMessage) error
	DeleteMeta(name string) error
}
type RemoteStore interface {
	GetMeta(name string) (stream io.ReadCloser, size int64, err error)
	GetTarget(path string) (stream io.ReadCloser, size int64, err error)
}
type Client struct {
	local  LocalStore
	remote RemoteStore
	rootVer      int
	targetsVer   int
	snapshotVer  int
	timestampVer int
	targets data.TargetFiles
	localMeta map[string]json.RawMessage
	db *verify.DB
	consistentSnapshot bool
	MaxDelegations int
	MaxRootRotations int
}
func NewClient(local LocalStore, remote RemoteStore) *Client {
	return &Client{
		local:            local,
		remote:           remote,
		MaxDelegations:   defaultMaxDelegations,
		MaxRootRotations: defaultMaxRootRotations,
	}
}
func (c *Client) Init(rootKeys []*data.PublicKey, threshold int) error {
	if len(rootKeys) < threshold {
		return ErrInsufficientKeys
	}
	rootJSON, err := c.downloadMetaUnsafe("root.json", defaultRootDownloadLimit)
	if err != nil {
		return err
	}
	c.db = verify.NewDB()
	rootKeyIDs := make([]string, 0, len(rootKeys))
	for _, key := range rootKeys {
		for _, id := range key.IDs() {
			rootKeyIDs = append(rootKeyIDs, id)
			if err := c.db.AddKey(id, key); err != nil {
				return err
			}
		}
	}
	role := &data.Role{Threshold: threshold, KeyIDs: rootKeyIDs}
	if err := c.db.AddRole("root", role); err != nil {
		return err
	}
	if err := c.decodeRoot(rootJSON); err != nil {
		return err
	}
	return c.local.SetMeta("root.json", rootJSON)
}
func (c *Client) Update() (data.TargetFiles, error) {
	if err := c.UpdateRoots(); err != nil {
		if _, ok := err.(verify.ErrExpired); ok {
			return nil, ErrDecodeFailed{"root.json", err}
		}
		return nil, err
	}
	timestampJSON, err := c.downloadMetaUnsafe("timestamp.json", defaultTimestampDownloadLimit)
	if err != nil {
		return nil, err
	}
	snapshotMeta, err := c.decodeTimestamp(timestampJSON)
	if err != nil {
		return nil, err
	}
	if err := c.local.SetMeta("timestamp.json", timestampJSON); err != nil {
		return nil, err
	}
	snapshotJSON, err := c.downloadMetaFromTimestamp("snapshot.json", snapshotMeta)
	if err != nil {
		return nil, err
	}
	snapshotMetas, err := c.decodeSnapshot(snapshotJSON)
	if err != nil {
		return nil, err
	}
	if err := c.local.SetMeta("snapshot.json", snapshotJSON); err != nil {
		return nil, err
	}
	if _, ok := snapshotMetas["root.json"]; ok {
		log.Println("root pinning is not supported in Spec 1.0.19")
	}
	var updatedTargets data.TargetFiles
	targetsMeta := snapshotMetas["targets.json"]
	if !c.hasMetaFromSnapshot("targets.json", targetsMeta) {
		targetsJSON, err := c.downloadMetaFromSnapshot("targets.json", targetsMeta)
		if err != nil {
			return nil, err
		}
		updatedTargets, err = c.decodeTargets(targetsJSON)
		if err != nil {
			return nil, err
		}
		if err := c.local.SetMeta("targets.json", targetsJSON); err != nil {
			return nil, err
		}
	}
	return updatedTargets, nil
}
func (c *Client) UpdateRoots() error {
	if err := c.loadAndVerifyLocalRootMeta(   true); err != nil {
		return err
	}
	m, err := c.local.GetMeta()
	if err != nil {
		return err
	}
	type KeyInfo struct {
		KeyIDs    map[string]bool
		Threshold int
	}
	getKeyInfo := func(role string) KeyInfo {
		keyIDs := make(map[string]bool)
		for k := range c.db.GetRole(role).KeyIDs {
			keyIDs[k] = true
		}
		return KeyInfo{keyIDs, c.db.GetRole(role).Threshold}
	}
	nonRootKeyInfo := map[string]KeyInfo{"timestamp": {}, "snapshot": {}, "targets": {}}
	for k := range nonRootKeyInfo {
		nonRootKeyInfo[k] = getKeyInfo(k)
	}
	consistentSnapshot := c.consistentSnapshot
	c.consistentSnapshot = true
	nRootMetadata := m["root.json"]
	for i := 0; i < c.MaxRootRotations; i++ {
		nPlusOne := c.rootVer + 1
		nPlusOneRootPath := util.VersionedPath("root.json", nPlusOne)
		nPlusOneRootMetadata, err := c.downloadMetaUnsafe(nPlusOneRootPath, defaultRootDownloadLimit)
		if err != nil {
			if _, ok := err.(ErrMissingRemoteMetadata); ok {
				break
			}
			return err
		}
		nPlusOneRootMetadataSigned, err := c.verifyRoot(nRootMetadata, nPlusOneRootMetadata)
		if err != nil {
			return err
		}
		if _, err := c.verifyRoot(nPlusOneRootMetadata, nPlusOneRootMetadata); err != nil {
			return err
		}
		if nPlusOneRootMetadataSigned.Version != nPlusOne {
			return verify.ErrWrongVersion{
				Given:    nPlusOneRootMetadataSigned.Version,
				Expected: nPlusOne,
			}
		}
		c.rootVer = nPlusOneRootMetadataSigned.Version
		consistentSnapshot = nPlusOneRootMetadataSigned.ConsistentSnapshot
		if err := c.local.SetMeta("root.json", nPlusOneRootMetadata); err != nil {
			return err
		}
		nRootMetadata = nPlusOneRootMetadata
	}  
	if err := c.loadAndVerifyLocalRootMeta(   false); err != nil {
		return err
	}
	countDeleted := func(s1 map[string]bool, s2 map[string]bool) int {
		c := 0
		for k := range s1 {
			if _, ok := s2[k]; !ok {
				c++
			}
		}
		return c
	}
	for topLevelRolename := range nonRootKeyInfo {
		ki := getKeyInfo(topLevelRolename)
		if countDeleted(nonRootKeyInfo[topLevelRolename].KeyIDs, ki.KeyIDs) >= nonRootKeyInfo[topLevelRolename].Threshold {
			deleteMeta := map[string][]string{
				"timestamp": {"timestamp.json"},
				"snapshot":  {"timestamp.json", "snapshot.json"},
				"targets":   {"snapshot.json", "targets.json"},
			}
			for _, r := range deleteMeta[topLevelRolename] {
				c.local.DeleteMeta(r)
			}
		}
	}
	c.consistentSnapshot = consistentSnapshot
	return nil
}
func (c *Client) getLocalMeta() error {
	if err := c.loadAndVerifyLocalRootMeta(   false); err != nil {
		return err
	}
	meta, err := c.local.GetMeta()
	if err != nil {
		return nil
	}
	if timestampJSON, ok := meta["timestamp.json"]; ok {
		timestamp := &data.Timestamp{}
		if err := c.db.UnmarshalTrusted(timestampJSON, timestamp, "timestamp"); err != nil {
			return err
		}
		c.timestampVer = timestamp.Version
	}
	if snapshotJSON, ok := meta["snapshot.json"]; ok {
		snapshot := &data.Snapshot{}
		if err := c.db.UnmarshalTrusted(snapshotJSON, snapshot, "snapshot"); err != nil {
			return err
		}
		c.snapshotVer = snapshot.Version
	}
	if targetsJSON, ok := meta["targets.json"]; ok {
		targets := &data.Targets{}
		if err := c.db.UnmarshalTrusted(targetsJSON, targets, "targets"); err != nil {
			return err
		}
		c.targetsVer = targets.Version
		c.loadTargets(targets.Targets)
	}
	c.localMeta = meta
	return nil
}
func (c *Client) loadAndVerifyLocalRootMeta(ignoreExpiredCheck bool) error {
	meta, err := c.local.GetMeta()
	if err != nil {
		return err
	}
	rootJSON, ok := meta["root.json"]
	if !ok {
		return ErrNoRootKeys
	}
	s := &data.Signed{}
	if err := json.Unmarshal(rootJSON, s); err != nil {
		return err
	}
	root := &data.Root{}
	if err := json.Unmarshal(s.Signed, root); err != nil {
		return err
	}
	ndb := verify.NewDB()
	for id, k := range root.Keys {
		if err := ndb.AddKey(id, k); err != nil {
			if _, ok := err.(verify.ErrWrongID); !ok {
				return err
			}
		}
	}
	for name, role := range root.Roles {
		if err := ndb.AddRole(name, role); err != nil {
			return err
		}
	}
	if ignoreExpiredCheck {
		if err := ndb.VerifyIgnoreExpiredCheck(s, "root", 0); err != nil {
			return err
		}
	} else {
		if err := ndb.Verify(s, "root", 0); err != nil {
			return err
		}
	}
	c.consistentSnapshot = root.ConsistentSnapshot
	c.rootVer = root.Version
	c.db = ndb
	return nil
}
func (c *Client) verifyRoot(aJSON []byte, bJSON []byte) (*data.Root, error) {
	aSigned := &data.Signed{}
	if err := json.Unmarshal(aJSON, aSigned); err != nil {
		return nil, err
	}
	aRoot := &data.Root{}
	if err := json.Unmarshal(aSigned.Signed, aRoot); err != nil {
		return nil, err
	}
	bSigned := &data.Signed{}
	if err := json.Unmarshal(bJSON, bSigned); err != nil {
		return nil, err
	}
	bRoot := &data.Root{}
	if err := json.Unmarshal(bSigned.Signed, bRoot); err != nil {
		return nil, err
	}
	ndb := verify.NewDB()
	for id, k := range aRoot.Keys {
		if err := ndb.AddKey(id, k); err != nil {
			if _, ok := err.(verify.ErrWrongID); !ok {
				return nil, err
			}
		}
	}
	for name, role := range aRoot.Roles {
		if err := ndb.AddRole(name, role); err != nil {
			return nil, err
		}
	}
	if err := ndb.VerifySignatures(bSigned, "root"); err != nil {
		return nil, err
	}
	return bRoot, nil
}
func (c *Client) loadTargets(targets data.TargetFiles) {
	c.targets = make(data.TargetFiles)
	for name, meta := range targets {
		c.targets[name] = meta
		c.targets[util.NormalizeTarget(name)] = meta
	}
}
func (c *Client) downloadMetaUnsafe(name string, maxMetaSize int64) ([]byte, error) {
	r, size, err := c.remote.GetMeta(name)
	if err != nil {
		if IsNotFound(err) {
			return nil, ErrMissingRemoteMetadata{name}
		}
		return nil, ErrDownloadFailed{name, err}
	}
	defer r.Close()
	if size > maxMetaSize {
		return nil, ErrMetaTooLarge{name, size, maxMetaSize}
	}
	return ioutil.ReadAll(io.LimitReader(r, maxMetaSize))
}
func (c *Client) getRootAndLocalVersionsUnsafe() error {
	type versionData struct {
		Signed struct {
			Version int
		}
	}
	meta, err := c.local.GetMeta()
	if err != nil {
		return err
	}
	getVersion := func(name string) (int, error) {
		m, ok := meta[name]
		if !ok {
			return 0, nil
		}
		var data versionData
		if err := json.Unmarshal(m, &data); err != nil {
			return 0, err
		}
		return data.Signed.Version, nil
	}
	c.timestampVer, err = getVersion("timestamp.json")
	if err != nil {
		return err
	}
	c.snapshotVer, err = getVersion("snapshot.json")
	if err != nil {
		return err
	}
	c.targetsVer, err = getVersion("targets.json")
	if err != nil {
		return err
	}
	root, ok := meta["root.json"]
	if !ok {
		return errors.New("tuf: missing local root after downloading, this should not be possible")
	}
	c.localMeta = map[string]json.RawMessage{"root.json": root}
	return nil
}
type remoteGetFunc func(string) (io.ReadCloser, int64, error)
func (c *Client) downloadHashed(file string, get remoteGetFunc, hashes data.Hashes) (io.ReadCloser, int64, error) {
	for _, path := range util.HashedPaths(file, hashes) {
		r, size, err := get(path)
		if err != nil {
			if IsNotFound(err) {
				continue
			}
			return nil, 0, err
		}
		return r, size, nil
	}
	return nil, 0, ErrNotFound{file}
}
func (c *Client) downloadTarget(file string, get remoteGetFunc, hashes data.Hashes) (io.ReadCloser, int64, error) {
	if c.consistentSnapshot {
		return c.downloadHashed(file, get, hashes)
	} else {
		return get(file)
	}
}
func (c *Client) downloadMeta(name string, version int, m data.FileMeta) ([]byte, error) {
	r, size, err := func() (io.ReadCloser, int64, error) {
		if c.consistentSnapshot {
			path := util.VersionedPath(name, version)
			r, size, err := c.remote.GetMeta(path)
			if err == nil {
				return r, size, nil
			}
			return nil, 0, err
		} else {
			return c.remote.GetMeta(name)
		}
	}()
	if err != nil {
		if IsNotFound(err) {
			return nil, ErrMissingRemoteMetadata{name}
		}
		return nil, err
	}
	defer r.Close()
	var stream io.Reader
	if m.Length != 0 {
		if size >= 0 && size != m.Length {
			return nil, ErrWrongSize{name, size, m.Length}
		}
		stream = io.LimitReader(r, m.Length)
	} else {
		stream = r
	}
	return ioutil.ReadAll(stream)
}
func (c *Client) downloadMetaFromSnapshot(name string, m data.SnapshotFileMeta) ([]byte, error) {
	b, err := c.downloadMeta(name, m.Version, m.FileMeta)
	if err != nil {
		return nil, err
	}
	meta, err := util.GenerateSnapshotFileMeta(bytes.NewReader(b), m.HashAlgorithms()...)
	if err != nil {
		return nil, err
	}
	if err := util.SnapshotFileMetaEqual(meta, m); err != nil {
		return nil, ErrDownloadFailed{name, err}
	}
	return b, nil
}
func (c *Client) downloadMetaFromTimestamp(name string, m data.TimestampFileMeta) ([]byte, error) {
	b, err := c.downloadMeta(name, m.Version, m.FileMeta)
	if err != nil {
		return nil, err
	}
	meta, err := util.GenerateTimestampFileMeta(bytes.NewReader(b), m.HashAlgorithms()...)
	if err != nil {
		return nil, err
	}
	if err := util.TimestampFileMetaEqual(meta, m); err != nil {
		return nil, ErrDownloadFailed{name, err}
	}
	return b, nil
}
func (c *Client) decodeRoot(b json.RawMessage) error {
	root := &data.Root{}
	if err := c.db.Unmarshal(b, root, "root", c.rootVer); err != nil {
		return ErrDecodeFailed{"root.json", err}
	}
	c.rootVer = root.Version
	c.consistentSnapshot = root.ConsistentSnapshot
	return nil
}
func (c *Client) decodeSnapshot(b json.RawMessage) (data.SnapshotFiles, error) {
	snapshot := &data.Snapshot{}
	if err := c.db.Unmarshal(b, snapshot, "snapshot", c.snapshotVer); err != nil {
		return data.SnapshotFiles{}, ErrDecodeFailed{"snapshot.json", err}
	}
	c.snapshotVer = snapshot.Version
	return snapshot.Meta, nil
}
func (c *Client) decodeTargets(b json.RawMessage) (data.TargetFiles, error) {
	targets := &data.Targets{}
	if err := c.db.Unmarshal(b, targets, "targets", c.targetsVer); err != nil {
		return nil, ErrDecodeFailed{"targets.json", err}
	}
	updatedTargets := make(data.TargetFiles)
	for path, meta := range targets.Targets {
		if local, ok := c.targets[path]; ok {
			if err := util.TargetFileMetaEqual(local, meta); err == nil {
				continue
			}
		}
		updatedTargets[path] = meta
	}
	c.targetsVer = targets.Version
	c.loadTargets(targets.Targets)
	return updatedTargets, nil
}
func (c *Client) decodeTimestamp(b json.RawMessage) (data.TimestampFileMeta, error) {
	timestamp := &data.Timestamp{}
	if err := c.db.Unmarshal(b, timestamp, "timestamp", c.timestampVer); err != nil {
		return data.TimestampFileMeta{}, ErrDecodeFailed{"timestamp.json", err}
	}
	c.timestampVer = timestamp.Version
	return timestamp.Meta["snapshot.json"], nil
}
func (c *Client) hasMetaFromSnapshot(name string, m data.SnapshotFileMeta) bool {
	_, ok := c.localMetaFromSnapshot(name, m)
	return ok
}