diff --git a/charts/const.go b/charts/const.go
new file mode 100644
index 00000000..fd8c4f32
--- /dev/null
+++ b/charts/const.go
@@ -0,0 +1,30 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package charts
+
+import (
+ "os"
+ "path/filepath"
+)
+
+var (
+ chartBasePath string
+)
+
+func init() {
+ chartBasePath = filepath.Join(os.Getenv("CURDIR"), "benchmark/kubernetes")
+}
diff --git a/charts/genesis.go b/charts/genesis.go
new file mode 100644
index 00000000..2abb5e5d
--- /dev/null
+++ b/charts/genesis.go
@@ -0,0 +1,80 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package charts
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+
+ "github.com/getamis/istanbul-tools/genesis"
+)
+
+type GenesisChart struct {
+ name string
+ chartPath string
+ genesisFile string
+ args []string
+}
+
+func NewGenesisChart(addrs []common.Address, gasLimit uint64) *GenesisChart {
+ chartPath := filepath.Join(chartBasePath, "genesis-block")
+ genesisPath := filepath.Join(chartPath, ".genesis")
+ err := os.MkdirAll(genesisPath, 0700)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ chart := &GenesisChart{
+ name: "genesis-block",
+ chartPath: chartPath,
+ genesisFile: genesis.NewFileAt(
+ genesisPath,
+ false,
+ genesis.Validators(addrs...),
+ genesis.GasLimit(gasLimit),
+ ),
+ }
+
+ relPath := strings.Replace(chart.genesisFile, chartPath+"/", "", 1)
+ chart.Override("genesisFileName", relPath)
+
+ return chart
+}
+
+func (chart *GenesisChart) Override(key, value string) {
+ chart.args = append(chart.args, fmt.Sprintf("%s=%s", key, value))
+}
+
+func (chart *GenesisChart) Install(debug bool) error {
+ defer os.RemoveAll(filepath.Dir(chart.genesisFile))
+
+ return installRelease(
+ chart.name,
+ chart.args,
+ chart.chartPath,
+ debug,
+ )
+}
+
+func (chart *GenesisChart) Uninstall() error {
+ return uninstallRelease(chart.name)
+}
diff --git a/charts/static_nodes.go b/charts/static_nodes.go
new file mode 100644
index 00000000..fcd4c09a
--- /dev/null
+++ b/charts/static_nodes.go
@@ -0,0 +1,78 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package charts
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/getamis/istanbul-tools/common"
+)
+
+type StaticNodesChart struct {
+ name string
+ chartPath string
+ staticNodesFile string
+ args []string
+}
+
+func NewStaticNodesChart(nodekeys []string, ipAddrs []string) *StaticNodesChart {
+ chartPath := filepath.Join(chartBasePath, "static-nodes")
+ staticNodesPath := filepath.Join(chartPath, ".static-nodes")
+ err := os.MkdirAll(staticNodesPath, 0700)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ if len(nodekeys) != len(ipAddrs) {
+ log.Println("The number of nodekeys and the number of IP address should be equal")
+ return nil
+ }
+
+ chart := &StaticNodesChart{
+ name: "static-nodes",
+ chartPath: chartPath,
+ staticNodesFile: common.GenerateStaticNodesAt(staticNodesPath, nodekeys, ipAddrs),
+ }
+
+ relPath := strings.Replace(chart.staticNodesFile, chartPath+"/", "", 1)
+ chart.Override("fileName", relPath)
+
+ return chart
+}
+
+func (chart *StaticNodesChart) Override(key, value string) {
+ chart.args = append(chart.args, fmt.Sprintf("%s=%s", key, value))
+}
+
+func (chart *StaticNodesChart) Install(debug bool) error {
+ defer os.RemoveAll(filepath.Dir(chart.staticNodesFile))
+
+ return installRelease(
+ chart.name,
+ chart.args,
+ chart.chartPath,
+ debug,
+ )
+}
+
+func (chart *StaticNodesChart) Uninstall() error {
+ return uninstallRelease(chart.name)
+}
diff --git a/charts/utils.go b/charts/utils.go
new file mode 100644
index 00000000..3ca59063
--- /dev/null
+++ b/charts/utils.go
@@ -0,0 +1,98 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package charts
+
+import (
+ "errors"
+ "fmt"
+ "os/exec"
+ "strings"
+)
+
+func newInstallCommand() *exec.Cmd {
+ return exec.Command("helm", "install")
+}
+
+func newUninstallCommand() *exec.Cmd {
+ return exec.Command("helm", "delete", "--purge")
+}
+
+func installRelease(name string, args []string, path string, debug bool) error {
+ cmd := newInstallCommand()
+
+ if name != "" {
+ cmd.Args = append(cmd.Args, "--name")
+ cmd.Args = append(cmd.Args, name)
+ }
+
+ if len(args) > 0 {
+ cmd.Args = append(cmd.Args, "--set")
+ cmd.Args = append(cmd.Args, strings.Join(args, ","))
+ }
+
+ cmd.Args = append(cmd.Args, path)
+
+ if debug {
+ cmd.Args = append(cmd.Args, "--dry-run")
+ cmd.Args = append(cmd.Args, "--debug")
+ }
+
+ cmd.Args = append(cmd.Args, "--wait")
+
+ if debug {
+ fmt.Println(cmd.Args)
+ }
+
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ fmt.Println(err, string(output))
+ return err
+ }
+
+ fmt.Println(string(output))
+ return nil
+}
+
+func uninstallRelease(release string) error {
+ cmd := newUninstallCommand()
+
+ if release != "" {
+ cmd.Args = append(cmd.Args, release)
+ } else {
+ return errors.New("Unknown release name")
+ }
+
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ fmt.Println(err)
+ return err
+ }
+
+ fmt.Println(string(output))
+ return nil
+}
+
+func ListCharts() {
+ cmd := exec.Command("helm", "list")
+ output, err := cmd.Output()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ fmt.Println(string(output))
+}
diff --git a/charts/validator.go b/charts/validator.go
new file mode 100644
index 00000000..974a3ae0
--- /dev/null
+++ b/charts/validator.go
@@ -0,0 +1,61 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package charts
+
+import (
+ "fmt"
+ "path/filepath"
+)
+
+type ValidatorChart struct {
+ name string
+ chartPath string
+ args []string
+}
+
+func NewValidatorChart(name string, args []string) *ValidatorChart {
+ chartPath := filepath.Join(chartBasePath, "validator")
+
+ chart := &ValidatorChart{
+ name: "validator-" + name,
+ args: args,
+ chartPath: chartPath,
+ }
+
+ return chart
+}
+
+func (chart *ValidatorChart) Override(key, value string) {
+ chart.args = append(chart.args, fmt.Sprintf("%s=%s", key, value))
+}
+
+func (chart *ValidatorChart) Install(debug bool) error {
+ return installRelease(
+ chart.name,
+ chart.args,
+ chart.chartPath,
+ debug,
+ )
+}
+
+func (chart *ValidatorChart) Uninstall() error {
+ return uninstallRelease(chart.name)
+}
+
+func (chart *ValidatorChart) Name() string {
+ return chart.name
+}
diff --git a/common/utils.go b/common/utils.go
index 2f5c200d..6a31a8fb 100644
--- a/common/utils.go
+++ b/common/utils.go
@@ -19,13 +19,17 @@ package common
import (
"crypto/ecdsa"
"crypto/rand"
+ "encoding/json"
"fmt"
+ "io/ioutil"
"log"
+ "net"
"os"
"path/filepath"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/getamis/go-ethereum/p2p/discover"
uuid "github.com/satori/go.uuid"
)
@@ -35,6 +39,14 @@ const (
nodekeyFileName = "nodekey"
)
+func GenerateIPs(num int) (ips []string) {
+ for i := 0; i < num; i++ {
+ ips = append(ips, fmt.Sprintf("10.1.1.%d", i+2))
+ }
+
+ return ips
+}
+
func GenerateRandomDir() (string, error) {
err := os.MkdirAll(filepath.Join(defaultLocalDir), 0700)
if err != nil {
@@ -83,6 +95,44 @@ func SaveNodeKey(key *ecdsa.PrivateKey, dataDir string) error {
return nil
}
+func GenerateStaticNodesAt(dir string, nodekeys []string, ipAddrs []string) (filename string) {
+ var nodes []string
+
+ for i, nodekey := range nodekeys {
+ key, err := crypto.HexToECDSA(nodekey)
+ if err != nil {
+ log.Printf("Failed to create key, err: %v\n", err)
+ return ""
+ }
+ node := discover.NewNode(
+ discover.PubkeyID(&key.PublicKey),
+ net.ParseIP(ipAddrs[i]),
+ 0,
+ uint16(30303))
+
+ nodes = append(nodes, node.String())
+ }
+
+ filename = filepath.Join(dir, "static-nodes.json")
+ bytes, _ := json.Marshal(nodes)
+ if err := ioutil.WriteFile(filename, bytes, 0644); err != nil {
+ log.Printf("Failed to write '%s', err: %v\n", filename, err)
+ return ""
+ }
+
+ return filename
+}
+
+func GenerateStaticNodes(nodekeys []string, ipAddrs []string) (filename string) {
+ dir, err := GenerateRandomDir()
+ if err != nil {
+ log.Printf("Failed to generate directory, err: %v\n", err)
+ return ""
+ }
+
+ return GenerateStaticNodesAt(dir, nodekeys, ipAddrs)
+}
+
func RandomHex() string {
b, _ := RandomBytes(32)
return common.BytesToHash(b).Hex()
diff --git a/tests/chart_installer.go b/tests/chart_installer.go
new file mode 100644
index 00000000..7fc222a6
--- /dev/null
+++ b/tests/chart_installer.go
@@ -0,0 +1,22 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package tests
+
+type ChartInstaller interface {
+ Install(bool) error
+ Uninstall() error
+}
diff --git a/tests/load/load_test.go b/tests/load/load_test.go
new file mode 100644
index 00000000..4399e629
--- /dev/null
+++ b/tests/load/load_test.go
@@ -0,0 +1,85 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package load
+
+import (
+ "testing"
+
+ "github.com/getamis/istanbul-tools/charts"
+ "github.com/getamis/istanbul-tools/common"
+
+ "github.com/getamis/istanbul-tools/tests"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("TPS-01: Large amount of transactions", func() {
+ tests.CaseTable("with number of validators",
+ func(numberOfValidators int) {
+ tests.CaseTable("with gas limit",
+ func(gaslimit int) {
+ tests.CaseTable("with txpool size",
+ func(txpoolSize int) {
+ runTests(numberOfValidators, gaslimit, txpoolSize)
+ },
+
+ tests.Case("2048", 2048),
+ tests.Case("10240", 10240),
+ )
+ },
+
+ tests.Case("21000*1000", 21000*1000),
+ tests.Case("21000*3000", 21000*3000),
+ )
+ },
+
+ tests.Case("4 validators", 4),
+ tests.Case("7 validators", 7),
+ tests.Case("10 validators", 10),
+ )
+})
+
+func runTests(numberOfValidators int, gaslimit int, txpoolSize int) {
+ Describe("", func() {
+ var (
+ genesisChart tests.ChartInstaller
+ staticNodesChart tests.ChartInstaller
+ )
+
+ BeforeEach(func() {
+ _, nodekeys, addrs := common.GenerateKeys(numberOfValidators)
+ genesisChart = charts.NewGenesisChart(addrs, uint64(gaslimit))
+ Expect(genesisChart.Install(false)).To(BeNil())
+
+ staticNodesChart = charts.NewStaticNodesChart(nodekeys, common.GenerateIPs(len(nodekeys)))
+ Expect(staticNodesChart.Install(false)).To(BeNil())
+ })
+
+ AfterEach(func() {
+ Expect(genesisChart.Uninstall()).To(BeNil())
+ Expect(staticNodesChart.Uninstall()).To(BeNil())
+ })
+
+ It("", func() {
+ })
+ })
+}
+
+func IstanbulLoadTest(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Istanbul Load Test Suite")
+}
diff --git a/tests/table.go b/tests/table.go
new file mode 100644
index 00000000..f1802a17
--- /dev/null
+++ b/tests/table.go
@@ -0,0 +1,80 @@
+// Copyright 2017 AMIS Technologies
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package tests
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/onsi/ginkgo"
+)
+
+func CaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
+ caseTable(description, itBody, entries, false, false)
+ return true
+}
+
+/*
+You can focus a table with `FCaseTable`.
+*/
+func FCaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
+ caseTable(description, itBody, entries, false, true)
+ return true
+}
+
+/*
+You can mark a table as pending with `PCaseTable`.
+*/
+func PCaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
+ caseTable(description, itBody, entries, true, false)
+ return true
+}
+
+/*
+You can mark a table as pending with `XCaseTable`.
+*/
+func XCaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
+ caseTable(description, itBody, entries, true, false)
+ return true
+}
+
+func caseTable(description string, itBody interface{}, entries []tableEntry, pending bool, focused bool) {
+ itBodyValue := reflect.ValueOf(itBody)
+ if itBodyValue.Kind() != reflect.Func {
+ panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody))
+ }
+
+ if pending {
+ ginkgo.PDescribe(description, func() {
+ for _, entry := range entries {
+ entry.generate(itBodyValue, entries, pending, focused)
+ }
+ })
+ } else if focused {
+ ginkgo.FDescribe(description, func() {
+ for _, entry := range entries {
+ entry.generate(itBodyValue, entries, pending, focused)
+ }
+ })
+ } else {
+ ginkgo.Describe(description, func() {
+ for _, entry := range entries {
+ entry.generate(itBodyValue, entries, pending, focused)
+ }
+ })
+ }
+}
diff --git a/tests/table_entry.go b/tests/table_entry.go
new file mode 100644
index 00000000..09e9c070
--- /dev/null
+++ b/tests/table_entry.go
@@ -0,0 +1,85 @@
+package tests
+
+import (
+ "reflect"
+
+ "github.com/onsi/ginkgo"
+)
+
+/*
+TableEntry represents an entry in a table test. You generally use the `Entry` constructor.
+*/
+type tableEntry struct {
+ Description string
+ Parameters []interface{}
+ Pending bool
+ Focused bool
+}
+
+func (t tableEntry) generate(itBody reflect.Value, entries []tableEntry, pending bool, focused bool) {
+ if t.Pending {
+ ginkgo.PDescribe(t.Description, func() {
+ for _, entry := range entries {
+ entry.generate(itBody, entries, pending, focused)
+ }
+ })
+ return
+ }
+
+ values := []reflect.Value{}
+ for i, param := range t.Parameters {
+ var value reflect.Value
+
+ if param == nil {
+ inType := itBody.Type().In(i)
+ value = reflect.Zero(inType)
+ } else {
+ value = reflect.ValueOf(param)
+ }
+
+ values = append(values, value)
+ }
+
+ body := func() {
+ itBody.Call(values)
+ }
+
+ if t.Focused {
+ ginkgo.FDescribe(t.Description, body)
+ } else {
+ ginkgo.Describe(t.Description, body)
+ }
+}
+
+/*
+Entry constructs a tableEntry.
+
+The first argument is a required description (this becomes the content of the generated Ginkgo `It`).
+Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`.
+
+Each Entry ends up generating an individual Ginkgo It.
+*/
+func Case(description string, parameters ...interface{}) tableEntry {
+ return tableEntry{description, parameters, false, false}
+}
+
+/*
+You can focus a particular entry with FEntry. This is equivalent to FIt.
+*/
+func FCase(description string, parameters ...interface{}) tableEntry {
+ return tableEntry{description, parameters, false, true}
+}
+
+/*
+You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
+*/
+func PCase(description string, parameters ...interface{}) tableEntry {
+ return tableEntry{description, parameters, true, false}
+}
+
+/*
+You can mark a particular entry as pending with XEntry. This is equivalent to XIt.
+*/
+func XCase(description string, parameters ...interface{}) tableEntry {
+ return tableEntry{description, parameters, true, false}
+}