func (st *setDeviceState) ValidateFieldIsNotZero(path string) error {
	if st.HasSetField(path) {
		if st.Device.FieldIsZero(path) {
			return newInvalidFieldValueError(path).WithCause(errFieldIsZero.WithAttributes("name", path))
		}
		return nil
	}
	v, ok := st.zeroPaths[path]
	if !ok {
		st.zeroPaths[path] = false
		return nil
	}
	if v {
		panic(fmt.Sprintf("path `%s` requested to be both zero and not zero", path))
	}
	return nil
}
ValidateFieldIsNotZero ensures that path is not zero.
package networkserver
import (
	"bytes"
	"context"
	"fmt"
	"strings"
	pbtypes "github.com/gogo/protobuf/types"
	"go.thethings.network/lorawan-stack/v3/pkg/auth/rights"
	"go.thethings.network/lorawan-stack/v3/pkg/band"
	"go.thethings.network/lorawan-stack/v3/pkg/crypto"
	"go.thethings.network/lorawan-stack/v3/pkg/crypto/cryptoutil"
	"go.thethings.network/lorawan-stack/v3/pkg/errors"
	"go.thethings.network/lorawan-stack/v3/pkg/events"
	"go.thethings.network/lorawan-stack/v3/pkg/frequencyplans"
	"go.thethings.network/lorawan-stack/v3/pkg/log"
	. "go.thethings.network/lorawan-stack/v3/pkg/networkserver/internal"
	"go.thethings.network/lorawan-stack/v3/pkg/networkserver/internal/time"
	"go.thethings.network/lorawan-stack/v3/pkg/networkserver/mac"
	"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
	"go.thethings.network/lorawan-stack/v3/pkg/types"
)
var (
	evtCreateEndDevice = events.Define(
		"ns.end_device.create", "create end device",
		events.WithVisibility(ttnpb.RIGHT_APPLICATION_DEVICES_READ),
		events.WithAuthFromContext(),
		events.WithClientInfoFromContext(),
	)
	evtUpdateEndDevice = events.Define(
		"ns.end_device.update", "update end device",
		events.WithVisibility(ttnpb.RIGHT_APPLICATION_DEVICES_READ),
		events.WithUpdatedFieldsDataType(),
		events.WithAuthFromContext(),
		events.WithClientInfoFromContext(),
	)
	evtDeleteEndDevice = events.Define(
		"ns.end_device.delete", "delete end device",
		events.WithVisibility(ttnpb.RIGHT_APPLICATION_DEVICES_READ),
		events.WithAuthFromContext(),
		events.WithClientInfoFromContext(),
	)
)
const maxRequiredDeviceReadRightCount = 3
func appendRequiredDeviceReadRights(rights []ttnpb.Right, gets ...string) []ttnpb.Right {
	if len(gets) == 0 {
		return rights
	}
	rights = append(rights,
		ttnpb.RIGHT_APPLICATION_DEVICES_READ,
	)
	if ttnpb.HasAnyField(gets,
		"pending_session.queued_application_downlinks",
		"queued_application_downlinks",
		"session.queued_application_downlinks",
	) {
		rights = append(rights, ttnpb.RIGHT_APPLICATION_TRAFFIC_READ)
	}
	if ttnpb.HasAnyField(gets,
		"mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		"mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		"mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
		"pending_mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		"pending_mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		"pending_mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
		"pending_session.keys.f_nwk_s_int_key.key",
		"pending_session.keys.nwk_s_enc_key.key",
		"pending_session.keys.s_nwk_s_int_key.key",
		"session.keys.f_nwk_s_int_key.key",
		"session.keys.nwk_s_enc_key.key",
		"session.keys.s_nwk_s_int_key.key",
	) {
		rights = append(rights, ttnpb.RIGHT_APPLICATION_DEVICES_READ_KEYS)
	}
	return rights
}
func addDeviceGetPaths(paths ...string) []string {
	gets := paths
	if ttnpb.HasAnyField(paths,
		"mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		"mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		"mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
		"pending_mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		"pending_mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		"pending_mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
		"pending_session.keys.f_nwk_s_int_key.key",
		"pending_session.keys.nwk_s_enc_key.key",
		"pending_session.keys.s_nwk_s_int_key.key",
		"session.keys.f_nwk_s_int_key.key",
		"session.keys.nwk_s_enc_key.key",
		"session.keys.s_nwk_s_int_key.key",
	) {
		if ttnpb.HasAnyField(paths,
			"pending_session.keys.f_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"pending_session.keys.f_nwk_s_int_key.encrypted_key",
				"pending_session.keys.f_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"pending_session.keys.nwk_s_enc_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"pending_session.keys.nwk_s_enc_key.encrypted_key",
				"pending_session.keys.nwk_s_enc_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"pending_session.keys.s_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"pending_session.keys.s_nwk_s_int_key.encrypted_key",
				"pending_session.keys.s_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"session.keys.f_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"session.keys.f_nwk_s_int_key.encrypted_key",
				"session.keys.f_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"session.keys.nwk_s_enc_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"session.keys.nwk_s_enc_key.encrypted_key",
				"session.keys.nwk_s_enc_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"session.keys.s_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"session.keys.s_nwk_s_int_key.encrypted_key",
				"session.keys.s_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"pending_mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"pending_mac_state.queued_join_accept.keys.f_nwk_s_int_key.encrypted_key",
				"pending_mac_state.queued_join_accept.keys.f_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"pending_mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"pending_mac_state.queued_join_accept.keys.nwk_s_enc_key.encrypted_key",
				"pending_mac_state.queued_join_accept.keys.nwk_s_enc_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"pending_mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"pending_mac_state.queued_join_accept.keys.s_nwk_s_int_key.encrypted_key",
				"pending_mac_state.queued_join_accept.keys.s_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"mac_state.queued_join_accept.keys.f_nwk_s_int_key.encrypted_key",
				"mac_state.queued_join_accept.keys.f_nwk_s_int_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"mac_state.queued_join_accept.keys.nwk_s_enc_key.encrypted_key",
				"mac_state.queued_join_accept.keys.nwk_s_enc_key.kek_label",
			)
		}
		if ttnpb.HasAnyField(paths,
			"mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
		) {
			gets = ttnpb.AddFields(gets,
				"mac_state.queued_join_accept.keys.s_nwk_s_int_key.encrypted_key",
				"mac_state.queued_join_accept.keys.s_nwk_s_int_key.kek_label",
			)
		}
	}
	return gets
}
func unwrapSelectedSessionKeys(ctx context.Context, kv crypto.KeyVault, dev *ttnpb.EndDevice, paths ...string) error {
	if dev.PendingSession != nil && ttnpb.HasAnyField(paths,
		"pending_session.keys.f_nwk_s_int_key.key",
		"pending_session.keys.nwk_s_enc_key.key",
		"pending_session.keys.s_nwk_s_int_key.key",
	) {
		sk, err := cryptoutil.UnwrapSelectedSessionKeys(ctx, kv, dev.PendingSession.SessionKeys, "pending_session.keys", paths...)
		if err != nil {
			return err
		}
		dev.PendingSession.SessionKeys = sk
	}
	if dev.Session != nil && ttnpb.HasAnyField(paths,
		"session.keys.f_nwk_s_int_key.key",
		"session.keys.nwk_s_enc_key.key",
		"session.keys.s_nwk_s_int_key.key",
	) {
		sk, err := cryptoutil.UnwrapSelectedSessionKeys(ctx, kv, dev.Session.SessionKeys, "session.keys", paths...)
		if err != nil {
			return err
		}
		dev.Session.SessionKeys = sk
	}
	if dev.PendingMacState.GetQueuedJoinAccept() != nil && ttnpb.HasAnyField(paths,
		"pending_mac_state.queued_join_accept.keys.f_nwk_s_int_key.key",
		"pending_mac_state.queued_join_accept.keys.nwk_s_enc_key.key",
		"pending_mac_state.queued_join_accept.keys.s_nwk_s_int_key.key",
	) {
		sk, err := cryptoutil.UnwrapSelectedSessionKeys(ctx, kv, dev.PendingMacState.QueuedJoinAccept.Keys, "pending_mac_state.queued_join_accept.keys", paths...)
		if err != nil {
			return err
		}
		dev.PendingMacState.QueuedJoinAccept.Keys = sk
	}
	return nil
}
func (ns *NetworkServer) Get(ctx context.Context, req *ttnpb.GetEndDeviceRequest) (*ttnpb.EndDevice, error) {
	if err := rights.RequireApplication(ctx, req.ApplicationIdentifiers, appendRequiredDeviceReadRights(
		make([]ttnpb.Right, 0, maxRequiredDeviceReadRightCount),
		req.FieldMask.GetPaths()...,
	)...); err != nil {
		return nil, err
	}
	dev, ctx, err := ns.devices.GetByID(ctx, req.ApplicationIdentifiers, req.DeviceId, addDeviceGetPaths(req.FieldMask.GetPaths()...))
	if err != nil {
		logRegistryRPCError(ctx, err, "Failed to get device from registry")
		return nil, err
	}
	if err := unwrapSelectedSessionKeys(ctx, ns.KeyVault, dev, req.FieldMask.GetPaths()...); err != nil {
		log.FromContext(ctx).WithError(err).Error("Failed to unwrap selected keys")
		return nil, err
	}
	return ttnpb.FilterGetEndDevice(dev, req.FieldMask.GetPaths()...)
}
func newInvalidFieldValueError(field string) *errors.Error {
	return errInvalidFieldValue.WithAttributes("field", field)
}
type setDeviceState struct {
	Device *ttnpb.EndDevice
	paths     []string
	extraSets []string
	extraGets []string
	pathsCache     map[string]bool
	extraSetsCache map[string]bool
	extraGetsCache map[string]bool
	zeroPaths map[string]bool
	onGet     []func(*ttnpb.EndDevice) error
}
func hasAnyField(fs []string, cache map[string]bool, paths ...string) bool {
outer:
	for _, p := range paths {
		i := len(p)
		for ; i > 0; i = strings.LastIndex(p[:i], ".") {
			p := p[:i]
			v, ok := cache[p]
			if !ok {
				continue
			}
			if !v {
				continue outer
			}
			return true
		}
		v := ttnpb.HasAnyField(fs, p)
		cache[p] = v
		if v {
			return v
		}
	}
	return false
}
func (st *setDeviceState) hasPathField(paths ...string) bool {
	return hasAnyField(st.paths, st.pathsCache, paths...)
}
func (st *setDeviceState) HasSetField(paths ...string) bool {
	return st.hasPathField(paths...) || hasAnyField(st.extraSets, st.extraSetsCache, paths...)
}
func (st *setDeviceState) HasGetField(paths ...string) bool {
	return st.hasPathField(paths...) || hasAnyField(st.extraGets, st.extraGetsCache, paths...)
}
func addFields(hasField func(...string) bool, selected []string, cache map[string]bool, paths ...string) []string {
	for _, p := range paths {
		if hasField(p) {
			continue
		}
		cache[p] = true
		selected = append(selected, p)
	}
	return selected
}
func (st *setDeviceState) AddSetFields(paths ...string) {
	st.extraSets = addFields(st.HasSetField, st.extraSets, st.extraSetsCache, paths...)
}
func (st *setDeviceState) AddGetFields(paths ...string) {
	st.extraGets = addFields(st.HasGetField, st.extraGets, st.extraGetsCache, paths...)
}
func (st *setDeviceState) SetFields() []string {
	return append(st.paths, st.extraSets...)
}
func (st *setDeviceState) GetFields() []string {
	return append(st.paths, st.extraGets...)
}
func (st *setDeviceState) WithField(f func(*ttnpb.EndDevice) error, path string) error {
	if st.HasSetField(path) {
		return f(st.Device)
	}
	st.AddGetFields(path)
	st.onGet = append(st.onGet, func(stored *ttnpb.EndDevice) error {
		return f(stored)
	})
	return nil
}
func (st *setDeviceState) WithFields(f func(map[string]*ttnpb.EndDevice) error, paths ...string) error {
	storedPaths := make([]string, 0, len(paths))
	m := make(map[string]*ttnpb.EndDevice, len(paths))
	for _, p := range paths {
		if st.HasSetField(p) {
			m[p] = st.Device
		} else {
			storedPaths = append(storedPaths, p)
		}
	}
	if len(storedPaths) == 0 {
		return f(m)
	}
	st.AddGetFields(storedPaths...)
	st.onGet = append(st.onGet, func(stored *ttnpb.EndDevice) error {
		if stored == nil {
			return f(m)
		}
		for _, p := range storedPaths {
			m[p] = stored
		}
		return f(m)
	})
	return nil
}
func (st *setDeviceState) ValidateField(isValid func(*ttnpb.EndDevice) bool, path string) error {
	return st.WithField(func(dev *ttnpb.EndDevice) error {
		if !isValid(dev) {
			return newInvalidFieldValueError(path)
		}
		return nil
	}, path)
}
var errFieldNotZero = errors.DefineInvalidArgument("field_not_zero", "field `{name}` is not zero")
func (st *setDeviceState) ValidateFieldIsZero(path string) error {
	if st.HasSetField(path) {
		if !st.Device.FieldIsZero(path) {
			return newInvalidFieldValueError(path).WithCause(errFieldNotZero.WithAttributes("name", path))
		}
		return nil
	}
	v, ok := st.zeroPaths[path]
	if !ok {
		st.zeroPaths[path] = true
		return nil
	}
	if !v {
		panic(fmt.Sprintf("path `%s` requested to be both zero and not zero", path))
	}
	return nil
}
var errFieldIsZero = errors.DefineInvalidArgument("field_is_zero", "field `{name}` is zero")