Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

Commit

Permalink
Merge pull request #173 from libp2p/interop
Browse files Browse the repository at this point in the history
use GitHub Actions to test interopability of releases
  • Loading branch information
marten-seemann authored Sep 3, 2020
2 parents 169040b + 65b7ce9 commit 649cf9b
Show file tree
Hide file tree
Showing 7 changed files with 404 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/interop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -e

SERVER=$1
CLIENT=$2
SERVER_ADDR="/ip4/127.0.0.1/udp/12345/quic"

for k1 in server*.key; do
for k2 in client*.key; do
echo "Running with server $SERVER ($k1) and client $CLIENT ($k2)"
./$SERVER -role server -key $k1 -peerkey $k2 -addr $SERVER_ADDR &
./$CLIENT -role client -key $k2 -peerkey $k1 -addr $SERVER_ADDR
wait &
done;
done;
95 changes: 95 additions & 0 deletions .github/workflows/interop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: interop
on:
push:
branches: master
tags:
pull_request:
branches: master

jobs:
keygen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v1
with:
go-version: '^1.15'
- name: Generate keys
run: |
go build -o keygen integrationtests/keygen/keygen.go
./keygen -prefix server
./keygen -prefix client
- name: Upload keys
uses: actions/upload-artifact@v2
with:
name: keys
path: ./*.key
matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v2
- id: set-matrix
run: |
TASKS=$(echo $(grep -o '^[^//]*' .github/workflows/matrix.jsonc) | sed 's/ //g' )
echo $TASKS
echo "::set-output name=matrix::$TASKS"
builder:
needs: [ matrix ]
runs-on: ubuntu-latest
strategy:
matrix:
cfg: ${{ fromJson(needs.matrix.outputs.matrix) }}
name: Builder (${{ matrix.cfg.commit }}, Go ${{ matrix.cfg.go }})
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-go@v1
with:
go-version: ${{ matrix.cfg.go }}
- run: go version
- name: Build transport
run: |
cp -r integrationtests builder
git checkout ${{ matrix.cfg.commit }}
rm -rf integrationtests || true
mv builder integrationtests
git reflog --decorate -1
if [[ `git merge-base --is-ancestor HEAD 126c64772ba0aef0b2b6d58ff36e55a93f9253a7; echo $?` == "1" ]]; then
go build -o transport-go${{ matrix.cfg.go }}-${{ matrix.cfg.commit }} integrationtests/main.go
else
go build -tags oldstream -o transport-go${{ matrix.cfg.go }}-${{ matrix.cfg.commit }} integrationtests/main.go
fi
- name: Upload binary
uses: actions/upload-artifact@v2
with:
name: binary
path: ./transport-go${{ matrix.cfg.go }}-${{ matrix.cfg.commit }}
interop:
runs-on: ubuntu-latest
needs: [ matrix, keygen, builder ]
strategy:
matrix:
server: ${{ fromJson(needs.matrix.outputs.matrix) }}
client: ${{ fromJson(needs.matrix.outputs.matrix) }}
name: server (${{ matrix.server.commit }}, Go ${{ matrix.server.go }}) - client (${{ matrix.client.commit }}, Go ${{ matrix.client.go }})
steps:
- uses: actions/checkout@v2
- run: mkdir interop
- name: Download keys
uses: actions/download-artifact@v2
with:
name: keys
path: interop/
- name: Download binary
uses: actions/download-artifact@v2
with:
name: binary
path: interop/
- name: Run interop
run: |
cd interop
chmod 744 transport*
../.github/workflows/interop.sh transport-go${{ matrix.server.go }}-${{ matrix.server.commit }} transport-go${{ matrix.client.go }}-${{ matrix.client.commit }}
11 changes: 11 additions & 0 deletions .github/workflows/matrix.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{ "go": "1.13", "commit": "v0.6.0" },
{ "go": "1.14", "commit": "v0.6.0" },
{ "go": "1.13", "commit": "v0.7.0" },
{ "go": "1.14", "commit": "v0.7.0" },
// v0.7.1 was never released in IPFS.
{ "go": "1.14", "commit": "v0.8.0" },
{ "go": "1.15", "commit": "v0.8.0" },
{ "go": "1.14", "commit": "HEAD" },
{ "go": "1.15", "commit": "HEAD" }
]
61 changes: 61 additions & 0 deletions integrationtests/keygen/keygen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"crypto/rand"
"flag"
"io/ioutil"
"log"

"github.com/libp2p/go-libp2p-core/crypto"
)

func main() {
prefix := flag.String("prefix", "", "output file name prefix")
flag.Parse()

if err := exportKeys(*prefix); err != nil {
log.Fatal(err)
}
}

func exportKeys(prefix string) error {
rsa, _, err := crypto.GenerateRSAKeyPair(2048, rand.Reader)
if err != nil {
return err
}
if err := writeKey(rsa, prefix+"-rsa"); err != nil {
return err
}

ecdsa, _, err := crypto.GenerateECDSAKeyPair(rand.Reader)
if err != nil {
return err
}
if err := writeKey(ecdsa, prefix+"-ecdsa"); err != nil {
return err
}

ed, _, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
return err
}
if err := writeKey(ed, prefix+"-ed25519"); err != nil {
return err
}

sec, _, err := crypto.GenerateSecp256k1Key(rand.Reader)
if err != nil {
return err
}
return writeKey(sec, prefix+"-secp256k1")
}

func writeKey(priv crypto.PrivKey, name string) error {
privBytes, err := crypto.MarshalPrivateKey(priv)
if err != nil {
log.Fatal(err)
}
filename := name + ".key"
log.Println("Exporting key to", filename)
return ioutil.WriteFile(filename, privBytes, 0644)
}
162 changes: 162 additions & 0 deletions integrationtests/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package main

import (
"bytes"
"context"
"crypto/rand"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"time"

"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
libp2pquic "github.com/libp2p/go-libp2p-quic-transport"
ma "github.com/multiformats/go-multiaddr"

"github.com/libp2p/go-libp2p-quic-transport/integrationtests/stream"
)

func main() {
hostKeyFile := flag.String("key", "", "file containing the libp2p private key")
peerKeyFile := flag.String("peerkey", "", "file containing the libp2p private key of the peer")
addrStr := flag.String("addr", "", "address to listen on (for the server) or to dial (for the client)")
role := flag.String("role", "", "server or client")
flag.Parse()

hostKey, peerPubKey, err := readKeys(*hostKeyFile, *peerKeyFile)
if err != nil {
log.Fatal(err)
}
addr, err := ma.NewMultiaddr(*addrStr)
if err != nil {
log.Fatal(err)
}

switch *role {
case "server":
if err := runServer(hostKey, peerPubKey, addr); err != nil {
log.Fatal(err)
}
case "client":
if err := runClient(hostKey, peerPubKey, addr); err != nil {
log.Fatal(err)
}
}
}

// We pass in both the private keys of host and peer.
// We never use the private key of the peer though.
// That's why this function returns the peer's public key.
func readKeys(hostKeyFile, peerKeyFile string) (crypto.PrivKey, crypto.PubKey, error) {
// read the host key
hostKeyBytes, err := ioutil.ReadFile(hostKeyFile)
if err != nil {
return nil, nil, err
}
hostKey, err := crypto.UnmarshalPrivateKey(hostKeyBytes)
if err != nil {
return nil, nil, err
}
// read the peers key
peerKeyBytes, err := ioutil.ReadFile(peerKeyFile)
if err != nil {
return nil, nil, err
}
peerKey, err := crypto.UnmarshalPrivateKey(peerKeyBytes)
if err != nil {
return nil, nil, err
}
return hostKey, peerKey.GetPublic(), nil
}

func runServer(hostKey crypto.PrivKey, peerKey crypto.PubKey, addr ma.Multiaddr) error {
tr, err := libp2pquic.NewTransport(hostKey, nil, nil)
if err != nil {
return err
}
ln, err := tr.Listen(addr)
if err != nil {
return err
}
conn, err := ln.Accept()
if err != nil {
return err
}
defer ln.Close()
if !conn.RemotePublicKey().Equals(peerKey) {
return errors.New("mismatching public keys")
}
clientPeerID, err := peer.IDFromPublicKey(peerKey)
if err != nil {
return err
}
if conn.RemotePeer() != clientPeerID {
return fmt.Errorf("remote Peer ID mismatch. Got %s, expected %s", conn.RemotePeer().Pretty(), clientPeerID.Pretty())
}
for {
st, err := conn.AcceptStream()
if err != nil {
return nil
}
str := stream.WrapStream(st)
defer str.Close()
data, err := ioutil.ReadAll(str)
if err != nil {
return err
}
if _, err := str.Write(data); err != nil {
return err
}
if err := str.CloseWrite(); err != nil {
return err
}
}
}

func runClient(hostKey crypto.PrivKey, peerKey crypto.PubKey, addr ma.Multiaddr) error {
tr, err := libp2pquic.NewTransport(hostKey, nil, nil)
if err != nil {
return err
}
serverPeerID, err := peer.IDFromPublicKey(peerKey)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := tr.Dial(ctx, addr, serverPeerID)
if err != nil {
return err
}
defer conn.Close()
if !conn.RemotePublicKey().Equals(peerKey) {
return errors.New("mismatching public keys")
}
if conn.RemotePeer() != serverPeerID {
return fmt.Errorf("remote Peer ID mismatch. Got %s, expected %s", conn.RemotePeer().Pretty(), serverPeerID.Pretty())
}
st, err := conn.OpenStream()
if err != nil {
return err
}
str := stream.WrapStream(st)
data := make([]byte, 1<<15)
rand.Read(data)
if _, err := str.Write(data); err != nil {
return err
}
if err := str.CloseWrite(); err != nil {
return err
}
echoed, err := ioutil.ReadAll(str)
if err != nil {
return err
}
if !bytes.Equal(data, echoed) {
return errors.New("echoed data does not match")
}
return nil
}
30 changes: 30 additions & 0 deletions integrationtests/stream/stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package stream

import (
"io"
"time"

"github.com/libp2p/go-libp2p-core/mux"
)

type Stream interface {
io.Reader
io.Writer
io.Closer

CloseWrite() error
CloseRead() error
Reset() error

SetDeadline(time.Time) error
SetReadDeadline(time.Time) error
SetWriteDeadline(time.Time) error
}

type stream struct {
mux.MuxedStream
}

func WrapStream(str mux.MuxedStream) *stream {
return &stream{MuxedStream: str}
}
Loading

0 comments on commit 649cf9b

Please sign in to comment.