From e52953efdde9007fb2d553ca4d03c51c798badb3 Mon Sep 17 00:00:00 2001 From: tnasu Date: Fri, 28 Jan 2022 11:45:02 +0900 Subject: [PATCH] e2e: allow running of single node using the e2e app (backport) (#7024) Co-authored-by: Callum Waters --- cmd/ostracon/commands/run_node.go | 4 +-- proxy/client.go | 7 +++++ rpc/openapi/openapi.yaml | 2 +- test/e2e/Makefile | 6 ++-- test/e2e/README.md | 34 ++++++++++++++++++++- test/e2e/app/app.go | 51 ++++++++++++++++++++++++++++++- test/e2e/app/snapshots.go | 2 +- test/e2e/app/state.go | 2 +- test/e2e/docker/Dockerfile | 2 +- test/e2e/node/built-in.toml | 4 +++ test/e2e/{app => node}/config.go | 15 +++++++++ test/e2e/{app => node}/main.go | 11 ++++--- test/e2e/node/socket.toml | 5 +++ 13 files changed, 128 insertions(+), 17 deletions(-) create mode 100644 test/e2e/node/built-in.toml rename test/e2e/{app => node}/config.go (78%) rename test/e2e/{app => node}/main.go (97%) create mode 100644 test/e2e/node/socket.toml diff --git a/cmd/ostracon/commands/run_node.go b/cmd/ostracon/commands/run_node.go index 056a69694..1dbf30cc1 100644 --- a/cmd/ostracon/commands/run_node.go +++ b/cmd/ostracon/commands/run_node.go @@ -46,9 +46,7 @@ func AddNodeFlags(cmd *cobra.Command) { "proxy_app", config.ProxyApp, "proxy app address, or one of: 'kvstore',"+ - " 'persistent_kvstore',"+ - " 'counter',"+ - " 'counter_serial' or 'noop' for local testing.") + " 'persistent_kvstore', 'counter', 'e2e' or 'noop' for local testing.") cmd.Flags().String("abci", config.ABCI, "specify abci transport (socket | grpc)") // rpc flags diff --git a/proxy/client.go b/proxy/client.go index 3ee4335e5..2a646b420 100644 --- a/proxy/client.go +++ b/proxy/client.go @@ -8,6 +8,7 @@ import ( "github.com/line/ostracon/abci/example/kvstore" "github.com/line/ostracon/abci/types" tmsync "github.com/line/ostracon/libs/sync" + e2e "github.com/line/ostracon/test/e2e/app" ) // ClientCreator creates new ABCI clients. @@ -79,6 +80,12 @@ func DefaultClientCreator(addr, transport, dbDir string) ClientCreator { return NewLocalClientCreator(kvstore.NewApplication()) case "persistent_kvstore": return NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(dbDir)) + case "e2e": + app, err := e2e.NewApplication(e2e.DefaultConfig(dbDir)) + if err != nil { + panic(err) + } + return NewLocalClientCreator(app) case "noop": return NewLocalClientCreator(types.NewBaseApplication()) default: diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index b6f6451c0..6d2a3bccb 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -335,7 +335,7 @@ paths: https://godoc.org/github.com/line/ostracon/libs/pubsub/query. ```go - import rpchttp "github.com/tendermint/rpc/client/http" + import rpchttp "github.com/line/ostracon/rpc/client/http" import "github.com/line/ostracon/types" client := rpchttp.New("tcp:0.0.0.0:26657", "/websocket") diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 21491c018..42f484fd8 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -6,8 +6,8 @@ docker: # We need to build support for database backends into the app in # order to build a binary with an Ostracon node in it (for built-in # ABCI testing). -app: - go build -o build/app -tags libsodium,badgerdb,boltdb,cleveldb,rocksdb ./app +node: + go build -o build/node -tags libsodium,badgerdb,boltdb,cleveldb,rocksdb ./node # To be used primarily by the e2e docker instance. If you want to produce this binary # elsewhere, then run go build in the maverick directory. @@ -20,4 +20,4 @@ generator: runner: go build -o build/runner ./runner -.PHONY: all app docker generator maverick runner +.PHONY: all node docker generator maverick runner diff --git a/test/e2e/README.md b/test/e2e/README.md index c98549e4d..b0d0f52a5 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -122,10 +122,42 @@ Docker does not enable IPv6 by default. To do so, enter the following in } ``` -## Benchmarking testnets +## Benchmarking Testnets It is also possible to run a simple benchmark on a testnet. This is done through the `benchmark` command. This manages the entire process: setting up the environment, starting the test net, waiting for a considerable amount of blocks to be used (currently 100), and then returning the following metrics from the sample of the blockchain: - Average time to produce a block - Standard deviation of producing a block - Minimum and maximum time to produce a block + +## Running Individual Nodes + +The E2E test harness is designed to run several nodes of varying configurations within docker. It is also possible to run a single node in the case of running larger, geographically-dispersed testnets. To run a single node you can either run: + +**Built-in** + +```bash +make node +ostracon init validator +OCHOME=$HOME/.ostracon ./build/node ./node/built-in.toml +``` + +To make things simpler the e2e application can also be run in the ostracon binary +by running + +```bash +ostracon start --proxy-app e2e +``` + +However this won't offer the same level of configurability of the application. + +**Socket** + +```bash +make node +ostracon init validator +ostracon start +./build/node ./node.socket.toml +``` + +Check `node/config.go` to see how the settings of the test application can be tweaked. diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index 107eecf0f..732dece4d 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -1,4 +1,4 @@ -package main +package app import ( "bytes" @@ -31,6 +31,55 @@ type Application struct { restoreChunks [][]byte } +// Config allows for the setting of high level parameters for running the e2e Application +// KeyType and ValidatorUpdates must be the same for all nodes running the same application. +type Config struct { + // The directory with which state.json will be persisted in. Usually $HOME/.ostracon/data + Dir string `toml:"dir"` + + // SnapshotInterval specifies the height interval at which the application + // will take state sync snapshots. Defaults to 0 (disabled). + SnapshotInterval uint64 `toml:"snapshot_interval"` + + // RetainBlocks specifies the number of recent blocks to retain. Defaults to + // 0, which retains all blocks. Must be greater that PersistInterval, + // SnapshotInterval and EvidenceAgeHeight. + RetainBlocks uint64 `toml:"retain_blocks"` + + // KeyType sets the curve that will be used by validators. + // Options are ed25519 & secp256k1 + KeyType string `toml:"key_type"` + + // PersistInterval specifies the height interval at which the application + // will persist state to disk. Defaults to 1 (every height), setting this to + // 0 disables state persistence. + PersistInterval uint64 `toml:"persist_interval"` + + // ValidatorUpdates is a map of heights to validator names and their power, + // and will be returned by the ABCI application. For example, the following + // changes the power of validator01 and validator02 at height 1000: + // + // [validator_update.1000] + // validator01 = 20 + // validator02 = 10 + // + // Specifying height 0 returns the validator update during InitChain. The + // application returns the validator updates as-is, i.e. removing a + // validator must be done by returning it with power 0, and any validators + // not specified are not changed. + // + // height <-> pubkey <-> voting power + ValidatorUpdates map[string]map[string]uint8 `toml:"validator_update"` +} + +func DefaultConfig(dir string) *Config { + return &Config{ + PersistInterval: 1, + SnapshotInterval: 100, + Dir: dir, + } +} + // NewApplication creates the application. func NewApplication(cfg *Config) (*Application, error) { state, err := NewState(filepath.Join(cfg.Dir, "state.json"), cfg.PersistInterval) diff --git a/test/e2e/app/snapshots.go b/test/e2e/app/snapshots.go index 28c53d730..4ef397866 100644 --- a/test/e2e/app/snapshots.go +++ b/test/e2e/app/snapshots.go @@ -1,5 +1,5 @@ // nolint: gosec -package main +package app import ( "encoding/json" diff --git a/test/e2e/app/state.go b/test/e2e/app/state.go index ad9960105..33a124caa 100644 --- a/test/e2e/app/state.go +++ b/test/e2e/app/state.go @@ -1,5 +1,5 @@ //nolint: gosec -package main +package app import ( "crypto/sha256" diff --git a/test/e2e/docker/Dockerfile b/test/e2e/docker/Dockerfile index c4d05d18c..793897a04 100644 --- a/test/e2e/docker/Dockerfile +++ b/test/e2e/docker/Dockerfile @@ -28,7 +28,7 @@ COPY . . RUN make build && cp build/ostracon /usr/bin/ostracon COPY test/e2e/docker/entrypoint* /usr/bin/ RUN cd test/e2e && make maverick && cp build/maverick /usr/bin/maverick -RUN cd test/e2e && make app && cp build/app /usr/bin/app +RUN cd test/e2e && make node && cp build/node /usr/bin/app # Set up runtime directory. We don't use a separate runtime image since we need # e.g. leveldb and rocksdb which are already installed in the build image. diff --git a/test/e2e/node/built-in.toml b/test/e2e/node/built-in.toml new file mode 100644 index 000000000..0a2146a58 --- /dev/null +++ b/test/e2e/node/built-in.toml @@ -0,0 +1,4 @@ +snapshot_interval = 100 +persist_interval = 1 +chain_id = "test-chain" +protocol = "builtin" diff --git a/test/e2e/app/config.go b/test/e2e/node/config.go similarity index 78% rename from test/e2e/app/config.go rename to test/e2e/node/config.go index c8c982241..dfa572dcf 100644 --- a/test/e2e/app/config.go +++ b/test/e2e/node/config.go @@ -6,6 +6,8 @@ import ( "fmt" "github.com/BurntSushi/toml" + + "github.com/line/ostracon/test/e2e/app" ) // Config is the application configuration. @@ -23,6 +25,19 @@ type Config struct { PrivValKey string `toml:"privval_key"` PrivValState string `toml:"privval_state"` Misbehaviors map[string]string `toml:"misbehaviors"` + KeyType string `toml:"key_type"` +} + +// App extracts out the application specific configuration parameters +func (cfg *Config) App() *app.Config { + return &app.Config{ + Dir: cfg.Dir, + SnapshotInterval: cfg.SnapshotInterval, + RetainBlocks: cfg.RetainBlocks, + KeyType: cfg.KeyType, + ValidatorUpdates: cfg.ValidatorUpdates, + PersistInterval: cfg.PersistInterval, + } } // LoadConfig loads the configuration from disk. diff --git a/test/e2e/app/main.go b/test/e2e/node/main.go similarity index 97% rename from test/e2e/app/main.go rename to test/e2e/node/main.go index 33f7679af..3d31bc72f 100644 --- a/test/e2e/app/main.go +++ b/test/e2e/node/main.go @@ -30,6 +30,7 @@ import ( "github.com/line/ostracon/privval" "github.com/line/ostracon/proxy" rpcserver "github.com/line/ostracon/rpc/jsonrpc/server" + "github.com/line/ostracon/test/e2e/app" e2e "github.com/line/ostracon/test/e2e/pkg" mcs "github.com/line/ostracon/test/maverick/consensus" maverick "github.com/line/ostracon/test/maverick/node" @@ -100,7 +101,7 @@ func run(configFile string) error { // startApp starts the application server, listening for connections from Ostracon. func startApp(cfg *Config) error { - app, err := NewApplication(cfg) + app, err := app.NewApplication(cfg.App()) if err != nil { return err } @@ -121,7 +122,7 @@ func startApp(cfg *Config) error { // // FIXME There is no way to simply load the configuration from a file, so we need to pull in Viper. func startNode(cfg *Config) error { - app, err := NewApplication(cfg) + app, err := app.NewApplication(cfg.App()) if err != nil { return err } @@ -213,10 +214,10 @@ func startLightClient(cfg *Config) error { } // FIXME: Temporarily disconnected maverick until it is redesigned -// startMaverick starts a Maverick node that runs the application directly. It assumes the Tendermint -// configuration is in $TMHOME/config/tendermint.toml. +// startMaverick starts a Maverick node that runs the application directly. It assumes the Ostracon +// configuration is in $OCHOME/config/ostracon.toml. func startMaverick(cfg *Config) error { - app, err := NewApplication(cfg) + app, err := app.NewApplication(cfg.App()) if err != nil { return err } diff --git a/test/e2e/node/socket.toml b/test/e2e/node/socket.toml new file mode 100644 index 000000000..2f7913e62 --- /dev/null +++ b/test/e2e/node/socket.toml @@ -0,0 +1,5 @@ +snapshot_interval = 100 +persist_interval = 1 +chain_id = "test-chain" +protocol = "socket" +listen = "tcp://127.0.0.1:26658"