<commit_before>package scipipe

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"os"
	t "testing"
	"time"
)

func TestShellHasInOutPorts(t *t.T) {
	tt := Sh("echo {i:in1} {o:out1}")
	tt.OutPathFuncs["out1"] = func() string {
		return fmt.Sprint(tt.InPaths["in1"], ".bar")
	}

	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
	go func() { tt.InPorts["in1"] <- NewFileTarget("foo.txt") }()
	go tt.Run()
	<-tt.OutPorts["out1"]

	assert.NotNil(t, tt.InPorts["in1"], "InPorts are nil!")
	assert.NotNil(t, tt.OutPorts["out1"], "OutPorts are nil!")

	cleanFiles("foo.txt", "foo.txt.bar")
}

func TestShellCloseOutPortOnInPortClose(t *t.T) {
	fooTask := Sh("echo foo > {o:out1}")
	fooTask.OutPathFuncs["out1"] = func() string {
		return "foo.txt"
	}

	barReplacer := Sh("sed 's/foo/bar/g' {i:foo} > {o:bar}")
	barReplacer.OutPathFuncs["bar"] = func() string {
		return barReplacer.GetInPath("foo") + ".bar"
	}

	barReplacer.InPorts["foo"] = fooTask.OutPorts["out1"]

	go fooTask.Run()
	go barReplacer.Run()
	<-barReplacer.OutPorts["bar"]

	// Assert no more content coming on channels
	assert.Nil(t, <-fooTask.OutPorts["out1"])
	assert.Nil(t, <-barReplacer.OutPorts["bar"])

	_, fooErr := os.Stat("foo.txt")
	assert.Nil(t, fooErr)
	_, barErr := os.Stat("foo.txt.bar")
	assert.Nil(t, barErr)

	cleanFiles("foo.txt", "foo.txt.bar")
}

func TestReplacePlaceholdersInCmd(t *t.T) {
	rawCmd := "echo {i:in1} > {o:out1}"
	tt := Sh(rawCmd)
	tt.OutPathFuncs["out1"] = func() string {
		return fmt.Sprint(tt.InPaths["in1"], ".bar")
	}

	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
	ift := NewFileTarget("foo.txt")
	go func() {
		defer close(tt.InPorts["in1"])
		tt.InPorts["in1"] <- ift
	}()

	// Assert inport is still open after first read
	inPortsClosed := tt.receiveInputs()
	assert.Equal(t, false, inPortsClosed)

	// Assert inport is closed after second read
	inPortsClosed = tt.receiveInputs()
	assert.Equal(t, true, inPortsClosed)

	// Assert InPath is correct
	assert.Equal(t, "foo.txt", tt.InPaths["in1"], "foo.txt")

	// Assert placeholders are correctly replaced in command
	cmd := tt.formatCommand(rawCmd)
	assert.EqualValues(t, "echo foo.txt > foo.txt.bar", cmd)
}

func TestParameterCommand(t *t.T) {
	cmb := NewCombinatoricsTask()

	// An abc file printer
	abc := Sh("echo {p:a} {p:b} {p:c} > {o:out}")
	abc.OutPathFuncs["out"] = func() string {
		return fmt.Sprintf(
			"%s_%s_%s.txt",
			abc.Params["a"],
			abc.Params["b"],
			abc.Params["c"],
		)
	}

	// A printer task
	prt := Sh("cat {i:in} >> /tmp/log.txt; rm {i:in}")

	// Connection info
	abc.ParamPorts["a"] = cmb.A
	abc.ParamPorts["b"] = cmb.B
	abc.ParamPorts["c"] = cmb.C
	prt.InPorts["in"] = abc.OutPorts["out"]

	pl := NewPipeline()
	pl.AddTasks(cmb, abc, prt)
	pl.Run()

	// Run tests
	_, err := os.Stat("/tmp/log.txt")
	assert.Nil(t, err)

	cleanFiles("/tmp/log.txt")
}

func TestTaskWithoutInputsOutputs(t *t.T) {
	f := "/tmp/hej.txt"
	tsk := Sh("echo hej > " + f)
	tsk.Run()
	_, err := os.Stat(f)
	assert.Nil(t, err)
	cleanFiles(f)
}

func TestDontOverWriteExistingOutputs(t *t.T) {
	f := "/tmp/hej.txt"

	// Assert file does not exist before running
	_, e1 := os.Stat(f)
	assert.NotNil(t, e1)

	// Run pipeline a first time
	tsk := Sh("echo hej > {o:hej}")
	tsk.OutPathFuncs["hej"] = func() string { return f }
	prt := Sh("echo {i:in} Done!")
	prt.InPorts["in"] = tsk.OutPorts["hej"]
	pl := NewPipeline()
	pl.AddTasks(tsk, prt)
	pl.Run()

	// Assert file DO exist after running
	fiBef, e2 := os.Stat(f)
	assert.Nil(t, e2)

	// Get modified time before
	mtBef := fiBef.ModTime()

	// Make sure some time has passed before the second write
	time.Sleep(1 * time.Millisecond)

	// Run again with different output
	tsk = Sh("echo hej > {o:hej}")
	tsk.OutPathFuncs["hej"] = func() string { return f }
	prt.InPorts["in"] = tsk.OutPorts["hej"]
	pl = NewPipeline()
	pl.AddTasks(tsk, prt)
	pl.Run()

	// Assert exists
	fiAft, e3 := os.Stat(f)
	assert.Nil(t, e3)

	// Get modified time AFTER second run
	mtAft := fiAft.ModTime()
	fmt.Printf("*** Time after: %s\n", mtAft)

	// Assert file is not modified!
	assert.EqualValues(t, mtBef, mtAft)

	cleanFiles(f)
}

// Helper functions

func cleanFiles(fileNames ...string) {
	for _, fileName := range fileNames {
		if _, err := os.Stat(fileName); err == nil {
			fmt.Println("Removing file", fileName)
			os.Remove(fileName)
			fmt.Println("Successfully removed file", fileName)
		}
	}
}

// Helper tasks

type CombinatoricsTask struct {
	BaseTask
	A chan string
	B chan string
	C chan string
}

func NewCombinatoricsTask() *CombinatoricsTask {
	return &CombinatoricsTask{
		A: make(chan string, BUFSIZE),
		B: make(chan string, BUFSIZE),
		C: make(chan string, BUFSIZE),
	}
}

func (proc *CombinatoricsTask) Run() {
	defer close(proc.A)
	defer close(proc.B)
	defer close(proc.C)

	for _, a := range SS("a1", "a2", "a3") {
		for _, b := range SS("b1", "b2", "b3") {
			for _, c := range SS("c1", "c2", "c3") {
				proc.A <- a
				proc.B <- b
				proc.C <- c
			}
		}
	}
}
<commit_msg>Removing excessive printouts<commit_after>package scipipe

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"os"
	t "testing"
	"time"
)

func TestShellHasInOutPorts(t *t.T) {
	tt := Sh("echo {i:in1} {o:out1}")
	tt.OutPathFuncs["out1"] = func() string {
		return fmt.Sprint(tt.InPaths["in1"], ".bar")
	}

	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
	go func() { tt.InPorts["in1"] <- NewFileTarget("foo.txt") }()
	go tt.Run()
	<-tt.OutPorts["out1"]

	assert.NotNil(t, tt.InPorts["in1"], "InPorts are nil!")
	assert.NotNil(t, tt.OutPorts["out1"], "OutPorts are nil!")

	cleanFiles("foo.txt", "foo.txt.bar")
}

func TestShellCloseOutPortOnInPortClose(t *t.T) {
	fooTask := Sh("echo foo > {o:out1}")
	fooTask.OutPathFuncs["out1"] = func() string {
		return "foo.txt"
	}

	barReplacer := Sh("sed 's/foo/bar/g' {i:foo} > {o:bar}")
	barReplacer.OutPathFuncs["bar"] = func() string {
		return barReplacer.GetInPath("foo") + ".bar"
	}

	barReplacer.InPorts["foo"] = fooTask.OutPorts["out1"]

	go fooTask.Run()
	go barReplacer.Run()
	<-barReplacer.OutPorts["bar"]

	// Assert no more content coming on channels
	assert.Nil(t, <-fooTask.OutPorts["out1"])
	assert.Nil(t, <-barReplacer.OutPorts["bar"])

	_, fooErr := os.Stat("foo.txt")
	assert.Nil(t, fooErr)
	_, barErr := os.Stat("foo.txt.bar")
	assert.Nil(t, barErr)

	cleanFiles("foo.txt", "foo.txt.bar")
}

func TestReplacePlaceholdersInCmd(t *t.T) {
	rawCmd := "echo {i:in1} > {o:out1}"
	tt := Sh(rawCmd)
	tt.OutPathFuncs["out1"] = func() string {
		return fmt.Sprint(tt.InPaths["in1"], ".bar")
	}

	tt.InPorts["in1"] = make(chan *FileTarget, BUFSIZE)
	ift := NewFileTarget("foo.txt")
	go func() {
		defer close(tt.InPorts["in1"])
		tt.InPorts["in1"] <- ift
	}()

	// Assert inport is still open after first read
	inPortsClosed := tt.receiveInputs()
	assert.Equal(t, false, inPortsClosed)

	// Assert inport is closed after second read
	inPortsClosed = tt.receiveInputs()
	assert.Equal(t, true, inPortsClosed)

	// Assert InPath is correct
	assert.Equal(t, "foo.txt", tt.InPaths["in1"], "foo.txt")

	// Assert placeholders are correctly replaced in command
	cmd := tt.formatCommand(rawCmd)
	assert.EqualValues(t, "echo foo.txt > foo.txt.bar", cmd)
}

func TestParameterCommand(t *t.T) {
	cmb := NewCombinatoricsTask()

	// An abc file printer
	abc := Sh("echo {p:a} {p:b} {p:c} > {o:out}")
	abc.OutPathFuncs["out"] = func() string {
		return fmt.Sprintf(
			"%s_%s_%s.txt",
			abc.Params["a"],
			abc.Params["b"],
			abc.Params["c"],
		)
	}

	// A printer task
	prt := Sh("cat {i:in} >> /tmp/log.txt; rm {i:in}")

	// Connection info
	abc.ParamPorts["a"] = cmb.A
	abc.ParamPorts["b"] = cmb.B
	abc.ParamPorts["c"] = cmb.C
	prt.InPorts["in"] = abc.OutPorts["out"]

	pl := NewPipeline()
	pl.AddTasks(cmb, abc, prt)
	pl.Run()

	// Run tests
	_, err := os.Stat("/tmp/log.txt")
	assert.Nil(t, err)

	cleanFiles("/tmp/log.txt")
}

func TestTaskWithoutInputsOutputs(t *t.T) {
	f := "/tmp/hej.txt"
	tsk := Sh("echo hej > " + f)
	tsk.Run()
	_, err := os.Stat(f)
	assert.Nil(t, err)
	cleanFiles(f)
}

func TestDontOverWriteExistingOutputs(t *t.T) {
	f := "/tmp/hej.txt"

	// Assert file does not exist before running
	_, e1 := os.Stat(f)
	assert.NotNil(t, e1)

	// Run pipeline a first time
	tsk := Sh("echo hej > {o:hej}")
	tsk.OutPathFuncs["hej"] = func() string { return f }
	prt := Sh("echo {i:in} Done!")
	prt.InPorts["in"] = tsk.OutPorts["hej"]
	pl := NewPipeline()
	pl.AddTasks(tsk, prt)
	pl.Run()

	// Assert file DO exist after running
	fiBef, e2 := os.Stat(f)
	assert.Nil(t, e2)

	// Get modified time before
	mtBef := fiBef.ModTime()

	// Make sure some time has passed before the second write
	time.Sleep(1 * time.Millisecond)

	// Run again with different output
	tsk = Sh("echo hej > {o:hej}")
	tsk.OutPathFuncs["hej"] = func() string { return f }
	prt.InPorts["in"] = tsk.OutPorts["hej"]
	pl = NewPipeline()
	pl.AddTasks(tsk, prt)
	pl.Run()

	// Assert exists
	fiAft, e3 := os.Stat(f)
	assert.Nil(t, e3)

	// Get modified time AFTER second run
	mtAft := fiAft.ModTime()

	// Assert file is not modified!
	assert.EqualValues(t, mtBef, mtAft)

	cleanFiles(f)
}

// Helper functions

func cleanFiles(fileNames ...string) {
	for _, fileName := range fileNames {
		if _, err := os.Stat(fileName); err == nil {
			os.Remove(fileName)
			fmt.Println("Successfully removed file", fileName)
		}
	}
}

// Helper tasks

type CombinatoricsTask struct {
	BaseTask
	A chan string
	B chan string
	C chan string
}

func NewCombinatoricsTask() *CombinatoricsTask {
	return &CombinatoricsTask{
		A: make(chan string, BUFSIZE),
		B: make(chan string, BUFSIZE),
		C: make(chan string, BUFSIZE),
	}
}

func (proc *CombinatoricsTask) Run() {
	defer close(proc.A)
	defer close(proc.B)
	defer close(proc.C)

	for _, a := range SS("a1", "a2", "a3") {
		for _, b := range SS("b1", "b2", "b3") {
			for _, c := range SS("c1", "c2", "c3") {
				proc.A <- a
				proc.B <- b
				proc.C <- c
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 John Beil.
// Use of this source code is governed by the MIT License.
// The MIT license that can be found in the LICENSE file.

// BuoyBot 1.1
// Obtains latest observation for NBDC Station 46026
// Tweets observation from @SFBuoy
// Each line contains 19 data points
// Headers are in the first two lines
// Latest data is in the third line
// Other lines are not needed

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/ChimeraCoder/anaconda"
)

// First two rows of text file, fixed width delimited, used for debugging
const header = "#YY  MM DD hh mm WDIR WSPD GST  WVHT   DPD   APD MWD   PRES  ATMP  WTMP  DEWP  VIS PTDY  TIDE\n#yr  mo dy hr mn degT m/s  m/s     m   sec   sec degT   hPa  degC  degC  degC  nmi  hPa    ft"

// struct to store observation data
type BuoyData struct {
	Date                  string
	WindDirection         string
	WindSpeed             float64
	SignificantWaveHeight float64
	DominantWavePeriod    int
	AveragePeriod         float64
	MeanWaveDirection     string
	AirTemperature        float64
	WaterTemperature      float64
}

// struct to store credentials
type Config struct {
	UserName       string `json:"UserName"`
	ConsumerKey    string `json:"ConsumerKey"`
	ConsumerSecret string `json:"ConsumerSecret"`
	Token          string `json:"Token"`
	TokenSecret    string `json:"TokenSecret"`
}

func main() {
	fmt.Println("Starting BuoyBot...")

	config := Config{}
	loadConfig(&config)

	// var data string
	var noaaUrl string = "http://www.ndbc.noaa.gov/data/realtime2/46026.txt"

	var api *anaconda.TwitterApi
	api = anaconda.NewTwitterApi(config.Token, config.TokenSecret)
	anaconda.SetConsumerKey(config.ConsumerKey)
	anaconda.SetConsumerSecret(config.ConsumerSecret)

	c := time.Tick(10800 * time.Second) // post update every three hours
	for _ = range c {

		body := getDataFromURL(noaaUrl)
		output := parseData(body)

		//  create go routine and post update every 6 hours
		//  60 sec * 60 min * 6 hours = 21600 seconds
		// go updateAtInterval(21600, url, config)
		// select {} // this will cause the program to run forever

		// print raw data for most recent observation
		// fmt.Println(header)
		// fmt.Println(data)
		fmt.Println(output)

		// search - use for debugging rather than tweeting
		// searchResult, _ := api.GetSearch("buoybot", nil)
		// for _, tweet := range searchResult.Statuses {
		// 	fmt.Println(tweet.Text)
		// }

		// post tweet
		tweet, err := api.PostTweet(output, nil)
		if err != nil {
			fmt.Println("update error:", err)
		} else {
			fmt.Println(tweet.Text)
		}

	}

}

func getDataFromURL(url string) (body []byte) {
	fmt.Println("Fetching data...")
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("Error fetching data:", err)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("ioutil error:", err)
	}
	fmt.Println("Status:", resp.Status)
	return
}

// load config
func loadConfig(config *Config) {
	// Get config
	file, _ := os.Open("config.json")
	decoder := json.NewDecoder(file)
	err := decoder.Decode(&config)
	if err != nil {
		fmt.Println("error loading config.json:", err)
	}
}

// process latest observation
func parseData(d []byte) string {
	var data string = string(d[188:281])
	// convert most recent observation into array of strings
	datafield := strings.Fields(data)

	// convert wave height from meters to feet
	waveheightmeters, _ := strconv.ParseFloat(datafield[8], 64)
	waveheightfeet := waveheightmeters * 3.28084

	// convert wave direction from degrees to cardinal
	wavedegrees, _ := strconv.ParseInt(datafield[11], 0, 64)
	wavecardinal := direction(wavedegrees)

	// convert wind speed from m/s to mph
	windspeedms, _ := strconv.ParseFloat((datafield[6]), 64)
	windspeedmph := windspeedms / 0.44704

	// convert wind direction from degrees to cardinal
	winddegrees, _ := strconv.ParseInt(datafield[5], 0, 64)
	windcardinal := direction(winddegrees)

	// convert air temp from C to F
	airtempC, _ := strconv.ParseFloat(datafield[13], 64)
	airtempF := airtempC*9/5 + 32
	airtempF = RoundPlus(airtempF, 1)

	// convert water temp from C to F
	watertempC, _ := strconv.ParseFloat(datafield[14], 64)
	watertempF := watertempC*9/5 + 32
	watertempF = RoundPlus(watertempF, 1)

	// process date/time and convert to PST
	rawtime := strings.Join(datafield[0:5], " ")
	t, err := time.Parse("2006 01 02 15 04", rawtime)
	if err != nil {
		fmt.Println(err)
	}
	loc, err := time.LoadLocation("America/Los_Angeles")
	if err != nil {
		fmt.Println(err)
	}
	t = t.In(loc)
	// fmt.Println(t.Format(time.RFC822))

	// concatenate data to output and print to console
	output := fmt.Sprint("\nSF Buoy at ", t.Format(time.RFC822), "\nSwell: ", strconv.FormatFloat(float64(waveheightfeet), 'f', 1, 64), "ft at ", datafield[9], " sec from ", wavecardinal, "\nWind: ", strconv.FormatFloat(float64(windspeedmph), 'f', 0, 64), "mph from ", windcardinal, "\nTemp: Air ", airtempF, "F / Water: ", watertempF, "F")
	// fmt.Println(output)
	return output
}

// given degrees returns cardinal direction
func direction(deg int64) string {
	switch {
	case deg < 0:
		return "ERROR - DEGREE LESS THAN ZERO"
	case deg <= 11:
		return "N"
	case deg <= 34:
		return "NNE"
	case deg <= 56:
		return "NE"
	case deg <= 79:
		return "ENE"
	case deg <= 101:
		return "E"
	case deg <= 124:
		return "ESE"
	case deg <= 146:
		return "SE"
	case deg <= 169:
		return "SSE"
	case deg <= 191:
		return "S"
	case deg <= 214:
		return "SSW"
	case deg <= 236:
		return "SW"
	case deg <= 259:
		return "WSW"
	case deg <= 281:
		return "W"
	case deg <= 304:
		return "WNW"
	case deg <= 326:
		return "NW"
	case deg <= 349:
		return "NNW"
	case deg <= 360:
		return "N"
	default:
		return "ERROR - DEGREE GREATER THAN 360"
	}
}

// round input to nearest integer
func Round(f float64) float64 {
	return math.Floor(f + .5)
}

// round input to defined number of decimals
func RoundPlus(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return Round(f*shift) / shift
}
<commit_msg>created searchAtInterval and tweetAtInterval functions. ticker now runs on goroutine.<commit_after>// Copyright (c) 2015 John Beil.
// Use of this source code is governed by the MIT License.
// The MIT license that can be found in the LICENSE file.

// BuoyBot 1.1
// Obtains latest observation for NBDC Station 46026
// Tweets observation from @SFBuoy
// Each line contains 19 data points
// Headers are in the first two lines
// Latest data is in the third line
// Other lines are not needed

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/ChimeraCoder/anaconda"
)

// First two rows of text file, fixed width delimited, used for debugging
const header = "#YY  MM DD hh mm WDIR WSPD GST  WVHT   DPD   APD MWD   PRES  ATMP  WTMP  DEWP  VIS PTDY  TIDE\n#yr  mo dy hr mn degT m/s  m/s     m   sec   sec degT   hPa  degC  degC  degC  nmi  hPa    ft"

const noaaUrl = "http://www.ndbc.noaa.gov/data/realtime2/46026.txt"

// struct to store observation data
type BuoyData struct {
	Date                  string
	WindDirection         string
	WindSpeed             float64
	SignificantWaveHeight float64
	DominantWavePeriod    int
	AveragePeriod         float64
	MeanWaveDirection     string
	AirTemperature        float64
	WaterTemperature      float64
}

// struct to store credentials
type Config struct {
	UserName       string `json:"UserName"`
	ConsumerKey    string `json:"ConsumerKey"`
	ConsumerSecret string `json:"ConsumerSecret"`
	Token          string `json:"Token"`
	TokenSecret    string `json:"TokenSecret"`
}

func main() {
	fmt.Println("Starting BuoyBot...")

	config := Config{}
	loadConfig(&config)

	go tweetAtInterval(10800, config)

	// use search for debugging so tweets are not posted
	// go searchAtInterval(30, "buoybot", config)

	select {} // this will cause the program to run forever

}

func searchAtInterval(n time.Duration, query string, config Config) {
	var api *anaconda.TwitterApi
	api = anaconda.NewTwitterApi(config.Token, config.TokenSecret)
	anaconda.SetConsumerKey(config.ConsumerKey)
	anaconda.SetConsumerSecret(config.ConsumerSecret)
	for _ = range time.Tick(n * time.Second) {
		// t := time.Now()

		searchResult, _ := api.GetSearch(query, nil)
		for _, tweet := range searchResult.Statuses {
			fmt.Println(tweet.Text)
		}
	}
}

func tweetAtInterval(n time.Duration, config Config) {
	var api *anaconda.TwitterApi
	api = anaconda.NewTwitterApi(config.Token, config.TokenSecret)
	anaconda.SetConsumerKey(config.ConsumerKey)
	anaconda.SetConsumerSecret(config.ConsumerSecret)
	for _ = range time.Tick(n * time.Second) {

		observation := getDataFromURL(noaaUrl)
		output := parseData(observation)

		tweet, err := api.PostTweet(output, nil)
		if err != nil {
			fmt.Println("update error:", err)
		} else {
			fmt.Println(tweet.Text)
		}
	}
}

func getDataFromURL(url string) (body []byte) {
	fmt.Println("Fetching data...")
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("Error fetching data:", err)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("ioutil error:", err)
	}
	fmt.Println("Status:", resp.Status)
	return
}

// load config
func loadConfig(config *Config) {
	// Get config
	file, _ := os.Open("config.json")
	decoder := json.NewDecoder(file)
	err := decoder.Decode(&config)
	if err != nil {
		fmt.Println("error loading config.json:", err)
	}
}

// process latest observation
func parseData(d []byte) string {
	var data string = string(d[188:281])
	// convert most recent observation into array of strings
	datafield := strings.Fields(data)

	// convert wave height from meters to feet
	waveheightmeters, _ := strconv.ParseFloat(datafield[8], 64)
	waveheightfeet := waveheightmeters * 3.28084

	// convert wave direction from degrees to cardinal
	wavedegrees, _ := strconv.ParseInt(datafield[11], 0, 64)
	wavecardinal := direction(wavedegrees)

	// convert wind speed from m/s to mph
	windspeedms, _ := strconv.ParseFloat((datafield[6]), 64)
	windspeedmph := windspeedms / 0.44704

	// convert wind direction from degrees to cardinal
	winddegrees, _ := strconv.ParseInt(datafield[5], 0, 64)
	windcardinal := direction(winddegrees)

	// convert air temp from C to F
	airtempC, _ := strconv.ParseFloat(datafield[13], 64)
	airtempF := airtempC*9/5 + 32
	airtempF = RoundPlus(airtempF, 1)

	// convert water temp from C to F
	watertempC, _ := strconv.ParseFloat(datafield[14], 64)
	watertempF := watertempC*9/5 + 32
	watertempF = RoundPlus(watertempF, 1)

	// process date/time and convert to PST
	rawtime := strings.Join(datafield[0:5], " ")
	t, err := time.Parse("2006 01 02 15 04", rawtime)
	if err != nil {
		fmt.Println(err)
	}
	loc, err := time.LoadLocation("America/Los_Angeles")
	if err != nil {
		fmt.Println(err)
	}
	t = t.In(loc)
	// fmt.Println(t.Format(time.RFC822))

	// concatenate data to output and print to console
	output := fmt.Sprint("\nSF Buoy at ", t.Format(time.RFC822), "\nSwell: ", strconv.FormatFloat(float64(waveheightfeet), 'f', 1, 64), "ft at ", datafield[9], " sec from ", wavecardinal, "\nWind: ", strconv.FormatFloat(float64(windspeedmph), 'f', 0, 64), "mph from ", windcardinal, "\nTemp: Air ", airtempF, "F / Water: ", watertempF, "F")
	// fmt.Println(output)
	return output
}

// given degrees returns cardinal direction
func direction(deg int64) string {
	switch {
	case deg < 0:
		return "ERROR - DEGREE LESS THAN ZERO"
	case deg <= 11:
		return "N"
	case deg <= 34:
		return "NNE"
	case deg <= 56:
		return "NE"
	case deg <= 79:
		return "ENE"
	case deg <= 101:
		return "E"
	case deg <= 124:
		return "ESE"
	case deg <= 146:
		return "SE"
	case deg <= 169:
		return "SSE"
	case deg <= 191:
		return "S"
	case deg <= 214:
		return "SSW"
	case deg <= 236:
		return "SW"
	case deg <= 259:
		return "WSW"
	case deg <= 281:
		return "W"
	case deg <= 304:
		return "WNW"
	case deg <= 326:
		return "NW"
	case deg <= 349:
		return "NNW"
	case deg <= 360:
		return "N"
	default:
		return "ERROR - DEGREE GREATER THAN 360"
	}
}

// round input to nearest integer
func Round(f float64) float64 {
	return math.Floor(f + .5)
}

// round input to defined number of decimals
func RoundPlus(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return Round(f*shift) / shift
}
<|endoftext|>


<commit_before>package flags_test

import (
	"github.com/jessevdk/go-flags"
	"testing"
)

func assertString(t *testing.T, a string, b string) {
	if a != b {
		t.Errorf("Expected %#v, but got %#v", b, a)
	}
}
func assertStringArray(t *testing.T, a []string, b []string) {
	if len(a) != len(b) {
		t.Errorf("Expected %#v, but got %#v", b, a)
		return
	}

	for i, v := range a {
		if b[i] != v {
			t.Errorf("Expected %#v, but got %#v", b, a)
			return
		}
	}
}

func assertBoolArray(t *testing.T, a []bool, b []bool) {
	if len(a) != len(b) {
		t.Errorf("Expected %#v, but got %#v", b, a)
		return
	}

	for i, v := range a {
		if b[i] != v {
			t.Errorf("Expected %#v, but got %#v", b, a)
			return
		}
	}
}

func assertParseSuccess(t *testing.T, data interface{}, args ...string) []string {
	parser := flags.NewParser(data, flags.Default &^ flags.PrintErrors)
	ret, err := parser.ParseArgs(args)

	if err != nil {
		t.Fatalf("Unexpected parse error: %s", err)
		return nil
	}

	return ret
}

func assertParseFail(t *testing.T, typ flags.ErrorType, msg string, data interface{}, args ...string) {
	parser := flags.NewParser(data, flags.Default &^ flags.PrintErrors)
	_, err := parser.ParseArgs(args)

	if err == nil {
		t.Fatalf("Expected error: %s", msg)
		return
	}

	if e, ok := err.(*flags.Error); !ok {
		t.Fatalf("Expected Error type, but got %#v", err)
		return
	} else {
		if e.Type != typ {
			t.Errorf("Expected error type {%s}, but got {%s}", typ, e.Type)
		}

		if e.Message != msg {
			t.Errorf("Expected error message %#v, but got %#v", msg, e.Message)
		}
	}
}

func TestShort(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v")

	assertStringArray(t, ret, []string{})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}
}

func TestShortMultiConcat(t *testing.T) {
	var opts = struct {
		V bool `short:"v"`
		O bool `short:"o"`
		F bool `short:"f"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vo", "-f")

	assertStringArray(t, ret, []string{})

	if !opts.V {
		t.Errorf("Expected V to be true")
	}

	if !opts.O {
		t.Errorf("Expected O to be true")
	}

	if !opts.F {
		t.Errorf("Expected F to be true")
	}
}

func TestShortMultiSlice(t *testing.T) {
	var opts = struct {
		Values []bool `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v", "-v")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.Values, []bool{true, true})
}

func TestShortMultiSliceConcat(t *testing.T) {
	var opts = struct {
		Values []bool `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vvv")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.Values, []bool{true, true, true})
}

func TestShortWithEqualArg(t *testing.T) {
	var opts = struct {
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v=value")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "value")
}

func TestShortWithArg(t *testing.T) {
	var opts = struct {
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vvalue")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "value")
}

func TestShortArg(t *testing.T) {
	var opts = struct {
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v", "value")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "value")
}

func TestShortMultiWithEqualArg(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-ffv=value")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.F, []bool{true, true})
	assertString(t, opts.Value, "value")
}

func TestShortMultiArg(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-ffv", "value")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.F, []bool{true, true})
	assertString(t, opts.Value, "value")
}

func TestShortMultiArgConcatFail(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	assertParseFail(t, flags.ErrUnknownFlag, "", &opts, "-ffvvalue")
}

func TestShortMultiArgConcat(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vff")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "ff")
}
<commit_msg>Fix expected error for short tests<commit_after>package flags_test

import (
	"github.com/jessevdk/go-flags"
	"testing"
)

func assertString(t *testing.T, a string, b string) {
	if a != b {
		t.Errorf("Expected %#v, but got %#v", b, a)
	}
}
func assertStringArray(t *testing.T, a []string, b []string) {
	if len(a) != len(b) {
		t.Errorf("Expected %#v, but got %#v", b, a)
		return
	}

	for i, v := range a {
		if b[i] != v {
			t.Errorf("Expected %#v, but got %#v", b, a)
			return
		}
	}
}

func assertBoolArray(t *testing.T, a []bool, b []bool) {
	if len(a) != len(b) {
		t.Errorf("Expected %#v, but got %#v", b, a)
		return
	}

	for i, v := range a {
		if b[i] != v {
			t.Errorf("Expected %#v, but got %#v", b, a)
			return
		}
	}
}

func assertParseSuccess(t *testing.T, data interface{}, args ...string) []string {
	parser := flags.NewParser(data, flags.Default &^ flags.PrintErrors)
	ret, err := parser.ParseArgs(args)

	if err != nil {
		t.Fatalf("Unexpected parse error: %s", err)
		return nil
	}

	return ret
}

func assertParseFail(t *testing.T, typ flags.ErrorType, msg string, data interface{}, args ...string) {
	parser := flags.NewParser(data, flags.Default &^ flags.PrintErrors)
	_, err := parser.ParseArgs(args)

	if err == nil {
		t.Fatalf("Expected error: %s", msg)
		return
	}

	if e, ok := err.(*flags.Error); !ok {
		t.Fatalf("Expected Error type, but got %#v", err)
		return
	} else {
		if e.Type != typ {
			t.Errorf("Expected error type {%s}, but got {%s}", typ, e.Type)
		}

		if e.Message != msg {
			t.Errorf("Expected error message %#v, but got %#v", msg, e.Message)
		}
	}
}

func TestShort(t *testing.T) {
	var opts = struct {
		Value bool `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v")

	assertStringArray(t, ret, []string{})

	if !opts.Value {
		t.Errorf("Expected Value to be true")
	}
}

func TestShortMultiConcat(t *testing.T) {
	var opts = struct {
		V bool `short:"v"`
		O bool `short:"o"`
		F bool `short:"f"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vo", "-f")

	assertStringArray(t, ret, []string{})

	if !opts.V {
		t.Errorf("Expected V to be true")
	}

	if !opts.O {
		t.Errorf("Expected O to be true")
	}

	if !opts.F {
		t.Errorf("Expected F to be true")
	}
}

func TestShortMultiSlice(t *testing.T) {
	var opts = struct {
		Values []bool `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v", "-v")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.Values, []bool{true, true})
}

func TestShortMultiSliceConcat(t *testing.T) {
	var opts = struct {
		Values []bool `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vvv")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.Values, []bool{true, true, true})
}

func TestShortWithEqualArg(t *testing.T) {
	var opts = struct {
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v=value")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "value")
}

func TestShortWithArg(t *testing.T) {
	var opts = struct {
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vvalue")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "value")
}

func TestShortArg(t *testing.T) {
	var opts = struct {
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-v", "value")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "value")
}

func TestShortMultiWithEqualArg(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	assertParseFail(t, flags.ErrUnknownFlag, "unknown flag `='", &opts, "-ffv=value")
}

func TestShortMultiArg(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-ffv", "value")

	assertStringArray(t, ret, []string{})
	assertBoolArray(t, opts.F, []bool{true, true})
	assertString(t, opts.Value, "value")
}

func TestShortMultiArgConcatFail(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	assertParseFail(t, flags.ErrUnknownFlag, "unknown flag `a'", &opts, "-ffvvalue")
}

func TestShortMultiArgConcat(t *testing.T) {
	var opts = struct {
		F []bool `short:"f"`
		Value string `short:"v"`
	}{}

	ret := assertParseSuccess(t, &opts, "-vff")

	assertStringArray(t, ret, []string{})
	assertString(t, opts.Value, "ff")
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "fmt"
    "encoding/binary"
    "bytes"
    "net"

    "errors"

    "github.com/sabhiram/go-colorize"
    wol "github.com/sabhiram/go-wol/lib"

    "github.com/jessevdk/go-flags"
)

var (
    // Define holders for the cli arguments we wish to parse
    Options struct {
        Version     bool   `short:"v" long:"version"`
        Help        bool   `short:"h" long:"help"`
        BroadcastIP string `short:"b" long:"bcast" default:"255.255.255.255"`
        UDPPort     string `short:"p" long:"port" default:"9"`
    }
)

// Helper function to dump the usage and print an error if specified,
// it also returns the exit code requested to the function (saves me a line)
func printUsageGetExitCode(s string, e int) int {
    if len(s) > 0 {
        fmt.Printf(s)
    }
    fmt.Printf(getAppUsageString())
    return e
}

// Function to send a magic packet to a given mac address
func sendMagicPacket(macAddr string) error {
    magicPacket, err := wol.NewMagicPacket(macAddr)
    if err == nil {
        var buf bytes.Buffer
        binary.Write(&buf, binary.BigEndian, magicPacket)

        fmt.Printf("Attempting to send a magic packet to MAC %s\n", macAddr)
        fmt.Printf("... Broadcasting to IP: %s\n", Options.BroadcastIP)
        fmt.Printf("... Using UDP Port:     %s\n", Options.UDPPort)

        udpAddr, err := net.ResolveUDPAddr("udp", Options.BroadcastIP + ":" + Options.UDPPort)
        if err != nil {
            fmt.Printf("Unable to get a UDP address for %s\n", Options.BroadcastIP)
            return err
        }

        connection, err := net.DialUDP("udp", nil, udpAddr)
        if err != nil {
            fmt.Printf("Unable to dial UDP addr for %s\n", Options.BroadcastIP)
            return err
        }
        defer connection.Close()

        bytesWritten, err := connection.Write(buf.Bytes())
        if bytesWritten != 102 {
            fmt.Printf("%d bytes written, %d expected!\n", bytesWritten, 102)
        }
    }
    return err
}

// Run one of the supported commands
func runCommand(cmd string, args []string, aliases map[string]string) error {
    var err error
    fmt.Printf("The aliases map is: %v\n", aliases)
    switch cmd {

    case "alias":
        if len(args) >= 2 {
            // TODO: Validate mac address
            alias, mac := args[0], args[1]
            aliases[alias] = mac
            return flushUserAliases(aliases)
        } else {
            return errors.New("alias command requires a <name> and a <mac>")
        }

    case "list":
        if len(aliases) == 0 {
            fmt.Printf("No aliases found! Add one with \"wol alias <name> <mac>\"\n")
        } else {
            for alias, mac := range aliases {
                fmt.Printf("    %s - %s\n", alias, mac)
            }
        }

    case "remove":
        if len(args) > 0 {
            alias := args[0]
            delete(aliases, alias)
            return flushUserAliases(aliases)
        } else {
            return errors.New("remove command requires a <name> of an alias")
        }

    case "wake":
        if len(args) > 0 {
            err = sendMagicPacket(args[0])
            if err == nil {
                fmt.Printf("Magic packet sent successfully to %s\n", args[0])
            }
        } else {
            err = errors.New("No mac address specified to wake command")
        }

    default:
        panic("Invalid command passed to runCommand")

    }
    return err
}

// Main entry point for binary
func main() {
    var args []string

    // Load the list of aliases from ~/.config/go-wol/aliases
    aliases, err := loadUserAliases()

    // Parse arguments which might get passed to "wol"
    parser := flags.NewParser(&Options, flags.Default & ^flags.HelpFlag)
    args, err = parser.Parse()

    exitCode := 0
    switch {

    // Parse Error, print usage
    case err != nil:
        exitCode = printUsageGetExitCode("", 1)

    // No arguments, or help requested, print usage
    case len(os.Args) == 1 || Options.Help:
        exitCode = printUsageGetExitCode("", 0)

    // "--version" requested
    case Options.Version:
        fmt.Printf("%s\n", Version)

    // Make sure we are being asked to run a command
    case len(args) == 0:
        exitCode = printUsageGetExitCode("No command specified, see usage:\n", 1)

    // All other cases go here
    case true:
        cmd, args := args[0], args[1:]
        if isValidCommand(cmd) {
            err = runCommand(cmd, args, aliases)
            if err != nil {
                exitCode = printUsageGetExitCode(
                    fmt.Sprintf("%s\n", err.Error()), 1)
            }
        } else {
            exitCode = printUsageGetExitCode(
                fmt.Sprintf("Unknown command %s, see usage:\n", colorize.ColorString(cmd, "red")), 1)
        }

    }
    os.Exit(exitCode)
}
<commit_msg>aliasing working :)<commit_after>package main

import (
    "os"
    "fmt"
    "encoding/binary"
    "bytes"
    "net"

    "errors"

    "github.com/sabhiram/go-colorize"
    wol "github.com/sabhiram/go-wol/lib"

    "github.com/jessevdk/go-flags"
)

var (
    // Define holders for the cli arguments we wish to parse
    Options struct {
        Version     bool   `short:"v" long:"version"`
        Help        bool   `short:"h" long:"help"`
        BroadcastIP string `short:"b" long:"bcast" default:"255.255.255.255"`
        UDPPort     string `short:"p" long:"port" default:"9"`
    }
)

// Helper function to dump the usage and print an error if specified,
// it also returns the exit code requested to the function (saves me a line)
func printUsageGetExitCode(s string, e int) int {
    if len(s) > 0 {
        fmt.Printf(s)
    }
    fmt.Printf(getAppUsageString())
    return e
}

// Function to send a magic packet to a given mac address
func sendMagicPacket(macAddr string) error {
    magicPacket, err := wol.NewMagicPacket(macAddr)
    if err == nil {
        var buf bytes.Buffer
        binary.Write(&buf, binary.BigEndian, magicPacket)

        fmt.Printf("Attempting to send a magic packet to MAC %s\n", macAddr)
        fmt.Printf("... Broadcasting to IP: %s\n", Options.BroadcastIP)
        fmt.Printf("... Using UDP Port:     %s\n", Options.UDPPort)

        udpAddr, err := net.ResolveUDPAddr("udp", Options.BroadcastIP + ":" + Options.UDPPort)
        if err != nil {
            fmt.Printf("Unable to get a UDP address for %s\n", Options.BroadcastIP)
            return err
        }

        connection, err := net.DialUDP("udp", nil, udpAddr)
        if err != nil {
            fmt.Printf("Unable to dial UDP addr for %s\n", Options.BroadcastIP)
            return err
        }
        defer connection.Close()

        bytesWritten, err := connection.Write(buf.Bytes())
        if bytesWritten != 102 {
            fmt.Printf("%d bytes written, %d expected!\n", bytesWritten, 102)
        }
    }
    return err
}

// Run one of the supported commands
func runCommand(cmd string, args []string, aliases map[string]string) error {
    var err error
    switch cmd {

    case "alias":
        if len(args) >= 2 {
            // TODO: Validate mac address
            alias, mac := args[0], args[1]
            aliases[alias] = mac
            return flushUserAliases(aliases)
        } else {
            return errors.New("alias command requires a <name> and a <mac>")
        }

    case "list":
        if len(aliases) == 0 {
            fmt.Printf("No aliases found! Add one with \"wol alias <name> <mac>\"\n")
        } else {
            for alias, mac := range aliases {
                fmt.Printf("    %s - %s\n", alias, mac)
            }
        }

    case "remove":
        if len(args) > 0 {
            alias := args[0]
            delete(aliases, alias)
            return flushUserAliases(aliases)
        } else {
            return errors.New("remove command requires a <name> of an alias")
        }

    case "wake":
        if len(args) > 0 {
            macAddr := args[0]

            // If we got an alias - use that as the mac addr
            if val, ok := aliases[macAddr]; ok {
                macAddr = val
            }

            err = sendMagicPacket(macAddr)
            if err == nil {
                fmt.Printf("Magic packet sent successfully to %s\n", macAddr)
            }
        } else {
            err = errors.New("No mac address specified to wake command")
        }

    default:
        panic("Invalid command passed to runCommand")

    }
    return err
}

// Main entry point for binary
func main() {
    var args []string

    // Load the list of aliases from ~/.config/go-wol/aliases
    aliases, err := loadUserAliases()

    // Parse arguments which might get passed to "wol"
    parser := flags.NewParser(&Options, flags.Default & ^flags.HelpFlag)
    args, err = parser.Parse()

    exitCode := 0
    switch {

    // Parse Error, print usage
    case err != nil:
        exitCode = printUsageGetExitCode("", 1)

    // No arguments, or help requested, print usage
    case len(os.Args) == 1 || Options.Help:
        exitCode = printUsageGetExitCode("", 0)

    // "--version" requested
    case Options.Version:
        fmt.Printf("%s\n", Version)

    // Make sure we are being asked to run a command
    case len(args) == 0:
        exitCode = printUsageGetExitCode("No command specified, see usage:\n", 1)

    // All other cases go here
    case true:
        cmd, args := args[0], args[1:]
        if isValidCommand(cmd) {
            err = runCommand(cmd, args, aliases)
            if err != nil {
                exitCode = printUsageGetExitCode(
                    fmt.Sprintf("%s\n", err.Error()), 1)
            }
        } else {
            exitCode = printUsageGetExitCode(
                fmt.Sprintf("Unknown command %s, see usage:\n", colorize.ColorString(cmd, "red")), 1)
        }

    }
    os.Exit(exitCode)
}
<|endoftext|>


<commit_before>package sia

import (
	"errors"
	"math/big"
)

// Used to keep track of how many signatures an input has been signed by.
type InputSignatures struct {
	RemainingSignatures uint8
	PossibleKeys        []PublicKey
	UsedKeys            map[uint8]struct{}
}

// Add a block to the state struct.
func (s *State) AcceptBlock(b *Block) (err error) {
	bid := b.ID() // Function is not implemented.

	_, exists := s.BadBlocks[bid]
	if exists {
		err = errors.New("Block is in bad list")
		return
	}

	if b.Version != 1 {
		s.BadBlocks[bid] = struct{}{}
		err = errors.New("Block is not version 1")
		return
	}

	_, exists = s.BlockMap[bid]
	if exists {
		err = errors.New("Block exists in block map.")
		return
	}

	/*_, exists = s.OrphanBlocks[bid]
	if exists {
		err = errors.New("Block exists in orphan list")
		return
	} */

	parentBlockNode, exists := s.BlockMap[b.ParentBlock]
	if !exists {
		// OrphanBlocks[bid] = b
		err = errors.New("Block is an orphan")
		return
	}

	// If timestamp is in the future, store in future blocks list.
	// If timestamp is too far in the past, reject and put in bad blocks.

	// Check the amount of work done by the block.
	if !ValidHeader(parentBlockNode.Target, b) {
		err = errors.New("Block does not meet target")
		s.BadBlocks[bid] = struct{}{}
		return
	}

	// Add the block to the block tree.
	newBlockNode := new(BlockNode)
	newBlockNode.Block = b
	parentBlockNode.Children = append(parentBlockNode.Children, newBlockNode)
	// newBlockNode.Verified = false // implicit value, stated explicity for prosperity.
	newBlockNode.Height = parentBlockNode.Height + 1
	copy(newBlockNode.RecentTimestamps[:], parentBlockNode.RecentTimestamps[1:])
	newBlockNode.RecentTimestamps[10] = b.Timestamp
	s.BlockMap[bid] = newBlockNode

	var timePassed Timestamp
	var expectedTimePassed Timestamp
	var blockWindow BlockHeight
	if newBlockNode.Height < 5000 {
		// Calculate new target, using block 0 timestamp.
		timePassed = b.Timestamp - s.BlockRoot.Block.Timestamp
		expectedTimePassed = TargetSecondsPerBlock * Timestamp(newBlockNode.Height)
		blockWindow = newBlockNode.Height
	} else {
		// Calculate new target, using block Height-5000 timestamp.
		timePassed = b.Timestamp - s.BlockMap[s.ConsensusState.CurrentPath[newBlockNode.Height-5000]].Block.Timestamp
		expectedTimePassed = TargetSecondsPerBlock * 5000
		blockWindow = 5000
	}

	// Adjustment as a float = timePassed / expectedTimePassed / blockWindow.
	targetAdjustment := big.NewRat(int64(timePassed), int64(expectedTimePassed)*int64(blockWindow))

	// Enforce a maximum targetAdjustment
	if targetAdjustment.Cmp(MaxAdjustmentUp) == 1 {
		targetAdjustment = MaxAdjustmentUp
	} else if targetAdjustment.Cmp(MaxAdjustmentDown) == -1 {
		targetAdjustment = MaxAdjustmentDown
	}

	// Take the target adjustment and apply it to the target slice,
	// using rational numbers. Truncate the result.
	oldTarget := new(big.Int).SetBytes(parentBlockNode.Target[:])
	ratOldTarget := new(big.Rat).SetInt(oldTarget)
	ratNewTarget := ratOldTarget.Mul(targetAdjustment, ratOldTarget)
	intNewTarget := new(big.Int).Div(ratNewTarget.Num(), ratNewTarget.Denom())
	newTargetBytes := intNewTarget.Bytes()
	offset := len(newBlockNode.Target[:]) - len(newTargetBytes)
	copy(newBlockNode.Target[offset:], newTargetBytes)

	// Add the parent target to the depth of the block in the tree.
	blockWeight := new(big.Rat).SetFrac(big.NewInt(1), new(big.Int).SetBytes(parentBlockNode.Target[:]))
	newBlockNode.Depth = BlockWeight(new(big.Rat).Add(parentBlockNode.Depth, blockWeight))

	// If the new node is .5% heavier than the other node, switch to the new fork.
	currentWeight := new(big.Rat).SetFrac(big.NewInt(1), new(big.Int).SetBytes(s.BlockMap[s.ConsensusState.CurrentBlock].Target[:]))
	threshold := new(big.Rat).Mul(currentWeight, SurpassThreshold)
	requiredDepth := new(big.Rat).Add(s.BlockMap[s.ConsensusState.CurrentBlock].Depth, threshold)
	if (*big.Rat)(newBlockNode.Depth).Cmp(requiredDepth) == 1 {
		// Find the common parent between the new fork and the current
		// fork, keeping track of which path is taken through the
		// children of the parents so that we can re-trace as we
		// validate the blocks.
		currentNode := parentBlockNode
		value := s.ConsensusState.CurrentPath[currentNode.Height]
		var parentHistory []BlockID
		for value != currentNode.Block.ID() {
			parentHistory = append(parentHistory, currentNode.Block.ID())
			currentNode = s.BlockMap[currentNode.Block.ParentBlock]
			value = s.ConsensusState.CurrentPath[currentNode.Height]
		}

		// Remove blocks from the ConsensusState until we get to the
		// same parent that we are forking from.
		var rewoundBlocks []BlockID
		for s.ConsensusState.CurrentBlock != currentNode.Block.ID() {
			rewoundBlocks = append(rewoundBlocks, s.ConsensusState.CurrentBlock)
			s.RewindABlock()
		}

		// Validate each block in the parent history in order, updating
		// the state as we go.  If at some point a block doesn't
		// verify, you get to walk all the way backwards and forwards
		// again.
		validatedBlocks := 0
		for i := len(parentHistory) - 1; i >= 0; i-- {
			err = s.ValidateBlock(b)
			if err != nil {
				// Add the whole tree of blocks to BadBlocks,
				// deleting them from BlockMap

				// Rewind the validated blocks
				for i := 0; i < validatedBlocks; i++ {
					s.RewindABlock()
				}

				// Integrate the rewound blocks
				for i := len(rewoundBlocks) - 1; i >= 0; i-- {
					err = s.ValidateBlock(s.BlockMap[rewoundBlocks[i]].Block)
					if err != nil {
						panic(err)
					}
				}

				break
			}
			validatedBlocks += 1
		}

		// Do something to the transaction pool.
	} else {
		// Do something to the transaction pool.
	}

	return
}

// ValidateBlock will both verify the block AND update the consensus state.
// Calling integrate block is not needed.
func (s *State) ValidateBlock(b *Block) (err error) {
	// Check the hash on the merkle tree of transactions.

	var appliedTransactions []Transaction
	minerSubsidy := Currency(0)
	for _, txn := range b.Transactions {
		err = s.ValidateTxn(txn, s.BlockMap[b.ID()].Height)
		if err != nil {
			s.BadBlocks[b.ID()] = struct{}{}
			break
		}

		// Apply the transaction to the ConsensusState, adding it to the list of applied transactions.
		s.ApplyTransaction(txn)
		appliedTransactions = append(appliedTransactions, txn)

		minerSubsidy += txn.MinerFee
	}

	if err != nil {
		// Rewind transactions added to ConsensusState.
		for i := len(appliedTransactions) - 1; i >= 0; i-- {
			s.ReverseTransaction(appliedTransactions[i])
		}
		return
	}

	// Add outputs for all of the missed proofs in the open transactions.

	// Add coin inflation to the miner subsidy.

	// Add output contianing miner fees + block subsidy.
	bid := b.ID()
	minerSubsidyID := OutputID(HashBytes(append(bid[:], []byte("blockReward")...)))
	minerSubsidyOutput := Output{
		Value:     minerSubsidy,
		SpendHash: b.MinerAddress,
	}
	s.ConsensusState.UnspentOutputs[minerSubsidyID] = minerSubsidyOutput

	// s.BlockMap[b.ID()].Verified = true

	s.ConsensusState.CurrentBlock = b.ID()
	s.ConsensusState.CurrentPath[s.BlockMap[b.ID()].Height] = b.ID()

	return
}

// Add a function that integrates a block without verifying it.

func (s *State) ValidateTxn(t Transaction, currentHeight BlockHeight) (err error) {
	if t.Version != 1 {
		err = errors.New("Transaction version is not recognized.")
		return
	}

	inputSum := Currency(0)
	outputSum := t.MinerFee
	var inputSignaturesMap map[OutputID]InputSignatures
	for _, input := range t.Inputs {
		utxo, exists := s.ConsensusState.UnspentOutputs[input.OutputID]
		if !exists {
			err = errors.New("Transaction spends a nonexisting output")
			return
		}

		inputSum += utxo.Value

		// Check that the spend conditions match the hash listed in the output.

		// Check the timelock on the spend conditions is expired.
		if input.SpendConditions.TimeLock < currentHeight {
			err = errors.New("Output spent before timelock expiry.")
			return
		}

		// Create the condition for the input signatures and add it to the input signatures map.
		_, exists = inputSignaturesMap[input.OutputID]
		if exists {
			err = errors.New("Output spent twice in same transaction")
			return
		}
		var newInputSignatures InputSignatures
		newInputSignatures.RemainingSignatures = input.SpendConditions.NumSignatures
		newInputSignatures.PossibleKeys = input.SpendConditions.PublicKeys
		inputSignaturesMap[input.OutputID] = newInputSignatures
	}

	for _, output := range t.Outputs {
		outputSum += output.Value
	}

	for _, contract := range t.FileContracts {
		if contract.Start < currentHeight {
			err = errors.New("Contract starts in the future.")
			return
		}
		if contract.End <= contract.Start {
			err = errors.New("Contract duration must be at least one block.")
			return
		}
	}

	/*
		for _, proof := range t.StorageProofs {
			// Check that the proof passes.
			// Check that the proof has not already been submitted.
		}
	*/

	if inputSum != outputSum {
		err = errors.New("Inputs do not equal outputs for transaction.")
		return
	}

	for _, sig := range t.Signatures {
		// Check that each signature signs a unique pubkey where
		// RemainingSignatures > 0.
		if inputSignaturesMap[sig.InputID].RemainingSignatures == 0 {
			err = errors.New("Friviolous Signature detected.")
			return
		}
		_, exists := inputSignaturesMap[sig.InputID].UsedKeys[sig.PublicKeyIndex]
		if exists {
			err = errors.New("public key used twice while signing")
			return
		}

		// Check the timelock on the signature.
		if sig.TimeLock < currentHeight {
			err = errors.New("signature timelock has not expired")
			return
		}

		// Check that the actual signature is valid, following the covered fields struct.
	}

	return
}

func (s *State) ApplyTransaction(t Transaction) {
	// Remove all inputs from the unspent outputs list
	for _, input := range t.Inputs {
		s.ConsensusState.SpentOutputs[input.OutputID] = s.ConsensusState.UnspentOutputs[input.OutputID]
		delete(s.ConsensusState.UnspentOutputs, input.OutputID)
	}

	// Add all outputs to the unspent outputs list
	for i, output := range t.Outputs {
		newOutputID := OutputID(HashBytes(append((t.Inputs[0].OutputID)[:], EncUint64(uint64(i))...)))
		s.ConsensusState.UnspentOutputs[newOutputID] = output
	}

	// Add all outputs created by storage proofs.
	/*
		for _, sp := range t.StorageProofs {
			// Need to check that the contract fund has sufficient funds remaining.

			newOutputID := HashBytes(append(ContractID), []byte(n))
			output := Output {
				Value: s.ConsensusState.OpenContracts[sp.ContractID].ValidProofPayout,
				SpendHash: s.ConsensusState.OpenContracts[sp.ContractID].ValidProofAddress,
			}
			s.ConsensusState.UnspentOutputs[newOutputID] = output

			// need a counter or some way to determine what the index of
			// the window is.
		}
	*/
}

// Pulls just this transaction out of the ConsensusState.
func (s *State) ReverseTransaction(t Transaction) {
	// Remove all outputs created by storage proofs.

	// Remove all outputs created by outputs.
	for i := range t.Outputs {
		outputID := OutputID(HashBytes(append((t.Inputs[0].OutputID)[:], EncUint64(uint64(i))...)))
		delete(s.ConsensusState.UnspentOutputs, outputID)
	}

	// Add all outputs spent by inputs.
	for _, input := range t.Inputs {
		s.ConsensusState.UnspentOutputs[input.OutputID] = s.ConsensusState.SpentOutputs[input.OutputID]
		delete(s.ConsensusState.SpentOutputs, input.OutputID)
	}
}

// Pulls the most recent block out of the ConsensusState.
func (s *State) RewindABlock() {
	block := s.BlockMap[s.ConsensusState.CurrentBlock].Block
	for i := len(block.Transactions) - 1; i >= 0; i-- {
		s.ReverseTransaction(block.Transactions[i])
	}

	s.ConsensusState.CurrentBlock = block.ParentBlock
	delete(s.ConsensusState.CurrentPath, s.BlockMap[block.ID()].Height)
}
<commit_msg>split map checking into separate function<commit_after>package sia

import (
	"errors"
	"math/big"
)

// Used to keep track of how many signatures an input has been signed by.
type InputSignatures struct {
	RemainingSignatures uint8
	PossibleKeys        []PublicKey
	UsedKeys            map[uint8]struct{}
}

// checkMaps looks through the maps known to the state and sees if the block id
// has been cached anywhere.
func (s *State) checkMaps(b *Block) (parentBlockNode *BlockNode, err error) {
	// See if the block is a known invalid block.
	_, exists := s.BadBlocks[b.ID()]
	if exists {
		err = errors.New("block is known to be invalid")
		return
	}

	// See if the block is a known valid block.
	_, exists = s.BlockMap[b.ID()]
	if exists {
		err = errors.New("Block exists in block map.")
		return
	}

	/*
		// See if the block is a known orphan.
		_, exists = s.OrphanBlocks[b.ID()]
		if exists {
			err = errors.New("Block exists in orphan list")
			return
		}
	*/

	// See if the block's parent is known.
	parentBlockNode, exists = s.BlockMap[b.ParentBlock]
	if !exists {
		// OrphanBlocks[b.ID()] = b
		err = errors.New("Block is an orphan")
		return
	}

	return
}

// Add a block to the state struct.
func (s *State) AcceptBlock(b *Block) (err error) {
	// Check the version of the block.
	if b.Version != 1 {
		s.BadBlocks[b.ID()] = struct{}{}
		err = errors.New("Block is not version 1")
		return
	}

	// Check the maps in the state to see if the block is already known.
	parentBlockNode, err := s.checkMaps(b)
	if err != nil {
		return
	}

	// If timestamp is in the future, store in future blocks list.
	// If timestamp is too far in the past, reject and put in bad blocks.

	// Check the amount of work done by the block.
	if !ValidHeader(parentBlockNode.Target, b) {
		err = errors.New("Block does not meet target")
		s.BadBlocks[b.ID()] = struct{}{}
		return
	}

	// Add the block to the block tree.
	newBlockNode := new(BlockNode)
	newBlockNode.Block = b
	parentBlockNode.Children = append(parentBlockNode.Children, newBlockNode)
	// newBlockNode.Verified = false // implicit value, stated explicity for prosperity.
	newBlockNode.Height = parentBlockNode.Height + 1
	copy(newBlockNode.RecentTimestamps[:], parentBlockNode.RecentTimestamps[1:])
	newBlockNode.RecentTimestamps[10] = b.Timestamp
	s.BlockMap[b.ID()] = newBlockNode

	var timePassed Timestamp
	var expectedTimePassed Timestamp
	var blockWindow BlockHeight
	if newBlockNode.Height < 5000 {
		// Calculate new target, using block 0 timestamp.
		timePassed = b.Timestamp - s.BlockRoot.Block.Timestamp
		expectedTimePassed = TargetSecondsPerBlock * Timestamp(newBlockNode.Height)
		blockWindow = newBlockNode.Height
	} else {
		// Calculate new target, using block Height-5000 timestamp.
		timePassed = b.Timestamp - s.BlockMap[s.ConsensusState.CurrentPath[newBlockNode.Height-5000]].Block.Timestamp
		expectedTimePassed = TargetSecondsPerBlock * 5000
		blockWindow = 5000
	}

	// Adjustment as a float = timePassed / expectedTimePassed / blockWindow.
	targetAdjustment := big.NewRat(int64(timePassed), int64(expectedTimePassed)*int64(blockWindow))

	// Enforce a maximum targetAdjustment
	if targetAdjustment.Cmp(MaxAdjustmentUp) == 1 {
		targetAdjustment = MaxAdjustmentUp
	} else if targetAdjustment.Cmp(MaxAdjustmentDown) == -1 {
		targetAdjustment = MaxAdjustmentDown
	}

	// Take the target adjustment and apply it to the target slice,
	// using rational numbers. Truncate the result.
	oldTarget := new(big.Int).SetBytes(parentBlockNode.Target[:])
	ratOldTarget := new(big.Rat).SetInt(oldTarget)
	ratNewTarget := ratOldTarget.Mul(targetAdjustment, ratOldTarget)
	intNewTarget := new(big.Int).Div(ratNewTarget.Num(), ratNewTarget.Denom())
	newTargetBytes := intNewTarget.Bytes()
	offset := len(newBlockNode.Target[:]) - len(newTargetBytes)
	copy(newBlockNode.Target[offset:], newTargetBytes)

	// Add the parent target to the depth of the block in the tree.
	blockWeight := new(big.Rat).SetFrac(big.NewInt(1), new(big.Int).SetBytes(parentBlockNode.Target[:]))
	newBlockNode.Depth = BlockWeight(new(big.Rat).Add(parentBlockNode.Depth, blockWeight))

	// If the new node is .5% heavier than the other node, switch to the new fork.
	currentWeight := new(big.Rat).SetFrac(big.NewInt(1), new(big.Int).SetBytes(s.BlockMap[s.ConsensusState.CurrentBlock].Target[:]))
	threshold := new(big.Rat).Mul(currentWeight, SurpassThreshold)
	requiredDepth := new(big.Rat).Add(s.BlockMap[s.ConsensusState.CurrentBlock].Depth, threshold)
	if (*big.Rat)(newBlockNode.Depth).Cmp(requiredDepth) == 1 {
		// Find the common parent between the new fork and the current
		// fork, keeping track of which path is taken through the
		// children of the parents so that we can re-trace as we
		// validate the blocks.
		currentNode := parentBlockNode
		value := s.ConsensusState.CurrentPath[currentNode.Height]
		var parentHistory []BlockID
		for value != currentNode.Block.ID() {
			parentHistory = append(parentHistory, currentNode.Block.ID())
			currentNode = s.BlockMap[currentNode.Block.ParentBlock]
			value = s.ConsensusState.CurrentPath[currentNode.Height]
		}

		// Remove blocks from the ConsensusState until we get to the
		// same parent that we are forking from.
		var rewoundBlocks []BlockID
		for s.ConsensusState.CurrentBlock != currentNode.Block.ID() {
			rewoundBlocks = append(rewoundBlocks, s.ConsensusState.CurrentBlock)
			s.RewindABlock()
		}

		// Validate each block in the parent history in order, updating
		// the state as we go.  If at some point a block doesn't
		// verify, you get to walk all the way backwards and forwards
		// again.
		validatedBlocks := 0
		for i := len(parentHistory) - 1; i >= 0; i-- {
			err = s.ValidateBlock(b)
			if err != nil {
				// Add the whole tree of blocks to BadBlocks,
				// deleting them from BlockMap

				// Rewind the validated blocks
				for i := 0; i < validatedBlocks; i++ {
					s.RewindABlock()
				}

				// Integrate the rewound blocks
				for i := len(rewoundBlocks) - 1; i >= 0; i-- {
					err = s.ValidateBlock(s.BlockMap[rewoundBlocks[i]].Block)
					if err != nil {
						panic(err)
					}
				}

				break
			}
			validatedBlocks += 1
		}

		// Do something to the transaction pool.
	} else {
		// Do something to the transaction pool.
	}

	return
}

// ValidateBlock will both verify the block AND update the consensus state.
// Calling integrate block is not needed.
func (s *State) ValidateBlock(b *Block) (err error) {
	// Check the hash on the merkle tree of transactions.

	var appliedTransactions []Transaction
	minerSubsidy := Currency(0)
	for _, txn := range b.Transactions {
		err = s.ValidateTxn(txn, s.BlockMap[b.ID()].Height)
		if err != nil {
			s.BadBlocks[b.ID()] = struct{}{}
			break
		}

		// Apply the transaction to the ConsensusState, adding it to the list of applied transactions.
		s.ApplyTransaction(txn)
		appliedTransactions = append(appliedTransactions, txn)

		minerSubsidy += txn.MinerFee
	}

	if err != nil {
		// Rewind transactions added to ConsensusState.
		for i := len(appliedTransactions) - 1; i >= 0; i-- {
			s.ReverseTransaction(appliedTransactions[i])
		}
		return
	}

	// Add outputs for all of the missed proofs in the open transactions.

	// Add coin inflation to the miner subsidy.

	// Add output contianing miner fees + block subsidy.
	bid := b.ID()
	minerSubsidyID := OutputID(HashBytes(append(bid[:], []byte("blockReward")...)))
	minerSubsidyOutput := Output{
		Value:     minerSubsidy,
		SpendHash: b.MinerAddress,
	}
	s.ConsensusState.UnspentOutputs[minerSubsidyID] = minerSubsidyOutput

	// s.BlockMap[b.ID()].Verified = true

	s.ConsensusState.CurrentBlock = b.ID()
	s.ConsensusState.CurrentPath[s.BlockMap[b.ID()].Height] = b.ID()

	return
}

// Add a function that integrates a block without verifying it.

func (s *State) ValidateTxn(t Transaction, currentHeight BlockHeight) (err error) {
	if t.Version != 1 {
		err = errors.New("Transaction version is not recognized.")
		return
	}

	inputSum := Currency(0)
	outputSum := t.MinerFee
	var inputSignaturesMap map[OutputID]InputSignatures
	for _, input := range t.Inputs {
		utxo, exists := s.ConsensusState.UnspentOutputs[input.OutputID]
		if !exists {
			err = errors.New("Transaction spends a nonexisting output")
			return
		}

		inputSum += utxo.Value

		// Check that the spend conditions match the hash listed in the output.

		// Check the timelock on the spend conditions is expired.
		if input.SpendConditions.TimeLock < currentHeight {
			err = errors.New("Output spent before timelock expiry.")
			return
		}

		// Create the condition for the input signatures and add it to the input signatures map.
		_, exists = inputSignaturesMap[input.OutputID]
		if exists {
			err = errors.New("Output spent twice in same transaction")
			return
		}
		var newInputSignatures InputSignatures
		newInputSignatures.RemainingSignatures = input.SpendConditions.NumSignatures
		newInputSignatures.PossibleKeys = input.SpendConditions.PublicKeys
		inputSignaturesMap[input.OutputID] = newInputSignatures
	}

	for _, output := range t.Outputs {
		outputSum += output.Value
	}

	for _, contract := range t.FileContracts {
		if contract.Start < currentHeight {
			err = errors.New("Contract starts in the future.")
			return
		}
		if contract.End <= contract.Start {
			err = errors.New("Contract duration must be at least one block.")
			return
		}
	}

	/*
		for _, proof := range t.StorageProofs {
			// Check that the proof passes.
			// Check that the proof has not already been submitted.
		}
	*/

	if inputSum != outputSum {
		err = errors.New("Inputs do not equal outputs for transaction.")
		return
	}

	for _, sig := range t.Signatures {
		// Check that each signature signs a unique pubkey where
		// RemainingSignatures > 0.
		if inputSignaturesMap[sig.InputID].RemainingSignatures == 0 {
			err = errors.New("Friviolous Signature detected.")
			return
		}
		_, exists := inputSignaturesMap[sig.InputID].UsedKeys[sig.PublicKeyIndex]
		if exists {
			err = errors.New("public key used twice while signing")
			return
		}

		// Check the timelock on the signature.
		if sig.TimeLock < currentHeight {
			err = errors.New("signature timelock has not expired")
			return
		}

		// Check that the actual signature is valid, following the covered fields struct.
	}

	return
}

func (s *State) ApplyTransaction(t Transaction) {
	// Remove all inputs from the unspent outputs list
	for _, input := range t.Inputs {
		s.ConsensusState.SpentOutputs[input.OutputID] = s.ConsensusState.UnspentOutputs[input.OutputID]
		delete(s.ConsensusState.UnspentOutputs, input.OutputID)
	}

	// Add all outputs to the unspent outputs list
	for i, output := range t.Outputs {
		newOutputID := OutputID(HashBytes(append((t.Inputs[0].OutputID)[:], EncUint64(uint64(i))...)))
		s.ConsensusState.UnspentOutputs[newOutputID] = output
	}

	// Add all outputs created by storage proofs.
	/*
		for _, sp := range t.StorageProofs {
			// Need to check that the contract fund has sufficient funds remaining.

			newOutputID := HashBytes(append(ContractID), []byte(n))
			output := Output {
				Value: s.ConsensusState.OpenContracts[sp.ContractID].ValidProofPayout,
				SpendHash: s.ConsensusState.OpenContracts[sp.ContractID].ValidProofAddress,
			}
			s.ConsensusState.UnspentOutputs[newOutputID] = output

			// need a counter or some way to determine what the index of
			// the window is.
		}
	*/
}

// Pulls just this transaction out of the ConsensusState.
func (s *State) ReverseTransaction(t Transaction) {
	// Remove all outputs created by storage proofs.

	// Remove all outputs created by outputs.
	for i := range t.Outputs {
		outputID := OutputID(HashBytes(append((t.Inputs[0].OutputID)[:], EncUint64(uint64(i))...)))
		delete(s.ConsensusState.UnspentOutputs, outputID)
	}

	// Add all outputs spent by inputs.
	for _, input := range t.Inputs {
		s.ConsensusState.UnspentOutputs[input.OutputID] = s.ConsensusState.SpentOutputs[input.OutputID]
		delete(s.ConsensusState.SpentOutputs, input.OutputID)
	}
}

// Pulls the most recent block out of the ConsensusState.
func (s *State) RewindABlock() {
	block := s.BlockMap[s.ConsensusState.CurrentBlock].Block
	for i := len(block.Transactions) - 1; i >= 0; i-- {
		s.ReverseTransaction(block.Transactions[i])
	}

	s.ConsensusState.CurrentBlock = block.ParentBlock
	delete(s.ConsensusState.CurrentPath, s.BlockMap[block.ID()].Height)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pat

import (
	"net/http"

	"github.com/gorilla/mux"
)

// Creates a stack of HTTP handlers. Each HTTP handler is responsible for
// calling the next. The handlers are executed in reverse order, the last is
// called first.
func use(handler http.Handler, middleware ...func(http.Handler) http.Handler) http.Handler {
	for _, m := range middleware {
		handler = m(handler)
	}
	return handler
}

// registerPat a pattern with a handler for the given request method.
func registerPat(r *mux.Router, meth, pat string, h http.Handler, middleware []func(http.Handler) http.Handler) *mux.Route {
	return r.NewRoute().PathPrefix(pat).Handler(use(h, middleware...)).Methods(meth)
}

// Delete registers a pattern with a handler for DELETE requests.
func Delete(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "DELETE", pat, h, middleware)
}

// Get registers a pattern with a handler for GET requests.
func Get(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "GET", pat, h, middleware)
}

// Head registers a pattern with a handler for HEAD requests.
func Head(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "HEAD", pat, h, middleware)
}

// Post registers a pattern with a handler for POST requests.
func Post(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "POST", pat, h, middleware)
}

// Put registers a pattern with a handler for PUT requests.
func Put(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "PUT", pat, h, middleware)
}
<commit_msg>pat: add Options<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pat

import (
	"net/http"

	"github.com/gorilla/mux"
)

// Creates a stack of HTTP handlers. Each HTTP handler is responsible for
// calling the next. The handlers are executed in reverse order, the last is
// called first.
func use(handler http.Handler, middleware ...func(http.Handler) http.Handler) http.Handler {
	for _, m := range middleware {
		handler = m(handler)
	}
	return handler
}

// registerPat a pattern with a handler for the given request method.
func registerPat(r *mux.Router, meth, pat string, h http.Handler, middleware []func(http.Handler) http.Handler) *mux.Route {
	return r.NewRoute().PathPrefix(pat).Handler(use(h, middleware...)).Methods(meth)
}

// Delete registers a pattern with a handler for DELETE requests.
func Delete(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "DELETE", pat, h, middleware)
}

// Get registers a pattern with a handler for GET requests.
func Get(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "GET", pat, h, middleware)
}

// Head registers a pattern with a handler for HEAD requests.
func Head(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "HEAD", pat, h, middleware)
}

// Options registers a pattern with a handler for OPTIONS requests.
func Options(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "OPTIONS", pat, h, middleware)
}

// Post registers a pattern with a handler for POST requests.
func Post(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "POST", pat, h, middleware)
}

// Put registers a pattern with a handler for PUT requests.
func Put(r *mux.Router, pat string, h http.Handler, middleware ...func(http.Handler) http.Handler) *mux.Route {
	return registerPat(r, "PUT", pat, h, middleware)
}
<|endoftext|>


<commit_before>package adyen

/**********
* Payment *
**********/

// One-click functionality gives the shopper the option to store their payment details with the merchant, within the Adyen environment.
//
// In this type of transaction, the shopper needs to enter the CVC code for the transaction to get through.
//
// Link: https://docs.adyen.com/developers/api-reference/payments-api#recurring
const (
	RecurringPaymentOneClick              = "ONECLICK"
	RecurringPaymentRecurring             = "RECURRING"
	ShopperInteractionContAuth            = "ContAuth"
	SelectRecurringDetailReferenceLatests = "LATEST"
)

// AuthoriseEncrypted structure for Authorisation request (with encrypted card information)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type AuthoriseEncrypted struct {
	AdditionalData                   *AdditionalData `json:"additionalData,omitempty"`
	Amount                           *Amount         `json:"amount"`
	BillingAddress                   *Address        `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address        `json:"deliveryAddress,omitempty"`
	Reference                        string          `json:"reference"`
	MerchantAccount                  string          `json:"merchantAccount"`
	ShopperReference                 string          `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring      `json:"recurring,omitempty"`
	ShopperEmail                     string          `json:"shopperEmail,omitempty"`
	ShopperInteraction               string          `json:"shopperInteraction,omitempty"`
	ShopperIP                        string          `json:"shopperIP,omitempty"`
	ShopperLocale                    string          `json:"shopperLocale,omitempty"`
	ShopperName                      *Name           `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string          `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo    `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                int             `json:"captureDelayHours,omitempty"`
}

// Authorise structure for Authorisation request (card is not encrypted)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type Authorise struct {
	Card                             *Card        `json:"card,omitempty"`
	Amount                           *Amount      `json:"amount"`
	BillingAddress                   *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address     `json:"deliveryAddress,omitempty"`
	Reference                        string       `json:"reference"`
	MerchantAccount                  string       `json:"merchantAccount"`
	ShopperReference                 string       `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring   `json:"recurring,omitempty"`
	ShopperEmail                     string       `json:"shopperEmail,omitempty"`
	ShopperInteraction               string       `json:"shopperInteraction,omitempty"`
	ShopperIP                        string       `json:"shopperIP,omitempty"`
	ShopperLocale                    string       `json:"shopperLocale,omitempty"`
	ShopperName                      *Name        `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string       `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                int          `json:"captureDelayHours,omitempty"`
}

// AuthoriseResponse is a response structure for Adyen
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentresult
type AuthoriseResponse struct {
	PspReference   string          `json:"pspReference"`
	ResultCode     string          `json:"resultCode"`
	AuthCode       string          `json:"authCode"`
	RefusalReason  string          `json:"refusalReason"`
	IssuerURL      string          `json:"issuerUrl"`
	MD             string          `json:"md"`
	PaRequest      string          `json:"paRequest"`
	AdditionalData *AdditionalData `json:"additionalData,omitempty"`
}

// AdditionalData stores encrypted information about customer's credit card
type AdditionalData struct {
	Content            string      `json:"card.encrypted.json,omitempty"`
	AliasType          string      `json:"aliasType,omitempty"`
	Alias              string      `json:"alias,omitempty"`
	ExpiryDate         string      `json:"expiryDate,omitempty`
	CardBin            string      `json:cardBin,omitempty`
	CardSummary        string      `json:cardSummary,omitempty`
	PaymentMethod      string      `json:paymentMethod,omitempty`
	CardPaymentMethod  string      `json:cardPaymentMethod,omitempty`
	CardIssuingCountry string      `json:cardIssuingCountry,omitempty`
	ExecuteThreeD      *StringBool `json:"executeThreeD,omitempty"`
}

// BrowserInfo hold information on the user browser
type BrowserInfo struct {
	AcceptHeader string `json:"acceptHeader"`
	UserAgent    string `json:"userAgent"`
}

// Recurring hold the behavior for a future payment : could be ONECLICK or RECURRING
type Recurring struct {
	Contract string `json:"contract"`
}

/*************
* Payment 3D *
*************/

// Authorise3D structure for Authorisation request (card is not encrypted)
//
// https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest3d
type Authorise3D struct {
	BillingAddress  *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress *Address     `json:"deliveryAddress,omitempty"`
	MD              string       `json:"md"`
	MerchantAccount string       `json:"merchantAccount"`
	BrowserInfo     *BrowserInfo `json:"browserInfo"`
	PaResponse      string       `json:"paResponse"`
	ShopperEmail    string       `json:"shopperEmail,omitempty"`
	ShopperIP       string       `json:"shopperIP,omitempty"`
	ShopperLocale   string       `json:"shopperLocale,omitempty"`
	ShopperName     *Name        `json:"shopperName,omitempty"`
}

/*******************
* Directory lookup *
*******************/

// DirectoryLookupRequest - get list of available payment methods based on skin, country and order details
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryrequest
// CountryCode could be used to test local payment methods, if client's IP is from different country
type DirectoryLookupRequest struct {
	CurrencyCode      string `url:"currencyCode"`
	MerchantAccount   string `url:"merchantAccount"`
	PaymentAmount     int    `url:"paymentAmount"`
	SkinCode          string `url:"skinCode"`
	MerchantReference string `url:"merchantReference"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
}

// DirectoryLookupResponse - api response for DirectoryLookupRequest
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryresponse
type DirectoryLookupResponse struct {
	PaymentMethods []PaymentMethod `json:"paymentMethods"`
}

// PaymentMethod - structure for single payment method in directory look up response
//
// Part of DirectoryLookupResponse
type PaymentMethod struct {
	BrandCode string   `json:"brandCode"`
	Name      string   `json:"name"`
	Logos     logos    `json:"logos"`
	Issuers   []issuer `json:"issuers"`
}

// logos - payment method logos
//
// Part of DirectoryLookupResponse
type logos struct {
	Normal string `json:"normal"`
	Small  string `json:"small"`
	Tiny   string `json:"tiny"`
}

// issuer - bank issuer type
//
// Part of DirectoryLookupResponse
type issuer struct {
	IssuerID string `json:"issuerId"`
	Name     string `json:"name"`
}

/***********
* Skip HPP *
***********/

// SkipHppRequest contains data that would be used to create Adyen HPP redirect URL
//
// Link: https://docs.adyen.com/developers/ecommerce-integration/local-payment-methods
//
// Request description: https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#skipdetailsrequest
type SkipHppRequest struct {
	MerchantReference string `url:"merchantReference"`
	PaymentAmount     int    `url:"paymentAmount"`
	CurrencyCode      string `url:"currencyCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
	SkinCode          string `url:"skinCode"`
	MerchantAccount   string `url:"merchantAccount"`
	ShopperLocale     string `url:"shopperLocale"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	BrandCode         string `url:"brandCode"`
	IssuerID          string `url:"issuerId"`
}
<commit_msg>change capturedelayhours to pointer<commit_after>package adyen

/**********
* Payment *
**********/

// One-click functionality gives the shopper the option to store their payment details with the merchant, within the Adyen environment.
//
// In this type of transaction, the shopper needs to enter the CVC code for the transaction to get through.
//
// Link: https://docs.adyen.com/developers/api-reference/payments-api#recurring
const (
	RecurringPaymentOneClick              = "ONECLICK"
	RecurringPaymentRecurring             = "RECURRING"
	ShopperInteractionContAuth            = "ContAuth"
	SelectRecurringDetailReferenceLatests = "LATEST"
)

// AuthoriseEncrypted structure for Authorisation request (with encrypted card information)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type AuthoriseEncrypted struct {
	AdditionalData                   *AdditionalData `json:"additionalData,omitempty"`
	Amount                           *Amount         `json:"amount"`
	BillingAddress                   *Address        `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address        `json:"deliveryAddress,omitempty"`
	Reference                        string          `json:"reference"`
	MerchantAccount                  string          `json:"merchantAccount"`
	ShopperReference                 string          `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring      `json:"recurring,omitempty"`
	ShopperEmail                     string          `json:"shopperEmail,omitempty"`
	ShopperInteraction               string          `json:"shopperInteraction,omitempty"`
	ShopperIP                        string          `json:"shopperIP,omitempty"`
	ShopperLocale                    string          `json:"shopperLocale,omitempty"`
	ShopperName                      *Name           `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string          `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo    `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                *int            `json:"captureDelayHours,omitempty"`
}

// Authorise structure for Authorisation request (card is not encrypted)
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest
type Authorise struct {
	Card                             *Card        `json:"card,omitempty"`
	Amount                           *Amount      `json:"amount"`
	BillingAddress                   *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress                  *Address     `json:"deliveryAddress,omitempty"`
	Reference                        string       `json:"reference"`
	MerchantAccount                  string       `json:"merchantAccount"`
	ShopperReference                 string       `json:"shopperReference,omitempty"` // Mandatory for recurring payment
	Recurring                        *Recurring   `json:"recurring,omitempty"`
	ShopperEmail                     string       `json:"shopperEmail,omitempty"`
	ShopperInteraction               string       `json:"shopperInteraction,omitempty"`
	ShopperIP                        string       `json:"shopperIP,omitempty"`
	ShopperLocale                    string       `json:"shopperLocale,omitempty"`
	ShopperName                      *Name        `json:"shopperName,omitempty"`
	SelectedRecurringDetailReference string       `json:"selectedRecurringDetailReference,omitempty"`
	BrowserInfo                      *BrowserInfo `json:"browserInfo,omitempty"` // Required for a 3DS process
	CaptureDelayHours                *int         `json:"captureDelayHours,omitempty"`
}

// AuthoriseResponse is a response structure for Adyen
//
// Link - https://docs.adyen.com/developers/api-reference/payments-api#paymentresult
type AuthoriseResponse struct {
	PspReference   string          `json:"pspReference"`
	ResultCode     string          `json:"resultCode"`
	AuthCode       string          `json:"authCode"`
	RefusalReason  string          `json:"refusalReason"`
	IssuerURL      string          `json:"issuerUrl"`
	MD             string          `json:"md"`
	PaRequest      string          `json:"paRequest"`
	AdditionalData *AdditionalData `json:"additionalData,omitempty"`
}

// AdditionalData stores encrypted information about customer's credit card
type AdditionalData struct {
	Content            string      `json:"card.encrypted.json,omitempty"`
	AliasType          string      `json:"aliasType,omitempty"`
	Alias              string      `json:"alias,omitempty"`
	ExpiryDate         string      `json:"expiryDate,omitempty`
	CardBin            string      `json:cardBin,omitempty`
	CardSummary        string      `json:cardSummary,omitempty`
	PaymentMethod      string      `json:paymentMethod,omitempty`
	CardPaymentMethod  string      `json:cardPaymentMethod,omitempty`
	CardIssuingCountry string      `json:cardIssuingCountry,omitempty`
	ExecuteThreeD      *StringBool `json:"executeThreeD,omitempty"`
}

// BrowserInfo hold information on the user browser
type BrowserInfo struct {
	AcceptHeader string `json:"acceptHeader"`
	UserAgent    string `json:"userAgent"`
}

// Recurring hold the behavior for a future payment : could be ONECLICK or RECURRING
type Recurring struct {
	Contract string `json:"contract"`
}

/*************
* Payment 3D *
*************/

// Authorise3D structure for Authorisation request (card is not encrypted)
//
// https://docs.adyen.com/developers/api-reference/payments-api#paymentrequest3d
type Authorise3D struct {
	BillingAddress  *Address     `json:"billingAddress,omitempty"`
	DeliveryAddress *Address     `json:"deliveryAddress,omitempty"`
	MD              string       `json:"md"`
	MerchantAccount string       `json:"merchantAccount"`
	BrowserInfo     *BrowserInfo `json:"browserInfo"`
	PaResponse      string       `json:"paResponse"`
	ShopperEmail    string       `json:"shopperEmail,omitempty"`
	ShopperIP       string       `json:"shopperIP,omitempty"`
	ShopperLocale   string       `json:"shopperLocale,omitempty"`
	ShopperName     *Name        `json:"shopperName,omitempty"`
}

/*******************
* Directory lookup *
*******************/

// DirectoryLookupRequest - get list of available payment methods based on skin, country and order details
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryrequest
// CountryCode could be used to test local payment methods, if client's IP is from different country
type DirectoryLookupRequest struct {
	CurrencyCode      string `url:"currencyCode"`
	MerchantAccount   string `url:"merchantAccount"`
	PaymentAmount     int    `url:"paymentAmount"`
	SkinCode          string `url:"skinCode"`
	MerchantReference string `url:"merchantReference"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
}

// DirectoryLookupResponse - api response for DirectoryLookupRequest
//
// Description - https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#directoryresponse
type DirectoryLookupResponse struct {
	PaymentMethods []PaymentMethod `json:"paymentMethods"`
}

// PaymentMethod - structure for single payment method in directory look up response
//
// Part of DirectoryLookupResponse
type PaymentMethod struct {
	BrandCode string   `json:"brandCode"`
	Name      string   `json:"name"`
	Logos     logos    `json:"logos"`
	Issuers   []issuer `json:"issuers"`
}

// logos - payment method logos
//
// Part of DirectoryLookupResponse
type logos struct {
	Normal string `json:"normal"`
	Small  string `json:"small"`
	Tiny   string `json:"tiny"`
}

// issuer - bank issuer type
//
// Part of DirectoryLookupResponse
type issuer struct {
	IssuerID string `json:"issuerId"`
	Name     string `json:"name"`
}

/***********
* Skip HPP *
***********/

// SkipHppRequest contains data that would be used to create Adyen HPP redirect URL
//
// Link: https://docs.adyen.com/developers/ecommerce-integration/local-payment-methods
//
// Request description: https://docs.adyen.com/developers/api-reference/hosted-payment-pages-api#skipdetailsrequest
type SkipHppRequest struct {
	MerchantReference string `url:"merchantReference"`
	PaymentAmount     int    `url:"paymentAmount"`
	CurrencyCode      string `url:"currencyCode"`
	ShipBeforeDate    string `url:"shipBeforeDate"`
	SkinCode          string `url:"skinCode"`
	MerchantAccount   string `url:"merchantAccount"`
	ShopperLocale     string `url:"shopperLocale"`
	SessionsValidity  string `url:"sessionValidity"`
	MerchantSig       string `url:"merchantSig"`
	CountryCode       string `url:"countryCode"`
	BrandCode         string `url:"brandCode"`
	IssuerID          string `url:"issuerId"`
}
<|endoftext|>