diff --git a/.gitignore b/.gitignore index c633f8bcb..3f8c2d362 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ web/app/node_modules .idea memcoin +deb-package/deb +deb-package/dist diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..b832b9f12 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "contracts/evoting/unikernel/unikraft"] + path = contracts/evoting/unikernel/unikraft + url = https://github.com/unikraft/unikraft +[submodule "contracts/evoting/unikernel/libs/lwip"] + path = contracts/evoting/unikernel/libs/lwip + url = https://github.com/unikraft/lib-lwip +[submodule "contracts/evoting/unikernel/libs/newlib"] + path = contracts/evoting/unikernel/libs/newlib + url = https://github.com/unikraft/lib-newlib +[submodule "contracts/evoting/unikernel/libs/libsodium"] + path = contracts/evoting/unikernel/libs/libsodium + url = https://github.com/cs-pub-ro/lib-libsodium diff --git a/Makefile b/Makefile index e7a38ac82..8b7be1b3a 100644 --- a/Makefile +++ b/Makefile @@ -22,4 +22,8 @@ test_integration: go test ./integration build: - go build -ldflags="-X $(versionFlag) -X $(timeFlag)" ./cli/memcoin \ No newline at end of file + go build -ldflags="-X $(versionFlag) -X $(timeFlag)" ./cli/memcoin + +deb: + GOOS=linux GOARCH=amd64 make build + cd deb-package; ./build-deb.sh; cd .. \ No newline at end of file diff --git a/README.md b/README.md index 7a36440db..f9f493c80 100644 --- a/README.md +++ b/README.md @@ -184,4 +184,8 @@ timeFlag="github.com/dedis/d-voting.BuildTime=`date +'%d/%m/%y_%H:%M'`" go build -ldflags="-X $versionFlag -X $timeFlag" ./cli/memcoin ``` -Note that `make build` will do that for you. \ No newline at end of file +Note that `make build` will do that for you. You can also cross-compile with: + +```sh +GOOS=linux GOARCH=amd64 make build +``` \ No newline at end of file diff --git a/cli/memcoin/mod.go b/cli/memcoin/mod.go index eec96e93b..52761f164 100644 --- a/cli/memcoin/mod.go +++ b/cli/memcoin/mod.go @@ -36,6 +36,7 @@ import ( shuffle "github.com/dedis/d-voting/services/shuffle/neff/controller" cosipbft "github.com/dedis/d-voting/cli/cosipbftcontroller" + mino "github.com/dedis/d-voting/cli/minocontroller" "github.com/dedis/d-voting/cli/postinstall" evoting "github.com/dedis/d-voting/contracts/evoting/controller" metrics "github.com/dedis/d-voting/metrics/controller" @@ -44,7 +45,6 @@ import ( db "go.dedis.ch/dela/core/store/kv/controller" pool "go.dedis.ch/dela/core/txn/pool/controller" signed "go.dedis.ch/dela/core/txn/signed/controller" - mino "go.dedis.ch/dela/mino/minogrpc/controller" proxy "go.dedis.ch/dela/mino/proxy/http/controller" _ "github.com/dedis/d-voting/services/shuffle/neff/json" diff --git a/cli/minocontroller/actions.go b/cli/minocontroller/actions.go new file mode 100644 index 000000000..6b4f87671 --- /dev/null +++ b/cli/minocontroller/actions.go @@ -0,0 +1,102 @@ +// This file contains the implementation of the controller actions. +// +// Documentation Last Review: 07.10.2020 +// + +package minocontroller + +import ( + "crypto/tls" + "encoding/base64" + "fmt" + + "go.dedis.ch/dela/cli/node" + "go.dedis.ch/dela/mino" + "go.dedis.ch/dela/mino/minogrpc" + "golang.org/x/xerrors" +) + +// CertAction is an action to list the certificates known by the server. +// +// - implements node.ActionTemplate +type certAction struct{} + +// Execute implements node.ActionTemplate. It prints the list of certificates +// known by the server with the address associated and the expiration date. +func (a certAction) Execute(req node.Context) error { + var m minogrpc.Joinable + + err := req.Injector.Resolve(&m) + if err != nil { + return xerrors.Errorf("couldn't resolve: %v", err) + } + + m.GetCertificateStore().Range(func(addr mino.Address, cert *tls.Certificate) bool { + fmt.Fprintf(req.Out, "Address: %v Certificate: %v\n", addr, cert.Leaf.NotAfter) + return true + }) + + return nil +} + +// TokenAction is an action to generate a token that will be valid for another +// server to join the network of participants. +// +// - implements node.ActionTemplate +type tokenAction struct{} + +// Execute implements node.ActionTemplate. It generates a token that will be +// valid for the amount of time given in the request. +func (a tokenAction) Execute(req node.Context) error { + exp := req.Flags.Duration("expiration") + + var m minogrpc.Joinable + err := req.Injector.Resolve(&m) + if err != nil { + return xerrors.Errorf("couldn't resolve: %v", err) + } + + token := m.GenerateToken(exp) + + digest, err := m.GetCertificateStore().Hash(m.GetCertificate()) + if err != nil { + return xerrors.Errorf("couldn't hash certificate: %v", err) + } + + fmt.Fprintf(req.Out, "--token %s --cert-hash %s\n", + token, base64.StdEncoding.EncodeToString(digest)) + + return nil +} + +// JoinAction is an action to join a network of participants by providing a +// valid token and the certificate hash. +// +// - implements node.ActionTemplate +type joinAction struct{} + +// Execute implements node.ActionTemplate. It parses the request and send the +// join request to the distant node. +func (a joinAction) Execute(req node.Context) error { + token := req.Flags.String("token") + addr := req.Flags.String("address") + certHash := req.Flags.String("cert-hash") + + var m minogrpc.Joinable + err := req.Injector.Resolve(&m) + if err != nil { + return xerrors.Errorf("couldn't resolve: %v", err) + } + + cert, err := base64.StdEncoding.DecodeString(certHash) + if err != nil { + return xerrors.Errorf("couldn't decode digest: %v", err) + } + + err = m.Join(addr, token, cert) + if err != nil { + return xerrors.Errorf("couldn't join: %v", err) + } + + return nil +} diff --git a/cli/minocontroller/mod.go b/cli/minocontroller/mod.go new file mode 100644 index 000000000..cdbe0a385 --- /dev/null +++ b/cli/minocontroller/mod.go @@ -0,0 +1,275 @@ +// Package controller implements a controller for minogrpc. +// +// The controller can be used in a CLI to inject a dependency for Mino. It will +// start the overlay on the start command, and make sure resources are cleaned +// when the CLI daemon is stopping. +// +// Documentation Last Review: 07.10.2020 +// +package minocontroller + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "io" + "math" + "net" + "path/filepath" + "time" + + "go.dedis.ch/dela" + "go.dedis.ch/dela/cli" + "go.dedis.ch/dela/cli/node" + "go.dedis.ch/dela/core/store/kv" + "go.dedis.ch/dela/crypto/loader" + "go.dedis.ch/dela/mino" + "go.dedis.ch/dela/mino/minogrpc" + "go.dedis.ch/dela/mino/minogrpc/certs" + "go.dedis.ch/dela/mino/minogrpc/session" + "go.dedis.ch/dela/mino/router/tree" + "golang.org/x/xerrors" +) + +const certKeyName = "cert.key" + +// MiniController is an initializer with the minimum set of commands. +// +// - implements node.Initializer +type miniController struct { + random io.Reader + curve elliptic.Curve +} + +// NewController returns a new initializer to start an instance of Minogrpc. +func NewController() node.Initializer { + return miniController{ + random: rand.Reader, + curve: elliptic.P521(), + } +} + +// Build implements node.Initializer. It populates the builder with the commands +// to control Minogrpc. +func (m miniController) SetCommands(builder node.Builder) { + builder.SetStartFlags( + cli.IntFlag{ + Name: "port", + Usage: "set the port to listen on", + Value: 2000, + }, + cli.BoolFlag{ + Name: "externalip", + Usage: "will try to get a non-loopback IP address, instead of 127.0.0.1", + Value: false, + }, + ) + + cmd := builder.SetCommand("minogrpc") + cmd.SetDescription("Network overlay administration") + + sub := cmd.SetSubCommand("certificates") + sub.SetDescription("list the certificates of the server") + sub.SetAction(builder.MakeAction(certAction{})) + + sub = cmd.SetSubCommand("token") + sub.SetDescription("generate a token to share to others to join the network") + sub.SetFlags( + cli.DurationFlag{ + Name: "expiration", + Usage: "amount of time before expiration", + Value: 24 * time.Hour, + }, + ) + sub.SetAction(builder.MakeAction(tokenAction{})) + + sub = cmd.SetSubCommand("join") + sub.SetDescription("join a network of participants") + sub.SetFlags( + cli.StringFlag{ + Name: "token", + Usage: "secret token generated by the node to join", + Required: true, + }, + cli.StringFlag{ + Name: "address", + Usage: "address of the node to join", + Required: true, + }, + cli.StringFlag{ + Name: "cert-hash", + Usage: "certificate hash of the distant server", + Required: true, + }, + ) + sub.SetAction(builder.MakeAction(joinAction{})) +} + +// OnStart implements node.Initializer. It starts the minogrpc instance and +// injects it in the dependency resolver. +func (m miniController) OnStart(ctx cli.Flags, inj node.Injector) error { + + port := ctx.Int("port") + if port < 0 || port > math.MaxUint16 { + return xerrors.Errorf("invalid port value %d", port) + } + + rter := tree.NewRouter(minogrpc.NewAddressFactory()) + + var err error + ip := "127.0.0.1" + + if ctx.Bool("externalip") { + ip, err = getExternalIP() + if err != nil { + return xerrors.Errorf("failed to get external IP address: %v", err) + } + } + + addr := minogrpc.ParseAddress(ip, uint16(port)) + + var db kv.DB + err = inj.Resolve(&db) + if err != nil { + return xerrors.Errorf("injector: %v", err) + } + + certs := certs.NewDiskStore(db, session.AddressFactory{}) + + key, err := m.getKey(ctx) + if err != nil { + return xerrors.Errorf("cert private key: %v", err) + } + + opts := []minogrpc.Option{ + minogrpc.WithStorage(certs), + minogrpc.WithCertificateKey(key, key.Public()), + } + + o, err := minogrpc.NewMinogrpc(addr, rter, opts...) + if err != nil { + return xerrors.Errorf("couldn't make overlay: %v", err) + } + + inj.Inject(o) + + dela.Logger.Info().Msgf("%v is running", o) + + return nil +} + +// StoppableMino is an extension of Mino to allow one to stop the instance. +type StoppableMino interface { + mino.Mino + + GracefulStop() error +} + +// OnStop implements node.Initializer. It stops the network overlay. +func (m miniController) OnStop(inj node.Injector) error { + var o StoppableMino + err := inj.Resolve(&o) + if err != nil { + return xerrors.Errorf("injector: %v", err) + } + + err = o.GracefulStop() + if err != nil { + return xerrors.Errorf("while stopping mino: %v", err) + } + + return nil +} + +func (m miniController) getKey(flags cli.Flags) (*ecdsa.PrivateKey, error) { + loader := loader.NewFileLoader(filepath.Join(flags.Path("config"), certKeyName)) + + keydata, err := loader.LoadOrCreate(newGenerator(m.random, m.curve)) + if err != nil { + return nil, xerrors.Errorf("while loading: %v", err) + } + + key, err := x509.ParseECPrivateKey(keydata) + if err != nil { + return nil, xerrors.Errorf("while parsing: %v", err) + } + + return key, nil +} + +// generator can generate a private key compatible with the x509 certificate. +// +// - implements loader.Generator +type generator struct { + random io.Reader + curve elliptic.Curve +} + +func newGenerator(r io.Reader, c elliptic.Curve) loader.Generator { + return generator{ + random: r, + curve: c, + } +} + +// Generate implements loader.Generator. It returns the serialized data of a +// private key generated from the an elliptic curve. The data is formatted as a +// PEM block "EC PRIVATE KEY". +func (g generator) Generate() ([]byte, error) { + priv, err := ecdsa.GenerateKey(g.curve, g.random) + if err != nil { + return nil, xerrors.Errorf("ecdsa: %v", err) + } + + data, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return nil, xerrors.Errorf("while marshaling: %v", err) + } + + return data, nil +} + +// getExternalIP returns an external IP, which is up and a loopback one. Taken +// from https://stackoverflow.com/a/23558495 +func getExternalIP() (string, error) { + ifaces, err := net.Interfaces() + if err != nil { + return "", xerrors.Errorf("failed to get interfaces: %v", err) + } + + for _, iface := range ifaces { + if iface.Flags&net.FlagUp == 0 { + continue // interface down + } + if iface.Flags&net.FlagLoopback != 0 { + continue // loopback interface + } + + addrs, err := iface.Addrs() + if err != nil { + return "", xerrors.Errorf("failed to get addresses: %v", err) + } + + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + + if ip == nil || ip.IsLoopback() { + continue + } + + ip = ip.To4() + if ip == nil { + continue // not an ipv4 address + } + return ip.String(), nil + } + } + return "", xerrors.New("no external IP found. Are you connected to a network?") +} diff --git a/contracts/evoting/evoting.go b/contracts/evoting/evoting.go index 59fd326fe..4688a53f8 100644 --- a/contracts/evoting/evoting.go +++ b/contracts/evoting/evoting.go @@ -18,6 +18,7 @@ import ( "time" "github.com/dedis/d-voting/contracts/evoting/types" + "go.dedis.ch/dela" "go.dedis.ch/dela/core/execution" "go.dedis.ch/dela/core/execution/native" "go.dedis.ch/dela/core/ordering/cosipbft/authority" @@ -40,10 +41,9 @@ const ( // parameters for the Unikernel connection const ( - unikernelAddr = "127.0.0.1:1234" - dialTimeout = time.Second * 60 - writeTimeout = time.Second * 60 - readTimeout = time.Second * 60 + dialTimeout = time.Second * 60 + writeTimeout = time.Second * 60 + readTimeout = time.Second * 60 ) // evotingCommand implements the commands of the Evoting contract. @@ -643,17 +643,22 @@ func (e evotingCommand) combineShares(snap store.Snapshot, step execution.Step) return xerrors.Errorf("failed to create dir: %v", err) } - tmp, err := ioutil.TempDir(e.exportFolder, "decrypted-ballots") - if err != nil { - return xerrors.Errorf("failed to create temp dir: %v", err) - } + // we must pass to the Unikernel the RELATIVE folder path from the one + // mounted, i.e. e.exportFolder. This folder MUST be uniq for each + // transaction. + txIDHex := hex.EncodeToString(step.Current.GetID()) + tmp := fmt.Sprintf("decrypted-ballots-%s-%s-%d/", tx.ElectionID[:4], txIDHex[:4], time.Now().UnixNano()) + + dela.Logger.Info().Msgf("using the following folder: %s", tmp) - err = exportPubshares(tmp, election.PubsharesUnits, numBallots, numChunks) + absPath := filepath.Join(e.exportFolder, tmp) + + err = exportPubshares(absPath, election.PubsharesUnits, numBallots, numChunks) if err != nil { return xerrors.Errorf("failed to export pubshares: %v", err) } - conn, err := e.dialer("tcp", unikernelAddr, dialTimeout) + conn, err := e.dialer("tcp", e.unikernelAddr, dialTimeout) if err != nil { return xerrors.Errorf("failed to dial TCP: %v", err) } @@ -715,7 +720,7 @@ func (e evotingCommand) combineShares(snap store.Snapshot, step execution.Step) return xerrors.Errorf("an error ocurred in the unikernel: %s", readRes) } - rawBallots, err := importBallots(tmp, numChunks) + rawBallots, err := importBallots(absPath, numChunks) if err != nil { return xerrors.Errorf("failed to import ballots: %v", err) } @@ -942,6 +947,8 @@ func exportPubshares(folder string, units types.PubsharesUnits, numBallots, numC } } } + + file.Close() } return nil diff --git a/contracts/evoting/mod.go b/contracts/evoting/mod.go index 1bd61aa0a..820d8411a 100644 --- a/contracts/evoting/mod.go +++ b/contracts/evoting/mod.go @@ -114,14 +114,15 @@ type Contract struct { rosterFac authority.Factory transactionFac serde.Factory - dialer DialerFactory - exportFolder string + dialer DialerFactory + exportFolder string + unikernelAddr string } // NewContract creates a new Value contract func NewContract(accessKey, rosterKey []byte, srvc access.Service, pedersen dkg.DKG, rosterFac authority.Factory, exportFolder string, - dialer DialerFactory) Contract { + dialer DialerFactory, unikernelAddr string) Contract { ctx := json.NewContext() @@ -142,8 +143,9 @@ func NewContract(accessKey, rosterKey []byte, srvc access.Service, rosterFac: rosterFac, transactionFac: transactionFac, - dialer: dialer, - exportFolder: exportFolder, + dialer: dialer, + exportFolder: exportFolder, + unikernelAddr: unikernelAddr, } contract.cmd = evotingCommand{Contract: &contract, prover: proof.HashVerify} diff --git a/contracts/evoting/mod_test.go b/contracts/evoting/mod_test.go index 5e0a0509b..9c2eb6575 100644 --- a/contracts/evoting/mod_test.go +++ b/contracts/evoting/mod_test.go @@ -81,14 +81,14 @@ func TestExecute(t *testing.T) { service := fakeAccess{err: fake.GetError()} rosterFac := fakeAuthorityFactory{} - contract := NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil) + contract := NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil, "") err := contract.Execute(fakeStore{}, makeStep(t)) require.EqualError(t, err, "identity not authorized: fake.PublicKey ("+fake.GetError().Error()+")") service = fakeAccess{} - contract = NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil) + contract = NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil, "") err = contract.Execute(fakeStore{}, makeStep(t)) require.EqualError(t, err, "\"evoting:command\" not found in tx arg") @@ -145,7 +145,7 @@ func TestCommand_CreateElection(t *testing.T) { service := fakeAccess{err: fake.GetError()} rosterFac := fakeAuthorityFactory{} - contract := NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil) + contract := NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil, "") cmd := evotingCommand{ Contract: &contract, @@ -1053,7 +1053,7 @@ func TestCommand_DecryptBallots(t *testing.T) { require.NoError(t, err) cmd.dialer = func(network, address string, timeout time.Duration) (net.Conn, error) { - return &fake.Conn{}, nil + return &fake.Conn{Path: tmpDir}, nil } err = cmd.combineShares(snap, makeStep(t, ElectionArg, string(data))) @@ -1389,7 +1389,7 @@ func initElectionAndContract() (types.Election, Contract) { service := fakeAccess{err: fake.GetError()} rosterFac := fakeAuthorityFactory{} - contract := NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil) + contract := NewContract(evotingAccessKey[:], rosterKey[:], service, fakeDkg, rosterFac, "", nil, "") return dummyElection, contract } diff --git a/contracts/evoting/unikernel/apps/combine_shares/README.md b/contracts/evoting/unikernel/apps/combine_shares/README.md index 40ee61e3c..4d489df4d 100644 --- a/contracts/evoting/unikernel/apps/combine_shares/README.md +++ b/contracts/evoting/unikernel/apps/combine_shares/README.md @@ -9,49 +9,53 @@ functionality provided by [libsodium](https://github.com/jedisct1/libsodium). ## Requirements +- To build the kernel: + ```sh -sudo apt-get install git -sudo apt-get install make -sudo apt-get install gcc -sudo apt-get install libncurses-dev -sudo apt-get install flex -sudo apt-get install bison -sudo apt-get install unzip -sudo apt-get install socat -sudo apt-get install uuid-runtime -sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils \ - virtinst virt-manager -sudo apt install net-tools +sudo apt install bison build-essential flex git libncurses-dev unzip ``` -## Setup - -Apart from the current repository, you also need to clone: +- To run the kernel in a debian VM, the following packages must be installed +in the VM: -* [Unikraft](https://github.com/unikraft/unikraft) -* [lwip](https://github.com/unikraft/lib-lwip) -* [newlib](https://github.com/unikraft/lib-newlib) -* [libsodium](https://github.com/unikraft/lib-libsodium) - -Make sure each repository is on the `staging` branch. +```sh +sudo apt install \ + bridge-utils \ + fuse \ + libvirt-clients \ + libvirt-daemon-system \ + qemu-kvm \ + net-tools \ + sgabios \ + socat \ + uuid-runtime \ + virtinst \ + virt-manager +``` -For the libsodium library make sure the following change is included: -https://github.com/unikraft/lib-libsodium/pull/4. +## Setup It is easiest to use a file hierarchy such as: +```tree +unikernel + ┣ apps + ┃ ┗ combine_shares + ┣ libs + ┃ ┣ libsodium + ┃ ┣ lwip + ┃ ┗ newlib + ┗ unikraft ``` -. -|-- apps/ -| `-- app-smart-contract-config/ -|-- libs/ -| |-- lwip/ -| |-- libsodium/ -| `-- newlib/ -`-- unikraft/ + +Please run the following command, which will clone the submodules in the above +illustrated paths: + +```sh +git submodule update --init --recursive ``` -If you use a file hierarhy different than the one above, then edit the +If you use a file hierarchy different than the one above, then edit the `Makefile` file and update the variables to point to the correct location: * Update the `UK_ROOT` variable to point to the location of the Unikraft clone. @@ -62,11 +66,23 @@ If you get a `Cannot find library` error at one of the following steps, you may need to add the `lib-` prefix to the libraries listed in the `LIBS` variable. For instance, `$(UK_LIBS)/lwip` becomes `$(UK_LIBS)/lib-lwip`. +Here is the list of the required repositories that you also need as submodules: + +* [libsodium](https://github.com/unikraft/lib-libsodium) +* [lwip](https://github.com/unikraft/lib-lwip) +* [newlib](https://github.com/unikraft/lib-newlib) +* [Unikraft](https://github.com/unikraft/unikraft) + +For the libsodium library, the submodule still points to the source of the following PR: +https://github.com/unikraft/lib-libsodium/pull/4. + +For the other libraries, please make sure that each repository is on the `staging` branch. + ## Configure -Configure the build by running: +Configure the build by running in `contracts/evoting/unikernel/apps/combine_shares`: -``` +```sh make menuconfig ``` @@ -75,7 +91,7 @@ configuration is loaded from the `Config.uk` file. Then add support for 9pfs, by selecting `Library Configuration` -> `vfscore: VFS Core Interface` -> `vfscore: VFS Configuration`. The select `Automatically mount a root filesystem (/)` and select `9pfs`. For the `Default root device` option fill `fs0`. You should end -up with an image such as the one in `9pfs_config.png`. Save the conifugration +up with an image such as the one in `9pfs_config.png`. Save the configuration and exit. As a result of this, a `.config` file is created. @@ -85,12 +101,12 @@ A KVM unikernel image will be built from the configuration. Build the unikernel KVM image by running: -``` +```sh make ``` The resulting image is in the `build/` subfolder, usually named -`build/app-smart-contract-config_kvm-x86_64`. +`build/combine_shares_kvm-x86_64`. ## Run @@ -98,8 +114,11 @@ The resulting image is to be loaded as part of a KVM virtual machine with a software bridge allowing outside network access to the server application. Start the image by using the `run` script (you require `sudo` privileges): +```sh +./start-unikernel unikernel_paths ``` -$ ./run + +```output [...] Booting from ROM... 1: Set IPv4 address 172.44.0.2 mask 255.255.255.0 gw 172.44.0.1 @@ -126,15 +145,15 @@ the `mnt/` folder. On another terminal, use `nc` to connect via TCP to the program and send the commands: -``` -$ nc 172.44.0.2 1024 +```sh +nc 172.44.0.2 1024 ec_multiply 2c7be86ab07488ba43e8e03d85a67625cfbf98c8544de4c877241b7aaafc7fe3 ``` The running program will print out a summary of received commands: -``` +```sh Received connection from 172.44.0.1:38596 Received: ec_multiply Operation: ec_multiply diff --git a/contracts/evoting/unikernel/apps/combine_shares/create-bridge b/contracts/evoting/unikernel/apps/combine_shares/create-bridge new file mode 100755 index 000000000..6a404075d --- /dev/null +++ b/contracts/evoting/unikernel/apps/combine_shares/create-bridge @@ -0,0 +1,7 @@ +#!/bin/bash + +source ../config/network.env + +echo "Creating bridge ${bridge_iface} with IP address ${uk_gw} ..." +sudo brctl addbr "${bridge_iface}" || true +sudo ifconfig "${bridge_iface}" "${uk_gw}" diff --git a/contracts/evoting/unikernel/apps/combine_shares/qemu-guest b/contracts/evoting/unikernel/apps/combine_shares/qemu-guest index 473363384..33c231f1b 100755 --- a/contracts/evoting/unikernel/apps/combine_shares/qemu-guest +++ b/contracts/evoting/unikernel/apps/combine_shares/qemu-guest @@ -432,7 +432,7 @@ usage() echo " -x Run guest in background, a socket is created for the serial output" echo " -P Create the guest in paused state" echo " -t [TYPE] Set guest type: x86pc, x86q35, arm64v" - echo " -G [NAME] Set name of guest to NAME" + echo " -U [NAME] Set name of guest to NAME" echo " -g [PORT] Run a GDB server for the guest at port PORT (e.g., 1234)" echo " Note: QEMU process stays alive on guest shutdown/reset" echo " -T [LOGFILE] Enable tracing of CPU events (fine-grained results with -W)" @@ -480,7 +480,7 @@ usage() echo " $0 -c 2 -m 2048 -b virbr0 -b virbr1 -q root.qcow2 -d /dev/sdb -d /dev/sdc" } -while getopts :hnN:b:V:f:G:d:q:S:I:e:k:i:a:c:m:v:lrs:p:HxCDG:g:PT:WQ:M:t: OPT; do +while getopts :hnN:b:V:f:G:d:q:S:I:e:k:i:a:c:m:v:lrs:p:HxCDUG:g:PT:WQ:M:t: OPT; do case ${OPT} in v) OPT_VIDEOVNC=0 @@ -666,7 +666,7 @@ EOF C) OPT_CTRLC=1 ;; - G) + U) ARG_GUESTNAME="${OPTARG}" ;; Q) diff --git a/contracts/evoting/unikernel/apps/combine_shares/run b/contracts/evoting/unikernel/apps/combine_shares/run deleted file mode 100755 index d8880a621..000000000 --- a/contracts/evoting/unikernel/apps/combine_shares/run +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -fs_mount="mnt/" -kvm_image="build/combine_shares_kvm-x86_64" -qemu_script="./qemu-guest" -bridge_iface="uk0" -bridge_ip="172.44.0.1" -vm_ip="172.44.0.2" - -echo "Creating bridge $bridge_iface with IP address $bridge_ip ..." -sudo brctl addbr "$bridge_iface" || true -sudo ifconfig "$bridge_iface" "$bridge_ip" - -echo "Starting KVM image connected to bridge interface $bridge_iface ..." -"$qemu_script" -k "$kvm_image" -e "$fs_mount" -b "$bridge_iface" -m 4096 -a "netdev.ipv4_addr=172.44.0.2 netdev.ipv4_gw_addr=172.44.0.1 netdev.ipv4_subnet_mask=255.255.255.0 -- 1024" diff --git a/contracts/evoting/unikernel/apps/combine_shares/start-unikernel b/contracts/evoting/unikernel/apps/combine_shares/start-unikernel new file mode 100755 index 000000000..e3fefaf13 --- /dev/null +++ b/contracts/evoting/unikernel/apps/combine_shares/start-unikernel @@ -0,0 +1,7 @@ +#!/bin/bash + +source ../config/config.env +source ../config/network.env + +echo "Starting KVM image connected to bridge interface ${bridge_iface} ..." +"${qemu_script}" -k "${kvm_image}" -e "${fs_mount}" -b "${bridge_iface}" -m ${qemu_ram} -a "netdev.ipv4_addr=${uk_ip} netdev.ipv4_gw_addr=${uk_gw} netdev.ipv4_subnet_mask=255.255.255.0 -- ${uk_port}" diff --git a/contracts/evoting/unikernel/apps/config/config.env b/contracts/evoting/unikernel/apps/config/config.env new file mode 100755 index 000000000..f08cc306a --- /dev/null +++ b/contracts/evoting/unikernel/apps/config/config.env @@ -0,0 +1,5 @@ +# unikernel config +export fs_mount="mnt/" +export qemu_ram="1024" +export kvm_image="./build/combine_shares_kvm-x86_64" +export qemu_script="./qemu-guest" diff --git a/contracts/evoting/unikernel/apps/config/network.env b/contracts/evoting/unikernel/apps/config/network.env new file mode 100644 index 000000000..38f5b985a --- /dev/null +++ b/contracts/evoting/unikernel/apps/config/network.env @@ -0,0 +1,4 @@ +export uk_gw="172.44.0.1" +export uk_ip="172.44.0.2" +export uk_port="1024" +export bridge_iface="uk0" \ No newline at end of file diff --git a/contracts/evoting/unikernel/libs/libsodium b/contracts/evoting/unikernel/libs/libsodium new file mode 160000 index 000000000..771b45771 --- /dev/null +++ b/contracts/evoting/unikernel/libs/libsodium @@ -0,0 +1 @@ +Subproject commit 771b45771be590c0ec73b40b6707ea51e200da15 diff --git a/contracts/evoting/unikernel/libs/lwip b/contracts/evoting/unikernel/libs/lwip new file mode 160000 index 000000000..40f2c7731 --- /dev/null +++ b/contracts/evoting/unikernel/libs/lwip @@ -0,0 +1 @@ +Subproject commit 40f2c7731e2f00042d6b13b134e6b07ebe769c49 diff --git a/contracts/evoting/unikernel/libs/newlib b/contracts/evoting/unikernel/libs/newlib new file mode 160000 index 000000000..243a75ce3 --- /dev/null +++ b/contracts/evoting/unikernel/libs/newlib @@ -0,0 +1 @@ +Subproject commit 243a75ce377f91d607e5ece9b1f6791b13005c25 diff --git a/contracts/evoting/unikernel/unikraft b/contracts/evoting/unikernel/unikraft new file mode 160000 index 000000000..1939bce1f --- /dev/null +++ b/contracts/evoting/unikernel/unikraft @@ -0,0 +1 @@ +Subproject commit 1939bce1f90e2d3ad028aadefe33758e08c39f89 diff --git a/deb-package/README.md b/deb-package/README.md new file mode 100644 index 000000000..fe4923759 --- /dev/null +++ b/deb-package/README.md @@ -0,0 +1,120 @@ +# Packaging D-Voting in an installable .deb file + +## Requirements + +- gem +- build-essential +- git +- fpm (`sudo gem install fpm`) +- go (see https://go.dev/doc/install) + +```sh +sudo apt install rubygems build-essential git +``` + +## Get the code + +```sh +git clone --branch packaging https://github.com/dedis/d-voting.git --recursive +``` + +## Build the Unikernel + +Follow instructions from the `contracts/evoting/unikernel/apps/combine_shares`. +In summary: + +```sh +cd contracts/evoting/unikernel/apps/combine_shares +git submodule update --init --recursive +make menuconfig +make +``` + +## Build the deb package + +from the root folder, use make: + +```sh +make deb +``` + +Make sure that a git tag exist, i.e `git describe` shows your tag. + +The resulting .deb can be found in the `dist/` folder. + +## Things to do after install + +### EPFL + +Get the token and certificate (24h * 30 = 720): + +```sh +sudo memcoin --config /var/opt/dedis/dvoting/data/dela minogrpc token \ + --expiration 720h +``` + +This result, which looks like as follow, will be given to node's operators: + +``` +--token b6VhdQEPXKOtZHpng8E8jw== --cert-hash oNeyrA864P2cP+TT6IE6GvkeEI/Ec4rOlZWEWiQkQKk= +``` + +### Participants (node's operators) + +Join the network. This operation will make the node share its certificate to the +MASTER node, which, in turn, will share its known certificates to the node. Note +that the certificates are stored in the DB, which means that this operation must +be re-done in case the DB is reset. + +```sh +sudo memcoin --config /var/opt/dedis/dvoting/data/dela minogrpc join \ + --address --token --cert-hash +``` + +Get the node's address and public key: + +```sh +sudo memcoin --config /var/opt/dedis/dvoting/data/dela ordering export +``` + +This will yield a base64 encoded string `
:`. + +It will have to be provided to EPFL. + +## Setup the chain, from EPFL + +**1: Create the chain**: + +Do not forget to include ourself, the EPFL node! + +```sh +sudo memcoin --config /var/opt/dedis/dvoting/data/dela ordering setup \ + --member \ + --member <...> + ... +``` + +**2: grant access for each node to sign transactions on the evoting smart contract**: + +```sh +PK=<> # taken from the "ordering export", the part after ":" +sudo memcoin --config /var/opt/dedis/dvoting/data/dela pool add \ + --key /home/user/master_key.key \ + --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Access \ + --args access:grant_id --args 0300000000000000000000000000000000000000000000000000000000000000 \ + --args access:grant_contract --args go.dedis.ch/dela.Evoting \ + --args access:grant_command --args all \ + --args access:identity --args $PK \ + --args access:command --args GRANT +``` + +You should also grant access to the master key. + +### Test + +```sh +sudo memcoin --config /var/opt/dedis/dvoting/data/dela e-voting scenarioTest \ + --proxy-addr1 "http://192.168.232.133:9080" \ + --proxy-addr2 "http://192.168.232.134:9080" \ + --proxy-addr3 "http://192.168.232.135:9080" +``` diff --git a/deb-package/build-deb.sh b/deb-package/build-deb.sh new file mode 100755 index 000000000..90f3b9825 --- /dev/null +++ b/deb-package/build-deb.sh @@ -0,0 +1,67 @@ +#! /usr/bin/env bash +set -xe + +# cleanup previous installations +rm -rf deb + +# create binaries dir +INSTALL_DIR="deb/opt/dedis/dvoting/bin" +mkdir -p $INSTALL_DIR + +# copy binaries to deb/opt/dedis/dvoting/bin +UKAPP_DIR="../contracts/evoting/unikernel/apps/combine_shares" +cp $UKAPP_DIR/qemu-guest $INSTALL_DIR +cp $UKAPP_DIR/build/combine_shares_kvm-x86_64 $INSTALL_DIR + +DVOTING_CLI_DIR=".." +cp $DVOTING_CLI_DIR/memcoin $INSTALL_DIR + +# add config files +cp -a pkg/etc deb +cp -a pkg/lib deb +cp -a pkg/opt deb +cp -a pkg/var deb + +# add folders +mkdir -p deb/var/log/dedis/dvoting + +# adjust permissions +find deb ! -perm -a+r -exec chmod a+r {} \; + +# get version from git without v prefix +GITVERSION=$(git describe --abbrev=0) +VERSION=${GITVERSION:1} +if [[ -z "${ITERATION}" ]] +then + ITERATION="0" +fi + +# fpm needs an existing output directory +OUTPUT_DIR="dist" +mkdir -p $OUTPUT_DIR + +fpm \ + --force -t deb -a all -s dir -C deb -n dedis-dvoting -v ${VERSION} \ + --iteration ${ITERATION} \ + --deb-user dvoting \ + --deb-group dvoting \ + --depends bridge-utils \ + --depends fuse \ + --depends qemu-kvm \ + --depends libvirt-daemon-system \ + --depends net-tools \ + --depends sgabios \ + --depends socat \ + --depends uuid-runtime \ + --depends virtinst \ + --depends virt-manager \ + --before-install pkg/before-install.sh \ + --after-install pkg/after-install.sh \ + --before-remove pkg/before-remove.sh \ + --after-remove pkg/after-remove.sh \ + --url https://dedis.github.com/dedis/dvoting \ + --description 'D-Voting package for Unicore' \ + --package dist . + +# cleanup +rm -rf ./deb diff --git a/deb-package/pkg/after-install.sh b/deb-package/pkg/after-install.sh new file mode 100644 index 000000000..74ed40ee2 --- /dev/null +++ b/deb-package/pkg/after-install.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# fix permissions +# dvoting:dedis will be applied automatically on sub dirs +chown root:root /opt/dedis + +# allow ls in sub dirs +chmod 755 /opt/dedis + +chown root:root /lib/systemd/system + +enable_service() { +SERVICE=$1 +# Inspired from Debian packages (e.g. /var/lib/dpkg/info/openssh-server.postinst) +# was-enabled defaults to true, so new installations run enable. +if deb-systemd-helper --quiet was-enabled ${SERVICE}; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable ${SERVICE} >/dev/null || true +else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state ${SERVICE} >/dev/null || true +fi +} + +enable_service dvoting-uk.service + +DVOTING_SERVICE=dvoting.service +enable_service ${DVOTING_SERVICE} +systemctl start ${DVOTING_SERVICE} + +ln -s /opt/dedis/dvoting/bin/memcoin /usr/bin/memcoin diff --git a/deb-package/pkg/after-remove.sh b/deb-package/pkg/after-remove.sh new file mode 100644 index 000000000..0eb1d1bb9 --- /dev/null +++ b/deb-package/pkg/after-remove.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# Inspired from Debian packages (e.g. /var/lib/dpkg/info/openssh-server.postinst) +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi + +if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge dvoting.service >/dev/null + deb-systemd-helper purge dvoting-uk.service >/dev/null +fi diff --git a/deb-package/pkg/before-install.sh b/deb-package/pkg/before-install.sh new file mode 100644 index 000000000..64c3fcf11 --- /dev/null +++ b/deb-package/pkg/before-install.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# create dvoting group +if ! getent group dvoting >/dev/null; then + groupadd -r dvoting +fi + +# create dedis group +if ! getent group dedis >/dev/null; then + groupadd -r dedis +fi + +# create dvoting user +if ! getent passwd dvoting >/dev/null; then +useradd -M -r -g dedis -d /var/opt/dedis/dvoting \ +-s /usr/sbin/nologin -c "D-Voting user" dvoting +fi + +# modify user to be in these groups +usermod -aG dedis dvoting diff --git a/deb-package/pkg/before-remove.sh b/deb-package/pkg/before-remove.sh new file mode 100644 index 000000000..dec70f1c2 --- /dev/null +++ b/deb-package/pkg/before-remove.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# stop service +systemctl stop dvoting.service +systemctl stop dvoting-uk.service + +rm -f /usr/bin/memcoin diff --git a/deb-package/pkg/etc/dedis/dvoting/network.env b/deb-package/pkg/etc/dedis/dvoting/network.env new file mode 100644 index 000000000..1c425aac2 --- /dev/null +++ b/deb-package/pkg/etc/dedis/dvoting/network.env @@ -0,0 +1,8 @@ +export uk_gw="172.44.0.1" +export uk_ip="172.44.0.2" +export uk_port="1024" +export bridge_iface="uk0" + +export dela_port="9000" +export prometheus_addr="0.0.0.0:9100" +export proxy_addr="0.0.0.0:9080" diff --git a/deb-package/pkg/lib/systemd/system/dvoting-uk.service b/deb-package/pkg/lib/systemd/system/dvoting-uk.service new file mode 100644 index 000000000..3ca8df804 --- /dev/null +++ b/deb-package/pkg/lib/systemd/system/dvoting-uk.service @@ -0,0 +1,22 @@ +[Unit] +Description=Unikernel Service +After=network.target + +[Service] +User=root + +StandardInput=tty +StandardOutput=tty +# starting unikernel via qemu requires a free tty (tty1 didn't work) +TTYPath=/dev/tty10 + +ExecStartPre=/opt/dedis/dvoting/bin/create-bridge +ExecStart=/opt/dedis/dvoting/bin/start-unikernel + +KillSignal=SIGINT + +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/deb-package/pkg/lib/systemd/system/dvoting.service b/deb-package/pkg/lib/systemd/system/dvoting.service new file mode 100644 index 000000000..a11b14663 --- /dev/null +++ b/deb-package/pkg/lib/systemd/system/dvoting.service @@ -0,0 +1,16 @@ +[Unit] +Description=D-Voting Node Service +After=network.target dvoting-uk.service +Requires=dvoting-uk.service + +[Service] +User=dvoting + +ExecStartPre=/bin/rm -f /var/opt/dedis/dvoting/data/dela/daemon.sockcd +ExecStart=/opt/dedis/dvoting/bin/start-dvoting + +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/deb-package/pkg/opt/dedis/dvoting/bin/create-bridge b/deb-package/pkg/opt/dedis/dvoting/bin/create-bridge new file mode 100755 index 000000000..f83018fc9 --- /dev/null +++ b/deb-package/pkg/opt/dedis/dvoting/bin/create-bridge @@ -0,0 +1,7 @@ +#!/bin/bash + +source /etc/dedis/dvoting/network.env + +echo "Creating bridge ${bridge_iface} with IP address ${uk_gw} ..." +sudo brctl addbr "${bridge_iface}" || true +sudo ifconfig "${bridge_iface}" "${uk_gw}" diff --git a/deb-package/pkg/opt/dedis/dvoting/bin/start-dvoting b/deb-package/pkg/opt/dedis/dvoting/bin/start-dvoting new file mode 100755 index 000000000..d7cf5656f --- /dev/null +++ b/deb-package/pkg/opt/dedis/dvoting/bin/start-dvoting @@ -0,0 +1,9 @@ +#!/bin/bash + +sleep 5 + +source /opt/dedis/dvoting/config/config.env +source /etc/dedis/dvoting/network.env + +echo "Starting ${dela_bin} on port ${dela_port} using folder ${dela_data} ..." +LLVL=info ${dela_bin} --config ${dela_data} start --unikerneladdr ${uk_ip}":"${uk_port} --unikernelfolder ${fs_mount} --externalip --postinstall --promaddr ${prometheus_addr} --proxyaddr ${proxy_addr} --port ${dela_port} diff --git a/deb-package/pkg/opt/dedis/dvoting/bin/start-unikernel b/deb-package/pkg/opt/dedis/dvoting/bin/start-unikernel new file mode 100755 index 000000000..cb437145a --- /dev/null +++ b/deb-package/pkg/opt/dedis/dvoting/bin/start-unikernel @@ -0,0 +1,7 @@ +#!/bin/bash + +source /opt/dedis/dvoting/config/config.env +source /etc/dedis/dvoting/network.env + +echo "Starting KVM image connected to bridge interface ${bridge_iface} ..." +"${qemu_script}" -k "${kvm_image}" -e "${fs_mount}" -b "${bridge_iface}" -m ${qemu_ram} -a "netdev.ipv4_addr=${uk_ip} netdev.ipv4_gw_addr=${uk_gw} netdev.ipv4_subnet_mask=255.255.255.0 -- ${uk_port}" diff --git a/deb-package/pkg/opt/dedis/dvoting/config/config.env b/deb-package/pkg/opt/dedis/dvoting/config/config.env new file mode 100644 index 000000000..96bb18cff --- /dev/null +++ b/deb-package/pkg/opt/dedis/dvoting/config/config.env @@ -0,0 +1,9 @@ +# dela config +export dela_bin="/opt/dedis/dvoting/bin/memcoin" +export dela_data="/var/opt/dedis/dvoting/data/dela" + +# unikernel config +export fs_mount="/var/opt/dedis/dvoting/data/mnt" +export qemu_ram="1024" +export kvm_image="/opt/dedis/dvoting/bin/combine_shares_kvm-x86_64" +export qemu_script="/opt/dedis/dvoting/bin/qemu-guest" diff --git a/deb-package/pkg/var/opt/dedis/dvoting/data/dela/access.json b/deb-package/pkg/var/opt/dedis/dvoting/data/dela/access.json new file mode 100644 index 000000000..c1e898354 --- /dev/null +++ b/deb-package/pkg/var/opt/dedis/dvoting/data/dela/access.json @@ -0,0 +1 @@ +{"\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000":"eyJFeHByZXNzaW9ucyI6eyJnby5kZWRpcy5jaC9kZWxhLkFjY2VzczphbGwiOnsiSWRlbnRpdGllcyI6W3siTmFtZSI6IkJMUy1DVVJWRS1CTjI1NiIsIkRhdGEiOiJRaEx0WXhPTmkvK0JDT25maDVBSHoyRmkraUxOSnFrVllpMGRPdnc4Y053MDBLMytoS25SNEVoWEUrcWE2bzlKK09wbHNjL2JMeDBSVU9NdmdVQ1VQQ0tDRFk0WC9iblBaNEpZblJNdDdmYURCVnhFRGpsVmVmZ3lGZy9MeWpXbGNlZkJvTTJ5Rlh4NWlZNTZyQTZHblBBMUowbE9ZdThibmU2OUZrQUhyVmM9In1dLCJNYXRjaGVzIjpbWzBdXX19fQ=="} diff --git a/deb-package/pkg/var/opt/dedis/dvoting/data/mnt/.keep b/deb-package/pkg/var/opt/dedis/dvoting/data/mnt/.keep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/deb-package/pkg/var/opt/dedis/dvoting/data/mnt/.keep @@ -0,0 +1 @@ + diff --git a/integration/dvotingdela.go b/integration/dvotingdela.go index d7ade9867..f3c0aed04 100644 --- a/integration/dvotingdela.go +++ b/integration/dvotingdela.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "io" "math/rand" + "net" "os" "path/filepath" "strconv" @@ -249,9 +250,13 @@ func newDVotingNode(t require.TestingT, path string, randSource rand.Source) dVo dkg := pedersen.NewPedersen(onet, srvc, pool, electionFac, signer) + newConn := func(network, address string, timeout time.Duration) (net.Conn, error) { + return &fake.Conn{Path: path}, nil + } + rosterKey := [32]byte{} evoting.RegisterContract(exec, evoting.NewContract(evotingAccessKey[:], rosterKey[:], - accessService, dkg, rosterFac, filepath.Join(path, "mnt"), fake.NewConn)) + accessService, dkg, rosterFac, filepath.Join(path, "mnt"), newConn, "")) neffShuffle := neff.NewNeffShuffle(onet, srvc, pool, blocks, electionFac, signer) diff --git a/internal/testing/fake/election.go b/internal/testing/fake/election.go index a6b801097..6b66c035d 100644 --- a/internal/testing/fake/election.go +++ b/internal/testing/fake/election.go @@ -133,6 +133,7 @@ func NewConn(network, address string, timeout time.Duration) (net.Conn, error) { // // - implements net.Conn type Conn struct { + Path string } func (f *Conn) Read(b []byte) (n int, err error) { @@ -153,7 +154,7 @@ func (f *Conn) Write(b []byte) (n int, err error) { folder := string(b[8:]) - err = combineShares(folder, int(numChunks), int(numNodes)) + err = combineShares(filepath.Join(f.Path, folder), int(numChunks), int(numNodes)) if err != nil { return 0, xerrors.Errorf("failed to combine shares: %v", err) } diff --git a/services/dkg/pedersen/controller/mod.go b/services/dkg/pedersen/controller/mod.go index 07e63b0ac..bfebbbb44 100644 --- a/services/dkg/pedersen/controller/mod.go +++ b/services/dkg/pedersen/controller/mod.go @@ -3,6 +3,7 @@ package controller import ( "encoding" "encoding/json" + "net" "path/filepath" "go.dedis.ch/dela/core/txn/pool" @@ -48,6 +49,21 @@ type controller struct{} // Build implements node.Initializer. func (m controller) SetCommands(builder node.Builder) { + builder.SetStartFlags( + cli.StringFlag{ + Name: "unikerneladdr", + Usage: "address of the Unikernel", + Value: "127.0.0.1:1234", + Required: false, + }, + cli.StringFlag{ + Name: "unikernelfolder", + Usage: "absolute path of the shared folder. If set, then will also use a real TCP connection insted of faking the Unikernel.", + Value: "", + Required: false, + }, + ) + electionIDFlag := cli.StringFlag{ Name: "electionID", Usage: "the election ID, formatted in hexadecimal", @@ -185,9 +201,17 @@ func (m controller) OnStart(ctx cli.Flags, inj node.Injector) error { inj.Inject(dkg) + dialer := net.DialTimeout + unikernelFolder := ctx.String("unikernelfolder") + + if unikernelFolder == "" { + unikernelFolder = filepath.Join(ctx.Path("config"), "mnt") + dialer = fake.NewConn + } + rosterKey := [32]byte{} c := evoting.NewContract(evotingAccessKey[:], rosterKey[:], access, dkg, - rosterFac, filepath.Join(ctx.Path("config"), "mnt"), fake.NewConn) + rosterFac, unikernelFolder, dialer, ctx.String("unikerneladdr")) evoting.RegisterContract(exec, c) return nil