<commit_before>package types

const (
	//Ubuntu14MdmPackageName MDM package name for Ubuntu14
	Ubuntu14MdmPackageName = "emc-scaleio-mdm"

	//Ubuntu14SdsPackageName SDS package name for Ubuntu14
	Ubuntu14SdsPackageName = "emc-scaleio-sds"

	//Ubuntu14SdcPackageName SDC package name for Ubuntu14
	Ubuntu14SdcPackageName = "emc-scaleio-sdc"

	//Ubuntu14LiaPackageName LIA package name for Ubuntu14
	Ubuntu14LiaPackageName = "emc-scaleio-lia"

	//Ubuntu14GwPackageName GW package name for Ubuntu14
	Ubuntu14GwPackageName = "emc-scaleio-gateway"

	//Rhel7MdmPackageName MDM package name for RHEL7
	Rhel7MdmPackageName = "EMC-ScaleIO-mdm-2.0-10000.2072.el7.x86_64"

	//Rhel7SdsPackageName SDS package name for RHEL7
	Rhel7SdsPackageName = "EMC-ScaleIO-sds-2.0-10000.2072.el7.x86_64"

	//Rhel7SdcPackageName SDC package name for RHEL7
	Rhel7SdcPackageName = "EMC-ScaleIO-sdc-2.0-10000.2072.el7.x86_64"

	//Rhel7LiaPackageName LIA package name for RHEL7
	Rhel7LiaPackageName = "EMC-ScaleIO-lia-2.0-10000.2072.el7.x86_64"

	//Rhel7GwPackageName GW package name for RHEL7
	Rhel7GwPackageName = "EMC-ScaleIO-gateway-2.0-10000.2072.x86_64"

	//RexRayPackageName rexray package name
	RexRayPackageName = "rexray"

	//DvdcliPackageName DVDCLI package name
	DvdcliPackageName = "dvdcli"
)

const (
	//PersonaMdmPrimary is the first MDM
	PersonaMdmPrimary = 1

	//PersonaMdmSecondary is the second MDM
	PersonaMdmSecondary = 2

	//PersonaTb is the tie breaker
	PersonaTb = 3

	//PersonaNode is just a normal data node
	PersonaNode = 4
)

const (
	//StateUnknown will start with a fresh installation (or upgrade)
	StateUnknown = 0

	//StateCleanPrereqsReboot after kernel version has been updated
	StateCleanPrereqsReboot = 1

	//StatePrerequisitesInstalled the prerequisite packages are installed
	StatePrerequisitesInstalled = 2

	//StateBasePackagedInstalled the base ScaleIO packages are installed
	StateBasePackagedInstalled = 3

	//StateInitializeCluster the cluster is setup, now initial the cluster
	StateInitializeCluster = 4

	//StateInstallRexRay install rexray
	StateInstallRexRay = 5

	//StateCleanInstallReboot after installing all components
	StateCleanInstallReboot = 6

	//StateSystemReboot system is rebooting
	StateSystemReboot = 7

	//StateFinishInstall the agent node installation is complete
	StateFinishInstall = 1024

	//StateUpgradeCluster start the upgrade process
	StateUpgradeCluster = 2048

	//StateFatalInstall the agent node installation had a fatal error
	//manual intervention is required for now
	StateFatalInstall = 4096
)

//Version describes the version of the REST API
type Version struct {
	VersionInt int               `json:"versionint"`
	VersionStr string            `json:"versionstr"`
	BuildStr   string            `json:"buildstr,omitempty"`
	KeyValue   map[string]string `json:"keyvalue,omitempty"`
}

//Ubuntu14Packages describes the download URIs for Ubuntu install packages
type Ubuntu14Packages struct {
	Mdm string `json:"ubuntu14mdm"`
	Sds string `json:"ubuntu14sds"`
	Sdc string `json:"ubuntu14sdc"`
	Lia string `json:"ubuntu14lia"`
	Gw  string `json:"ubuntu14gw"`
}

//Rhel7Packages describes the download URIs for CentOS install packages
type Rhel7Packages struct {
	Mdm string `json:"rhel7mdm"`
	Sds string `json:"rhel7sds"`
	Sdc string `json:"rhel7sdc"`
	Lia string `json:"rhel7lia"`
	Gw  string `json:"rhel7gw"`
}

//ScaleIONode node definition
type ScaleIONode struct {
	AgentID     string            `json:"name"`
	TaskID      string            `json:"taskid"`
	ExecutorID  string            `json:"executorid"`
	OfferID     string            `json:"offerid"`
	IPAddress   string            `json:"ipaddress"`
	Hostname    string            `json:"hostname"`
	Index       int               `json:"index"`
	Persona     int               `json:"persona"`
	State       int               `json:"state"`
	InCluster   bool              `json:"incluster"`
	LastContact int64             `json:"lastcontact"`
	KeyValue    map[string]string `json:"keyvalue,omitempty"`
}

//ScaleIONodes collection of ScaleIONode
type ScaleIONodes []*ScaleIONode

//ScaleIOPreConfig information to attach Data nodes to an existing
//ScaleIO cluster
type ScaleIOPreConfig struct {
	PreConfigEnabled     bool   `json:"preconfigenabled"`
	PrimaryMdmAddress    string `json:"preconfigprimdm"`
	SecondaryMdmAddress  string `json:"preconfigsecmdm"`
	TieBreakerMdmAddress string `json:"preconfigtbmdm"`
	GatewayAddress       string `json:"preconfiggateway"`
}

//ScaleIOConfig describes the configuration for this cluster
type ScaleIOConfig struct {
	ClusterID        string            `json:"clusterid"`
	ClusterName      string            `json:"clustername"`
	LbGateway        string            `json:"lbgateway"`
	ProtectionDomain string            `json:"protectiondomain"` //optional. Default: pd
	StoragePool      string            `json:"storagepool"`      //optional. Default: sp
	AdminPassword    string            `json:"adminpassword"`    //optional. Default: Scaleio123
	BlockDevice      string            `json:"blockdevice"`      //optional. Default: /dev/xvdf
	KeyValue         map[string]string `json:"keyvalue,omitempty"`
	Nodes            ScaleIONodes
	Preconfig        ScaleIOPreConfig
	Ubuntu14         Ubuntu14Packages
	Rhel7            Rhel7Packages
}

//IsolatorConfig describes the configuration for the mesos isolator
//on these ScaleIO nodes
type IsolatorConfig struct {
	Binary string `json:"isolatorbinary"`
}

//RexrayConfig describes the configuration for REX-Ray on these ScaleIO nodes
type RexrayConfig struct {
	Branch  string `json:"rexraybranch"`
	Version string `json:"rexrayversion"`
}

//ScaleIOFramework describes the overall framework state
type ScaleIOFramework struct {
	SchedulerAddress string            `json:"scheduleraddress"`
	LogLevel         string            `json:"loglevel"`
	DemoMode         bool              `json:"demomode"`
	Debug            bool              `json:"debug"`
	Experimental     bool              `json:"experimental"`
	KeyValue         map[string]string `json:"keyvalue,omitempty"`
	ScaleIO          ScaleIOConfig
	Rexray           RexrayConfig
	Isolator         IsolatorConfig
}

//UpdateNode describes an executor going through a state change
type UpdateNode struct {
	Acknowledged bool              `json:"acknowledged"`
	ExecutorID   string            `json:"executorid"`
	State        int               `json:"state"`
	KeyValue     map[string]string `json:"keyvalue,omitempty"`
}

//AddNode describes an executor being added to the ScaleIO cluster
type AddNode struct {
	Acknowledged bool              `json:"acknowledged"`
	ExecutorID   string            `json:"executorid"`
	KeyValue     map[string]string `json:"keyvalue,omitempty"`
}

//PingNode describes a "I am still here" update
type PingNode struct {
	Acknowledged bool              `json:"acknowledged"`
	ExecutorID   string            `json:"executorid"`
	KeyValue     map[string]string `json:"keyvalue,omitempty"`
}
<commit_msg>Fix the RHEL7 package names<commit_after>package types

const (
	//Ubuntu14MdmPackageName MDM package name for Ubuntu14
	Ubuntu14MdmPackageName = "emc-scaleio-mdm"

	//Ubuntu14SdsPackageName SDS package name for Ubuntu14
	Ubuntu14SdsPackageName = "emc-scaleio-sds"

	//Ubuntu14SdcPackageName SDC package name for Ubuntu14
	Ubuntu14SdcPackageName = "emc-scaleio-sdc"

	//Ubuntu14LiaPackageName LIA package name for Ubuntu14
	Ubuntu14LiaPackageName = "emc-scaleio-lia"

	//Ubuntu14GwPackageName GW package name for Ubuntu14
	Ubuntu14GwPackageName = "emc-scaleio-gateway"

	//Rhel7MdmPackageName MDM package name for RHEL7
	Rhel7MdmPackageName = "EMC-ScaleIO-mdm"

	//Rhel7SdsPackageName SDS package name for RHEL7
	Rhel7SdsPackageName = "EMC-ScaleIO-sds"

	//Rhel7SdcPackageName SDC package name for RHEL7
	Rhel7SdcPackageName = "EMC-ScaleIO-sdc"

	//Rhel7LiaPackageName LIA package name for RHEL7
	Rhel7LiaPackageName = "EMC-ScaleIO-lia"

	//Rhel7GwPackageName GW package name for RHEL7
	Rhel7GwPackageName = "EMC-ScaleIO-gateway"

	//RexRayPackageName rexray package name
	RexRayPackageName = "rexray"

	//DvdcliPackageName DVDCLI package name
	DvdcliPackageName = "dvdcli"
)

const (
	//PersonaMdmPrimary is the first MDM
	PersonaMdmPrimary = 1

	//PersonaMdmSecondary is the second MDM
	PersonaMdmSecondary = 2

	//PersonaTb is the tie breaker
	PersonaTb = 3

	//PersonaNode is just a normal data node
	PersonaNode = 4
)

const (
	//StateUnknown will start with a fresh installation (or upgrade)
	StateUnknown = 0

	//StateCleanPrereqsReboot after kernel version has been updated
	StateCleanPrereqsReboot = 1

	//StatePrerequisitesInstalled the prerequisite packages are installed
	StatePrerequisitesInstalled = 2

	//StateBasePackagedInstalled the base ScaleIO packages are installed
	StateBasePackagedInstalled = 3

	//StateInitializeCluster the cluster is setup, now initial the cluster
	StateInitializeCluster = 4

	//StateInstallRexRay install rexray
	StateInstallRexRay = 5

	//StateCleanInstallReboot after installing all components
	StateCleanInstallReboot = 6

	//StateSystemReboot system is rebooting
	StateSystemReboot = 7

	//StateFinishInstall the agent node installation is complete
	StateFinishInstall = 1024

	//StateUpgradeCluster start the upgrade process
	StateUpgradeCluster = 2048

	//StateFatalInstall the agent node installation had a fatal error
	//manual intervention is required for now
	StateFatalInstall = 4096
)

//Version describes the version of the REST API
type Version struct {
	VersionInt int               `json:"versionint"`
	VersionStr string            `json:"versionstr"`
	BuildStr   string            `json:"buildstr,omitempty"`
	KeyValue   map[string]string `json:"keyvalue,omitempty"`
}

//Ubuntu14Packages describes the download URIs for Ubuntu install packages
type Ubuntu14Packages struct {
	Mdm string `json:"ubuntu14mdm"`
	Sds string `json:"ubuntu14sds"`
	Sdc string `json:"ubuntu14sdc"`
	Lia string `json:"ubuntu14lia"`
	Gw  string `json:"ubuntu14gw"`
}

//Rhel7Packages describes the download URIs for CentOS install packages
type Rhel7Packages struct {
	Mdm string `json:"rhel7mdm"`
	Sds string `json:"rhel7sds"`
	Sdc string `json:"rhel7sdc"`
	Lia string `json:"rhel7lia"`
	Gw  string `json:"rhel7gw"`
}

//ScaleIONode node definition
type ScaleIONode struct {
	AgentID     string            `json:"name"`
	TaskID      string            `json:"taskid"`
	ExecutorID  string            `json:"executorid"`
	OfferID     string            `json:"offerid"`
	IPAddress   string            `json:"ipaddress"`
	Hostname    string            `json:"hostname"`
	Index       int               `json:"index"`
	Persona     int               `json:"persona"`
	State       int               `json:"state"`
	InCluster   bool              `json:"incluster"`
	LastContact int64             `json:"lastcontact"`
	KeyValue    map[string]string `json:"keyvalue,omitempty"`
}

//ScaleIONodes collection of ScaleIONode
type ScaleIONodes []*ScaleIONode

//ScaleIOPreConfig information to attach Data nodes to an existing
//ScaleIO cluster
type ScaleIOPreConfig struct {
	PreConfigEnabled     bool   `json:"preconfigenabled"`
	PrimaryMdmAddress    string `json:"preconfigprimdm"`
	SecondaryMdmAddress  string `json:"preconfigsecmdm"`
	TieBreakerMdmAddress string `json:"preconfigtbmdm"`
	GatewayAddress       string `json:"preconfiggateway"`
}

//ScaleIOConfig describes the configuration for this cluster
type ScaleIOConfig struct {
	ClusterID        string            `json:"clusterid"`
	ClusterName      string            `json:"clustername"`
	LbGateway        string            `json:"lbgateway"`
	ProtectionDomain string            `json:"protectiondomain"` //optional. Default: pd
	StoragePool      string            `json:"storagepool"`      //optional. Default: sp
	AdminPassword    string            `json:"adminpassword"`    //optional. Default: Scaleio123
	BlockDevice      string            `json:"blockdevice"`      //optional. Default: /dev/xvdf
	KeyValue         map[string]string `json:"keyvalue,omitempty"`
	Nodes            ScaleIONodes
	Preconfig        ScaleIOPreConfig
	Ubuntu14         Ubuntu14Packages
	Rhel7            Rhel7Packages
}

//IsolatorConfig describes the configuration for the mesos isolator
//on these ScaleIO nodes
type IsolatorConfig struct {
	Binary string `json:"isolatorbinary"`
}

//RexrayConfig describes the configuration for REX-Ray on these ScaleIO nodes
type RexrayConfig struct {
	Branch  string `json:"rexraybranch"`
	Version string `json:"rexrayversion"`
}

//ScaleIOFramework describes the overall framework state
type ScaleIOFramework struct {
	SchedulerAddress string            `json:"scheduleraddress"`
	LogLevel         string            `json:"loglevel"`
	DemoMode         bool              `json:"demomode"`
	Debug            bool              `json:"debug"`
	Experimental     bool              `json:"experimental"`
	KeyValue         map[string]string `json:"keyvalue,omitempty"`
	ScaleIO          ScaleIOConfig
	Rexray           RexrayConfig
	Isolator         IsolatorConfig
}

//UpdateNode describes an executor going through a state change
type UpdateNode struct {
	Acknowledged bool              `json:"acknowledged"`
	ExecutorID   string            `json:"executorid"`
	State        int               `json:"state"`
	KeyValue     map[string]string `json:"keyvalue,omitempty"`
}

//AddNode describes an executor being added to the ScaleIO cluster
type AddNode struct {
	Acknowledged bool              `json:"acknowledged"`
	ExecutorID   string            `json:"executorid"`
	KeyValue     map[string]string `json:"keyvalue,omitempty"`
}

//PingNode describes a "I am still here" update
type PingNode struct {
	Acknowledged bool              `json:"acknowledged"`
	ExecutorID   string            `json:"executorid"`
	KeyValue     map[string]string `json:"keyvalue,omitempty"`
}
<|endoftext|>


<commit_before>package scoop_protocol

// KinesisWriterConfig is used to configure a KinesisWriter
import (
	"errors"
	"fmt"
	"time"
)

// AnnotatedKinesisConfig is a Kinesis configuration annotated with meta information.
type AnnotatedKinesisConfig struct {
	ID               int
	AWSAccount       int64
	Team             string
	Version          int
	Contact          string
	Usage            string
	ConsumingLibrary string
	SpadeConfig      KinesisWriterConfig
	LastEditedAt     time.Time
	LastChangedBy    string
	Dropped          bool
	DroppedReason    string
}

// KinesisWriterEventConfig describes how a given Event is written to a Kinesis stream.
type KinesisWriterEventConfig struct {
	Filter       string
	FilterFunc   func(map[string]string) bool `json:"-"`
	Fields       []string
	FieldRenames map[string]string
	FullFieldMap map[string]string `json:"-"`
}

// KinesisWriterConfig describes a Kinesis Writer that the processor uses to export data to a Kinesis Stream/Firehose
// Make sure to call Validate() on Spade after loading this from JSON to populate some derived fields.
type KinesisWriterConfig struct {
	StreamName             string
	StreamRole             string
	StreamType             string // StreamType should be either "stream" or "firehose"
	StreamRegion           string // AWS region to write to. Blank to use default region.
	Compress               bool   // true if compress data with flate, false to output json
	FirehoseRedshiftStream bool   // true if JSON destined for Firehose->Redshift streaming
	EventNameTargetField   string // Field name to write the event's name to (useful for uncompressed streams)
	ExcludeEmptyFields     bool   // true if empty fields should be excluded from the JSON
	BufferSize             int
	MaxAttemptsPerRecord   int
	RetryDelay             string

	Events map[string]*KinesisWriterEventConfig

	Globber GlobberConfig
	Batcher BatcherConfig
}

var allowedRegions = map[string]struct{}{
	"us-east-1": {},
	"us-west-2": {},
}

// Validate returns an error if the Kinesis Writer config is not valid, or nil if it is.
// It also sets the FilterFunc on Events with Filters and populates FullFieldMap.
func (c *KinesisWriterConfig) Validate() error {
	if c.StreamType == "" || c.StreamName == "" {
		return fmt.Errorf("Mandatory fields stream type and stream name aren't populated")
	}

	err := c.Globber.Validate()
	if err != nil {
		return fmt.Errorf("globber config invalid: %v", err)
	}

	err = c.Batcher.Validate()
	if err != nil {
		return fmt.Errorf("batcher config invalid: %v", err)
	}

	if _, ok := allowedRegions[c.StreamRegion]; c.StreamRegion != "" && !ok {
		return fmt.Errorf("invalid region: %s", c.StreamRegion)
	}

	for _, e := range c.Events {
		if e.Filter != "" {
			e.FilterFunc = filterFuncs[e.Filter]
			if e.FilterFunc == nil {
				return fmt.Errorf("unknown filter: %s", e.Filter)
			}
		}
		e.FullFieldMap = make(map[string]string, len(e.Fields))
		if e.FieldRenames == nil {
			e.FieldRenames = make(map[string]string)
		}
		for _, f := range e.Fields {
			if renamed, ok := e.FieldRenames[f]; ok {
				e.FullFieldMap[f] = renamed
			} else {
				e.FullFieldMap[f] = f
			}
		}
	}

	if c.FirehoseRedshiftStream && (c.StreamType != "firehose" || c.Compress) {
		return fmt.Errorf("Redshift streaming only valid with non-compressed firehose")
	}

	_, err = time.ParseDuration(c.RetryDelay)
	return err
}

var filterFuncs = map[string]func(map[string]string) bool{
	"isAGSEvent": func(fields map[string]string) bool {
		return fields["adg_product_id"] == "600505cc-de2f-4b99-9960-c47ee5d23f04"
	},
	"isChannelIDSet": func(fields map[string]string) bool {
		return fields["channel_id"] != ""
	},
	"isUserIDSet": func(fields map[string]string) bool {
		return fields["user_id"] != ""
	},
	"isVod": func(fields map[string]string) bool {
		return fields["vod_id"] != "" && fields["vod_type"] != "clip"
	},
	"isLiveClipContent": func(fields map[string]string) bool {
		return fields["source_content_type"] == "live"
	},
	"isTwilightApp": func(fields map[string]string) bool {
		return fields["client_app"] == "twilight"
	},
}

// BatcherConfig is used to configure a batcher instance
type BatcherConfig struct {
	// MaxSize is the max combined size of the batch
	MaxSize int

	// MaxEntries is the max number of entries that can be batched together
	// if batches does not have an entry limit, set MaxEntries as -1
	MaxEntries int

	// MaxAge is the max age of the oldest entry in the glob
	MaxAge string

	// BufferLength is the length of the channel where newly
	// submitted entries are stored, decreasing the size of this
	// buffer can cause stalls, and increasing the size can increase
	// shutdown time
	BufferLength int
}

// Validate returns an error if the batcher config is invalid, nil otherwise.
func (c *BatcherConfig) Validate() error {
	maxAge, err := time.ParseDuration(c.MaxAge)
	if err != nil {
		return err
	}

	if maxAge <= 0 {
		return errors.New("MaxAge must be a positive value")
	}

	if c.MaxSize <= 0 {
		return errors.New("MaxSize must be a positive value")
	}

	if c.MaxEntries <= 0 && c.MaxEntries != -1 {
		return errors.New("MaxEntries must be a positive value or -1")
	}

	if c.BufferLength == 0 {
		return errors.New("BufferLength must be a positive value")
	}

	return nil
}

// GlobberConfig is used to configure a globber instance
type GlobberConfig struct {
	// MaxSize is the max size per glob before compression
	MaxSize int

	// MaxAge is the max age of the oldest entry in the glob
	MaxAge string

	// BufferLength is the length of the channel where newly
	// submitted entries are stored, decreasing the size of this
	// buffer can cause stalls, and increasing the size can increase
	// shutdown time
	BufferLength int
}

// Validate returns an error if the config is invalid, nil otherwise.
func (c *GlobberConfig) Validate() error {
	maxAge, err := time.ParseDuration(c.MaxAge)
	if err != nil {
		return err
	}

	if maxAge <= 0 {
		return errors.New("MaxAge must be a positive value")
	}

	if c.MaxSize <= 0 {
		return errors.New("MaxSize must be a positive value")
	}

	if c.BufferLength == 0 {
		return errors.New("BufferLength must be a positive value")
	}

	return nil
}
<commit_msg>Update ags filter<commit_after>package scoop_protocol

// KinesisWriterConfig is used to configure a KinesisWriter
import (
	"errors"
	"fmt"
	"time"
)

// AnnotatedKinesisConfig is a Kinesis configuration annotated with meta information.
type AnnotatedKinesisConfig struct {
	ID               int
	AWSAccount       int64
	Team             string
	Version          int
	Contact          string
	Usage            string
	ConsumingLibrary string
	SpadeConfig      KinesisWriterConfig
	LastEditedAt     time.Time
	LastChangedBy    string
	Dropped          bool
	DroppedReason    string
}

// KinesisWriterEventConfig describes how a given Event is written to a Kinesis stream.
type KinesisWriterEventConfig struct {
	Filter       string
	FilterFunc   func(map[string]string) bool `json:"-"`
	Fields       []string
	FieldRenames map[string]string
	FullFieldMap map[string]string `json:"-"`
}

// KinesisWriterConfig describes a Kinesis Writer that the processor uses to export data to a Kinesis Stream/Firehose
// Make sure to call Validate() on Spade after loading this from JSON to populate some derived fields.
type KinesisWriterConfig struct {
	StreamName             string
	StreamRole             string
	StreamType             string // StreamType should be either "stream" or "firehose"
	StreamRegion           string // AWS region to write to. Blank to use default region.
	Compress               bool   // true if compress data with flate, false to output json
	FirehoseRedshiftStream bool   // true if JSON destined for Firehose->Redshift streaming
	EventNameTargetField   string // Field name to write the event's name to (useful for uncompressed streams)
	ExcludeEmptyFields     bool   // true if empty fields should be excluded from the JSON
	BufferSize             int
	MaxAttemptsPerRecord   int
	RetryDelay             string

	Events map[string]*KinesisWriterEventConfig

	Globber GlobberConfig
	Batcher BatcherConfig
}

var allowedRegions = map[string]struct{}{
	"us-east-1": {},
	"us-west-2": {},
}

// Validate returns an error if the Kinesis Writer config is not valid, or nil if it is.
// It also sets the FilterFunc on Events with Filters and populates FullFieldMap.
func (c *KinesisWriterConfig) Validate() error {
	if c.StreamType == "" || c.StreamName == "" {
		return fmt.Errorf("Mandatory fields stream type and stream name aren't populated")
	}

	err := c.Globber.Validate()
	if err != nil {
		return fmt.Errorf("globber config invalid: %v", err)
	}

	err = c.Batcher.Validate()
	if err != nil {
		return fmt.Errorf("batcher config invalid: %v", err)
	}

	if _, ok := allowedRegions[c.StreamRegion]; c.StreamRegion != "" && !ok {
		return fmt.Errorf("invalid region: %s", c.StreamRegion)
	}

	for _, e := range c.Events {
		if e.Filter != "" {
			e.FilterFunc = filterFuncs[e.Filter]
			if e.FilterFunc == nil {
				return fmt.Errorf("unknown filter: %s", e.Filter)
			}
		}
		e.FullFieldMap = make(map[string]string, len(e.Fields))
		if e.FieldRenames == nil {
			e.FieldRenames = make(map[string]string)
		}
		for _, f := range e.Fields {
			if renamed, ok := e.FieldRenames[f]; ok {
				e.FullFieldMap[f] = renamed
			} else {
				e.FullFieldMap[f] = f
			}
		}
	}

	if c.FirehoseRedshiftStream && (c.StreamType != "firehose" || c.Compress) {
		return fmt.Errorf("Redshift streaming only valid with non-compressed firehose")
	}

	_, err = time.ParseDuration(c.RetryDelay)
	return err
}

var filterFuncs = map[string]func(map[string]string) bool{
	"isAGSEvent": func(fields map[string]string) bool {
		return fields["adg_product_id"] == "600505cc-de2f-4b99-9960-c47ee5d23f04" || fields["adg_product_id"] == "9233cb11-d375-4dd1-bd5e-b6d328fd5403"
	},
	"isChannelIDSet": func(fields map[string]string) bool {
		return fields["channel_id"] != ""
	},
	"isUserIDSet": func(fields map[string]string) bool {
		return fields["user_id"] != ""
	},
	"isVod": func(fields map[string]string) bool {
		return fields["vod_id"] != "" && fields["vod_type"] != "clip"
	},
	"isLiveClipContent": func(fields map[string]string) bool {
		return fields["source_content_type"] == "live"
	},
	"isTwilightApp": func(fields map[string]string) bool {
		return fields["client_app"] == "twilight"
	},
}

// BatcherConfig is used to configure a batcher instance
type BatcherConfig struct {
	// MaxSize is the max combined size of the batch
	MaxSize int

	// MaxEntries is the max number of entries that can be batched together
	// if batches does not have an entry limit, set MaxEntries as -1
	MaxEntries int

	// MaxAge is the max age of the oldest entry in the glob
	MaxAge string

	// BufferLength is the length of the channel where newly
	// submitted entries are stored, decreasing the size of this
	// buffer can cause stalls, and increasing the size can increase
	// shutdown time
	BufferLength int
}

// Validate returns an error if the batcher config is invalid, nil otherwise.
func (c *BatcherConfig) Validate() error {
	maxAge, err := time.ParseDuration(c.MaxAge)
	if err != nil {
		return err
	}

	if maxAge <= 0 {
		return errors.New("MaxAge must be a positive value")
	}

	if c.MaxSize <= 0 {
		return errors.New("MaxSize must be a positive value")
	}

	if c.MaxEntries <= 0 && c.MaxEntries != -1 {
		return errors.New("MaxEntries must be a positive value or -1")
	}

	if c.BufferLength == 0 {
		return errors.New("BufferLength must be a positive value")
	}

	return nil
}

// GlobberConfig is used to configure a globber instance
type GlobberConfig struct {
	// MaxSize is the max size per glob before compression
	MaxSize int

	// MaxAge is the max age of the oldest entry in the glob
	MaxAge string

	// BufferLength is the length of the channel where newly
	// submitted entries are stored, decreasing the size of this
	// buffer can cause stalls, and increasing the size can increase
	// shutdown time
	BufferLength int
}

// Validate returns an error if the config is invalid, nil otherwise.
func (c *GlobberConfig) Validate() error {
	maxAge, err := time.ParseDuration(c.MaxAge)
	if err != nil {
		return err
	}

	if maxAge <= 0 {
		return errors.New("MaxAge must be a positive value")
	}

	if c.MaxSize <= 0 {
		return errors.New("MaxSize must be a positive value")
	}

	if c.BufferLength == 0 {
		return errors.New("BufferLength must be a positive value")
	}

	return nil
}
<|endoftext|>


<commit_before>package scoop_protocol

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"time"

	"github.com/twitchscience/scoop_protocol/msg_signer"
)

type ColumnDefinition struct {
	InboundName           string
	OutboundName          string
	Transformer           string // this should match one of the types in redshift types
	ColumnCreationOptions string
}

type Config struct {
	EventName string
	Columns   []ColumnDefinition
	Version   int
}

type Action string

const (
	ADD    Action = "add"
	DELETE Action = "delete"
	RENAME Action = "rename"
)

// Operation represents a single change to a schema
type Operation struct {
	Action         Action
	Name           string
	ActionMetadata map[string]string
}

func NewAddOperation(outbound, inbound, type_, options string) Operation {
	return Operation{
		Action: ADD,
		Name:   outbound,
		ActionMetadata: map[string]string{
			"inbound":        inbound,
			"column_type":    type_,
			"column_options": options,
		},
	}
}
func NewDeleteOperation(outbound string) Operation {
	return Operation{
		Action:         DELETE,
		Name:           outbound,
		ActionMetadata: map[string]string{},
	}
}
func NewRenameOperation(current, new string) Operation {
	return Operation{
		Action: RENAME,
		Name:   current,
		ActionMetadata: map[string]string{
			"new_outbound": new,
		},
	}
}

type RowCopyRequest struct {
	KeyName      string
	TableName    string
	TableVersion int
}

type ManifestRowCopyRequest struct {
	ManifestURL string
	TableName   string
}

type LoadCheckRequest struct {
	ManifestURL string
}

type LoadCheckResponse struct {
	LoadStatus  LoadStatus
	ManifestURL string
}

type ScoopHealthCheck struct {
	RedshiftDBConnError *string
	IngesterDBConnError *string
}

type LoadStatus string

const (
	LoadNotFound   LoadStatus = "load-not-found"
	LoadFailed     LoadStatus = "load-failed"
	LoadInProgress LoadStatus = "load-in-progress"
	LoadComplete   LoadStatus = "load-complete"
)

type ScoopSigner interface {
	GetConfig(io.Reader) (*Config, error)
	GetRowCopyRequest(io.Reader) (*RowCopyRequest, error)
	SignJsonBody(interface{}) ([]byte, error)
	SignBody([]byte) ([]byte, error)
}

type FakeScoopSigner struct{}

type AuthScoopSigner struct {
	TimeSigner *msg_signer.TimeSigner
	Exp        time.Duration
}

var (
	BadVerified error = errors.New("Bad Signature")
)

// For now we are turning off the signer
func GetScoopSigner() ScoopSigner {
	return &FakeScoopSigner{}
}

func (s *AuthScoopSigner) SignJsonBody(o interface{}) ([]byte, error) {
	req, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	return s.SignBody(req)
}

func (s *AuthScoopSigner) SignBody(b []byte) ([]byte, error) {
	return s.TimeSigner.Sign(b), nil
}

func (s *AuthScoopSigner) GetConfig(body io.Reader) (*Config, error) {
	msg, verified := s.TimeSigner.PagedVerify(body, s.Exp)
	if !verified {
		return nil, BadVerified
	}

	c := new(Config)
	err := json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (s *AuthScoopSigner) GetRowCopyRequest(body io.Reader) (*RowCopyRequest, error) {
	msg, verified := s.TimeSigner.PagedVerify(body, s.Exp)
	if !verified {
		return nil, BadVerified
	}

	c := new(RowCopyRequest)
	err := json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (s *FakeScoopSigner) SignJsonBody(o interface{}) ([]byte, error) {
	req, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	return s.SignBody(req)
}

// does nothing
func (s *FakeScoopSigner) SignBody(b []byte) ([]byte, error) {
	return b, nil
}

func (s *FakeScoopSigner) GetConfig(b io.Reader) (*Config, error) {
	c := new(Config)
	msg, err := ioutil.ReadAll(b)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (s *FakeScoopSigner) GetRowCopyRequest(b io.Reader) (*RowCopyRequest, error) {
	c := new(RowCopyRequest)
	msg, err := ioutil.ReadAll(b)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}
<commit_msg>Add new operations<commit_after>package scoop_protocol

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"time"

	"github.com/twitchscience/scoop_protocol/msg_signer"
)

type ColumnDefinition struct {
	InboundName           string
	OutboundName          string
	Transformer           string // this should match one of the types in redshift types
	ColumnCreationOptions string
}

type Config struct {
	EventName string
	Columns   []ColumnDefinition
	Version   int
}

type Action string

const (
	ADD                Action = "add"
	DELETE             Action = "delete"
	RENAME             Action = "rename"
	REQUEST_DROP_EVENT Action = "request_drop_event" // mark a table for manual deletion
	DROP_EVENT         Action = "drop_event"         // table has been dropped
	CANCEL_DROP_EVENT  Action = "cancel_drop_event"  // used to unmark the table for deletion
)

// Operation represents a single change to a schema
type Operation struct {
	Action         Action
	Name           string
	ActionMetadata map[string]string
}

func NewAddOperation(outbound, inbound, type_, options string) Operation {
	return Operation{
		Action: ADD,
		Name:   outbound,
		ActionMetadata: map[string]string{
			"inbound":        inbound,
			"column_type":    type_,
			"column_options": options,
		},
	}
}

func NewDeleteOperation(outbound string) Operation {
	return Operation{
		Action:         DELETE,
		Name:           outbound,
		ActionMetadata: map[string]string{},
	}
}

func NewRenameOperation(current, new string) Operation {
	return Operation{
		Action: RENAME,
		Name:   current,
		ActionMetadata: map[string]string{
			"new_outbound": new,
		},
	}
}

func NewRequestDropEventOperation(reason string) Operation {
	return Operation{
		Action:         REQUEST_DROP_EVENT,
		Name:           "", // nil would be better, but most code can't handle it.
		ActionMetadata: map[string]string{"reason": reason},
	}
}

func NewDropEventOperation(reason string) Operation {
	return Operation{
		Action:         DROP_EVENT,
		Name:           "", // nil would be better, but most code can't handle it.
		ActionMetadata: map[string]string{"reason": reason},
	}
}

func NewCancelDropEventOperation(reason string) Operation {
	return Operation{
		Action:         CANCEL_DROP_EVENT,
		Name:           "", // nil would be better, but most code can't handle it.
		ActionMetadata: map[string]string{},
	}
}

type RowCopyRequest struct {
	KeyName      string
	TableName    string
	TableVersion int
}

type ManifestRowCopyRequest struct {
	ManifestURL string
	TableName   string
}

type LoadCheckRequest struct {
	ManifestURL string
}

type LoadCheckResponse struct {
	LoadStatus  LoadStatus
	ManifestURL string
}

type ScoopHealthCheck struct {
	RedshiftDBConnError *string
	IngesterDBConnError *string
}

type LoadStatus string

const (
	LoadNotFound   LoadStatus = "load-not-found"
	LoadFailed     LoadStatus = "load-failed"
	LoadInProgress LoadStatus = "load-in-progress"
	LoadComplete   LoadStatus = "load-complete"
)

type ScoopSigner interface {
	GetConfig(io.Reader) (*Config, error)
	GetRowCopyRequest(io.Reader) (*RowCopyRequest, error)
	SignJsonBody(interface{}) ([]byte, error)
	SignBody([]byte) ([]byte, error)
}

type FakeScoopSigner struct{}

type AuthScoopSigner struct {
	TimeSigner *msg_signer.TimeSigner
	Exp        time.Duration
}

var (
	BadVerified error = errors.New("Bad Signature")
)

// For now we are turning off the signer
func GetScoopSigner() ScoopSigner {
	return &FakeScoopSigner{}
}

func (s *AuthScoopSigner) SignJsonBody(o interface{}) ([]byte, error) {
	req, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	return s.SignBody(req)
}

func (s *AuthScoopSigner) SignBody(b []byte) ([]byte, error) {
	return s.TimeSigner.Sign(b), nil
}

func (s *AuthScoopSigner) GetConfig(body io.Reader) (*Config, error) {
	msg, verified := s.TimeSigner.PagedVerify(body, s.Exp)
	if !verified {
		return nil, BadVerified
	}

	c := new(Config)
	err := json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (s *AuthScoopSigner) GetRowCopyRequest(body io.Reader) (*RowCopyRequest, error) {
	msg, verified := s.TimeSigner.PagedVerify(body, s.Exp)
	if !verified {
		return nil, BadVerified
	}

	c := new(RowCopyRequest)
	err := json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (s *FakeScoopSigner) SignJsonBody(o interface{}) ([]byte, error) {
	req, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	return s.SignBody(req)
}

// does nothing
func (s *FakeScoopSigner) SignBody(b []byte) ([]byte, error) {
	return b, nil
}

func (s *FakeScoopSigner) GetConfig(b io.Reader) (*Config, error) {
	c := new(Config)
	msg, err := ioutil.ReadAll(b)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (s *FakeScoopSigner) GetRowCopyRequest(b io.Reader) (*RowCopyRequest, error) {
	c := new(RowCopyRequest)
	msg, err := ioutil.ReadAll(b)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(msg, c)
	if err != nil {
		return nil, err
	}

	return c, nil
}
<|endoftext|>


<commit_before>/*
package proto defines a set of structures used to negotiate an update between an
an application (the client) and an equinox update service.
*/
package proto

import "time"

type PatchKind string

const (
	PatchRaw    PatchKind = "none"
	PatchBSDIFF PatchKind = "bsdiff"
)

type Request struct {
	AppID         string `json:"app_id"`
	Channel       string `json:"channel"`
	OS            string `json:"os"`
	Arch          string `json:"arch"`
	GoARM         string `json:"goarm"`
	TargetVersion string `json:"target_version"`

	CurrentVersion string `json:"current_version"`
	CurrentSHA256  string `json:"current_sha256"`
}

type Response struct {
	Available   bool      `json:"available"`
	DownloadURL string    `json:"download_url"`
	Checksum    string    `json:"checksum"`
	Signature   string    `json:"signature"`
	Patch       PatchKind `json:"patch_type"`
	Version     string    `json:"version"`
	Release     Release   `json:"release"`
}

type Release struct {
	Title       string    `json:"title"`
	Version     string    `json:"version"`
	Description string    `json:"description"`
	CreateDate  time.Time `json:"create_date"`
}
<commit_msg>HACK: Make sure JSON scheme is a string<commit_after>/*
package proto defines a set of structures used to negotiate an update between an
an application (the client) and an equinox update service.
*/
package proto

import "time"

type PatchKind string

const (
	PatchRaw    PatchKind = "none"
	PatchBSDIFF PatchKind = "bsdiff"
)

type Request struct {
	AppID         string `json:"app_id"`
	Channel       string `json:"channel"`
	OS            string `json:"os"`
	Arch          string `json:"arch"`
	GoARM         string `json:"goarm"`
	TargetVersion string `json:"target_version"`

	CurrentVersion string `json:"current_version"`
	CurrentSHA256  string `json:"current_sha256"`
}

type Response struct {
	Available   bool      `json:"available"`
	DownloadURL string    `json:"download_url"`
	Checksum    string    `json:"checksum"`
	Signature   string    `json:"signature"`
	Patch       PatchKind `json:"patch_type,string"`
	Version     string    `json:"version"`
	Release     Release   `json:"release"`
}

type Release struct {
	Title       string    `json:"title"`
	Version     string    `json:"version"`
	Description string    `json:"description"`
	CreateDate  time.Time `json:"create_date"`
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Manish R Jain <manishrjain@gmail.com>
 *
 * 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 query

import (
	"fmt"
	"math"

	"github.com/google/flatbuffers/go"
	"github.com/manishrjain/dgraph/posting"
	"github.com/manishrjain/dgraph/posting/types"
	"github.com/manishrjain/dgraph/query/result"
	"github.com/manishrjain/dgraph/uid"
	"github.com/manishrjain/dgraph/x"
)

var log = x.Log("query")

type Mattr struct {
	Attr string
	Msg  *Message

	ResultUids  []byte // Flatbuffer result.Uids
	ResultValue []byte // gob.Encode
}

type Message struct {
	Id    uint64 // Dgraph Id
	Xid   string // External Id
	Attrs []Mattr
}

type Node struct {
	Id  uint64
	Xid string
}

func extract(l *posting.List, uids *[]byte, value *[]byte) error {
	b := flatbuffers.NewBuilder(0)
	var p types.Posting

	llen := l.Length()
	if ok := l.Get(&p, l.Length()-1); ok {
		if p.Uid() == math.MaxUint64 {
			// Contains a value posting, not useful for Uids vector.
			llen -= 1
		}
	}

	result.UidsStartUidVector(b, llen)
	for i := l.Length() - 1; i >= 0; i-- {
		if ok := l.Get(&p, i); !ok {
			return fmt.Errorf("While retrieving posting")
		}
		if p.Uid() == math.MaxUint64 {
			*value = make([]byte, p.ValueLength())
			copy(*value, p.ValueBytes())

		} else {
			b.PrependUint64(p.Uid())
		}
	}
	vend := b.EndVector(llen)

	result.UidsStart(b)
	result.UidsAddUid(b, vend)
	end := result.UidsEnd(b)
	b.Finish(end)

	buf := b.Bytes[b.Head():]
	*uids = make([]byte, len(buf))
	copy(*uids, buf)
	return nil
}

func Run(m *Message) error {
	if len(m.Xid) > 0 {
		u, err := uid.GetOrAssign(m.Xid)
		if err != nil {
			x.Err(log, err).WithField("xid", m.Xid).Error(
				"While GetOrAssign uid from external id")
			return err
		}
		log.WithField("xid", m.Xid).WithField("uid", u).Debug("GetOrAssign")
		m.Id = u
	}

	if m.Id == 0 {
		err := fmt.Errorf("Query internal id is zero")
		x.Err(log, err).Error("Invalid query")
		return err
	}

	for idx := range m.Attrs {
		mattr := &m.Attrs[idx]
		key := posting.Key(m.Id, mattr.Attr)
		pl := posting.Get(key)

		if err := extract(pl, &mattr.ResultUids, &mattr.ResultValue); err != nil {
			x.Err(log, err).WithField("uid", m.Id).WithField("attr", mattr.Attr).
				Error("While extracting data from posting list")
		}
		if mattr.Msg != nil {
			if err := Run(mattr.Msg); err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Target for tomorrow<commit_after>/*
 * Copyright 2015 Manish R Jain <manishrjain@gmail.com>
 *
 * 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 query

import (
	"fmt"
	"math"

	"github.com/google/flatbuffers/go"
	"github.com/manishrjain/dgraph/posting"
	"github.com/manishrjain/dgraph/posting/types"
	"github.com/manishrjain/dgraph/query/result"
	"github.com/manishrjain/dgraph/uid"
	"github.com/manishrjain/dgraph/x"
)

// Aim to get this query working:
// {
//   me {
//     id
//     firstName
//     lastName
//     birthday {
//       month
//       day
//     }
//     friends {
//       name
//     }
//   }
// }

var log = x.Log("query")

type Mattr struct {
	Attr string
	Msg  *Message

	ResultUids  []byte // Flatbuffer result.Uids
	ResultValue []byte // gob.Encode
}

type Message struct {
	Id    uint64 // Dgraph Id
	Xid   string // External Id
	Attrs []Mattr
}

type Node struct {
	Id  uint64
	Xid string
}

func extract(l *posting.List, uids *[]byte, value *[]byte) error {
	b := flatbuffers.NewBuilder(0)
	var p types.Posting

	llen := l.Length()
	if ok := l.Get(&p, l.Length()-1); ok {
		if p.Uid() == math.MaxUint64 {
			// Contains a value posting, not useful for Uids vector.
			llen -= 1
		}
	}

	result.UidsStartUidVector(b, llen)
	for i := l.Length() - 1; i >= 0; i-- {
		if ok := l.Get(&p, i); !ok {
			return fmt.Errorf("While retrieving posting")
		}
		if p.Uid() == math.MaxUint64 {
			*value = make([]byte, p.ValueLength())
			copy(*value, p.ValueBytes())

		} else {
			b.PrependUint64(p.Uid())
		}
	}
	vend := b.EndVector(llen)

	result.UidsStart(b)
	result.UidsAddUid(b, vend)
	end := result.UidsEnd(b)
	b.Finish(end)

	buf := b.Bytes[b.Head():]
	*uids = make([]byte, len(buf))
	copy(*uids, buf)
	return nil
}

func Run(m *Message) error {
	if len(m.Xid) > 0 {
		u, err := uid.GetOrAssign(m.Xid)
		if err != nil {
			x.Err(log, err).WithField("xid", m.Xid).Error(
				"While GetOrAssign uid from external id")
			return err
		}
		log.WithField("xid", m.Xid).WithField("uid", u).Debug("GetOrAssign")
		m.Id = u
	}

	if m.Id == 0 {
		err := fmt.Errorf("Query internal id is zero")
		x.Err(log, err).Error("Invalid query")
		return err
	}

	for idx := range m.Attrs {
		mattr := &m.Attrs[idx]
		key := posting.Key(m.Id, mattr.Attr)
		pl := posting.Get(key)

		if err := extract(pl, &mattr.ResultUids, &mattr.ResultValue); err != nil {
			x.Err(log, err).WithField("uid", m.Id).WithField("attr", mattr.Attr).
				Error("While extracting data from posting list")
		}
		if mattr.Msg != nil {
			if err := Run(mattr.Msg); err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Yahoo Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.

package rdl

const Version = "1.4.10"
<commit_msg>Set version to 1.4.11<commit_after>// Copyright 2015 Yahoo Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.

package rdl

const Version = "1.4.11"
<|endoftext|>


<commit_before>// Copyright 2015 LinkedIn Corp. 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.Copyright [201X] LinkedIn Corp. 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.

package goavro

import (
	"bytes"
	"fmt"
	"testing"
)

func TestRecordRequiresSchema(t *testing.T) {
	_, err := NewRecord()
	checkErrorFatal(t, err, "cannot build record: no schema defined")
}

func TestRecordFieldNames(t *testing.T) {
	someJSONSchema := `{"type":"record","name":"org.foo.Y","fields":[{"type":"int","name":"X"},{"type":"string","name":"W"}]}`
	someRecord, err := NewRecord(RecordSchema(someJSONSchema))
	checkErrorFatal(t, err, nil)
	if someRecord.Name != "org.foo.Y" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecord.Name, "org.foo.Y")
	}
	if someRecord.Fields[0].Name != "org.foo.X" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecord.Fields[0].Name, "org.foo.X")
	}
	if someRecord.Fields[1].Name != "org.foo.W" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecord.Fields[1].Name, "org.foo.W")
	}
}

func TestRecordFieldBailsWithoutName(t *testing.T) {
	schema := make(map[string]interface{})

	schema["type"] = "int"
	_, err := newRecordField(schema)
	checkError(t, err, "ought to have name key")

	schema["name"] = 5
	_, err = newRecordField(schema)
	checkError(t, err, "name ought to be non-empty string")

	schema["name"] = ""
	_, err = newRecordField(schema)
	checkError(t, err, "name ought to be non-empty string")
}

func TestRecordFieldChecksSchema(t *testing.T) {
	var err error
	schema := make(map[string]interface{})

	schema["name"] = ""
	_, err = newRecordField(schema)
	checkError(t, err, "name ought to be non-empty string")

	schema["name"] = "someRecordField"
	_, err = newRecordField(schema)
	checkError(t, err, fmt.Errorf("ought to have type key"))
}

func TestRecordField(t *testing.T) {
	schema := make(map[string]interface{})
	schema["name"] = "someRecordField"
	schema["type"] = "int"
	schema["doc"] = "contans some integer"
	someRecordField, err := newRecordField(schema)
	checkError(t, err, nil)
	if someRecordField.Name != "someRecordField" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecordField.Name, "someRecordField")
	}
}

func TestRecordBailsWithoutName(t *testing.T) {
	recordFields := make([]*recordField, 0)
	{
		schema := make(map[string]interface{})
		schema["name"] = "someRecordField"
		schema["type"] = "int"
		schema["doc"] = "contans some integer"
		someRecordField, err := newRecordField(schema)
		checkErrorFatal(t, err, nil)
		recordFields = append(recordFields, someRecordField)
	}

	schema := make(map[string]interface{})
	schema["fields"] = recordFields

	schema["name"] = 5
	_, err := NewRecord(recordSchemaRaw(schema))
	checkErrorFatal(t, err, "ought to be non-empty string")

	schema["name"] = ""
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, "ought to be non-empty string")
}

func TestRecordBailsWithoutFields(t *testing.T) {
	schema := make(map[string]interface{})

	schema["name"] = "someRecord"
	_, err := NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("record requires one or more fields"))

	schema["fields"] = 5
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("record fields ought to be non-empty array"))

	schema["fields"] = make([]interface{}, 0)
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("record fields ought to be non-empty array"))

	fields := make([]interface{}, 0)
	fields = append(fields, "int")
	schema["fields"] = fields
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("expected: map[string]interface{}; received: string"))
}

func TestRecordFieldUnion(t *testing.T) {
	someJSONSchema := `{"type":"record","name":"Foo","fields":[{"type":["null","string"],"name":"field1"}]}`
	_, err := NewRecord(RecordSchema(someJSONSchema))
	checkError(t, err, nil)
}

func TestRecordGetFieldSchema(t *testing.T) {
	outerSchema := `
{
  "type": "record",
  "name": "TestRecord",
  "fields": [
    {
      "name": "value",
      "type": "int"
    },
    {
      "name": "rec",
      "type": {
        "type": "array",
        "items": {
          "type": "record",
          "name": "TestRecord2",
          "fields": [
            {
              "name": "stringValue",
              "type": "string"
            },
            {
              "name": "intValue",
              "type": "int"
            }
          ]
        }
      }
    }
  ]
}
`
	outerRecord, err := NewRecord(RecordSchema(outerSchema))
	checkErrorFatal(t, err, nil)
	// make sure it bails when no such schema
	_, err = outerRecord.GetFieldSchema("no-such-field")
	checkError(t, err, "no such field: no-such-field")
	// get the inner schema
	schema, err := outerRecord.GetFieldSchema("rec")
	checkErrorFatal(t, err, nil)
	_, ok := schema.(map[string]interface{})
	if !ok {
		t.Errorf("Actual: %#v; Expected: %#v", ok, true)
	}
}

func TestNullField(t *testing.T) {
	someJSONSchema := `{"type":"record","name":"Foo","fields":[{"type":"null","name":"field1"}]}`

	codec, err := NewCodec(someJSONSchema)
	checkError(t, err, nil)

	bb := bytes.NewBufferString("")

	rec, err := NewRecord(RecordSchema(someJSONSchema))
	checkError(t, err, nil)

	err = codec.Encode(bb, rec)
	checkError(t, err, nil)
}
<commit_msg>Update expexted error text for TestRecordGetFieldSchema<commit_after>// Copyright 2015 LinkedIn Corp. 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.Copyright [201X] LinkedIn Corp. 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.

package goavro

import (
	"bytes"
	"fmt"
	"testing"
)

func TestRecordRequiresSchema(t *testing.T) {
	_, err := NewRecord()
	checkErrorFatal(t, err, "cannot build record: no schema defined")
}

func TestRecordFieldNames(t *testing.T) {
	someJSONSchema := `{"type":"record","name":"org.foo.Y","fields":[{"type":"int","name":"X"},{"type":"string","name":"W"}]}`
	someRecord, err := NewRecord(RecordSchema(someJSONSchema))
	checkErrorFatal(t, err, nil)
	if someRecord.Name != "org.foo.Y" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecord.Name, "org.foo.Y")
	}
	if someRecord.Fields[0].Name != "org.foo.X" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecord.Fields[0].Name, "org.foo.X")
	}
	if someRecord.Fields[1].Name != "org.foo.W" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecord.Fields[1].Name, "org.foo.W")
	}
}

func TestRecordFieldBailsWithoutName(t *testing.T) {
	schema := make(map[string]interface{})

	schema["type"] = "int"
	_, err := newRecordField(schema)
	checkError(t, err, "ought to have name key")

	schema["name"] = 5
	_, err = newRecordField(schema)
	checkError(t, err, "name ought to be non-empty string")

	schema["name"] = ""
	_, err = newRecordField(schema)
	checkError(t, err, "name ought to be non-empty string")
}

func TestRecordFieldChecksSchema(t *testing.T) {
	var err error
	schema := make(map[string]interface{})

	schema["name"] = ""
	_, err = newRecordField(schema)
	checkError(t, err, "name ought to be non-empty string")

	schema["name"] = "someRecordField"
	_, err = newRecordField(schema)
	checkError(t, err, fmt.Errorf("ought to have type key"))
}

func TestRecordField(t *testing.T) {
	schema := make(map[string]interface{})
	schema["name"] = "someRecordField"
	schema["type"] = "int"
	schema["doc"] = "contans some integer"
	someRecordField, err := newRecordField(schema)
	checkError(t, err, nil)
	if someRecordField.Name != "someRecordField" {
		t.Errorf("Actual: %#v; Expected: %#v", someRecordField.Name, "someRecordField")
	}
}

func TestRecordBailsWithoutName(t *testing.T) {
	recordFields := make([]*recordField, 0)
	{
		schema := make(map[string]interface{})
		schema["name"] = "someRecordField"
		schema["type"] = "int"
		schema["doc"] = "contans some integer"
		someRecordField, err := newRecordField(schema)
		checkErrorFatal(t, err, nil)
		recordFields = append(recordFields, someRecordField)
	}

	schema := make(map[string]interface{})
	schema["fields"] = recordFields

	schema["name"] = 5
	_, err := NewRecord(recordSchemaRaw(schema))
	checkErrorFatal(t, err, "ought to be non-empty string")

	schema["name"] = ""
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, "ought to be non-empty string")
}

func TestRecordBailsWithoutFields(t *testing.T) {
	schema := make(map[string]interface{})

	schema["name"] = "someRecord"
	_, err := NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("record requires one or more fields"))

	schema["fields"] = 5
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("record fields ought to be non-empty array"))

	schema["fields"] = make([]interface{}, 0)
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("record fields ought to be non-empty array"))

	fields := make([]interface{}, 0)
	fields = append(fields, "int")
	schema["fields"] = fields
	_, err = NewRecord(recordSchemaRaw(schema))
	checkError(t, err, fmt.Errorf("expected: map[string]interface{}; received: string"))
}

func TestRecordFieldUnion(t *testing.T) {
	someJSONSchema := `{"type":"record","name":"Foo","fields":[{"type":["null","string"],"name":"field1"}]}`
	_, err := NewRecord(RecordSchema(someJSONSchema))
	checkError(t, err, nil)
}

func TestRecordGetFieldSchema(t *testing.T) {
	outerSchema := `
{
  "type": "record",
  "name": "TestRecord",
  "fields": [
    {
      "name": "value",
      "type": "int"
    },
    {
      "name": "rec",
      "type": {
        "type": "array",
        "items": {
          "type": "record",
          "name": "TestRecord2",
          "fields": [
            {
              "name": "stringValue",
              "type": "string"
            },
            {
              "name": "intValue",
              "type": "int"
            }
          ]
        }
      }
    }
  ]
}
`
	outerRecord, err := NewRecord(RecordSchema(outerSchema))
	checkErrorFatal(t, err, nil)
	// make sure it bails when no such schema
	_, err = outerRecord.GetFieldSchema("no_such_field")
	checkError(t, err, "no such field: \"no_such_field\"")
	// get the inner schema
	schema, err := outerRecord.GetFieldSchema("rec")
	checkErrorFatal(t, err, nil)
	_, ok := schema.(map[string]interface{})
	if !ok {
		t.Errorf("Actual: %#v; Expected: %#v", ok, true)
	}
}

func TestNullField(t *testing.T) {
	someJSONSchema := `{"type":"record","name":"Foo","fields":[{"type":"null","name":"field1"}]}`

	codec, err := NewCodec(someJSONSchema)
	checkError(t, err, nil)

	bb := bytes.NewBufferString("")

	rec, err := NewRecord(RecordSchema(someJSONSchema))
	checkError(t, err, nil)

	err = codec.Encode(bb, rec)
	checkError(t, err, nil)
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"
	"time"

	"github.com/ava-labs/avalanchego/codec"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/vms/components/avax"
	"github.com/ava-labs/avalanchego/vms/components/verify"

	safemath "github.com/ava-labs/avalanchego/utils/math"
)

var (
	errNilTx                     = errors.New("tx is nil")
	errWeightTooSmall            = errors.New("weight of this validator is too low")
	errWeightTooLarge            = errors.New("weight of this validator is too large")
	errStakeTooShort             = errors.New("staking period is too short")
	errStakeTooLong              = errors.New("staking period is too long")
	errInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee")
	errTooManyShares             = fmt.Errorf("a staker can only require at most %d shares from delegators", PercentDenominator)

	_ UnsignedProposalTx = &UnsignedAddValidatorTx{}
	_ TimedTx            = &UnsignedAddValidatorTx{}
)

// UnsignedAddValidatorTx is an unsigned addValidatorTx
type UnsignedAddValidatorTx struct {
	// Metadata, inputs and outputs
	BaseTx `serialize:"true"`
	// Describes the delegatee
	Validator Validator `serialize:"true" json:"validator"`
	// Where to send staked tokens when done validating
	Stake []*avax.TransferableOutput `serialize:"true" json:"stake"`
	// Where to send staking rewards when done validating
	RewardsOwner verify.Verifiable `serialize:"true" json:"rewardsOwner"`
	// Fee this validator charges delegators as a percentage, times 10,000
	// For example, if this validator has Shares=300,000 then they take 30% of rewards from delegators
	Shares uint32 `serialize:"true" json:"shares"`
}

// StartTime of this validator
func (tx *UnsignedAddValidatorTx) StartTime() time.Time {
	return tx.Validator.StartTime()
}

// EndTime of this validator
func (tx *UnsignedAddValidatorTx) EndTime() time.Time {
	return tx.Validator.EndTime()
}

// Weight of this validator
func (tx *UnsignedAddValidatorTx) Weight() uint64 {
	return tx.Validator.Weight()
}

// Verify return nil iff [tx] is valid
func (tx *UnsignedAddValidatorTx) Verify(
	ctx *snow.Context,
	c codec.Manager,
	minStake uint64,
	maxStake uint64,
	minStakeDuration time.Duration,
	maxStakeDuration time.Duration,
	minDelegationFee uint32,
) error {
	switch {
	case tx == nil:
		return errNilTx
	case tx.syntacticallyVerified: // already passed syntactic verification
		return nil
	case tx.Validator.Wght < minStake: // Ensure validator is staking at least the minimum amount
		return errWeightTooSmall
	case tx.Validator.Wght > maxStake: // Ensure validator isn't staking too much
		return errWeightTooLarge
	case tx.Shares > PercentDenominator: // Ensure delegators shares are in the allowed amount
		return errTooManyShares
	case tx.Shares < minDelegationFee:
		return errInsufficientDelegationFee
	}

	duration := tx.Validator.Duration()
	switch {
	case duration < minStakeDuration: // Ensure staking length is not too short
		return errStakeTooShort
	case duration > maxStakeDuration: // Ensure staking length is not too long
		return errStakeTooLong
	}

	if err := tx.BaseTx.Verify(ctx, c); err != nil {
		return fmt.Errorf("failed to verify BaseTx: %w", err)
	}
	if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil {
		return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
	}

	totalStakeWeight := uint64(0)
	for _, out := range tx.Stake {
		if err := out.Verify(); err != nil {
			return fmt.Errorf("failed to verify output: %w", err)
		}
		newWeight, err := safemath.Add64(totalStakeWeight, out.Output().Amount())
		if err != nil {
			return err
		}
		totalStakeWeight = newWeight
	}

	switch {
	case !avax.IsSortedTransferableOutputs(tx.Stake, Codec):
		return errOutputsNotSorted
	case totalStakeWeight != tx.Validator.Wght:
		return fmt.Errorf("validator weight %d is not equal to total stake weight %d", tx.Validator.Wght, totalStakeWeight)
	}

	// cache that this is valid
	tx.syntacticallyVerified = true
	return nil
}

// SemanticVerify this transaction is valid.
func (tx *UnsignedAddValidatorTx) SemanticVerify(
	vm *VM,
	parentState mutableState,
	stx *Tx,
) (
	versionedState,
	versionedState,
	func() error,
	func() error,
	TxError,
) {
	// Verify the tx is well-formed
	if err := tx.Verify(
		vm.ctx,
		vm.codec,
		vm.MinValidatorStake,
		vm.MaxValidatorStake,
		vm.MinStakeDuration,
		vm.MaxStakeDuration,
		vm.MinDelegationFee,
	); err != nil {
		return nil, nil, nil, nil, permError{err}
	}

	currentStakers := parentState.CurrentStakerChainState()
	pendingStakers := parentState.PendingStakerChainState()

	outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.Stake))
	copy(outs, tx.Outs)
	copy(outs[len(tx.Outs):], tx.Stake)

	if vm.bootstrapped {
		currentTimestamp := parentState.GetTimestamp()
		// Ensure the proposed validator starts after the current time
		if startTime := tx.StartTime(); !currentTimestamp.Before(startTime) {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"validator's start time (%s) at or before current timestamp (%s)",
					startTime,
					currentTimestamp,
				),
			}
		} else if startTime.After(currentTimestamp.Add(maxFutureStartTime)) {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"validator start time (%s) more than two weeks after current chain timestamp (%s)",
					startTime,
					currentTimestamp,
				),
			}
		}

		// Ensure this validator isn't currently a validator.
		_, err := currentStakers.GetValidator(tx.Validator.NodeID)
		if err == nil {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"validator %s is already a primary network validator",
					tx.Validator.NodeID,
				),
			}
		}
		if err != database.ErrNotFound {
			return nil, nil, nil, nil, tempError{
				fmt.Errorf(
					"failed to find whether %s is a validator: %w",
					tx.Validator.NodeID.PrefixedString(constants.NodeIDPrefix),
					err,
				),
			}
		}

		// Ensure this validator isn't about to become a validator.
		_, err = pendingStakers.GetStakerByNodeID(tx.Validator.NodeID)
		if err == nil {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"validator %s is about to become a primary network validator",
					tx.Validator.NodeID,
				),
			}
		}
		if err != database.ErrNotFound {
			return nil, nil, nil, nil, tempError{
				fmt.Errorf(
					"failed to find whether %s is about to become a validator: %w",
					tx.Validator.NodeID,
					err,
				),
			}
		}

		// Verify the flowcheck
		if err := vm.semanticVerifySpend(parentState, tx, tx.Ins, outs, stx.Creds, 0, vm.ctx.AVAXAssetID); err != nil {
			switch err.(type) {
			case permError:
				return nil, nil, nil, nil, permError{
					fmt.Errorf("failed semanticVerifySpend: %w", err),
				}
			default:
				return nil, nil, nil, nil, tempError{
					fmt.Errorf("failed semanticVerifySpend: %w", err),
				}
			}
		}
	}

	// Set up the state if this tx is committed
	newlyPendingStakers := pendingStakers.AddStaker(stx)
	onCommitState := NewVersionedState(parentState, currentStakers, newlyPendingStakers)

	// Consume the UTXOS
	vm.consumeInputs(onCommitState, tx.Ins)
	// Produce the UTXOS
	txID := tx.ID()
	vm.produceOutputs(onCommitState, txID, tx.Outs)

	// Set up the state if this tx is aborted
	onAbortState := NewVersionedState(parentState, currentStakers, pendingStakers)
	// Consume the UTXOS
	vm.consumeInputs(onAbortState, tx.Ins)
	// Produce the UTXOS
	vm.produceOutputs(onAbortState, txID, outs)

	return onCommitState, onAbortState, nil, nil, nil
}

// InitiallyPrefersCommit returns true if the proposed validators start time is
// after the current wall clock time,
func (tx *UnsignedAddValidatorTx) InitiallyPrefersCommit(vm *VM) bool {
	return tx.StartTime().After(vm.clock.Time())
}

/*
// NewAddValidatorTx returns a new NewAddValidatorTx
func (vm *VM) newAddValidatorTx(
	stakeAmt, // Amount the delegator stakes
	startTime, // Unix time they start delegating
	endTime uint64, // Unix time they stop delegating
	nodeID ids.ShortID, // ID of the node we are delegating to
	rewardAddress ids.ShortID, // Address to send reward to, if applicable
	shares uint32, // 10,000 times percentage of reward taken from delegators
	keys []*crypto.PrivateKeySECP256K1R, // Keys providing the staked tokens
	changeAddr ids.ShortID, // Address to send change to, if there is any
) (*Tx, error) {
	ins, unlockedOuts, lockedOuts, signers, err := vm.stake(vm.DB, keys, stakeAmt, 0, changeAddr)
	if err != nil {
		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
	}
	// Create the tx
	utx := &UnsignedAddValidatorTx{
		BaseTx: BaseTx{BaseTx: avax.BaseTx{
			NetworkID:    vm.ctx.NetworkID,
			BlockchainID: vm.ctx.ChainID,
			Ins:          ins,
			Outs:         unlockedOuts,
		}},
		Validator: Validator{
			NodeID: nodeID,
			Start:  startTime,
			End:    endTime,
			Wght:   stakeAmt,
		},
		Stake: lockedOuts,
		RewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 1,
			Addrs:     []ids.ShortID{rewardAddress},
		},
		Shares: shares,
	}
	tx := &Tx{UnsignedTx: utx}
	if err := tx.Sign(vm.codec, signers); err != nil {
		return nil, err
	}
	return tx, utx.Verify(
		vm.ctx,
		vm.codec,
		vm.MinValidatorStake,
		vm.MaxValidatorStake,
		vm.MinStakeDuration,
		vm.MaxStakeDuration,
		vm.MinDelegationFee,
	)
}
*/
<commit_msg>added NodeID- prefix to more error messages<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"
	"time"

	"github.com/ava-labs/avalanchego/codec"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/vms/components/avax"
	"github.com/ava-labs/avalanchego/vms/components/verify"

	safemath "github.com/ava-labs/avalanchego/utils/math"
)

var (
	errNilTx                     = errors.New("tx is nil")
	errWeightTooSmall            = errors.New("weight of this validator is too low")
	errWeightTooLarge            = errors.New("weight of this validator is too large")
	errStakeTooShort             = errors.New("staking period is too short")
	errStakeTooLong              = errors.New("staking period is too long")
	errInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee")
	errTooManyShares             = fmt.Errorf("a staker can only require at most %d shares from delegators", PercentDenominator)

	_ UnsignedProposalTx = &UnsignedAddValidatorTx{}
	_ TimedTx            = &UnsignedAddValidatorTx{}
)

// UnsignedAddValidatorTx is an unsigned addValidatorTx
type UnsignedAddValidatorTx struct {
	// Metadata, inputs and outputs
	BaseTx `serialize:"true"`
	// Describes the delegatee
	Validator Validator `serialize:"true" json:"validator"`
	// Where to send staked tokens when done validating
	Stake []*avax.TransferableOutput `serialize:"true" json:"stake"`
	// Where to send staking rewards when done validating
	RewardsOwner verify.Verifiable `serialize:"true" json:"rewardsOwner"`
	// Fee this validator charges delegators as a percentage, times 10,000
	// For example, if this validator has Shares=300,000 then they take 30% of rewards from delegators
	Shares uint32 `serialize:"true" json:"shares"`
}

// StartTime of this validator
func (tx *UnsignedAddValidatorTx) StartTime() time.Time {
	return tx.Validator.StartTime()
}

// EndTime of this validator
func (tx *UnsignedAddValidatorTx) EndTime() time.Time {
	return tx.Validator.EndTime()
}

// Weight of this validator
func (tx *UnsignedAddValidatorTx) Weight() uint64 {
	return tx.Validator.Weight()
}

// Verify return nil iff [tx] is valid
func (tx *UnsignedAddValidatorTx) Verify(
	ctx *snow.Context,
	c codec.Manager,
	minStake uint64,
	maxStake uint64,
	minStakeDuration time.Duration,
	maxStakeDuration time.Duration,
	minDelegationFee uint32,
) error {
	switch {
	case tx == nil:
		return errNilTx
	case tx.syntacticallyVerified: // already passed syntactic verification
		return nil
	case tx.Validator.Wght < minStake: // Ensure validator is staking at least the minimum amount
		return errWeightTooSmall
	case tx.Validator.Wght > maxStake: // Ensure validator isn't staking too much
		return errWeightTooLarge
	case tx.Shares > PercentDenominator: // Ensure delegators shares are in the allowed amount
		return errTooManyShares
	case tx.Shares < minDelegationFee:
		return errInsufficientDelegationFee
	}

	duration := tx.Validator.Duration()
	switch {
	case duration < minStakeDuration: // Ensure staking length is not too short
		return errStakeTooShort
	case duration > maxStakeDuration: // Ensure staking length is not too long
		return errStakeTooLong
	}

	if err := tx.BaseTx.Verify(ctx, c); err != nil {
		return fmt.Errorf("failed to verify BaseTx: %w", err)
	}
	if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil {
		return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
	}

	totalStakeWeight := uint64(0)
	for _, out := range tx.Stake {
		if err := out.Verify(); err != nil {
			return fmt.Errorf("failed to verify output: %w", err)
		}
		newWeight, err := safemath.Add64(totalStakeWeight, out.Output().Amount())
		if err != nil {
			return err
		}
		totalStakeWeight = newWeight
	}

	switch {
	case !avax.IsSortedTransferableOutputs(tx.Stake, Codec):
		return errOutputsNotSorted
	case totalStakeWeight != tx.Validator.Wght:
		return fmt.Errorf("validator weight %d is not equal to total stake weight %d", tx.Validator.Wght, totalStakeWeight)
	}

	// cache that this is valid
	tx.syntacticallyVerified = true
	return nil
}

// SemanticVerify this transaction is valid.
func (tx *UnsignedAddValidatorTx) SemanticVerify(
	vm *VM,
	parentState mutableState,
	stx *Tx,
) (
	versionedState,
	versionedState,
	func() error,
	func() error,
	TxError,
) {
	// Verify the tx is well-formed
	if err := tx.Verify(
		vm.ctx,
		vm.codec,
		vm.MinValidatorStake,
		vm.MaxValidatorStake,
		vm.MinStakeDuration,
		vm.MaxStakeDuration,
		vm.MinDelegationFee,
	); err != nil {
		return nil, nil, nil, nil, permError{err}
	}

	currentStakers := parentState.CurrentStakerChainState()
	pendingStakers := parentState.PendingStakerChainState()

	outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.Stake))
	copy(outs, tx.Outs)
	copy(outs[len(tx.Outs):], tx.Stake)

	if vm.bootstrapped {
		currentTimestamp := parentState.GetTimestamp()
		// Ensure the proposed validator starts after the current time
		if startTime := tx.StartTime(); !currentTimestamp.Before(startTime) {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"validator's start time (%s) at or before current timestamp (%s)",
					startTime,
					currentTimestamp,
				),
			}
		} else if startTime.After(currentTimestamp.Add(maxFutureStartTime)) {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"validator start time (%s) more than two weeks after current chain timestamp (%s)",
					startTime,
					currentTimestamp,
				),
			}
		}

		// Ensure this validator isn't currently a validator.
		_, err := currentStakers.GetValidator(tx.Validator.NodeID)
		if err == nil {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"%s is already a primary network validator",
					tx.Validator.NodeID.PrefixedString(constants.NodeIDPrefix),
				),
			}
		}
		if err != database.ErrNotFound {
			return nil, nil, nil, nil, tempError{
				fmt.Errorf(
					"failed to find whether %s is a validator: %w",
					tx.Validator.NodeID.PrefixedString(constants.NodeIDPrefix),
					err,
				),
			}
		}

		// Ensure this validator isn't about to become a validator.
		_, err = pendingStakers.GetStakerByNodeID(tx.Validator.NodeID)
		if err == nil {
			return nil, nil, nil, nil, permError{
				fmt.Errorf(
					"%s is about to become a primary network validator",
					tx.Validator.NodeID.PrefixedString(constants.NodeIDPrefix),
				),
			}
		}
		if err != database.ErrNotFound {
			return nil, nil, nil, nil, tempError{
				fmt.Errorf(
					"failed to find whether %s is about to become a validator: %w",
					tx.Validator.NodeID.PrefixedString(constants.NodeIDPrefix),
					err,
				),
			}
		}

		// Verify the flowcheck
		if err := vm.semanticVerifySpend(parentState, tx, tx.Ins, outs, stx.Creds, 0, vm.ctx.AVAXAssetID); err != nil {
			switch err.(type) {
			case permError:
				return nil, nil, nil, nil, permError{
					fmt.Errorf("failed semanticVerifySpend: %w", err),
				}
			default:
				return nil, nil, nil, nil, tempError{
					fmt.Errorf("failed semanticVerifySpend: %w", err),
				}
			}
		}
	}

	// Set up the state if this tx is committed
	newlyPendingStakers := pendingStakers.AddStaker(stx)
	onCommitState := NewVersionedState(parentState, currentStakers, newlyPendingStakers)

	// Consume the UTXOS
	vm.consumeInputs(onCommitState, tx.Ins)
	// Produce the UTXOS
	txID := tx.ID()
	vm.produceOutputs(onCommitState, txID, tx.Outs)

	// Set up the state if this tx is aborted
	onAbortState := NewVersionedState(parentState, currentStakers, pendingStakers)
	// Consume the UTXOS
	vm.consumeInputs(onAbortState, tx.Ins)
	// Produce the UTXOS
	vm.produceOutputs(onAbortState, txID, outs)

	return onCommitState, onAbortState, nil, nil, nil
}

// InitiallyPrefersCommit returns true if the proposed validators start time is
// after the current wall clock time,
func (tx *UnsignedAddValidatorTx) InitiallyPrefersCommit(vm *VM) bool {
	return tx.StartTime().After(vm.clock.Time())
}

/*
// NewAddValidatorTx returns a new NewAddValidatorTx
func (vm *VM) newAddValidatorTx(
	stakeAmt, // Amount the delegator stakes
	startTime, // Unix time they start delegating
	endTime uint64, // Unix time they stop delegating
	nodeID ids.ShortID, // ID of the node we are delegating to
	rewardAddress ids.ShortID, // Address to send reward to, if applicable
	shares uint32, // 10,000 times percentage of reward taken from delegators
	keys []*crypto.PrivateKeySECP256K1R, // Keys providing the staked tokens
	changeAddr ids.ShortID, // Address to send change to, if there is any
) (*Tx, error) {
	ins, unlockedOuts, lockedOuts, signers, err := vm.stake(vm.DB, keys, stakeAmt, 0, changeAddr)
	if err != nil {
		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
	}
	// Create the tx
	utx := &UnsignedAddValidatorTx{
		BaseTx: BaseTx{BaseTx: avax.BaseTx{
			NetworkID:    vm.ctx.NetworkID,
			BlockchainID: vm.ctx.ChainID,
			Ins:          ins,
			Outs:         unlockedOuts,
		}},
		Validator: Validator{
			NodeID: nodeID,
			Start:  startTime,
			End:    endTime,
			Wght:   stakeAmt,
		},
		Stake: lockedOuts,
		RewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 1,
			Addrs:     []ids.ShortID{rewardAddress},
		},
		Shares: shares,
	}
	tx := &Tx{UnsignedTx: utx}
	if err := tx.Sign(vm.codec, signers); err != nil {
		return nil, err
	}
	return tx, utx.Verify(
		vm.ctx,
		vm.codec,
		vm.MinValidatorStake,
		vm.MaxValidatorStake,
		vm.MinStakeDuration,
		vm.MaxStakeDuration,
		vm.MinDelegationFee,
	)
}
*/
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"

	"github.com/ava-labs/gecko/chains/atomic"
	"github.com/ava-labs/gecko/database"
	"github.com/ava-labs/gecko/database/versiondb"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/utils/crypto"
	"github.com/ava-labs/gecko/utils/hashing"
	"github.com/ava-labs/gecko/vms/components/ava"
	"github.com/ava-labs/gecko/vms/components/verify"
	"github.com/ava-labs/gecko/vms/secp256k1fx"

	safemath "github.com/ava-labs/gecko/utils/math"
)

var (
	errNoExportOutputs  = errors.New("no export outputs")
	errOutputsNotSorted = errors.New("outputs not sorted")
	errOverflowExport   = errors.New("overflow when computing export amount + txFee")
)

// UnsignedExportTx is an unsigned ExportTx
type UnsignedExportTx struct {
	BaseTx `serialize:"true"`
	// Outputs that are exported to the X-Chain
	ExportedOutputs []*ava.TransferableOutput `serialize:"true"`
}

// initialize [tx]. Sets [tx.vm], [tx.unsignedBytes], [tx.bytes], [tx.id]
func (tx *UnsignedExportTx) initialize(vm *VM, bytes []byte) error {
	if tx.vm != nil { // already been initialized
		return nil
	}
	tx.vm = vm
	tx.bytes = bytes
	tx.id = ids.NewID(hashing.ComputeHash256Array(bytes))
	var err error
	tx.unsignedBytes, err = Codec.Marshal(interface{}(tx))
	if err != nil {
		return fmt.Errorf("couldn't marshal UnsignedExportTx: %w", err)
	}
	return nil
}

// InputUTXOs returns an empty set
func (tx *UnsignedExportTx) InputUTXOs() ids.Set { return ids.Set{} }

// Verify this transaction is well-formed
func (tx *UnsignedExportTx) Verify() error {
	switch {
	case tx == nil:
		return errNilTx
	case tx.syntacticallyVerified: // already passed syntactic verification
		return nil
	case len(tx.ExportedOutputs) == 0:
		return errNoExportOutputs
	}

	if err := tx.BaseTx.Verify(); err != nil {
		return err
	}

	for _, out := range tx.ExportedOutputs {
		if err := out.Verify(); err != nil {
			return err
		}
	}
	if !ava.IsSortedTransferableOutputs(tx.ExportedOutputs, Codec) {
		return errOutputsNotSorted
	}

	allOuts := make([]*ava.TransferableOutput, len(tx.Outs)+len(tx.ExportedOutputs))
	copy(allOuts, tx.Outs)
	copy(allOuts[len(tx.Outs):], tx.ExportedOutputs)
	if err := syntacticVerifySpend(tx.Ins, allOuts, tx.vm.txFee, 0, tx.vm.avaxAssetID); err != nil {
		return err
	}

	tx.syntacticallyVerified = true
	return nil
}

// SemanticVerify this transaction is valid.
func (tx *UnsignedExportTx) SemanticVerify(db database.Database, creds []verify.Verifiable) TxError {
	if err := tx.Verify(); err != nil {
		return permError{err}
	}
	return tx.vm.semanticVerifySpend(db, tx, tx.Ins, tx.Outs, creds)
}

// Accept this transaction.
func (tx *UnsignedExportTx) Accept(batch database.Batch) error {
	// Produce exported UTXOs
	smDB := tx.vm.Ctx.SharedMemory.GetDatabase(tx.vm.avm)
	defer tx.vm.Ctx.SharedMemory.ReleaseDatabase(tx.vm.avm)

	vsmDB := versiondb.New(smDB)
	state := ava.NewPrefixedState(vsmDB, Codec)

	txID := tx.ID()
	for i, out := range tx.ExportedOutputs {
		if err := state.FundPlatformUTXO(&ava.UTXO{
			UTXOID: ava.UTXOID{
				TxID:        txID,
				OutputIndex: uint32(len(tx.BaseTx.Outs) + i),
			},
			Asset: out.Asset,
			Out:   out.Output(),
		}); err != nil {
			return err
		}
	}

	sharedBatch, err := vsmDB.CommitBatch()
	if err != nil {
		return err
	}
	return atomic.WriteAll(batch, sharedBatch)
}

// Create a new transaction
func (vm *VM) newExportTx(
	amount uint64, // Amount of tokens to export
	to ids.ShortID, // Address of X-Chain recipient
	keys []*crypto.PrivateKeySECP256K1R, // Pay the fee and provide the tokens
) (*AtomicTx, error) {
	toBurn, err := safemath.Add64(amount, vm.txFee)
	if err != nil {
		return nil, errOverflowExport
	}
	ins, outs, signers, err := vm.burn(vm.DB, keys, toBurn, 0)
	if err != nil {
		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
	}

	// Create the transaction
	utx := &UnsignedExportTx{
		BaseTx: BaseTx{
			NetworkID:    vm.Ctx.NetworkID,
			BlockchainID: vm.Ctx.ChainID,
			Ins:          ins,
			Outs:         outs, // Non-exported outputs
		},
		ExportedOutputs: []*ava.TransferableOutput{{ // Exported to X-Chain
			Asset: ava.Asset{ID: vm.avaxAssetID},
			Out: &secp256k1fx.TransferOutput{
				Amt: amount,
				OutputOwners: secp256k1fx.OutputOwners{
					Locktime:  0,
					Threshold: 1,
					Addrs:     []ids.ShortID{to},
				},
			},
		}},
	}
	tx := &AtomicTx{UnsignedAtomicTx: utx}
	if err := vm.signAtomicTx(tx, signers); err != nil {
		return nil, err
	}
	return tx, utx.Verify()
}
<commit_msg>include exported outputs in json repr of UnsignedExportTx<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/ava-labs/gecko/chains/atomic"
	"github.com/ava-labs/gecko/database"
	"github.com/ava-labs/gecko/database/versiondb"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/utils/crypto"
	"github.com/ava-labs/gecko/utils/hashing"
	"github.com/ava-labs/gecko/vms/components/ava"
	"github.com/ava-labs/gecko/vms/components/verify"
	"github.com/ava-labs/gecko/vms/secp256k1fx"

	safemath "github.com/ava-labs/gecko/utils/math"
)

var (
	errNoExportOutputs  = errors.New("no export outputs")
	errOutputsNotSorted = errors.New("outputs not sorted")
	errOverflowExport   = errors.New("overflow when computing export amount + txFee")
)

// UnsignedExportTx is an unsigned ExportTx
type UnsignedExportTx struct {
	BaseTx `serialize:"true"`
	// Outputs that are exported to the X-Chain
	ExportedOutputs []*ava.TransferableOutput `serialize:"true"`
}

// initialize [tx]. Sets [tx.vm], [tx.unsignedBytes], [tx.bytes], [tx.id]
func (tx *UnsignedExportTx) initialize(vm *VM, bytes []byte) error {
	if tx.vm != nil { // already been initialized
		return nil
	}
	tx.vm = vm
	tx.bytes = bytes
	tx.id = ids.NewID(hashing.ComputeHash256Array(bytes))
	var err error
	tx.unsignedBytes, err = Codec.Marshal(interface{}(tx))
	if err != nil {
		return fmt.Errorf("couldn't marshal UnsignedExportTx: %w", err)
	}
	return nil
}

// InputUTXOs returns an empty set
func (tx *UnsignedExportTx) InputUTXOs() ids.Set { return ids.Set{} }

// Verify this transaction is well-formed
func (tx *UnsignedExportTx) Verify() error {
	switch {
	case tx == nil:
		return errNilTx
	case tx.syntacticallyVerified: // already passed syntactic verification
		return nil
	case len(tx.ExportedOutputs) == 0:
		return errNoExportOutputs
	}

	if err := tx.BaseTx.Verify(); err != nil {
		return err
	}

	for _, out := range tx.ExportedOutputs {
		if err := out.Verify(); err != nil {
			return err
		}
	}
	if !ava.IsSortedTransferableOutputs(tx.ExportedOutputs, Codec) {
		return errOutputsNotSorted
	}

	allOuts := make([]*ava.TransferableOutput, len(tx.Outs)+len(tx.ExportedOutputs))
	copy(allOuts, tx.Outs)
	copy(allOuts[len(tx.Outs):], tx.ExportedOutputs)
	if err := syntacticVerifySpend(tx.Ins, allOuts, tx.vm.txFee, 0, tx.vm.avaxAssetID); err != nil {
		return err
	}

	tx.syntacticallyVerified = true
	return nil
}

// SemanticVerify this transaction is valid.
func (tx *UnsignedExportTx) SemanticVerify(db database.Database, creds []verify.Verifiable) TxError {
	if err := tx.Verify(); err != nil {
		return permError{err}
	}
	return tx.vm.semanticVerifySpend(db, tx, tx.Ins, tx.Outs, creds)
}

// Accept this transaction.
func (tx *UnsignedExportTx) Accept(batch database.Batch) error {
	// Produce exported UTXOs
	smDB := tx.vm.Ctx.SharedMemory.GetDatabase(tx.vm.avm)
	defer tx.vm.Ctx.SharedMemory.ReleaseDatabase(tx.vm.avm)

	vsmDB := versiondb.New(smDB)
	state := ava.NewPrefixedState(vsmDB, Codec)

	txID := tx.ID()
	for i, out := range tx.ExportedOutputs {
		if err := state.FundPlatformUTXO(&ava.UTXO{
			UTXOID: ava.UTXOID{
				TxID:        txID,
				OutputIndex: uint32(len(tx.BaseTx.Outs) + i),
			},
			Asset: out.Asset,
			Out:   out.Output(),
		}); err != nil {
			return err
		}
	}

	sharedBatch, err := vsmDB.CommitBatch()
	if err != nil {
		return err
	}
	return atomic.WriteAll(batch, sharedBatch)
}

// MarshalJSON marshals [tx] to JSON
func (tx *UnsignedExportTx) MarshalJSON() ([]byte, error) {
	// Marshal the base tx
	baseTxJSON, err := json.Marshal(tx.BaseTx)
	if err != nil {
		return nil, err
	}
	exportedOutsJSON, err := json.Marshal(tx.ExportedOutputs)
	if err != nil {
		return nil, err
	}
	buffer := bytes.NewBufferString("{")
	buffer.WriteString(fmt.Sprintf("\"baseTx\":%s,", string(baseTxJSON)))
	buffer.WriteString(fmt.Sprintf("\"exportedOutputs\":%s", string(exportedOutsJSON)))
	buffer.WriteString("}")
	return buffer.Bytes(), nil
}

// Create a new transaction
func (vm *VM) newExportTx(
	amount uint64, // Amount of tokens to export
	to ids.ShortID, // Address of X-Chain recipient
	keys []*crypto.PrivateKeySECP256K1R, // Pay the fee and provide the tokens
) (*AtomicTx, error) {
	toBurn, err := safemath.Add64(amount, vm.txFee)
	if err != nil {
		return nil, errOverflowExport
	}
	ins, outs, signers, err := vm.burn(vm.DB, keys, toBurn, 0)
	if err != nil {
		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
	}

	// Create the transaction
	utx := &UnsignedExportTx{
		BaseTx: BaseTx{
			NetworkID:    vm.Ctx.NetworkID,
			BlockchainID: vm.Ctx.ChainID,
			Ins:          ins,
			Outs:         outs, // Non-exported outputs
		},
		ExportedOutputs: []*ava.TransferableOutput{{ // Exported to X-Chain
			Asset: ava.Asset{ID: vm.avaxAssetID},
			Out: &secp256k1fx.TransferOutput{
				Amt: amount,
				OutputOwners: secp256k1fx.OutputOwners{
					Locktime:  0,
					Threshold: 1,
					Addrs:     []ids.ShortID{to},
				},
			},
		}},
	}
	tx := &AtomicTx{UnsignedAtomicTx: utx}
	if err := vm.signAtomicTx(tx, signers); err != nil {
		return nil, err
	}
	return tx, utx.Verify()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"reflect"
	"sort"
)

type tests struct {
	num  int
	in   []int
	want bool
}

var testData = []tests{
	{1, []int{0}, true},
	{2, []int{0, 1}, true},
	{3, []int{0, 0}, true},
	{4, []int{1, 0}, true},
	{5, []int{1, 1}, true},
	{6, []int{1, 2, 3}, true},
	{7, []int{3, 2, 1}, true}, //<------To do still failed
	{8, []int{1, 2, 1}, true},
	{9, []int{2, 1, 1}, true},
	{10, []int{1, 3, 2, 4}, true},
	{11, []int{1, 2, 4, 3}, true},
	{12, []int{1, 4, 3, 2}, false},
	{13, []int{1, 3, 4, 2}, false},
	{14, []int{1, 5, 3, 3, 7}, true},
	{15, []int{1, 5, 3, 3, 3, 3, 3, 3, 3, 3, 7}, true},
	{16, []int{1, 5, 3, 3, 3, 7, 3, 3, 3, 3, 7}, false},
	{17, []int{6, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7}, true},
	{18, []int{1, 3, 3, 3, 4, 3, 3, 3, 3, 7}, true},
	{19, []int{1, 2, 3, 3, 3, 3, 3, 3, 3, 7, 5}, true},
	{20, []int{7, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5}, true},
	{21, []int{2, 3, 3, 7, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5}, true},
	{22, []int{1, 3, 3, 3, 4, 3, 3, 3, 3, 7}, true},
	{23, []int{1, 5, 3, 4, 7}, true},
}

func main() {

	//total := 0
	input := testData
	for _, v := range input {
		//fmt.Println("TEST", v.num)
		if swapSort(v.in) == v.want {
			fmt.Println("TEST", v.num, "TRUE:PASSED")
		} else {
			fmt.Println("TEST", v.num, "FAILLED")
		}
	}
}

func swapSort(list []int) bool {

	if sort.IntsAreSorted(list) {
		//Nothing to do, already sorted
		return true
	}

	//duplicate the sorted list to compare later
	sortedList := make([]int, len(list))
	copy(sortedList, list)
	sort.Ints(sortedList)

	// count any number that are duplicate or duplicates in a flush
	dup := 0

	// while you check if current number is greater in value than next item in list
	// if so check if number that are duplicate or duplicates in a flush
	for i := 0; i < len(list)-1; i++ {
		if list[i] > list[i+1] {
			dup++
			for k := i + 1; k < len(list)-1; k++ {
				//fmt.Println(list[k], "==", list[k+1], "or", list[k]+1, "==", list[k+1], ":A")
				if list[k] == list[k+1] || list[k]+1 == list[k+1] {
					if list[i] == list[k+1] {
						break
					}
					dup++
				} else {
					break
				}
			}
			//store the value to swap
			val := list[i]
			//remove value from list
			list = append(list[:i], list[i+1:]...)
			//insert value at new location
			list = insert(list, dup+i, val)
			// compare lists, if not same more than one swap needed return false
			fmt.Println(list, ":B:")
			return compare(list, sortedList)
		}
	}

	//something went wrong as IntsAreSorted should of returned true
	fmt.Println("ERR?:", list)
	return true

}

func insert(s []int, at int, val int) []int {
	// Make sure there is enough room
	s = append(s, 0)
	// Move all elements of s up one slot
	copy(s[at+1:], s[at:])
	// Insert the new element at the now free position
	s[at] = val
	//return the swapped element slice
	return s
}

func compare(a []int, b []int) bool {
	return reflect.DeepEqual(a, b)
}
<commit_msg>one test case left to fix Signed-off-by: michael_bow <mbow@pylit.io><commit_after>package main

import (
	"fmt"
	"reflect"
	"sort"
)

type tests struct {
	in   []int
	want bool
}

var testData = []tests{
	{[]int{1}, true},        //0
	{[]int{1, 1}, true},     //1
	{[]int{1, 2}, true},     //2
	{[]int{2, 1}, true},     //3
	{[]int{2, 2}, true},     //4
	{[]int{1, 1, 1}, true},  //5
	{[]int{1, 1, 2}, true},  //6
	{[]int{1, 2, 1}, true},  //7
	{[]int{1, 2, 2}, true},  //8
	{[]int{1, 2, 3}, true},  //9
	{[]int{1, 3, 1}, true},  //10
	{[]int{1, 3, 2}, true},  //11
	{[]int{1, 3, 3}, true},  //12
	{[]int{2, 1, 1}, true},  //13
	{[]int{2, 1, 2}, true},  //14
	{[]int{2, 1, 3}, true},  //15
	{[]int{2, 2, 1}, true},  //16
	{[]int{2, 2, 3}, true},  //17
	{[]int{2, 3, 1}, false}, //18
	{[]int{2, 3, 2}, true},  //19
	{[]int{2, 3, 3}, true},  //20
	{[]int{3, 1, 1}, true},  //21
	{[]int{3, 1, 2}, false}, //22
	{[]int{3, 1, 3}, true},  //23
	{[]int{3, 2, 1}, true},  //24

	{[]int{2, 3, 2, 4}, true},  //25
	{[]int{1, 2, 4, 3}, true},  //26
	{[]int{1, 4, 3, 2}, true},  //27
	{[]int{1, 3, 4, 2}, false}, //28

	{[]int{1, 5, 3, 3, 7}, true},  //29
	{[]int{1, 3, 5, 3, 4}, false}, //30

	{[]int{1, 5, 3, 3, 3, 3, 3, 3, 3, 3, 7}, true},                                                           //31
	{[]int{1, 5, 3, 3, 3, 7, 3, 3, 3, 3, 7}, false},                                                          //32
	{[]int{6, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7}, false},                                              //33
	{[]int{1, 3, 3, 3, 4, 3, 3, 3, 3, 7}, true},                                                              //34
	{[]int{1, 2, 3, 3, 3, 3, 3, 3, 3, 7, 5}, true},                                                           //35
	{[]int{7, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5}, false},                                                 //36
	{[]int{2, 3, 3, 7, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5}, false},                                              //37
	{[]int{1, 3, 3, 3, 4, 3, 3, 3, 3, 7}, true},                                                              //38
	{[]int{1, 5, 3, 4, 7}, false},                                                                            //39
	{[]int{3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1}, true},                                //40
	{[]int{1, 1, 1, 3, 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 3, 3, 3}, false}, //41
	{[]int{1, 1, 1, 3, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 3, 3, 3}, false}, //42

	{[]int{3, 1, 1, 2, 2, 3, 3, 1}, true},                                                   //43 f
	{[]int{1, 1, 3, 1, 1, 2, 2, 3, 3, 1}, true},                                             //44 f
	{[]int{1, 1, 1, 30, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 12, 24, 1, 50, 60}, true}, //45 f
	{[]int{1, 1, 3, 1, 1, 2, 2, 3, 3, 1}, true},                                             //46 f
	{[]int{3, 6, 3, 3, 3, 4, 4, 4, 5, 5, 5, 3, 6, 6, 7}, true},                              //47 f
}

func main() {

	input := testData
	failed := 0
	totaltests := 0

	for i, v := range input {
		//fmt.Println("")
		if swapNcompare(v.in) == v.want {
			fmt.Println("TEST", i, "PASS")
		} else {

			fmt.Println("TEST", i, "!!FAIL!!")
			failed++
		}
		totaltests++
	}
	fmt.Println("PASS:", totaltests-failed, "|", "Fail:", failed)
}

func swapNcompare(list []int) bool {

	if sort.IntsAreSorted(list) {
		//Nothing to do, already sorted
		return true
	}

	//duplicate the sorted list to compare later
	sortedList := make([]int, len(list))
	copy(sortedList, list)
	sort.Ints(sortedList)

	oneSwapSort(list)

	// compare if the same only one swap needed
	return compare(list, sortedList)

}

func compare(a []int, b []int) bool {
	return reflect.DeepEqual(a, b)
}

func oneSwapSort(a []int) {
	// pass in the slice and the current index, desending
	for i := len(a) - 1; i >= 0; i-- {
		pos := i
		change := false

		b := make([]int, len(a))
		copy(b, a)

		for pos > 0 && b[pos-1] == b[pos] {
			pos--
		}

		for pos > 0 && b[pos-1] > b[pos] {
			b[pos-1], b[pos] = b[pos], b[pos-1]
			change = true
			pos--

		}

		if change {
			//fmt.Println("swap:", upperBound, a[upperBound], "with", pos, a[pos])
			a[i], a[pos] = a[pos], a[i]
			return
		}
	}

}
<|endoftext|>