<commit_before>package gopcap

import (
	"testing"
	"time"
)

type ByteReader []byte

func (r ByteReader) Read(p []byte) (int, error) {
	n := copy(p, r)
	return n, nil
}

func TestCheckMagicNum(t *testing.T) {
	in := []ByteReader{
		ByteReader{0xa1, 0xb2, 0xc3, 0xd4},
		ByteReader{0xd4, 0xc3, 0xb2, 0xa1},
		ByteReader{0xd4, 0xc3, 0xb2, 0xa0},
		ByteReader{0xd4, 0xc3, 0xb2},
	}

	first := []bool{true, true, false, false}
	second := []bool{false, true, false, false}
	third := []error{nil, nil, NotAPcapFile, InsufficientLength}

	for i, input := range in {
		out1, out2, out3 := checkMagicNum(input)

		if out1 != first[i] {
			t.Errorf("Unexpected first return val: expected %v, got %v.", first[i], out1)
		}

		if out2 != second[i] {
			t.Errorf("Unexpected second return val: expected %v, got %v.", second[i], out2)
		}

		if out3 != third[i] {
			t.Errorf("Unexpected third return val: expected %v, got %v.", third[i], out3)
		}
	}
}

func TestPopulatePacketHeaderGood(t *testing.T) {
	in := ByteReader{0xfa, 0x4f, 0xef, 0x44, 0x64, 0xfd, 0x09, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00}
	pkt := new(Packet)
	err := populatePacketHeader(pkt, in, true)
	correct_ts := 321259*time.Hour + 31*time.Minute + 6*time.Second + 654*time.Millisecond + 692*time.Microsecond

	if err != nil {
		t.Errorf("Received unexpected error: %v", err)
	}
	if pkt.Timestamp != correct_ts {
		t.Errorf("Incorrect timestamp: expected %v, got %v", correct_ts, pkt.Timestamp)
	}
	if pkt.IncludedLen != uint32(96) {
		t.Errorf("Incorrect included length: expected %v, got %v", 96, pkt.IncludedLen)
	}
	if pkt.ActualLen != uint32(96) {
		t.Errorf("Incorrect actual length: expected %v, got %v", 96, pkt.ActualLen)
	}
}

func TestPopulatePacketHeaderErr(t *testing.T) {
	in := ByteReader{0xfa}
	pkt := new(Packet)
	err := populatePacketHeader(pkt, in, false)

	if err != InsufficientLength {
		t.Errorf("Unexpected error: expected %v, got %v", InsufficientLength, err)
	}
}

func TestPopulateFileHeaderGood(t *testing.T) {
	in := ByteReader{0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}
	fle := new(PcapFile)
	err := populateFileHeader(fle, in, true)

	if err != nil {
		t.Errorf("Received unexpected error: %v", err)
	}
	if fle.MajorVersion != uint16(2) {
		t.Errorf("Incorrectly parsed major version: expected %v, got %v.", 2, fle.MajorVersion)
	}
	if fle.MinorVersion != uint16(4) {
		t.Errorf("Incorrectly parsed minor version: expected %v, got %v.", 4, fle.MinorVersion)
	}
	if fle.TZCorrection != int32(0) {
		t.Errorf("Got nonzero TZ correction: %v.", fle.TZCorrection)
	}
	if fle.SigFigs != uint32(0) {
		t.Errorf("Got nonzero sig figs: %v.", fle.SigFigs)
	}
	if fle.MaxLen != uint32(65535) {
		t.Errorf("Incorrectly parsed maximum len: expected %v, got %v.", 65535, fle.MaxLen)
	}
	if fle.LinkType != ETHERNET {
		t.Errorf("Incorrect link type: expected %v, got %v.", ETHERNET, fle.LinkType)
	}
}

func TestPopulateFileHeaderErr(t *testing.T) {
	in := ByteReader{0xfa}
	fle := new(PcapFile)
	err := populateFileHeader(fle, in, false)

	if err != InsufficientLength {
		t.Errorf("Unexpected error: expected %v, got %v", InsufficientLength, err)
	}
}
<commit_msg>Don't export the byteReader type.<commit_after>package gopcap

import (
	"testing"
	"time"
)

type byteReader []byte

func (r byteReader) Read(p []byte) (int, error) {
	n := copy(p, r)
	return n, nil
}

func TestCheckMagicNum(t *testing.T) {
	in := []byteReader{
		byteReader{0xa1, 0xb2, 0xc3, 0xd4},
		byteReader{0xd4, 0xc3, 0xb2, 0xa1},
		byteReader{0xd4, 0xc3, 0xb2, 0xa0},
		byteReader{0xd4, 0xc3, 0xb2},
	}

	first := []bool{true, true, false, false}
	second := []bool{false, true, false, false}
	third := []error{nil, nil, NotAPcapFile, InsufficientLength}

	for i, input := range in {
		out1, out2, out3 := checkMagicNum(input)

		if out1 != first[i] {
			t.Errorf("Unexpected first return val: expected %v, got %v.", first[i], out1)
		}

		if out2 != second[i] {
			t.Errorf("Unexpected second return val: expected %v, got %v.", second[i], out2)
		}

		if out3 != third[i] {
			t.Errorf("Unexpected third return val: expected %v, got %v.", third[i], out3)
		}
	}
}

func TestPopulatePacketHeaderGood(t *testing.T) {
	in := byteReader{0xfa, 0x4f, 0xef, 0x44, 0x64, 0xfd, 0x09, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00}
	pkt := new(Packet)
	err := populatePacketHeader(pkt, in, true)
	correct_ts := 321259*time.Hour + 31*time.Minute + 6*time.Second + 654*time.Millisecond + 692*time.Microsecond

	if err != nil {
		t.Errorf("Received unexpected error: %v", err)
	}
	if pkt.Timestamp != correct_ts {
		t.Errorf("Incorrect timestamp: expected %v, got %v", correct_ts, pkt.Timestamp)
	}
	if pkt.IncludedLen != uint32(96) {
		t.Errorf("Incorrect included length: expected %v, got %v", 96, pkt.IncludedLen)
	}
	if pkt.ActualLen != uint32(96) {
		t.Errorf("Incorrect actual length: expected %v, got %v", 96, pkt.ActualLen)
	}
}

func TestPopulatePacketHeaderErr(t *testing.T) {
	in := byteReader{0xfa}
	pkt := new(Packet)
	err := populatePacketHeader(pkt, in, false)

	if err != InsufficientLength {
		t.Errorf("Unexpected error: expected %v, got %v", InsufficientLength, err)
	}
}

func TestPopulateFileHeaderGood(t *testing.T) {
	in := byteReader{0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}
	fle := new(PcapFile)
	err := populateFileHeader(fle, in, true)

	if err != nil {
		t.Errorf("Received unexpected error: %v", err)
	}
	if fle.MajorVersion != uint16(2) {
		t.Errorf("Incorrectly parsed major version: expected %v, got %v.", 2, fle.MajorVersion)
	}
	if fle.MinorVersion != uint16(4) {
		t.Errorf("Incorrectly parsed minor version: expected %v, got %v.", 4, fle.MinorVersion)
	}
	if fle.TZCorrection != int32(0) {
		t.Errorf("Got nonzero TZ correction: %v.", fle.TZCorrection)
	}
	if fle.SigFigs != uint32(0) {
		t.Errorf("Got nonzero sig figs: %v.", fle.SigFigs)
	}
	if fle.MaxLen != uint32(65535) {
		t.Errorf("Incorrectly parsed maximum len: expected %v, got %v.", 65535, fle.MaxLen)
	}
	if fle.LinkType != ETHERNET {
		t.Errorf("Incorrect link type: expected %v, got %v.", ETHERNET, fle.LinkType)
	}
}

func TestPopulateFileHeaderErr(t *testing.T) {
	in := byteReader{0xfa}
	fle := new(PcapFile)
	err := populateFileHeader(fle, in, false)

	if err != InsufficientLength {
		t.Errorf("Unexpected error: expected %v, got %v", InsufficientLength, err)
	}
}
<|endoftext|>


<commit_before>package narcissus

import (
	"testing"

	"honnef.co/go/augeas"
)

type foo struct {
	augeasPath string
	A          string `path:"a"`
}

type bar struct{}

func TestParseNotAPtr(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(foo{
		augeasPath: "/files/some/path",
	})

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "not a ptr" {
		t.Errorf("Expected error not a ptr, got %s", err.Error())
	}
}

func TestParseNotAStruct(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	f := "foo"
	err = n.Parse(&f)

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "not a struct" {
		t.Errorf("Expected error not a struct, got %s", err.Error())
	}
}

func TestParseFieldNotFound(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(&foo{
		augeasPath: "/files/some/path",
	})

	t.Skip("Fix this")

	if err == nil {
		t.Error("Expected an error, got nothing")
	}
}

func TestNoAugeasPathValue(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(&foo{})

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "no augeasPath value and no default" {
		t.Errorf("Expected error no augeasPath value and no default, got %s", err.Error())
	}
}

func TestNoAugeasPathField(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(&bar{})

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "no augeasPath field" {
		t.Errorf("Expected error no augeasPath field, got %s", err.Error())
	}
}

type simpleValues struct {
	augeasPath string
	Str        string   `path:"str"`
	Int        int      `path:"int"`
	Bool       bool     `path:"bool"`
	SlStr      []string `path:"slstr"`
	SlInt      []int    `path:"slint"`
	SlBool     []bool   `path:"slbool"`
}

func TestGetStringField(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	n.Augeas.Set("/test/str", "foo")
	n.Augeas.Set("/test/int", "42")
	n.Augeas.Set("/test/bool", "true")
	n.Augeas.Set("/test/slstr[1]", "a")
	n.Augeas.Set("/test/slstr[2]", "b")
	n.Augeas.Set("/test/slint[1]", "1")
	n.Augeas.Set("/test/slint[2]", "2")
	n.Augeas.Set("/test/slbool[1]", "true")
	n.Augeas.Set("/test/slbool[2]", "false")
	s := &simpleValues{
		augeasPath: "/test",
	}
	err = n.Parse(s)

	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	if s.Str != "foo" {
		t.Errorf("Expected foo, got %s", s.Str)
	}

	if s.Int != 42 {
		t.Errorf("Expected 42, got %v", s.Int)
	}

	if s.Bool != true {
		t.Errorf("Expected true, got %v", s.Bool)
	}

	if len(s.SlStr) != 2 {
		t.Errorf("Expected 2 elements, got %v", len(s.SlStr))
	}

	if s.SlStr[1] != "b" {
		t.Errorf("Expected element to be b, got %s", s.SlStr[1])
	}

	if len(s.SlInt) != 2 {
		t.Errorf("Expected 2 elements, got %v", len(s.SlInt))
	}

	if s.SlInt[1] != 2 {
		t.Errorf("Expected element to be 2, got %v", s.SlInt[1])
	}

	if len(s.SlBool) != 2 {
		t.Errorf("Expected 2 elements, got %v", len(s.SlBool))
	}

	if s.SlBool[0] != true {
		t.Errorf("Expected element to be true, got %v", s.SlBool[0])
	}

	if s.SlBool[1] != false {
		t.Errorf("Expected element to be false, got %v", s.SlBool[1])
	}
}
<commit_msg>Test slices apart<commit_after>package narcissus

import (
	"testing"

	"honnef.co/go/augeas"
)

type foo struct {
	augeasPath string
	A          string `path:"a"`
}

type bar struct{}

func TestParseNotAPtr(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(foo{
		augeasPath: "/files/some/path",
	})

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "not a ptr" {
		t.Errorf("Expected error not a ptr, got %s", err.Error())
	}
}

func TestParseNotAStruct(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	f := "foo"
	err = n.Parse(&f)

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "not a struct" {
		t.Errorf("Expected error not a struct, got %s", err.Error())
	}
}

func TestParseFieldNotFound(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(&foo{
		augeasPath: "/files/some/path",
	})

	t.Skip("Fix this")

	if err == nil {
		t.Error("Expected an error, got nothing")
	}
}

func TestNoAugeasPathValue(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(&foo{})

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "no augeasPath value and no default" {
		t.Errorf("Expected error no augeasPath value and no default, got %s", err.Error())
	}
}

func TestNoAugeasPathField(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	err = n.Parse(&bar{})

	if err == nil {
		t.Error("Expected an error, got nothing")
	}

	if err.Error() != "no augeasPath field" {
		t.Errorf("Expected error no augeasPath field, got %s", err.Error())
	}
}

type simpleValues struct {
	augeasPath string
	Str        string `path:"str"`
	Int        int    `path:"int"`
	Bool       bool   `path:"bool"`
}

func TestGetStringField(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	n.Augeas.Set("/test/str", "foo")
	n.Augeas.Set("/test/int", "42")
	n.Augeas.Set("/test/bool", "true")
	n.Augeas.Set("/test/slstr[1]", "a")
	n.Augeas.Set("/test/slstr[2]", "b")
	n.Augeas.Set("/test/slint[1]", "1")
	n.Augeas.Set("/test/slint[2]", "2")
	n.Augeas.Set("/test/slbool[1]", "true")
	n.Augeas.Set("/test/slbool[2]", "false")
	s := &simpleValues{
		augeasPath: "/test",
	}
	err = n.Parse(s)

	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	if s.Str != "foo" {
		t.Errorf("Expected foo, got %s", s.Str)
	}

	if s.Int != 42 {
		t.Errorf("Expected 42, got %v", s.Int)
	}

	if s.Bool != true {
		t.Errorf("Expected true, got %v", s.Bool)
	}
}

type sliceValues struct {
	augeasPath string
	SlStr      []string `path:"slstr"`
	SlInt      []int    `path:"slint"`
	SlBool     []bool   `path:"slbool"`
}

func TestGetSliceField(t *testing.T) {
	aug, err := augeas.New("", "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)
	n.Augeas.Set("/test/slstr[1]", "a")
	n.Augeas.Set("/test/slstr[2]", "b")
	n.Augeas.Set("/test/slint[1]", "1")
	n.Augeas.Set("/test/slint[2]", "2")
	n.Augeas.Set("/test/slbool[1]", "true")
	n.Augeas.Set("/test/slbool[2]", "false")
	s := &sliceValues{
		augeasPath: "/test",
	}
	err = n.Parse(s)

	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}
	if len(s.SlStr) != 2 {
		t.Errorf("Expected 2 elements, got %v", len(s.SlStr))
	}

	if s.SlStr[1] != "b" {
		t.Errorf("Expected element to be b, got %s", s.SlStr[1])
	}

	if len(s.SlInt) != 2 {
		t.Errorf("Expected 2 elements, got %v", len(s.SlInt))
	}

	if s.SlInt[1] != 2 {
		t.Errorf("Expected element to be 2, got %v", s.SlInt[1])
	}

	if len(s.SlBool) != 2 {
		t.Errorf("Expected 2 elements, got %v", len(s.SlBool))
	}

	if s.SlBool[0] != true {
		t.Errorf("Expected element to be true, got %v", s.SlBool[0])
	}

	if s.SlBool[1] != false {
		t.Errorf("Expected element to be false, got %v", s.SlBool[1])
	}
}
<|endoftext|>


<commit_before>package gitbackend

import (
	"fmt"
	"github.com/libgit2/git2go"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"testing"
	"text/scanner"
	"time"
)

func TestNewFS(t *testing.T) {
	path := "tmp/bla/test"
	err := os.RemoveAll(path)
	checkFatal(t, err)
	fileStore, err := NewFileStore(path, true)
	checkFatal(t, err)
	fileInfo, err := os.Lstat(path)
	checkFatal(t, err)
	if !fileInfo.IsDir() {
		t.Fatalf("%s is not a directory.", path)
	}
	repo, err := git.OpenRepository(path)
	checkFatal(t, err)
	if !repo.IsBare() {
		t.Fatalf("%s is not a Bare Repository.", path)
	}

	paths, err := fileStore.ReadRoot()
	checkFatal(t, err)
	if len(paths) != 0 {
		t.Fatalf("paths should have length 0, but is %d", len(paths))
	}

	_, err = fileStore.ReadDir("foo")
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}
}

func TestReadRoot(t *testing.T) {
	repo := createTestRepo(t)
	seedTestRepo(t, repo)
	fileStore, err := NewFileStore(repo.Workdir(), false)
	checkFatal(t, err)

	paths, err := fileStore.ReadRoot()
	checkFatal(t, err)
	if len(paths) != 2 {
		t.Fatalf("paths should have length 2, but is %d\npaths contains: %v", len(paths), paths)
	}

	if paths[0].Name() != "bar" {
		t.Fatalf("First path should be bar, but is %s\npaths contains: %v", paths[0].Name(), paths)
	}
}

func TestReadDir(t *testing.T) {
	repo := createTestRepo(t)
	seedTestRepo(t, repo)
	fileStore, err := NewFileStore(repo.Workdir(), false)
	checkFatal(t, err)

	paths, err := fileStore.ReadDir("bar")
	checkFatal(t, err)
	if len(paths) != 1 {
		t.Fatalf("paths should have length 1, but is %d\npaths contains: %v", len(paths), paths)
	}

	if paths[0].Name() != "baz.txt" {
		t.Fatalf("First path should be foo.txt, but is %s\npaths contains: %v", paths[0].Name(), paths)
	}

	_, err = fileStore.ReadDir("foo")
	if err == nil {
		t.Fatalf("expected error, but nothing was returned\npaths contains: %v", paths)
	}
}

func TestReadFile(t *testing.T) {
	repo := createTestRepo(t)
	seedTestRepo(t, repo)
	fileStore, err := NewFileStore(repo.Workdir(), false)
	checkFatal(t, err)

	reader, err := fileStore.ReadFile("foo.txt")
	checkFatal(t, err)
	s := readAll(reader)
	if s != fmt.Sprintf("Hello World\n") {
		t.Fatalf("Expected: 'Hello World\n'\nactual: '%s'", s)
	}

	reader, err = fileStore.ReadFile("bar/baz.txt")
	checkFatal(t, err)
	s = readAll(reader)
	if s != fmt.Sprintf("This is Baz\n") {
		t.Fatalf("Expected: 'This is Baz\n'\nactual: '%s'", s)
	}

	reader, err = fileStore.ReadFile("boo.txt")
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}
}

func createTestRepo(t *testing.T) *git.Repository {
	// figure out where we can create the test repo
	path, err := ioutil.TempDir("", "test_repo")
	checkFatal(t, err)
	repo, err := git.InitRepository(path, false)
	checkFatal(t, err)

	return repo
}

func seedTestRepo(t *testing.T, repo *git.Repository) (*git.Oid, *git.Oid) {
	err := exec.Command("cp", "-Rf", "tests/repo/.", repo.Workdir()).Run()
	checkFatal(t, err)

	b, err := exec.Command("find", "tests/repo").Output()
	checkFatal(t, err)
	fmt.Println(string(b))
	b, err = exec.Command("find", repo.Workdir()).Output()
	checkFatal(t, err)
	fmt.Println(string(b))

	loc, err := time.LoadLocation("Europe/Berlin")
	checkFatal(t, err)
	sig := &git.Signature{
		Name:  "Rand Om Hacker",
		Email: "random@hacker.com",
		When:  time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
	}

	idx, err := repo.Index()
	checkFatal(t, err)
	filepath.Walk(repo.Workdir(), func(path string, info os.FileInfo, _ error) (err error) {
		if info.IsDir() {
			return
		}
		lenWorkdir := len(repo.Workdir())
		if path[lenWorkdir:lenWorkdir+4] == ".git" {
			return
		}
		err = idx.AddByPath(path[lenWorkdir:])
		checkFatal(t, err)
		return
	})
	treeId, err := idx.WriteTree()
	checkFatal(t, err)

	message := "This is a commit\n"
	tree, err := repo.LookupTree(treeId)
	checkFatal(t, err)
	commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
	checkFatal(t, err)

	return commitId, treeId
}

func checkFatal(t *testing.T, err error) {
	if err == nil {
		return
	}

	// The failure happens at wherever we were called, not here
	_, file, line, ok := runtime.Caller(1)
	if !ok {
		t.Fatal()
	}

	t.Fatalf("Fail at %v:%v; %v", file, line, err)
}

func readAll(reader io.Reader) (data string) {
	data = ""
	var s scanner.Scanner
	s.Init(reader)
	s.Whitespace = 1
	tok := s.Scan()
	for tok != scanner.EOF {
		data += s.TokenText()
		tok = s.Scan()
	}
	return
}
<commit_msg>[tests] remove debug logs<commit_after>package gitbackend

import (
	"fmt"
	"github.com/libgit2/git2go"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"testing"
	"text/scanner"
	"time"
)

func TestNewFS(t *testing.T) {
	path := "tmp/bla/test"
	err := os.RemoveAll(path)
	checkFatal(t, err)
	fileStore, err := NewFileStore(path, true)
	checkFatal(t, err)
	fileInfo, err := os.Lstat(path)
	checkFatal(t, err)
	if !fileInfo.IsDir() {
		t.Fatalf("%s is not a directory.", path)
	}
	repo, err := git.OpenRepository(path)
	checkFatal(t, err)
	if !repo.IsBare() {
		t.Fatalf("%s is not a Bare Repository.", path)
	}

	paths, err := fileStore.ReadRoot()
	checkFatal(t, err)
	if len(paths) != 0 {
		t.Fatalf("paths should have length 0, but is %d", len(paths))
	}

	_, err = fileStore.ReadDir("foo")
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}
}

func TestReadRoot(t *testing.T) {
	repo := createTestRepo(t)
	seedTestRepo(t, repo)
	fileStore, err := NewFileStore(repo.Workdir(), false)
	checkFatal(t, err)

	paths, err := fileStore.ReadRoot()
	checkFatal(t, err)
	if len(paths) != 2 {
		t.Fatalf("paths should have length 2, but is %d\npaths contains: %v", len(paths), paths)
	}

	if paths[0].Name() != "bar" {
		t.Fatalf("First path should be bar, but is %s\npaths contains: %v", paths[0].Name(), paths)
	}
}

func TestReadDir(t *testing.T) {
	repo := createTestRepo(t)
	seedTestRepo(t, repo)
	fileStore, err := NewFileStore(repo.Workdir(), false)
	checkFatal(t, err)

	paths, err := fileStore.ReadDir("bar")
	checkFatal(t, err)
	if len(paths) != 1 {
		t.Fatalf("paths should have length 1, but is %d\npaths contains: %v", len(paths), paths)
	}

	if paths[0].Name() != "baz.txt" {
		t.Fatalf("First path should be foo.txt, but is %s\npaths contains: %v", paths[0].Name(), paths)
	}

	_, err = fileStore.ReadDir("foo")
	if err == nil {
		t.Fatalf("expected error, but nothing was returned\npaths contains: %v", paths)
	}
}

func TestReadFile(t *testing.T) {
	repo := createTestRepo(t)
	seedTestRepo(t, repo)
	fileStore, err := NewFileStore(repo.Workdir(), false)
	checkFatal(t, err)

	reader, err := fileStore.ReadFile("foo.txt")
	checkFatal(t, err)
	s := readAll(reader)
	if s != fmt.Sprintf("Hello World\n") {
		t.Fatalf("Expected: 'Hello World\n'\nactual: '%s'", s)
	}

	reader, err = fileStore.ReadFile("bar/baz.txt")
	checkFatal(t, err)
	s = readAll(reader)
	if s != fmt.Sprintf("This is Baz\n") {
		t.Fatalf("Expected: 'This is Baz\n'\nactual: '%s'", s)
	}

	reader, err = fileStore.ReadFile("boo.txt")
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}
}

func createTestRepo(t *testing.T) *git.Repository {
	// figure out where we can create the test repo
	path, err := ioutil.TempDir("", "test_repo")
	checkFatal(t, err)
	repo, err := git.InitRepository(path, false)
	checkFatal(t, err)

	return repo
}

func seedTestRepo(t *testing.T, repo *git.Repository) (*git.Oid, *git.Oid) {
	err := exec.Command("cp", "-Rf", "tests/repo/.", repo.Workdir()).Run()
	checkFatal(t, err)

	loc, err := time.LoadLocation("Europe/Berlin")
	checkFatal(t, err)
	sig := &git.Signature{
		Name:  "Rand Om Hacker",
		Email: "random@hacker.com",
		When:  time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
	}

	idx, err := repo.Index()
	checkFatal(t, err)
	filepath.Walk(repo.Workdir(), func(path string, info os.FileInfo, _ error) (err error) {
		if info.IsDir() {
			return
		}
		lenWorkdir := len(repo.Workdir())
		if path[lenWorkdir:lenWorkdir+4] == ".git" {
			return
		}
		err = idx.AddByPath(path[lenWorkdir:])
		checkFatal(t, err)
		return
	})
	treeId, err := idx.WriteTree()
	checkFatal(t, err)

	message := "This is a commit\n"
	tree, err := repo.LookupTree(treeId)
	checkFatal(t, err)
	commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
	checkFatal(t, err)

	return commitId, treeId
}

func checkFatal(t *testing.T, err error) {
	if err == nil {
		return
	}

	// The failure happens at wherever we were called, not here
	_, file, line, ok := runtime.Caller(1)
	if !ok {
		t.Fatal()
	}

	t.Fatalf("Fail at %v:%v; %v", file, line, err)
}

func readAll(reader io.Reader) (data string) {
	data = ""
	var s scanner.Scanner
	s.Init(reader)
	s.Whitespace = 1
	tok := s.Scan()
	for tok != scanner.EOF {
		data += s.TokenText()
		tok = s.Scan()
	}
	return
}
<|endoftext|>


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

// Package main is the main package for fix imports
package main

import (
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/corsc/go-tools/commons"
	"github.com/corsc/go-tools/fiximports/fiximports"
)

func usage() {
	commons.LogError("Usage of %s:\n", os.Args[0])
	commons.LogError("\tfiximports [flags] # runs on package in current directory\n")
	commons.LogError("\tfiximports [flags] directory\n")
	commons.LogError("\tfiximports [flags] files... # must be a single package\n")
	commons.LogError("Flags:\n")
	flag.PrintDefaults()
}

func main() {
	updateFile := false

	flag.Usage = usage
	flag.BoolVar(&updateFile, "w", false, "write result to (source) file instead of stdout")
	flag.Parse()

	argsToFile := fiximports.FilesFromArgsFactory(flag.NArg())
	filenames, err := argsToFile.FileNames()

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(-1)
	}

	var outputWriter io.Writer
	if !updateFile {
		// default write to os.Stdout
		outputWriter = os.Stdout
	}

	fiximports.ProcessFiles(filenames, outputWriter)
}
<commit_msg>[All] Quickly add Go Mod support and vendor deps<commit_after>// Copyright 2017- Corey Scott http://www.sage42.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package main is the main package for fix imports
package main

import (
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/corsc/go-tools/commons"
	"github.com/corsc/go-tools/fiximports/fiximports"
)

func usage() {
	commons.LogError("Usage of %s:\n", os.Args[0])
	commons.LogError("\tfiximports [flags] # runs on package in current directory\n")
	commons.LogError("\tfiximports [flags] directory\n")
	commons.LogError("\tfiximports [flags] files... # must be a single package\n")
	commons.LogError("Flags:\n")
	flag.PrintDefaults()
}

func main() {
	updateFile := false

	flag.Usage = usage
	flag.BoolVar(&updateFile, "w", false, "write result to (source) file instead of stdout")
	flag.Parse()

	argsToFile := fiximports.FilesFromArgsFactory(flag.NArg())
	filenames, err := argsToFile.FileNames()

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(-1)
	}

	var outputWriter io.Writer
	if !updateFile {
		// default write to os.Stdout
		outputWriter = os.Stdout
	}

	fiximports.ProcessFiles(filenames, outputWriter)
}
<|endoftext|>


<commit_before>package dht

import (
	"code.google.com/p/vitess/go/cache"
	"container/ring"
)

var (
	// Values "inspired" by jch's dht.c.
	MaxInfoHashes    = 16384
	MaxInfoHashPeers = 2048
)

// For the inner map, the key address in binary form. value=ignored.
type peerContactsSet struct {
	set map[string]bool
	// Needed to ensure different peers are returned each time.
	ring *ring.Ring
}

// next returns up to 8 peer contacts, if available. Further calls will return a
// different set of contacts, if possible.
func (p *peerContactsSet) next() []string {
	count := kNodes
	if count > p.Size() {
		count = p.Size()
	}
	x := make([]string, 0, count)
	var next *ring.Ring
	for i := 0; i < count; i++ {
		next = p.ring.Next()
		x = append(x, next.Value.(string))
		p.ring = next
	}
	return x
}

func (p *peerContactsSet) put(peerContact string) bool {
	if p.Size() > MaxInfoHashPeers {
		return false
	}
	if ok := p.set[peerContact]; !ok {
		p.set[peerContact] = true

		r := &ring.Ring{Value: peerContact}
		if p.ring == nil {
			p.ring = r
		} else {
			p.ring.Link(r)
		}
		return true
	}
	return false
}

func (p *peerContactsSet) Size() int {
	return len(p.set)
}

func newPeerStore() *peerStore {
	return &peerStore{
		infoHashPeers:        cache.NewLRUCache(uint64(MaxInfoHashes)),
		localActiveDownloads: make(map[InfoHash]bool),
	}
}

type peerStore struct {
	// cache of peers for infohashes. Each key is an infohash and the
	// values are peerContactsSet.
	infoHashPeers *cache.LRUCache
	// infoHashes for which we are peers.
	localActiveDownloads map[InfoHash]bool
}

func (h *peerStore) size() int {
	length, _, _, _ := h.infoHashPeers.Stats()
	return int(length)
}

func (h *peerStore) get(ih InfoHash) *peerContactsSet {
	c, ok := h.infoHashPeers.Get(string(ih))
	if !ok {
		return nil
	}
	contacts := c.(*peerContactsSet)
	return contacts
}

// count shows the number of know peers for the given infohash.
func (h *peerStore) count(ih InfoHash) int {
	peers := h.get(ih)
	if peers == nil {
		return 0
	}
	return peers.Size()
}

// peerContacts returns a random set of 8 peers for the ih InfoHash.
func (h *peerStore) peerContacts(ih InfoHash) []string {
	peers := h.get(ih)
	if peers == nil {
		return nil
	}
	return peers.next()
}

// updateContact adds peerContact as a peer for the provided ih. Returns true
// if the contact was added, false otherwise (e.g: already present) .
func (h *peerStore) addContact(ih InfoHash, peerContact string) bool {
	var peers *peerContactsSet
	p, ok := h.infoHashPeers.Get(string(ih))
	if ok {
		var okType bool
		peers, okType = p.(*peerContactsSet)
		if okType && peers != nil {
			return peers.put(peerContact)
		}
	}
	if h.size() > MaxInfoHashes {
		// Already tracking too many infohashes. Drop this insertion.
		return false
	}
	peers = &peerContactsSet{set: make(map[string]bool)}
	h.infoHashPeers.Set(string(ih), peers)
	return peers.put(peerContact)
}

func (h *peerStore) addLocalDownload(ih InfoHash) {
	h.localActiveDownloads[ih] = true
}

func (h *peerStore) hasLocalDownload(ih InfoHash) bool {
	_, ok := h.localActiveDownloads[ih]
	return ok
}
<commit_msg>peer_store note on memory usage<commit_after>package dht

import (
	"code.google.com/p/vitess/go/cache"
	"container/ring"
)

var (
	// The default values were inspired by jch's dht.c. The formula to calculate the memory
	// usage is: MaxInfoHashes*MaxInfoHashPeers*len(peerContact).
	//
	// len(peerContact) is ~6 bytes, so after several days the contact store with the default
	// values should consume 192MB of memory.

	// MaxInfoHashes is the limit of number of infohashes for which we should keep a peer list.
	// If this value and MaxInfoHashPeers are unchanged, after several days the used space in
	// RAM would approach 192MB. Large values help keeping the DHT network healthy.
	MaxInfoHashes = 16384
	// MaxInfoHashPeers is the limit of number of peers to be tracked for each infohash. One
	// single peer contact typically consumes 6 bytes.
	MaxInfoHashPeers = 2048
)

// For the inner map, the key address in binary form. value=ignored.
type peerContactsSet struct {
	set map[string]bool
	// Needed to ensure different peers are returned each time.
	ring *ring.Ring
}

// next returns up to 8 peer contacts, if available. Further calls will return a
// different set of contacts, if possible.
func (p *peerContactsSet) next() []string {
	count := kNodes
	if count > p.Size() {
		count = p.Size()
	}
	x := make([]string, 0, count)
	var next *ring.Ring
	for i := 0; i < count; i++ {
		next = p.ring.Next()
		x = append(x, next.Value.(string))
		p.ring = next
	}
	return x
}

func (p *peerContactsSet) put(peerContact string) bool {
	if p.Size() > MaxInfoHashPeers {
		return false
	}
	if ok := p.set[peerContact]; !ok {
		p.set[peerContact] = true

		r := &ring.Ring{Value: peerContact}
		if p.ring == nil {
			p.ring = r
		} else {
			p.ring.Link(r)
		}
		return true
	}
	return false
}

func (p *peerContactsSet) Size() int {
	return len(p.set)
}

func newPeerStore() *peerStore {
	return &peerStore{
		infoHashPeers:        cache.NewLRUCache(uint64(MaxInfoHashes)),
		localActiveDownloads: make(map[InfoHash]bool),
	}
}

type peerStore struct {
	// cache of peers for infohashes. Each key is an infohash and the
	// values are peerContactsSet.
	infoHashPeers *cache.LRUCache
	// infoHashes for which we are peers.
	localActiveDownloads map[InfoHash]bool
}

func (h *peerStore) size() int {
	length, _, _, _ := h.infoHashPeers.Stats()
	return int(length)
}

func (h *peerStore) get(ih InfoHash) *peerContactsSet {
	c, ok := h.infoHashPeers.Get(string(ih))
	if !ok {
		return nil
	}
	contacts := c.(*peerContactsSet)
	return contacts
}

// count shows the number of know peers for the given infohash.
func (h *peerStore) count(ih InfoHash) int {
	peers := h.get(ih)
	if peers == nil {
		return 0
	}
	return peers.Size()
}

// peerContacts returns a random set of 8 peers for the ih InfoHash.
func (h *peerStore) peerContacts(ih InfoHash) []string {
	peers := h.get(ih)
	if peers == nil {
		return nil
	}
	return peers.next()
}

// updateContact adds peerContact as a peer for the provided ih. Returns true
// if the contact was added, false otherwise (e.g: already present) .
func (h *peerStore) addContact(ih InfoHash, peerContact string) bool {
	var peers *peerContactsSet
	p, ok := h.infoHashPeers.Get(string(ih))
	if ok {
		var okType bool
		peers, okType = p.(*peerContactsSet)
		if okType && peers != nil {
			return peers.put(peerContact)
		}
	}
	if h.size() > MaxInfoHashes {
		// Already tracking too many infohashes. Drop this insertion.
		return false
	}
	peers = &peerContactsSet{set: make(map[string]bool)}
	h.infoHashPeers.Set(string(ih), peers)
	return peers.put(peerContact)
}

func (h *peerStore) addLocalDownload(ih InfoHash) {
	h.localActiveDownloads[ih] = true
}

func (h *peerStore) hasLocalDownload(ih InfoHash) bool {
	_, ok := h.localActiveDownloads[ih]
	return ok
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"strconv"

	"github.com/codegangsta/cli"
)

func (u *SomaUtil) GetCliArgumentCount(c *cli.Context) int {
	a := c.Args()
	if !a.Present() {
		return 0
	}
	return len(a.Tail()) + 1
}

func (u *SomaUtil) ValidateCliArgument(c *cli.Context, pos uint8, s string) {
	a := c.Args()
	if a.Get(int(pos)-1) != s {
		u.Abort(fmt.Sprintf("Syntax error, missing keyword: ", s))
	}
}

func (u *SomaUtil) ValidateCliMinArgumentCount(c *cli.Context, i uint8) {
	ct := u.GetCliArgumentCount(c)
	if ct < int(i) {
		u.Abort(fmt.Sprintf(
			"Syntax error, incorrect argument count (%d < %d+ expected)",
			ct,
			i,
		))
	}
}

func (u *SomaUtil) ValidateCliArgumentCount(c *cli.Context, i uint8) {
	a := c.Args()
	if i == 0 {
		if a.Present() {
			u.Abort("Syntax error, command takes no arguments")
		}
	} else {
		if !a.Present() || len(a.Tail()) != (int(i)-1) {
			u.Abort(fmt.Sprintf(
				"Syntax error, incorrect argument count (expected: %d, received %d)",
				i,
				len(a.Tail()),
			))
		}
	}
}

func (u *SomaUtil) GetFullArgumentSlice(c *cli.Context) []string {
	sl := []string{c.Args().First()}
	sl = append(sl, c.Args().Tail()...)
	return sl
}

func (u *SomaUtil) ParseVariableArguments(keys []string, rKeys []string, args []string) (map[string]string, []string) {
	// return map of the parse result
	result := make(map[string]string)
	// map to test which required keys were found
	argumentCheck := make(map[string]bool)
	// return slice which optional keys were found
	optionalKeys := make([]string, 0)
	// no required keys is valid
	if len(rKeys) > 0 {
		for _, key := range rKeys {
			argumentCheck[key] = false
		}
	}
	skipNext := false

	for pos, val := range args {
		// skip current argument if last argument was a keyword
		if skipNext {
			skipNext = false
			continue
		}

		if u.SliceContainsString(val, keys) {
			// check back-to-back keywords
			u.CheckStringNotAKeyword(args[pos+1], keys)
			result[val] = args[pos+1]
			argumentCheck[val] = true
			skipNext = true
			if !u.SliceContainsString(val, rKeys) {
				optionalKeys = append(optionalKeys, val)
			}
			continue
		}
		// keywords trigger continue, arguments are skipped over.
		// reaching this is an error
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check we managed to collect all required keywords
	for _, v := range argumentCheck {
		if !v {
			u.Abort(fmt.Sprintf("Syntax error, missing keyword: %s", v))
		}
	}

	return result, optionalKeys
}

/*
 * This function parses whitespace separated argument lists of
 * keyword/value pairs were keywords can be specified multiple
 * times, some keywords are required and some only allowed once.
 * Sequence of multiple keywords are detected and lead to abort
 *
 * multKeys => [ "port", "transport" ]
 * uniqKeys => [ "team" ]
 * reqKeys  => [ "team" ]
 * args     => [ "port", "53", "transport", "tcp", "transport",
 *               "udp", "team", "ITOMI" ]
 *
 * result => result["team"] = [ "ITOMI" ]
 *           result["port"] = [ "53" ]
 *           result["transport"] = [ "tcp", "udp" ]
 */
func (u *SomaUtil) ParseVariadicArguments(
	multKeys []string, // keys that may appear multiple times
	uniqKeys []string, // keys that are allowed at most once
	reqKeys []string, // keys that are required at least one
	args []string, // arguments to parse
) map[string][]string {
	// returns a map of slices of string
	result := make(map[string][]string)

	// merge key slices
	keys := append(multKeys, uniqKeys...)

	// helper to skip over next value in args slice
	skip := false

	for pos, val := range args {
		// skip current arg if last argument was a keyword
		if skip {
			skip = false
			continue
		}

		if u.SliceContainsString(val, keys) {
			// check for back-to-back keyswords
			u.CheckStringNotAKeyword(args[pos+1], keys)

			// append value of current keyword into result map
			result[val] = append(result[val], args[pos+1])
			skip = true
			continue
		}
		// keywords trigger continue before this
		// values after keywords are skip'ed
		// reaching this is an error
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check if we managed to collect all required keywords
	for _, key := range reqKeys {
		// ok is false if slice is nil
		if _, ok := result[key]; !ok {
			u.Abort(fmt.Sprintf("Syntax error, missing keyword: %s", key))
		}
	}

	// check if unique keywords were only specified once
	for _, key := range uniqKeys {
		if sl, ok := result[key]; ok && (len(sl) > 1) {
			u.Abort(fmt.Sprintf("Syntax error, keyword must only be provided once: %s", key))
		}
	}

	return result
}

func (u *SomaUtil) ParseVariadicCheckArguments(args []string) (
	map[string][]string,
	[]proto.CheckConfigConstraint,
	[]proto.CheckConfigThreshold,
) {
	// create return objects
	result := make(map[string][]string)
	constraints := []proto.CheckConfigConstraint{}
	thresholds := []proto.CheckConfigThreshold{}
	var err error

	multiple := []string{"threshold", "constraint"}
	unique := []string{"in", "on", "with", "interval", "inheritance", "childrenonly", "extern"}
	required := []string{"in", "on", "with", "interval"}

	constraintTypes := []string{"service", "attribute", "system", "custom", "oncall", "native"}
	// merge key slices
	keys := append(multiple, unique...)

	// iteration helper
	skip := false
	skipcount := 0

argloop:
	for pos, val := range args {
		// skip current arg if it was already consumed
		if skip {
			skipcount--
			if skipcount == 0 {
				skip = false
			}
			continue
		}

		if u.SliceContainsString(val, keys) {
			// check for back-to-back keyswords
			u.CheckStringNotAKeyword(args[pos+1], keys)

			switch val {
			case "threshold":
				if len(args[pos+1:]) < 6 {
					u.Abort("Syntax error, incomplete threshold specification")
				}
				t := u.ParseVariadicArguments(
					[]string{},
					[]string{"predicate", "level", "value"},
					[]string{"predicate", "level", "value"},
					args[pos+1:pos+7])
				thr := proto.CheckConfigThreshold{}
				thr.Predicate.Symbol = t["predicate"][0]
				thr.Level.Name = t["level"][0]
				if thr.Value, err = strconv.ParseInt(t["value"][0], 10, 64); err != nil {
					u.Abort(fmt.Sprintf("Syntax error, value argument not numeric: %s",
						t["value"][0]))
				}
				thresholds = append(thresholds, thr)
				skip = true
				skipcount = 6
				continue argloop
			case "constraint":
				// argument is the start of a constraint specification.
				// check we have enough arguments left
				if len(args[pos+1:]) < 3 {
					u.Abort("Syntax error, incomplete constraint specification")
				}
				// check constraint type specification
				if !u.SliceContainsString(args[pos+2], constraintTypes) {
					u.Abort(fmt.Sprintf("Syntax error, unknown contraint type: %s",
						args[pos+1]))
				}
				constr := proto.CheckConfigConstraint{}
				constr.ConstraintType = args[pos+1]
				switch constr.ConstraintType {
				case "service":
					constr.Service = &proto.PropertyService{}
					switch args[pos+2] {
					case "name":
						constr.Service.Name = args[pos+3]
					default:
						u.Abort(fmt.Sprintf("Syntax error, can not constraint service to %s",
							args[pos+2]))
					}
				case "attribute":
					constr.Attribute = &proto.ServiceAttribute{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				case "system":
					constr.System = &proto.PropertySystem{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				case "custom":
					constr.Custom = &proto.PropertyCustom{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				case "oncall":
					constr.Oncall = &proto.PropertyOncall{}
					switch args[pos+2] {
					case "id":
						constr.Oncall.Id = args[pos+3]
					case "name":
						constr.Oncall.Name = args[pos+3]
					default:
						u.Abort(fmt.Sprintf("Syntax error, can not constraint oncall to %s",
							args[pos+2]))
					}
				case "native":
					constr.Native = &proto.PropertyNative{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				}
				constraints = append(constraints, constr)
				skip = true
				skipcount = 3
				continue argloop
			case "on":
				result["on/type"] = append(result["on/type"], args[pos+1])
				result["on/object"] = append(result["on/object"], args[pos+2])
				result[val] = append(result[val], fmt.Sprintf("%s::%s", args[pos+1], args[pos+2]))
				skip = true
				skipcount = 2
				continue argloop
			default:
				// regular key/value keyword
				result[val] = append(result[val], args[pos+1])
				skip = true
				skipcount = 1
				continue argloop
			}
		}
		// error is reached if argument was not skipped and not a
		// recognized keyword
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check if all required keywords were collected
	for _, key := range required {
		if _, ok := result[key]; !ok {
			u.Abort(fmt.Sprintf("Syntax error, missing keyword: %s", key))
		}
	}

	// check if unique keywords were only specuified once
	for _, key := range unique {
		// check ok since unique may still be optional
		if sl, ok := result[key]; ok && (len(sl) > 1) {
			u.Abort(fmt.Sprintf("Syntax error, keyword must only be provided once: %s", key))
		}
	}

	return result, constraints, thresholds
}

func (u *SomaUtil) ParseVariadicCapabilityArguments(
	multKeys []string, // keys that may appear multiple times
	uniqKeys []string, // keys that are allowed at most once
	reqKeys []string, // keys that are required at least one
	args []string, // arguments to parse
) (map[string][]string, []proto.CapabilityConstraint) {
	// returns a map of slices of string
	result := make(map[string][]string)
	constr := make([]proto.CapabilityConstraint, 0)

	// merge key slices
	multKeys = append(multKeys, []string{"constraint", "demux"}...)
	keys := append(multKeys, uniqKeys...)

	// helper to skip over next value in args slice
	skip := false
	skipcount := 0

	for pos, val := range args {
		// skip current arg if last argument was a keyword
		if skip {
			skipcount--
			if skipcount == 0 {
				skip = false
			}
			continue
		}

		if u.SliceContainsString(val, keys) {
			// there must be at least one arguments left
			if len(args[pos+1:]) < 1 {
				u.Abort("Syntax error, incomplete key/value specification (too few items left to parse)")
			}
			// check for back-to-back keyswords
			u.CheckStringNotAKeyword(args[pos+1], keys)

			switch val {
			case "constraint":
				// must be at least 3 items left
				if len(args[pos+1:]) < 3 {
					u.Abort("Syntax error, incomplete constraint specification")
				}
				// constraint must be type `system` or `attribute`
				switch args[pos+1] {
				case "system":
					u.CheckStringIsSystemProperty(args[pos+2])
				case "attribute":
					u.CheckStringIsServiceAttribute(args[pos+2])
				default:
					u.Abort(fmt.Sprintf("Syntax error, invalid constraint type: %s", args[pos+1]))
				}
				constr = append(constr, CapabilityConstraint{
					Type:  args[pos+1],
					Name:  args[pos+2],
					Value: args[pos+3],
				})
				skip = true
				skipcount = 3
				continue
			case "demux":
				// argument to demux must be a service attribute
				u.CheckStringIsServiceAttribute(args[pos+1])
				fallthrough
			default:
				// append value of current keyword into result map
				result[val] = append(result[val], args[pos+1])
				skip = true
				skipcount = 1
				continue
			}
		}
		// keywords trigger continue before this
		// values after keywords are skip'ed
		// reaching this is an error
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check if we managed to collect all required keywords
	for _, key := range reqKeys {
		// ok is false if slice is nil
		if _, ok := result[key]; !ok {
			u.Abort(fmt.Sprintf("Syntax error, missing required keyword: %s", key))
		}
	}

	// check if unique keywords were only specified once
	for _, key := range uniqKeys {
		if sl, ok := result[key]; ok && (len(sl) > 1) {
			u.Abort(fmt.Sprintf("Syntax error, keyword must only be provided once: %s", key))
		}
	}

	return result, constr
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Fix somaproto namespacing<commit_after>package util

import (
	"fmt"
	"strconv"

	"github.com/codegangsta/cli"
)

func (u *SomaUtil) GetCliArgumentCount(c *cli.Context) int {
	a := c.Args()
	if !a.Present() {
		return 0
	}
	return len(a.Tail()) + 1
}

func (u *SomaUtil) ValidateCliArgument(c *cli.Context, pos uint8, s string) {
	a := c.Args()
	if a.Get(int(pos)-1) != s {
		u.Abort(fmt.Sprintf("Syntax error, missing keyword: ", s))
	}
}

func (u *SomaUtil) ValidateCliMinArgumentCount(c *cli.Context, i uint8) {
	ct := u.GetCliArgumentCount(c)
	if ct < int(i) {
		u.Abort(fmt.Sprintf(
			"Syntax error, incorrect argument count (%d < %d+ expected)",
			ct,
			i,
		))
	}
}

func (u *SomaUtil) ValidateCliArgumentCount(c *cli.Context, i uint8) {
	a := c.Args()
	if i == 0 {
		if a.Present() {
			u.Abort("Syntax error, command takes no arguments")
		}
	} else {
		if !a.Present() || len(a.Tail()) != (int(i)-1) {
			u.Abort(fmt.Sprintf(
				"Syntax error, incorrect argument count (expected: %d, received %d)",
				i,
				len(a.Tail()),
			))
		}
	}
}

func (u *SomaUtil) GetFullArgumentSlice(c *cli.Context) []string {
	sl := []string{c.Args().First()}
	sl = append(sl, c.Args().Tail()...)
	return sl
}

func (u *SomaUtil) ParseVariableArguments(keys []string, rKeys []string, args []string) (map[string]string, []string) {
	// return map of the parse result
	result := make(map[string]string)
	// map to test which required keys were found
	argumentCheck := make(map[string]bool)
	// return slice which optional keys were found
	optionalKeys := make([]string, 0)
	// no required keys is valid
	if len(rKeys) > 0 {
		for _, key := range rKeys {
			argumentCheck[key] = false
		}
	}
	skipNext := false

	for pos, val := range args {
		// skip current argument if last argument was a keyword
		if skipNext {
			skipNext = false
			continue
		}

		if u.SliceContainsString(val, keys) {
			// check back-to-back keywords
			u.CheckStringNotAKeyword(args[pos+1], keys)
			result[val] = args[pos+1]
			argumentCheck[val] = true
			skipNext = true
			if !u.SliceContainsString(val, rKeys) {
				optionalKeys = append(optionalKeys, val)
			}
			continue
		}
		// keywords trigger continue, arguments are skipped over.
		// reaching this is an error
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check we managed to collect all required keywords
	for _, v := range argumentCheck {
		if !v {
			u.Abort(fmt.Sprintf("Syntax error, missing keyword: %s", v))
		}
	}

	return result, optionalKeys
}

/*
 * This function parses whitespace separated argument lists of
 * keyword/value pairs were keywords can be specified multiple
 * times, some keywords are required and some only allowed once.
 * Sequence of multiple keywords are detected and lead to abort
 *
 * multKeys => [ "port", "transport" ]
 * uniqKeys => [ "team" ]
 * reqKeys  => [ "team" ]
 * args     => [ "port", "53", "transport", "tcp", "transport",
 *               "udp", "team", "ITOMI" ]
 *
 * result => result["team"] = [ "ITOMI" ]
 *           result["port"] = [ "53" ]
 *           result["transport"] = [ "tcp", "udp" ]
 */
func (u *SomaUtil) ParseVariadicArguments(
	multKeys []string, // keys that may appear multiple times
	uniqKeys []string, // keys that are allowed at most once
	reqKeys []string, // keys that are required at least one
	args []string, // arguments to parse
) map[string][]string {
	// returns a map of slices of string
	result := make(map[string][]string)

	// merge key slices
	keys := append(multKeys, uniqKeys...)

	// helper to skip over next value in args slice
	skip := false

	for pos, val := range args {
		// skip current arg if last argument was a keyword
		if skip {
			skip = false
			continue
		}

		if u.SliceContainsString(val, keys) {
			// check for back-to-back keyswords
			u.CheckStringNotAKeyword(args[pos+1], keys)

			// append value of current keyword into result map
			result[val] = append(result[val], args[pos+1])
			skip = true
			continue
		}
		// keywords trigger continue before this
		// values after keywords are skip'ed
		// reaching this is an error
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check if we managed to collect all required keywords
	for _, key := range reqKeys {
		// ok is false if slice is nil
		if _, ok := result[key]; !ok {
			u.Abort(fmt.Sprintf("Syntax error, missing keyword: %s", key))
		}
	}

	// check if unique keywords were only specified once
	for _, key := range uniqKeys {
		if sl, ok := result[key]; ok && (len(sl) > 1) {
			u.Abort(fmt.Sprintf("Syntax error, keyword must only be provided once: %s", key))
		}
	}

	return result
}

func (u *SomaUtil) ParseVariadicCheckArguments(args []string) (
	map[string][]string,
	[]proto.CheckConfigConstraint,
	[]proto.CheckConfigThreshold,
) {
	// create return objects
	result := make(map[string][]string)
	constraints := []proto.CheckConfigConstraint{}
	thresholds := []proto.CheckConfigThreshold{}
	var err error

	multiple := []string{"threshold", "constraint"}
	unique := []string{"in", "on", "with", "interval", "inheritance", "childrenonly", "extern"}
	required := []string{"in", "on", "with", "interval"}

	constraintTypes := []string{"service", "attribute", "system", "custom", "oncall", "native"}
	// merge key slices
	keys := append(multiple, unique...)

	// iteration helper
	skip := false
	skipcount := 0

argloop:
	for pos, val := range args {
		// skip current arg if it was already consumed
		if skip {
			skipcount--
			if skipcount == 0 {
				skip = false
			}
			continue
		}

		if u.SliceContainsString(val, keys) {
			// check for back-to-back keyswords
			u.CheckStringNotAKeyword(args[pos+1], keys)

			switch val {
			case "threshold":
				if len(args[pos+1:]) < 6 {
					u.Abort("Syntax error, incomplete threshold specification")
				}
				t := u.ParseVariadicArguments(
					[]string{},
					[]string{"predicate", "level", "value"},
					[]string{"predicate", "level", "value"},
					args[pos+1:pos+7])
				thr := proto.CheckConfigThreshold{}
				thr.Predicate.Symbol = t["predicate"][0]
				thr.Level.Name = t["level"][0]
				if thr.Value, err = strconv.ParseInt(t["value"][0], 10, 64); err != nil {
					u.Abort(fmt.Sprintf("Syntax error, value argument not numeric: %s",
						t["value"][0]))
				}
				thresholds = append(thresholds, thr)
				skip = true
				skipcount = 6
				continue argloop
			case "constraint":
				// argument is the start of a constraint specification.
				// check we have enough arguments left
				if len(args[pos+1:]) < 3 {
					u.Abort("Syntax error, incomplete constraint specification")
				}
				// check constraint type specification
				if !u.SliceContainsString(args[pos+2], constraintTypes) {
					u.Abort(fmt.Sprintf("Syntax error, unknown contraint type: %s",
						args[pos+1]))
				}
				constr := proto.CheckConfigConstraint{}
				constr.ConstraintType = args[pos+1]
				switch constr.ConstraintType {
				case "service":
					constr.Service = &proto.PropertyService{}
					switch args[pos+2] {
					case "name":
						constr.Service.Name = args[pos+3]
					default:
						u.Abort(fmt.Sprintf("Syntax error, can not constraint service to %s",
							args[pos+2]))
					}
				case "attribute":
					constr.Attribute = &proto.ServiceAttribute{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				case "system":
					constr.System = &proto.PropertySystem{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				case "custom":
					constr.Custom = &proto.PropertyCustom{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				case "oncall":
					constr.Oncall = &proto.PropertyOncall{}
					switch args[pos+2] {
					case "id":
						constr.Oncall.Id = args[pos+3]
					case "name":
						constr.Oncall.Name = args[pos+3]
					default:
						u.Abort(fmt.Sprintf("Syntax error, can not constraint oncall to %s",
							args[pos+2]))
					}
				case "native":
					constr.Native = &proto.PropertyNative{
						Name:  args[pos+2],
						Value: args[pos+3],
					}
				}
				constraints = append(constraints, constr)
				skip = true
				skipcount = 3
				continue argloop
			case "on":
				result["on/type"] = append(result["on/type"], args[pos+1])
				result["on/object"] = append(result["on/object"], args[pos+2])
				result[val] = append(result[val], fmt.Sprintf("%s::%s", args[pos+1], args[pos+2]))
				skip = true
				skipcount = 2
				continue argloop
			default:
				// regular key/value keyword
				result[val] = append(result[val], args[pos+1])
				skip = true
				skipcount = 1
				continue argloop
			}
		}
		// error is reached if argument was not skipped and not a
		// recognized keyword
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check if all required keywords were collected
	for _, key := range required {
		if _, ok := result[key]; !ok {
			u.Abort(fmt.Sprintf("Syntax error, missing keyword: %s", key))
		}
	}

	// check if unique keywords were only specuified once
	for _, key := range unique {
		// check ok since unique may still be optional
		if sl, ok := result[key]; ok && (len(sl) > 1) {
			u.Abort(fmt.Sprintf("Syntax error, keyword must only be provided once: %s", key))
		}
	}

	return result, constraints, thresholds
}

func (u *SomaUtil) ParseVariadicCapabilityArguments(
	multKeys []string, // keys that may appear multiple times
	uniqKeys []string, // keys that are allowed at most once
	reqKeys []string, // keys that are required at least one
	args []string, // arguments to parse
) (map[string][]string, []proto.CapabilityConstraint) {
	// returns a map of slices of string
	result := make(map[string][]string)
	constr := make([]proto.CapabilityConstraint, 0)

	// merge key slices
	multKeys = append(multKeys, []string{"constraint", "demux"}...)
	keys := append(multKeys, uniqKeys...)

	// helper to skip over next value in args slice
	skip := false
	skipcount := 0

	for pos, val := range args {
		// skip current arg if last argument was a keyword
		if skip {
			skipcount--
			if skipcount == 0 {
				skip = false
			}
			continue
		}

		if u.SliceContainsString(val, keys) {
			// there must be at least one arguments left
			if len(args[pos+1:]) < 1 {
				u.Abort("Syntax error, incomplete key/value specification (too few items left to parse)")
			}
			// check for back-to-back keyswords
			u.CheckStringNotAKeyword(args[pos+1], keys)

			switch val {
			case "constraint":
				// must be at least 3 items left
				if len(args[pos+1:]) < 3 {
					u.Abort("Syntax error, incomplete constraint specification")
				}
				// constraint must be type `system` or `attribute`
				switch args[pos+1] {
				case "system":
					u.CheckStringIsSystemProperty(args[pos+2])
				case "attribute":
					u.CheckStringIsServiceAttribute(args[pos+2])
				default:
					u.Abort(fmt.Sprintf("Syntax error, invalid constraint type: %s", args[pos+1]))
				}
				constr = append(constr, proto.CapabilityConstraint{
					Type:  args[pos+1],
					Name:  args[pos+2],
					Value: args[pos+3],
				})
				skip = true
				skipcount = 3
				continue
			case "demux":
				// argument to demux must be a service attribute
				u.CheckStringIsServiceAttribute(args[pos+1])
				fallthrough
			default:
				// append value of current keyword into result map
				result[val] = append(result[val], args[pos+1])
				skip = true
				skipcount = 1
				continue
			}
		}
		// keywords trigger continue before this
		// values after keywords are skip'ed
		// reaching this is an error
		u.Abort(fmt.Sprintf("Syntax error, erroneus argument: %s", val))
	}

	// check if we managed to collect all required keywords
	for _, key := range reqKeys {
		// ok is false if slice is nil
		if _, ok := result[key]; !ok {
			u.Abort(fmt.Sprintf("Syntax error, missing required keyword: %s", key))
		}
	}

	// check if unique keywords were only specified once
	for _, key := range uniqKeys {
		if sl, ok := result[key]; ok && (len(sl) > 1) {
			u.Abort(fmt.Sprintf("Syntax error, keyword must only be provided once: %s", key))
		}
	}

	return result, constr
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/fern4lvarez/piladb/pila"
	"github.com/fern4lvarez/piladb/pkg/uuid"
	"github.com/fern4lvarez/piladb/pkg/version"

	"github.com/gorilla/mux"
)

// Conn represents the current piladb connection, containing
// the Pila instance and its status.
type Conn struct {
	Pila   *pila.Pila
	Status *Status
}

// NewConn creates and returns a new piladb connection.
func NewConn() *Conn {
	conn := &Conn{}
	conn.Pila = pila.NewPila()
	conn.Status = NewStatus(version.CommitHash(), time.Now(), MemStats())
	return conn
}

// Connection Handlers

// statusHandler writes the piladb status into the response.
func (c *Conn) statusHandler(w http.ResponseWriter, r *http.Request) {
	c.Status.Update(time.Now(), MemStats())

	w.Header().Set("Content-Type", "application/json")
	log.Println(r.Method, r.URL, http.StatusOK)
	w.Write(c.Status.ToJSON())
}

// databasesHandler returns the information of the running databases.
func (c *Conn) databasesHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "PUT" {
		c.createDatabaseHandler(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	log.Println(r.Method, r.URL, http.StatusOK)
	w.Write(c.Pila.Status().ToJSON())
}

// createDatabaseHandler creates a Database and returns 201 and the ID and name
// of the Database.
func (c *Conn) createDatabaseHandler(w http.ResponseWriter, r *http.Request) {
	name := r.FormValue("name")
	if name == "" {
		log.Println(r.Method, r.URL, http.StatusBadRequest, "missing name")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	db := pila.NewDatabase(name)
	err := c.Pila.AddDatabase(db)
	if err != nil {
		log.Println(r.Method, r.URL, http.StatusConflict, err)
		w.WriteHeader(http.StatusConflict)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	log.Println(r.Method, r.URL, http.StatusCreated)
	w.WriteHeader(http.StatusCreated)
	w.Write(db.Status().ToJSON())
}

// databaseHandler returns the information of a single database given its ID
// or name.
func (c *Conn) databaseHandler(databaseID string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)

		// we override the mux vars to be able to test
		// an arbitrary database ID
		if databaseID != "" {
			vars = map[string]string{
				"id": databaseID,
			}
		}

		db, ok := ResourceDatabase(c, vars["id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", vars["id"]))
			return
		}

		if r.Method == "DELETE" {
			_ = c.Pila.RemoveDatabase(db.ID)
			log.Println(r.Method, r.URL, http.StatusNoContent)
			w.WriteHeader(http.StatusNoContent)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		log.Println(r.Method, r.URL, http.StatusOK)
		w.Write(db.Status().ToJSON())
	})
}

// stacksHandler handles the stacks of a database, being able to get the status
// of them, or create a new one.
func (c *Conn) stacksHandler(databaseID string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)

		// we override the mux vars to be able to test
		// an arbitrary database ID
		if databaseID != "" {
			vars = map[string]string{
				"database_id": databaseID,
			}
		}

		db, ok := ResourceDatabase(c, vars["database_id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", vars["database_id"]))
			return
		}

		if r.Method == "PUT" {
			c.createStackHandler(w, r, db.ID.String())
			return
		}

		res, err := db.StacksStatus().ToJSON()
		if err != nil {
			log.Println(r.Method, r.URL, http.StatusBadRequest,
				"error on response serialization:", err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Write(res)
		log.Println(r.Method, r.URL, http.StatusOK)

	})
}

// createStackHandler handles the creation of a stack, given a database
// by its id. Returns the status of the new stack.
func (c *Conn) createStackHandler(w http.ResponseWriter, r *http.Request, databaseID string) {
	name := r.FormValue("name")
	if name == "" {
		log.Println(r.Method, r.URL, http.StatusBadRequest, "missing name")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	db, ok := c.Pila.Database(uuid.UUID(databaseID))
	if !ok {
		c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", databaseID))
		return
	}

	stack := pila.NewStack(name)
	err := db.AddStack(stack)
	if err != nil {
		log.Println(r.Method, r.URL, http.StatusConflict, err)
		w.WriteHeader(http.StatusConflict)
		return
	}

	// Do not check error as the Status of a new stack does
	// not contain types that could cause such case.
	// See http://golang.org/src/encoding/json/encode.go?s=5438:5481#L125
	res, _ := stack.Status().ToJSON()

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	w.Write(res)
	log.Println(r.Method, r.URL, http.StatusCreated)
}

// stackHandler handles operations on a single stack of a database. It holds
// the PUSH, POP and PEEK methods, and the stack deletion.
func (c *Conn) stackHandler(params *map[string]string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		// we override the mux vars to be able to test
		// an arbitrary database and stack ID
		if params != nil {
			vars = *params
		}

		db, ok := ResourceDatabase(c, vars["database_id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", vars["database_id"]))
			return
		}

		stack, ok := ResourceStack(db, vars["stack_id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("stack %s is Gone", vars["stack_id"]))
			return
		}

		if r.Method == "POST" {
			c.pushStackHandler(w, r, stack)
			return
		}

		if r.Method == "DELETE" {
			_ = r.ParseForm()
			if _, ok := r.Form["flush"]; ok {
				c.flushStackHandler(w, r, stack)
				return
			}
			if _, ok := r.Form["full"]; ok {
				c.deleteStackHandler(w, r, db, stack)
				return
			}
			c.popStackHandler(w, r, stack)
			return
		}
	})
}

// pushStackHandler adds an element into a Stack and returns 200 and the element.
func (c *Conn) pushStackHandler(w http.ResponseWriter, r *http.Request, stack *pila.Stack) {
	if r.Body == nil {
		log.Println(r.Method, r.URL, http.StatusBadRequest,
			"no element provided")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	var element pila.Element
	err := element.Decode(r.Body)
	if err != nil {
		log.Println(r.Method, r.URL, http.StatusBadRequest,
			"error on decoding element:", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	stack.Push(element.Value)

	log.Println(r.Method, r.URL, http.StatusOK, element.Value)
	w.Header().Set("Content-Type", "application/json")

	// Do not check error as we consider our element
	// suitable for a JSON encoding.
	b, _ := element.ToJSON()
	w.Write(b)
}

// popStackHandler extracts the peek element of a Stack, returns 200 and returns it.
func (c *Conn) popStackHandler(w http.ResponseWriter, r *http.Request, stack *pila.Stack) {
	value, ok := stack.Pop()
	if !ok {
		log.Println(r.Method, r.URL, http.StatusNoContent)
		w.WriteHeader(http.StatusNoContent)
		return
	}

	element := pila.Element{Value: value}

	log.Println(r.Method, r.URL, http.StatusOK, element.Value)
	w.Header().Set("Content-Type", "application/json")

	// Do not check error as we consider our element
	// suitable for a JSON encoding.
	b, _ := element.ToJSON()
	w.Write(b)
}

// flushStackHandler flushes the Stack, setting the size to 0 and emptying all
// the content.
func (c *Conn) flushStackHandler(w http.ResponseWriter, r *http.Request, stack *pila.Stack) {
	stack.Flush()

	log.Println(r.Method, r.URL, http.StatusOK)
	w.Header().Set("Content-Type", "application/json")

	// Do not check error as we consider that a flushed
	// stack has no JSON encoding issues.
	b, _ := stack.Status().ToJSON()
	w.Write(b)
}

// deleteStackHandler deletes the Stack from a database.
func (c *Conn) deleteStackHandler(w http.ResponseWriter, r *http.Request, database *pila.Database, stack *pila.Stack) {
	stack.Flush()

	// Do not check output as we validated that
	// stack always exists
	_ = database.RemoveStack(stack.ID)

	log.Println(r.Method, r.URL, http.StatusNoContent)
	w.WriteHeader(http.StatusNoContent)
	return
}

// notFoundHandler logs and returns a 404 NotFound response.
func (c *Conn) notFoundHandler(w http.ResponseWriter, r *http.Request) {
	log.Println(r.Method, r.URL, http.StatusNotFound)
	http.NotFound(w, r)
}

// goneHandler logs and returns a 410 Gone response with information
// about the missing resource.
func (c *Conn) goneHandler(w http.ResponseWriter, r *http.Request, message string) {
	log.Println(r.Method, r.URL,
		http.StatusGone, message)
	w.WriteHeader(http.StatusGone)
}
<commit_msg>pilad: fmt for PR<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/fern4lvarez/piladb/pila"
	"github.com/fern4lvarez/piladb/pkg/uuid"
	"github.com/fern4lvarez/piladb/pkg/version"

	"github.com/gorilla/mux"
)

// Conn represents the current piladb connection, containing
// the Pila instance and its status.
type Conn struct {
	Pila   *pila.Pila
	Status *Status
}

// NewConn creates and returns a new piladb connection.
func NewConn() *Conn {
	conn := &Conn{}
	conn.Pila = pila.NewPila()
	conn.Status = NewStatus(version.CommitHash(), time.Now(), MemStats())
	return conn
}

// Connection Handlers

// statusHandler writes the piladb status into the response.
func (c *Conn) statusHandler(w http.ResponseWriter, r *http.Request) {
	c.Status.Update(time.Now(), MemStats())

	w.Header().Set("Content-Type", "application/json")
	log.Println(r.Method, r.URL, http.StatusOK)
	w.Write(c.Status.ToJSON())
}

// databasesHandler returns the information of the running databases.
func (c *Conn) databasesHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "PUT" {
		c.createDatabaseHandler(w, r)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	log.Println(r.Method, r.URL, http.StatusOK)
	w.Write(c.Pila.Status().ToJSON())
}

// createDatabaseHandler creates a Database and returns 201 and the ID and name
// of the Database.
func (c *Conn) createDatabaseHandler(w http.ResponseWriter, r *http.Request) {
	name := r.FormValue("name")
	if name == "" {
		log.Println(r.Method, r.URL, http.StatusBadRequest, "missing name")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	db := pila.NewDatabase(name)
	err := c.Pila.AddDatabase(db)
	if err != nil {
		log.Println(r.Method, r.URL, http.StatusConflict, err)
		w.WriteHeader(http.StatusConflict)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	log.Println(r.Method, r.URL, http.StatusCreated)
	w.WriteHeader(http.StatusCreated)
	w.Write(db.Status().ToJSON())
}

// databaseHandler returns the information of a single database given its ID
// or name.
func (c *Conn) databaseHandler(databaseID string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)

		// we override the mux vars to be able to test
		// an arbitrary database ID
		if databaseID != "" {
			vars = map[string]string{
				"id": databaseID,
			}
		}

		db, ok := ResourceDatabase(c, vars["id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", vars["id"]))
			return
		}

		if r.Method == "DELETE" {
			_ = c.Pila.RemoveDatabase(db.ID)
			log.Println(r.Method, r.URL, http.StatusNoContent)
			w.WriteHeader(http.StatusNoContent)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		log.Println(r.Method, r.URL, http.StatusOK)
		w.Write(db.Status().ToJSON())
	})
}

// stacksHandler handles the stacks of a database, being able to get the status
// of them, or create a new one.
func (c *Conn) stacksHandler(databaseID string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)

		// we override the mux vars to be able to test
		// an arbitrary database ID
		if databaseID != "" {
			vars = map[string]string{
				"database_id": databaseID,
			}
		}

		db, ok := ResourceDatabase(c, vars["database_id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", vars["database_id"]))
			return
		}

		if r.Method == "PUT" {
			c.createStackHandler(w, r, db.ID.String())
			return
		}

		res, err := db.StacksStatus().ToJSON()
		if err != nil {
			log.Println(r.Method, r.URL, http.StatusBadRequest,
				"error on response serialization:", err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Write(res)
		log.Println(r.Method, r.URL, http.StatusOK)

	})
}

// createStackHandler handles the creation of a stack, given a database
// by its id. Returns the status of the new stack.
func (c *Conn) createStackHandler(w http.ResponseWriter, r *http.Request, databaseID string) {
	name := r.FormValue("name")
	if name == "" {
		log.Println(r.Method, r.URL, http.StatusBadRequest, "missing name")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	db, ok := c.Pila.Database(uuid.UUID(databaseID))
	if !ok {
		c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", databaseID))
		return
	}

	stack := pila.NewStack(name)
	err := db.AddStack(stack)
	if err != nil {
		log.Println(r.Method, r.URL, http.StatusConflict, err)
		w.WriteHeader(http.StatusConflict)
		return
	}

	// Do not check error as the Status of a new stack does
	// not contain types that could cause such case.
	// See http://golang.org/src/encoding/json/encode.go?s=5438:5481#L125
	res, _ := stack.Status().ToJSON()

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	w.Write(res)
	log.Println(r.Method, r.URL, http.StatusCreated)
}

// stackHandler handles operations on a single stack of a database. It holds
// the PUSH, POP and PEEK methods, and the stack deletion.
func (c *Conn) stackHandler(params *map[string]string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		// we override the mux vars to be able to test
		// an arbitrary database and stack ID
		if params != nil {
			vars = *params
		}

		db, ok := ResourceDatabase(c, vars["database_id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("database %s is Gone", vars["database_id"]))
			return
		}

		stack, ok := ResourceStack(db, vars["stack_id"])
		if !ok {
			c.goneHandler(w, r, fmt.Sprintf("stack %s is Gone", vars["stack_id"]))
			return
		}

		if r.Method == "POST" {
			c.pushStackHandler(w, r, stack)
			return
		}

		if r.Method == "DELETE" {
			_ = r.ParseForm()
			if _, ok := r.Form["flush"]; ok {
				c.flushStackHandler(w, r, stack)
				return
			}
			if _, ok := r.Form["full"]; ok {
				c.deleteStackHandler(w, r, db, stack)
				return
			}
			c.popStackHandler(w, r, stack)
			return
		}
	})
}

// pushStackHandler adds an element into a Stack and returns 200 and the element.
func (c *Conn) pushStackHandler(w http.ResponseWriter, r *http.Request, stack *pila.Stack) {
	if r.Body == nil {
		log.Println(r.Method, r.URL, http.StatusBadRequest,
			"no element provided")
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	var element pila.Element
	err := element.Decode(r.Body)
	if err != nil {
		log.Println(r.Method, r.URL, http.StatusBadRequest,
			"error on decoding element:", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	stack.Push(element.Value)

	log.Println(r.Method, r.URL, http.StatusOK, element.Value)
	w.Header().Set("Content-Type", "application/json")

	// Do not check error as we consider our element
	// suitable for a JSON encoding.
	b, _ := element.ToJSON()
	w.Write(b)
}

// popStackHandler extracts the peek element of a Stack, returns 200 and returns it.
func (c *Conn) popStackHandler(w http.ResponseWriter, r *http.Request, stack *pila.Stack) {
	value, ok := stack.Pop()
	if !ok {
		log.Println(r.Method, r.URL, http.StatusNoContent)
		w.WriteHeader(http.StatusNoContent)
		return
	}

	element := pila.Element{Value: value}

	log.Println(r.Method, r.URL, http.StatusOK, element.Value)
	w.Header().Set("Content-Type", "application/json")

	// Do not check error as we consider our element
	// suitable for a JSON encoding.
	b, _ := element.ToJSON()
	w.Write(b)
}

// flushStackHandler flushes the Stack, setting the size to 0 and emptying all
// the content.
func (c *Conn) flushStackHandler(w http.ResponseWriter, r *http.Request, stack *pila.Stack) {
	stack.Flush()

	log.Println(r.Method, r.URL, http.StatusOK)
	w.Header().Set("Content-Type", "application/json")

	// Do not check error as we consider that a flushed
	// stack has no JSON encoding issues.
	b, _ := stack.Status().ToJSON()
	w.Write(b)
}

// deleteStackHandler deletes the Stack from a database.
func (c *Conn) deleteStackHandler(w http.ResponseWriter, r *http.Request, database *pila.Database, stack *pila.Stack) {
	stack.Flush()

	// Do not check output as we validated that
	// stack always exists.
	_ = database.RemoveStack(stack.ID)

	log.Println(r.Method, r.URL, http.StatusNoContent)
	w.WriteHeader(http.StatusNoContent)
	return
}

// notFoundHandler logs and returns a 404 NotFound response.
func (c *Conn) notFoundHandler(w http.ResponseWriter, r *http.Request) {
	log.Println(r.Method, r.URL, http.StatusNotFound)
	http.NotFound(w, r)
}

// goneHandler logs and returns a 410 Gone response with information
// about the missing resource.
func (c *Conn) goneHandler(w http.ResponseWriter, r *http.Request, message string) {
	log.Println(r.Method, r.URL,
		http.StatusGone, message)
	w.WriteHeader(http.StatusGone)
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/params"
	"github.com/ethereum/go-ethereum/pow"
	"github.com/ethereum/go-ethereum/rlp"
	"gopkg.in/fatih/set.v0"
)

const (
	// must be bumped when consensus algorithm is changed, this forces the upgradedb
	// command to be run (forces the blocks to be imported again using the new algorithm)
	BlockChainVersion = 1
)

var statelogger = logger.NewLogger("BLOCK")

type BlockProcessor struct {
	db      common.Database
	extraDb common.Database
	// Mutex for locking the block processor. Blocks can only be handled one at a time
	mutex sync.Mutex
	// Canonical block chain
	bc *ChainManager
	// non-persistent key/value memory storage
	mem map[string]*big.Int
	// Proof of work used for validating
	Pow pow.PoW

	txpool *TxPool

	// The last attempted block is mainly used for debugging purposes
	// This does not have to be a valid block and will be set during
	// 'Process' & canonical validation.
	lastAttemptedBlock *types.Block

	events event.Subscription

	eventMux *event.TypeMux
}

func NewBlockProcessor(db, extra common.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
	sm := &BlockProcessor{
		db:       db,
		extraDb:  extra,
		mem:      make(map[string]*big.Int),
		Pow:      pow,
		bc:       chainManager,
		eventMux: eventMux,
		txpool:   txpool,
	}

	return sm
}

func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
	coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
	coinbase.SetGasPool(block.Header().GasLimit)

	// Process the transactions on to parent state
	receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
	if err != nil {
		return nil, err
	}

	return receipts, nil
}

func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
	// If we are mining this block and validating we want to set the logs back to 0
	//statedb.EmptyLogs()

	cb := statedb.GetStateObject(coinbase.Address())
	_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb)
	if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
		// If the account is managed, remove the invalid nonce.
		from, _ := tx.From()
		self.bc.TxState().RemoveNonce(from, tx.Nonce())
		return nil, nil, err
	}

	// Update the state with pending changes
	statedb.Update()

	cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas))
	receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative)

	logs := statedb.GetLogs(tx.Hash())
	receipt.SetLogs(logs)
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

	glog.V(logger.Debug).Infoln(receipt)

	// Notify all subscribers
	if !transientProcess {
		go self.eventMux.Post(TxPostEvent{tx})
		go self.eventMux.Post(logs)
	}

	return receipt, gas, err
}
func (self *BlockProcessor) ChainManager() *ChainManager {
	return self.bc
}

func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
	var (
		receipts      types.Receipts
		totalUsedGas  = big.NewInt(0)
		err           error
		cumulativeSum = new(big.Int)
	)

	for i, tx := range txs {
		statedb.StartRecord(tx.Hash(), block.Hash(), i)

		receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
		if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
			return nil, err
		}

		if err != nil {
			glog.V(logger.Core).Infoln("TX err:", err)
		}
		receipts = append(receipts, receipt)

		cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
	}

	if block.GasUsed().Cmp(totalUsedGas) != 0 {
		return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas))
	}

	if transientProcess {
		go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()})
	}

	return receipts, err
}

// Process block will attempt to process the given block's transactions and applies them
// on top of the block's parent state (given it exists) and will return wether it was
// successful or not.
func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, logs state.Logs, err error) {
	// Processing a blocks may never happen simultaneously
	sm.mutex.Lock()
	defer sm.mutex.Unlock()

	header := block.Header()
	if sm.bc.HasBlock(header.Hash()) {
		return nil, nil, &KnownBlockError{header.Number, header.Hash()}
	}

	if !sm.bc.HasBlock(header.ParentHash) {
		return nil, nil, ParentError(header.ParentHash)
	}
	parent := sm.bc.GetBlock(header.ParentHash)

	return sm.processWithParent(block, parent)
}

func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big.Int, logs state.Logs, err error) {
	sm.lastAttemptedBlock = block

	// Create a new state based on the parent's root (e.g., create copy)
	state := state.New(parent.Root(), sm.db)

	// Block validation
	if err = sm.ValidateHeader(block.Header(), parent.Header()); err != nil {
		return
	}

	// There can be at most two uncles
	if len(block.Uncles()) > 2 {
		return nil, nil, ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles()))
	}

	receipts, err := sm.TransitionState(state, parent, block, false)
	if err != nil {
		return
	}

	header := block.Header()

	// Validate the received block's bloom with the one derived from the generated receipts.
	// For valid blocks this should always validate to true.
	rbloom := types.CreateBloom(receipts)
	if rbloom != header.Bloom {
		err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
		return
	}

	// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
	// can be used by light clients to make sure they've received the correct Txs
	txSha := types.DeriveSha(block.Transactions())
	if txSha != header.TxHash {
		err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
		return
	}

	// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
	receiptSha := types.DeriveSha(receipts)
	if receiptSha != header.ReceiptHash {
		err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
		return
	}

	// Verify uncles
	if err = sm.VerifyUncles(state, block, parent); err != nil {
		return
	}
	// Accumulate static rewards; block reward, uncle's and uncle inclusion.
	AccumulateRewards(state, block)

	// Commit state objects/accounts to a temporary trie (does not save)
	// used to calculate the state root.
	state.Update()
	if header.Root != state.Root() {
		err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
		return
	}

	// Calculate the td for this block
	td = CalculateTD(block, parent)
	// Sync the current block's state to the database
	state.Sync()

	// Remove transactions from the pool
	sm.txpool.RemoveSet(block.Transactions())

	// This puts transactions in a extra db for rpc
	for i, tx := range block.Transactions() {
		putTx(sm.extraDb, tx, block, uint64(i))
	}

	return td, state.Logs(), nil
}

// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
	if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
		return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
	}

	expd := CalcDifficulty(block, parent)
	if expd.Cmp(block.Difficulty) != 0 {
		return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
	}

	// block.gasLimit - parent.gasLimit <= parent.gasLimit / GasLimitBoundDivisor
	a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
	a.Abs(a)
	b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
	if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
		return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
	}

	// Allow future blocks up to 10 seconds
	if int64(block.Time) > time.Now().Unix()+4 {
		return BlockFutureErr
	}

	if new(big.Int).Sub(block.Number, parent.Number).Cmp(big.NewInt(1)) != 0 {
		return BlockNumberErr
	}

	if block.Time <= parent.Time {
		return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time)
	}

	// Verify the nonce of the block. Return an error if it's not valid
	if !sm.Pow.Verify(types.NewBlockWithHeader(block)) {
		return ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
	}

	return nil
}

func AccumulateRewards(statedb *state.StateDB, block *types.Block) {
	reward := new(big.Int).Set(BlockReward)

	for _, uncle := range block.Uncles() {
		num := new(big.Int).Add(big.NewInt(8), uncle.Number)
		num.Sub(num, block.Number())

		r := new(big.Int)
		r.Mul(BlockReward, num)
		r.Div(r, big.NewInt(8))

		statedb.AddBalance(uncle.Coinbase, r)

		reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
	}

	// Get the account associated with the coinbase
	statedb.AddBalance(block.Header().Coinbase, reward)
}

func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
	ancestors := set.New()
	uncles := set.New()
	ancestorHeaders := make(map[common.Hash]*types.Header)
	for _, ancestor := range sm.bc.GetAncestors(block, 7) {
		ancestorHeaders[ancestor.Hash()] = ancestor.Header()
		ancestors.Add(ancestor.Hash())
		// Include ancestors uncles in the uncle set. Uncles must be unique.
		for _, uncle := range ancestor.Uncles() {
			uncles.Add(uncle.Hash())
		}
	}

	uncles.Add(block.Hash())
	for _, uncle := range block.Uncles() {
		if uncles.Has(uncle.Hash()) {
			// Error not unique
			return UncleError("Uncle not unique")
		}

		uncles.Add(uncle.Hash())

		if ancestors.Has(uncle.Hash()) {
			return UncleError("Uncle is ancestor")
		}

		if !ancestors.Has(uncle.ParentHash) {
			return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
		}

		if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash]); err != nil {
			return ValidationError(fmt.Sprintf("%v", err))
		}

	}

	return nil
}

func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
	if !sm.bc.HasBlock(block.Header().ParentHash) {
		return nil, ParentError(block.Header().ParentHash)
	}

	sm.lastAttemptedBlock = block

	var (
		parent = sm.bc.GetBlock(block.Header().ParentHash)
		state  = state.New(parent.Root(), sm.db)
	)

	sm.TransitionState(state, parent, block, true)

	return state.Logs(), nil
}

func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint64) {
	rlpEnc, err := rlp.EncodeToBytes(tx)
	if err != nil {
		glog.V(logger.Debug).Infoln("Failed encoding tx", err)
		return
	}
	db.Put(tx.Hash().Bytes(), rlpEnc)

	var txExtra struct {
		BlockHash  common.Hash
		BlockIndex uint64
		Index      uint64
	}
	txExtra.BlockHash = block.Hash()
	txExtra.BlockIndex = block.NumberU64()
	txExtra.Index = i
	rlpMeta, err := rlp.EncodeToBytes(txExtra)
	if err != nil {
		glog.V(logger.Debug).Infoln("Failed encoding tx meta data", err)
		return
	}
	db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
}
<commit_msg>core: improved uncle validation error message<commit_after>package core

import (
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/params"
	"github.com/ethereum/go-ethereum/pow"
	"github.com/ethereum/go-ethereum/rlp"
	"gopkg.in/fatih/set.v0"
)

const (
	// must be bumped when consensus algorithm is changed, this forces the upgradedb
	// command to be run (forces the blocks to be imported again using the new algorithm)
	BlockChainVersion = 1
)

var statelogger = logger.NewLogger("BLOCK")

type BlockProcessor struct {
	db      common.Database
	extraDb common.Database
	// Mutex for locking the block processor. Blocks can only be handled one at a time
	mutex sync.Mutex
	// Canonical block chain
	bc *ChainManager
	// non-persistent key/value memory storage
	mem map[string]*big.Int
	// Proof of work used for validating
	Pow pow.PoW

	txpool *TxPool

	// The last attempted block is mainly used for debugging purposes
	// This does not have to be a valid block and will be set during
	// 'Process' & canonical validation.
	lastAttemptedBlock *types.Block

	events event.Subscription

	eventMux *event.TypeMux
}

func NewBlockProcessor(db, extra common.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
	sm := &BlockProcessor{
		db:       db,
		extraDb:  extra,
		mem:      make(map[string]*big.Int),
		Pow:      pow,
		bc:       chainManager,
		eventMux: eventMux,
		txpool:   txpool,
	}

	return sm
}

func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
	coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
	coinbase.SetGasPool(block.Header().GasLimit)

	// Process the transactions on to parent state
	receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
	if err != nil {
		return nil, err
	}

	return receipts, nil
}

func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
	// If we are mining this block and validating we want to set the logs back to 0
	//statedb.EmptyLogs()

	cb := statedb.GetStateObject(coinbase.Address())
	_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb)
	if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
		// If the account is managed, remove the invalid nonce.
		from, _ := tx.From()
		self.bc.TxState().RemoveNonce(from, tx.Nonce())
		return nil, nil, err
	}

	// Update the state with pending changes
	statedb.Update()

	cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas))
	receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative)

	logs := statedb.GetLogs(tx.Hash())
	receipt.SetLogs(logs)
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

	glog.V(logger.Debug).Infoln(receipt)

	// Notify all subscribers
	if !transientProcess {
		go self.eventMux.Post(TxPostEvent{tx})
		go self.eventMux.Post(logs)
	}

	return receipt, gas, err
}
func (self *BlockProcessor) ChainManager() *ChainManager {
	return self.bc
}

func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
	var (
		receipts      types.Receipts
		totalUsedGas  = big.NewInt(0)
		err           error
		cumulativeSum = new(big.Int)
	)

	for i, tx := range txs {
		statedb.StartRecord(tx.Hash(), block.Hash(), i)

		receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
		if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
			return nil, err
		}

		if err != nil {
			glog.V(logger.Core).Infoln("TX err:", err)
		}
		receipts = append(receipts, receipt)

		cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
	}

	if block.GasUsed().Cmp(totalUsedGas) != 0 {
		return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas))
	}

	if transientProcess {
		go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()})
	}

	return receipts, err
}

// Process block will attempt to process the given block's transactions and applies them
// on top of the block's parent state (given it exists) and will return wether it was
// successful or not.
func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, logs state.Logs, err error) {
	// Processing a blocks may never happen simultaneously
	sm.mutex.Lock()
	defer sm.mutex.Unlock()

	header := block.Header()
	if sm.bc.HasBlock(header.Hash()) {
		return nil, nil, &KnownBlockError{header.Number, header.Hash()}
	}

	if !sm.bc.HasBlock(header.ParentHash) {
		return nil, nil, ParentError(header.ParentHash)
	}
	parent := sm.bc.GetBlock(header.ParentHash)

	return sm.processWithParent(block, parent)
}

func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big.Int, logs state.Logs, err error) {
	sm.lastAttemptedBlock = block

	// Create a new state based on the parent's root (e.g., create copy)
	state := state.New(parent.Root(), sm.db)

	// Block validation
	if err = sm.ValidateHeader(block.Header(), parent.Header()); err != nil {
		return
	}

	// There can be at most two uncles
	if len(block.Uncles()) > 2 {
		return nil, nil, ValidationError("Block can only contain one uncle (contained %v)", len(block.Uncles()))
	}

	receipts, err := sm.TransitionState(state, parent, block, false)
	if err != nil {
		return
	}

	header := block.Header()

	// Validate the received block's bloom with the one derived from the generated receipts.
	// For valid blocks this should always validate to true.
	rbloom := types.CreateBloom(receipts)
	if rbloom != header.Bloom {
		err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
		return
	}

	// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
	// can be used by light clients to make sure they've received the correct Txs
	txSha := types.DeriveSha(block.Transactions())
	if txSha != header.TxHash {
		err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
		return
	}

	// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
	receiptSha := types.DeriveSha(receipts)
	if receiptSha != header.ReceiptHash {
		err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
		return
	}

	// Verify uncles
	if err = sm.VerifyUncles(state, block, parent); err != nil {
		return
	}
	// Accumulate static rewards; block reward, uncle's and uncle inclusion.
	AccumulateRewards(state, block)

	// Commit state objects/accounts to a temporary trie (does not save)
	// used to calculate the state root.
	state.Update()
	if header.Root != state.Root() {
		err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
		return
	}

	// Calculate the td for this block
	td = CalculateTD(block, parent)
	// Sync the current block's state to the database
	state.Sync()

	// Remove transactions from the pool
	sm.txpool.RemoveSet(block.Transactions())

	// This puts transactions in a extra db for rpc
	for i, tx := range block.Transactions() {
		putTx(sm.extraDb, tx, block, uint64(i))
	}

	return td, state.Logs(), nil
}

// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
	if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
		return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
	}

	expd := CalcDifficulty(block, parent)
	if expd.Cmp(block.Difficulty) != 0 {
		return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
	}

	// block.gasLimit - parent.gasLimit <= parent.gasLimit / GasLimitBoundDivisor
	a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
	a.Abs(a)
	b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
	if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
		return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
	}

	// Allow future blocks up to 10 seconds
	if int64(block.Time) > time.Now().Unix()+4 {
		return BlockFutureErr
	}

	if new(big.Int).Sub(block.Number, parent.Number).Cmp(big.NewInt(1)) != 0 {
		return BlockNumberErr
	}

	if block.Time <= parent.Time {
		return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time)
	}

	// Verify the nonce of the block. Return an error if it's not valid
	if !sm.Pow.Verify(types.NewBlockWithHeader(block)) {
		return ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
	}

	return nil
}

func AccumulateRewards(statedb *state.StateDB, block *types.Block) {
	reward := new(big.Int).Set(BlockReward)

	for _, uncle := range block.Uncles() {
		num := new(big.Int).Add(big.NewInt(8), uncle.Number)
		num.Sub(num, block.Number())

		r := new(big.Int)
		r.Mul(BlockReward, num)
		r.Div(r, big.NewInt(8))

		statedb.AddBalance(uncle.Coinbase, r)

		reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
	}

	// Get the account associated with the coinbase
	statedb.AddBalance(block.Header().Coinbase, reward)
}

func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
	ancestors := set.New()
	uncles := set.New()
	ancestorHeaders := make(map[common.Hash]*types.Header)
	for _, ancestor := range sm.bc.GetAncestors(block, 7) {
		ancestorHeaders[ancestor.Hash()] = ancestor.Header()
		ancestors.Add(ancestor.Hash())
		// Include ancestors uncles in the uncle set. Uncles must be unique.
		for _, uncle := range ancestor.Uncles() {
			uncles.Add(uncle.Hash())
		}
	}

	uncles.Add(block.Hash())
	for i, uncle := range block.Uncles() {
		if uncles.Has(uncle.Hash()) {
			// Error not unique
			return UncleError("Uncle not unique")
		}

		uncles.Add(uncle.Hash())

		if ancestors.Has(uncle.Hash()) {
			return UncleError("Uncle is ancestor")
		}

		if !ancestors.Has(uncle.ParentHash) {
			return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
		}

		if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash]); err != nil {
			return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, uncle.Hash().Bytes()[:4], err))
		}
	}

	return nil
}

func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
	if !sm.bc.HasBlock(block.Header().ParentHash) {
		return nil, ParentError(block.Header().ParentHash)
	}

	sm.lastAttemptedBlock = block

	var (
		parent = sm.bc.GetBlock(block.Header().ParentHash)
		state  = state.New(parent.Root(), sm.db)
	)

	sm.TransitionState(state, parent, block, true)

	return state.Logs(), nil
}

func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint64) {
	rlpEnc, err := rlp.EncodeToBytes(tx)
	if err != nil {
		glog.V(logger.Debug).Infoln("Failed encoding tx", err)
		return
	}
	db.Put(tx.Hash().Bytes(), rlpEnc)

	var txExtra struct {
		BlockHash  common.Hash
		BlockIndex uint64
		Index      uint64
	}
	txExtra.BlockHash = block.Hash()
	txExtra.BlockIndex = block.NumberU64()
	txExtra.Index = i
	rlpMeta, err := rlp.EncodeToBytes(txExtra)
	if err != nil {
		glog.V(logger.Debug).Infoln("Failed encoding tx meta data", err)
		return
	}
	db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
}
<|endoftext|>