Skip to content

Commit

Permalink
Add tool and script for interop testing (#3417)
Browse files Browse the repository at this point in the history
* add tool and script for interop testing

* identity

* lint

* merge upstream, fix conflict, update script, add comment

* add comma separated support for --peer=

* remove NUM_VALIDATORS, comma fix

* WIP docker image

* use CI builder

* pr feedback

* whatever antoine says

* ignore git in docker

* jobs=auto

* disable remote cache

* try to cache the golang part

* try to cache the golang part

* nvm

* From Antoine with love

* fix
  • Loading branch information
prestonvanloon authored and rauljordan committed Sep 9, 2019
1 parent af07c13 commit 3708a8f
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 13 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bazel-*
.git
1 change: 1 addition & 0 deletions beacon-chain/node/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"//shared/featureconfig:go_default_library",
"//shared/params:go_default_library",
"//shared/prometheus:go_default_library",
"//shared/sliceutil:go_default_library",
"//shared/tracing:go_default_library",
"//shared/version:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
Expand Down
3 changes: 2 additions & 1 deletion beacon-chain/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/prometheus"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
"github.com/prysmaticlabs/prysm/shared/tracing"
"github.com/prysmaticlabs/prysm/shared/version"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -213,7 +214,7 @@ func (b *BeaconNode) registerP2P(ctx *cli.Context) error {

svc, err := p2p.NewService(&p2p.Config{
NoDiscovery: ctx.GlobalBool(cmd.NoDiscovery.Name),
StaticPeers: ctx.GlobalStringSlice(cmd.StaticPeers.Name),
StaticPeers: sliceutil.SplitCommaSeparated(ctx.GlobalStringSlice(cmd.StaticPeers.Name)),
BootstrapNodeAddr: bootnodeENR,
RelayNodeAddr: ctx.GlobalString(cmd.RelayNode.Name),
HostAddress: ctx.GlobalString(cmd.P2PHost.Name),
Expand Down
24 changes: 24 additions & 0 deletions interop.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM gcr.io/prysmaticlabs/build-agent AS builder

WORKDIR /workspace

COPY . /workspace/.

# Build binaries for minimal configuration.
RUN bazel build --define ssz=minimal --jobs=auto --remote_cache= \
//beacon-chain \
//validator \
//tools/interop/convert-keys


FROM gcr.io/whiteblock/base:ubuntu1804

COPY --from=builder /workspace/bazel-bin/beacon-chain/linux_amd64_stripped/beacon-chain .
COPY --from=builder /workspace/bazel-bin/validator/linux_amd64_pure_stripped/validator .
COPY --from=builder /workspace/bazel-bin/tools/interop/convert-keys/linux_amd64_stripped/convert-keys .

RUN mkdir /launch

COPY scripts/interop_start.sh /launch/start.sh

ENTRYPOINT ["start.sh"]
93 changes: 93 additions & 0 deletions scripts/interop_start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/bin/bash

"""
2019/09/08 -- Interop start script.
This script is intended for dockerfile deployment for interop testing.
This script is fragile and subject to break as flags change.
Use at your own risk!
Use with interop.Dockerfile from the workspace root:
docker build -f interop.Dockerfile .
"""

# Flags
IDENTITY="" # P2P private key
PEERS="" # Comma separated list of peers
GEN_STATE="" # filepath to ssz encoded state.
PORT="8000" # port to serve p2p traffic
YAML_KEY_FILE="" # Path to yaml keyfile as defined here: https://github.com/ethereum/eth2.0-pm/tree/master/interop/mocked_start

# Constants
BEACON_LOG_FILE="/tmp/beacon.log"
VALIDATOR_LOG_FILE="/tmp/validator.log"

usage() {
echo "--identity=<identity>"
echo "--peer=<peer>"
echo "--num-validators=<number>"
echo "--gen-state=<file path>"
port "--port=<port number>"
}

while [ "$1" != "" ];
do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | sed 's/^[^=]*=//g'`

case $PARAM in
--identity)
IDENTITY=$VALUE
;;
--peers)
PEERS+=",$VALUE"
;;
--validator-keys)
YAML_KEY_FILE=$VALUE
;;
--gen-state)
GEN_STATE=$VALUE
;;
--port)
PORT=$VALUE
;;
--help)
usage
exit
;;
*)
echo "ERROR: unknown parameter \"$PARAM\""
usage
exit 1
;;
esac
shift
done


echo "Converting hex yaml keys to a format that Prysm understands"

# Expect YAML keys in hex encoded format. Convert this into the format the the validator already understands.
./convert-keys $YAML_KEY_FILE /tmp/keys.json

echo "Starting beacon chain and logging to $BEACON_LOG_FILE"

BEACON_FLAGS="--bootstrap-node= \
--deposit-contract=0xD775140349E6A5D12524C6ccc3d6A1d4519D4029 \
--p2p-port=$PORT \
--peer=$PEERS \
--interop-genesis-state=$GEN_STATE \
--p2p-priv-key=$IDENTITY \
--log-file=$BEACON_LOG_FILE"

./beacon-chain $BEACON_FLAGS &

echo "Starting validator client and logging to $VALIDATOR_LOG_FILE"

VALIDATOR_FLAGS="--monitoring-port=9091 \
--unencrypted-keys /tmp/keys.json \
--log-file=$VALIDATOR_LOG_FILE
./validator- $VALIDATOR_FLAGS &
13 changes: 13 additions & 0 deletions shared/sliceutil/slice.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package sliceutil

import (
"strings"
)

// SubsetUint64 returns true if the first array is
// completely contained in the second array with time
// complexity of approximately o(n).
Expand Down Expand Up @@ -259,3 +263,12 @@ func IntersectionByteSlices(s ...[][]byte) [][]byte {
}
return inter
}

// SplitCommaSeparated values from the list. Example: []string{"a,b", "c,d"} becomes []string{"a", "b", "c", "d"}.
func SplitCommaSeparated(arr []string) []string {
var result []string
for _, val := range arr {
result = append(result, strings.Split(val, ",")...)
}
return result
}
26 changes: 26 additions & 0 deletions shared/sliceutil/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,29 @@ func TestIntersectionByteSlices(t *testing.T) {
}
}
}

func TestSplitCommaSeparated(t *testing.T) {
tests := []struct {
input []string
output []string
}{
{
input: []string{"a,b", "c,d"},
output: []string{"a", "b", "c", "d"},
},
{
input: []string{"a", "b,c,d"},
output: []string{"a", "b", "c", "d"},
},
{
input: []string{"a", "b", "c"},
output: []string{"a", "b", "c"},
},
}

for _, tt := range tests {
if result := SplitCommaSeparated(tt.input); !reflect.DeepEqual(result, tt.output) {
t.Errorf("SplitCommaSeparated(%v) = %v; wanted %v", tt.input, result, tt.output)
}
}
}
18 changes: 18 additions & 0 deletions tools/interop/convert-keys/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/prysmaticlabs/prysm/tools/interop/convert-keys",
visibility = ["//visibility:public"],
deps = [
"//tools/unencrypted-keys-gen:go_default_library",
"@in_gopkg_yaml_v2//:go_default_library",
],
)

go_binary(
name = "convert-keys",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
65 changes: 65 additions & 0 deletions tools/interop/convert-keys/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Used for converting keys.yaml files from eth2.0-pm for interop testing.
// See: https://github.com/ethereum/eth2.0-pm/tree/master/interop/mocked_start
//
// This code can be discarded after interop testing.
package main

import (
"encoding/hex"
"fmt"
"io/ioutil"
"log"
"os"

"gopkg.in/yaml.v2"
keygen "github.com/prysmaticlabs/prysm/tools/unencrypted-keys-gen"
)

// KeyPair with hex encoded data.
type KeyPair struct {
Priv string `yaml:"privkey"`
Pub string `yaml:"pubkey"`
}

// KeyPairs represent the data format in the upstream yaml.
type KeyPairs []KeyPair

func main() {
if len(os.Args) < 3 {
fmt.Println("Usage: convert-keys path/to/keys.yaml path/to/output.json")
return
}
inFile := os.Args[1]

in, err := ioutil.ReadFile(inFile)
if err != nil {
log.Fatalf("Failed to read file %s: %v", inFile, err)
}
data := make(KeyPairs, 0)
if err := yaml.Unmarshal(in, &data); err != nil {
log.Fatalf("Failed to unmarshal yaml: %v", err)
}

out := &keygen.UnencryptedKeysContainer{}
for _, key := range data {
pk, err := hex.DecodeString(key.Priv[2:])
if err != nil {
log.Fatalf("Failed to decode hex string %s: %v", key.Priv, err)
}

out.Keys = append(out.Keys, &keygen.UnencryptedKeys{
ValidatorKey: pk,
WithdrawalKey: pk,
})
}

outFile, err := os.Create(os.Args[2])
if err != nil {
log.Fatalf("Failed to create file at %s: %v", os.Args[2], err)
}
defer outFile.Close()
if err := keygen.SaveUnencryptedKeysToFile(outFile, out); err != nil {
log.Fatalf("Failed to save %v", err)
}
log.Printf("Wrote %s\n", os.Args[2])
}
4 changes: 3 additions & 1 deletion tools/unencrypted-keys-gen/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/prysmaticlabs/prysm/tools/unencrypted-keys-gen",
visibility = ["//visibility:private"],
visibility = [
"//tools/interop/convert-keys:__pkg__",
],
deps = [
"//shared/bls:go_default_library",
],
Expand Down
21 changes: 12 additions & 9 deletions tools/unencrypted-keys-gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ var (
overwrite = flag.Bool("overwrite", false, "If the key file exists, it will be overwritten")
)

type unencryptedKeysContainer struct {
Keys []*unencryptedKeys `json:"keys"`
// UnencryptedKeysContainer defines the structure of the unecrypted key JSON file.
type UnencryptedKeysContainer struct {
Keys []*UnencryptedKeys `json:"keys"`
}

type unencryptedKeys struct {
// UnencryptedKeys is the inner struct of the JSON file.
type UnencryptedKeys struct {
ValidatorKey []byte `json:"validator_key"`
WithdrawalKey []byte `json:"withdrawal_key"`
}
Expand Down Expand Up @@ -53,14 +55,14 @@ func main() {
}()

ctnr := generateUnencryptedKeys(rand.Reader)
if err := saveUnencryptedKeysToFile(file, ctnr); err != nil {
if err := SaveUnencryptedKeysToFile(file, ctnr); err != nil {
log.Fatal(err)
}
}

func generateUnencryptedKeys(r io.Reader) *unencryptedKeysContainer {
ctnr := &unencryptedKeysContainer{
Keys: make([]*unencryptedKeys, *numKeys),
func generateUnencryptedKeys(r io.Reader) *UnencryptedKeysContainer {
ctnr := &UnencryptedKeysContainer{
Keys: make([]*UnencryptedKeys, *numKeys),
}
for i := 0; i < *numKeys; i++ {
signingKey, err := bls.RandKey(r)
Expand All @@ -71,15 +73,16 @@ func generateUnencryptedKeys(r io.Reader) *unencryptedKeysContainer {
if err != nil {
log.Fatal(err)
}
ctnr.Keys[i] = &unencryptedKeys{
ctnr.Keys[i] = &UnencryptedKeys{
ValidatorKey: signingKey.Marshal(),
WithdrawalKey: withdrawalKey.Marshal(),
}
}
return ctnr
}

func saveUnencryptedKeysToFile(w io.Writer, ctnr *unencryptedKeysContainer) error {
// SaveUnencryptedKeysToFile JSON encodes the container and writes to the writer.
func SaveUnencryptedKeysToFile(w io.Writer, ctnr *UnencryptedKeysContainer) error {
enc, err := json.Marshal(ctnr)
if err != nil {
log.Fatal(err)
Expand Down
4 changes: 2 additions & 2 deletions tools/unencrypted-keys-gen/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
func TestSavesUnencryptedKeys(t *testing.T) {
ctnr := generateUnencryptedKeys(rand.Reader)
buf := new(bytes.Buffer)
if err := saveUnencryptedKeysToFile(buf, ctnr); err != nil {
if err := SaveUnencryptedKeysToFile(buf, ctnr); err != nil {
t.Fatal(err)
}
enc := buf.Bytes()
dec := &unencryptedKeysContainer{}
dec := &UnencryptedKeysContainer{}
if err := json.Unmarshal(enc, dec); err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 3708a8f

Please sign in to comment.