<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	logging "log"
	"net"
	"os"
	"syscall"
)

var log *logging.Logger = logging.New(os.Stderr, "bi: ", 0)

type readWriter struct {
	io.Reader
	io.Writer
}

func NewReadWriter(r io.Reader, w io.Writer) io.ReadWriter {
	return &readWriter{r, w}
}

// Flags are only used for online mode
var onlineMode = flag.Bool("online", false, "Use online mode")
var server = flag.String("server", "punter.inf.ed.ac.uk", "server ip")
var port = flag.Int("port", 9001, "server port")
var name = flag.String("name", "blueiris", "bot name")

type HandshakeRequest struct {
	Me string `json:"me"`
}

type HandshakeResponse struct {
	You string `json:"you"`
}

type PunterID uint
type SiteID uint

// Offset of river in Map.Rivers
type RiverOffset int
type RiverOffsets []RiverOffset

// Offset of site in Map.Sites
type SiteOffset int

type ScoreValue int

type Site struct {
	ID     SiteID       `json:"id"`
	Rivers RiverOffsets `json:"rivers",omitempty`
}

type River struct {
	Source  SiteID   `json:"source"`
	Target  SiteID   `json:"target"`
	Claimed bool     `json:"claimed",omitempty`
	Owner   PunterID `json:"owner",omitempty`
}

type Map struct {
	Sites  []Site   `json:"sites"`
	Rivers []River  `json:"rivers"`
	Mines  []SiteID `json:"mines"`
	// Lookup site from siteID
	SiteMap map[SiteID]SiteOffset `json:siteMap,omitEmpty`
}

func (m *Map) DecorateMap() (err error) {
	m.SiteMap = make(map[SiteID]SiteOffset)
	for i, site := range m.Sites {
		m.SiteMap[site.ID] = SiteOffset(i)
	}
	for i, river := range m.Rivers {
		{
			sourceIndex := m.SiteMap[river.Source]
			m.Sites[sourceIndex].Rivers = append(m.Sites[sourceIndex].Rivers, RiverOffset(i))
		}
		{
			targetIndex := m.SiteMap[river.Target]
			m.Sites[targetIndex].Rivers = append(m.Sites[targetIndex].Rivers, RiverOffset(i))
		}
	}
	return
}

type SetupRequest struct {
	Punter  PunterID `json:"punter"`
	Punters int      `json:"punters"`
	Map     Map      `json:"map"`
}

type State struct {
	Punter  PunterID `json:"punter"`
	Punters int      `json:"punters"`
	Map     Map      `json:"map"`
}

type SetupResponse struct {
	Ready PunterID `json:"ready"`
	State *State   `json:"state",omitempty`
}

type Claim struct {
	Punter PunterID `json:"punter"`
	Source SiteID   `json:"source"`
	Target SiteID   `json:"target"`
}

type Pass struct {
	Punter PunterID `json:"punter"`
}

// Poor man's union type. Only one of Claim or Pass is non-nil
type Move struct {
	Claim *Claim `json:"claim",omitempty`
	Pass  *Pass  `json:"pass",omitempty`
	State *State `json:"state",omitempty`
}

func (m Move) String() string {
	if m.Claim != nil {
		return fmt.Sprintf("claim:%+v", m.Claim)
	} else if m.Pass != nil {
		return fmt.Sprintf("pass:%+v", m.Pass)
	} else {
		return "empty"
	}
}

type Moves struct {
	Moves []Move `json:"moves"`
}

type Score struct {
	Punter PunterID   `json:"punter"`
	Score  ScoreValue `json:"score"`
}

type Stop struct {
	Moves  []Move  `json:"moves"`
	Scores []Score `json:"scores"`
}

// Poor man's union. Only one of Move or Stop is non-nil
type ServerMove struct {
	Move  *Moves `json:"move",omitempty`
	Stop  *Stop  `json:"stop",omitempty`
	State *State `json:"state",omitempty`
}

func findServer() (conn net.Conn, err error) {
	p := *port
	serverAddress := fmt.Sprintf("%s:%d", *server, p)
	// log.Printf("Trying %s", serverAddress)
	conn, err = net.Dial("tcp", serverAddress)
	if err == nil {
		return
	}
	log.Fatal()
	return
}

func send(writer io.Writer, d interface{}) (err error) {
	var b []byte
	buf := bytes.NewBuffer(nil)
	err = json.NewEncoder(buf).Encode(d)
	if err != nil {
		return
	}
	b = buf.Bytes()
	// Don't need to send linefeed at end
	b = b[:len(b)-1]
	msg := fmt.Sprintf("%d:%s", len(b), b)
	// log.Printf("Sending: %s", msg)
	_, err = io.WriteString(writer, msg)
	// log.Printf("sent %d bytes", n)
	if err != nil {
		return
	}
	return err
}

func receiveRaw(reader io.Reader) (b1 []byte, err error) {
	var i int
	_, err = fmt.Fscanf(reader, "%d:", &i)
	if err != nil {
		return
	}
	// log.Printf("Reading %d bytes", i)
	b1 = make([]byte, i)
	offset := 0
	for offset < i {
		var n int
		n, err = reader.Read(b1[offset:])
		if err != nil {
			return
		}
		offset += n
	}
	// log.Printf("Bytes: %d %s", len(b1), string(b1))
	// listen for reply
	return
}

func receive(conn io.Reader, d interface{}) (err error) {
	var b1 []byte
	b1, err = receiveRaw(conn)
	if err != nil {
		return
	}
	// log.Printf("Received Bytes: %d %s", len(b1), string(b1))
	err = json.Unmarshal(b1, d)
	return err
}

func handshake(conn io.ReadWriter) (err error) {
	handshakeRequest := HandshakeRequest{*name}
	err = send(conn, &handshakeRequest)
	if err != nil {
		return
	}

	// log.Printf("Waiting for reply")
	// listen for reply
	var handshakeResponse HandshakeResponse
	err = receive(conn, &handshakeResponse)
	// log.Printf("response %v\n", handshakeResponse)
	if err != nil {
		return
	}
	return
}

func setup(conn io.ReadWriter) (state State, err error) {
	var setupRequest SetupRequest
	err = receive(conn, &setupRequest)
	if err != nil {
		return
	}
	// log.Printf("Received setupRequest %v", setupRequest)
	state, err = doSetup(conn, setupRequest)
	return
}

func doSetup(writer io.Writer, setupRequest SetupRequest) (state State, err error) {
	state.Punter = setupRequest.Punter
	state.Punters = setupRequest.Punters
	state.Map = setupRequest.Map
	state.Map.DecorateMap()
	setupResponse := SetupResponse{setupRequest.Punter, nil}
	if !*onlineMode {
		setupResponse.State = &state
	}
	err = send(writer, &setupResponse)
	return
}

func processServerMove(conn io.ReadWriter, state State, serverMove ServerMove) (err error) {
	if serverMove.Move != nil {
		return doMoves(conn, state, *serverMove.Move)
	} else if serverMove.Stop != nil {
		return doStop(state, *serverMove.Stop)
	} else {
		return
	}
}

func doMoves(conn io.ReadWriter, state State, moves Moves) (err error) {
	err = processServerMoves(conn, state, moves)
	if err != nil {
		return
	}
	err = pickMove(conn, state)
	if err != nil {
		return
	}
	return
}

func processServerMoves(conn io.ReadWriter, state State, moves Moves) (err error) {
	for _, move := range moves.Moves {
		if move.Claim != nil {
			for riverIndex, river := range state.Map.Rivers {
				if river.Source == move.Claim.Source &&
					river.Target == move.Claim.Target {
					river.Claimed = true
					river.Owner = move.Claim.Punter
					state.Map.Rivers[riverIndex] = river
					break
				}
			}
		}
	}
	return
}

func pickMove(conn io.ReadWriter, state State) (err error) {
	var move Move
	move, err = pickFirstUnclaimed(state)
	if err != nil {
		return
	}
	if !*onlineMode {
		move.State = &state
	}
	log.Printf("Move: %v", move)
	err = send(conn, move)
	if err != nil {
		return
	}
	return
}

func doStop(state State, stop Stop) (err error) {
	log.Printf("I am punter %d", state.Punter)
	for _, score := range stop.Scores {
		log.Printf("Punter: %d score: %d", score.Punter, score.Score)
	}
	return
}

func pickPass(state State) (move Move, err error) {
	move.Pass = &Pass{state.Punter}
	return
}

func pickFirstUnclaimed(state State) (move Move, err error) {
	for _, river := range state.Map.Rivers {
		if river.Claimed == false {
			move.Claim = &Claim{state.Punter, river.Source, river.Target}
			return
		}
	}
	return pickPass(state)
}

func runOnlineMode() (err error) {
	conn, err := findServer()
	if err != nil {
		return
	}
	// log.Printf("connected")
	err = handshake(conn)
	if err != nil {
		return
	}

	// log.Printf("setup")

	setupRequest, err := setup(conn)
	if err != nil {
		return
	}

	// log.Printf("game")
	for {
		var serverMove ServerMove
		err = receive(conn, &serverMove)
		if err != nil {
			return
		}
		err = processServerMove(conn, setupRequest, serverMove)
		if err != nil {
			return
		}
	}
	return
}

func runOfflineMode() (err error) {
	conn := NewReadWriter(os.Stdin, os.Stdout)
	// log.Printf("connected")
	err = handshake(conn)
	if err != nil {
		return
	}

	var b1 []byte
	b1, err = receiveRaw(conn)
	if err != nil {
		return
	}
	var serverRequest map[string]interface{}
	err = json.Unmarshal(b1, &serverRequest)
	if err != nil {
		return
	}

	if serverRequest["punter"] != nil {
		// log.Printf("setup")
		var setupRequest SetupRequest
		err = json.Unmarshal(b1, &setupRequest)
		if err != nil {
			return
		}
		_, err = doSetup(conn, setupRequest)
		return
	} else if serverRequest["move"] != nil {
		// log.Printf("move")
		var serverMove ServerMove
		err = json.Unmarshal(b1, &serverMove)
		if err != nil {
			return
		}
		return doMoves(conn, *serverMove.State, *serverMove.Move)
	} else if serverRequest["stop"] != nil {
		// log.Printf("stop")
		var serverMove ServerMove
		err = json.Unmarshal(b1, &serverMove)
		if err != nil {
			return
		}
		return doStop(*serverMove.State, *serverMove.Stop)
	} else {
		err = fmt.Errorf("Unknown server request %s", b1)
	}
	return
}

// This is needed when running under lamduct on VM. Otherwise
// EAGAIN.
func fixIO() {
	fd := int(os.Stdin.Fd())
	syscall.SetNonblock(fd, false)
}

func main() {
	var err error
	flag.Parse()
	fixIO()
	if *onlineMode {
		// log.Printf("online mode")
		err = runOnlineMode()
	} else {
		// log.Printf("offline mode")
		err = runOfflineMode()
	}
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Turned on some logging<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	logging "log"
	"net"
	"os"
	"syscall"
)

var log *logging.Logger = logging.New(os.Stderr, "bi: ", 0)

type readWriter struct {
	io.Reader
	io.Writer
}

func NewReadWriter(r io.Reader, w io.Writer) io.ReadWriter {
	return &readWriter{r, w}
}

// Flags are only used for online mode
var onlineMode = flag.Bool("online", false, "Use online mode")
var server = flag.String("server", "punter.inf.ed.ac.uk", "server ip")
var port = flag.Int("port", 9001, "server port")
var name = flag.String("name", "blueiris", "bot name")

type HandshakeRequest struct {
	Me string `json:"me"`
}

type HandshakeResponse struct {
	You string `json:"you"`
}

type PunterID uint
type SiteID uint

// Offset of river in Map.Rivers
type RiverOffset int
type RiverOffsets []RiverOffset

// Offset of site in Map.Sites
type SiteOffset int

type ScoreValue int

type Site struct {
	ID     SiteID       `json:"id"`
	Rivers RiverOffsets `json:"rivers",omitempty`
}

type River struct {
	Source  SiteID   `json:"source"`
	Target  SiteID   `json:"target"`
	Claimed bool     `json:"claimed",omitempty`
	Owner   PunterID `json:"owner",omitempty`
	// Use for searching
	Flags int `json:-`
}

type Map struct {
	Sites  []Site   `json:"sites"`
	Rivers []River  `json:"rivers"`
	Mines  []SiteID `json:"mines"`
	// Lookup site from siteID
	SiteMap map[SiteID]SiteOffset `json:siteMap,omitEmpty`
}

func (m *Map) DecorateMap() (err error) {
	m.SiteMap = make(map[SiteID]SiteOffset)
	for i, site := range m.Sites {
		m.SiteMap[site.ID] = SiteOffset(i)
	}
	for i, river := range m.Rivers {
		{
			sourceIndex := m.SiteMap[river.Source]
			m.Sites[sourceIndex].Rivers = append(m.Sites[sourceIndex].Rivers, RiverOffset(i))
		}
		{
			targetIndex := m.SiteMap[river.Target]
			m.Sites[targetIndex].Rivers = append(m.Sites[targetIndex].Rivers, RiverOffset(i))
		}
	}
	return
}

type SetupRequest struct {
	Punter  PunterID `json:"punter"`
	Punters int      `json:"punters"`
	Map     Map      `json:"map"`
}

type State struct {
	Punter  PunterID `json:"punter"`
	Punters int      `json:"punters"`
	Map     Map      `json:"map"`
}

type SetupResponse struct {
	Ready PunterID `json:"ready"`
	State *State   `json:"state",omitempty`
}

type Claim struct {
	Punter PunterID `json:"punter"`
	Source SiteID   `json:"source"`
	Target SiteID   `json:"target"`
}

type Pass struct {
	Punter PunterID `json:"punter"`
}

// Poor man's union type. Only one of Claim or Pass is non-nil
type Move struct {
	Claim *Claim `json:"claim",omitempty`
	Pass  *Pass  `json:"pass",omitempty`
	State *State `json:"state",omitempty`
}

func (m Move) String() string {
	if m.Claim != nil {
		return fmt.Sprintf("claim:%+v", m.Claim)
	} else if m.Pass != nil {
		return fmt.Sprintf("pass:%+v", m.Pass)
	} else {
		return "empty"
	}
}

type Moves struct {
	Moves []Move `json:"moves"`
}

type Score struct {
	Punter PunterID   `json:"punter"`
	Score  ScoreValue `json:"score"`
}

type Stop struct {
	Moves  []Move  `json:"moves"`
	Scores []Score `json:"scores"`
}

// Poor man's union. Only one of Move or Stop is non-nil
type ServerMove struct {
	Move  *Moves `json:"move",omitempty`
	Stop  *Stop  `json:"stop",omitempty`
	State *State `json:"state",omitempty`
}

func findServer() (conn net.Conn, err error) {
	p := *port
	serverAddress := fmt.Sprintf("%s:%d", *server, p)
	// log.Printf("Trying %s", serverAddress)
	conn, err = net.Dial("tcp", serverAddress)
	if err == nil {
		return
	}
	log.Fatal()
	return
}

func send(writer io.Writer, d interface{}) (err error) {
	var b []byte
	buf := bytes.NewBuffer(nil)
	err = json.NewEncoder(buf).Encode(d)
	if err != nil {
		return
	}
	b = buf.Bytes()
	// Don't need to send linefeed at end
	b = b[:len(b)-1]
	msg := fmt.Sprintf("%d:%s", len(b), b)
	// log.Printf("Sending: %s", msg)
	_, err = io.WriteString(writer, msg)
	// log.Printf("sent %d bytes", n)
	if err != nil {
		return
	}
	return err
}

func receiveRaw(reader io.Reader) (b1 []byte, err error) {
	var i int
	_, err = fmt.Fscanf(reader, "%d:", &i)
	if err != nil {
		return
	}
	// log.Printf("Reading %d bytes", i)
	b1 = make([]byte, i)
	offset := 0
	for offset < i {
		var n int
		n, err = reader.Read(b1[offset:])
		if err != nil {
			return
		}
		offset += n
	}
	// log.Printf("Bytes: %d %s", len(b1), string(b1))
	// listen for reply
	return
}

func receive(conn io.Reader, d interface{}) (err error) {
	var b1 []byte
	b1, err = receiveRaw(conn)
	if err != nil {
		return
	}
	// log.Printf("Received Bytes: %d %s", len(b1), string(b1))
	err = json.Unmarshal(b1, d)
	return err
}

func handshake(conn io.ReadWriter) (err error) {
	handshakeRequest := HandshakeRequest{*name}
	err = send(conn, &handshakeRequest)
	if err != nil {
		return
	}

	// log.Printf("Waiting for reply")
	// listen for reply
	var handshakeResponse HandshakeResponse
	err = receive(conn, &handshakeResponse)
	// log.Printf("response %v\n", handshakeResponse)
	if err != nil {
		return
	}
	return
}

func setup(conn io.ReadWriter) (state *State, err error) {
	var setupRequest SetupRequest
	err = receive(conn, &setupRequest)
	if err != nil {
		return
	}
	if pretty, err2 := json.MarshalIndent(&setupRequest, "", " "); err2 == nil {
		log.Printf("Received setupRequest %s", pretty)
	}
	state, err = doSetup(conn, setupRequest)
	return
}

func doSetup(writer io.Writer, setupRequest SetupRequest) (state *State, err error) {
	state = &State{}
	state.Punter = setupRequest.Punter
	state.Punters = setupRequest.Punters
	state.Map = setupRequest.Map
	state.Map.DecorateMap()
	setupResponse := SetupResponse{setupRequest.Punter, nil}
	if !*onlineMode {
		setupResponse.State = state
	}
	err = send(writer, &setupResponse)
	return
}

func processServerMove(conn io.ReadWriter, state *State, serverMove ServerMove) (err error) {
	if serverMove.Move != nil {
		return doMoves(conn, state, *serverMove.Move)
	} else if serverMove.Stop != nil {
		return doStop(state, *serverMove.Stop)
	} else {
		return
	}
}

func doMoves(conn io.ReadWriter, state *State, moves Moves) (err error) {
	err = processServerMoves(conn, state, moves)
	if err != nil {
		return
	}
	err = pickMove(conn, state)
	if err != nil {
		return
	}
	return
}

type RiverEdge struct {
	Source SiteID
	Target SiteID
}

func (state *State) SlowRiverToRiverIndex(edge RiverEdge) (ro RiverOffset) {
	ro = RiverOffset(-1)
	for riverIndex, river := range state.Map.Rivers {
		if edge.Source == river.Source && edge.Target == river.Target {
			ro = RiverOffset(riverIndex)
			break
		}
	}
	return
}

func (state *State) RiverToRiverOffset(edge RiverEdge) (ro RiverOffset) {
	sourceOffset := state.Map.SiteMap[edge.Source]
	sourceRivers := state.Map.Sites[sourceOffset].Rivers
	targetOffset := state.Map.SiteMap[edge.Target]
	targetRivers := state.Map.Sites[targetOffset].Rivers
	var shorterRivers RiverOffsets
	if len(sourceRivers) < len(targetRivers) {
		shorterRivers = sourceRivers
	} else {
		shorterRivers = targetRivers
	}
	ro = RiverOffset(-1)
	for _, riverOffset := range shorterRivers {
		river := state.Map.Rivers[riverOffset]
		if edge.Source == river.Source && edge.Target == river.Target {
			ro = riverOffset
			break
		}
	}
	return
}

func processServerMoves(conn io.ReadWriter, state *State, moves Moves) (err error) {
	for _, move := range moves.Moves {
		if move.Claim != nil {
			edge := RiverEdge{move.Claim.Source, move.Claim.Target}
			riverOffset := state.RiverToRiverOffset(edge)
			state.Map.Rivers[riverOffset].Claimed = true
			state.Map.Rivers[riverOffset].Owner = move.Claim.Punter
		}
	}
	return
}

func pickMove(conn io.ReadWriter, state *State) (err error) {
	var move Move
	move, err = pickExtendRoute(state)
	if err != nil {
		return
	}
	if !*onlineMode {
		move.State = state
	}
	log.Printf("Move: %v", move)
	err = send(conn, move)
	if err != nil {
		return
	}
	return
}

func doStop(state *State, stop Stop) (err error) {
	log.Printf("I am punter %d", state.Punter)
	for _, score := range stop.Scores {
		log.Printf("Punter: %d score: %d", score.Punter, score.Score)
	}
	return
}

func pickPass(state *State) (move Move, err error) {
	move.Pass = &Pass{state.Punter}
	return
}

func pickFirstUnclaimed(state *State) (move Move, err error) {
	for _, river := range state.Map.Rivers {
		if river.Claimed == false {
			move.Claim = &Claim{state.Punter, river.Source, river.Target}
			return
		}
	}
	return pickPass(state)
}

func (s *State) ClaimRiverOffset(riverOffset RiverOffset, owner PunterID) {
	s.Map.Rivers[riverOffset].Claimed = true
	s.Map.Rivers[riverOffset].Owner = owner
}

func (s *State) UnclaimRiverOffset(riverOffset RiverOffset) {
	s.Map.Rivers[riverOffset].Claimed = false
	s.Map.Rivers[riverOffset].Owner = PunterID(0)
}

type RiverScore struct {
	ScoreValue  ScoreValue
	RiverOffset RiverOffset
}

func pickExtendRoute(state *State) (move Move, err error) {
	best := RiverScore{ScoreValue(-1), RiverOffset(-1)}
	for _, mine := range state.Map.Mines {
		mineSiteOffset := state.Map.SiteMap[mine]
		localScore := bestMove(state, mineSiteOffset, mineSiteOffset)
		if localScore.ScoreValue > best.ScoreValue {
			best = localScore
		}
	}
	if best.ScoreValue > 0 {
		river := state.Map.Rivers[best.RiverOffset]
		move.Claim = &Claim{state.Punter, river.Source, river.Target}
		return
	}
	log.Printf("Couldn't pick extended route.")
	return pickFirstUnclaimed(state)
}

func bestMove(state *State, mineSiteOffset SiteOffset, currentOffset SiteOffset) (best RiverScore) {
	for _, riverOffset := range state.Map.Sites[currentOffset].Rivers {
		river := state.Map.Rivers[riverOffset]
		var localBest RiverScore
		if river.Claimed == false {
			localBest.ScoreValue = 1
			localBest.RiverOffset = riverOffset
		} else if river.Owner == state.Punter {
			if river.Flags == 0 {
				state.Map.Rivers[riverOffset].Flags = 1
				sourceOffset := state.Map.SiteMap[river.Source]
				targetOffset := state.Map.SiteMap[river.Target]
				var otherEndOffset SiteOffset
				if sourceOffset == currentOffset {
					otherEndOffset = targetOffset
				} else if targetOffset == currentOffset {
					otherEndOffset = sourceOffset
				} else {
					panic("Unknown offset")
				}
				localBest := bestMove(state, mineSiteOffset, otherEndOffset)
				if localBest.ScoreValue > 0 {
					// Bonus for deeper chains
					localBest.ScoreValue += 1
				}
				state.Map.Rivers[riverOffset].Flags = 0
			}
		}
		if localBest.ScoreValue > best.ScoreValue {
			best = localBest
		}
	}
	return
}

func runOnlineMode() (err error) {
	conn, err := findServer()
	if err != nil {
		return
	}
	// log.Printf("connected")
	err = handshake(conn)
	if err != nil {
		return
	}

	// log.Printf("setup")

	var state *State
	state, err = setup(conn)
	if err != nil {
		return
	}

	// log.Printf("game")
	for {
		var serverMove ServerMove
		err = receive(conn, &serverMove)
		if err != nil {
			return
		}
		err = processServerMove(conn, state, serverMove)
		if err != nil {
			return
		}
	}
	return
}

func runOfflineMode() (err error) {
	conn := NewReadWriter(os.Stdin, os.Stdout)
	// log.Printf("connected")
	err = handshake(conn)
	if err != nil {
		return
	}

	var b1 []byte
	b1, err = receiveRaw(conn)
	if err != nil {
		return
	}
	var serverRequest map[string]interface{}
	err = json.Unmarshal(b1, &serverRequest)
	if err != nil {
		return
	}

	if serverRequest["punter"] != nil {
		// log.Printf("setup")
		var setupRequest SetupRequest
		err = json.Unmarshal(b1, &setupRequest)
		if err != nil {
			return
		}
		_, err = doSetup(conn, setupRequest)
		return
	} else if serverRequest["move"] != nil {
		// log.Printf("move")
		var serverMove ServerMove
		err = json.Unmarshal(b1, &serverMove)
		if err != nil {
			return
		}
		return doMoves(conn, serverMove.State, *serverMove.Move)
	} else if serverRequest["stop"] != nil {
		// log.Printf("stop")
		var serverMove ServerMove
		err = json.Unmarshal(b1, &serverMove)
		if err != nil {
			return
		}
		return doStop(serverMove.State, *serverMove.Stop)
	} else {
		err = fmt.Errorf("Unknown server request %s", b1)
	}
	return
}

// This is needed when running under lamduct on VM. Otherwise
// EAGAIN.
func fixIO() {
	fd := int(os.Stdin.Fd())
	syscall.SetNonblock(fd, false)
}

func main() {
	var err error
	flag.Parse()
	fixIO()
	if *onlineMode {
		// log.Printf("online mode")
		err = runOnlineMode()
	} else {
		// log.Printf("offline mode")
		err = runOfflineMode()
	}
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before><commit_msg>Fixes, now should work<commit_after><|endoftext|>


<commit_before>package authorization_test

import (
	"context"
	"encoding/json"
	"errors"
	"reflect"
	"testing"

	"github.com/solher/styx/authorization"
	"github.com/solher/styx/memory"
	"github.com/solher/styx/policies"
	"github.com/solher/styx/redis"
	"github.com/solher/styx/resources"
	"github.com/solher/styx/sessions"
)

var (
	simpleResource = &resources.Resource{
		Name:     "Foobar",
		Hostname: "foo.bar.com",
	}

	simplePublicResource = &resources.Resource{
		Name:     "Foobar",
		Hostname: "foo.bar.com",
		Public:   boolCpy(true),
	}
)

var (
	emptyPoliciesSession = &sessions.Session{
		Token: "F00bAr",
	}

	onePolicySession = &sessions.Session{
		Token:    "F00bAr",
		Policies: []string{"Foo"},
	}
)

var (
	simpleGrantPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{Resource: "Foobar"},
		},
	}

	twoResourcesPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{Resource: "Foobar"},
			{Resource: "Foobar2"},
		},
	}

	deniedPathsPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{
				Resource: "Foobar",
			},
			{
				Resource: "Foobar",
				Paths:    []string{"/bar", "/bar2"},
				Deny:     boolCpy(true),
			},
		},
	}

	wildcardDeniedPathPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{
				Resource: "Foobar",
			},
			{
				Resource: "Foobar",
				Paths:    []string{"/foo/*"},
				Deny:     boolCpy(true),
				Enabled:  boolCpy(true),
			},
		},
	}

	emptyPermissionsPolicy = &policies.Policy{
		Name:        "Foo",
		Permissions: []policies.Permission{},
	}

	guestPolicy = &policies.Policy{
		Name:    "guest",
		Enabled: boolCpy(true),
		Permissions: []policies.Permission{
			{
				Resource: "Foobar",
				Paths:    []string{"/*"},
			},
		},
	}
)

// TestAuthorizeToken runs tests on the AuthorizeToken function.
func TestAuthorizeToken(t *testing.T) {
	var testCases = []struct {
		name string // Test case name

		hostname string // Input hostname
		path     string // Input path
		token    string // Input token

		policyRepoFindByNamePolicy         *policies.Policy    // Policy returned by the policy repo FindByName method
		policyRepoFindByNameError          error               // Error returned by the policy repo FindByName method
		resourceRepoFindByHostnameResource *resources.Resource // Resource returned by the resource repo FindByHostname method
		resourceRepoFindByHostnameError    error               // Error returned by the resource repo FindByHostname method
		sessionRepoFindByTokenSession      *sessions.Session   // Session returned by the session repo FindByToken method
		sessionRepoFindByTokenError        error               // Error returned by the session repo FindByToken method

		session       *sessions.Session // Expected result
		errorExpected bool              // Expected error presence
		err           error             // Expected error (ignored if errorExpected == true and error == nil)
	}{
		{
			name:     "no policies in session",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy: simpleGrantPolicy, policyRepoFindByNameError: nil,
			resourceRepoFindByHostnameResource: simpleResource, resourceRepoFindByHostnameError: nil,
			sessionRepoFindByTokenSession: emptyPoliciesSession, sessionRepoFindByTokenError: nil,
			session: nil, errorExpected: true,
		},
		{
			name:     "no permissions in policy",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         emptyPermissionsPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: true,
		},
		{
			name:     "simple grant",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         simpleGrantPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            onePolicySession, errorExpected: false,
		},
		{
			name:     "path denied",
			hostname: simpleResource.Hostname, path: "/bar2", token: "F00bAr",
			policyRepoFindByNamePolicy:         deniedPathsPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: true,
		},
		{
			name:     "path granted because of public resource",
			hostname: simplePublicResource.Hostname, path: "/bar2", token: "F00bAr",
			policyRepoFindByNamePolicy:         deniedPathsPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simplePublicResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: false,
		},
		{
			name:     "wildcarded path denied",
			hostname: simpleResource.Hostname, path: "/foo/abc/def", token: "F00bAr",
			policyRepoFindByNamePolicy:         wildcardDeniedPathPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: true,
		},
		{
			name:     "trailing slash escaped",
			hostname: simpleResource.Hostname, path: "/foo/", token: "F00bAr",
			policyRepoFindByNamePolicy:         wildcardDeniedPathPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            onePolicySession, errorExpected: false,
		},
		{
			name:     "guest policy does not exists",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         nil,
			policyRepoFindByNameError:          memory.WithErrNotFound(errors.New("policy not found")),
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      nil,
			sessionRepoFindByTokenError:        redis.WithErrNotFound(errors.New("session not found")),
			session:                            nil, errorExpected: true,
		},
		{
			name:     "guest policy grant",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         guestPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      nil,
			sessionRepoFindByTokenError:        redis.WithErrNotFound(errors.New("session not found")),
			session:                            nil, errorExpected: false,
		},
	}

	policyRepo, resourceRepo, sessionRepo := &policyRepo{}, &resourceRepo{}, &sessionRepo{}
	service := authorization.NewService(policyRepo, resourceRepo, sessionRepo)
	ctx := context.Background()
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			policyRepo.findByNamePolicy, policyRepo.findByNameError = tc.policyRepoFindByNamePolicy, tc.policyRepoFindByNameError
			resourceRepo.findByHostnameResource, resourceRepo.findByHostnameError = tc.resourceRepoFindByHostnameResource, tc.resourceRepoFindByHostnameError
			sessionRepo.findByTokenSession, sessionRepo.findByTokenError = tc.sessionRepoFindByTokenSession, tc.sessionRepoFindByTokenError
			session, err := service.AuthorizeToken(ctx, tc.hostname, tc.path, tc.token)

			if tc.errorExpected != (err != nil) {
				t.Errorf(`expected err presence to be "%v", got "%s"`, format(tc.errorExpected), format(err))
			}
			if !reflect.DeepEqual(session, tc.session) {
				t.Errorf(`expected session to be "%v", got "%v"`, format(tc.session), format(session))
			}
		})
	}
}

func format(v interface{}) string {
	if v == nil {
		return "nil"
	}
	val := reflect.ValueOf(v)
	switch val.Kind() {
	case reflect.Ptr, reflect.Interface, reflect.Array, reflect.Slice:
		if val.IsNil() {
			return "nil"
		}
	}
	switch t := v.(type) {
	case error:
		return t.Error()
	default:
		m, _ := json.Marshal(v)
		return string(m)
	}
}

func boolCpy(b bool) *bool { return &b }

type policyRepo struct {
	findByNamePolicy *policies.Policy
	findByNameError  error
}

func (r *policyRepo) FindByName(ctx context.Context, name string) (*policies.Policy, error) {
	return r.findByNamePolicy, r.findByNameError
}

type resourceRepo struct {
	findByHostnameResource *resources.Resource
	findByHostnameError    error
}

func (r *resourceRepo) FindByHostname(ctx context.Context, hostname string) (*resources.Resource, error) {
	return r.findByHostnameResource, r.findByHostnameError
}

type sessionRepo struct {
	findByTokenSession *sessions.Session
	findByTokenError   error
}

func (r *sessionRepo) FindByToken(ctx context.Context, token string) (*sessions.Session, error) {
	return r.findByTokenSession, r.findByTokenError
}
<commit_msg>Service tests fixed<commit_after>package authorization_test

import (
	"context"
	"encoding/json"
	"errors"
	"reflect"
	"testing"

	"github.com/solher/styx/authorization"
	"github.com/solher/styx/policies"
	"github.com/solher/styx/resources"
	"github.com/solher/styx/sessions"
)

var (
	simpleResource = &resources.Resource{
		Name:     "Foobar",
		Hostname: "foo.bar.com",
	}

	simplePublicResource = &resources.Resource{
		Name:     "Foobar",
		Hostname: "foo.bar.com",
		Public:   boolCpy(true),
	}
)

var (
	emptyPoliciesSession = &sessions.Session{
		Token: "F00bAr",
	}

	onePolicySession = &sessions.Session{
		Token:    "F00bAr",
		Policies: []string{"Foo"},
	}
)

var (
	simpleGrantPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{Resource: "Foobar"},
		},
	}

	twoResourcesPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{Resource: "Foobar"},
			{Resource: "Foobar2"},
		},
	}

	deniedPathsPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{
				Resource: "Foobar",
			},
			{
				Resource: "Foobar",
				Paths:    []string{"/bar", "/bar2"},
				Deny:     boolCpy(true),
			},
		},
	}

	wildcardDeniedPathPolicy = &policies.Policy{
		Name: "Foo",
		Permissions: []policies.Permission{
			{
				Resource: "Foobar",
			},
			{
				Resource: "Foobar",
				Paths:    []string{"/foo/*"},
				Deny:     boolCpy(true),
				Enabled:  boolCpy(true),
			},
		},
	}

	emptyPermissionsPolicy = &policies.Policy{
		Name:        "Foo",
		Permissions: []policies.Permission{},
	}

	guestPolicy = &policies.Policy{
		Name:    "guest",
		Enabled: boolCpy(true),
		Permissions: []policies.Permission{
			{
				Resource: "Foobar",
				Paths:    []string{"/*"},
			},
		},
	}
)

// TestAuthorizeToken runs tests on the AuthorizeToken function.
func TestAuthorizeToken(t *testing.T) {
	var testCases = []struct {
		name string // Test case name

		hostname string // Input hostname
		path     string // Input path
		token    string // Input token

		policyRepoFindByNamePolicy         *policies.Policy    // Policy returned by the policy repo FindByName method
		policyRepoFindByNameError          error               // Error returned by the policy repo FindByName method
		resourceRepoFindByHostnameResource *resources.Resource // Resource returned by the resource repo FindByHostname method
		resourceRepoFindByHostnameError    error               // Error returned by the resource repo FindByHostname method
		sessionRepoFindByTokenSession      *sessions.Session   // Session returned by the session repo FindByToken method
		sessionRepoFindByTokenError        error               // Error returned by the session repo FindByToken method

		session       *sessions.Session // Expected result
		errorExpected bool              // Expected error presence
		err           error             // Expected error (ignored if errorExpected == true and error == nil)
	}{
		{
			name:     "no policies in session",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy: simpleGrantPolicy, policyRepoFindByNameError: nil,
			resourceRepoFindByHostnameResource: simpleResource, resourceRepoFindByHostnameError: nil,
			sessionRepoFindByTokenSession: emptyPoliciesSession, sessionRepoFindByTokenError: nil,
			session: nil, errorExpected: true,
		},
		{
			name:     "no permissions in policy",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         emptyPermissionsPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: true,
		},
		{
			name:     "simple grant",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         simpleGrantPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            onePolicySession, errorExpected: false,
		},
		{
			name:     "path denied",
			hostname: simpleResource.Hostname, path: "/bar2", token: "F00bAr",
			policyRepoFindByNamePolicy:         deniedPathsPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: true,
		},
		{
			name:     "path granted because of public resource",
			hostname: simplePublicResource.Hostname, path: "/bar2", token: "F00bAr",
			policyRepoFindByNamePolicy:         deniedPathsPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simplePublicResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: false,
		},
		{
			name:     "wildcarded path denied",
			hostname: simpleResource.Hostname, path: "/foo/abc/def", token: "F00bAr",
			policyRepoFindByNamePolicy:         wildcardDeniedPathPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            nil, errorExpected: true,
		},
		{
			name:     "trailing slash escaped",
			hostname: simpleResource.Hostname, path: "/foo/", token: "F00bAr",
			policyRepoFindByNamePolicy:         wildcardDeniedPathPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      onePolicySession,
			sessionRepoFindByTokenError:        nil,
			session:                            onePolicySession, errorExpected: false,
		},
		{
			name:     "guest policy does not exists",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         nil,
			policyRepoFindByNameError:          policies.WithErrNotFound(errors.New("policy not found")),
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      nil,
			sessionRepoFindByTokenError:        sessions.WithErrNotFound(errors.New("session not found")),
			session:                            nil, errorExpected: true,
		},
		{
			name:     "guest policy grant",
			hostname: simpleResource.Hostname, path: "", token: "F00bAr",
			policyRepoFindByNamePolicy:         guestPolicy,
			policyRepoFindByNameError:          nil,
			resourceRepoFindByHostnameResource: simpleResource,
			resourceRepoFindByHostnameError:    nil,
			sessionRepoFindByTokenSession:      nil,
			sessionRepoFindByTokenError:        sessions.WithErrNotFound(errors.New("session not found")),
			session:                            nil, errorExpected: false,
		},
	}

	policyRepo, resourceRepo, sessionRepo := &policyRepo{}, &resourceRepo{}, &sessionRepo{}
	service := authorization.NewService(policyRepo, resourceRepo, sessionRepo)
	ctx := context.Background()
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			policyRepo.findByNamePolicy, policyRepo.findByNameError = tc.policyRepoFindByNamePolicy, tc.policyRepoFindByNameError
			resourceRepo.findByHostnameResource, resourceRepo.findByHostnameError = tc.resourceRepoFindByHostnameResource, tc.resourceRepoFindByHostnameError
			sessionRepo.findByTokenSession, sessionRepo.findByTokenError = tc.sessionRepoFindByTokenSession, tc.sessionRepoFindByTokenError
			session, err := service.AuthorizeToken(ctx, tc.hostname, tc.path, tc.token)

			if tc.errorExpected != (err != nil) {
				t.Errorf(`expected err presence to be "%v", got "%s"`, format(tc.errorExpected), format(err))
			}
			if !reflect.DeepEqual(session, tc.session) {
				t.Errorf(`expected session to be "%v", got "%v"`, format(tc.session), format(session))
			}
		})
	}
}

func format(v interface{}) string {
	if v == nil {
		return "nil"
	}
	val := reflect.ValueOf(v)
	switch val.Kind() {
	case reflect.Ptr, reflect.Interface, reflect.Array, reflect.Slice:
		if val.IsNil() {
			return "nil"
		}
	}
	switch t := v.(type) {
	case error:
		return t.Error()
	default:
		m, _ := json.Marshal(v)
		return string(m)
	}
}

func boolCpy(b bool) *bool { return &b }

type policyRepo struct {
	findByNamePolicy *policies.Policy
	findByNameError  error
}

func (r *policyRepo) FindByName(ctx context.Context, name string) (*policies.Policy, error) {
	return r.findByNamePolicy, r.findByNameError
}

type resourceRepo struct {
	findByHostnameResource *resources.Resource
	findByHostnameError    error
}

func (r *resourceRepo) FindByHostname(ctx context.Context, hostname string) (*resources.Resource, error) {
	return r.findByHostnameResource, r.findByHostnameError
}

type sessionRepo struct {
	findByTokenSession *sessions.Session
	findByTokenError   error
}

func (r *sessionRepo) FindByToken(ctx context.Context, token string) (*sessions.Session, error) {
	return r.findByTokenSession, r.findByTokenError
}
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2017 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/stefanwichmann/go.hue"
	"strconv"
	"strings"
	"time"
)

var lightsSupportingDimming = []string{"Dimmable Light", "Color Temperature Light", "Color Light", "Extended Color Light"}
var lightsSupportingColorTemperature = []string{"Color Temperature Light", "Extended Color Light"}
var lightsSupportingXYColor = []string{"Color Light", "Extended Color Light"}

// Light represents a single physical hue light in your system.
// It is used to read and write it's state.
type Light struct {
	ID                       int
	Name                     string
	HueLight                 hue.Light
	SavedLightState          LightState
	CurrentLightState        LightState
	TargetLightState         LightState
	Scheduled                bool
	On                       bool
	Reachable                bool
	Tracking                 bool
	Automatic                bool
	Dimmable                 bool
	SupportsColorTemperature bool
	SupportsXYColor          bool
	Configuration            *Configuration
	Schedule                 Schedule
	Interval                 Interval
}

const lightUpdateIntervalInSeconds = 1
const lightTransitionIntervalInSeconds = 1
const stateUpdateIntervalInSeconds = 60

func (light *Light) updateCyclic(configuration *Configuration) {
	light.Configuration = configuration

	// Filter devices we can't control
	if !light.Dimmable && !light.SupportsXYColor && !light.SupportsColorTemperature {
		log.Printf("💡 Device %v doesn't support any functionality Kelvin uses. Ignoring...", light.Name)
		return
	}

	light.updateSchedule()
	light.updateInterval()
	light.updateTargetLightState()

	// Start cyclic polling
	log.Debugf("💡 Light %s: Starting cyclic update...", light.Name)
	lightUpdateTick := time.Tick(lightUpdateIntervalInSeconds * time.Second)
	stateUpdateTick := time.Tick(stateUpdateIntervalInSeconds * time.Second)
	for {
		select {
		case <-time.After(time.Until(light.Schedule.endOfDay) + 1*time.Second):
			// Day has ended, calculate new schedule
			light.updateSchedule()
		case <-stateUpdateTick:
			// update interval and color every minute
			light.updateInterval()
			light.updateTargetLightState()
		case <-lightUpdateTick:
			light.update()
		}
	}
}

func (light *Light) initialize() error {
	attr, err := light.HueLight.GetLightAttributes()
	if err != nil {
		return err
	}

	// initialize non changing values
	light.Name = attr.Name
	light.Dimmable = containsString(lightsSupportingDimming, attr.Type)
	light.SupportsColorTemperature = containsString(lightsSupportingColorTemperature, attr.Type)
	light.SupportsXYColor = containsString(lightsSupportingXYColor, attr.Type)

	light.updateCurrentLightState()
	return nil
}

func (light *Light) updateCurrentLightState() error {
	attr, err := light.HueLight.GetLightAttributes()
	if err != nil {
		return err
	}
	light.Reachable = attr.State.Reachable
	if !light.Reachable {
		light.On = false
		light.CurrentLightState = LightState{0, []float32{0, 0}, 0}
	} else {
		light.On = attr.State.On
		light.CurrentLightState = lightStateFromHueValues(attr.State.Ct, attr.State.Xy, attr.State.Bri)
	}
	return nil
}

func (light *Light) update() error {
	// refresh current state
	light.updateCurrentLightState()

	// is the light associated to any schedule?
	if !light.Scheduled {
		return nil
	}

	// Light reachable or on?
	if !light.Reachable || !light.On {
		if light.Tracking {
			log.Printf("💡 Light %s is no longer reachable or turned on. Clearing state.", light.Name)
			light.Tracking = false
			light.Automatic = false
			return nil
		}

		// ignore light because we are not tracking it.
		return nil
	}

	// did the light just appear?
	if !light.Tracking {
		log.Printf("💡 Light %s just appeared. Initializing state to %vK at %v%% brightness.", light.Name, light.TargetLightState.ColorTemperature, light.TargetLightState.Brightness)

		// For initialization we set the state again and again for 10 seconds
		// because during startup the zigbee communication might be unstable
		for index := 0; index < 9; index++ {
			light.setLightState(light.TargetLightState)
		}
		// safe the state of the last iteration
		setLightState, err := light.setLightState(light.TargetLightState)
		if err != nil {
			return err
		}
		light.SavedLightState = setLightState
		//light.TargetLightState = setLightState
		light.Tracking = true
		light.Automatic = true
		log.Debugf("💡 Light %s was updated to %vK at %v%% brightness", light.Name, light.TargetLightState.ColorTemperature, light.TargetLightState.Brightness)
		return nil
	}

	// light in manual state
	if !light.Automatic {
		return nil
	}

	// did the user manually change the light state?
	if !light.SavedLightState.equals(light.CurrentLightState) {
		if log.GetLevel() == log.DebugLevel {
			log.Debugf("💡 Light %s was manually changed. Current state: %+v - Saved state: %+v", light.Name, light.CurrentLightState, light.SavedLightState)
		} else {
			log.Printf("💡 Light %s was changed manually. Disabling Kelvin.", light.Name)
		}
		light.Automatic = false
		return nil
	}

	// Update needed?
	if light.TargetLightState.equals(light.CurrentLightState) {
		return nil
	}
	log.Debugf("💡 Light %s - Target and Current state differ: %v, %v", light.Name, light.TargetLightState, light.CurrentLightState)

	// Light is reachable, turned on and in automatic state. Update to new state.
	setLightState, err := light.setLightState(light.TargetLightState)
	if err != nil {
		return err
	}

	// Debug: Compare values
	if !setLightState.equals(light.TargetLightState) {
		log.Debugf("💡 Light %s - Target and Set state differ: %v, %v", light.Name, light.TargetLightState, setLightState)
	}

	light.SavedLightState = setLightState
	//light.TargetLightState = setLightState
	log.Printf("💡 Light %s was updated to %vK at %v%% brightness", light.Name, light.TargetLightState.ColorTemperature, light.TargetLightState.Brightness)
	return nil
}

func (light *Light) setLightState(state LightState) (LightState, error) {
	// Don't send repeated "On" as this slows the bridge down (see https://developers.meethue.com/faq-page #Performance)
	var hueLightState hue.SetLightState
	colorTemperature, color, brightness := state.convertValuesToHue()
	if light.SupportsXYColor && (state.Color[0] != 0 || state.Color[1] != 0) {
		hueLightState.Xy = []float32{color[0], color[1]}
	} else if light.SupportsColorTemperature && state.ColorTemperature != 0 {
		hueLightState.Ct = strconv.Itoa(colorTemperature)
	}
	if light.Dimmable && state.Brightness != 0 {
		hueLightState.Bri = strconv.Itoa(brightness)
	}
	hueLightState.TransitionTime = strconv.Itoa(lightTransitionIntervalInSeconds * 10) // conversion to 100ms-value

	results, err := light.HueLight.SetState(hueLightState)
	if err != nil {
		return LightState{0, []float32{0, 0}, 0}, err
	}

	// iterate over result to acquire set values
	for _, result := range results {
		for key, value := range result.Success {
			path := strings.Split(key, "/")

			switch path[len(path)-1] {
			case "xy":
				color = []float32{} // clear old color values
				for _, elem := range value.([]interface{}) {
					color = append(color, float32(elem.(float64)))
				}
			case "bri":
				brightness = int(value.(float64))
			case "ct":
				colorTemperature = int(value.(float64))
			}
		}
	}
	setLightState := lightStateFromHueValues(colorTemperature, color, brightness)

	// wait while the light is in transition before returning
	time.Sleep(lightTransitionIntervalInSeconds + 1*time.Second)
	return setLightState, nil
}

func (light *Light) updateConfiguration(configuration *Configuration) {
	light.Configuration = configuration
	light.updateSchedule()
	light.updateInterval()
	light.updateTargetLightState()
}

func (light *Light) updateSchedule() {
	newSchedule, err := light.Configuration.lightScheduleForDay(light.ID, time.Now())
	if err != nil {
		log.Printf("💡 Light %v is not associated to any schedule. Ignoring...", light.Name)
		light.Schedule = newSchedule // assign empty schedule
		light.Scheduled = false
		return
	}
	light.Schedule = newSchedule
	light.Scheduled = true
	log.Printf("💡 Light %s: Activating schedule for %v (sunrise: %v, sunset: %v)", light.Name, light.Schedule.endOfDay.Format("Jan 2 2006"), light.Schedule.sunrise.Time.Format("15:04"), light.Schedule.sunset.Time.Format("15:04"))
	light.updateInterval()
}

func (light *Light) updateInterval() {
	if !light.Scheduled {
		log.Debugf("💡 Light %v is not associated to any schedule. No interval to update...", light.Name)
		return
	}

	newInterval, err := light.Schedule.currentInterval(time.Now())
	if err != nil {
		log.Printf("💡 Light %v has no active interval. Ignoring...", light.Name)
		light.Interval = newInterval // assign empty interval
		return
	}
	if newInterval != light.Interval {
		light.Interval = newInterval
		log.Printf("💡 Light %s: Activating interval %v - %v", light.Name, light.Interval.Start.Time.Format("15:04"), light.Interval.End.Time.Format("15:04"))
	}
}

func (light *Light) updateTargetLightState() {
	if !light.Scheduled {
		log.Debugf("💡 Light %v is not associated to any schedule. No target light state to update...", light.Name)
		return
	}

	newLightState := light.Interval.calculateLightStateInInterval(time.Now())
	newLightState.isValid()

	// First initialization of the TargetLightState
	if light.TargetLightState.ColorTemperature == 0 && len(light.TargetLightState.Color) == 0 && light.TargetLightState.Brightness == 0 {
		light.TargetLightState = newLightState
		log.Debugf("💡 Light %s: Initialized target state to %+v", light.Name, light.TargetLightState)
		return
	}

	if !light.TargetLightState.equals(newLightState) {
		light.TargetLightState = newLightState
		log.Debugf("💡 Light %s: Updated target state to %+v", light.Name, light.TargetLightState)
	}
}
<commit_msg>Validate lightstate before use<commit_after>// MIT License
//
// Copyright (c) 2017 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/stefanwichmann/go.hue"
	"strconv"
	"strings"
	"time"
)

var lightsSupportingDimming = []string{"Dimmable Light", "Color Temperature Light", "Color Light", "Extended Color Light"}
var lightsSupportingColorTemperature = []string{"Color Temperature Light", "Extended Color Light"}
var lightsSupportingXYColor = []string{"Color Light", "Extended Color Light"}

// Light represents a single physical hue light in your system.
// It is used to read and write it's state.
type Light struct {
	ID                       int
	Name                     string
	HueLight                 hue.Light
	SavedLightState          LightState
	CurrentLightState        LightState
	TargetLightState         LightState
	Scheduled                bool
	On                       bool
	Reachable                bool
	Tracking                 bool
	Automatic                bool
	Dimmable                 bool
	SupportsColorTemperature bool
	SupportsXYColor          bool
	Configuration            *Configuration
	Schedule                 Schedule
	Interval                 Interval
}

const lightUpdateIntervalInSeconds = 1
const lightTransitionIntervalInSeconds = 1
const stateUpdateIntervalInSeconds = 60

func (light *Light) updateCyclic(configuration *Configuration) {
	light.Configuration = configuration

	// Filter devices we can't control
	if !light.Dimmable && !light.SupportsXYColor && !light.SupportsColorTemperature {
		log.Printf("💡 Device %v doesn't support any functionality Kelvin uses. Ignoring...", light.Name)
		return
	}

	light.updateSchedule()
	light.updateInterval()
	light.updateTargetLightState()

	// Start cyclic polling
	log.Debugf("💡 Light %s: Starting cyclic update...", light.Name)
	lightUpdateTick := time.Tick(lightUpdateIntervalInSeconds * time.Second)
	stateUpdateTick := time.Tick(stateUpdateIntervalInSeconds * time.Second)
	for {
		select {
		case <-time.After(time.Until(light.Schedule.endOfDay) + 1*time.Second):
			// Day has ended, calculate new schedule
			light.updateSchedule()
		case <-stateUpdateTick:
			// update interval and color every minute
			light.updateInterval()
			light.updateTargetLightState()
		case <-lightUpdateTick:
			light.update()
		}
	}
}

func (light *Light) initialize() error {
	attr, err := light.HueLight.GetLightAttributes()
	if err != nil {
		return err
	}

	// initialize non changing values
	light.Name = attr.Name
	light.Dimmable = containsString(lightsSupportingDimming, attr.Type)
	light.SupportsColorTemperature = containsString(lightsSupportingColorTemperature, attr.Type)
	light.SupportsXYColor = containsString(lightsSupportingXYColor, attr.Type)

	light.updateCurrentLightState()
	return nil
}

func (light *Light) updateCurrentLightState() error {
	attr, err := light.HueLight.GetLightAttributes()
	if err != nil {
		return err
	}
	light.Reachable = attr.State.Reachable
	if !light.Reachable {
		light.On = false
		light.CurrentLightState = LightState{0, []float32{0, 0}, 0}
	} else {
		light.On = attr.State.On
		light.CurrentLightState = lightStateFromHueValues(attr.State.Ct, attr.State.Xy, attr.State.Bri)
	}
	light.CurrentLightState.isValid()
	return nil
}

func (light *Light) update() error {
	// refresh current state
	light.updateCurrentLightState()

	// is the light associated to any schedule?
	if !light.Scheduled {
		return nil
	}

	// Light reachable or on?
	if !light.Reachable || !light.On {
		if light.Tracking {
			log.Printf("💡 Light %s is no longer reachable or turned on. Clearing state.", light.Name)
			light.Tracking = false
			light.Automatic = false
			return nil
		}

		// ignore light because we are not tracking it.
		return nil
	}

	// did the light just appear?
	if !light.Tracking {
		log.Printf("💡 Light %s just appeared. Initializing state to %vK at %v%% brightness.", light.Name, light.TargetLightState.ColorTemperature, light.TargetLightState.Brightness)

		// For initialization we set the state again and again for 10 seconds
		// because during startup the zigbee communication might be unstable
		for index := 0; index < 9; index++ {
			light.setLightState(light.TargetLightState)
		}
		// safe the state of the last iteration
		setLightState, err := light.setLightState(light.TargetLightState)
		if err != nil {
			return err
		}
		light.SavedLightState = setLightState
		//light.TargetLightState = setLightState
		light.Tracking = true
		light.Automatic = true
		log.Debugf("💡 Light %s was updated to %vK at %v%% brightness", light.Name, light.TargetLightState.ColorTemperature, light.TargetLightState.Brightness)
		return nil
	}

	// light in manual state
	if !light.Automatic {
		return nil
	}

	// did the user manually change the light state?
	if !light.SavedLightState.equals(light.CurrentLightState) {
		if log.GetLevel() == log.DebugLevel {
			log.Debugf("💡 Light %s was manually changed. Current state: %+v - Saved state: %+v", light.Name, light.CurrentLightState, light.SavedLightState)
		} else {
			log.Printf("💡 Light %s was changed manually. Disabling Kelvin.", light.Name)
		}
		light.Automatic = false
		return nil
	}

	// Update needed?
	if light.TargetLightState.equals(light.CurrentLightState) {
		return nil
	}
	log.Debugf("💡 Light %s - Target and Current state differ: %v, %v", light.Name, light.TargetLightState, light.CurrentLightState)

	// Light is reachable, turned on and in automatic state. Update to new state.
	setLightState, err := light.setLightState(light.TargetLightState)
	if err != nil {
		return err
	}

	// Debug: Compare values
	if !setLightState.equals(light.TargetLightState) {
		log.Debugf("💡 Light %s - Target and set state differ: %v, %v", light.Name, light.TargetLightState, setLightState)
	}

	light.SavedLightState = setLightState
	log.Printf("💡 Light %s was updated to %vK at %v%% brightness", light.Name, light.TargetLightState.ColorTemperature, light.TargetLightState.Brightness)
	return nil
}

func (light *Light) setLightState(state LightState) (LightState, error) {
	state.isValid()

	// Don't send repeated "On" as this slows the bridge down (see https://developers.meethue.com/faq-page #Performance)
	var hueLightState hue.SetLightState
	colorTemperature, color, brightness := state.convertValuesToHue()
	if light.SupportsXYColor && (state.Color[0] != 0 || state.Color[1] != 0) {
		hueLightState.Xy = []float32{color[0], color[1]}
	} else if light.SupportsColorTemperature && state.ColorTemperature != 0 {
		hueLightState.Ct = strconv.Itoa(colorTemperature)
	}
	if light.Dimmable && state.Brightness != 0 {
		hueLightState.Bri = strconv.Itoa(brightness)
	}
	hueLightState.TransitionTime = strconv.Itoa(lightTransitionIntervalInSeconds * 10) // conversion to 100ms-value

	results, err := light.HueLight.SetState(hueLightState)
	if err != nil {
		return LightState{0, []float32{0, 0}, 0}, err
	}

	// iterate over result to acquire set values
	for _, result := range results {
		for key, value := range result.Success {
			path := strings.Split(key, "/")

			switch path[len(path)-1] {
			case "xy":
				color = []float32{} // clear old color values
				for _, elem := range value.([]interface{}) {
					color = append(color, float32(elem.(float64)))
				}
			case "bri":
				brightness = int(value.(float64))
			case "ct":
				colorTemperature = int(value.(float64))
			}
		}
	}
	setLightState := lightStateFromHueValues(colorTemperature, color, brightness)
	setLightState.isValid()

	// wait while the light is in transition before returning
	time.Sleep(lightTransitionIntervalInSeconds + 1*time.Second)
	return setLightState, nil
}

func (light *Light) updateConfiguration(configuration *Configuration) {
	light.Configuration = configuration
	light.updateSchedule()
	light.updateInterval()
	light.updateTargetLightState()
}

func (light *Light) updateSchedule() {
	newSchedule, err := light.Configuration.lightScheduleForDay(light.ID, time.Now())
	if err != nil {
		log.Printf("💡 Light %v is not associated to any schedule. Ignoring...", light.Name)
		light.Schedule = newSchedule // assign empty schedule
		light.Scheduled = false
		return
	}
	light.Schedule = newSchedule
	light.Scheduled = true
	log.Printf("💡 Light %s: Activating schedule for %v (sunrise: %v, sunset: %v)", light.Name, light.Schedule.endOfDay.Format("Jan 2 2006"), light.Schedule.sunrise.Time.Format("15:04"), light.Schedule.sunset.Time.Format("15:04"))
	light.updateInterval()
}

func (light *Light) updateInterval() {
	if !light.Scheduled {
		log.Debugf("💡 Light %v is not associated to any schedule. No interval to update...", light.Name)
		return
	}

	newInterval, err := light.Schedule.currentInterval(time.Now())
	if err != nil {
		log.Printf("💡 Light %v has no active interval. Ignoring...", light.Name)
		light.Interval = newInterval // assign empty interval
		return
	}
	if newInterval != light.Interval {
		light.Interval = newInterval
		log.Printf("💡 Light %s: Activating interval %v - %v", light.Name, light.Interval.Start.Time.Format("15:04"), light.Interval.End.Time.Format("15:04"))
	}
}

func (light *Light) updateTargetLightState() {
	if !light.Scheduled {
		log.Debugf("💡 Light %v is not associated to any schedule. No target light state to update...", light.Name)
		return
	}

	newLightState := light.Interval.calculateLightStateInInterval(time.Now())
	newLightState.isValid()

	// First initialization of the TargetLightState
	if light.TargetLightState.ColorTemperature == 0 && len(light.TargetLightState.Color) == 0 && light.TargetLightState.Brightness == 0 {
		light.TargetLightState = newLightState
		log.Debugf("💡 Light %s: Initialized target state to %+v", light.Name, light.TargetLightState)
		return
	}

	if !light.TargetLightState.equals(newLightState) {
		light.TargetLightState = newLightState
		log.Debugf("💡 Light %s: Updated target state to %+v", light.Name, light.TargetLightState)
	}
}
<|endoftext|>


<commit_before>package client

import (
	"context"
	"crypto/rsa"
	spb "github.com/PomeloCloud/BFTRaft4go/proto/server"
	"github.com/PomeloCloud/BFTRaft4go/server"
	"github.com/PomeloCloud/BFTRaft4go/utils"
	"github.com/patrickmn/go-cache"
	"sync"
	"time"
	"strconv"
)

type BFTRaftClient struct {
	Id         uint64
	PrivateKey *rsa.PrivateKey
	AlphaNodes []*spb.BFTRaftClient
	Groups     *cache.Cache
	CmdResChan map[uint64]map[uint64]chan []byte
	Lock       sync.RWMutex
}

type ClientOptions struct {
	PrivateKey []byte
}

// bootstraps is a list of server address believed to be the member of the network
// the list does not need to contain alpha nodes since all of the nodes on the network will get informed
func NewClient(bootstraps []string, opts ClientOptions) (*BFTRaftClient, error) {
	privateKey, err := server.ParsePrivateKey(opts.PrivateKey)
	if err != nil {
		return nil, err
	}
	publicKey := server.PublicKeyFromPrivate(privateKey)
	bftclient := &BFTRaftClient{
		Id:         server.HashPublicKey(publicKey),
		PrivateKey: privateKey,
		Lock:       sync.RWMutex{},
		AlphaNodes: []*spb.BFTRaftClient{},
		Groups:     cache.New(1*time.Minute, 1*time.Minute),
	}
	bftclient.RefreshAlphaNodes(bootstraps)
	return bftclient, nil
}

func (brc *BFTRaftClient) RefreshAlphaNodes(bootstraps []string) {
	nodes := utils.AlphaNodes(bootstraps)
	for _, node := range nodes {
		if c, err := utils.GetClusterRPC(node.ServerAddr); err == nil {
			brc.AlphaNodes = append(brc.AlphaNodes, &c)
		}
	}
}

func (brc *BFTRaftClient) GetGroupHosts(groupId uint64) *[]*spb.Host {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.Groups.Get(cacheKey); found {
		return cached.(*[]*spb.Host)
	}
	hosts := utils.MajorityResponse(brc.AlphaNodes, func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GroupHosts(context.Background(), &spb.GroupId{GroupId: groupId}); err == nil {
			return &res.Nodes, utils.NodesSignData(res.Nodes)
		} else {
			return nil, []byte{}
		}
	}).(*[]*spb.Host)
	if hosts != nil {
		brc.Groups.Set(cacheKey, hosts, cache.DefaultExpiration)
	}
	return hosts
}
<commit_msg>client exec command<commit_after>package client

import (
	"context"
	"crypto/rsa"
	"errors"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	spb "github.com/PomeloCloud/BFTRaft4go/proto/server"
	"github.com/PomeloCloud/BFTRaft4go/server"
	"github.com/PomeloCloud/BFTRaft4go/utils"
	"github.com/patrickmn/go-cache"
)

type BFTRaftClient struct {
	Id          uint64
	PrivateKey  *rsa.PrivateKey
	AlphaNodes  []*spb.BFTRaftClient
	GroupHosts  *cache.Cache
	GroupLeader *cache.Cache
	CmdResChan  map[uint64]map[uint64]chan []byte
	Counter     int64
	Lock        sync.RWMutex
}

type ClientOptions struct {
	PrivateKey []byte
}

// bootstraps is a list of server address believed to be the member of the network
// the list does not need to contain alpha nodes since all of the nodes on the network will get informed
func NewClient(bootstraps []string, opts ClientOptions) (*BFTRaftClient, error) {
	privateKey, err := server.ParsePrivateKey(opts.PrivateKey)
	if err != nil {
		return nil, err
	}
	publicKey := server.PublicKeyFromPrivate(privateKey)
	bftclient := &BFTRaftClient{
		Id:          server.HashPublicKey(publicKey),
		PrivateKey:  privateKey,
		Lock:        sync.RWMutex{},
		AlphaNodes:  []*spb.BFTRaftClient{},
		GroupHosts:  cache.New(1*time.Minute, 1*time.Minute),
		GroupLeader: cache.New(1*time.Minute, 1*time.Minute),
		Counter:     0,
	}
	bftclient.RefreshAlphaNodes(bootstraps)
	return bftclient, nil
}

func (brc *BFTRaftClient) RefreshAlphaNodes(bootstraps []string) {
	nodes := utils.AlphaNodes(bootstraps)
	for _, node := range nodes {
		if c, err := utils.GetClusterRPC(node.ServerAddr); err == nil {
			brc.AlphaNodes = append(brc.AlphaNodes, &c)
		}
	}
}

func (brc *BFTRaftClient) GetGroupHosts(groupId uint64) *[]*spb.Host {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.GroupHosts.Get(cacheKey); found {
		return cached.(*[]*spb.Host)
	}
	hosts := utils.MajorityResponse(brc.AlphaNodes, func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GroupHosts(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			return &res.Nodes, utils.NodesSignData(res.Nodes)
		} else {
			return nil, []byte{}
		}
	}).(*[]*spb.Host)
	if hosts != nil {
		brc.GroupHosts.Set(cacheKey, hosts, cache.DefaultExpiration)
	}
	return hosts
}

func (brc *BFTRaftClient) GetGroupLeader(groupId uint64) spb.BFTRaftClient {
	cacheKey := strconv.Itoa(int(groupId))
	if cached, found := brc.GroupLeader.Get(cacheKey); found {
		return cached.(spb.BFTRaftClient)
	}
	leaderHost := utils.MajorityResponse(brc.AlphaNodes, func(client spb.BFTRaftClient) (interface{}, []byte) {
		if res, err := client.GetGroupLeader(
			context.Background(), &spb.GroupId{GroupId: groupId},
		); err == nil {
			// TODO: verify signature
			return &res.Node, []byte(res.Node.ServerAddr)
		} else {
			return nil, []byte{}
		}
	}).(*spb.Host)
	if leaderHost != nil {
		if leader, err := utils.GetClusterRPC(leaderHost.ServerAddr); err == nil {
			brc.GroupHosts.Set(cacheKey, leader, cache.DefaultExpiration)
			return leader
		}
	}
	return nil
}

func (brc *BFTRaftClient) ExecCommand(groupId uint64, funcId uint64, arg []byte) (*[]byte, error) {
	leader := brc.GetGroupLeader(groupId)
	if leader == nil {
		return nil, errors.New("cannot found leader")
	}
	reqId := uint64(atomic.AddInt64(&brc.Counter, 1))
	cmdReq := &spb.CommandRequest{
		Group:     groupId,
		ClientId:  brc.Id,
		RequestId: reqId,
		FuncId:    funcId,
		Arg:       arg,
	}
	signData := server.ExecCommandSignData(cmdReq)
	cmdReq.Signature = server.Sign(brc.PrivateKey, signData)
	brc.CmdResChan[groupId][reqId] = make(chan []byte)
	if cmdRes, err := leader.ExecCommand(context.Background(), cmdReq); err == nil {
		// TODO: verify signature
		// TODO: update leader if needed
		// TODO: verify response matches request
		brc.CmdResChan[groupId][reqId] <- cmdRes.Result
	}
	expectedResponse := len(*brc.GetGroupHosts(groupId)) / 2
	responseReceived := map[uint64][]byte{}
	responseHashes := []uint64{}
	replicationCompleted := make(chan bool, 1)
	go func() {
		for res := range brc.CmdResChan[groupId][reqId] {
			hash := utils.HashData(res)
			responseReceived[hash] = res
			responseHashes = append(responseHashes, hash)
			if len(responseReceived) > expectedResponse {
				replicationCompleted <- true
				break
			}
		}
	}()
	select {
	case <-replicationCompleted:
		majorityHash := utils.PickMajority(responseHashes)
		return &responseReceived[majorityHash], nil
	case <-time.After(10 * time.Second):
		close(brc.CmdResChan[groupId][reqId])
		delete(brc.CmdResChan[groupId], reqId)
		return nil, errors.New("does not receive enough response")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package blockchain

import (
	"fmt"

	"github.com/btcsuite/btcd/database"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// TxData contains contextual information about transactions such as which block
// they were found in and whether or not the outputs are spent.
type TxData struct {
	Tx          *btcutil.Tx
	Hash        *wire.ShaHash
	BlockHeight int32
	Spent       []bool
	Err         error
}

// TxStore is used to store transactions needed by other transactions for things
// such as script validation and double spend prevention.  This also allows the
// transaction data to be treated as a view since it can contain the information
// from the point-of-view of different points in the chain.
type TxStore map[wire.ShaHash]*TxData

// connectTransactions updates the passed map by applying transaction and
// spend information for all the transactions in the passed block.  Only
// transactions in the passed map are updated.
func connectTransactions(txStore TxStore, block *btcutil.Block) error {
	// Loop through all of the transactions in the block to see if any of
	// them are ones we need to update and spend based on the results map.
	for _, tx := range block.Transactions() {
		// Update the transaction store with the transaction information
		// if it's one of the requested transactions.
		msgTx := tx.MsgTx()
		if txD, exists := txStore[*tx.Sha()]; exists {
			txD.Tx = tx
			txD.BlockHeight = block.Height()
			txD.Spent = make([]bool, len(msgTx.TxOut))
			txD.Err = nil
		}

		// Spend the origin transaction output.
		for _, txIn := range msgTx.TxIn {
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			if originTx, exists := txStore[*originHash]; exists {
				if originIndex > uint32(len(originTx.Spent)) {
					continue
				}
				originTx.Spent[originIndex] = true
			}
		}
	}

	return nil
}

// disconnectTransactions updates the passed map by undoing transaction and
// spend information for all transactions in the passed block.  Only
// transactions in the passed map are updated.
func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
	// Loop through all of the transactions in the block to see if any of
	// them are ones that need to be undone based on the transaction store.
	for _, tx := range block.Transactions() {
		// Clear this transaction from the transaction store if needed.
		// Only clear it rather than deleting it because the transaction
		// connect code relies on its presence to decide whether or not
		// to update the store and any transactions which exist on both
		// sides of a fork would otherwise not be updated.
		if txD, exists := txStore[*tx.Sha()]; exists {
			txD.Tx = nil
			txD.BlockHeight = 0
			txD.Spent = nil
			txD.Err = database.ErrTxShaMissing
		}

		// Unspend the origin transaction output.
		for _, txIn := range tx.MsgTx().TxIn {
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			originTx, exists := txStore[*originHash]
			if exists && originTx.Tx != nil && originTx.Err == nil {
				if originIndex > uint32(len(originTx.Spent)) {
					continue
				}
				originTx.Spent[originIndex] = false
			}
		}
	}

	return nil
}

// fetchTxStoreMain fetches transaction data about the provided set of
// transactions from the point of view of the end of the main chain.  It takes
// a flag which specifies whether or not fully spent transaction should be
// included in the results.
func fetchTxStoreMain(db database.Db, txSet map[wire.ShaHash]struct{}, includeSpent bool) TxStore {
	// Just return an empty store now if there are no requested hashes.
	txStore := make(TxStore)
	if len(txSet) == 0 {
		return txStore
	}

	// The transaction store map needs to have an entry for every requested
	// transaction.  By default, all the transactions are marked as missing.
	// Each entry will be filled in with the appropriate data below.
	txList := make([]*wire.ShaHash, 0, len(txSet))
	for hash := range txSet {
		hashCopy := hash
		txStore[hash] = &TxData{Hash: &hashCopy, Err: database.ErrTxShaMissing}
		txList = append(txList, &hashCopy)
	}

	// Ask the database (main chain) for the list of transactions.  This
	// will return the information from the point of view of the end of the
	// main chain.  Choose whether or not to include fully spent
	// transactions depending on the passed flag.
	var txReplyList []*database.TxListReply
	if includeSpent {
		txReplyList = db.FetchTxByShaList(txList)
	} else {
		txReplyList = db.FetchUnSpentTxByShaList(txList)
	}
	for _, txReply := range txReplyList {
		// Lookup the existing results entry to modify.  Skip
		// this reply if there is no corresponding entry in
		// the transaction store map which really should not happen, but
		// be safe.
		txD, ok := txStore[*txReply.Sha]
		if !ok {
			continue
		}

		// Fill in the transaction details.  A copy is used here since
		// there is no guarantee the returned data isn't cached and
		// this code modifies the data.  A bug caused by modifying the
		// cached data would likely be difficult to track down and could
		// cause subtle errors, so avoid the potential altogether.
		txD.Err = txReply.Err
		if txReply.Err == nil {
			txD.Tx = btcutil.NewTx(txReply.Tx)
			txD.BlockHeight = txReply.Height
			txD.Spent = make([]bool, len(txReply.TxSpent))
			copy(txD.Spent, txReply.TxSpent)
		}
	}

	return txStore
}

// fetchTxStore fetches transaction data about the provided set of transactions
// from the point of view of the given node.  For example, a given node might
// be down a side chain where a transaction hasn't been spent from its point of
// view even though it might have been spent in the main chain (or another side
// chain).  Another scenario is where a transaction exists from the point of
// view of the main chain, but doesn't exist in a side chain that branches
// before the block that contains the transaction on the main chain.
func (b *BlockChain) fetchTxStore(node *blockNode, txSet map[wire.ShaHash]struct{}) (TxStore, error) {
	// Get the previous block node.  This function is used over simply
	// accessing node.parent directly as it will dynamically create previous
	// block nodes as needed.  This helps allow only the pieces of the chain
	// that are needed to remain in memory.
	prevNode, err := b.getPrevNodeFromNode(node)
	if err != nil {
		return nil, err
	}

	// If we haven't selected a best chain yet or we are extending the main
	// (best) chain with a new block, fetch the requested set from the point
	// of view of the end of the main (best) chain without including fully
	// spent transactions in the results.  This is a little more efficient
	// since it means less transaction lookups are needed.
	if b.bestChain == nil || (prevNode != nil && prevNode.hash.IsEqual(b.bestChain.hash)) {
		txStore := fetchTxStoreMain(b.db, txSet, false)
		return txStore, nil
	}

	// Fetch the requested set from the point of view of the end of the
	// main (best) chain including fully spent transactions.  The fully
	// spent transactions are needed because the following code unspends
	// them to get the correct point of view.
	txStore := fetchTxStoreMain(b.db, txSet, true)

	// The requested node is either on a side chain or is a node on the main
	// chain before the end of it.  In either case, we need to undo the
	// transactions and spend information for the blocks which would be
	// disconnected during a reorganize to the point of view of the
	// node just before the requested node.
	detachNodes, attachNodes := b.getReorganizeNodes(prevNode)
	for e := detachNodes.Front(); e != nil; e = e.Next() {
		n := e.Value.(*blockNode)
		block, err := b.db.FetchBlockBySha(n.hash)
		if err != nil {
			return nil, err
		}

		disconnectTransactions(txStore, block)
	}

	// The transaction store is now accurate to either the node where the
	// requested node forks off the main chain (in the case where the
	// requested node is on a side chain), or the requested node itself if
	// the requested node is an old node on the main chain.  Entries in the
	// attachNodes list indicate the requested node is on a side chain, so
	// if there are no nodes to attach, we're done.
	if attachNodes.Len() == 0 {
		return txStore, nil
	}

	// The requested node is on a side chain, so we need to apply the
	// transactions and spend information from each of the nodes to attach.
	for e := attachNodes.Front(); e != nil; e = e.Next() {
		n := e.Value.(*blockNode)
		block, exists := b.blockCache[*n.hash]
		if !exists {
			return nil, fmt.Errorf("unable to find block %v in "+
				"side chain cache for transaction search",
				n.hash)
		}

		connectTransactions(txStore, block)
	}

	return txStore, nil
}

// fetchInputTransactions fetches the input transactions referenced by the
// transactions in the given block from its point of view.  See fetchTxList
// for more details on what the point of view entails.
func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Block) (TxStore, error) {
	// Build a map of in-flight transactions because some of the inputs in
	// this block could be referencing other transactions earlier in this
	// block which are not yet in the chain.
	txInFlight := map[wire.ShaHash]int{}
	transactions := block.Transactions()
	for i, tx := range transactions {
		txInFlight[*tx.Sha()] = i
	}

	// Loop through all of the transaction inputs (except for the coinbase
	// which has no inputs) collecting them into sets of what is needed and
	// what is already known (in-flight).
	txNeededSet := make(map[wire.ShaHash]struct{})
	txStore := make(TxStore)
	for i, tx := range transactions[1:] {
		for _, txIn := range tx.MsgTx().TxIn {
			// Add an entry to the transaction store for the needed
			// transaction with it set to missing by default.
			originHash := &txIn.PreviousOutPoint.Hash
			txD := &TxData{Hash: originHash, Err: database.ErrTxShaMissing}
			txStore[*originHash] = txD

			// It is acceptable for a transaction input to reference
			// the output of another transaction in this block only
			// if the referenced transaction comes before the
			// current one in this block.  Update the transaction
			// store acccordingly when this is the case.  Otherwise,
			// we still need the transaction.
			//
			// NOTE: The >= is correct here because i is one less
			// than the actual position of the transaction within
			// the block due to skipping the coinbase.
			if inFlightIndex, ok := txInFlight[*originHash]; ok &&
				i >= inFlightIndex {

				originTx := transactions[inFlightIndex]
				txD.Tx = originTx
				txD.BlockHeight = node.height
				txD.Spent = make([]bool, len(originTx.MsgTx().TxOut))
				txD.Err = nil
			} else {
				txNeededSet[*originHash] = struct{}{}
			}
		}
	}

	// Request the input transactions from the point of view of the node.
	txNeededStore, err := b.fetchTxStore(node, txNeededSet)
	if err != nil {
		return nil, err
	}

	// Merge the results of the requested transactions and the in-flight
	// transactions.
	for _, txD := range txNeededStore {
		txStore[*txD.Hash] = txD
	}

	return txStore, nil
}

// FetchTransactionStore fetches the input transactions referenced by the
// passed transaction from the point of view of the end of the main chain.  It
// also attempts to fetch the transaction itself so the returned TxStore can be
// examined for duplicate transactions.
func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx, includeSpent bool) (TxStore, error) {
	// Create a set of needed transactions from the transactions referenced
	// by the inputs of the passed transaction.  Also, add the passed
	// transaction itself as a way for the caller to detect duplicates.
	txNeededSet := make(map[wire.ShaHash]struct{})
	txNeededSet[*tx.Sha()] = struct{}{}
	for _, txIn := range tx.MsgTx().TxIn {
		txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
	}

	// Request the input transactions from the point of view of the end of
	// the main chain without including fully spent trasactions in the
	// results.  Fully spent transactions are only needed for chain
	// reorganization which does not apply here.
	txStore := fetchTxStoreMain(b.db, txNeededSet, includeSpent)
	return txStore, nil
}
<commit_msg>update comment to reflect new includeSpent flag<commit_after>// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package blockchain

import (
	"fmt"

	"github.com/btcsuite/btcd/database"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// TxData contains contextual information about transactions such as which block
// they were found in and whether or not the outputs are spent.
type TxData struct {
	Tx          *btcutil.Tx
	Hash        *wire.ShaHash
	BlockHeight int32
	Spent       []bool
	Err         error
}

// TxStore is used to store transactions needed by other transactions for things
// such as script validation and double spend prevention.  This also allows the
// transaction data to be treated as a view since it can contain the information
// from the point-of-view of different points in the chain.
type TxStore map[wire.ShaHash]*TxData

// connectTransactions updates the passed map by applying transaction and
// spend information for all the transactions in the passed block.  Only
// transactions in the passed map are updated.
func connectTransactions(txStore TxStore, block *btcutil.Block) error {
	// Loop through all of the transactions in the block to see if any of
	// them are ones we need to update and spend based on the results map.
	for _, tx := range block.Transactions() {
		// Update the transaction store with the transaction information
		// if it's one of the requested transactions.
		msgTx := tx.MsgTx()
		if txD, exists := txStore[*tx.Sha()]; exists {
			txD.Tx = tx
			txD.BlockHeight = block.Height()
			txD.Spent = make([]bool, len(msgTx.TxOut))
			txD.Err = nil
		}

		// Spend the origin transaction output.
		for _, txIn := range msgTx.TxIn {
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			if originTx, exists := txStore[*originHash]; exists {
				if originIndex > uint32(len(originTx.Spent)) {
					continue
				}
				originTx.Spent[originIndex] = true
			}
		}
	}

	return nil
}

// disconnectTransactions updates the passed map by undoing transaction and
// spend information for all transactions in the passed block.  Only
// transactions in the passed map are updated.
func disconnectTransactions(txStore TxStore, block *btcutil.Block) error {
	// Loop through all of the transactions in the block to see if any of
	// them are ones that need to be undone based on the transaction store.
	for _, tx := range block.Transactions() {
		// Clear this transaction from the transaction store if needed.
		// Only clear it rather than deleting it because the transaction
		// connect code relies on its presence to decide whether or not
		// to update the store and any transactions which exist on both
		// sides of a fork would otherwise not be updated.
		if txD, exists := txStore[*tx.Sha()]; exists {
			txD.Tx = nil
			txD.BlockHeight = 0
			txD.Spent = nil
			txD.Err = database.ErrTxShaMissing
		}

		// Unspend the origin transaction output.
		for _, txIn := range tx.MsgTx().TxIn {
			originHash := &txIn.PreviousOutPoint.Hash
			originIndex := txIn.PreviousOutPoint.Index
			originTx, exists := txStore[*originHash]
			if exists && originTx.Tx != nil && originTx.Err == nil {
				if originIndex > uint32(len(originTx.Spent)) {
					continue
				}
				originTx.Spent[originIndex] = false
			}
		}
	}

	return nil
}

// fetchTxStoreMain fetches transaction data about the provided set of
// transactions from the point of view of the end of the main chain.  It takes
// a flag which specifies whether or not fully spent transaction should be
// included in the results.
func fetchTxStoreMain(db database.Db, txSet map[wire.ShaHash]struct{}, includeSpent bool) TxStore {
	// Just return an empty store now if there are no requested hashes.
	txStore := make(TxStore)
	if len(txSet) == 0 {
		return txStore
	}

	// The transaction store map needs to have an entry for every requested
	// transaction.  By default, all the transactions are marked as missing.
	// Each entry will be filled in with the appropriate data below.
	txList := make([]*wire.ShaHash, 0, len(txSet))
	for hash := range txSet {
		hashCopy := hash
		txStore[hash] = &TxData{Hash: &hashCopy, Err: database.ErrTxShaMissing}
		txList = append(txList, &hashCopy)
	}

	// Ask the database (main chain) for the list of transactions.  This
	// will return the information from the point of view of the end of the
	// main chain.  Choose whether or not to include fully spent
	// transactions depending on the passed flag.
	var txReplyList []*database.TxListReply
	if includeSpent {
		txReplyList = db.FetchTxByShaList(txList)
	} else {
		txReplyList = db.FetchUnSpentTxByShaList(txList)
	}
	for _, txReply := range txReplyList {
		// Lookup the existing results entry to modify.  Skip
		// this reply if there is no corresponding entry in
		// the transaction store map which really should not happen, but
		// be safe.
		txD, ok := txStore[*txReply.Sha]
		if !ok {
			continue
		}

		// Fill in the transaction details.  A copy is used here since
		// there is no guarantee the returned data isn't cached and
		// this code modifies the data.  A bug caused by modifying the
		// cached data would likely be difficult to track down and could
		// cause subtle errors, so avoid the potential altogether.
		txD.Err = txReply.Err
		if txReply.Err == nil {
			txD.Tx = btcutil.NewTx(txReply.Tx)
			txD.BlockHeight = txReply.Height
			txD.Spent = make([]bool, len(txReply.TxSpent))
			copy(txD.Spent, txReply.TxSpent)
		}
	}

	return txStore
}

// fetchTxStore fetches transaction data about the provided set of transactions
// from the point of view of the given node.  For example, a given node might
// be down a side chain where a transaction hasn't been spent from its point of
// view even though it might have been spent in the main chain (or another side
// chain).  Another scenario is where a transaction exists from the point of
// view of the main chain, but doesn't exist in a side chain that branches
// before the block that contains the transaction on the main chain.
func (b *BlockChain) fetchTxStore(node *blockNode, txSet map[wire.ShaHash]struct{}) (TxStore, error) {
	// Get the previous block node.  This function is used over simply
	// accessing node.parent directly as it will dynamically create previous
	// block nodes as needed.  This helps allow only the pieces of the chain
	// that are needed to remain in memory.
	prevNode, err := b.getPrevNodeFromNode(node)
	if err != nil {
		return nil, err
	}

	// If we haven't selected a best chain yet or we are extending the main
	// (best) chain with a new block, fetch the requested set from the point
	// of view of the end of the main (best) chain without including fully
	// spent transactions in the results.  This is a little more efficient
	// since it means less transaction lookups are needed.
	if b.bestChain == nil || (prevNode != nil && prevNode.hash.IsEqual(b.bestChain.hash)) {
		txStore := fetchTxStoreMain(b.db, txSet, false)
		return txStore, nil
	}

	// Fetch the requested set from the point of view of the end of the
	// main (best) chain including fully spent transactions.  The fully
	// spent transactions are needed because the following code unspends
	// them to get the correct point of view.
	txStore := fetchTxStoreMain(b.db, txSet, true)

	// The requested node is either on a side chain or is a node on the main
	// chain before the end of it.  In either case, we need to undo the
	// transactions and spend information for the blocks which would be
	// disconnected during a reorganize to the point of view of the
	// node just before the requested node.
	detachNodes, attachNodes := b.getReorganizeNodes(prevNode)
	for e := detachNodes.Front(); e != nil; e = e.Next() {
		n := e.Value.(*blockNode)
		block, err := b.db.FetchBlockBySha(n.hash)
		if err != nil {
			return nil, err
		}

		disconnectTransactions(txStore, block)
	}

	// The transaction store is now accurate to either the node where the
	// requested node forks off the main chain (in the case where the
	// requested node is on a side chain), or the requested node itself if
	// the requested node is an old node on the main chain.  Entries in the
	// attachNodes list indicate the requested node is on a side chain, so
	// if there are no nodes to attach, we're done.
	if attachNodes.Len() == 0 {
		return txStore, nil
	}

	// The requested node is on a side chain, so we need to apply the
	// transactions and spend information from each of the nodes to attach.
	for e := attachNodes.Front(); e != nil; e = e.Next() {
		n := e.Value.(*blockNode)
		block, exists := b.blockCache[*n.hash]
		if !exists {
			return nil, fmt.Errorf("unable to find block %v in "+
				"side chain cache for transaction search",
				n.hash)
		}

		connectTransactions(txStore, block)
	}

	return txStore, nil
}

// fetchInputTransactions fetches the input transactions referenced by the
// transactions in the given block from its point of view.  See fetchTxList
// for more details on what the point of view entails.
func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Block) (TxStore, error) {
	// Build a map of in-flight transactions because some of the inputs in
	// this block could be referencing other transactions earlier in this
	// block which are not yet in the chain.
	txInFlight := map[wire.ShaHash]int{}
	transactions := block.Transactions()
	for i, tx := range transactions {
		txInFlight[*tx.Sha()] = i
	}

	// Loop through all of the transaction inputs (except for the coinbase
	// which has no inputs) collecting them into sets of what is needed and
	// what is already known (in-flight).
	txNeededSet := make(map[wire.ShaHash]struct{})
	txStore := make(TxStore)
	for i, tx := range transactions[1:] {
		for _, txIn := range tx.MsgTx().TxIn {
			// Add an entry to the transaction store for the needed
			// transaction with it set to missing by default.
			originHash := &txIn.PreviousOutPoint.Hash
			txD := &TxData{Hash: originHash, Err: database.ErrTxShaMissing}
			txStore[*originHash] = txD

			// It is acceptable for a transaction input to reference
			// the output of another transaction in this block only
			// if the referenced transaction comes before the
			// current one in this block.  Update the transaction
			// store acccordingly when this is the case.  Otherwise,
			// we still need the transaction.
			//
			// NOTE: The >= is correct here because i is one less
			// than the actual position of the transaction within
			// the block due to skipping the coinbase.
			if inFlightIndex, ok := txInFlight[*originHash]; ok &&
				i >= inFlightIndex {

				originTx := transactions[inFlightIndex]
				txD.Tx = originTx
				txD.BlockHeight = node.height
				txD.Spent = make([]bool, len(originTx.MsgTx().TxOut))
				txD.Err = nil
			} else {
				txNeededSet[*originHash] = struct{}{}
			}
		}
	}

	// Request the input transactions from the point of view of the node.
	txNeededStore, err := b.fetchTxStore(node, txNeededSet)
	if err != nil {
		return nil, err
	}

	// Merge the results of the requested transactions and the in-flight
	// transactions.
	for _, txD := range txNeededStore {
		txStore[*txD.Hash] = txD
	}

	return txStore, nil
}

// FetchTransactionStore fetches the input transactions referenced by the
// passed transaction from the point of view of the end of the main chain.  It
// also attempts to fetch the transaction itself so the returned TxStore can be
// examined for duplicate transactions.
func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx, includeSpent bool) (TxStore, error) {
	// Create a set of needed transactions from the transactions referenced
	// by the inputs of the passed transaction.  Also, add the passed
	// transaction itself as a way for the caller to detect duplicates.
	txNeededSet := make(map[wire.ShaHash]struct{})
	txNeededSet[*tx.Sha()] = struct{}{}
	for _, txIn := range tx.MsgTx().TxIn {
		txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
	}

	// Request the input transactions from the point of view of the end of
	// the main chain with or without without including fully spent transactions
    // in the results.
	txStore := fetchTxStoreMain(b.db, txNeededSet, includeSpent)
	return txStore, nil
}
<|endoftext|>


<commit_before>package gountries

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"
)

// New creates an Query object and unmarshals the json file.
func New() *Query {

	if queryInited == false {
		queryInstance = &Query{
			Countries:    populateCountries(),
			Subdivisions: populateSubdivisions(),
		}
		queryInited = true
	}

	return queryInstance
}

func populateCountries() (countries []Country) {

	// Load countries into memory
	//
	//
	countries = []Country{}

	countriesPath := filepath.Join("data", "yaml", "countries")

	if info, err := ioutil.ReadDir(countriesPath); err == nil {

		for _, v := range info {

			if !v.IsDir() {

				if file, err := ioutil.ReadFile(filepath.Join(countriesPath, v.Name())); err == nil {

					country := Country{}
					if err := yaml.Unmarshal(file, &country); err == nil {

						// Save
						countries = append(countries, country)

					}

				}

			}

		}

	} else {
		panic(fmt.Errorf("Error loading Countries: %s", err))
	}
	return
}

func populateSubdivisions() (list map[string][]SubDivision) {

	// Load countries into memory
	//

	list = map[string][]SubDivision{}

	subdivisionsPath := filepath.Join("data", "yaml", "subdivisions")

	if info, err := ioutil.ReadDir(subdivisionsPath); err == nil {

		for _, v := range info {
			//fmt.Println(v.IsDir())

			if !v.IsDir() {

				if file, err := ioutil.ReadFile(filepath.Join(subdivisionsPath, v.Name())); err == nil {

					subdivisions := []SubDivision{}

					if err := yaml.Unmarshal(file, &subdivisions); err == nil {

						// Save
						//subdivisions = append(subdivisions, subdivision...)

						list[strings.Split(v.Name(), ".")[0]] = subdivisions
					}

				}

			}

		}

	} else {
		panic("Error loading Subdivisions")
	}

	return
}
<commit_msg>Allows passing custom location for data folder<commit_after>package gountries

import (
	"fmt"
	"io/ioutil"
	"path"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"
)

// New creates an Query object and unmarshals the json file.
func New() *Query {

	dataPath := filepath.Join("data", "yaml")

	return NewFromPath(dataPath)
}

// NewFromPath creates a Query object from data folder in provided path
func NewFromPath(dataPath string) *Query {

	if queryInited == false {
		queryInstance = &Query{
			Countries:    populateCountries(dataPath),
			Subdivisions: populateSubdivisions(dataPath),
		}
		queryInited = true
	}

	return queryInstance
}

func populateCountries(dataPath string) (countries []Country) {

	// Load countries into memory
	//
	//
	countries = []Country{}

	countriesPath := path.Join(dataPath, "countries")

	if info, err := ioutil.ReadDir(countriesPath); err == nil {

		for _, v := range info {

			if !v.IsDir() {

				if file, err := ioutil.ReadFile(filepath.Join(countriesPath, v.Name())); err == nil {

					country := Country{}
					if err := yaml.Unmarshal(file, &country); err == nil {

						// Save
						countries = append(countries, country)

					}

				}

			}

		}

	} else {
		panic(fmt.Errorf("Error loading Countries: %s", err))
	}
	return
}

func populateSubdivisions(dataPath string) (list map[string][]SubDivision) {

	// Load countries into memory
	//

	list = map[string][]SubDivision{}

	subdivisionsPath := path.Join(dataPath, "subdivisions")

	if info, err := ioutil.ReadDir(subdivisionsPath); err == nil {

		for _, v := range info {
			//fmt.Println(v.IsDir())

			if !v.IsDir() {

				if file, err := ioutil.ReadFile(filepath.Join(subdivisionsPath, v.Name())); err == nil {

					subdivisions := []SubDivision{}

					if err := yaml.Unmarshal(file, &subdivisions); err == nil {

						// Save
						//subdivisions = append(subdivisions, subdivision...)

						list[strings.Split(v.Name(), ".")[0]] = subdivisions
					}

				}

			}

		}

	} else {
		panic("Error loading Subdivisions")
	}

	return
}
<|endoftext|>