<commit_before>package config

import (
	"fmt"
	"strings"
)

/* Config objects */
type Meta struct {
	node Node // origin node
}

func (meta Meta) Source() string {
	if meta.node.Source != nil {
		return meta.node.Source.String()
	} else {
		// some nodes are implicitly created, and thus not directly applicable..
		return ""
	}
}

func (meta Meta) Path() string {
	return meta.node.Path
}

type ServiceFrontend struct {
	Meta `json:"-"`

	IPv4 string `json:"ipv4,omitempty"`
	IPv6 string `json:"ipv6,omitempty"`
	TCP  uint16 `json:"tcp,omitempty"`
	UDP  uint16 `json:"udp,omitempty"`
}

type ServiceBackend struct {
	Meta `json:"-"`

	IPv4 string `json:"ipv4,omitempty"`
	IPv6 string `json:"ipv6,omitempty"`
	TCP  uint16 `json:"tcp,omitempty"`
	UDP  uint16 `json:"udp,omitempty"`

	Weight uint `json:"weight"` // default: 10
}

const ServiceBackendWeight uint = 10

type Service struct {
	Meta `json:"-"`

	Frontend *ServiceFrontend
	Backends map[string]ServiceBackend
}

func (service *Service) setBackend(backendName string, serviceBackend ServiceBackend, remove bool) {
	if service.Backends == nil {
		service.Backends = make(map[string]ServiceBackend)
	}

	if remove {
		delete(service.Backends, backendName)
	} else {
		service.Backends[backendName] = serviceBackend
	}
}

func (service *Service) merge(other Service) {
	if other.Frontend != nil {
		service.Frontend = other.Frontend
	}

	// backends
	if service.Backends == nil {
		service.Backends = make(map[string]ServiceBackend)
	}

	for backendName, backend := range other.Backends {
		service.Backends[backendName] = backend
	}
}

type Route struct {
	Meta `json:"-"`

	// IPv4/IPv6 prefix to match
	// empty for default match
	Prefix string `json:",omitempty"`

	// Override backend IPv4/IPv6 address for ipvs
	Gateway string `json:",omitempty"`

	// Configure IPVS fwd-method for destination:
	//  droute tunnel masq
	// Filter out backend if set to empty string.
	IPVSMethod string `json:",omitempty"`
}

// Top-level config object
type Config struct {
	Routes   map[string]Route
	Services map[string]Service
}

func (config *Config) setService(serviceName string, service Service) {
	if config.Services == nil {
		config.Services = map[string]Service{serviceName: service}
	} else {
		config.Services[serviceName] = service
	}
}

func (config *Config) updateServices(node Node) error {
	if node.Remove {
		config.Services = nil
	}

	return nil
}

func (config *Config) updateService(node Node, serviceName string) error {
	if node.Remove {
		delete(config.Services, serviceName)
	} else {
		service := config.Services[serviceName]

		config.setService(serviceName, service)
	}

	return nil
}

func (config *Config) updateServiceFrontend(node Node, serviceName string, serviceFrontend ServiceFrontend) error {
	service := config.Services[serviceName]

	if node.Remove {
		service.Frontend = nil
	} else {
		service.Frontend = &serviceFrontend
	}

	config.setService(serviceName, service)

	return nil
}

func (config *Config) updateServiceBackends(node Node, serviceName string) error {
	service := config.Services[serviceName]

	if node.Remove {
		service.Backends = nil
	}

	config.setService(serviceName, service)

	return nil
}

func (config *Config) updateServiceBackend(node Node, serviceName string, backendName string, serviceBackend ServiceBackend) error {
	service := config.Services[serviceName]

	service.setBackend(backendName, serviceBackend, node.Remove)
	config.setService(serviceName, service)

	return nil
}

func (config *Config) updateRoutes(node Node) error {
	if node.Remove {
		config.Routes = nil
	}

	return nil
}

func (config *Config) updateRoute(node Node, routeName string, route Route) error {
	if node.Remove {
		// clear
		delete(config.Routes, routeName)

	} else if config.Routes == nil {
		config.Routes = map[string]Route{routeName: route}

	} else {
		config.Routes[routeName] = route
	}

	return nil
}

// Update config from Node
func (config *Config) update(node Node) error {
	nodePath := strings.Split(node.Path, "/")

	if len(node.Path) == 0 {
		// Split("", "/") would give [""]
		nodePath = nil
	}

	// match config tree path
	if len(nodePath) == 0 && node.IsDir {
		// XXX: just ignore? Undefined if it makes sense to do anything here
		return nil

	} else if len(nodePath) == 1 && nodePath[0] == "services" && node.IsDir {
		// recursive on all services
		return config.updateServices(node)

	} else if len(nodePath) >= 2 && nodePath[0] == "services" {
		serviceName := nodePath[1]

		if len(nodePath) == 2 && node.IsDir {
			return config.updateService(node, serviceName)

		} else if len(nodePath) == 3 && nodePath[2] == "frontend" && !node.IsDir {
			var serviceFrontend = ServiceFrontend{
				Meta: Meta{node: node},
			}

			if err := node.unmarshal(&serviceFrontend); err != nil {
				return fmt.Errorf("service %s frontend: %s", serviceName, err)
			}

			return config.updateServiceFrontend(node, serviceName, serviceFrontend)

		} else if len(nodePath) == 3 && nodePath[2] == "backends" && node.IsDir {
			// recursive on all backends
			return config.updateServiceBackends(node, serviceName)

		} else if len(nodePath) >= 4 && nodePath[2] == "backends" {
			backendName := nodePath[3]

			if len(nodePath) == 4 && !node.IsDir {
				var serviceBackend = ServiceBackend{
					Meta:   Meta{node: node},
					Weight: ServiceBackendWeight,
				}

				if err := node.unmarshal(&serviceBackend); err != nil {
					return fmt.Errorf("service %s backend %s: %s", serviceName, backendName, err)
				}

				return config.updateServiceBackend(node, serviceName, backendName, serviceBackend)

			} else {
				return fmt.Errorf("Ignore unknown service %s backends node", serviceName)
			}

		} else {
			return fmt.Errorf("Ignore unknown service %s node", serviceName)
		}

	} else if len(nodePath) == 1 && nodePath[0] == "routes" && node.IsDir {
		return config.updateRoutes(node)

	} else if len(nodePath) >= 2 && nodePath[0] == "routes" {
		routeName := nodePath[1]

		if len(nodePath) == 2 && !node.IsDir {
			var route = Route{
				Meta: Meta{node: node},
			}

			if err := node.unmarshal(&route); err != nil {
				return fmt.Errorf("route %s: %s", routeName, err)
			}

			return config.updateRoute(node, routeName, route)

		} else {
			return fmt.Errorf("Ignore unknown route node")
		}
	} else {
		return fmt.Errorf("Ignore unknown node")
	}
}

func (config Config) walk(visit func(node Node)) (err error) {
	defer func() {
		if panicValue := recover(); panicValue == nil {

		} else if panicError, ok := panicValue.(error); ok {
			err = panicError
		} else {
			panic(panicValue)
		}
	}()

	if config.Routes != nil {
		for routeName, route := range config.Routes {
			visit(makeNode(route, "routes", routeName))
		}
	}

	if config.Services != nil {
		for serviceName, service := range config.Services {
			if service.Frontend != nil {
				visit(makeNode(service.Frontend, "services", serviceName, "frontend"))
			}

			for backendName, backend := range service.Backends {
				visit(makeNode(backend, "services", serviceName, "backends", backendName))
			}
		}
	}

	return
}

func (config Config) compile() (map[string]Node, error) {
	var nodes = map[string]Node{}

	err := config.walk(func(node Node) {
		nodes[node.Path] = node
	})
	return nodes, err
}

func (config *Config) merge(mergeConfig Config) {
	for serviceName, mergeService := range mergeConfig.Services {
		service := config.Services[serviceName]

		service.merge(mergeService)

		if config.Services == nil {
			config.Services = map[string]Service{serviceName: service}
		} else {
			config.Services[serviceName] = service
		}
	}

	for routeName, route := range mergeConfig.Routes {
		if config.Routes == nil {
			config.Routes = map[string]Route{routeName: route}
		} else {
			config.Routes[routeName] = route
		}
	}
}
<commit_msg>config/config: simplify service.setBackend()<commit_after>package config

import (
	"fmt"
	"strings"
)

/* Config objects */
type Meta struct {
	node Node // origin node
}

func (meta Meta) Source() string {
	if meta.node.Source != nil {
		return meta.node.Source.String()
	} else {
		// some nodes are implicitly created, and thus not directly applicable..
		return ""
	}
}

func (meta Meta) Path() string {
	return meta.node.Path
}

type ServiceFrontend struct {
	Meta `json:"-"`

	IPv4 string `json:"ipv4,omitempty"`
	IPv6 string `json:"ipv6,omitempty"`
	TCP  uint16 `json:"tcp,omitempty"`
	UDP  uint16 `json:"udp,omitempty"`
}

type ServiceBackend struct {
	Meta `json:"-"`

	IPv4 string `json:"ipv4,omitempty"`
	IPv6 string `json:"ipv6,omitempty"`
	TCP  uint16 `json:"tcp,omitempty"`
	UDP  uint16 `json:"udp,omitempty"`

	Weight uint `json:"weight"` // default: 10
}

const ServiceBackendWeight uint = 10

type Service struct {
	Meta `json:"-"`

	Frontend *ServiceFrontend
	Backends map[string]ServiceBackend
}

func (service *Service) setBackend(backendName string, serviceBackend ServiceBackend) {
	if service.Backends == nil {
		service.Backends = map[string]ServiceBackend{backendName: serviceBackend}
	} else {
		service.Backends[backendName] = serviceBackend
	}
}

func (service *Service) merge(other Service) {
	if other.Frontend != nil {
		service.Frontend = other.Frontend
	}

	// backends
	if service.Backends == nil {
		service.Backends = make(map[string]ServiceBackend)
	}

	for backendName, backend := range other.Backends {
		service.Backends[backendName] = backend
	}
}

type Route struct {
	Meta `json:"-"`

	// IPv4/IPv6 prefix to match
	// empty for default match
	Prefix string `json:",omitempty"`

	// Override backend IPv4/IPv6 address for ipvs
	Gateway string `json:",omitempty"`

	// Configure IPVS fwd-method for destination:
	//  droute tunnel masq
	// Filter out backend if set to empty string.
	IPVSMethod string `json:",omitempty"`
}

// Top-level config object
type Config struct {
	Routes   map[string]Route
	Services map[string]Service
}

func (config *Config) setService(serviceName string, service Service) {
	if config.Services == nil {
		config.Services = map[string]Service{serviceName: service}
	} else {
		config.Services[serviceName] = service
	}
}

func (config *Config) updateServices(node Node) error {
	if node.Remove {
		config.Services = nil
	}

	return nil
}

func (config *Config) updateService(node Node, serviceName string) error {
	if node.Remove {
		delete(config.Services, serviceName)
	} else {
		service := config.Services[serviceName]

		config.setService(serviceName, service)
	}

	return nil
}

func (config *Config) updateServiceFrontend(node Node, serviceName string, serviceFrontend ServiceFrontend) error {
	service := config.Services[serviceName]

	if node.Remove {
		service.Frontend = nil
	} else {
		service.Frontend = &serviceFrontend
	}

	config.setService(serviceName, service)

	return nil
}

func (config *Config) updateServiceBackends(node Node, serviceName string) error {
	service := config.Services[serviceName]

	if node.Remove {
		service.Backends = nil
	}

	config.setService(serviceName, service)

	return nil
}

func (config *Config) updateServiceBackend(node Node, serviceName string, backendName string, serviceBackend ServiceBackend) error {
	service := config.Services[serviceName]

	if node.Remove {
		delete(service.Backends, backendName)
	} else {
		service.setBackend(backendName, serviceBackend)
	}

	config.setService(serviceName, service)

	return nil
}

func (config *Config) updateRoutes(node Node) error {
	if node.Remove {
		config.Routes = nil
	}

	return nil
}

func (config *Config) updateRoute(node Node, routeName string, route Route) error {
	if node.Remove {
		// clear
		delete(config.Routes, routeName)

	} else if config.Routes == nil {
		config.Routes = map[string]Route{routeName: route}

	} else {
		config.Routes[routeName] = route
	}

	return nil
}

// Update config from Node
func (config *Config) update(node Node) error {
	nodePath := strings.Split(node.Path, "/")

	if len(node.Path) == 0 {
		// Split("", "/") would give [""]
		nodePath = nil
	}

	// match config tree path
	if len(nodePath) == 0 && node.IsDir {
		// XXX: just ignore? Undefined if it makes sense to do anything here
		return nil

	} else if len(nodePath) == 1 && nodePath[0] == "services" && node.IsDir {
		// recursive on all services
		return config.updateServices(node)

	} else if len(nodePath) >= 2 && nodePath[0] == "services" {
		serviceName := nodePath[1]

		if len(nodePath) == 2 && node.IsDir {
			return config.updateService(node, serviceName)

		} else if len(nodePath) == 3 && nodePath[2] == "frontend" && !node.IsDir {
			var serviceFrontend = ServiceFrontend{
				Meta: Meta{node: node},
			}

			if err := node.unmarshal(&serviceFrontend); err != nil {
				return fmt.Errorf("service %s frontend: %s", serviceName, err)
			}

			return config.updateServiceFrontend(node, serviceName, serviceFrontend)

		} else if len(nodePath) == 3 && nodePath[2] == "backends" && node.IsDir {
			// recursive on all backends
			return config.updateServiceBackends(node, serviceName)

		} else if len(nodePath) >= 4 && nodePath[2] == "backends" {
			backendName := nodePath[3]

			if len(nodePath) == 4 && !node.IsDir {
				var serviceBackend = ServiceBackend{
					Meta:   Meta{node: node},
					Weight: ServiceBackendWeight,
				}

				if err := node.unmarshal(&serviceBackend); err != nil {
					return fmt.Errorf("service %s backend %s: %s", serviceName, backendName, err)
				}

				return config.updateServiceBackend(node, serviceName, backendName, serviceBackend)

			} else {
				return fmt.Errorf("Ignore unknown service %s backends node", serviceName)
			}

		} else {
			return fmt.Errorf("Ignore unknown service %s node", serviceName)
		}

	} else if len(nodePath) == 1 && nodePath[0] == "routes" && node.IsDir {
		return config.updateRoutes(node)

	} else if len(nodePath) >= 2 && nodePath[0] == "routes" {
		routeName := nodePath[1]

		if len(nodePath) == 2 && !node.IsDir {
			var route = Route{
				Meta: Meta{node: node},
			}

			if err := node.unmarshal(&route); err != nil {
				return fmt.Errorf("route %s: %s", routeName, err)
			}

			return config.updateRoute(node, routeName, route)

		} else {
			return fmt.Errorf("Ignore unknown route node")
		}
	} else {
		return fmt.Errorf("Ignore unknown node")
	}
}

func (config Config) walk(visit func(node Node)) (err error) {
	defer func() {
		if panicValue := recover(); panicValue == nil {

		} else if panicError, ok := panicValue.(error); ok {
			err = panicError
		} else {
			panic(panicValue)
		}
	}()

	if config.Routes != nil {
		for routeName, route := range config.Routes {
			visit(makeNode(route, "routes", routeName))
		}
	}

	if config.Services != nil {
		for serviceName, service := range config.Services {
			if service.Frontend != nil {
				visit(makeNode(service.Frontend, "services", serviceName, "frontend"))
			}

			for backendName, backend := range service.Backends {
				visit(makeNode(backend, "services", serviceName, "backends", backendName))
			}
		}
	}

	return
}

func (config Config) compile() (map[string]Node, error) {
	var nodes = map[string]Node{}

	err := config.walk(func(node Node) {
		nodes[node.Path] = node
	})
	return nodes, err
}

func (config *Config) merge(mergeConfig Config) {
	for serviceName, mergeService := range mergeConfig.Services {
		service := config.Services[serviceName]

		service.merge(mergeService)

		if config.Services == nil {
			config.Services = map[string]Service{serviceName: service}
		} else {
			config.Services[serviceName] = service
		}
	}

	for routeName, route := range mergeConfig.Routes {
		if config.Routes == nil {
			config.Routes = map[string]Route{routeName: route}
		} else {
			config.Routes[routeName] = route
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"os"
)

/*Root is the base struct containing bloodlines configs */
type Root struct {
	SQL        MySQL      `json:"mysql"`
	Sendgrid   Sendgrid   `json:"sendgrid"`
	TownCenter TownCenter `json:"towncenter"`
	Bloodlines Bloodlines `json:"bloodlines"`
	Rabbit     Rabbit     `json:"rabbit"`
	Statsd     Statsd     `json:"statsd"`
	Stripe     Stripe     `json:"stripe"`
	Port       string     `json:"port"`
}

/*MySQL contains information for connecting to a MySQL instance */
type MySQL struct {
	Host     string `json:"host"`
	Port     string `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
	Database string `json:"database"`
}

/*Sendgrid has connection information for the sendgrid gateway */
type Sendgrid struct {
	APIKey    string `json:"api_key"`
	FromEmail string `json:"from_email"`
	FromName  string `json:"from_name"`
	Host      string `json:"host"`
}

type Stripe struct {
	Secret string `json:"secret"`
	Public string `json:"public"`
}

/*TownCenter has connection information for the town center service */
type TownCenter struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

/*Bloodlines has connection information for the bloodlines service */
type Bloodlines struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

/*Rabbit has connection info for RabbitMQ*/
type Rabbit struct {
	Host string `json:"host"`
	Port string `json:"port"`
	PubQ string `json:"pubq"`
}

/*Statsd contains connection information for graphite stats*/
type Statsd struct {
	Host   string `json:"host"`
	Port   string `json:"port"`
	Prefix string `json:"prefix"`
}

/*Init returns a populated Root struct from config.json */
func Init(filename string) (*Root, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(file)

	root := &Root{}
	err = decoder.Decode(root)
	if err != nil {
		return nil, err
	}
	return root, nil
}
<commit_msg>Add covenant config<commit_after>package config

import (
	"encoding/json"
	"os"
)

/*Root is the base struct containing bloodlines configs */
type Root struct {
	SQL        MySQL      `json:"mysql"`
	Sendgrid   Sendgrid   `json:"sendgrid"`
	TownCenter TownCenter `json:"towncenter"`
	Bloodlines Bloodlines `json:"bloodlines"`
	Covenant   Covenant   `json:"covenant"`
	Rabbit     Rabbit     `json:"rabbit"`
	Statsd     Statsd     `json:"statsd"`
	Stripe     Stripe     `json:"stripe"`
	Port       string     `json:"port"`
}

/*MySQL contains information for connecting to a MySQL instance */
type MySQL struct {
	Host     string `json:"host"`
	Port     string `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
	Database string `json:"database"`
}

/*Sendgrid has connection information for the sendgrid gateway */
type Sendgrid struct {
	APIKey    string `json:"api_key"`
	FromEmail string `json:"from_email"`
	FromName  string `json:"from_name"`
	Host      string `json:"host"`
}

type Stripe struct {
	Secret string `json:"secret"`
	Public string `json:"public"`
}

/*TownCenter has connection information for the town center service */
type TownCenter struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

/*Bloodlines has connection information for the bloodlines service */
type Bloodlines struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

/*Covenant has connection information for the covenant service */
type Covenant struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

/*Rabbit has connection info for RabbitMQ*/
type Rabbit struct {
	Host string `json:"host"`
	Port string `json:"port"`
	PubQ string `json:"pubq"`
}

/*Statsd contains connection information for graphite stats*/
type Statsd struct {
	Host   string `json:"host"`
	Port   string `json:"port"`
	Prefix string `json:"prefix"`
}

/*Init returns a populated Root struct from config.json */
func Init(filename string) (*Root, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(file)

	root := &Root{}
	err = decoder.Decode(root)
	if err != nil {
		return nil, err
	}
	return root, nil
}
<|endoftext|>


<commit_before>package config

import (
	"crypto"
	"crypto/x509"
	"encoding/base64"
	"errors"
	"os"
	"path/filepath"

	u "github.com/jbenet/go-ipfs/util"
)

// Identity tracks the configuration of the local node's identity.
type Identity struct {
	PeerID  string
	PrivKey string
}

// Datastore tracks the configuration of the datastore.
type Datastore struct {
	Type string
	Path string
}

// Addresses stores the (string) multiaddr addresses for the node.
type Addresses struct {
	Swarm string // address for the swarm network
	API   string // address for the local API (RPC)
}

// BootstrapPeer is a peer used to bootstrap the network.
type BootstrapPeer struct {
	Address string
	PeerID  string // until multiaddr supports ipfs, use another field.
}

// Config is used to load IPFS config files.
type Config struct {
	Identity  Identity         // local node's peer identity
	Datastore Datastore        // local node's storage
	Addresses Addresses        // local node's addresses
	Bootstrap []*BootstrapPeer // local nodes's bootstrap peers
}

const DefaultPathRoot = "~/.go-ipfs"
const DefaultConfigFile = "config"
const DefaultDataStoreDirectory = "datastore"
const EnvDir = "IPFS_DIR"

// PathRoot returns the default configuration root directory
func PathRoot() (string, error) {
	dir := os.Getenv(EnvDir)
	var err error
	if len(dir) == 0 {
		dir, err = u.TildeExpansion(DefaultPathRoot)
	}
	return dir, err
}

// Path returns the path `extension` relative to the configuration root. If an
// empty string is provided for `configroot`, the default root is used.
func Path(configroot, extension string) (string, error) {
	if len(configroot) == 0 {
		dir, err := PathRoot()
		return filepath.Join(dir, extension), err
	} else {
		return filepath.Join(configroot, extension), nil
	}
}

// DataStorePath returns the default data store path given a configuration root
// (set an empty string to have the default configuration root)
func DataStorePath(configroot string) (string, error) {
	return Path(configroot, DefaultDataStoreDirectory);
}

// Filename returns the configuration file path given a configuration root
// directory. If the configuration root directory is empty, use the default one
func Filename(configroot string) (string, error) {
	return Path(configroot, DefaultConfigFile);
}

// DecodePrivateKey is a helper to decode the users PrivateKey
func (i *Identity) DecodePrivateKey(passphrase string) (crypto.PrivateKey, error) {
	pkb, err := base64.StdEncoding.DecodeString(i.PrivKey)
	if err != nil {
		return nil, err
	}

	// currently storing key unencrypted. in the future we need to encrypt it.
	// TODO(security)
	return x509.ParsePKCS1PrivateKey(pkb)
}

// Load reads given file and returns the read config, or error.
func Load(filename string) (*Config, error) {
	// if nothing is there, fail. User must run 'ipfs init'
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return nil, errors.New("ipfs not initialized, please run 'ipfs init'")
	}

	var cfg Config
	err := ReadConfigFile(filename, &cfg)
	if err != nil {
		return nil, err
	}

	// tilde expansion on datastore path
	cfg.Datastore.Path, err = u.TildeExpansion(cfg.Datastore.Path)
	if err != nil {
		return nil, err
	}

	return &cfg, err
}

// Set sets the value of a particular config key
func Set(filename, key, value string) error {
	return WriteConfigKey(filename, key, value)
}
<commit_msg>Fix error handling<commit_after>package config

import (
	"crypto"
	"crypto/x509"
	"encoding/base64"
	"errors"
	"os"
	"path/filepath"

	u "github.com/jbenet/go-ipfs/util"
)

// Identity tracks the configuration of the local node's identity.
type Identity struct {
	PeerID  string
	PrivKey string
}

// Datastore tracks the configuration of the datastore.
type Datastore struct {
	Type string
	Path string
}

// Addresses stores the (string) multiaddr addresses for the node.
type Addresses struct {
	Swarm string // address for the swarm network
	API   string // address for the local API (RPC)
}

// BootstrapPeer is a peer used to bootstrap the network.
type BootstrapPeer struct {
	Address string
	PeerID  string // until multiaddr supports ipfs, use another field.
}

// Config is used to load IPFS config files.
type Config struct {
	Identity  Identity         // local node's peer identity
	Datastore Datastore        // local node's storage
	Addresses Addresses        // local node's addresses
	Bootstrap []*BootstrapPeer // local nodes's bootstrap peers
}

const DefaultPathRoot = "~/.go-ipfs"
const DefaultConfigFile = "config"
const DefaultDataStoreDirectory = "datastore"
const EnvDir = "IPFS_DIR"

// PathRoot returns the default configuration root directory
func PathRoot() (string, error) {
	dir := os.Getenv(EnvDir)
	var err error
	if len(dir) == 0 {
		dir, err = u.TildeExpansion(DefaultPathRoot)
	}
	return dir, err
}

// Path returns the path `extension` relative to the configuration root. If an
// empty string is provided for `configroot`, the default root is used.
func Path(configroot, extension string) (string, error) {
	if len(configroot) == 0 {
		dir, err := PathRoot()
		if err != nil {
			return "", err
		} else {
			return filepath.Join(dir, extension), nil
		}

	} else {
		return filepath.Join(configroot, extension), nil
	}
}

// DataStorePath returns the default data store path given a configuration root
// (set an empty string to have the default configuration root)
func DataStorePath(configroot string) (string, error) {
	return Path(configroot, DefaultDataStoreDirectory);
}

// Filename returns the configuration file path given a configuration root
// directory. If the configuration root directory is empty, use the default one
func Filename(configroot string) (string, error) {
	return Path(configroot, DefaultConfigFile);
}

// DecodePrivateKey is a helper to decode the users PrivateKey
func (i *Identity) DecodePrivateKey(passphrase string) (crypto.PrivateKey, error) {
	pkb, err := base64.StdEncoding.DecodeString(i.PrivKey)
	if err != nil {
		return nil, err
	}

	// currently storing key unencrypted. in the future we need to encrypt it.
	// TODO(security)
	return x509.ParsePKCS1PrivateKey(pkb)
}

// Load reads given file and returns the read config, or error.
func Load(filename string) (*Config, error) {
	// if nothing is there, fail. User must run 'ipfs init'
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return nil, errors.New("ipfs not initialized, please run 'ipfs init'")
	}

	var cfg Config
	err := ReadConfigFile(filename, &cfg)
	if err != nil {
		return nil, err
	}

	// tilde expansion on datastore path
	cfg.Datastore.Path, err = u.TildeExpansion(cfg.Datastore.Path)
	if err != nil {
		return nil, err
	}

	return &cfg, err
}

// Set sets the value of a particular config key
func Set(filename, key, value string) error {
	return WriteConfigKey(filename, key, value)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package awsmodel

import (
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/featureflag"
	"k8s.io/kops/pkg/model"
)

// AWSModelContext provides the context for the aws model
type AWSModelContext struct {
	*model.KopsModelContext
}

// UseLaunchTemplate checks if we need to use a launch template rather than configuration
func UseLaunchTemplate(ig *kops.InstanceGroup) bool {
	if featureflag.EnableLaunchTemplates.Enabled() {
		return true
	}
	// @note: this mixed instance polices was added before the feature flag, to keep the
	// same behviour we also check this. But since the feature hasn't been cut into a tagged
	// release it possible to use just the feature flag??
	if ig.Spec.MixedInstancesPolicy != nil {
		return true
	}

	return false
}
<commit_msg>- cleaning up the UseLaunchTemplate(), we need the check for the LT and mixed policies to be seperate as you can have a mixed instance policy without going full LT<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package awsmodel

import (
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/featureflag"
	"k8s.io/kops/pkg/model"
)

// AWSModelContext provides the context for the aws model
type AWSModelContext struct {
	*model.KopsModelContext
}

// UseMixedInstancePolicies indicates if we are using mixed instance policies
func UseMixedInstancePolicies(ig *kops.InstanceGroup) bool {
	return ig.Spec.MixedInstancesPolicy != nil
}

// UseLaunchTemplate checks if we need to use a launch template rather than configuration
func UseLaunchTemplate(ig *kops.InstanceGroup) bool {
	if featureflag.EnableLaunchTemplates.Enabled() {
		return true
	}

	return UseMixedInstancePolicies(ig)
}
<|endoftext|>


<commit_before>package plugins

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/setting"
)

type GrafanaNetPlugin struct {
	Slug    string `json:"slug"`
	Version string `json:"version"`
}

type GithubLatest struct {
	Stable  string `json:"stable"`
	Testing string `json:"testing"`
}

func StartPluginUpdateChecker() {
	if !setting.CheckForUpdates {
		return
	}

	// do one check directly
	go checkForUpdates()

	ticker := time.NewTicker(time.Minute * 10)
	for {
		select {
		case <-ticker.C:
			checkForUpdates()
		}
	}
}

func getAllExternalPluginSlugs() string {
	str := ""

	for _, plug := range Plugins {
		if plug.IsCorePlugin {
			continue
		}

		str += plug.Id + ","
	}

	return str
}

func checkForUpdates() {
	log.Trace("Checking for updates")

	client := http.Client{Timeout: time.Duration(5 * time.Second)}

	pluginSlugs := getAllExternalPluginSlugs()
	resp, err := client.Get("https://grafana.net/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion)

	if err != nil {
		log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error())
		return
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Trace("Update check failed, reading response from grafana.net, %v", err.Error())
		return
	}

	gNetPlugins := []GrafanaNetPlugin{}
	err = json.Unmarshal(body, &gNetPlugins)
	if err != nil {
		log.Trace("Failed to unmarshal plugin repo, reading response from grafana.net, %v", err.Error())
		return
	}

	for _, plug := range Plugins {
		for _, gplug := range gNetPlugins {
			if gplug.Slug == plug.Id {
				plug.GrafanaNetVersion = gplug.Version
				plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion
			}
		}
	}

	resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json")
	if err != nil {
		log.Trace("Failed to get lates.json repo from github: %v", err.Error())
		return
	}

	defer resp2.Body.Close()
	body, err = ioutil.ReadAll(resp2.Body)
	if err != nil {
		log.Trace("Update check failed, reading response from github.net, %v", err.Error())
		return
	}

	var githubLatest GithubLatest
	err = json.Unmarshal(body, &githubLatest)
	if err != nil {
		log.Trace("Failed to unmarshal github latest, reading response from github: %v", err.Error())
		return
	}

	if strings.Contains(setting.BuildVersion, "-") {
		GrafanaLatestVersion = githubLatest.Testing
		GrafanaHasUpdate = strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
	} else {
		GrafanaLatestVersion = githubLatest.Stable
		GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
	}
}
<commit_msg>fixed update check<commit_after>package plugins

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/setting"
)

type GrafanaNetPlugin struct {
	Slug    string `json:"slug"`
	Version string `json:"version"`
}

type GithubLatest struct {
	Stable  string `json:"stable"`
	Testing string `json:"testing"`
}

func StartPluginUpdateChecker() {
	if !setting.CheckForUpdates {
		return
	}

	// do one check directly
	go checkForUpdates()

	ticker := time.NewTicker(time.Minute * 10)
	for {
		select {
		case <-ticker.C:
			checkForUpdates()
		}
	}
}

func getAllExternalPluginSlugs() string {
	str := ""

	for _, plug := range Plugins {
		if plug.IsCorePlugin {
			continue
		}

		str += plug.Id + ","
	}

	return str
}

func checkForUpdates() {
	log.Trace("Checking for updates")

	client := http.Client{Timeout: time.Duration(5 * time.Second)}

	pluginSlugs := getAllExternalPluginSlugs()
	resp, err := client.Get("https://grafana.net/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion)

	if err != nil {
		log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error())
		return
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Trace("Update check failed, reading response from grafana.net, %v", err.Error())
		return
	}

	gNetPlugins := []GrafanaNetPlugin{}
	err = json.Unmarshal(body, &gNetPlugins)
	if err != nil {
		log.Trace("Failed to unmarshal plugin repo, reading response from grafana.net, %v", err.Error())
		return
	}

	for _, plug := range Plugins {
		for _, gplug := range gNetPlugins {
			if gplug.Slug == plug.Id {
				plug.GrafanaNetVersion = gplug.Version
				plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion
			}
		}
	}

	resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json")
	if err != nil {
		log.Trace("Failed to get lates.json repo from github: %v", err.Error())
		return
	}

	defer resp2.Body.Close()
	body, err = ioutil.ReadAll(resp2.Body)
	if err != nil {
		log.Trace("Update check failed, reading response from github.net, %v", err.Error())
		return
	}

	var githubLatest GithubLatest
	err = json.Unmarshal(body, &githubLatest)
	if err != nil {
		log.Trace("Failed to unmarshal github latest, reading response from github: %v", err.Error())
		return
	}

	if strings.Contains(setting.BuildVersion, "-") {
		GrafanaLatestVersion = githubLatest.Testing
		GrafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
	} else {
		GrafanaLatestVersion = githubLatest.Stable
		GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
	"fmt"

	"k8s.io/api/core/v1"
	policyv1 "k8s.io/api/policy/v1beta1"
	"k8s.io/apimachinery/pkg/types"

	arbcorev1 "github.com/kubernetes-incubator/kube-arbitrator/pkg/apis/scheduling/v1alpha1"
	"github.com/kubernetes-incubator/kube-arbitrator/pkg/apis/utils"
)

type TaskID types.UID

type TaskInfo struct {
	UID TaskID
	Job JobID

	Name      string
	Namespace string

	Resreq *Resource

	NodeName string
	Status   TaskStatus
	Priority int32

	Pod *v1.Pod
}

func getJobID(pod *v1.Pod) JobID {
	if len(pod.Annotations) != 0 {
		if gn, found := pod.Annotations[arbcorev1.GroupNameAnnotationKey]; found && len(gn) != 0 {
			// Make sure Pod and PodGroup belong to the same namespace.
			jobID := fmt.Sprintf("%s/%s", pod.Namespace, gn)
			return JobID(jobID)
		}
	}
	return JobID(utils.GetController(pod))
}

func NewTaskInfo(pod *v1.Pod) *TaskInfo {
	req := EmptyResource()

	// TODO(k82cn): also includes initContainers' resource.
	for _, c := range pod.Spec.Containers {
		req.Add(NewResource(c.Resources.Requests))
	}

	ti := &TaskInfo{
		UID:       TaskID(pod.UID),
		Job:       getJobID(pod),
		Name:      pod.Name,
		Namespace: pod.Namespace,
		NodeName:  pod.Spec.NodeName,
		Status:    getTaskStatus(pod),
		Priority:  1,

		Pod:    pod,
		Resreq: req,
	}

	if pod.Spec.Priority != nil {
		ti.Priority = *pod.Spec.Priority
	}

	return ti
}

func (ti *TaskInfo) Clone() *TaskInfo {
	return &TaskInfo{
		UID:       ti.UID,
		Job:       ti.Job,
		Name:      ti.Name,
		Namespace: ti.Namespace,
		NodeName:  ti.NodeName,
		Status:    ti.Status,
		Priority:  ti.Priority,
		Pod:       ti.Pod,
		Resreq:    ti.Resreq.Clone(),
	}
}

func (ti TaskInfo) String() string {
	return fmt.Sprintf("Task (%v:%v/%v): job %v, status %v, pri %v, resreq %v",
		ti.UID, ti.Namespace, ti.Name, ti.Job, ti.Status, ti.Priority, ti.Resreq)
}

// JobID is the type of JobInfo's ID.
type JobID types.UID

type tasksMap map[TaskID]*TaskInfo

type JobInfo struct {
	UID JobID

	Name      string
	Namespace string

	Queue QueueID

	Priority int

	NodeSelector map[string]string
	MinAvailable int32

	// All tasks of the Job.
	TaskStatusIndex map[TaskStatus]tasksMap
	Tasks           tasksMap

	Allocated    *Resource
	TotalRequest *Resource

	PodGroup *arbcorev1.PodGroup

	// TODO(k82cn): keep backward compatbility, removed it when v1alpha1 finalized.
	PDB *policyv1.PodDisruptionBudget
}

func NewJobInfo(uid JobID) *JobInfo {
	return &JobInfo{
		UID: uid,

		MinAvailable: 0,
		NodeSelector: make(map[string]string),

		Allocated:    EmptyResource(),
		TotalRequest: EmptyResource(),

		TaskStatusIndex: map[TaskStatus]tasksMap{},
		Tasks:           tasksMap{},
	}
}

func (ji *JobInfo) UnsetPodGroup() {
	ji.PodGroup = nil
}

func (ji *JobInfo) SetPodGroup(pg *arbcorev1.PodGroup) {
	ji.Name = pg.Name
	ji.Namespace = pg.Namespace
	ji.MinAvailable = pg.Spec.NumMember

	if len(pg.Spec.Queue) == 0 {
		ji.Queue = QueueID(pg.Namespace)
	} else {
		ji.Queue = QueueID(pg.Spec.Queue)
	}

	ji.PodGroup = pg
}

func (ji *JobInfo) SetPDB(pbd *policyv1.PodDisruptionBudget) {
	ji.Name = pbd.Name
	ji.MinAvailable = pbd.Spec.MinAvailable.IntVal

	ji.PDB = pbd
}

func (ji *JobInfo) UnsetPDB() {
	ji.PDB = nil
}

func (ji *JobInfo) GetTasks(statuses ...TaskStatus) []*TaskInfo {
	var res []*TaskInfo

	for _, status := range statuses {
		if tasks, found := ji.TaskStatusIndex[status]; found {
			for _, task := range tasks {
				res = append(res, task.Clone())
			}
		}
	}

	return res
}

func (ji *JobInfo) addTaskIndex(ti *TaskInfo) {
	if _, found := ji.TaskStatusIndex[ti.Status]; !found {
		ji.TaskStatusIndex[ti.Status] = tasksMap{}
	}

	ji.TaskStatusIndex[ti.Status][ti.UID] = ti
}

func (ji *JobInfo) AddTaskInfo(ti *TaskInfo) {
	ji.Tasks[ti.UID] = ti
	ji.addTaskIndex(ti)

	ji.TotalRequest.Add(ti.Resreq)

	if AllocatedStatus(ti.Status) {
		ji.Allocated.Add(ti.Resreq)
	}
}

func (ji *JobInfo) UpdateTaskStatus(task *TaskInfo, status TaskStatus) error {
	if err := validateStatusUpdate(task.Status, status); err != nil {
		return err
	}

	// Remove the task from the task list firstly
	ji.DeleteTaskInfo(task)

	// Update task's status to the target status
	task.Status = status
	ji.AddTaskInfo(task)

	return nil
}

func (ji *JobInfo) deleteTaskIndex(ti *TaskInfo) {
	if tasks, found := ji.TaskStatusIndex[ti.Status]; found {
		delete(tasks, ti.UID)

		if len(tasks) == 0 {
			delete(ji.TaskStatusIndex, ti.Status)
		}
	}
}

func (ji *JobInfo) DeleteTaskInfo(ti *TaskInfo) error {
	if task, found := ji.Tasks[ti.UID]; found {
		ji.TotalRequest.Sub(task.Resreq)

		if AllocatedStatus(task.Status) {
			ji.Allocated.Sub(task.Resreq)
		}

		delete(ji.Tasks, task.UID)

		ji.deleteTaskIndex(task)
		return nil
	}

	return fmt.Errorf("failed to find task <%v/%v> in job <%v/%v>",
		ti.Namespace, ti.Name, ji.Namespace, ji.Name)
}

func (ji *JobInfo) Clone() *JobInfo {
	info := &JobInfo{
		UID:       ji.UID,
		Name:      ji.Name,
		Namespace: ji.Namespace,
		Queue:     ji.Queue,

		MinAvailable: ji.MinAvailable,
		NodeSelector: map[string]string{},
		Allocated:    ji.Allocated.Clone(),
		TotalRequest: ji.TotalRequest.Clone(),

		PDB:      ji.PDB,
		PodGroup: ji.PodGroup,

		TaskStatusIndex: map[TaskStatus]tasksMap{},
		Tasks:           tasksMap{},
	}

	for k, v := range ji.NodeSelector {
		info.NodeSelector[k] = v
	}

	for _, task := range ji.Tasks {
		info.AddTaskInfo(task.Clone())
	}

	return info
}

func (ji JobInfo) String() string {
	res := ""

	i := 0
	for _, task := range ji.Tasks {
		res = res + fmt.Sprintf("\n\t %d: %v", i, task)
		i++
	}

	return fmt.Sprintf("Job (%v): name %v, minAvailable %d", ji.UID, ji.Name, ji.MinAvailable) + res
}
<commit_msg>apply enabled namespace as Queue to PDB<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
	"fmt"

	"k8s.io/api/core/v1"
	policyv1 "k8s.io/api/policy/v1beta1"
	"k8s.io/apimachinery/pkg/types"

	arbcorev1 "github.com/kubernetes-incubator/kube-arbitrator/pkg/apis/scheduling/v1alpha1"
	"github.com/kubernetes-incubator/kube-arbitrator/pkg/apis/utils"
)

type TaskID types.UID

type TaskInfo struct {
	UID TaskID
	Job JobID

	Name      string
	Namespace string

	Resreq *Resource

	NodeName string
	Status   TaskStatus
	Priority int32

	Pod *v1.Pod
}

func getJobID(pod *v1.Pod) JobID {
	if len(pod.Annotations) != 0 {
		if gn, found := pod.Annotations[arbcorev1.GroupNameAnnotationKey]; found && len(gn) != 0 {
			// Make sure Pod and PodGroup belong to the same namespace.
			jobID := fmt.Sprintf("%s/%s", pod.Namespace, gn)
			return JobID(jobID)
		}
	}
	return JobID(utils.GetController(pod))
}

func NewTaskInfo(pod *v1.Pod) *TaskInfo {
	req := EmptyResource()

	// TODO(k82cn): also includes initContainers' resource.
	for _, c := range pod.Spec.Containers {
		req.Add(NewResource(c.Resources.Requests))
	}

	ti := &TaskInfo{
		UID:       TaskID(pod.UID),
		Job:       getJobID(pod),
		Name:      pod.Name,
		Namespace: pod.Namespace,
		NodeName:  pod.Spec.NodeName,
		Status:    getTaskStatus(pod),
		Priority:  1,

		Pod:    pod,
		Resreq: req,
	}

	if pod.Spec.Priority != nil {
		ti.Priority = *pod.Spec.Priority
	}

	return ti
}

func (ti *TaskInfo) Clone() *TaskInfo {
	return &TaskInfo{
		UID:       ti.UID,
		Job:       ti.Job,
		Name:      ti.Name,
		Namespace: ti.Namespace,
		NodeName:  ti.NodeName,
		Status:    ti.Status,
		Priority:  ti.Priority,
		Pod:       ti.Pod,
		Resreq:    ti.Resreq.Clone(),
	}
}

func (ti TaskInfo) String() string {
	return fmt.Sprintf("Task (%v:%v/%v): job %v, status %v, pri %v, resreq %v",
		ti.UID, ti.Namespace, ti.Name, ti.Job, ti.Status, ti.Priority, ti.Resreq)
}

// JobID is the type of JobInfo's ID.
type JobID types.UID

type tasksMap map[TaskID]*TaskInfo

type JobInfo struct {
	UID JobID

	Name      string
	Namespace string

	Queue QueueID

	Priority int

	NodeSelector map[string]string
	MinAvailable int32

	// All tasks of the Job.
	TaskStatusIndex map[TaskStatus]tasksMap
	Tasks           tasksMap

	Allocated    *Resource
	TotalRequest *Resource

	PodGroup *arbcorev1.PodGroup

	// TODO(k82cn): keep backward compatbility, removed it when v1alpha1 finalized.
	PDB *policyv1.PodDisruptionBudget
}

func NewJobInfo(uid JobID) *JobInfo {
	return &JobInfo{
		UID: uid,

		MinAvailable: 0,
		NodeSelector: make(map[string]string),

		Allocated:    EmptyResource(),
		TotalRequest: EmptyResource(),

		TaskStatusIndex: map[TaskStatus]tasksMap{},
		Tasks:           tasksMap{},
	}
}

func (ji *JobInfo) UnsetPodGroup() {
	ji.PodGroup = nil
}

func (ji *JobInfo) SetPodGroup(pg *arbcorev1.PodGroup) {
	ji.Name = pg.Name
	ji.Namespace = pg.Namespace
	ji.MinAvailable = pg.Spec.NumMember

	if len(pg.Spec.Queue) == 0 {
		ji.Queue = QueueID(pg.Namespace)
	} else {
		ji.Queue = QueueID(pg.Spec.Queue)
	}

	ji.PodGroup = pg
}

func (ji *JobInfo) SetPDB(pdb *policyv1.PodDisruptionBudget) {
	ji.Name = pdb.Name
	ji.MinAvailable = pdb.Spec.MinAvailable.IntVal
	ji.Namespace = pdb.Namespace
	ji.Queue = QueueID(pdb.Namespace)

	ji.PDB = pbd
}

func (ji *JobInfo) UnsetPDB() {
	ji.PDB = nil
}

func (ji *JobInfo) GetTasks(statuses ...TaskStatus) []*TaskInfo {
	var res []*TaskInfo

	for _, status := range statuses {
		if tasks, found := ji.TaskStatusIndex[status]; found {
			for _, task := range tasks {
				res = append(res, task.Clone())
			}
		}
	}

	return res
}

func (ji *JobInfo) addTaskIndex(ti *TaskInfo) {
	if _, found := ji.TaskStatusIndex[ti.Status]; !found {
		ji.TaskStatusIndex[ti.Status] = tasksMap{}
	}

	ji.TaskStatusIndex[ti.Status][ti.UID] = ti
}

func (ji *JobInfo) AddTaskInfo(ti *TaskInfo) {
	ji.Tasks[ti.UID] = ti
	ji.addTaskIndex(ti)

	ji.TotalRequest.Add(ti.Resreq)

	if AllocatedStatus(ti.Status) {
		ji.Allocated.Add(ti.Resreq)
	}
}

func (ji *JobInfo) UpdateTaskStatus(task *TaskInfo, status TaskStatus) error {
	if err := validateStatusUpdate(task.Status, status); err != nil {
		return err
	}

	// Remove the task from the task list firstly
	ji.DeleteTaskInfo(task)

	// Update task's status to the target status
	task.Status = status
	ji.AddTaskInfo(task)

	return nil
}

func (ji *JobInfo) deleteTaskIndex(ti *TaskInfo) {
	if tasks, found := ji.TaskStatusIndex[ti.Status]; found {
		delete(tasks, ti.UID)

		if len(tasks) == 0 {
			delete(ji.TaskStatusIndex, ti.Status)
		}
	}
}

func (ji *JobInfo) DeleteTaskInfo(ti *TaskInfo) error {
	if task, found := ji.Tasks[ti.UID]; found {
		ji.TotalRequest.Sub(task.Resreq)

		if AllocatedStatus(task.Status) {
			ji.Allocated.Sub(task.Resreq)
		}

		delete(ji.Tasks, task.UID)

		ji.deleteTaskIndex(task)
		return nil
	}

	return fmt.Errorf("failed to find task <%v/%v> in job <%v/%v>",
		ti.Namespace, ti.Name, ji.Namespace, ji.Name)
}

func (ji *JobInfo) Clone() *JobInfo {
	info := &JobInfo{
		UID:       ji.UID,
		Name:      ji.Name,
		Namespace: ji.Namespace,
		Queue:     ji.Queue,

		MinAvailable: ji.MinAvailable,
		NodeSelector: map[string]string{},
		Allocated:    ji.Allocated.Clone(),
		TotalRequest: ji.TotalRequest.Clone(),

		PDB:      ji.PDB,
		PodGroup: ji.PodGroup,

		TaskStatusIndex: map[TaskStatus]tasksMap{},
		Tasks:           tasksMap{},
	}

	for k, v := range ji.NodeSelector {
		info.NodeSelector[k] = v
	}

	for _, task := range ji.Tasks {
		info.AddTaskInfo(task.Clone())
	}

	return info
}

func (ji JobInfo) String() string {
	res := ""

	i := 0
	for _, task := range ji.Tasks {
		res = res + fmt.Sprintf("\n\t %d: %v", i, task)
		i++
	}

	return fmt.Sprintf("Job (%v): name %v, minAvailable %d", ji.UID, ji.Name, ji.MinAvailable) + res
}
<|endoftext|>


<commit_before>package graphite

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/tsdb"
)

var (
	HttpClient = http.Client{Timeout: time.Duration(10 * time.Second)}
)

type GraphiteExecutor struct {
	*tsdb.DataSourceInfo
}

func NewGraphiteExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor {
	return &GraphiteExecutor{dsInfo}
}

var glog log.Logger

func init() {
	glog = log.New("tsdb.graphite")
	tsdb.RegisterExecutor("graphite", NewGraphiteExecutor)
}

func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
	result := &tsdb.BatchResult{}

	params := url.Values{
		"from":          []string{"-" + formatTimeRange(context.TimeRange.From)},
		"until":         []string{formatTimeRange(context.TimeRange.To)},
		"format":        []string{"json"},
		"maxDataPoints": []string{"500"},
	}

	for _, query := range queries {
		params["target"] = []string{query.Query}
		glog.Debug("Graphite request", "query", query.Query)
	}

	formData := params.Encode()
	glog.Info("Graphite request body", "formdata", formData)
	req, err := http.NewRequest(http.MethodPost, e.Url+"/render", strings.NewReader(formData))
	if err != nil {
		glog.Info("Failed to create request", "error", err)
		result.Error = fmt.Errorf("Failed to create request. error: %v", err)
		return result
	}

	if e.BasicAuth {
		req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
	}

	res, err := HttpClient.Do(req)
	if err != nil {
		result.Error = err
		return result
	}

	body, err := ioutil.ReadAll(res.Body)
	defer res.Body.Close()
	if err != nil {
		result.Error = err
		return result
	}

	if res.StatusCode == http.StatusUnauthorized {
		glog.Info("Request is Unauthorized", "status", res.Status, "body", string(body))
		result.Error = fmt.Errorf("Request is Unauthorized status: %v body: %s", res.Status, string(body))
		return result
	}

	var data []TargetResponseDTO
	err = json.Unmarshal(body, &data)
	if err != nil {
		glog.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body))
		result.Error = err
		return result
	}

	result.QueryResults = make(map[string]*tsdb.QueryResult)
	queryRes := &tsdb.QueryResult{}
	for _, series := range data {
		queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{
			Name:   series.Target,
			Points: series.DataPoints,
		})
	}

	result.QueryResults["A"] = queryRes
	return result
}

func formatTimeRange(input string) string {
	if input == "now" {
		return input
	}
	return strings.Replace(strings.Replace(input, "m", "min", -1), "M", "mon", -1)
}
<commit_msg>feat(tsdb): add missing content type and join url using path<commit_after>package graphite

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/tsdb"
)

var (
	HttpClient = http.Client{Timeout: time.Duration(10 * time.Second)}
)

type GraphiteExecutor struct {
	*tsdb.DataSourceInfo
}

func NewGraphiteExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor {
	return &GraphiteExecutor{dsInfo}
}

var glog log.Logger

func init() {
	glog = log.New("tsdb.graphite")
	tsdb.RegisterExecutor("graphite", NewGraphiteExecutor)
}

func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult {
	result := &tsdb.BatchResult{}

	params := url.Values{
		"from":          []string{"-" + formatTimeRange(context.TimeRange.From)},
		"until":         []string{formatTimeRange(context.TimeRange.To)},
		"format":        []string{"json"},
		"maxDataPoints": []string{"500"},
	}

	for _, query := range queries {
		params["target"] = []string{query.Query}
	}

	u, _ := url.Parse(e.Url)
	u.Path = path.Join(u.Path, "render")
	glog.Info("Graphite request body", "formdata", params.Encode())

	req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(params.Encode()))
	if err != nil {
		glog.Info("Failed to create request", "error", err)
		result.Error = fmt.Errorf("Failed to create request. error: %v", err)
		return result
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if e.BasicAuth {
		req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword)
	}

	res, err := HttpClient.Do(req)
	if err != nil {
		result.Error = err
		return result
	}

	body, err := ioutil.ReadAll(res.Body)
	defer res.Body.Close()
	if err != nil {
		result.Error = err
		return result
	}

	if res.StatusCode == http.StatusUnauthorized {
		glog.Info("Request is Unauthorized", "status", res.Status, "body", string(body))
		result.Error = fmt.Errorf("Request is Unauthorized status: %v body: %s", res.Status, string(body))
		return result
	}

	var data []TargetResponseDTO
	err = json.Unmarshal(body, &data)
	if err != nil {
		glog.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body))
		result.Error = err
		return result
	}

	result.QueryResults = make(map[string]*tsdb.QueryResult)
	queryRes := &tsdb.QueryResult{}
	for _, series := range data {
		queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{
			Name:   series.Target,
			Points: series.DataPoints,
		})
	}

	result.QueryResults["A"] = queryRes
	return result
}

func formatTimeRange(input string) string {
	if input == "now" {
		return input
	}
	return strings.Replace(strings.Replace(input, "m", "min", -1), "M", "mon", -1)
}
<|endoftext|>


<commit_before>package influxdb

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"path"
	"strings"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/tsdb"
	"github.com/grafana/grafana/pkg/tsdb/influxdb/flux"
	"golang.org/x/net/context/ctxhttp"
)

type InfluxDBExecutor struct {
	//*models.DataSource
	QueryParser    *InfluxdbQueryParser
	ResponseParser *ResponseParser
	//HttpClient     *http.Client
}

func NewInfluxDBExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
	return &InfluxDBExecutor{
		QueryParser:    &InfluxdbQueryParser{},
		ResponseParser: &ResponseParser{},
	}, nil
}

var (
	glog log.Logger
)

var ErrInvalidHttpMode error = errors.New("'httpMode' should be either 'GET' or 'POST'")

func init() {
	glog = log.New("tsdb.influxdb")
	tsdb.RegisterTsdbQueryEndpoint("influxdb", NewInfluxDBExecutor)
}

func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
	glog.Info("query", "q", tsdbQuery.Queries)

	version := dsInfo.JsonData.Get("version").MustString("")
	if version == "Flux" {
		return flux.Query(ctx, dsInfo, tsdbQuery)
	}

	// NOTE: the following path is currently only called from alerting queries
	// In dashboards, the request runs through proxy and are managed in the frontend

	query, err := e.getQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
	if err != nil {
		return nil, err
	}

	rawQuery, err := query.Build(tsdbQuery)
	if err != nil {
		return nil, err
	}

	if setting.Env == setting.DEV {
		glog.Debug("Influxdb query", "raw query", rawQuery)
	}

	req, err := e.createRequest(dsInfo, rawQuery)
	if err != nil {
		return nil, err
	}

	httpClient, err := dsInfo.GetHttpClient()
	if err != nil {
		return nil, err
	}

	resp, err := ctxhttp.Do(ctx, httpClient, req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	if resp.StatusCode/100 != 2 {
		return nil, fmt.Errorf("Influxdb returned statuscode invalid status code: %v", resp.Status)
	}

	var response Response
	dec := json.NewDecoder(resp.Body)
	dec.UseNumber()
	err = dec.Decode(&response)

	if err != nil {
		return nil, err
	}

	if response.Err != nil {
		return nil, response.Err
	}

	result := &tsdb.Response{}
	result.Results = make(map[string]*tsdb.QueryResult)
	result.Results["A"] = e.ResponseParser.Parse(&response, query)

	return result, nil
}

func (e *InfluxDBExecutor) getQuery(dsInfo *models.DataSource, queries []*tsdb.Query, context *tsdb.TsdbQuery) (*Query, error) {
	// The model supports multiple queries, but right now this is only used from
	// alerting so we only needed to support batch executing 1 query at a time.
	if len(queries) > 0 {
		query, err := e.QueryParser.Parse(queries[0].Model, dsInfo)
		if err != nil {
			return nil, err
		}
		return query, nil
	}
	return nil, fmt.Errorf("query request contains no queries")
}

func (e *InfluxDBExecutor) createRequest(dsInfo *models.DataSource, query string) (*http.Request, error) {
	u, err := url.Parse(dsInfo.Url)
	if err != nil {
		return nil, err
	}
	u.Path = path.Join(u.Path, "query")
	httpMode := dsInfo.JsonData.Get("httpMode").MustString("GET")

	req, err := func() (*http.Request, error) {
		switch httpMode {
		case "GET":
			return http.NewRequest(http.MethodGet, u.String(), nil)
		case "POST":
			bodyValues := url.Values{}
			bodyValues.Add("q", query)
			body := bodyValues.Encode()
			return http.NewRequest(http.MethodPost, u.String(), strings.NewReader(body))
		default:
			return nil, ErrInvalidHttpMode
		}
	}()

	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", "Grafana")

	params := req.URL.Query()
	params.Set("db", dsInfo.Database)
	params.Set("epoch", "s")

	if httpMode == "GET" {
		params.Set("q", query)
	} else if httpMode == "POST" {
		req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	}

	req.URL.RawQuery = params.Encode()

	if dsInfo.BasicAuth {
		req.SetBasicAuth(dsInfo.BasicAuthUser, dsInfo.DecryptedBasicAuthPassword())
	}

	if !dsInfo.BasicAuth && dsInfo.User != "" {
		req.SetBasicAuth(dsInfo.User, dsInfo.DecryptedPassword())
	}

	glog.Debug("Influxdb request", "url", req.URL.String())
	return req, nil
}
<commit_msg>InfluxDB: Less chatty logging (#26850)<commit_after>package influxdb

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"path"
	"strings"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/tsdb"
	"github.com/grafana/grafana/pkg/tsdb/influxdb/flux"
	"golang.org/x/net/context/ctxhttp"
)

type InfluxDBExecutor struct {
	//*models.DataSource
	QueryParser    *InfluxdbQueryParser
	ResponseParser *ResponseParser
	//HttpClient     *http.Client
}

func NewInfluxDBExecutor(datasource *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
	return &InfluxDBExecutor{
		QueryParser:    &InfluxdbQueryParser{},
		ResponseParser: &ResponseParser{},
	}, nil
}

var (
	glog log.Logger
)

var ErrInvalidHttpMode error = errors.New("'httpMode' should be either 'GET' or 'POST'")

func init() {
	glog = log.New("tsdb.influxdb")
	tsdb.RegisterTsdbQueryEndpoint("influxdb", NewInfluxDBExecutor)
}

func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
	glog.Debug("Received a query request", "numQueries", len(tsdbQuery.Queries))

	version := dsInfo.JsonData.Get("version").MustString("")
	if version == "Flux" {
		return flux.Query(ctx, dsInfo, tsdbQuery)
	}

	// NOTE: the following path is currently only called from alerting queries
	// In dashboards, the request runs through proxy and are managed in the frontend

	query, err := e.getQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
	if err != nil {
		return nil, err
	}

	rawQuery, err := query.Build(tsdbQuery)
	if err != nil {
		return nil, err
	}

	if setting.Env == setting.DEV {
		glog.Debug("Influxdb query", "raw query", rawQuery)
	}

	req, err := e.createRequest(dsInfo, rawQuery)
	if err != nil {
		return nil, err
	}

	httpClient, err := dsInfo.GetHttpClient()
	if err != nil {
		return nil, err
	}

	resp, err := ctxhttp.Do(ctx, httpClient, req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	if resp.StatusCode/100 != 2 {
		return nil, fmt.Errorf("Influxdb returned statuscode invalid status code: %v", resp.Status)
	}

	var response Response
	dec := json.NewDecoder(resp.Body)
	dec.UseNumber()
	err = dec.Decode(&response)

	if err != nil {
		return nil, err
	}

	if response.Err != nil {
		return nil, response.Err
	}

	result := &tsdb.Response{}
	result.Results = make(map[string]*tsdb.QueryResult)
	result.Results["A"] = e.ResponseParser.Parse(&response, query)

	return result, nil
}

func (e *InfluxDBExecutor) getQuery(dsInfo *models.DataSource, queries []*tsdb.Query, context *tsdb.TsdbQuery) (*Query, error) {
	// The model supports multiple queries, but right now this is only used from
	// alerting so we only needed to support batch executing 1 query at a time.
	if len(queries) > 0 {
		query, err := e.QueryParser.Parse(queries[0].Model, dsInfo)
		if err != nil {
			return nil, err
		}
		return query, nil
	}
	return nil, fmt.Errorf("query request contains no queries")
}

func (e *InfluxDBExecutor) createRequest(dsInfo *models.DataSource, query string) (*http.Request, error) {
	u, err := url.Parse(dsInfo.Url)
	if err != nil {
		return nil, err
	}
	u.Path = path.Join(u.Path, "query")
	httpMode := dsInfo.JsonData.Get("httpMode").MustString("GET")

	req, err := func() (*http.Request, error) {
		switch httpMode {
		case "GET":
			return http.NewRequest(http.MethodGet, u.String(), nil)
		case "POST":
			bodyValues := url.Values{}
			bodyValues.Add("q", query)
			body := bodyValues.Encode()
			return http.NewRequest(http.MethodPost, u.String(), strings.NewReader(body))
		default:
			return nil, ErrInvalidHttpMode
		}
	}()

	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", "Grafana")

	params := req.URL.Query()
	params.Set("db", dsInfo.Database)
	params.Set("epoch", "s")

	if httpMode == "GET" {
		params.Set("q", query)
	} else if httpMode == "POST" {
		req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	}

	req.URL.RawQuery = params.Encode()

	if dsInfo.BasicAuth {
		req.SetBasicAuth(dsInfo.BasicAuthUser, dsInfo.DecryptedBasicAuthPassword())
	}

	if !dsInfo.BasicAuth && dsInfo.User != "" {
		req.SetBasicAuth(dsInfo.User, dsInfo.DecryptedPassword())
	}

	glog.Debug("Influxdb request", "url", req.URL.String())
	return req, nil
}
<|endoftext|>


<commit_before>package consensus

import (
	"errors"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
)

// TODO: when testing the covered fields stuff, antagonistically try to cause
// seg faults by throwing covered fields objects at the state which point to
// nonexistent objects in the transaction.

var (
	MissingSignaturesErr = errors.New("transaction has inputs with missing signatures")
)

// Each input has a list of public keys and a required number of signatures.
// InputSignatures keeps track of which public keys have been used and how many
// more signatures are needed.
type InputSignatures struct {
	RemainingSignatures uint64
	PossibleKeys        []SiaPublicKey
	UsedKeys            map[uint64]struct{}
	Index               int
}

// sortedUnique checks that all of the elements in a []unit64 are sorted and
// without repeates, and also checks that the largest element is less than or
// equal to the biggest allowed element.
func sortedUnique(elems []uint64, biggestAllowed int) (err error) {
	if len(elems) == 0 {
		return
	}

	biggest := elems[0]
	for _, elem := range elems[1:] {
		if elem <= biggest {
			err = errors.New("covered fields sorting violation")
			return
		}
	}
	if int(biggest) > biggestAllowed {
		err = errors.New("covered fields indexing violation")
		return
	}
	return
}

// validCoveredFields makes sure that all covered fields objects in the
// signatures follow the rules. This means that if `WholeTransaction` is set to
// true, all fields except for `Signatures` must be empty. All fields must be
// sorted numerically, and there can be no repeats.
func (t Transaction) validCoveredFields() (err error) {
	for _, sig := range t.Signatures {
		// Check that all fields are empty if `WholeTransaction` is set.
		cf := sig.CoveredFields
		if cf.WholeTransaction {
			if len(cf.SiacoinInputs) != 0 ||
				len(cf.MinerFees) != 0 ||
				len(cf.SiacoinOutputs) != 0 ||
				len(cf.FileContracts) != 0 ||
				len(cf.StorageProofs) != 0 ||
				len(cf.SiafundInputs) != 0 ||
				len(cf.SiafundOutputs) != 0 ||
				len(cf.ArbitraryData) != 0 {
				err = errors.New("whole transaction is set but not all fields besides signatures are empty")
				return
			}
		}

		// Check that all fields are sorted, and without repeat values, and
		// that all elements point to objects that exists within the
		// transaction.
		err = sortedUnique(cf.SiacoinInputs, len(cf.SiacoinInputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.MinerFees, len(cf.MinerFees)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.SiacoinOutputs, len(cf.SiacoinOutputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.FileContracts, len(cf.FileContracts)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.StorageProofs, len(cf.StorageProofs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.SiafundInputs, len(cf.SiafundInputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.SiafundOutputs, len(cf.SiafundOutputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.ArbitraryData, len(cf.ArbitraryData)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.Signatures, len(cf.Signatures)-1)
		if err != nil {
			return
		}
	}

	return
}

// validSignatures takes a transaction and returns an error if the signatures
// are not all valid.
func (s *State) validSignatures(t Transaction) (err error) {
	// Check that all covered fields objects follow the rules.
	err = t.validCoveredFields()
	if err != nil {
		return
	}

	// Create the InputSignatures object for each input.
	sigMap := make(map[OutputID]*InputSignatures)
	for i, input := range t.SiacoinInputs {
		_, exists := sigMap[input.OutputID]
		if exists {
			return errors.New("output spent twice in the same transaction.")
		}

		inSig := &InputSignatures{
			RemainingSignatures: input.SpendConditions.NumSignatures,
			PossibleKeys:        input.SpendConditions.PublicKeys,
			Index:               i,
		}
		sigMap[input.OutputID] = inSig
	}

	// Check all of the signatures for validity.
	for i, sig := range t.Signatures {
		// Check that each signature signs a unique pubkey where
		// RemainingSignatures > 0.
		if sigMap[sig.InputID].RemainingSignatures == 0 {
			return errors.New("frivolous signature in transaction")
		}
		_, exists := sigMap[sig.InputID].UsedKeys[sig.PublicKeyIndex]
		if exists {
			return errors.New("one public key was used twice while signing an input")
		}

		// Check that the timelock has expired.
		if sig.TimeLock > s.height() {
			return errors.New("signature used before timelock expiration")
		}

		// Check that the signature verifies. Sia is built to support multiple
		// types of signature algorithms, this is handled by the switch
		// statement.
		publicKey := sigMap[sig.InputID].PossibleKeys[sig.PublicKeyIndex]
		switch publicKey.Algorithm {
		case SignatureEntropy:
			return crypto.ErrInvalidSignature
		case SignatureEd25519:
			// Decode the public key and signature.
			var decodedPK crypto.PublicKey
			err := encoding.Unmarshal(publicKey.Key, &decodedPK)
			if err != nil {
				return err
			}
			var decodedSig crypto.Signature
			err = encoding.Unmarshal(sig.Signature, &decodedSig)
			if err != nil {
				return err
			}

			sigHash := t.SigHash(i)
			err = crypto.VerifyHash(sigHash, decodedPK, decodedSig)
			if err != nil {
				return err
			}
		default:
			// If we don't recognize the identifier, assume that the signature
			// is valid; do nothing. This allows more signature types to be
			// added through soft forking.
		}

		// Subtract the number of signatures remaining for this input.
		sigMap[sig.InputID].RemainingSignatures -= 1
	}

	// Check that all inputs have been sufficiently signed.
	for _, reqSigs := range sigMap {
		if reqSigs.RemainingSignatures != 0 {
			return MissingSignaturesErr
		}
	}

	return nil
}

// ValidSignatures takes a transaction and determines whether the transaction
// contains a legal set of signatures, including checking the timelocks against
// the current state height.
func (s *State) ValidSignatures(t Transaction) error {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.validSignatures(t)
}
<commit_msg>add signature verification for siafunds<commit_after>package consensus

import (
	"errors"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
)

// TODO: when testing the covered fields stuff, antagonistically try to cause
// seg faults by throwing covered fields objects at the state which point to
// nonexistent objects in the transaction.

var (
	MissingSignaturesErr = errors.New("transaction has inputs with missing signatures")
)

// Each input has a list of public keys and a required number of signatures.
// InputSignatures keeps track of which public keys have been used and how many
// more signatures are needed.
type InputSignatures struct {
	RemainingSignatures uint64
	PossibleKeys        []SiaPublicKey
	UsedKeys            map[uint64]struct{}
	Index               int
}

// sortedUnique checks that all of the elements in a []unit64 are sorted and
// without repeates, and also checks that the largest element is less than or
// equal to the biggest allowed element.
func sortedUnique(elems []uint64, biggestAllowed int) (err error) {
	if len(elems) == 0 {
		return
	}

	biggest := elems[0]
	for _, elem := range elems[1:] {
		if elem <= biggest {
			err = errors.New("covered fields sorting violation")
			return
		}
	}
	if int(biggest) > biggestAllowed {
		err = errors.New("covered fields indexing violation")
		return
	}
	return
}

// validCoveredFields makes sure that all covered fields objects in the
// signatures follow the rules. This means that if `WholeTransaction` is set to
// true, all fields except for `Signatures` must be empty. All fields must be
// sorted numerically, and there can be no repeats.
func (t Transaction) validCoveredFields() (err error) {
	for _, sig := range t.Signatures {
		// Check that all fields are empty if `WholeTransaction` is set.
		cf := sig.CoveredFields
		if cf.WholeTransaction {
			if len(cf.SiacoinInputs) != 0 ||
				len(cf.MinerFees) != 0 ||
				len(cf.SiacoinOutputs) != 0 ||
				len(cf.FileContracts) != 0 ||
				len(cf.StorageProofs) != 0 ||
				len(cf.SiafundInputs) != 0 ||
				len(cf.SiafundOutputs) != 0 ||
				len(cf.ArbitraryData) != 0 {
				err = errors.New("whole transaction is set but not all fields besides signatures are empty")
				return
			}
		}

		// Check that all fields are sorted, and without repeat values, and
		// that all elements point to objects that exists within the
		// transaction.
		err = sortedUnique(cf.SiacoinInputs, len(cf.SiacoinInputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.MinerFees, len(cf.MinerFees)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.SiacoinOutputs, len(cf.SiacoinOutputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.FileContracts, len(cf.FileContracts)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.StorageProofs, len(cf.StorageProofs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.SiafundInputs, len(cf.SiafundInputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.SiafundOutputs, len(cf.SiafundOutputs)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.ArbitraryData, len(cf.ArbitraryData)-1)
		if err != nil {
			return
		}
		err = sortedUnique(cf.Signatures, len(cf.Signatures)-1)
		if err != nil {
			return
		}
	}

	return
}

// validSignatures takes a transaction and returns an error if the signatures
// are not all valid.
func (s *State) validSignatures(t Transaction) (err error) {
	// Check that all covered fields objects follow the rules.
	err = t.validCoveredFields()
	if err != nil {
		return
	}

	// Create the InputSignatures object for each input.
	sigMap := make(map[string]*InputSignatures)
	for i, input := range t.SiacoinInputs {
		stringID := string(input.OutputID[:])
		_, exists := sigMap[stringID]
		if exists {
			return errors.New("siacoin output spent twice in the same transaction.")
		}

		inSig := &InputSignatures{
			RemainingSignatures: input.SpendConditions.NumSignatures,
			PossibleKeys:        input.SpendConditions.PublicKeys,
			Index:               i,
		}
		sigMap[stringID] = inSig
	}
	for i, input := range t.SiafundInputs {
		stringID := string(input.OutputID[:])
		_, exists := sigMap[stringID]
		if exists {
			return errors.New("siafund output spent twice in the same transaction.")
		}

		inSig := &InputSignatures{
			RemainingSignatures: input.SpendConditions.NumSignatures,
			PossibleKeys:        input.SpendConditions.PublicKeys,
			Index:               i,
		}
		sigMap[stringID] = inSig
	}

	// Check all of the signatures for validity.
	for i, sig := range t.Signatures {
		stringID := string(sig.InputID[:])

		// Check that each signature signs a unique pubkey where
		// RemainingSignatures > 0.
		if sigMap[stringID].RemainingSignatures == 0 {
			return errors.New("frivolous signature in transaction")
		}
		_, exists := sigMap[stringID].UsedKeys[sig.PublicKeyIndex]
		if exists {
			return errors.New("one public key was used twice while signing an input")
		}

		// Check that the timelock has expired.
		if sig.TimeLock > s.height() {
			return errors.New("signature used before timelock expiration")
		}

		// Check that the signature verifies. Sia is built to support multiple
		// types of signature algorithms, this is handled by the switch
		// statement.
		publicKey := sigMap[stringID].PossibleKeys[sig.PublicKeyIndex]
		switch publicKey.Algorithm {
		case SignatureEntropy:
			return crypto.ErrInvalidSignature
		case SignatureEd25519:
			// Decode the public key and signature.
			var decodedPK crypto.PublicKey
			err := encoding.Unmarshal(publicKey.Key, &decodedPK)
			if err != nil {
				return err
			}
			var decodedSig crypto.Signature
			err = encoding.Unmarshal(sig.Signature, &decodedSig)
			if err != nil {
				return err
			}

			sigHash := t.SigHash(i)
			err = crypto.VerifyHash(sigHash, decodedPK, decodedSig)
			if err != nil {
				return err
			}
		default:
			// If we don't recognize the identifier, assume that the signature
			// is valid; do nothing. This allows more signature types to be
			// added through soft forking.
		}

		// Subtract the number of signatures remaining for this input.
		sigMap[stringID].RemainingSignatures -= 1
	}

	// Check that all inputs have been sufficiently signed.
	for _, reqSigs := range sigMap {
		if reqSigs.RemainingSignatures != 0 {
			return MissingSignaturesErr
		}
	}

	return nil
}

// ValidSignatures takes a transaction and determines whether the transaction
// contains a legal set of signatures, including checking the timelocks against
// the current state height.
func (s *State) ValidSignatures(t Transaction) error {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.validSignatures(t)
}
<|endoftext|>


<commit_before>// Copyright 2017 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package swarming

import (
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	miloProto "go.chromium.org/luci/common/proto/milo"
	"go.chromium.org/luci/grpc/grpcutil"
	"go.chromium.org/luci/logdog/common/types"
	milo "go.chromium.org/luci/milo/api/proto"
	"go.chromium.org/luci/milo/buildsource/rawpresentation"

	"golang.org/x/net/context"
)

// BuildInfoProvider provides build information.
//
// In a production system, this will be completely defaults. For testing, the
// various services and data sources may be substituted for testing stubs.
type BuildInfoProvider struct {
	// swarmingServiceFunc returns a SwarmingService instance for the supplied
	// parameters.
	//
	// If nil, a production fetcher will be generated.
	swarmingServiceFunc func(c context.Context, host string) (SwarmingService, error)
}

func (p *BuildInfoProvider) newSwarmingService(c context.Context, host string) (SwarmingService, error) {
	if p.swarmingServiceFunc == nil {
		return NewProdService(c, host)
	}
	return p.swarmingServiceFunc(c, host)
}

// GetBuildInfo resolves a Milo protobuf Step for a given Swarming task.
func (p *BuildInfoProvider) GetBuildInfo(c context.Context, req *milo.BuildInfoRequest_Swarming,
	projectHint string) (*milo.BuildInfoResponse, error) {

	// Load the Swarming task (no log content).
	sf, err := p.newSwarmingService(c, req.Host)
	if err != nil {
		logging.WithError(err).Errorf(c, "Failed to create Swarming fetcher.")
		return nil, grpcutil.Internal
	}

	// Use default Swarming host.
	host := sf.GetHost()
	logging.Infof(c, "Loading build info for Swarming host %s, task %s.", host, req.Task)

	fr, err := swarmingFetch(c, sf, req.Task, swarmingFetchParams{})
	if err != nil {
		if err == ErrNotMiloJob {
			logging.Warningf(c, "User requested nonexistent task or does not have permissions.")
			return nil, grpcutil.NotFound
		}

		logging.WithError(err).Errorf(c, "Failed to load Swarming task.")
		return nil, grpcutil.Internal
	}

	// Determine the LogDog annotation stream path for this Swarming task.
	//
	// On failure, will return a gRPC error.
	stream, err := resolveLogDogAnnotations(c, fr.res.Tags)
	if err != nil {
		logging.WithError(err).Warningf(c, "Failed to get annotation stream parameters.")
		return nil, err
	}

	logging.Fields{
		"host":    stream.Host,
		"project": stream.Project,
		"path":    stream.Path,
	}.Infof(c, "Resolved LogDog annotation stream.")

	step, err := rawpresentation.ReadAnnotations(c, stream)

	// Add Swarming task parameters to the Milo step.
	if err := addTaskToMiloStep(c, sf.GetHost(), fr.res, step); err != nil {
		return nil, err
	}

	prefix, name := stream.Path.Split()
	return &milo.BuildInfoResponse{
		Project: string(stream.Project),
		Step:    step,
		AnnotationStream: &miloProto.LogdogStream{
			Server: stream.Host,
			Prefix: string(prefix),
			Name:   string(name),
		},
	}, nil
}

// resolveLogDogAnnotations returns a configured AnnotationStream given the input
// parameters.
//
// This will return a gRPC error on failure.
func resolveLogDogAnnotations(c context.Context, tagsRaw []string) (*types.StreamAddr, error) {
	addr, err := resolveLogDogStreamAddrFromTags(swarmingTags(tagsRaw))
	if err != nil {
		logging.WithError(err).Debugf(c, "Could not resolve stream address from tags.")
		return nil, grpcutil.NotFound
	}
	return addr, nil
}

func resolveLogDogStreamAddrFromTags(tags map[string]string) (*types.StreamAddr, error) {
	// If we don't have a LUCI project, abort.
	luciProject, logLocation := tags["luci_project"], tags["log_location"]
	switch {
	case luciProject == "":
		return nil, errors.New("no 'luci_project' tag")
	case logLocation == "":
		return nil, errors.New("no 'log_location' tag")
	}

	addr, err := types.ParseURL(logLocation)
	if err != nil {
		return nil, errors.Annotate(err, "could not parse LogDog stream from location").Err()
	}

	// The LogDog stream's project should match the LUCI project.
	if string(addr.Project) != luciProject {
		return nil, errors.Reason("stream project %q doesn't match LUCI project %q", addr.Project, luciProject).Err()
	}

	return addr, nil
}
<commit_msg>[milo] Check error returned by ReadAnnotations<commit_after>// Copyright 2017 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package swarming

import (
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	miloProto "go.chromium.org/luci/common/proto/milo"
	"go.chromium.org/luci/grpc/grpcutil"
	"go.chromium.org/luci/logdog/common/types"
	milo "go.chromium.org/luci/milo/api/proto"
	"go.chromium.org/luci/milo/buildsource/rawpresentation"

	"golang.org/x/net/context"
)

// BuildInfoProvider provides build information.
//
// In a production system, this will be completely defaults. For testing, the
// various services and data sources may be substituted for testing stubs.
type BuildInfoProvider struct {
	// swarmingServiceFunc returns a SwarmingService instance for the supplied
	// parameters.
	//
	// If nil, a production fetcher will be generated.
	swarmingServiceFunc func(c context.Context, host string) (SwarmingService, error)
}

func (p *BuildInfoProvider) newSwarmingService(c context.Context, host string) (SwarmingService, error) {
	if p.swarmingServiceFunc == nil {
		return NewProdService(c, host)
	}
	return p.swarmingServiceFunc(c, host)
}

// GetBuildInfo resolves a Milo protobuf Step for a given Swarming task.
func (p *BuildInfoProvider) GetBuildInfo(c context.Context, req *milo.BuildInfoRequest_Swarming,
	projectHint string) (*milo.BuildInfoResponse, error) {

	// Load the Swarming task (no log content).
	sf, err := p.newSwarmingService(c, req.Host)
	if err != nil {
		logging.WithError(err).Errorf(c, "Failed to create Swarming fetcher.")
		return nil, grpcutil.Internal
	}

	// Use default Swarming host.
	host := sf.GetHost()
	logging.Infof(c, "Loading build info for Swarming host %s, task %s.", host, req.Task)

	fr, err := swarmingFetch(c, sf, req.Task, swarmingFetchParams{})
	if err != nil {
		if err == ErrNotMiloJob {
			logging.Warningf(c, "User requested nonexistent task or does not have permissions.")
			return nil, grpcutil.NotFound
		}

		logging.WithError(err).Errorf(c, "Failed to load Swarming task.")
		return nil, grpcutil.Internal
	}

	// Determine the LogDog annotation stream path for this Swarming task.
	//
	// On failure, will return a gRPC error.
	stream, err := resolveLogDogAnnotations(c, fr.res.Tags)
	if err != nil {
		logging.WithError(err).Warningf(c, "Failed to get annotation stream parameters.")
		return nil, err
	}

	logging.Fields{
		"host":    stream.Host,
		"project": stream.Project,
		"path":    stream.Path,
	}.Infof(c, "Resolved LogDog annotation stream.")

	step, err := rawpresentation.ReadAnnotations(c, stream)
	if err != nil {
		return nil, errors.Annotate(err, "failed to read annotations").Err()
	}

	// Add Swarming task parameters to the Milo step.
	if err := addTaskToMiloStep(c, sf.GetHost(), fr.res, step); err != nil {
		return nil, err
	}

	prefix, name := stream.Path.Split()
	return &milo.BuildInfoResponse{
		Project: string(stream.Project),
		Step:    step,
		AnnotationStream: &miloProto.LogdogStream{
			Server: stream.Host,
			Prefix: string(prefix),
			Name:   string(name),
		},
	}, nil
}

// resolveLogDogAnnotations returns a configured AnnotationStream given the input
// parameters.
//
// This will return a gRPC error on failure.
func resolveLogDogAnnotations(c context.Context, tagsRaw []string) (*types.StreamAddr, error) {
	addr, err := resolveLogDogStreamAddrFromTags(swarmingTags(tagsRaw))
	if err != nil {
		logging.WithError(err).Debugf(c, "Could not resolve stream address from tags.")
		return nil, grpcutil.NotFound
	}
	return addr, nil
}

func resolveLogDogStreamAddrFromTags(tags map[string]string) (*types.StreamAddr, error) {
	// If we don't have a LUCI project, abort.
	luciProject, logLocation := tags["luci_project"], tags["log_location"]
	switch {
	case luciProject == "":
		return nil, errors.New("no 'luci_project' tag")
	case logLocation == "":
		return nil, errors.New("no 'log_location' tag")
	}

	addr, err := types.ParseURL(logLocation)
	if err != nil {
		return nil, errors.Annotate(err, "could not parse LogDog stream from location").Err()
	}

	// The LogDog stream's project should match the LUCI project.
	if string(addr.Project) != luciProject {
		return nil, errors.Reason("stream project %q doesn't match LUCI project %q", addr.Project, luciProject).Err()
	}

	return addr, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcec

import (
	"crypto/ecdsa"
	"fmt"
	"math/big"
)

func isOdd(a *big.Int) bool {
	return a.Bit(0) == 1
}

const (
	pubkeyCompressed   byte = 0x2 // y_bit + x coord
	pubkeyUncompressed byte = 0x4 // x coord + y coord
	pubkeyHybrid       byte = 0x6 // y_bit + x coord + y coord
)

// ParsePubKey parses a public key for a koblitz curve from a bytestring into a
// ecdsa.Publickey, verifying that it is valid. It supports compressed,
// uncompressed and hybrid signature formats.
func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *ecdsa.PublicKey, err error) {
	pubkey := ecdsa.PublicKey{}
	pubkey.Curve = curve

	format := pubKeyStr[0]
	ybit := (format & 0x1) == 0x1
	format &= ^byte(0x1)

	switch len(pubKeyStr) {
	case 65: // normal public key
		if format != pubkeyUncompressed && format != pubkeyHybrid {
			return nil, fmt.Errorf("invalid magic in pubkey str: "+
				"%d", pubKeyStr[0])
		}

		pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
		pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
		// hybrid keys have extra information, make use of it.
		if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
			return nil, fmt.Errorf("ybit doesn't match oddness")
		}
	case 33: // compressed public key
		// format is 0x2 | solution, <X coordinate>
		// solution determines which solution of the curve we use.
		/// y^2 = x^3 + Curve.B
		if format != pubkeyCompressed {
			return nil, fmt.Errorf("invalid magic in compressed "+
				"pubkey string: %d", pubKeyStr[0])
		}
		pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
		// Y = +-sqrt(x^3 + B)
		x3 := new(big.Int).Mul(pubkey.X, pubkey.X)
		x3.Mul(x3, pubkey.X)
		x3.Add(x3, pubkey.Curve.Params().B)

		// now calculate sqrt mod p of x2 + B
		// This code used to do a full sqrt based on tonelli/shanks,
		// but this was replaced by the algorithms referenced in
		// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
		y := new(big.Int).Exp(x3, curve.QPlus1Div4(), pubkey.Curve.Params().P)

		if ybit != isOdd(y) {
			y.Sub(pubkey.Curve.Params().P, y)
		}
		if ybit != isOdd(y) {
			return nil, fmt.Errorf("ybit doesn't match oddness")
		}

		pubkey.Y = y
	default: // wrong!
		return nil, fmt.Errorf("invalid pub key length %d",
			len(pubKeyStr))
	}

	if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
		return nil, fmt.Errorf("pubkey X parameter is >= to P")
	}
	if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
		return nil, fmt.Errorf("pubkey Y parameter is >= to P")
	}
	if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
		return nil, fmt.Errorf("pubkey isn't on secp265k1 curve")
	}
	return &pubkey, nil
}

// PublicKey is an ecdsa.PublicKey with additional functions to
// serialize in uncompressed, compressed, and hybrid formats.
type PublicKey ecdsa.PublicKey

// SerializeUncompressed serializes a public key in a 65-byte uncompressed
// format.
func (p *PublicKey) SerializeUncompressed() []byte {
	b := make([]byte, 65)
	b[0] = pubkeyUncompressed
	copy(b[1:33], p.X.Bytes())
	copy(b[33:], p.Y.Bytes())
	return b
}

// SerializeCompressed serializes a public key in a 33-byte compressed format.
func (p *PublicKey) SerializeCompressed() []byte {
	b := make([]byte, 33)
	format := pubkeyCompressed
	if isOdd(p.Y) {
		format |= 0x1
	}
	b[0] = format
	copy(b[1:33], p.X.Bytes())
	return b
}

// SerializeHybrid serializes a public key in a 65-byte hybrid format.
func (p *PublicKey) SerializeHybrid() []byte {
	b := make([]byte, 65)
	format := pubkeyHybrid
	if isOdd(p.Y) {
		format |= 0x1
	}
	b[0] = format
	copy(b[1:33], p.X.Bytes())
	copy(b[33:], p.Y.Bytes())
	return b
}
<commit_msg>Add padding for pubkey numbers.<commit_after>// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcec

import (
	"crypto/ecdsa"
	"fmt"
	"math/big"
)

func isOdd(a *big.Int) bool {
	return a.Bit(0) == 1
}

const (
	pubkeyCompressed   byte = 0x2 // y_bit + x coord
	pubkeyUncompressed byte = 0x4 // x coord + y coord
	pubkeyHybrid       byte = 0x6 // y_bit + x coord + y coord
)

// ParsePubKey parses a public key for a koblitz curve from a bytestring into a
// ecdsa.Publickey, verifying that it is valid. It supports compressed,
// uncompressed and hybrid signature formats.
func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *ecdsa.PublicKey, err error) {
	pubkey := ecdsa.PublicKey{}
	pubkey.Curve = curve

	format := pubKeyStr[0]
	ybit := (format & 0x1) == 0x1
	format &= ^byte(0x1)

	switch len(pubKeyStr) {
	case 65: // normal public key
		if format != pubkeyUncompressed && format != pubkeyHybrid {
			return nil, fmt.Errorf("invalid magic in pubkey str: "+
				"%d", pubKeyStr[0])
		}

		pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
		pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
		// hybrid keys have extra information, make use of it.
		if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
			return nil, fmt.Errorf("ybit doesn't match oddness")
		}
	case 33: // compressed public key
		// format is 0x2 | solution, <X coordinate>
		// solution determines which solution of the curve we use.
		/// y^2 = x^3 + Curve.B
		if format != pubkeyCompressed {
			return nil, fmt.Errorf("invalid magic in compressed "+
				"pubkey string: %d", pubKeyStr[0])
		}
		pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
		// Y = +-sqrt(x^3 + B)
		x3 := new(big.Int).Mul(pubkey.X, pubkey.X)
		x3.Mul(x3, pubkey.X)
		x3.Add(x3, pubkey.Curve.Params().B)

		// now calculate sqrt mod p of x2 + B
		// This code used to do a full sqrt based on tonelli/shanks,
		// but this was replaced by the algorithms referenced in
		// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
		y := new(big.Int).Exp(x3, curve.QPlus1Div4(), pubkey.Curve.Params().P)

		if ybit != isOdd(y) {
			y.Sub(pubkey.Curve.Params().P, y)
		}
		if ybit != isOdd(y) {
			return nil, fmt.Errorf("ybit doesn't match oddness")
		}

		pubkey.Y = y
	default: // wrong!
		return nil, fmt.Errorf("invalid pub key length %d",
			len(pubKeyStr))
	}

	if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
		return nil, fmt.Errorf("pubkey X parameter is >= to P")
	}
	if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
		return nil, fmt.Errorf("pubkey Y parameter is >= to P")
	}
	if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
		return nil, fmt.Errorf("pubkey isn't on secp265k1 curve")
	}
	return &pubkey, nil
}

// PublicKey is an ecdsa.PublicKey with additional functions to
// serialize in uncompressed, compressed, and hybrid formats.
type PublicKey ecdsa.PublicKey

// SerializeUncompressed serializes a public key in a 65-byte uncompressed
// format.
func (p *PublicKey) SerializeUncompressed() []byte {
	b := make([]byte, 65)
	b[0] = pubkeyUncompressed
	copy(b[1:33], pad(32, p.X.Bytes()))
	copy(b[33:], pad(32, p.Y.Bytes()))
	return b
}

// SerializeCompressed serializes a public key in a 33-byte compressed format.
func (p *PublicKey) SerializeCompressed() []byte {
	b := make([]byte, 33)
	format := pubkeyCompressed
	if isOdd(p.Y) {
		format |= 0x1
	}
	b[0] = format
	copy(b[1:33], pad(32, p.X.Bytes()))
	return b
}

// SerializeHybrid serializes a public key in a 65-byte hybrid format.
func (p *PublicKey) SerializeHybrid() []byte {
	b := make([]byte, 65)
	format := pubkeyHybrid
	if isOdd(p.Y) {
		format |= 0x1
	}
	b[0] = format
	copy(b[1:33], pad(32, p.X.Bytes()))
	copy(b[33:], pad(32, p.Y.Bytes()))
	return b
}

func pad(size int, b []byte) []byte {
	// Prevent a possible panic if the input exceeds the expected size.
	if len(b) > size {
		size = len(b)
	}

	p := make([]byte, size)
	copy(p[size-len(b):], b)
	return p
}
<|endoftext|>