diff --git a/README.md b/README.md index ef63b5c..a2dc318 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,23 @@ Delayed-execution Ethereum client. Based on previous work by Shufan Wang. # Architecture -`dela/` is a modified version of `dela`. -`go-ethereum/` is a modified version of `go-ethereum v1.10.23` which integrates with `dela`. +`smc/` contains the Secret Management Committee code and provides the `dkgcli` command. +`go-ethereum/` is a modified version of `go-ethereum v1.10.23` which integrates with the SMC. # Design (WIP) - [x] When creating a transaction, the user agent IBE-encrypts the calldata with dela. - [x] The IBE label is sender address concatenated with big-endian 64-bit nonce - [x] The ciphertext is authenticated (HMAC-SHA256, encrypt-then-MAC) -- [ ] The chain only accepts encrypted transactions +- [x] ~~The chain only accepts encrypted transactions~~ delayed execution for all transactions - [x] The `to` address is encrypted -- [ ] The transaction receipt contains a symmetric encryption key +- [ ] ~~The transaction receipt contains a symmetric encryption key~~ - [ ] The execution layer can direct the SMC node to release an encryption label only after the transaction is finalized. -- [ ] TDH2, PVSS, beacon IBE options maybe +- [ ] ~~TDH2, PVSS, beacon IBE options maybe~~ sticking to pairings for compact decryption proofs - [ ] Direct contract creation doesn't work ? (nil address thing) # Running -Run `setup.sh` to build and install to `$GOPATH` the modified `dela and `go-ethereum`. +Run `setup.sh` to build and install `smc` and the modified `go-ethereum` to `$GOPATH`. Make sure [Foundry](https://getfoundry.sh/) is installed. Run `git submodule update --init --recursive contracts` to make sure the foundry dependencies are ready. diff --git a/dela/.github/workflows/go_lint.yml b/dela/.github/workflows/go_lint.yml deleted file mode 100644 index bfa0855..0000000 --- a/dela/.github/workflows/go_lint.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Go lint - -on: - push: - branches: [ master ] - pull_request: - types: - - opened - - synchronize - - reopened - -jobs: - - lint: - runs-on: ubuntu-latest - steps: - - name: Set up Go ^1.19 - uses: actions/setup-go@v3 - with: - go-version: ^1.19 - - - name: Check out code into the Go module directory - uses: actions/checkout@v3 - - - name: Lint - run: make lint - - - name: Vet - run: make vet \ No newline at end of file diff --git a/dela/.github/workflows/go_test.yml b/dela/.github/workflows/go_test.yml deleted file mode 100644 index a1a36db..0000000 --- a/dela/.github/workflows/go_test.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Go test - -on: - push: - branches: [ master ] - pull_request: - types: - - opened - - synchronize - - reopened - -jobs: - - test: - strategy: - matrix: - platform: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{matrix.platform}} - env: - LLVL: trace - steps: - - name: Set up Go ^1.19 - uses: actions/setup-go@v3 - with: - go-version: ^1.19 - - - name: Check out code into the Go module directory - uses: actions/checkout@v3 - - - name: Test without coverage - env: - CRY_LVL: "warn" - if: matrix.platform == 'macos-latest' || matrix.platform == 'windows-latest' - run: make test - - - name: Test with coverage - env: - CRY_LVL: "warn" - if: matrix.platform == 'ubuntu-latest' - run: make coverage - - - name: Sonarcloud scan - if: matrix.platform == 'ubuntu-latest' - uses: sonarsource/sonarcloud-github-action@master - with: - args: > - -Dsonar.organization=dedis - -Dsonar.projectKey=dedis_dela - -Dsonar.go.tests.reportPaths=report.json - -Dsonar.go.coverage.reportPaths=profile.cov - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - - name: Send coverage - if: matrix.platform == 'ubuntu-latest' - uses: shogo82148/actions-goveralls@v1 - with: - path-to-profile: profile.cov - parallel: true - - # notifies that all test jobs are finished. - finish: - needs: test - runs-on: ubuntu-latest - steps: - - uses: shogo82148/actions-goveralls@v1 - with: - parallel-finished: true \ No newline at end of file diff --git a/dela/.gitignore b/dela/.gitignore deleted file mode 100644 index dd92724..0000000 --- a/dela/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.DS_Store -cli/node/memcoin/memcoin -test/private.key -dkg/logs -dkg/pedersen/dkgcli/dkgcli diff --git a/dela/.gitrepo b/dela/.gitrepo deleted file mode 100644 index dbc8cb6..0000000 --- a/dela/.gitrepo +++ /dev/null @@ -1,12 +0,0 @@ -; DO NOT EDIT (unless you know what you are doing) -; -; This subdirectory is a git "subrepo", and this file is maintained by the -; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme -; -[subrepo] - remote = ssh://git@gitlab.epfl.ch/bettens-f3b/dela_f3b_ibe - branch = main - commit = 868de3cd65a53b2583bd962ff8d2d18f48f4f39d - parent = 4557afa284434fb8b3e71618db9e1facdf25ea20 - method = merge - cmdver = 0.4.6 diff --git a/dela/Dockerfile b/dela/Dockerfile deleted file mode 100644 index 5ed0823..0000000 --- a/dela/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# Specifies a parent image -FROM golang:1.20-alpine - -RUN apk add cmd:bash cmd:tmux cmd:xxd - -# Creates an app directory to hold your app’s source code -WORKDIR /app - -# Copies everything from your root directory into /app -COPY ./ /app/ - -# Installs Go dependencies -RUN go install ./dkg/pedersen_bn256/dkgcli diff --git a/dela/LICENSE b/dela/LICENSE deleted file mode 100644 index 2955582..0000000 --- a/dela/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2022, Decentralized and Distributed Systems Research Lab at EPFL -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dela/Makefile b/dela/Makefile deleted file mode 100644 index e1ed898..0000000 --- a/dela/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -.PHONY: all tidy generate lint vet test coverage pushdoc - -# Default "make" target to check locally that everything is ok, BEFORE pushing remotely -all: lint vet test - @echo "Done with the standard checks" - -tidy: - go mod tidy - -generate: tidy - go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.5 - go generate ./... - -# Some packages are excluded from staticcheck due to deprecated warnings: #208. -lint: tidy - # Coding style static check. - @go install honnef.co/go/tools/cmd/staticcheck@latest - staticcheck `go list ./... | grep -Ev "(go\.dedis\.ch/dela/internal/testing|go\.dedis\.ch/dela/mino/minogrpc/ptypes)"` - -vet: tidy - @echo "⚠️ Warning: the following only works with go >= 1.14" && \ - go install ./internal/mcheck && \ - go vet -vettool=`go env GOPATH`/bin/mcheck -commentLen -ifInit ./... - -# test runs all tests in DELA without coverage -test: tidy - go test ./... - -# test runs all tests in DELA and generate a coverage output (to be used by sonarcloud) -coverage: tidy - go test -json -covermode=count -coverprofile=profile.cov ./... | tee report.json - -# https://pkg.go.dev/go.dedis.ch/dela needs to be updated on the Go proxy side -# to get the latest master. This command refreshes the proxy with the latest -# commit on the upstream master branch. -# Note: CURL must be installed -pushdoc: - @echo "Requesting the proxy..." - @curl "https://proxy.golang.org/go.dedis.ch/dela/@v/$(shell git log origin/master -1 --format=format:%H).info" - @echo "\nDone." \ No newline at end of file diff --git a/dela/README.md b/dela/README.md deleted file mode 100644 index 454e752..0000000 --- a/dela/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# DELA F3B-IBE - -Software repository delivered as part of the "Optimizing Frontrunning Protection" research project at @dedis. - -The main point of interest is [dkg/pedersen_bn256](./dkg/pedersen_bn256). - -## Demo - -A simple demo simulating a frontrunning protected decentralized exchange is available. -Simply run `docker compose run demo` from the top-level of the repository. -Alternatively, `./demo.sh` can be run directly inside [tmux], -with the dependencies being Vim 9.0 (for `xxd`) and Go. - -[tmux]: https://tmux.github.io - -## Code Coverage - -``` -$ go test -covermode=count -coverprofile=profile.cov ./dkg/pedersen_bn256/ -ok go.dedis.ch/dela/dkg/pedersen_bn256 93.347s coverage: 96.7% of statements -``` diff --git a/dela/cli/cli.go b/dela/cli/cli.go deleted file mode 100644 index 0c8cdaf..0000000 --- a/dela/cli/cli.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package cli defines the Builder type, which allows one to build a CLI -// application in a modular way. -// -// var builder Builder -// builder.SetName("myapp") -// -// cmd := builder.SetCommand("hello") -// cmd.SetDescription("Say hello !") -// cmd.SetAction(func(flags Flags) error { -// fmt.Printf("Hello %s!\n", flags.String("dude")) -// }) -// -// builder.Build().Run(os.Args) -// -// An implementation of the builder is free to provide primitives to create more -// complex action. -// -// Documentation Last Review: 13.10.2020 -package cli - -import ( - "time" -) - -// Builder is an application builder interface. One can set properties of an -// application then build it. -type Builder interface { - Provider - - // Build returns the application. - Build() Application -} - -// Application is the main interface to run the CLI. -type Application interface { - Run(arguments []string) error -} - -// CommandBuilder is a command builder interface. One can set properties of a -// specific command like its name and description and what it should do when -// invoked. -type CommandBuilder interface { - // SetDescription sets the value of the description for this command. - SetDescription(value string) - - // SetFlags sets the flags for this command. - SetFlags(...Flag) - - // SetAction sets the action for this command. - SetAction(Action) - - // SetSubCommand creates a subcommand for this command. - SetSubCommand(name string) CommandBuilder -} - -// Action is a function that will be executed when a command is invoked. -type Action func(Flags) error - -// Flag is an identifier for the definition of the flags. -type Flag interface { - Flag() -} - -// Flags provides the primitives to an action to read the flags. -type Flags interface { - String(name string) string - - StringSlice(name string) []string - - Duration(name string) time.Duration - - Path(name string) string - - Int(name string) int - - Bool(name string) bool -} - -// Initializer defines a primitive for modules to add their commands. A cli will -// gather all the initializers from each desired modules and call the -// SetCommands for each of them. -type Initializer interface { - // SetCommands if the function called by the builder to add the modules' - // commands. The modules implement this function and use the provided - // provider to create its specific commands. - SetCommands(Provider) -} - -// Provider defines a primitive for modules to provide their commands -type Provider interface { - // SetCommand creates a new command with the given name and returns its - // builder. - SetCommand(name string) CommandBuilder -} diff --git a/dela/cli/crypto/crypto.go b/dela/cli/crypto/crypto.go deleted file mode 100644 index 965c936..0000000 --- a/dela/cli/crypto/crypto.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package main provides a cli for crypto operations like generating keys or -// displaying specific key formats. -package main - -import ( - "fmt" - "io" - "os" - - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/ucli" - bls "go.dedis.ch/dela/crypto/bls/command" -) - -var builder cli.Builder = ucli.NewBuilder("crypto", nil) -var printer io.Writer = os.Stderr - -func main() { - err := run(os.Args, bls.Initializer{}) - if err != nil { - fmt.Fprintf(printer, "%+v\n", err) - } -} - -func run(args []string, inits ...cli.Initializer) error { - for _, init := range inits { - init.SetCommands(builder) - } - - app := builder.Build() - err := app.Run(args) - if err != nil { - return err - } - - return nil -} diff --git a/dela/cli/crypto/crypto_test.go b/dela/cli/crypto/crypto_test.go deleted file mode 100644 index 2946c30..0000000 --- a/dela/cli/crypto/crypto_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" -) - -func TestMain_Happy(t *testing.T) { - oldPrinter := printer - defer func() { - printer = oldPrinter - }() - - builder = &fakeBuilder{} - buf := new(bytes.Buffer) - printer = buf - - main() - - require.Empty(t, buf) -} - -func TestMain_Error(t *testing.T) { - oldPrinter := printer - defer func() { - printer = oldPrinter - }() - - builder = &fakeBuilder{err: errors.New("fake")} - buf := new(bytes.Buffer) - printer = buf - - main() - require.Equal(t, "fake\n", buf.String()) -} - -func TestRun(t *testing.T) { - b := &fakeBuilder{} - builder = b - init := &fakeInit{} - - err := run([]string{"crypto"}, init) - require.NoError(t, err) - - require.True(t, b.called) - require.True(t, init.called) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeBuilder struct { - cli.Builder - err error - called bool -} - -func (f *fakeBuilder) Build() cli.Application { - f.called = true - return &fakeApp{err: f.err} -} - -func (f *fakeBuilder) SetCommand(name string) cli.CommandBuilder { - return fakeCommandBuilder{} -} - -type fakeCommandBuilder struct { -} - -func (b fakeCommandBuilder) SetSubCommand(name string) cli.CommandBuilder { - return b -} - -func (b fakeCommandBuilder) SetDescription(value string) { -} - -func (b fakeCommandBuilder) SetFlags(flags ...cli.Flag) { -} - -func (b fakeCommandBuilder) SetAction(a cli.Action) { -} - -type fakeApp struct { - err error -} - -func (f fakeApp) Run(arguments []string) error { - return f.err -} - -type fakeInit struct { - called bool -} - -func (f *fakeInit) SetCommands(cli.Provider) { - f.called = true -} diff --git a/dela/cli/flag.go b/dela/cli/flag.go deleted file mode 100644 index 78fda33..0000000 --- a/dela/cli/flag.go +++ /dev/null @@ -1,72 +0,0 @@ -package cli - -import "time" - -// StringFlag is a definition of a command flag expected to be parsed as a -// string. -// -// - implements cli.Flag -type StringFlag struct { - Name string - Usage string - Required bool - Value string -} - -// Flag implements cli.Flag. -func (flag StringFlag) Flag() {} - -// StringSliceFlag is a definition of a command flag expected to tbe parsed as a -// slice of strings. -// -// - implements cli.Flag -type StringSliceFlag struct { - Name string - Usage string - Required bool - Value []string -} - -// Flag implements cli.Flag. -func (flag StringSliceFlag) Flag() {} - -// DurationFlag is a definition of a command flag expected to be parsed as a -// duration. -// -// - implements cli.Flag -type DurationFlag struct { - Name string - Usage string - Required bool - Value time.Duration -} - -// Flag implements cli.Flag. -func (flag DurationFlag) Flag() {} - -// IntFlag is a definition of a command flag expected to be parsed as a integer. -// -// - implements cli.Flag -type IntFlag struct { - Name string - Usage string - Required bool - Value int -} - -// Flag implements cli.Flag. -func (flag IntFlag) Flag() {} - -// BoolFlag is a definition of a command flag expected to be parsed as a -// boolean. -// -// - implements cli.Flag -type BoolFlag struct { - Name string - Usage string - Required bool - Value bool -} - -// Flag implements cli.Flag. -func (flag BoolFlag) Flag() {} diff --git a/dela/cli/node/builder.go b/dela/cli/node/builder.go deleted file mode 100644 index 9467773..0000000 --- a/dela/cli/node/builder.go +++ /dev/null @@ -1,247 +0,0 @@ -// This file contains the implementation of a CLI builder. -// -// Documentation Last Review: 13.10.20202 -// - -package node - -import ( - "encoding/binary" - "encoding/json" - "io" - "os" - "os/signal" - "syscall" - - urfave "github.com/urfave/cli/v2" - "go.dedis.ch/dela" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/ucli" - "golang.org/x/xerrors" -) - -// CLIBuilder is an application builder that will build a CLI to start and -// control a node. -// -// - implements node.Builder -// - implements cli.Builder -type CLIBuilder struct { - cli.Builder - - daemonFactory DaemonFactory - injector Injector - actions *actionMap - startFlags []cli.Flag - inits []Initializer - writer io.Writer - - // In production, the daemon is stopped via SIGTERM. In case of testing, the - // channel will be closed instead, because of instability. - enableSignal bool - sigs chan os.Signal -} - -// NewBuilder returns a new empty builder. -func NewBuilder(inits ...Initializer) *CLIBuilder { - return NewBuilderWithCfg(nil, nil, inits...) -} - -// NewBuilderWithCfg returns a new empty builder with specific configurations. -func NewBuilderWithCfg(sigs chan os.Signal, out io.Writer, inits ...Initializer) *CLIBuilder { - if out == nil { - out = os.Stdout - } - - enabled := false - - if sigs == nil { - sigs = make(chan os.Signal, 1) - enabled = true - } - - injector := NewInjector() - - actions := &actionMap{} - - factory := socketFactory{ - injector: injector, - actions: actions, - out: out, - } - - // We are using urfave cli builder - builder := ucli.NewBuilder("Dela", nil, cli.StringFlag{ - Name: "config", - Usage: "path to the config folder", - Value: ".dela", - }) - - return &CLIBuilder{ - Builder: builder, - injector: injector, - actions: actions, - daemonFactory: factory, - enableSignal: enabled, - sigs: sigs, - inits: inits, - writer: out, - } -} - -// SetStartFlags implements node.Builder. It appends the given flags to the list -// of flags that will be used to create the start command. -func (b *CLIBuilder) SetStartFlags(flags ...cli.Flag) { - b.startFlags = append(b.startFlags, flags...) -} - -// MakeAction implements node.Builder. It creates a CLI action from the -// template. -func (b *CLIBuilder) MakeAction(tmpl ActionTemplate) cli.Action { - index := b.actions.Set(tmpl) - - return func(c cli.Flags) error { - client, err := b.daemonFactory.ClientFromContext(c) - if err != nil { - return xerrors.Errorf("couldn't make client: %v", err) - } - - // Encode the action ID over 2 bytes. - id := make([]byte, 2) - binary.LittleEndian.PutUint16(id, index) - - // Prepare a set of flags that will be transmitted to the daemon so that - // the action has access to the same flags and their values. - fset := make(FlagSet) - lookupFlags(fset, c.(*urfave.Context)) - - buf, err := json.Marshal(fset) - if err != nil { - return xerrors.Errorf("failed to marshal flag set: %v", err) - } - - err = client.Send(append(id, buf...)) - if err != nil { - return xerrors.Opaque(err) - } - - return nil - } -} - -func lookupFlags(fset FlagSet, ctx *urfave.Context) { - for _, ancestor := range ctx.Lineage() { - if ancestor.Command != nil { - fill(fset, ancestor.Command.Flags, ancestor) - } - - if ancestor.App != nil { - fill(fset, ancestor.App.Flags, ancestor) - } - } -} - -func fill(fset FlagSet, flags []urfave.Flag, ctx *urfave.Context) { - for _, flag := range flags { - names := flag.Names() - if len(names) > 0 { - fset[names[0]] = convert(ctx.Value(names[0])) - } - } -} - -func convert(v interface{}) interface{} { - switch value := v.(type) { - case urfave.StringSlice: - // StringSlice is an edge-case as it won't serialize correctly with JSON - // so we ask for the actual []string to allow a correct serialization. - return value.Value() - default: - return v - } -} - -// Build implements node.Builder. It returns the application. -func (b *CLIBuilder) Build() cli.Application { - for _, controller := range b.inits { - controller.SetCommands(b) - } - - cmd := b.SetCommand("start") - cmd.SetDescription("start the deamon") - cmd.SetFlags(b.startFlags...) - cmd.SetAction(b.start) - - return b.Builder.Build() -} - -func (b *CLIBuilder) start(flags cli.Flags) error { - if b.enableSignal { - signal.Notify(b.sigs, syscall.SIGINT, syscall.SIGTERM) - - defer signal.Stop(b.sigs) - } - - dir := flags.Path("config") - if dir != "" { - err := os.MkdirAll(dir, 0700) - if err != nil { - return xerrors.Errorf("couldn't make path: %v", err) - } - } - - daemon, err := b.daemonFactory.DaemonFromContext(flags) - if err != nil { - return xerrors.Errorf("couldn't make daemon: %v", err) - } - - for _, controller := range b.inits { - err = controller.OnStart(flags, b.injector) - if err != nil { - return xerrors.Errorf("couldn't run the controller: %v", err) - } - } - - // Daemon is started after the controllers so that everything has started - // when the daemon is available. - err = daemon.Listen() - if err != nil { - return xerrors.Errorf("couldn't start the daemon: %v", err) - } - - defer daemon.Close() - - <-b.sigs - signal.Stop(b.sigs) - - // Controllers are stopped in reverse order so that high level components - // are stopped before lower level ones (i.e. stop a service before the - // database to avoid errors). - for i := len(b.inits) - 1; i >= 0; i-- { - err = b.inits[i].OnStop(b.injector) - if err != nil { - return xerrors.Errorf("couldn't stop controller: %v", err) - } - } - - dela.Logger.Trace().Msg("daemon has been stopped") - - return nil -} - -// ActionMap stores actions and assigns a unique index to each. -type actionMap struct { - list []ActionTemplate -} - -func (m *actionMap) Set(a ActionTemplate) uint16 { - m.list = append(m.list, a) - return uint16(len(m.list) - 1) -} - -func (m *actionMap) Get(index uint16) ActionTemplate { - if int(index) >= len(m.list) { - return nil - } - - return m.list[index] -} diff --git a/dela/cli/node/builder_test.go b/dela/cli/node/builder_test.go deleted file mode 100644 index 65fd634..0000000 --- a/dela/cli/node/builder_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package node - -import ( - "flag" - "syscall" - "testing" - - "github.com/stretchr/testify/require" - urfave "github.com/urfave/cli/v2" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/ucli" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestCliBuilder_SetStartFlags(t *testing.T) { - builder := &CLIBuilder{} - - builder.SetStartFlags(cli.StringFlag{}, cli.IntFlag{}) - require.Len(t, builder.startFlags, 2) -} - -func TestCliBuilder_Start(t *testing.T) { - builder := NewBuilder(fakeInitializer{}) - - builder.sigs <- syscall.SIGTERM - close(builder.sigs) - - err := builder.start(FlagSet{}) - require.NoError(t, err) -} - -func TestCliBuilder_ForbiddenFolder_Start(t *testing.T) { - builder := NewBuilder(fakeInitializer{}) - - fset := flag.NewFlagSet("", 0) - fset.String("config", "\x00", "") - - ctx := urfave.NewContext(nil, fset, nil) - - err := builder.start(ctx) - require.Error(t, err) - require.Contains(t, err.Error(), "couldn't make path: mkdir \x00: ") - -} - -func TestCliBuilder_FailedDaemon_Start(t *testing.T) { - builder := NewBuilder(fakeInitializer{}) - - builder.daemonFactory = fakeFactory{err: fake.GetError()} - - err := builder.start(FlagSet{}) - require.EqualError(t, err, fake.Err("couldn't make daemon")) -} - -func TestCliBuilder_FailStartDaemon_Start(t *testing.T) { - builder := NewBuilder(fakeInitializer{}) - - builder.daemonFactory = fakeFactory{errDaemon: fake.GetError()} - - err := builder.start(FlagSet{}) - require.EqualError(t, err, fake.Err("couldn't start the daemon")) -} - -func TestCliBuilder_FailStartComponent_Start(t *testing.T) { - builder := NewBuilder(fakeInitializer{err: fake.GetError()}) - - err := builder.start(FlagSet{}) - require.EqualError(t, err, fake.Err("couldn't run the controller")) -} - -func TestCliBuilder_FailStopComponent_Start(t *testing.T) { - builder := NewBuilder(fakeInitializer{errStop: fake.GetError()}) - builder.enableSignal = false - close(builder.sigs) - - err := builder.start(FlagSet{}) - require.EqualError(t, err, fake.Err("couldn't stop controller")) -} - -func TestCliBuilder_MakeAction(t *testing.T) { - calls := &fake.Call{} - builder := &CLIBuilder{ - actions: &actionMap{}, - daemonFactory: fakeFactory{calls: calls}, - } - - fset := flag.NewFlagSet("", 0) - fset.Var(urfave.NewStringSlice("item 1", "item 2"), "flag-1", "") - fset.Int("flag-2", 20, "") - - ctx := urfave.NewContext(makeApp(), fset, nil) - - err := builder.MakeAction(fakeAction{})(ctx) - require.NoError(t, err) - - data := string(calls.Get(0, 0).([]byte)) - require.Equal(t, "\x00\x00"+`{"flag-1":["item 1","item 2"],"flag-2":20}`, data) - - builder.daemonFactory = fakeFactory{err: fake.GetError()} - err = builder.MakeAction(fakeAction{})(ctx) - require.EqualError(t, err, fake.Err("couldn't make client")) - - builder.daemonFactory = fakeFactory{errClient: fake.GetError()} - err = builder.MakeAction(fakeAction{})(ctx) - require.EqualError(t, err, fake.GetError().Error()) -} - -func TestCliBuilder_Build(t *testing.T) { - builder := &CLIBuilder{ - Builder: ucli.NewBuilder("test", nil), - actions: &actionMap{}, - daemonFactory: fakeFactory{}, - inits: []Initializer{fakeInitializer{}}, - } - - cb := builder.SetCommand("test") - cb.SetDescription("test description") - cb.SetAction(builder.MakeAction(fakeAction{})) - cb.SetFlags(cli.StringFlag{Name: "string-flag"}) - - sub := cb.SetSubCommand("subtest") - sub.SetDescription("subtest description") - sub.SetFlags(cli.DurationFlag{}, cli.IntFlag{}, cli.StringSliceFlag{}) - - cb = builder.SetCommand("another") - cb.SetAction(func(cli.Flags) error { - return nil - }) - - cb = builder.SetCommand("last") - cb.SetAction(func(cli.Flags) error { - return nil - }) - - // Build will add the start command, which is why we are expecting 5. - app := builder.Build().(*urfave.App) - require.Len(t, app.Commands, 5) -} - -func TestCliBuilder_UnknownType_BuildFlags(t *testing.T) { - defer func() { - r := recover() - require.Equal(t, "flag type '' not supported", r) - }() - - builder := &CLIBuilder{Builder: ucli.NewBuilder("test", nil)} - builder.SetStartFlags((cli.Flag)(nil)) - - builder.Build() -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeApp() *urfave.App { - return &urfave.App{ - Flags: []urfave.Flag{ - &urfave.StringSliceFlag{Name: "flag-1"}, - &urfave.IntFlag{Name: "flag-2"}, - }, - } -} diff --git a/dela/cli/node/daemon.go b/dela/cli/node/daemon.go deleted file mode 100644 index 8a38fcd..0000000 --- a/dela/cli/node/daemon.go +++ /dev/null @@ -1,283 +0,0 @@ -// This file contains the implementation of a client and a daemon talking -// through a UNIX socket. -// -// Documentation Last Review: 13.10.2020 -// - -package node - -import ( - "encoding/binary" - "encoding/json" - "fmt" - "io" - "net" - "path/filepath" - "sync" - "time" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/cli" - "golang.org/x/xerrors" -) - -const ioTimeout = 30 * time.Second - -// event is the structure sent over the connection between the client and the -// daemon and vice-versa using a JSON encoding. -type event struct { - Err bool - Value string -} - -// SocketClient opens a connection to a unix socket daemon to send commands. -// -// - implements node.Client -type socketClient struct { - socketpath string - out io.Writer - dialTimeout time.Duration - dialFn func(network, addr string, timeout time.Duration) (net.Conn, error) -} - -// Send implements node.Client. It opens a connection and sends the data to the -// daemon. It writes the result of the command to the output. -func (c socketClient) Send(data []byte) error { - conn, err := c.dialFn("unix", c.socketpath, c.dialTimeout) - if err != nil { - return xerrors.Errorf("couldn't open connection: %v", err) - } - - defer conn.Close() - - _, err = conn.Write(data) - if err != nil { - return xerrors.Errorf("couldn't write to daemon: %v", err) - } - - // The client will now wait for incoming messages from the daemon, either - // results of the command, or an error if something goes wrong. - dec := json.NewDecoder(conn) - var evt event - - for { - err = dec.Decode(&evt) - if err == io.EOF { - return nil - } - if err != nil { - return xerrors.Errorf("fail to decode event: %v", err) - } - - if evt.Err { - return xerrors.New(evt.Value) - } - - fmt.Fprintln(c.out, evt.Value) - } -} - -// SocketDaemon is a daemon using UNIX socket. This allows the permissions to be -// managed by the filesystem. A user must have read/write access to send a -// command to the daemon. -// -// - implements node.Daemon -type socketDaemon struct { - sync.WaitGroup - - logger zerolog.Logger - socketpath string - injector Injector - actions *actionMap - closing chan struct{} - readTimeout time.Duration - listenFn func(network, addr string) (net.Listener, error) -} - -// Listen implements node.Daemon. It starts the daemon by creating the unix -// socket file to the path. -func (d *socketDaemon) Listen() error { - socket, err := d.listenFn("unix", d.socketpath) - if err != nil { - return xerrors.Errorf("couldn't bind socket: %v", err) - } - - d.Add(2) - - go func() { - defer d.Done() - - <-d.closing - socket.Close() - }() - - go func() { - defer d.Done() - - for { - fd, err := socket.Accept() - if err != nil { - select { - case <-d.closing: - default: - dela.Logger.Err(err).Msg("daemon closed unexpectedly") - } - return - } - - go d.handleConn(fd) - } - }() - - return nil -} - -func (d *socketDaemon) handleConn(conn net.Conn) { - defer conn.Close() - - d.logger.Trace().Msg("daemon is handling a connection") - - // Read the first two bytes that will be converted into the action ID. - buffer := make([]byte, 2) - - conn.SetReadDeadline(time.Now().Add(d.readTimeout)) - - _, err := conn.Read(buffer) - if err == io.EOF { - // Connection closed upfront so it does not need further handling. This - // happens for instance when testing the connectivity of the daemon. - return - } - if err != nil { - d.sendError(conn, xerrors.Errorf("stream corrupted: %v", err)) - return - } - - dec := json.NewDecoder(conn) - - fset := make(FlagSet) - err = dec.Decode(&fset) - if err != nil { - d.sendError(conn, xerrors.Errorf("failed to decode flags: %v", err)) - return - } - - d.logger.Debug(). - Hex("command", buffer). - Str("flags", fmt.Sprintf("%v", fset)). - Msg("received command on the daemon") - - id := binary.LittleEndian.Uint16(buffer) - action := d.actions.Get(id) - - if action == nil { - d.sendError(conn, xerrors.Errorf("unknown command '%d'", id)) - return - } - - actx := Context{ - Injector: d.injector, - Flags: fset, - Out: newClientWriter(conn), - } - - err = action.Execute(actx) - if err != nil { - d.sendError(conn, xerrors.Errorf("command error: %v", err)) - return - } -} - -func (d *socketDaemon) sendError(conn net.Conn, err error) { - enc := json.NewEncoder(conn) - - d.logger.Debug().Err(err).Msg("sending error to client") - - // The event contains an error which will make the command on the client - // side fail with the value as the error message. - err = enc.Encode(event{Err: true, Value: err.Error()}) - if err != nil { - d.logger.Warn().Err(err).Msg("connection to daemon has error") - } -} - -// Close implements node.Daemon. It closes the daemon and waits for the go -// routines to close. -func (d *socketDaemon) Close() error { - close(d.closing) - d.Wait() - - return nil -} - -// clientWriter is a wrapper around a socket connection that will write the data -// using a JSON message wrapper. -// -// - implements io.Writer -type clientWriter struct { - enc *json.Encoder -} - -func newClientWriter(w io.Writer) *clientWriter { - return &clientWriter{ - enc: json.NewEncoder(w), - } -} - -// Write implements io.Writer. It wraps the data into a JSON message that is -// written to the parent writer. The number of written bytes returned -// corresponds to the input if successful. -func (w *clientWriter) Write(data []byte) (int, error) { - err := w.enc.Encode(event{Value: string(data)}) - if err != nil { - return 0, xerrors.Errorf("while packing data: %v", err) - } - - return len(data), nil -} - -// SocketFactory provides primitives to create a daemon and clients from a CLI -// context. -// -// - implements node.DaemonFactory -type socketFactory struct { - injector Injector - actions *actionMap - out io.Writer -} - -// ClientFromContext implements node.DaemonFactory. It creates a client based on -// the flags of the context. -func (f socketFactory) ClientFromContext(ctx cli.Flags) (Client, error) { - client := socketClient{ - socketpath: f.getSocketPath(ctx), - out: f.out, - dialTimeout: ioTimeout, - dialFn: net.DialTimeout, - } - - return client, nil -} - -// DaemonFromContext implements node.DaemonFactory. It creates a daemon based on -// the flags of the context. -func (f socketFactory) DaemonFromContext(ctx cli.Flags) (Daemon, error) { - socketpath := f.getSocketPath(ctx) - - daemon := &socketDaemon{ - logger: dela.Logger.With().Str("daemon", socketpath).Logger(), - socketpath: socketpath, - injector: f.injector, - actions: f.actions, - closing: make(chan struct{}), - readTimeout: ioTimeout, - listenFn: net.Listen, - } - - return daemon, nil -} - -func (f socketFactory) getSocketPath(ctx cli.Flags) string { - return filepath.Join(ctx.Path("config"), "daemon.sock") -} diff --git a/dela/cli/node/daemon_test.go b/dela/cli/node/daemon_test.go deleted file mode 100644 index 04ee0db..0000000 --- a/dela/cli/node/daemon_test.go +++ /dev/null @@ -1,344 +0,0 @@ -package node - -import ( - "bytes" - "encoding/json" - "net" - "os" - "path/filepath" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/internal/testing/fake" - "golang.org/x/xerrors" -) - -func TestSocketClient_Send(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - out := new(bytes.Buffer) - - client := socketClient{ - socketpath: filepath.Join(dir, "daemon.sock"), - out: out, - dialFn: net.DialTimeout, - } - - listen(t, client.socketpath) - - err = client.Send([]byte("deadbeef")) - require.NoError(t, err) - require.Equal(t, "deadbeef\n", out.String()) -} - -func TestSocketClient_FailDial_Send(t *testing.T) { - client := socketClient{ - socketpath: "", - dialFn: func(network, addr string, timeout time.Duration) (net.Conn, error) { - return nil, fake.GetError() - }, - } - - err := client.Send(nil) - require.EqualError(t, err, fake.Err("couldn't open connection")) -} - -func TestSocketClient_BadOutConn_Send(t *testing.T) { - client := socketClient{ - dialFn: func(network, addr string, timeout time.Duration) (net.Conn, error) { - return badConn{}, nil - }, - } - - err := client.Send([]byte{1, 2, 3}) - require.EqualError(t, err, fake.Err("couldn't write to daemon")) -} - -func TestSocketClient_BadInConn_Send(t *testing.T) { - client := socketClient{ - dialFn: func(network, addr string, timeout time.Duration) (net.Conn, error) { - return badConn{counter: fake.NewCounter(1)}, nil - }, - } - - err := client.Send([]byte{}) - require.EqualError(t, err, fake.Err("fail to decode event")) -} - -func TestSocketDaemon_Listen(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - fset := make(FlagSet) - fset["1"] = 1 - - buf, err := json.Marshal(&fset) - require.NoError(t, err) - - actions := &actionMap{} - actions.Set(fakeAction{ - intFlags: map[string]int{"1": 1}, - }) // id 0 - actions.Set(fakeAction{err: fake.GetError()}) // id 1 - - daemon := &socketDaemon{ - socketpath: filepath.Join(dir, "daemon.sock"), - actions: actions, - closing: make(chan struct{}), - readTimeout: 50 * time.Millisecond, - listenFn: net.Listen, - } - - err = daemon.Listen() - require.NoError(t, err) - - defer daemon.Close() - - out := new(bytes.Buffer) - client := socketClient{ - socketpath: daemon.socketpath, - out: out, - dialTimeout: time.Second, - dialFn: net.DialTimeout, - } - - err = client.Send(append([]byte{0x0, 0x0}, buf...)) - require.NoError(t, err) - require.Equal(t, "deadbeef\n", out.String()) - - err = client.Send(append([]byte{0x1, 0x0}, []byte("{}")...)) - require.EqualError(t, err, fake.Err("command error")) - - err = client.Send(append([]byte{0x2, 0x0}, []byte("{}")...)) - require.EqualError(t, err, "unknown command '2'") - - err = client.Send([]byte{0x0, 0x0, 0x0}) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to decode flags") - - err = client.Send([]byte{}) - require.Error(t, err) - require.Contains(t, err.Error(), "stream corrupted: ") -} - -func TestSocketDaemon_ConnectivityTest_Listen(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - daemon := &socketDaemon{ - socketpath: filepath.Join(dir, "daemon.sock"), - actions: &actionMap{}, - closing: make(chan struct{}), - readTimeout: 50 * time.Millisecond, - listenFn: net.Listen, - } - - err = daemon.Listen() - require.NoError(t, err) - - defer daemon.Close() - - conn, err := net.DialTimeout("unix", daemon.socketpath, 1*time.Second) - require.NoError(t, err) - require.NoError(t, conn.Close()) -} - -func TestSocketDaemon_FailBindSocket_Listen(t *testing.T) { - daemon := &socketDaemon{ - listenFn: func(network, addr string) (net.Listener, error) { - return nil, fake.GetError() - }, - } - - err := daemon.Listen() - require.EqualError(t, err, fake.Err("couldn't bind socket")) -} - -func TestSocketDaemon_ConnClosedFromClient_HandleConn(t *testing.T) { - logger, check := fake.CheckLog("connection to daemon has error") - - daemon := &socketDaemon{ - logger: logger, - actions: &actionMap{}, - closing: make(chan struct{}), - readTimeout: 50 * time.Millisecond, - } - - daemon.handleConn(badConn{}) - - check(t) -} - -func TestClientWriter_Write(t *testing.T) { - buffer := new(bytes.Buffer) - - w := newClientWriter(buffer) - - n, err := w.Write([]byte("deadbeef")) - require.NoError(t, err) - require.Equal(t, 8, n) -} - -func TestClientWriter_BadWriter_Write(t *testing.T) { - w := newClientWriter(fake.NewBadHash()) - - n, err := w.Write([]byte("deadbeef")) - require.Equal(t, 0, n) - require.EqualError(t, err, fake.Err("while packing data")) -} - -func TestSocketFactory_ClientFromContext(t *testing.T) { - factory := socketFactory{} - - client, err := factory.ClientFromContext(fakeContext{path: "cfgdir"}) - require.NoError(t, err) - require.NotNil(t, client) - require.Equal(t, filepath.Join("cfgdir", "daemon.sock"), - client.(socketClient).socketpath) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func listen(t *testing.T, path string) { - socket, err := net.Listen("unix", path) - require.NoError(t, err) - - go func() { - conn, err := socket.Accept() - require.NoError(t, err) - - defer conn.Close() - defer socket.Close() - - buffer := make([]byte, 100) - n, err := conn.Read(buffer) - require.NoError(t, err) - - enc := json.NewEncoder(conn) - err = enc.Encode(event{Value: string(buffer[:n])}) - require.NoError(t, err) - }() -} - -type fakeInitializer struct { - err error - errStop error -} - -func (c fakeInitializer) SetCommands(Builder) {} - -func (c fakeInitializer) OnStart(cli.Flags, Injector) error { - return c.err -} - -func (c fakeInitializer) OnStop(Injector) error { - return c.errStop -} - -type fakeClient struct { - err error - calls *fake.Call -} - -func (c fakeClient) Send(data []byte) error { - c.calls.Add(data) - return c.err -} - -type fakeDaemon struct { - Daemon - err error -} - -func (d fakeDaemon) Listen() error { - return d.err -} - -type fakeFactory struct { - DaemonFactory - err error - errClient error - errDaemon error - calls *fake.Call -} - -func (f fakeFactory) ClientFromContext(cli.Flags) (Client, error) { - return fakeClient{err: f.errClient, calls: f.calls}, f.err -} - -func (f fakeFactory) DaemonFromContext(cli.Flags) (Daemon, error) { - return fakeDaemon{err: f.errDaemon}, f.err -} - -type fakeAction struct { - err error - intFlags map[string]int -} - -func (a fakeAction) Execute(req Context) error { - if a.err != nil { - return a.err - } - - if a.intFlags != nil { - for k, v := range a.intFlags { - if req.Flags.Int(k) != v { - return xerrors.Errorf("missing flag %s", k) - } - } - } - - req.Out.Write([]byte("deadbeef")) - return nil -} - -type fakeContext struct { - cli.Flags - path string -} - -func (ctx fakeContext) Path(name string) string { - return ctx.path -} - -type badConn struct { - net.Conn - - counter *fake.Counter -} - -func (conn badConn) Read(data []byte) (int, error) { - if !conn.counter.Done() { - conn.counter.Decrease() - return len(data), nil - } - - return 0, fake.GetError() -} - -func (conn badConn) Write(data []byte) (int, error) { - if !conn.counter.Done() { - conn.counter.Decrease() - return len(data), nil - } - - return 0, fake.GetError() -} - -func (badConn) SetReadDeadline(t time.Time) error { - return nil -} - -func (badConn) Close() error { - return nil -} diff --git a/dela/cli/node/example_test.go b/dela/cli/node/example_test.go deleted file mode 100644 index 5c2fafb..0000000 --- a/dela/cli/node/example_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package node - -import ( - "fmt" - "os" - - "go.dedis.ch/dela/cli" -) - -func ExampleCLIBuilder_Build() { - builder := NewBuilder(exampleController{}) - - cmd := builder.SetCommand("bye") - - cmd.SetFlags(cli.StringFlag{ - Name: "name", - Usage: "set the name", - Value: "Bob", - }) - - // This action is only executed on the CLI process. It is also possible to - // call commands on the daemon after it has been started with "start". - cmd.SetAction(func(flags cli.Flags) error { - fmt.Printf("Bye, %s!", flags.String("name")) - return nil - }) - - app := builder.Build() - - err := app.Run([]string{os.Args[0], "bye", "--name", "Alice"}) - if err != nil { - panic("app failed: " + err.Error()) - } - - // Output: Bye, Alice! -} - -// Hello is an example of a component that can be injected and resolved on the -// daemon side. -type Hello interface { - SayTo(name string) -} - -type simpleHello struct{} - -func (simpleHello) SayTo(name string) { - fmt.Printf("Hello, %s!", name) -} - -// helloAction is an example of an action template to be executed on the daemon. -// -// - implements node.ActionTemplate -type helloAction struct{} - -// Execute implements node.ActionTemplate. It resolves the hello component and -// say hello to the name defined by the flag. -func (tmpl helloAction) Execute(ctx Context) error { - var hello Hello - err := ctx.Injector.Resolve(&hello) - if err != nil { - return err - } - - hello.SayTo(ctx.Flags.String("name")) - - return nil -} - -// exampleController is an example of a controller passed to the builder. It -// defines the command available and the component that are injected when the -// daemon is started. -// -// - implements node.Initializer -type exampleController struct{} - -// SetCommands implements node.Initializer. It defines the hello command. -func (exampleController) SetCommands(builder Builder) { - cmd := builder.SetCommand("hello") - - // Set an action that will be executed on the daemon. - cmd.SetAction(builder.MakeAction(helloAction{})) - - cmd.SetDescription("Say hello") - cmd.SetFlags(cli.StringFlag{ - Name: "name", - Usage: "set the name", - Value: "Bob", - }) -} - -// OnStart implements node.Initializer. It injects the hello component. -func (exampleController) OnStart(flags cli.Flags, inj Injector) error { - inj.Inject(simpleHello{}) - - return nil -} - -// OnStop implements node.Initializer. -func (exampleController) OnStop(Injector) error { - return nil -} diff --git a/dela/cli/node/flagset.go b/dela/cli/node/flagset.go deleted file mode 100644 index 4d4c903..0000000 --- a/dela/cli/node/flagset.go +++ /dev/null @@ -1,94 +0,0 @@ -// This file contains the implementation of a simplified flag set used on the -// daemon side. -// -// Documentation Last Review: 13.10.2020 -// - -package node - -import ( - "time" -) - -// FlagSet is a serializable flag set implementation. It allows to pack the -// flags coming from a CLI application and send them to a daemon. -// -// - implements cli.Flags -type FlagSet map[string]interface{} - -func (fset FlagSet) String(name string) string { - switch v := fset[name].(type) { - case string: - return v - default: - return "" - } -} - -// StringSlice implements cli.Flags. It returns the slice of strings associated -// with the flag name if it is set, otherwise it returns nil. -func (fset FlagSet) StringSlice(name string) []string { - switch v := fset[name].(type) { - case []interface{}: - values := make([]string, len(v)) - for i, str := range v { - values[i] = str.(string) - } - - return values - default: - return nil - } -} - -// Duration implements cli.Flags. It returns the duration associated with the -// flag name if it is set, otherwise it returns zero. -func (fset FlagSet) Duration(name string) time.Duration { - switch v := fset[name].(type) { - case float64: - return time.Duration(v) - default: - return time.Duration(0) - } -} - -// Path implements cli.Flags. It returns the path associated with the flag name -// if it is set, otherwise it returns an empty string. -func (fset FlagSet) Path(name string) string { - switch v := fset[name].(type) { - case string: - return v - default: - return "" - } -} - -// Int implements cli.Flags. It returns the integer associated with the flag if -// it is set, otherwise it returns zero. -func (fset FlagSet) Int(name string) int { - switch v := fset[name].(type) { - case int: - return v - case float64: - // This is the case where flags have been JSON marshalled/unmarshalled. - // In this case JSON uses the float type for numbers. - if v == float64(int(v)) { - return int(v) - } - - return 0 - default: - return 0 - } -} - -// Bool implements cli.Flags. It return the boolean associated with the flag if -// it is set, otherwise it returns false. -func (fset FlagSet) Bool(name string) bool { - switch v := fset[name].(type) { - case bool: - return v - default: - return false - } -} diff --git a/dela/cli/node/flagset_test.go b/dela/cli/node/flagset_test.go deleted file mode 100644 index 862d5f2..0000000 --- a/dela/cli/node/flagset_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package node - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestFlagSet_String(t *testing.T) { - fset := make(FlagSet) - fset["a"] = "something" - fset["b"] = 20 - - require.Equal(t, "something", fset.String("a")) - require.Equal(t, "", fset.String("b")) -} - -func TestFlagSet_StringSlice(t *testing.T) { - fset := make(FlagSet) - fset["a"] = []interface{}{"1", "2"} - fset["b"] = 123 - - require.Equal(t, []string{"1", "2"}, fset.StringSlice("a")) - require.Nil(t, fset.StringSlice("b")) -} - -func TestFlagSet_Duration(t *testing.T) { - fset := make(FlagSet) - fset["a"] = float64(1000.0) - fset["b"] = 1000 - - require.Equal(t, time.Duration(1000), fset.Duration("a")) - require.Equal(t, time.Duration(0), fset.Duration("b")) -} - -func TestFlagSet_Path(t *testing.T) { - fset := make(FlagSet) - fset["a"] = "/one/path" - fset["b"] = 123 - - require.Equal(t, "/one/path", fset.Path("a")) - require.Equal(t, "", fset.Path("b")) -} - -func TestFlagSet_Int(t *testing.T) { - fset := make(FlagSet) - fset["a"] = 20 - fset["b"] = "oops" - fset["c"] = 30.0 - fset["d"] = 30.1 - - require.Equal(t, 20, fset.Int("a")) - require.Equal(t, 0, fset.Int("b")) - require.Equal(t, 30, fset.Int("c")) - require.Equal(t, 0, fset.Int("d")) -} - -func TestFlagSet_Bool(t *testing.T) { - fset := make(FlagSet) - fset["a"] = true - fset["b"] = "oops" - fset["c"] = false - - require.Equal(t, true, fset.Bool("a")) - require.Equal(t, false, fset.Bool("b")) - require.Equal(t, false, fset.Bool("c")) -} diff --git a/dela/cli/node/injector.go b/dela/cli/node/injector.go deleted file mode 100644 index 8e839f6..0000000 --- a/dela/cli/node/injector.go +++ /dev/null @@ -1,57 +0,0 @@ -// This file contains the implementation of a dependency injector using -// reflection. -// -// Documentation Last Review: 13.10.2020 -// - -package node - -import ( - "reflect" - - "golang.org/x/xerrors" -) - -// ReflectInjector is a dependency injector that uses reflection to resolve -// specific interfaces. -// -// - implements node.Injector -type reflectInjector struct { - mapper map[reflect.Type]interface{} -} - -// NewInjector returns a empty injector. -func NewInjector() Injector { - return &reflectInjector{ - mapper: make(map[reflect.Type]interface{}), - } -} - -// Resolve implements node.Injector. It populates the given interface with the -// first compatible dependency. -func (inj *reflectInjector) Resolve(v interface{}) error { - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr { - return xerrors.New("expect a pointer") - } - - if !rv.Elem().IsValid() { - return xerrors.Errorf("reflect value '%v' is invalid", rv) - } - - for typ, value := range inj.mapper { - if typ.AssignableTo(rv.Elem().Type()) { - rv.Elem().Set(reflect.ValueOf(value)) - return nil - } - } - - return xerrors.Errorf("couldn't find dependency for '%v'", rv.Elem().Type()) -} - -// Inject implements node.Injector. It injects the dependency to be available -// later on. -func (inj *reflectInjector) Inject(v interface{}) { - key := reflect.TypeOf(v) - inj.mapper[key] = v -} diff --git a/dela/cli/node/injector_test.go b/dela/cli/node/injector_test.go deleted file mode 100644 index aa9ee36..0000000 --- a/dela/cli/node/injector_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package node - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestReflectInjector_Resolve(t *testing.T) { - inj := NewInjector() - - inj.Inject("abc") - - var dep string - err := inj.Resolve(&dep) - require.NoError(t, err) - require.Equal(t, "abc", dep) - - var dep2 uint64 - err = inj.Resolve(&dep2) - require.EqualError(t, err, "couldn't find dependency for 'uint64'") - - err = inj.Resolve((*interface{})(nil)) - require.EqualError(t, err, "reflect value '' is invalid") - - err = inj.Resolve(dep2) - require.EqualError(t, err, "expect a pointer") -} diff --git a/dela/cli/node/memcoin/mod.go b/dela/cli/node/memcoin/mod.go deleted file mode 100644 index bedb7a1..0000000 --- a/dela/cli/node/memcoin/mod.go +++ /dev/null @@ -1,81 +0,0 @@ -// Package main implements a ledger based on in-memory components. -// -// Unix example: -// -// # Expect GOPATH to be correctly set to have memcoin available. -// go install -// -// memcoin --config /tmp/node1 start --listen tcp://127.0.0.1:2001 & -// memcoin --config /tmp/node2 start --listen tcp://127.0.0.1:2002 & -// memcoin --config /tmp/node3 start --listen tcp://127.0.0.1:2003 & -// -// # Share the different certificates among the participants. -// memcoin --config /tmp/node2 minogrpc join --address //127.0.0.1:2001\ -// $(memcoin --config /tmp/node1 minogrpc token) -// memcoin --config /tmp/node3 minogrpc join --address //127.0.0.1:2001\ -// $(memcoin --config /tmp/node1 minogrpc token) -// -// # Create a chain with two members. -// memcoin --config /tmp/node1 ordering setup\ -// --member $(memcoin --config /tmp/node1 ordering export)\ -// --member $(memcoin --config /tmp/node2 ordering export) -// -// # Add the third after the chain is set up. -// memcoin --config /tmp/node1 ordering roster add\ -// --member $(memcoin --config /tmp/node3 ordering export) -// -package main - -import ( - "fmt" - "io" - "os" - - "go.dedis.ch/dela/cli/node" - access "go.dedis.ch/dela/contracts/access/controller" - cosipbft "go.dedis.ch/dela/core/ordering/cosipbft/controller" - 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" -) - -func main() { - err := run(os.Args) - if err != nil { - fmt.Printf("%+v\n", err) - } -} - -func run(args []string) error { - return runWithCfg(args, config{Writer: os.Stdout}) -} - -type config struct { - Channel chan os.Signal - Writer io.Writer -} - -func runWithCfg(args []string, cfg config) error { - builder := node.NewBuilderWithCfg( - cfg.Channel, - cfg.Writer, - db.NewController(), - mino.NewController(), - cosipbft.NewController(), - signed.NewManagerController(), - pool.NewController(), - access.NewController(), - proxy.NewController(), - ) - - app := builder.Build() - - err := app.Run(args) - if err != nil { - return err - } - - return nil -} diff --git a/dela/cli/node/memcoin/mod_test.go b/dela/cli/node/memcoin/mod_test.go deleted file mode 100644 index 876bbed..0000000 --- a/dela/cli/node/memcoin/mod_test.go +++ /dev/null @@ -1,276 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io" - "net" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestMemcoin_Main(t *testing.T) { - main() -} - -// This test creates a chain with initially 3 nodes. It then adds node 4 and 5 -// in two blocks. Node 4 does not share its certificate which means others won't -// be able to communicate, but the chain should proceed because of the -// threshold. -func TestMemcoin_Scenario_SetupAndTransactions(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "memcoin1") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - sigs := make(chan os.Signal) - wg := sync.WaitGroup{} - wg.Add(5) - - node1 := filepath.Join(dir, "node1") - node2 := filepath.Join(dir, "node2") - node3 := filepath.Join(dir, "node3") - node4 := filepath.Join(dir, "node4") - node5 := filepath.Join(dir, "node5") - - cfg := config{Channel: sigs, Writer: io.Discard} - - runNode(t, node1, cfg, 2111, &wg) - runNode(t, node2, cfg, 2112, &wg) - runNode(t, node3, cfg, 2113, &wg) - runNode(t, node4, cfg, 2114, &wg) - runNode(t, node5, cfg, 2115, &wg) - - defer func() { - // Simulate a Ctrl+C - close(sigs) - wg.Wait() - }() - - require.True(t, waitDaemon(t, []string{node1, node2, node3}), "daemon failed to start") - - // Share the certificates. - shareCert(t, node2, node1, "//127.0.0.1:2111") - shareCert(t, node3, node1, "//127.0.0.1:2111") - shareCert(t, node5, node1, "//127.0.0.1:2111") - - // Setup the chain with nodes 1 and 2. - args := append(append( - append( - []string{os.Args[0], "--config", node1, "ordering", "setup"}, - getExport(t, node1)..., - ), - getExport(t, node2)...), - getExport(t, node3)...) - - err = run(args) - require.NoError(t, err) - - // Add node 4 to the current chain. This node is not reachable from the - // others but transactions should work as the threshold is correct. - args = append([]string{ - os.Args[0], - "--config", node1, "ordering", "roster", "add", - "--wait", "60s"}, - getExport(t, node4)..., - ) - - err = run(args) - require.NoError(t, err) - - // Add node 5 which should be participating. - args = append([]string{ - os.Args[0], - "--config", node1, "ordering", "roster", "add", - "--wait", "60s"}, - getExport(t, node5)..., - ) - - err = run(args) - require.NoError(t, err) - - // Run a few transactions. - for i := 0; i < 5; i++ { - err = runWithCfg(args, config{}) - require.EqualError(t, err, "command error: transaction refused: duplicate in roster: 127.0.0.1:2115") - } - - // Test a timeout waiting for a transaction. - args[7] = "1ns" - err = runWithCfg(args, config{}) - require.EqualError(t, err, "command error: transaction not found after timeout") - - // Test a bad command. - err = runWithCfg([]string{os.Args[0], "ordering", "setup"}, cfg) - require.EqualError(t, err, `Required flag "member" not set`) -} - -// This test creates a chain with two nodes, then gracefully close them. It -// finally restarts both of them to make sure the chain can proceed after the -// restart. It basically tests if the components are correctly loaded from the -// persisten storage. -func TestMemcoin_Scenario_RestartNode(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "memcoin2") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - node1 := filepath.Join(dir, "node1") - node2 := filepath.Join(dir, "node2") - - // Setup the chain and closes the node. - setupChain(t, []string{node1, node2}, []uint16{2210, 2211}) - - sigs := make(chan os.Signal) - wg := sync.WaitGroup{} - wg.Add(2) - - cfg := config{Channel: sigs, Writer: io.Discard} - - // Now the node are restarted. It should correctly follow the existing chain - // and then participate to new blocks. - runNode(t, node1, cfg, 2210, &wg) - runNode(t, node2, cfg, 2211, &wg) - - defer func() { - // Simulate a Ctrl+C - close(sigs) - wg.Wait() - }() - - require.True(t, waitDaemon(t, []string{node1, node2}), "daemon failed to start") - - args := append([]string{ - os.Args[0], - "--config", node1, "ordering", "roster", "add", - "--wait", "60s"}, - getExport(t, node1)..., - ) - - err = run(args) - require.EqualError(t, err, "command error: transaction refused: duplicate in roster: 127.0.0.1:2210") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -const testDialTimeout = 500 * time.Millisecond - -func runNode(t *testing.T, node string, cfg config, port uint16, wg *sync.WaitGroup) { - go func() { - defer wg.Done() - - err := runWithCfg(makeNodeArg(node, port), cfg) - require.NoError(t, err) - }() -} - -func setupChain(t *testing.T, nodes []string, ports []uint16) { - sigs := make(chan os.Signal) - wg := sync.WaitGroup{} - wg.Add(len(nodes)) - - cfg := config{Channel: sigs, Writer: io.Discard} - - for i, node := range nodes { - runNode(t, node, cfg, ports[i], &wg) - } - - defer func() { - // Simulate a Ctrl+C - close(sigs) - wg.Wait() - }() - - waitDaemon(t, nodes) - - shareCert(t, nodes[1], nodes[0], fmt.Sprintf("//127.0.0.1:%d", ports[0])) - - args := append(append( - []string{os.Args[0], "--config", nodes[0], "ordering", "setup"}, - getExport(t, nodes[0])...), - getExport(t, nodes[1])..., - ) - - err := run(args) - require.NoError(t, err) -} - -func waitDaemon(t *testing.T, daemons []string) bool { - num := 50 - - for _, daemon := range daemons { - path := filepath.Join(daemon, "daemon.sock") - - for i := 0; i < num; i++ { - // Windows: we have to check the file as Dial on Windows creates the - // file and prevent to listen. - _, err := os.Stat(path) - if !os.IsNotExist(err) { - conn, err := net.DialTimeout("unix", path, testDialTimeout) - if err == nil { - conn.Close() - break - } - } - - time.Sleep(100 * time.Millisecond) - - if i+1 >= num { - return false - } - } - } - - return true -} - -func makeNodeArg(path string, port uint16) []string { - return []string{ - os.Args[0], "--config", path, "start", "--listen", "tcp://127.0.0.1:" + strconv.Itoa(int(port)), - } -} - -func shareCert(t *testing.T, path string, src string, addr string) { - args := append( - []string{os.Args[0], "--config", path, "minogrpc", "join", "--address", addr}, - getToken(t, src)..., - ) - - err := run(args) - require.NoError(t, err) -} - -func getToken(t *testing.T, path string) []string { - buffer := new(bytes.Buffer) - cfg := config{ - Writer: buffer, - } - - args := []string{os.Args[0], "--config", path, "minogrpc", "token"} - err := runWithCfg(args, cfg) - require.NoError(t, err) - - return strings.Split(buffer.String(), " ") -} - -func getExport(t *testing.T, path string) []string { - buffer := bytes.NewBufferString("--member ") - cfg := config{ - Writer: buffer, - } - - args := []string{os.Args[0], "--config", path, "ordering", "export"} - - err := runWithCfg(args, cfg) - require.NoError(t, err) - - return strings.Split(buffer.String(), " ") -} diff --git a/dela/cli/node/node.go b/dela/cli/node/node.go deleted file mode 100644 index 8120697..0000000 --- a/dela/cli/node/node.go +++ /dev/null @@ -1,86 +0,0 @@ -// Package node defines the Builder type, which builds an CLI application to -// controle a node. -// -// The application will have a start command by default and it provides a -// function to create actions that will eventually be executed on the running -// node. See the example. -// -// Document Last Review: 13.10.2020 -package node - -import ( - "io" - - "go.dedis.ch/dela/cli" -) - -// Builder is the builder that will be provided to the initializers, which can -// create commands and actions. -type Builder interface { - // SetCommand creates a new command and returns its builder. - SetCommand(name string) cli.CommandBuilder - - // SetStartFlags appends a list of flags that will be used to create the - // start command. - SetStartFlags(...cli.Flag) - - // MakeAction creates a CLI action from a given template. The template must - // implements the handler that will be executed on the daemon. - MakeAction(ActionTemplate) cli.Action -} - -// ActionTemplate is an extension of the cli.Action interface to allow an action -// to send a request to the daemon. -type ActionTemplate interface { - // Execute processes a command received from the CLI on the daemon. - Execute(Context) error -} - -// Context is the context available to the action when being invoked. It -// provides the dependency injector alongside with the input and output. -type Context struct { - Injector Injector - Flags cli.Flags - Out io.Writer -} - -// Injector is a dependency injection abstraction. -type Injector interface { - // Resolve populates the input with the dependency if any compatible exists. - Resolve(interface{}) error - - // Inject stores the dependency to be resolved later on. - Inject(interface{}) -} - -// Initializer is the interface that a module can implement to set its own -// commands and inject the dependencies that will be resolved in the actions. -type Initializer interface { - // Build populates the builder with the commands of the controller. - SetCommands(Builder) - - // OnStart starts the components of the initializer and populates the - // injector. - OnStart(cli.Flags, Injector) error - - // OnStop stops the components and cleans the resources. - OnStop(Injector) error -} - -// Client is the interface to send a message to the daemon. -type Client interface { - Send([]byte) error -} - -// Daemon is an IPC socket to communicate between a CLI and a node running. -type Daemon interface { - Listen() error - Close() error -} - -// DaemonFactory is an interface to create a daemon and clients to connect to -// it. -type DaemonFactory interface { - ClientFromContext(cli.Flags) (Client, error) - DaemonFromContext(cli.Flags) (Daemon, error) -} diff --git a/dela/cli/ucli/ucli.go b/dela/cli/ucli/ucli.go deleted file mode 100644 index 34bfa1c..0000000 --- a/dela/cli/ucli/ucli.go +++ /dev/null @@ -1,172 +0,0 @@ -// Package ucli provides a cli builder implementation based on the urfave/cli -// library. -package ucli - -import ( - "fmt" - - urfave "github.com/urfave/cli/v2" - "go.dedis.ch/dela/cli" -) - -// Builder implements a cli builder based on urfave/cli -// -// - implements cli.Builder -type Builder struct { - commands []*cmdBuilder - name string - action cli.Action - flags []cli.Flag -} - -// NewBuilder returns a new initialized builder. Action allows one to define a -// primary action, but can be nil if we only needs to define commands. Flags -// provides the global flags available from all the commands/subcommands. -func NewBuilder(name string, action cli.Action, flags ...cli.Flag) cli.Builder { - return &Builder{ - name: name, - action: action, - flags: flags, - } -} - -// Build implements cli.builder. -func (b Builder) Build() cli.Application { - app := &urfave.App{ - Name: b.name, - Commands: buildCommand(b.commands), - Action: makeAction(b.action), - Flags: buildFlags(b.flags), - } - - app.Setup() - - return app -} - -// SetCommand implements cli.Builder. -func (b *Builder) SetCommand(name string) cli.CommandBuilder { - cmd := &cmdBuilder{ - name: name, - } - b.commands = append(b.commands, cmd) - - return cmd -} - -// commandBuilder is the struct provided to build commands. -// -// - implements cli.CommandBuilder -type cmdBuilder struct { - name string - description string - action cli.Action - flags []urfave.Flag - subcommands []*cmdBuilder -} - -// SetDescription implements cli.CommandBuilder. -func (b *cmdBuilder) SetDescription(value string) { - b.description = value -} - -// SetFlags implements cli.CommandBuilder. -func (b *cmdBuilder) SetFlags(flags ...cli.Flag) { - b.flags = buildFlags(flags) -} - -// SetAction implements cli.CommandBuilder. -func (b *cmdBuilder) SetAction(action cli.Action) { - b.action = action -} - -// SetSubCommand implements cli.CommandBuilder. -func (b *cmdBuilder) SetSubCommand(name string) cli.CommandBuilder { - builder := &cmdBuilder{ - name: name, - } - b.subcommands = append(b.subcommands, builder) - - return builder -} - -// buildFlags converts cli.Flag to their corresponding urfave/cli. -func buildFlags(flags []cli.Flag) []urfave.Flag { - res := make([]urfave.Flag, len(flags)) - - for i, f := range flags { - var flag urfave.Flag - - switch e := f.(type) { - case cli.StringFlag: - flag = &urfave.StringFlag{ - Name: e.Name, - Usage: e.Usage, - Required: e.Required, - Value: e.Value, - } - case cli.StringSliceFlag: - flag = &urfave.StringSliceFlag{ - Name: e.Name, - Usage: e.Usage, - Required: e.Required, - Value: urfave.NewStringSlice(e.Value...), - } - case cli.DurationFlag: - flag = &urfave.DurationFlag{ - Name: e.Name, - Usage: e.Usage, - Required: e.Required, - Value: e.Value, - } - case cli.IntFlag: - flag = &urfave.IntFlag{ - Name: e.Name, - Usage: e.Usage, - Required: e.Required, - Value: e.Value, - } - case cli.BoolFlag: - flag = &urfave.BoolFlag{ - Name: e.Name, - Usage: e.Usage, - Required: e.Required, - Value: e.Value, - } - default: - panic(fmt.Sprintf("flag type '%T' not supported", f)) - } - - res[i] = flag - } - - return res -} - -// buildCommand recursively builds the commands from a cmdBuilder struct to a -// urfave commands. -func buildCommand(cmds []*cmdBuilder) []*urfave.Command { - commands := make([]*urfave.Command, len(cmds)) - - for i, cmd := range cmds { - commands[i] = &urfave.Command{ - Name: cmd.name, - Usage: cmd.description, - Action: makeAction(cmd.action), - Flags: cmd.flags, - Subcommands: buildCommand(cmd.subcommands), - } - } - - return commands -} - -// makeAction transforms a cli.Action to its urfave form. -func makeAction(action cli.Action) urfave.ActionFunc { - if action != nil { - return func(ctx *urfave.Context) error { - return action(ctx) - } - } - return nil -} diff --git a/dela/cli/ucli/ucli_test.go b/dela/cli/ucli/ucli_test.go deleted file mode 100644 index 01b3014..0000000 --- a/dela/cli/ucli/ucli_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package ucli - -import ( - "io" - "testing" - "time" - - "github.com/stretchr/testify/require" - urfave "github.com/urfave/cli/v2" - "go.dedis.ch/dela/cli" -) - -func TestBuild(t *testing.T) { - builder := NewBuilder("test", nil) - app := builder.Build().(*urfave.App) - - app.Writer = io.Discard - - require.Equal(t, "test", app.Name) - - err := app.Run([]string{"test"}) - require.NoError(t, err) -} - -func TestSetCommand(t *testing.T) { - builder := NewBuilder("test", nil) - - builder.SetCommand("first") - builder.SetCommand("second") - - app := builder.Build().(*urfave.App) - - require.Len(t, app.Commands, 3) - - require.Equal(t, "first", app.Commands[0].Name) - require.Equal(t, "second", app.Commands[1].Name) - require.Equal(t, "help", app.Commands[2].Name) - -} - -func TestCommandBuilder(t *testing.T) { - builder := NewBuilder("test", nil).(*Builder) - cmd := builder.SetCommand("first") - - fakeAction := func(flags cli.Flags) error { - return nil - } - - cmd.SetAction(fakeAction) - cmd.SetDescription("first action") - cmd.SetFlags(cli.StringFlag{ - Name: "arg", - Usage: "this is a test arg", - Required: true, - Value: "default", - }) - cmd.SetSubCommand("second") - - require.Len(t, builder.commands, 1) - require.Len(t, builder.flags, 0) - - cmd2 := builder.commands[0] - require.Len(t, cmd2.flags, 1) - require.Len(t, cmd2.subcommands, 1) -} - -func TestBuildFlags(t *testing.T) { - in := []cli.Flag{ - cli.StringFlag{ - Name: "name1", - Usage: "usage1", - Required: true, - Value: "value1", - }, - cli.StringSliceFlag{ - Name: "name2", - Usage: "usage2", - Required: true, - Value: []string{}, - }, - cli.DurationFlag{ - Name: "name3", - Usage: "usage3", - Required: true, - Value: time.Minute, - }, - cli.IntFlag{ - Name: "name4", - Usage: "usage4", - Required: true, - Value: 1, - }, - cli.BoolFlag{ - Name: "name5", - Usage: "usage5", - Required: true, - Value: true, - }, - } - - out := buildFlags(in) - require.Len(t, out, 5) - - require.Equal(t, "name1", out[0].Names()[0]) - require.Equal(t, "name2", out[1].Names()[0]) - require.Equal(t, "name3", out[2].Names()[0]) - require.Equal(t, "name4", out[3].Names()[0]) - require.Equal(t, "name5", out[4].Names()[0]) -} - -func TestBuildFlags_Panic(t *testing.T) { - defer func() { - r := recover() - require.Equal(t, "flag type '' not supported", r) - }() - - buildFlags([]cli.Flag{nil}) -} - -func TestMakeAction(t *testing.T) { - res := makeAction(nil) - require.Nil(t, res) - - isCalled := false - fakeAction := func(flags cli.Flags) error { - require.Nil(t, flags) - isCalled = true - return nil - } - - res = makeAction(fakeAction) - require.NotNil(t, res) - - out := res(nil) - require.NoError(t, out) - require.True(t, isCalled) -} diff --git a/dela/contracts/access/access.go b/dela/contracts/access/access.go deleted file mode 100644 index c434758..0000000 --- a/dela/contracts/access/access.go +++ /dev/null @@ -1,178 +0,0 @@ -// Package access implements a native contract to handle access. It allows an -// authorized identity to add access as an {ID, CONTRACT, COMMAND, IDENTITIES} -// quadruplet. -// -// ID is the credential identifier. This identifier is generally defined at the -// contract's creation. -// CONTRACT is the contract name. -// COMMAND specifies the command to grant access to on the contract. -// IDENTITIES is a list of standard base64 encoded bls public keys, separated by -// comas. -// -// Documentation Last Review: 02.02.2021 -package access - -import ( - "encoding/base64" - "encoding/hex" - "strings" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/crypto/bls" - "golang.org/x/xerrors" -) - -const ( - // ContractName is the name of the access contract. - ContractName = "go.dedis.ch/dela.Access" - - // GrantIDArg is the argument's name in the transaction that contains the - // provided id to grant - GrantIDArg = "access:grant_id" - - // GrantContractArg is the argument's name in the transaction that contain - // the provided contract name to grant the access to. - GrantContractArg = "access:grant_contract" - - // GrantCommandArg is the argument's name in the transaction that contains - // the provided command to grant access to. - GrantCommandArg = "access:grant_command" - - // IdentityArg is the argument's name in the transaction that contains the - // provided identity to grant access to. - IdentityArg = "access:identity" - - // CmdArg is the argument's name to indicate the kind of command we want to - // run on the contract. Should be one of the Command type. - CmdArg = "access:command" - - // credentialAllCommand defines the credential command that is allowed to - // perform all commands. - credentialAllCommand = "all" -) - -// Command defines a command for the command contract -type Command string - -const ( - // CmdSet defines the command to grant access - CmdSet Command = "GRANT" -) - -// NewCreds creates new credentials for an access contract execution. -func NewCreds(id []byte) access.Credential { - return access.NewContractCreds(id, ContractName, credentialAllCommand) -} - -// RegisterContract registers the access contract to the given execution -// service. -func RegisterContract(exec *native.Service, c Contract) { - exec.Set(ContractName, c) -} - -// Contract is the access contract that allows one to handle access. -// -// - implements native.Contract -type Contract struct { - // access is the access service that will be modified. - access access.Service - - // accessKey is the credential's ID allowed to use this smart contract - accessKey []byte - - store store.Readable -} - -// NewContract creates a new access contract -func NewContract(aKey []byte, srvc access.Service, store store.Readable) Contract { - return Contract{ - access: srvc, - accessKey: aKey, - store: store, - } -} - -// Execute implements native.Contract -func (c Contract) Execute(snap store.Snapshot, step execution.Step) error { - creds := NewCreds(c.accessKey) - - err := c.access.Match(c.store, creds, step.Current.GetIdentity()) - if err != nil { - return xerrors.Errorf("identity not authorized: %v (%v)", step.Current.GetIdentity(), err) - } - - cmd := step.Current.GetArg(CmdArg) - if len(cmd) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", CmdArg) - } - - switch Command(cmd) { - case CmdSet: - err := c.grant(snap, step) - if err != nil { - return xerrors.Errorf("failed to SET: %v", err) - } - default: - return xerrors.Errorf("access, unknown command: %s", cmd) - } - - return nil -} - -// grant perform the GRANT command -func (c Contract) grant(snap store.Snapshot, step execution.Step) error { - idHex := step.Current.GetArg(GrantIDArg) - if len(idHex) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", GrantIDArg) - } - - id, err := hex.DecodeString(string(idHex)) - if err != nil { - return xerrors.Errorf("failed to decode id from tx arg: %v", err) - } - - contractName := step.Current.GetArg(GrantContractArg) - if len(contractName) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", GrantContractArg) - } - - commandName := step.Current.GetArg(GrantCommandArg) - if len(commandName) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", GrantCommandArg) - } - - base64IDs := strings.Split(string(step.Current.GetArg(IdentityArg)), ",") - if len(base64IDs) == 0 || len(base64IDs[0]) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", IdentityArg) - } - - identities := make([]access.Identity, len(base64IDs)) - for i, base64ID := range base64IDs { - identity, err := base64.StdEncoding.DecodeString(string(base64ID)) - if err != nil { - return xerrors.Errorf("failed to decode base64ID: %v", err) - } - - pubKey, err := bls.NewPublicKey(identity) - if err != nil { - return xerrors.Errorf("failed to get public key: %v", err) - } - - identities[i] = pubKey - } - - credential := access.NewContractCreds(id, string(contractName), string(commandName)) - err = c.access.Grant(snap, credential, identities...) - if err != nil { - return xerrors.Errorf("failed to grant: %v", err) - } - - dela.Logger.Info().Str("contract", "access").Msgf("granted %x-%s-%s to %s", - id, contractName, commandName, identities) - - return nil -} diff --git a/dela/contracts/access/access_test.go b/dela/contracts/access/access_test.go deleted file mode 100644 index e5b2ede..0000000 --- a/dela/contracts/access/access_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package access - -import ( - "encoding/base64" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestExecute(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{err: fake.GetError()}, fakeStore{}) - err := contract.Execute(fakeStore{}, makeStep(t, CmdArg, "")) - require.EqualError(t, err, "identity not authorized: fake.PublicKey ("+fake.GetError().Error()+")") - - contract = NewContract([]byte{}, fakeAccess{}, fakeStore{}) - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "")) - require.EqualError(t, err, "'access:command' not found in tx arg") - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "fake")) - require.EqualError(t, err, "access, unknown command: fake") - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, string(CmdSet))) - require.EqualError(t, err, "failed to SET: 'access:grant_id' not found in tx arg") - - signer := bls.NewSigner() - buf, err := signer.GetPublicKey().MarshalBinary() - require.NoError(t, err) - id := base64.StdEncoding.EncodeToString(buf) - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, string(CmdSet), - GrantIDArg, "deadbeef", - GrantContractArg, "fake contract", - GrantCommandArg, "fake command", - IdentityArg, id)) - require.NoError(t, err) -} - -func TestGrant(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{}, fakeStore{}) - err := contract.grant(fakeStore{}, makeStep(t)) - require.EqualError(t, err, "'access:grant_id' not found in tx arg") - - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "x")) - require.EqualError(t, err, "failed to decode id from tx arg: encoding/hex: invalid byte: U+0078 'x'") - - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef")) - require.EqualError(t, err, "'access:grant_contract' not found in tx arg") - - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef", - GrantContractArg, "fake")) - require.EqualError(t, err, "'access:grant_command' not found in tx arg") - - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef", - GrantContractArg, "fake contract", - GrantCommandArg, "fake command")) - require.EqualError(t, err, "'access:identity' not found in tx arg") - - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef", - GrantContractArg, "fake contract", - GrantCommandArg, "fake command", - IdentityArg, "x")) - require.EqualError(t, err, "failed to decode base64ID: illegal base64 data at input byte 0") - - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef", - GrantContractArg, "fake contract", - GrantCommandArg, "fake command", - IdentityArg, "AA==")) - require.EqualError(t, err, "failed to get public key: bn256.G2: not enough data") - - signer := bls.NewSigner() - buf, err := signer.GetPublicKey().MarshalBinary() - require.NoError(t, err) - id := base64.StdEncoding.EncodeToString(buf) - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef", - GrantContractArg, "fake contract", - GrantCommandArg, "fake command", - IdentityArg, id)) - require.NoError(t, err) - - contract = NewContract([]byte{}, fakeAccess{err: fake.GetError()}, fakeStore{}) - err = contract.grant(fakeStore{}, makeStep(t, GrantIDArg, "deadbeef", - GrantContractArg, "fake contract", - GrantCommandArg, "fake command", - IdentityArg, id)) - require.EqualError(t, err, fake.Err("failed to grant")) -} - -func TestRegisterContract(t *testing.T) { - RegisterContract(native.NewExecution(), Contract{}) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeStep(t *testing.T, args ...string) execution.Step { - return execution.Step{Current: makeTx(t, args...)} -} - -func makeTx(t *testing.T, args ...string) txn.Transaction { - options := []signed.TransactionOption{} - for i := 0; i < len(args)-1; i += 2 { - options = append(options, signed.WithArg(args[i], []byte(args[i+1]))) - } - - tx, err := signed.NewTransaction(0, fake.PublicKey{}, options...) - require.NoError(t, err) - - return tx -} - -type fakeAccess struct { - access.Service - - err error -} - -func (srvc fakeAccess) Match(store.Readable, access.Credential, ...access.Identity) error { - return srvc.err -} - -func (srvc fakeAccess) Grant(store.Snapshot, access.Credential, ...access.Identity) error { - return srvc.err -} - -type fakeStore struct { - store.Snapshot -} - -func (s fakeStore) Get(key []byte) ([]byte, error) { - return nil, nil -} - -func (s fakeStore) Set(key, value []byte) error { - return nil -} diff --git a/dela/contracts/access/controller/access.json b/dela/contracts/access/controller/access.json deleted file mode 100644 index 9e26dfe..0000000 --- a/dela/contracts/access/controller/access.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/dela/contracts/access/controller/action.go b/dela/contracts/access/controller/action.go deleted file mode 100644 index 49fc3d4..0000000 --- a/dela/contracts/access/controller/action.go +++ /dev/null @@ -1,80 +0,0 @@ -// This file implements the action of the controller. -// -// Documentation Last Review: 02.02.2021 -// - -package controller - -import ( - "encoding/base64" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/cli/node" - accessContract "go.dedis.ch/dela/contracts/access" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/crypto/bls" - "golang.org/x/xerrors" -) - -// addAction is an action to add one or more identities. -// -// - implements node.ActionTemplate -type addAction struct{} - -// Execute implements node.ActionTemplate. It reads the list of identities and -// updates the access. -func (a addAction) Execute(ctx node.Context) error { - var exec *native.Service - err := ctx.Injector.Resolve(&exec) - if err != nil { - return xerrors.Errorf("failed to resolve native service: %v", err) - } - - var asrv access.Service - err = ctx.Injector.Resolve(&asrv) - if err != nil { - return xerrors.Errorf("failed to resolve access service: %v", err) - } - - var accessStore accessStore - err = ctx.Injector.Resolve(&accessStore) - if err != nil { - return xerrors.Errorf("failed to resolve access store: %v", err) - } - - idsStr := ctx.Flags.StringSlice("identity") - identities, err := parseIdentities(idsStr) - if err != nil { - return xerrors.Errorf("failed to parse identities: %v", err) - } - - err = asrv.Grant(accessStore, accessContract.NewCreds(aKey[:]), identities...) - if err != nil { - return xerrors.Errorf("failed to grant: %v", err) - } - - dela.Logger.Info().Msgf("access granted to %v", identities) - - return nil -} - -func parseIdentities(idsStr []string) ([]access.Identity, error) { - identities := make([]access.Identity, len(idsStr)) - - for i, id := range idsStr { - idBuf, err := base64.StdEncoding.DecodeString(id) - if err != nil { - return nil, xerrors.Errorf("failed to decode pub key '%s': %v", id, err) - } - - pk, err := bls.NewPublicKey(idBuf) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal identity '%s': %v", id, err) - } - - identities[i] = pk - } - - return identities, nil -} diff --git a/dela/contracts/access/controller/action_test.go b/dela/contracts/access/controller/action_test.go deleted file mode 100644 index ebe6b08..0000000 --- a/dela/contracts/access/controller/action_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package controller - -import ( - "encoding/base64" - "io" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestAddAction_Execute(t *testing.T) { - ctx := node.Context{ - Injector: node.NewInjector(), - Flags: make(node.FlagSet), - Out: io.Discard, - } - - action := addAction{} - err := action.Execute(ctx) - require.EqualError(t, err, "failed to resolve native service: couldn't find dependency for '*native.Service'") - - native := native.NewExecution() - ctx.Injector.Inject(native) - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to resolve access service: couldn't find dependency for 'access.Service'") - - access := fakeAccess{} - ctx.Injector.Inject(&access) - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to resolve access store: couldn't find dependency for 'controller.accessStore'") - - store := fakeStore{} - ctx.Injector.Inject(&store) - - err = action.Execute(ctx) - require.NoError(t, err) - - access.err = fake.GetError() - - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("failed to grant")) - - flags := fakeFlags{strings: make(map[string][]string)} - ctx.Flags = flags - flags.strings["identity"] = []string{"a"} - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to parse identities: failed to decode pub key 'a': illegal base64 data at input byte 0") - - flags.strings["identity"] = []string{"AA=="} - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to parse identities: failed to unmarshal identity 'AA==': bn256.G2: not enough data") - - signer := bls.NewSigner() - buf, err := signer.GetPublicKey().MarshalBinary() - require.NoError(t, err) - id := base64.StdEncoding.EncodeToString(buf) - flags.strings["identity"] = []string{id} - - access.err = nil - - err = action.Execute(ctx) - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeStore struct { - accessStore -} - -type fakeFlags struct { - cli.Flags - - strings map[string][]string -} - -func (f fakeFlags) StringSlice(name string) []string { - return f.strings[name] -} diff --git a/dela/contracts/access/controller/controller.go b/dela/contracts/access/controller/controller.go deleted file mode 100644 index 4c94899..0000000 --- a/dela/contracts/access/controller/controller.go +++ /dev/null @@ -1,82 +0,0 @@ -// Package controller implements a controller for the access contract. -// -// Documentation Last Review: 02.02.2021 -package controller - -import ( - "path/filepath" - - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - accessContract "go.dedis.ch/dela/contracts/access" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution/native" - "golang.org/x/xerrors" -) - -var aKey = [32]byte{1} - -// newStore is the function used to create the new store. It allows us to create -// a different store in the tests. -var newStore = func(path string) (accessStore, error) { - return newJstore(path) -} - -// miniController is a CLI initializer to allow a user to grant access to the -// access contract. -// -// - implements node.Initializer -type miniController struct{} - -// NewController creates a new minimal controller for the access contract. -func NewController() node.Initializer { - return miniController{} -} - -// SetCommands implements node.Initializer. It sets the command to control the -// service. -func (miniController) SetCommands(builder node.Builder) { - cmd := builder.SetCommand("access") - cmd.SetDescription("Handles the access contract") - - sub := cmd.SetSubCommand("add") - sub.SetDescription("add an identity") - sub.SetFlags(cli.StringSliceFlag{ - Name: "identity", - Usage: "identity to add, in the form of bls public keys", - Required: true, - }) - sub.SetAction(builder.MakeAction(addAction{})) -} - -// OnStart implements node.Initializer. It registers the access contract. -func (m miniController) OnStart(flags cli.Flags, inj node.Injector) error { - var access access.Service - err := inj.Resolve(&access) - if err != nil { - return xerrors.Errorf("failed to resolve access service: %v", err) - } - - var exec *native.Service - err = inj.Resolve(&exec) - if err != nil { - return xerrors.Errorf("failed to resolve native service: %v", err) - } - - accessStore, err := newStore(filepath.Join(flags.String("config"), "access.json")) - if err != nil { - return xerrors.Errorf("failed to create access store: %v", err) - } - - contract := accessContract.NewContract(aKey[:], access, accessStore) - accessContract.RegisterContract(exec, contract) - - inj.Inject(accessStore) - - return nil -} - -// OnStop implements node.Initializer. -func (miniController) OnStop(inj node.Injector) error { - return nil -} diff --git a/dela/contracts/access/controller/controller_test.go b/dela/contracts/access/controller/controller_test.go deleted file mode 100644 index a70c8aa..0000000 --- a/dela/contracts/access/controller/controller_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package controller - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestSetCommands(t *testing.T) { - ctrl := NewController() - - call := &fake.Call{} - ctrl.SetCommands(fakeBuilder{call: call}) - - require.Equal(t, call.Len(), 7) -} - -func TestOnStart(t *testing.T) { - ctrl := NewController() - - injector := node.NewInjector() - err := ctrl.OnStart(node.FlagSet{}, injector) - require.EqualError(t, err, "failed to resolve access service: couldn't find dependency for 'access.Service'") - - access := fakeAccess{} - injector.Inject(&access) - - err = ctrl.OnStart(node.FlagSet{}, injector) - require.EqualError(t, err, "failed to resolve native service: couldn't find dependency for '*native.Service'") - - native := native.NewExecution() - injector.Inject(native) - - oldStore := newStore - newStore = func(path string) (accessStore, error) { - return nil, fake.GetError() - } - - err = ctrl.OnStart(node.FlagSet{}, injector) - require.EqualError(t, err, fake.Err("failed to create access store")) - - newStore = oldStore - - err = ctrl.OnStart(node.FlagSet{}, injector) - require.NoError(t, err) -} - -func TestOnStop(t *testing.T) { - ctrl := NewController() - - err := ctrl.OnStop(nil) - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeCommandBuilder struct { - call *fake.Call -} - -func (b fakeCommandBuilder) SetSubCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return b -} - -func (b fakeCommandBuilder) SetDescription(value string) { - b.call.Add(value) -} - -func (b fakeCommandBuilder) SetFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeCommandBuilder) SetAction(a cli.Action) { - b.call.Add(a) -} - -type fakeBuilder struct { - call *fake.Call -} - -func (b fakeBuilder) SetCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return fakeCommandBuilder(b) -} - -func (b fakeBuilder) SetStartFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeBuilder) MakeAction(tmpl node.ActionTemplate) cli.Action { - b.call.Add(tmpl) - return nil -} - -type fakeAccess struct { - access.Service - - err error -} - -func (a fakeAccess) Grant(store store.Snapshot, creds access.Credential, idents ...access.Identity) error { - return a.err -} diff --git a/dela/contracts/access/controller/jstore.go b/dela/contracts/access/controller/jstore.go deleted file mode 100644 index 9e95fab..0000000 --- a/dela/contracts/access/controller/jstore.go +++ /dev/null @@ -1,113 +0,0 @@ -// This file implements a simple store based on a json file. -// -// Documentation Last Review: 02.02.2021 -// - -package controller - -import ( - "os" - "sync" - - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -// accessStore defines a simple read/write interface to store the access -type accessStore interface { - store.Writable - store.Readable -} - -func newJstore(path string) (accessStore, error) { - data := map[string][]byte{} - - ctx := json.NewContext() - - jstore := &jstore{ - ctx: ctx, - path: path, - data: data, - } - - if fileExist(path) { - buf, err := os.ReadFile(path) - if err != nil { - return nil, xerrors.Errorf("failed to read file '%s': %v", path, err) - } - - err = ctx.Unmarshal(buf, &data) - if err != nil { - return nil, xerrors.Errorf("failed to read json: %v", err) - } - } else { - err := jstore.saveFile() - if err != nil { - return nil, xerrors.Errorf("failed to save empty file: %v", err) - } - } - - return jstore, nil -} - -// jstore implements a simple store to store accesses on the access contract. It -// keeps the data in memory AND in a json file. -// -// - implements accessStore -type jstore struct { - sync.Mutex - - ctx serde.Context - - path string - data map[string][]byte -} - -func (s *jstore) Set(key []byte, value []byte) error { - s.Lock() - defer s.Unlock() - - s.data[string(key)] = value - s.saveFile() - - return nil -} - -func (s *jstore) Delete(key []byte) error { - s.Lock() - defer s.Unlock() - - delete(s.data, string(key)) - s.saveFile() - - return nil -} - -// return a nil value if not found -func (s *jstore) Get(key []byte) ([]byte, error) { - s.Lock() - defer s.Unlock() - - return s.data[string(key)], nil -} - -func (s *jstore) saveFile() error { - buf, err := s.ctx.Marshal(s.data) - if err != nil { - return xerrors.Errorf("failed to marshal data: %v", err) - } - - err = os.WriteFile(s.path, buf, 0644) - if err != nil { - return xerrors.Errorf("failed to save file '%s': %v", s.path, err) - } - - return nil -} - -func fileExist(path string) bool { - _, err := os.Stat(path) - return !os.IsNotExist(err) -} diff --git a/dela/contracts/access/controller/jstore_test.go b/dela/contracts/access/controller/jstore_test.go deleted file mode 100644 index cbf00b4..0000000 --- a/dela/contracts/access/controller/jstore_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package controller - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde/json" -) - -// constant holding the temporary dela directory name -const delaTestDir = "dela-test-" - -func TestJstore_New(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - path := filepath.Join(dir, "store.json") - store, err := newJstore(path) - require.NoError(t, err) - - require.NotNil(t, store) - - _, err = newJstore(dir) - require.Regexp(t, "^failed to read file", err.Error()) - - err = os.WriteFile(path, []byte(""), os.ModePerm) - require.NoError(t, err) - - _, err = newJstore(path) - require.EqualError(t, err, "failed to read json: unexpected end of JSON input") - - _, err = newJstore("/fake/file") - require.Regexp(t, "^failed to save empty file:", err.Error()) -} - -func TestJstore_Set_Get_Delete(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - path := filepath.Join(dir, "store.json") - store, err := newJstore(path) - require.NoError(t, err) - - key := []byte("key") - val := []byte("value") - - resp, err := store.Get(key) - require.NoError(t, err) - require.Nil(t, resp) - - err = store.Set(key, val) - require.NoError(t, err) - - resp, err = store.Get(key) - require.NoError(t, err) - require.Equal(t, val, resp) - - err = store.Delete(key) - require.NoError(t, err) - - resp, err = store.Get(key) - require.NoError(t, err) - require.Nil(t, resp) -} - -func TestJstore_SaveFile(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - path := filepath.Join(dir, "store.json") - store, err := newJstore(path) - require.NoError(t, err) - - store.(*jstore).data["key"] = []byte("value") - - err = store.(*jstore).saveFile() - require.NoError(t, err) - - store.(*jstore).ctx = fake.NewBadContext() - err = store.(*jstore).saveFile() - require.EqualError(t, err, fake.Err("failed to marshal data")) - - store.(*jstore).path = dir - store.(*jstore).ctx = json.NewContext() - err = store.(*jstore).saveFile() - require.Regexp(t, "^failed to save file", err.Error()) -} - -func TestJstore_Scenario(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - path := filepath.Join(dir, "store.json") - store, err := newJstore(path) - require.NoError(t, err) - - key1 := []byte("key1") - val1 := []byte("value1") - - key2 := []byte("key2") - val2 := []byte("value2") - - err = store.Set(key1, val1) - require.NoError(t, err) - - err = store.Set(key2, val2) - require.NoError(t, err) - - // Reading and updating the file with a new store - - store, err = newJstore(path) - require.NoError(t, err) - - resp, err := store.Get(key1) - require.NoError(t, err) - require.Equal(t, val1, resp) - - resp, err = store.Get(key2) - require.NoError(t, err) - require.Equal(t, val2, resp) - - err = store.Delete(key1) - require.NoError(t, err) - - // Reading with a 3rd store to see the update - - store, err = newJstore(path) - require.NoError(t, err) - - resp, err = store.Get(key1) - require.NoError(t, err) - require.Nil(t, resp) - - resp, err = store.Get(key2) - require.NoError(t, err) - require.Equal(t, val2, resp) -} diff --git a/dela/contracts/value/controller/controller.go b/dela/contracts/value/controller/controller.go deleted file mode 100644 index ee287ca..0000000 --- a/dela/contracts/value/controller/controller.go +++ /dev/null @@ -1,54 +0,0 @@ -package controller - -import ( - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/contracts/value" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution/native" - "golang.org/x/xerrors" -) - -// aKey is the access key used for the value contract -var aKey = [32]byte{2} - -// miniController is a CLI initializer to register the value contract -// -// - implements node.Initializer -type miniController struct { -} - -// NewController creates a new minimal controller for the value contract. -func NewController() node.Initializer { - return miniController{} -} - -// SetCommands implements node.Initializer. -func (miniController) SetCommands(builder node.Builder) { -} - -// OnStart implements node.Initializer. It registers the value contract. -func (m miniController) OnStart(flags cli.Flags, inj node.Injector) error { - var access access.Service - err := inj.Resolve(&access) - if err != nil { - return xerrors.Errorf("failed to resolve access service: %v", err) - } - - var exec *native.Service - err = inj.Resolve(&exec) - if err != nil { - return xerrors.Errorf("failed to resolve native service: %v", err) - } - - contract := value.NewContract(aKey[:], access) - - value.RegisterContract(exec, contract) - - return nil -} - -// OnStop implements node.Initializer. -func (miniController) OnStop(inj node.Injector) error { - return nil -} diff --git a/dela/contracts/value/controller/controller_test.go b/dela/contracts/value/controller/controller_test.go deleted file mode 100644 index aa6721a..0000000 --- a/dela/contracts/value/controller/controller_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package controller - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" -) - -func TestSetCommands(t *testing.T) { - ctrl := NewController() - ctrl.SetCommands(nil) -} - -func TestOnStart(t *testing.T) { - ctrl := NewController() - - injector := node.NewInjector() - err := ctrl.OnStart(node.FlagSet{}, injector) - require.EqualError(t, err, "failed to resolve access service: couldn't find dependency for 'access.Service'") - - access := fakeAccess{} - injector.Inject(&access) - - err = ctrl.OnStart(node.FlagSet{}, injector) - require.EqualError(t, err, "failed to resolve native service: couldn't find dependency for '*native.Service'") - - native := native.NewExecution() - injector.Inject(native) - - err = ctrl.OnStart(node.FlagSet{}, injector) - require.NoError(t, err) -} - -func TestOnStop(t *testing.T) { - ctrl := NewController() - - err := ctrl.OnStop(nil) - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeAccess struct { - access.Service - - err error -} - -func (a fakeAccess) Grant(store.Snapshot, access.Credential, ...access.Identity) error { - return a.err -} diff --git a/dela/contracts/value/value.go b/dela/contracts/value/value.go deleted file mode 100644 index b0fb0a8..0000000 --- a/dela/contracts/value/value.go +++ /dev/null @@ -1,249 +0,0 @@ -// Package value implements a simple native contract that can store, delete, and -// display values. -package value - -import ( - "fmt" - "io" - "sort" - "strings" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "golang.org/x/xerrors" -) - -// commands defines the commands of the value contract. This interface helps in -// testing the contract. -type commands interface { - write(snap store.Snapshot, step execution.Step) error - read(snap store.Snapshot, step execution.Step) error - delete(snap store.Snapshot, step execution.Step) error - list(snap store.Snapshot) error -} - -const ( - // ContractName is the name of the contract. - ContractName = "go.dedis.ch/dela.Value" - - // KeyArg is the argument's name in the transaction that contains the - // provided key to update. - KeyArg = "value:key" - - // ValueArg is the argument's name in the transaction that contains the - // provided value to set. - ValueArg = "value:value" - - // CmdArg is the argument's name to indicate the kind of command we want to - // run on the contract. Should be one of the Command type. - CmdArg = "value:command" - - // credentialAllCommand defines the credential command that is allowed to - // perform all commands. - credentialAllCommand = "all" -) - -// Command defines a type of command for the value contract -type Command string - -const ( - // CmdWrite defines the command to write a value - CmdWrite Command = "WRITE" - - // CmdRead defines a command to read a value - CmdRead Command = "READ" - - // CmdDelete defines a command to delete a value - CmdDelete Command = "DELETE" - - // CmdList defines a command to list all values set (and not deleted) - // so far. - CmdList Command = "LIST" -) - -// NewCreds creates new credentials for a value contract execution. We might -// want to use in the future a separate credential for each command. -func NewCreds(id []byte) access.Credential { - return access.NewContractCreds(id, ContractName, credentialAllCommand) -} - -// RegisterContract registers the value contract to the given execution service. -func RegisterContract(exec *native.Service, c Contract) { - exec.Set(ContractName, c) -} - -// Contract is a simple smart contract that allows one to handle the storage by -// performing CRUD operations. -// -// - implements native.Contract -type Contract struct { - // index contains all the keys set (and not delete) by this contract so far - index map[string]struct{} - - // access is the access control service managing this smart contract - access access.Service - - // accessKey is the access identifier allowed to use this smart contract - accessKey []byte - - // cmd provides the commands that can be executed by this smart contract - cmd commands - - // printer is the output used by the READ and LIST commands - printer io.Writer -} - -// NewContract creates a new Value contract -func NewContract(aKey []byte, srvc access.Service) Contract { - contract := Contract{ - index: map[string]struct{}{}, - access: srvc, - accessKey: aKey, - printer: infoLog{}, - } - - contract.cmd = valueCommand{Contract: &contract} - - return contract -} - -// Execute implements native.Contract. It runs the appropriate command. -func (c Contract) Execute(snap store.Snapshot, step execution.Step) error { - creds := NewCreds(c.accessKey) - - err := c.access.Match(snap, creds, step.Current.GetIdentity()) - if err != nil { - return xerrors.Errorf("identity not authorized: %v (%v)", - step.Current.GetIdentity(), err) - } - - cmd := step.Current.GetArg(CmdArg) - if len(cmd) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", CmdArg) - } - - switch Command(cmd) { - case CmdWrite: - err := c.cmd.write(snap, step) - if err != nil { - return xerrors.Errorf("failed to WRITE: %v", err) - } - case CmdRead: - err := c.cmd.read(snap, step) - if err != nil { - return xerrors.Errorf("failed to READ: %v", err) - } - case CmdDelete: - err := c.cmd.delete(snap, step) - if err != nil { - return xerrors.Errorf("failed to DELETE: %v", err) - } - case CmdList: - err := c.cmd.list(snap) - if err != nil { - return xerrors.Errorf("failed to LIST: %v", err) - } - default: - return xerrors.Errorf("unknown command: %s", cmd) - } - - return nil -} - -// valueCommand implements the commands of the value contract -// -// - implements commands -type valueCommand struct { - *Contract -} - -// write implements commands. It performs the WRITE command -func (c valueCommand) write(snap store.Snapshot, step execution.Step) error { - key := step.Current.GetArg(KeyArg) - if len(key) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", KeyArg) - } - - value := step.Current.GetArg(ValueArg) - if len(value) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", ValueArg) - } - - err := snap.Set(key, value) - if err != nil { - return xerrors.Errorf("failed to set value: %v", err) - } - - c.index[string(key)] = struct{}{} - - dela.Logger.Info().Str("contract", ContractName).Msgf("setting %x=%s", key, value) - - return nil -} - -// read implements commands. It performs the READ command -func (c valueCommand) read(snap store.Snapshot, step execution.Step) error { - key := step.Current.GetArg(KeyArg) - if len(key) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", KeyArg) - } - - val, err := snap.Get(key) - if err != nil { - return xerrors.Errorf("failed to get key '%s': %v", key, err) - } - - fmt.Fprintf(c.printer, "%x=%s", key, val) - - return nil -} - -// delete implements commands. It performs the DELETE command -func (c valueCommand) delete(snap store.Snapshot, step execution.Step) error { - key := step.Current.GetArg(KeyArg) - if len(key) == 0 { - return xerrors.Errorf("'%s' not found in tx arg", KeyArg) - } - - err := snap.Delete(key) - if err != nil { - return xerrors.Errorf("failed to delete key '%x': %v", key, err) - } - - delete(c.index, string(key)) - - return nil -} - -// list implements commands. It performs the LIST command -func (c valueCommand) list(snap store.Snapshot) error { - res := []string{} - - for k := range c.index { - v, err := snap.Get([]byte(k)) - if err != nil { - return xerrors.Errorf("failed to get key '%s': %v", k, err) - } - - res = append(res, fmt.Sprintf("%x=%s", k, v)) - } - - sort.Strings(res) - fmt.Fprint(c.printer, strings.Join(res, ",")) - - return nil -} - -// infoLog defines an output using zerolog -// -// - implements io.writer -type infoLog struct{} - -func (h infoLog) Write(p []byte) (int, error) { - dela.Logger.Info().Msg(string(p)) - - return len(p), nil -} diff --git a/dela/contracts/value/value_test.go b/dela/contracts/value/value_test.go deleted file mode 100644 index d0f53eb..0000000 --- a/dela/contracts/value/value_test.go +++ /dev/null @@ -1,248 +0,0 @@ -package value - -import ( - "bytes" - "encoding/hex" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestExecute(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{err: fake.GetError()}) - - err := contract.Execute(fakeStore{}, makeStep(t)) - require.EqualError(t, err, "identity not authorized: fake.PublicKey ("+fake.GetError().Error()+")") - - contract = NewContract([]byte{}, fakeAccess{}) - err = contract.Execute(fakeStore{}, makeStep(t)) - require.EqualError(t, err, "'value:command' not found in tx arg") - - contract.cmd = fakeCmd{err: fake.GetError()} - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "WRITE")) - require.EqualError(t, err, fake.Err("failed to WRITE")) - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "READ")) - require.EqualError(t, err, fake.Err("failed to READ")) - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "DELETE")) - require.EqualError(t, err, fake.Err("failed to DELETE")) - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "LIST")) - require.EqualError(t, err, fake.Err("failed to LIST")) - - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "fake")) - require.EqualError(t, err, "unknown command: fake") - - contract.cmd = fakeCmd{} - err = contract.Execute(fakeStore{}, makeStep(t, CmdArg, "WRITE")) - require.NoError(t, err) -} - -func TestCommand_Write(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{}) - - cmd := valueCommand{ - Contract: &contract, - } - - err := cmd.write(fake.NewSnapshot(), makeStep(t)) - require.EqualError(t, err, "'value:key' not found in tx arg") - - err = cmd.write(fake.NewSnapshot(), makeStep(t, KeyArg, "dummy")) - require.EqualError(t, err, "'value:value' not found in tx arg") - - err = cmd.write(fake.NewBadSnapshot(), makeStep(t, KeyArg, "dummy", ValueArg, "value")) - require.EqualError(t, err, fake.Err("failed to set value")) - - snap := fake.NewSnapshot() - - _, found := contract.index["dummy"] - require.False(t, found) - - err = cmd.write(snap, makeStep(t, KeyArg, "dummy", ValueArg, "value")) - require.NoError(t, err) - - _, found = contract.index["dummy"] - require.True(t, found) - - res, err := snap.Get([]byte("dummy")) - require.NoError(t, err) - require.Equal(t, "value", string(res)) -} - -func TestCommand_Read(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{}) - - cmd := valueCommand{ - Contract: &contract, - } - - key := []byte("dummy") - keyHex := hex.EncodeToString(key) - - err := cmd.read(fake.NewSnapshot(), makeStep(t)) - require.EqualError(t, err, "'value:key' not found in tx arg") - - err = cmd.read(fake.NewBadSnapshot(), makeStep(t, KeyArg, "dummy")) - require.EqualError(t, err, fake.Err("failed to get key 'dummy'")) - - snap := fake.NewSnapshot() - snap.Set(key, []byte("value")) - - buf := &bytes.Buffer{} - cmd.Contract.printer = buf - - err = cmd.read(snap, makeStep(t, KeyArg, "dummy")) - require.NoError(t, err) - - require.Equal(t, keyHex+"=value", buf.String()) -} - -func TestCommand_Delete(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{}) - - cmd := valueCommand{ - Contract: &contract, - } - - key := []byte("dummy") - keyHex := hex.EncodeToString(key) - keyStr := string(key) - - err := cmd.delete(fake.NewSnapshot(), makeStep(t)) - require.EqualError(t, err, "'value:key' not found in tx arg") - - err = cmd.delete(fake.NewBadSnapshot(), makeStep(t, KeyArg, keyStr)) - require.EqualError(t, err, fake.Err("failed to delete key '"+keyHex+"'")) - - snap := fake.NewSnapshot() - snap.Set(key, []byte("value")) - contract.index[keyStr] = struct{}{} - - err = cmd.delete(snap, makeStep(t, KeyArg, keyStr)) - require.NoError(t, err) - - res, err := snap.Get(key) - require.Nil(t, err) - require.Nil(t, res) - - _, found := contract.index[keyStr] - require.False(t, found) -} - -func TestCommand_List(t *testing.T) { - contract := NewContract([]byte{}, fakeAccess{}) - - key1 := "key1" - key2 := "key2" - - contract.index[key1] = struct{}{} - contract.index[key2] = struct{}{} - - buf := &bytes.Buffer{} - contract.printer = buf - - cmd := valueCommand{ - Contract: &contract, - } - - snap := fake.NewSnapshot() - snap.Set([]byte(key1), []byte("value1")) - snap.Set([]byte(key2), []byte("value2")) - - err := cmd.list(snap) - require.NoError(t, err) - - require.Equal(t, fmt.Sprintf("%x=value1,%x=value2", key1, key2), buf.String()) - - err = cmd.list(fake.NewBadSnapshot()) - // we can't assume an order from the map - require.Regexp(t, "^failed to get key", err.Error()) -} - -func TestInfoLog(t *testing.T) { - log := infoLog{} - - n, err := log.Write([]byte{0b0, 0b1}) - require.NoError(t, err) - require.Equal(t, 2, n) -} - -func TestRegisterContract(t *testing.T) { - RegisterContract(native.NewExecution(), Contract{}) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeStep(t *testing.T, args ...string) execution.Step { - return execution.Step{Current: makeTx(t, args...)} -} - -func makeTx(t *testing.T, args ...string) txn.Transaction { - options := []signed.TransactionOption{} - for i := 0; i < len(args)-1; i += 2 { - options = append(options, signed.WithArg(args[i], []byte(args[i+1]))) - } - - tx, err := signed.NewTransaction(0, fake.PublicKey{}, options...) - require.NoError(t, err) - - return tx -} - -type fakeAccess struct { - access.Service - - err error -} - -func (srvc fakeAccess) Match(store.Readable, access.Credential, ...access.Identity) error { - return srvc.err -} - -func (srvc fakeAccess) Grant(store.Snapshot, access.Credential, ...access.Identity) error { - return srvc.err -} - -type fakeStore struct { - store.Snapshot -} - -func (s fakeStore) Get(key []byte) ([]byte, error) { - return nil, nil -} - -func (s fakeStore) Set(key, value []byte) error { - return nil -} - -type fakeCmd struct { - err error -} - -func (c fakeCmd) write(snap store.Snapshot, step execution.Step) error { - return c.err -} - -func (c fakeCmd) read(snap store.Snapshot, step execution.Step) error { - return c.err -} - -func (c fakeCmd) delete(snap store.Snapshot, step execution.Step) error { - return c.err -} - -func (c fakeCmd) list(snap store.Snapshot) error { - return c.err -} diff --git a/dela/core/access/access.go b/dela/core/access/access.go deleted file mode 100644 index 2ae9c42..0000000 --- a/dela/core/access/access.go +++ /dev/null @@ -1,62 +0,0 @@ -// Package access defines the interfaces for Access Rights Controls. -// -// Documentation Last Review: 08.10.2020 -package access - -import ( - "encoding" - - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/serde" -) - -// Identity is an abstraction to uniquely identify a signer. -type Identity interface { - serde.Message - - encoding.TextMarshaler - - // Equal returns true when the other object is equal to the identity. - Equal(other interface{}) bool -} - -// Credential is an abstraction of an entity that allows one or several -// identities to access a given scope. -// -// As an example, the identifier is the username of a username/password pair. It -// defines the component to compare against. Then the password is the list of -// identities, verified beforehands, that will match, or won't, match to the -// identifier underlying permissions. The rule defines which scope should be -// verified so that the permissions can hold multiple of thoses. -// -// -- 0xdeadbeef -// -- "myContract:sayHello" -// -- Alice -// -- Bob -// -- "myContract:sayBye" -// -- Bob -// -// The example above shows two credentials for the contract "myContract" that is -// allowing two commands "sayHello" and "sayBye". Alice and Bob can say hello, -// but only Bob is allow to say bye. Alice can prove that she's allowed by -// providing the credential with the identifier 0xdeadbeef and the rule -// "myContract:sayHello". -type Credential interface { - // GetID returns the identifier of the credential. - GetID() []byte - - // GetRule returns the rule that is targetted by the credential. - GetRule() string -} - -// Service is an access control service that can read the storage to find -// permissions associated to the credentials, or update existing ones. -type Service interface { - // Match returns nil if the credentials can be matched to the group of - // identities. - Match(store store.Readable, creds Credential, idents ...Identity) error - - // Grant updates the store so that the group of identities will match the - // credentials. - Grant(store store.Snapshot, creds Credential, idents ...Identity) error -} diff --git a/dela/core/access/credentials.go b/dela/core/access/credentials.go deleted file mode 100644 index 23ad9b8..0000000 --- a/dela/core/access/credentials.go +++ /dev/null @@ -1,37 +0,0 @@ -// This file contains the implementation of contract credentials. -// -// Documentation Last Review: 08.10.2020 -// - -package access - -import "fmt" - -// ContractCredential defines the credential for a contract. It contains the -// name of the contract and an associated command. -type ContractCredential struct { - id []byte - contract string - command string -} - -// NewContractCreds creates new credential from the associated identifier, the -// name of the contract and its command. -func NewContractCreds(id []byte, contract, command string) ContractCredential { - return ContractCredential{ - id: id, - contract: contract, - command: command, - } -} - -// GetID implements access.Credential. It returns the identifier for the -// credential. -func (cc ContractCredential) GetID() []byte { - return append([]byte{}, cc.id...) -} - -// GetRule implements access.Credential. It returns the scope of the credential. -func (cc ContractCredential) GetRule() string { - return fmt.Sprintf("%s:%s", cc.contract, cc.command) -} diff --git a/dela/core/access/credentials_test.go b/dela/core/access/credentials_test.go deleted file mode 100644 index ddced5d..0000000 --- a/dela/core/access/credentials_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package access - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestContractCredentials_GetID(t *testing.T) { - creds := NewContractCreds([]byte{0xaa}, "contract", "cmd") - - require.Equal(t, []byte{0xaa}, creds.GetID()) -} - -func TestContractCredentials_GetRule(t *testing.T) { - creds := NewContractCreds([]byte{0xaa}, "contract", "cmd") - - require.Equal(t, "contract:cmd", creds.GetRule()) -} diff --git a/dela/core/access/darc/darc.go b/dela/core/access/darc/darc.go deleted file mode 100644 index b1d8578..0000000 --- a/dela/core/access/darc/darc.go +++ /dev/null @@ -1,95 +0,0 @@ -// Package darc implements Distributed Access Rights Controls. -// -// Documentation Last Review: 08.10.2020 -package darc - -import ( - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/access/darc/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// Service is an implementation of an access service that will allow one to -// store and verify access for a group of identities. -// -// - implements access.Service -type Service struct { - fac types.PermissionFactory - context serde.Context -} - -// NewService creates a new access service. -func NewService(ctx serde.Context) Service { - return Service{ - fac: types.NewFactory(), - context: ctx, - } -} - -// Match implements access.Service. It returns nil if the group of identities -// have access to the given credentials, otherwise a meaningful error on the -// reason if it does not have access. -func (srvc Service) Match(store store.Readable, creds access.Credential, idents ...access.Identity) error { - perm, err := srvc.readPermission(store, creds.GetID()) - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - - if perm == nil { - return xerrors.Errorf("permission %#x not found", creds.GetID()) - } - - err = perm.Match(creds.GetRule(), idents...) - if err != nil { - return xerrors.Errorf("permission: %v", err) - } - - return nil -} - -// Grant implements access.Service. It updates or creates the credential and -// grants the access to the group of identities. -func (srvc Service) Grant(store store.Snapshot, cred access.Credential, idents ...access.Identity) error { - perm, err := srvc.readPermission(store, cred.GetID()) - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - - if perm == nil { - perm = types.NewPermission() - } - - perm.Allow(cred.GetRule(), idents...) - - value, err := perm.Serialize(srvc.context) - if err != nil { - return xerrors.Errorf("failed to serialize: %v", err) - } - - err = store.Set(cred.GetID(), value) - if err != nil { - return xerrors.Errorf("store failed to write: %v", err) - } - - return nil -} - -func (srvc Service) readPermission(store store.Readable, key []byte) (types.Permission, error) { - value, err := store.Get(key) - if err != nil { - return nil, xerrors.Errorf("while reading: %v", err) - } - - if value == nil { - return nil, nil - } - - perm, err := srvc.fac.PermissionOf(srvc.context, value) - if err != nil { - return nil, xerrors.Errorf("permission malformed: %v", err) - } - - return perm, nil -} diff --git a/dela/core/access/darc/darc_test.go b/dela/core/access/darc/darc_test.go deleted file mode 100644 index f6a88b6..0000000 --- a/dela/core/access/darc/darc_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package darc - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/access/darc/types" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" -) - -var testCtx = json.NewContext() - -func TestService_Match(t *testing.T) { - store := fake.NewSnapshot() - - alice := bls.NewSigner() - bob := bls.NewSigner() - - creds := access.NewContractCreds([]byte{0xaa}, "test", "match") - - perm := types.NewPermission() - perm.Allow(creds.GetRule(), alice.GetPublicKey()) - data, err := perm.Serialize(testCtx) - require.NoError(t, err) - - store.Set([]byte{0xaa}, data) - store.Set([]byte{0xbb}, []byte{}) - - srvc := NewService(testCtx) - - err = srvc.Match(store, creds, alice.GetPublicKey()) - require.NoError(t, err) - - // Only the key of Alice is necessary, so it should pass. - err = srvc.Match(store, creds, alice.GetPublicKey(), bob.GetPublicKey()) - require.NoError(t, err) - - err = srvc.Match(store, creds, bob.GetPublicKey()) - require.Error(t, err) - require.Regexp(t, - "^permission: rule 'test:match': unauthorized: \\[bls:[[:xdigit:]]+\\]", err.Error()) - - err = srvc.Match(fake.NewBadSnapshot(), creds, alice.GetPublicKey()) - require.EqualError(t, err, fake.Err("store failed: while reading")) - - err = srvc.Match(store, access.NewContractCreds([]byte{0xcc}, "", "")) - require.EqualError(t, err, "permission 0xcc not found") - - err = srvc.Match(store, access.NewContractCreds([]byte{0xbb}, "", ""), alice.GetPublicKey()) - require.EqualError(t, err, - "store failed: permission malformed: JSON format: failed to unmarshal: unexpected end of JSON input") -} - -func TestService_Grant(t *testing.T) { - store := fake.NewSnapshot() - store.Set([]byte{0xbb}, []byte{}) - - creds := access.NewContractCreds([]byte{0xaa}, "test", "grant") - - alice := bls.NewSigner() - bob := bls.NewSigner() - - srvc := NewService(testCtx) - - err := srvc.Grant(store, creds, alice.GetPublicKey()) - require.NoError(t, err) - - err = srvc.Grant(store, creds, bob.GetPublicKey()) - require.NoError(t, err) - - err = srvc.Grant(fake.NewBadSnapshot(), creds) - require.EqualError(t, err, fake.Err("store failed: while reading")) - - err = srvc.Grant(store, access.NewContractCreds([]byte{0xbb}, "", "")) - require.EqualError(t, err, - "store failed: permission malformed: JSON format: failed to unmarshal: unexpected end of JSON input") - - srvc.fac = badFac{} - err = srvc.Grant(store, creds, alice.GetPublicKey()) - require.EqualError(t, err, fake.Err("failed to serialize")) - - badStore := fake.NewSnapshot() - badStore.ErrWrite = fake.GetError() - err = srvc.Grant(badStore, creds, alice.GetPublicKey()) - require.EqualError(t, err, fake.Err("store failed to write")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badFac struct { - types.PermissionFactory -} - -func (badFac) PermissionOf(serde.Context, []byte) (types.Permission, error) { - return badPerm{}, nil -} - -type badPerm struct { - types.Permission -} - -func (badPerm) Allow(string, ...access.Identity) {} - -func (badPerm) Serialize(serde.Context) ([]byte, error) { - return nil, fake.GetError() -} diff --git a/dela/core/access/darc/example_test.go b/dela/core/access/darc/example_test.go deleted file mode 100644 index ce61f03..0000000 --- a/dela/core/access/darc/example_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package darc - -import ( - "fmt" - "sync" - - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/serde/json" -) - -func ExampleService_Grant_alone() { - srvc := NewService(json.NewContext()) - - store := newStore() - - alice := bls.NewSigner() - bob := bls.NewSigner() - - credential := access.NewContractCreds([]byte{1}, "example", "hello") - - err := srvc.Grant(store, credential, alice.GetPublicKey()) - if err != nil { - panic("failed to grant alice: " + err.Error()) - } - - err = srvc.Match(store, credential, alice.GetPublicKey()) - if err != nil { - panic("alice has no access: " + err.Error()) - } else { - fmt.Println("Alice has the access") - } - - err = srvc.Match(store, credential, bob.GetPublicKey()) - if err != nil { - fmt.Println("Bob has no access") - } - - // Output: Alice has the access - // Bob has no access -} - -func ExampleService_Grant_group() { - srvc := NewService(json.NewContext()) - - store := newStore() - - alice := bls.NewSigner() - bob := bls.NewSigner() - - credential := access.NewContractCreds([]byte{1}, "example", "hello") - - err := srvc.Grant(store, credential, alice.GetPublicKey(), bob.GetPublicKey()) - if err != nil { - panic("failed to grant alice: " + err.Error()) - } - - err = srvc.Match(store, credential, alice.GetPublicKey(), bob.GetPublicKey()) - if err != nil { - panic("alice and bob have no access: " + err.Error()) - } else { - fmt.Println("[Alice, Bob] have the access") - } - - err = srvc.Match(store, credential, alice.GetPublicKey()) - if err != nil { - fmt.Println("Alice alone has no access") - } - - // Output: [Alice, Bob] have the access - // Alice alone has no access -} - -// inMemoryStore in a simple implementation of a store using an in-memory -// map. -// -// - implements store.Snapshot -type inMemoryStore struct { - sync.Mutex - - entries map[string][]byte -} - -func newStore() *inMemoryStore { - return &inMemoryStore{ - entries: make(map[string][]byte), - } -} - -// Get implements store.Readable. It returns the value associated to the key. -func (s *inMemoryStore) Get(key []byte) ([]byte, error) { - s.Lock() - defer s.Unlock() - - return s.entries[string(key)], nil -} - -// Set implements store.Writable. It sets the value for the key. -func (s *inMemoryStore) Set(key, value []byte) error { - s.Lock() - s.entries[string(key)] = value - s.Unlock() - - return nil -} - -// Delete implements store.Writable. It deletes the key from the store. -func (s *inMemoryStore) Delete(key []byte) error { - s.Lock() - delete(s.entries, string(key)) - s.Unlock() - - return nil -} diff --git a/dela/core/access/darc/json/json.go b/dela/core/access/darc/json/json.go deleted file mode 100644 index bc825c1..0000000 --- a/dela/core/access/darc/json/json.go +++ /dev/null @@ -1,155 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/access/darc/types" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - types.RegisterPermissionFormat(serde.FormatJSON, permFormat{}) -} - -// PermissionJSON is the JSON message for a permission. -type PermissionJSON struct { - Expressions map[string]ExpressionJSON -} - -// ExpressionJSON is the JSON message for an expression. -type ExpressionJSON struct { - Identities []json.RawMessage - Matches [][]int -} - -// PermFormat is the format to encode and decode permission messages. -// -// - implements serde.FormatEngine -type permFormat struct{} - -// Encode implements serde.FormatEngine. It encodes the permission message if -// appropriate, otherwise it returns an error. -func (permFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - perm, ok := msg.(*types.DisjunctivePermission) - if !ok { - return nil, xerrors.Errorf("invalid permission '%T'", msg) - } - - expressions := make(map[string]ExpressionJSON) - - for key, expr := range perm.GetRules() { - m, err := encodeExpression(ctx, expr) - if err != nil { - return nil, xerrors.Errorf("failed to encode expression: %v", err) - } - - expressions[key] = m - } - - m := PermissionJSON{Expressions: expressions} - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -func encodeExpression(ctx serde.Context, expr *types.Expression) (ExpressionJSON, error) { - - var m ExpressionJSON - identities := make(types.IdentitySet, 0) - - matches := make([][]int, len(expr.GetIdentitySets())) - - for i, match := range expr.GetIdentitySets() { - indices := make([]int, len(match)) - - for j, ident := range match { - index, found := identities.Search(ident) - if !found { - identities = append(identities, ident) - index = len(identities) - 1 - } - - indices[j] = index - } - - matches[i] = indices - } - - identitiesRaw := make([]json.RawMessage, len(identities)) - - for i, ident := range identities { - data, err := ident.Serialize(ctx) - if err != nil { - return m, xerrors.Errorf("failed to serialize identity: %v", err) - } - - identitiesRaw[i] = data - } - - m.Identities = identitiesRaw - m.Matches = matches - - return m, nil -} - -// Decode implements serde.FormatEngine. It populates the permission from the -// data if appropriate, otherwise it returns an error. -func (permFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := PermissionJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - opts := make([]types.PermissionOption, 0, len(m.Expressions)) - - for rule, raw := range m.Expressions { - expr, err := decodeExpr(ctx, raw) - if err != nil { - return nil, xerrors.Errorf("failed to decode expression: %v", err) - } - - opts = append(opts, types.WithExpression(rule, expr)) - } - - return types.NewPermission(opts...), nil -} - -func decodeExpr(ctx serde.Context, m ExpressionJSON) (*types.Expression, error) { - fac := ctx.GetFactory(types.PublicKeyFac{}) - - factory, ok := fac.(common.PublicKeyFactory) - if !ok { - return nil, xerrors.Errorf("invalid public key factory '%T'", fac) - } - - identities := make([]access.Identity, len(m.Identities)) - - for i, raw := range m.Identities { - pubkey, err := factory.PublicKeyOf(ctx, raw) - if err != nil { - return nil, xerrors.Errorf("public key: %v", err) - } - - identities[i] = pubkey - } - - matches := make([]types.IdentitySet, len(m.Matches)) - - for i, indices := range m.Matches { - matches[i] = make(types.IdentitySet, len(indices)) - - for j, index := range indices { - matches[i][j] = identities[index] - } - } - - return types.NewExpression(matches...), nil -} diff --git a/dela/core/access/darc/json/json_test.go b/dela/core/access/darc/json/json_test.go deleted file mode 100644 index e8e1576..0000000 --- a/dela/core/access/darc/json/json_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access/darc/types" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -const testValue = `{"Expressions":{"test":{"Identities":[{}],"Matches":[[0]]}}}` - -func TestPermFormat_Encode(t *testing.T) { - fmt := permFormat{} - - ctx := fake.NewContext() - - perm := types.NewPermission(types.WithRule("test", fake.PublicKey{})) - - data, err := fmt.Encode(ctx, perm) - require.NoError(t, err) - require.Equal(t, testValue, string(data)) - - _, err = fmt.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "invalid permission 'fake.Message'") - - _, err = fmt.Encode(fake.NewBadContext(), perm) - require.EqualError(t, err, fake.Err("failed to marshal")) - - perm = types.NewPermission(types.WithRule("test", fake.NewBadPublicKey())) - _, err = fmt.Encode(ctx, perm) - require.EqualError(t, err, fake.Err("failed to encode expression: failed to serialize identity")) -} - -func TestPermFormat_Decode(t *testing.T) { - fmt := permFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.PublicKeyFac{}, fake.PublicKeyFactory{}) - - msg, err := fmt.Decode(ctx, []byte(testValue)) - require.NoError(t, err) - require.Equal(t, types.NewPermission(types.WithRule("test", fake.PublicKey{})), msg) - - _, err = fmt.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - badCtx := serde.WithFactory(ctx, types.PublicKeyFac{}, nil) - _, err = fmt.Decode(badCtx, []byte(testValue)) - require.EqualError(t, err, "failed to decode expression: invalid public key factory ''") - - badCtx = serde.WithFactory(ctx, types.PublicKeyFac{}, fake.NewBadPublicKeyFactory()) - _, err = fmt.Decode(badCtx, []byte(testValue)) - require.EqualError(t, err, fake.Err("failed to decode expression: public key")) -} diff --git a/dela/core/access/darc/types/expr.go b/dela/core/access/darc/types/expr.go deleted file mode 100644 index ac688ae..0000000 --- a/dela/core/access/darc/types/expr.go +++ /dev/null @@ -1,126 +0,0 @@ -// -// Documentation Last Review: 12.10.2020 -// - -package types - -import ( - "go.dedis.ch/dela/core/access" - "golang.org/x/xerrors" -) - -// IdentitySet is a set of identities that belongs to one of the conjunction. -type IdentitySet []access.Identity - -// NewIdentitySet creates a new identity set from the list of identities by -// removing duplicates. -func NewIdentitySet(idents ...access.Identity) IdentitySet { - set := make(IdentitySet, 0, len(idents)) - if len(idents) == 0 { - return set - } - - for _, ident := range idents { - if !set.Contains(ident) { - set = append(set, ident) - } - } - - return set[:] -} - -// Contains returns true if the identity exists in the set. -func (set IdentitySet) Contains(target access.Identity) bool { - _, found := set.Search(target) - return found -} - -// Search searches for the target in the set and returns the index if it exists, -// otherwise a negative value. -func (set IdentitySet) Search(target access.Identity) (int, bool) { - for i, ident := range set { - if ident.Equal(target) { - return i, true - } - } - - return -1, false -} - -// IsSuperset return true if both sets are the same. -func (set IdentitySet) IsSuperset(o IdentitySet) bool { - if len(set) < len(o) { - return false - } - - for _, ident := range o { - if !set.Contains(ident) { - return false - } - } - - return true -} - -// Expression is the representation of the disjunctive normal form of the -// allowed groups of identities. -type Expression struct { - matches []IdentitySet -} - -// NewExpression creates a new expression from the list of identity sets. -func NewExpression(sets ...IdentitySet) *Expression { - return &Expression{ - matches: sets, - } -} - -// GetIdentitySets returns the list of identity sets. -func (expr *Expression) GetIdentitySets() []IdentitySet { - return append([]IdentitySet{}, expr.matches...) -} - -// Allow adds the group of identities as long as there is no duplicate. -func (expr *Expression) Allow(group []access.Identity) { - iset := NewIdentitySet(group...) - if len(iset) == 0 { - return - } - - for _, match := range expr.matches { - if match.IsSuperset(iset) { - // The group is already allowed. - return - } - } - - expr.matches = append(expr.matches, iset) -} - -// Deny removes the group of identities from the list of matching subsets. -func (expr *Expression) Deny(group []access.Identity) { - iset := NewIdentitySet(group...) - if len(iset) == 0 { - return - } - - for i, match := range expr.matches { - if iset.IsSuperset(match) { - expr.matches = append(expr.matches[:i], expr.matches[i+1:]...) - } - } -} - -// Match returns nil if the group are allowed for the rule, otherwise it returns -// the reason why it failed. -func (expr *Expression) Match(group []access.Identity) error { - iset := NewIdentitySet(group...) - - for _, match := range expr.matches { - if iset.IsSuperset(match) { - return nil - } - } - - return xerrors.Errorf("unauthorized: %v", group) -} diff --git a/dela/core/access/darc/types/expr_test.go b/dela/core/access/darc/types/expr_test.go deleted file mode 100644 index 8412b0a..0000000 --- a/dela/core/access/darc/types/expr_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package types - -import ( - "bytes" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" -) - -func TestIdentitySet_New(t *testing.T) { - iset := NewIdentitySet(newIdentity("A"), newIdentity("B"), newIdentity("A")) - require.Len(t, iset, 2) -} - -func TestIdentitySet_Search(t *testing.T) { - iset := NewIdentitySet(newIdentity("A"), newIdentity("B")) - - require.True(t, iset.Contains(newIdentity("A"))) - require.True(t, iset.Contains(newIdentity("B"))) - require.False(t, iset.Contains(newIdentity("C"))) -} - -func TestIdentitySet_IsSuperset(t *testing.T) { - iset := NewIdentitySet(newIdentity("A"), newIdentity("C")) - - require.True(t, iset.IsSuperset(iset)) - require.True(t, iset.IsSuperset(NewIdentitySet(newIdentity("A")))) - require.True(t, iset.IsSuperset(NewIdentitySet())) - require.False(t, iset.IsSuperset(NewIdentitySet(newIdentity("A"), newIdentity("B")))) - require.False(t, iset.IsSuperset(NewIdentitySet(newIdentity("B")))) -} - -func TestExpression_GetIdentitySets(t *testing.T) { - expr := Expression{ - matches: []IdentitySet{{}, {}}, - } - - require.Len(t, expr.GetIdentitySets(), 2) -} - -func TestExpression_Allow(t *testing.T) { - expr := NewExpression() - - expr.Allow(nil) - require.Len(t, expr.matches, 0) - - idents := []access.Identity{newIdentity("A"), newIdentity("B")} - - expr.Allow(idents) - require.Len(t, expr.matches, 1) - require.Len(t, expr.matches[0], 2) - - expr.Allow(idents) - require.Len(t, expr.matches, 1) - - expr.Allow([]access.Identity{newIdentity("A"), newIdentity("C")}) - require.Len(t, expr.matches, 2) -} - -func TestExpression_Deny(t *testing.T) { - expr := NewExpression() - expr.matches = []IdentitySet{ - NewIdentitySet(newIdentity("A"), newIdentity("B")), - NewIdentitySet(newIdentity("C")), - } - - expr.Deny(nil) - require.Len(t, expr.matches, 2) - - expr.Deny([]access.Identity{newIdentity("A")}) - require.Len(t, expr.matches, 2) - - expr.Deny([]access.Identity{newIdentity("A"), newIdentity("B")}) - require.Len(t, expr.matches, 1) -} - -func TestExpression_Match(t *testing.T) { - idents := []access.Identity{newIdentity("A"), newIdentity("B")} - - expr := NewExpression() - expr.Allow(idents) - - err := expr.Match(idents) - require.NoError(t, err) - - err = expr.Match([]access.Identity{newIdentity("A"), newIdentity("C")}) - require.EqualError(t, err, "unauthorized: ['A' 'C']") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeIdentity struct { - access.Identity - - buffer []byte -} - -func newIdentity(value string) fakeIdentity { - return fakeIdentity{buffer: []byte(value)} -} - -func (i fakeIdentity) String() string { - return fmt.Sprintf("'%s'", i.buffer) -} - -func (i fakeIdentity) Equal(o interface{}) bool { - other, ok := o.(fakeIdentity) - return ok && bytes.Equal(i.buffer, other.buffer) -} diff --git a/dela/core/access/darc/types/permission.go b/dela/core/access/darc/types/permission.go deleted file mode 100644 index e1af273..0000000 --- a/dela/core/access/darc/types/permission.go +++ /dev/null @@ -1,174 +0,0 @@ -// -// Documentation Last Review: 12.10.2020 -// - -package types - -import ( - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var permFormats = registry.NewSimpleRegistry() - -// RegisterPermissionFormat registers the engine for the provided format. -func RegisterPermissionFormat(c serde.Format, f serde.FormatEngine) { - permFormats.Register(c, f) -} - -// DisjunctivePermission is a permission implementation that is using the -// Disjunctive Normal Form to represent the groups of identities allowed for a -// given rule. -// -// - implements types.Permission -type DisjunctivePermission struct { - rules map[string]*Expression -} - -// PermissionOption is the option type to create an access control. -type PermissionOption func(*DisjunctivePermission) - -// WithRule is an option to grant a given group access to a rule. -func WithRule(rule string, group ...access.Identity) PermissionOption { - return func(perm *DisjunctivePermission) { - perm.Allow(rule, group...) - } -} - -// WithExpression is an option to set a rule from its expression. -func WithExpression(rule string, expr *Expression) PermissionOption { - return func(perm *DisjunctivePermission) { - perm.rules[rule] = expr - } -} - -// NewPermission returns a new empty instance of an access control. -func NewPermission(opts ...PermissionOption) *DisjunctivePermission { - a := &DisjunctivePermission{ - rules: make(map[string]*Expression), - } - - for _, opt := range opts { - opt(a) - } - - return a -} - -// GetRules returns a map of the expressions. -func (perm *DisjunctivePermission) GetRules() map[string]*Expression { - rules := make(map[string]*Expression) - - for rule, expr := range perm.rules { - rules[rule] = expr - } - - return rules -} - -// Allow implements types.Permission. It grants the permission to the group of -// identities as a single entity. -func (perm *DisjunctivePermission) Allow(rule string, group ...access.Identity) { - expr, ok := perm.rules[rule] - if !ok { - expr = NewExpression() - } - - expr.Allow(group) - - perm.rules[rule] = expr -} - -// Deny implements types.Permission. It denies the permission to the group of -// identities as a single entity by removing every subset matching this -// superset. -func (perm *DisjunctivePermission) Deny(rule string, group ...access.Identity) { - expr, ok := perm.rules[rule] - if !ok { - return - } - - expr.Deny(group) - - // Clean the the rule if it was the last group allowed. - if len(expr.matches) == 0 { - delete(perm.rules, rule) - } -} - -// Match implements types.Permission. It returns true if the rule exists and the -// group of identities is associated with it. -func (perm *DisjunctivePermission) Match(rule string, group ...access.Identity) error { - if len(group) == 0 { - return xerrors.New("expect at least one identity") - } - - expr, ok := perm.rules[rule] - if !ok { - return xerrors.Errorf("rule '%s' not found", rule) - } - - err := expr.Match(group) - if err != nil { - return xerrors.Errorf("rule '%s': %v", rule, err) - } - - return nil -} - -// Serialize implements serde.Message. It looks up the format and returns the -// serialized data of the permission. -func (perm *DisjunctivePermission) Serialize(ctx serde.Context) ([]byte, error) { - format := permFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, perm) - if err != nil { - return nil, xerrors.Errorf("couldn't encode access: %v", err) - } - - return data, nil -} - -// PublicKeyFac is the key of the public key factory. -type PublicKeyFac struct{} - -// permFac is the implementation of a permission factory. -// -// - implements types.PermissionFactory -type permFac struct { - fac common.PublicKeyFactory -} - -// NewFactory returns a new instance of the factory. -func NewFactory() PermissionFactory { - return permFac{ - fac: common.NewPublicKeyFactory(), - } -} - -// Deserialize implements serde.Factory. -func (f permFac) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.PermissionOf(ctx, data) -} - -// PermissionOf implements types.PermissionFactory. -func (f permFac) PermissionOf(ctx serde.Context, data []byte) (Permission, error) { - format := permFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, PublicKeyFac{}, f.fac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("%v format: %v", ctx.GetFormat(), err) - } - - access, ok := msg.(Permission) - if !ok { - return nil, xerrors.Errorf("invalid access '%T'", msg) - } - - return access, nil -} diff --git a/dela/core/access/darc/types/permission_test.go b/dela/core/access/darc/types/permission_test.go deleted file mode 100644 index a540f28..0000000 --- a/dela/core/access/darc/types/permission_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/internal/testing/fake" -) - -func init() { - RegisterPermissionFormat(fake.GoodFormat, fake.Format{Msg: &DisjunctivePermission{}}) - RegisterPermissionFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterPermissionFormat(fake.MsgFormat, fake.NewMsgFormat()) -} - -func TestPermission_WithRule(t *testing.T) { - perm := NewPermission(WithRule("A", newIdentity("AA"), newIdentity("BB"))) - - require.Len(t, perm.rules, 1) - require.Len(t, perm.rules["A"].matches, 1) - require.Len(t, perm.rules["A"].matches[0], 2) -} - -func TestPermission_WithExpression(t *testing.T) { - perm := NewPermission(WithExpression("test", NewExpression())) - require.Len(t, perm.rules, 1) -} - -func TestPermission_GetRules(t *testing.T) { - perm := NewPermission(WithRule("A", newIdentity("a")), WithRule("B", newIdentity("b"))) - - require.Len(t, perm.GetRules(), 2) -} - -func TestPermission_Allow(t *testing.T) { - perm := NewPermission() - - idents := []access.Identity{ - fakeIdentity{buffer: []byte{0xaa}}, - fakeIdentity{buffer: []byte{0xbb}}, - } - - perm.Allow("fake", idents...) - require.Len(t, perm.rules, 1) - - perm.Allow("another", idents...) - require.Len(t, perm.rules, 2) - - perm.Allow("fake") - require.Len(t, perm.rules, 2) - - perm.Deny("fake", idents...) - require.Len(t, perm.rules, 1) - - perm.Deny("fake", idents...) - require.Len(t, perm.rules, 1) -} - -func TestPermission_Deny(t *testing.T) { - perm := NewPermission() - perm.rules["fake"] = NewExpression(NewIdentitySet(newIdentity("A"))) - - perm.Deny("fake") - require.Len(t, perm.rules, 1) - - perm.Deny("fake", newIdentity("B")) - require.Len(t, perm.rules, 1) - - perm.Deny("fake", newIdentity("A")) - require.Len(t, perm.rules, 0) -} - -func TestPermission_Match(t *testing.T) { - idents := []access.Identity{ - fakeIdentity{buffer: []byte{0xaa}}, - fakeIdentity{buffer: []byte{0xbb}}, - } - - perm := NewPermission() - perm.Allow("fake", idents...) - - err := perm.Match("fake", idents...) - require.NoError(t, err) - - err = perm.Match("fake") - require.EqualError(t, err, "expect at least one identity") - - err = perm.Match("unknown", idents...) - require.EqualError(t, err, "rule 'unknown' not found") - - err = perm.Match("fake", newIdentity("C")) - require.EqualError(t, err, "rule 'fake': unauthorized: ['C']") -} - -func TestPermission_Serialize(t *testing.T) { - perm := NewPermission() - - data, err := perm.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = perm.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode access")) -} - -func TestPermissionFactory_Deserialize(t *testing.T) { - factory := NewFactory() - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.IsType(t, &DisjunctivePermission{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("FakeBad format")) - - _, err = factory.Deserialize(fake.NewMsgContext(), nil) - require.EqualError(t, err, "invalid access 'fake.Message'") -} diff --git a/dela/core/access/darc/types/types.go b/dela/core/access/darc/types/types.go deleted file mode 100644 index a8e0933..0000000 --- a/dela/core/access/darc/types/types.go +++ /dev/null @@ -1,38 +0,0 @@ -// Package types implements the darc messages. -// -// The messages are implemented in a separate package to prevent cycle imports -// when importing the serde formats. -// -// Documentation Last Review: 08.10.2020 -package types - -import ( - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/serde" -) - -// Permission is the interface of the underlying permissions used by the -// service. -type Permission interface { - serde.Message - - // Allow grants the permission to the rule to the group of identities as a - // single entity so that it will match if and only if the group agrees. - Allow(rule string, group ...access.Identity) - - // Deny denies the permission to the rule to the group of identities as a - // single entity. - Deny(rule string, group ...access.Identity) - - // Match returns a nil error if the group, or a subset of the group, is - // allowed. - Match(rule string, group ...access.Identity) error -} - -// PermissionFactory is the factory to serialize and deserialize the -// permissions. -type PermissionFactory interface { - serde.Factory - - PermissionOf(serde.Context, []byte) (Permission, error) -} diff --git a/dela/core/execution/execution.go b/dela/core/execution/execution.go deleted file mode 100644 index 3f54cfc..0000000 --- a/dela/core/execution/execution.go +++ /dev/null @@ -1,36 +0,0 @@ -// Package execution defines the service to execute a step in a validation -// batch. -// -// Documentation Last Review: 08.10.2020 -package execution - -import ( - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" -) - -// Step is a context of execution. It allows for example a smart contract to -// execute a given transaction knowing what previous transactions have already -// been accepted and executed in a block. -type Step struct { - Previous []txn.Transaction - Current txn.Transaction -} - -// Result is the result of a transaction execution. -type Result struct { - // Accepted is the success state of the transaction. - Accepted bool - - // Message gives a change to the execution to explain why a transaction has - // failed. - Message string -} - -// Service is the execution service that defines the primitives to execute a -// transaction. -type Service interface { - // Execute must apply the transaction to the trie and return the result of - // it. - Execute(snap store.Snapshot, step Step) (Result, error) -} diff --git a/dela/core/execution/native/example_test.go b/dela/core/execution/native/example_test.go deleted file mode 100644 index 5fcd47d..0000000 --- a/dela/core/execution/native/example_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package native - -import ( - "encoding/binary" - "fmt" - "sync" - - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto/bls" -) - -func ExampleService_Execute() { - srvc := NewExecution() - srvc.Set("example", exampleContract{}) - - store := newStore() - signer := bls.NewSigner() - - increment := make([]byte, 8) - binary.LittleEndian.PutUint64(increment, 5) - - opts := []signed.TransactionOption{ - signed.WithArg("increment", increment), - signed.WithArg(ContractArg, []byte("example")), - } - - tx, err := signed.NewTransaction(0, signer.GetPublicKey(), opts...) - if err != nil { - panic("failed to create transaction: " + err.Error()) - } - - step := execution.Step{ - Current: tx, - } - - for i := 0; i < 2; i++ { - res, err := srvc.Execute(store, step) - if err != nil { - panic("failed to execute: " + err.Error()) - } - - if res.Accepted { - fmt.Println("accepted") - } - } - - value, err := store.Get([]byte("counter")) - if err != nil { - panic("store failed: " + err.Error()) - } - - fmt.Println(binary.LittleEndian.Uint64(value)) - - // Output: accepted - // accepted - // 10 -} - -// exampleContract is an example contract that reads a counter value in the -// store and increase it with the increment in the transaction. -// -// - implements native.Contract -type exampleContract struct{} - -// Execute implements native.Contract. It increases the counter with the -// increment in the transaction. -func (exampleContract) Execute(store store.Snapshot, step execution.Step) error { - value, err := store.Get([]byte("counter")) - if err != nil { - return err - } - - counter := uint64(0) - if len(value) == 8 { - counter = binary.LittleEndian.Uint64(value) - } - - incr := binary.LittleEndian.Uint64(step.Current.GetArg("increment")) - - buffer := make([]byte, 8) - binary.LittleEndian.PutUint64(buffer, counter+incr) - - err = store.Set([]byte("counter"), buffer) - if err != nil { - return err - } - - return nil -} - -// inMemoryStore in a simple implementation of a store using an in-memory -// map. -// -// - implements store.Snapshot -type inMemoryStore struct { - sync.Mutex - - entries map[string][]byte -} - -func newStore() *inMemoryStore { - return &inMemoryStore{ - entries: make(map[string][]byte), - } -} - -// Get implements store.Readable. It returns the value associated to the key. -func (s *inMemoryStore) Get(key []byte) ([]byte, error) { - s.Lock() - defer s.Unlock() - - return s.entries[string(key)], nil -} - -// Set implements store.Writable. It sets the value for the key. -func (s *inMemoryStore) Set(key, value []byte) error { - s.Lock() - s.entries[string(key)] = value - s.Unlock() - - return nil -} - -// Delete implements store.Writable. It deletes the key from the store. -func (s *inMemoryStore) Delete(key []byte) error { - s.Lock() - delete(s.entries, string(key)) - s.Unlock() - - return nil -} diff --git a/dela/core/execution/native/native.go b/dela/core/execution/native/native.go deleted file mode 100644 index f9b5c23..0000000 --- a/dela/core/execution/native/native.go +++ /dev/null @@ -1,68 +0,0 @@ -// Package native implements an execution service to run native smart contracts. -// -// A native smart contract is written in Go and packaged with the application. -// -// Documentation Last Review: 08.10.2020 -package native - -import ( - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/store" - "golang.org/x/xerrors" -) - -const ( - // ContractArg is the argument key in the transaction to look up a contract. - ContractArg = "go.dedis.ch/dela.ContractArg" -) - -// Contract is the interface to implement to register a smart contract that will -// be executed natively. -type Contract interface { - Execute(store.Snapshot, execution.Step) error -} - -// Service is an execution service for packaged applications. Those -// applications have complete access to the trie and can directly update it. -// -// - implements execution.Service -type Service struct { - contracts map[string]Contract -} - -// NewExecution returns a new native execution. The given service will be -// executed for every incoming transaction. -func NewExecution() *Service { - return &Service{ - contracts: map[string]Contract{}, - } -} - -// Set stores the contract using the name as the key. A transaction can trigger -// this contract by using the same name as the contract argument. -func (ns *Service) Set(name string, contract Contract) { - ns.contracts[name] = contract -} - -// Execute implements execution.Service. It uses the executor to process the -// incoming transaction and return the result. -func (ns *Service) Execute(snap store.Snapshot, step execution.Step) (execution.Result, error) { - name := string(step.Current.GetArg(ContractArg)) - - contract := ns.contracts[name] - if contract == nil { - return execution.Result{}, xerrors.Errorf("unknown contract '%s'", name) - } - - res := execution.Result{ - Accepted: true, - } - - err := contract.Execute(snap, step) - if err != nil { - res.Accepted = false - res.Message = err.Error() - } - - return res, nil -} diff --git a/dela/core/execution/native/native_test.go b/dela/core/execution/native/native_test.go deleted file mode 100644 index 54f0be7..0000000 --- a/dela/core/execution/native/native_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package native - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestService_Execute(t *testing.T) { - srvc := NewExecution() - srvc.Set("abc", fakeExec{}) - srvc.Set("bad", fakeExec{err: fake.GetError()}) - - step := execution.Step{} - step.Current = fakeTx{contract: "abc"} - - res, err := srvc.Execute(nil, step) - require.NoError(t, err) - require.Equal(t, execution.Result{Accepted: true}, res) - - step.Current = fakeTx{contract: "bad"} - res, err = srvc.Execute(nil, step) - require.NoError(t, err) - require.Equal(t, execution.Result{Message: fake.GetError().Error()}, res) - - step.Current = fakeTx{contract: "none"} - _, err = srvc.Execute(nil, step) - require.EqualError(t, err, "unknown contract 'none'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeExec struct { - err error -} - -func (e fakeExec) Execute(store.Snapshot, execution.Step) error { - return e.err -} - -type fakeTx struct { - txn.Transaction - contract string -} - -func (tx fakeTx) GetArg(key string) []byte { - return []byte(tx.contract) -} diff --git a/dela/core/ordering/cosipbft/authority/authority.go b/dela/core/ordering/cosipbft/authority/authority.go deleted file mode 100644 index f4b5e13..0000000 --- a/dela/core/ordering/cosipbft/authority/authority.go +++ /dev/null @@ -1,56 +0,0 @@ -// Package authority defines the collective authority for cosipbft. -// -// The package also contains an implementation of a roster and the related -// change set. A roster is a list of participants where each of them has an Mino -// address and a corresponding public key that supports aggregation for the -// collective signing. -// -// Documentation Last Review: 13.10.2020 -package authority - -import ( - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// ChangeSet is the return of a diff between two authorities. -type ChangeSet interface { - serde.Message - - // NumChanges returns the number of changes that will be applied with this - // change set. - NumChanges() int - - // GetNewAddresses returns the list of addresses for the new members. - GetNewAddresses() []mino.Address -} - -// ChangeSetFactory is the factory to deserialize change sets. -type ChangeSetFactory interface { - serde.Factory - - ChangeSetOf(serde.Context, []byte) (ChangeSet, error) -} - -// Authority is an extension of the collective authority to provide primitives -// to append new players to it. -type Authority interface { - serde.Message - serde.Fingerprinter - crypto.CollectiveAuthority - - // Apply must apply the change set to the collective authority. It should - // first remove, then add the new players. - Apply(ChangeSet) Authority - - // Diff should return the change set to apply to get the given authority. - Diff(Authority) ChangeSet -} - -// Factory is the factory to deserialize authorities. -type Factory interface { - serde.Factory - - AuthorityOf(serde.Context, []byte) (Authority, error) -} diff --git a/dela/core/ordering/cosipbft/authority/changeset.go b/dela/core/ordering/cosipbft/authority/changeset.go deleted file mode 100644 index 4f5875e..0000000 --- a/dela/core/ordering/cosipbft/authority/changeset.go +++ /dev/null @@ -1,130 +0,0 @@ -// This file contains the implementation of the change set. -// -// Documentation Last Review: 13.10.2020 -// - -package authority - -import ( - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var csetFormats = registry.NewSimpleRegistry() - -// RegisterChangeSetFormat registers the engine for the provided format. -func RegisterChangeSetFormat(c serde.Format, f serde.FormatEngine) { - csetFormats.Register(c, f) -} - -// RosterChangeSet is the smallest data model to update an authority to another. -// -// - implements authority.ChangeSet -type RosterChangeSet struct { - remove []uint - addrs []mino.Address - pubkeys []crypto.PublicKey -} - -// NewChangeSet creates a new empty change set. -func NewChangeSet() *RosterChangeSet { - return &RosterChangeSet{} -} - -// GetPublicKeys returns the list of public keys of the new participants. -func (set *RosterChangeSet) GetPublicKeys() []crypto.PublicKey { - return append([]crypto.PublicKey{}, set.pubkeys...) -} - -// GetNewAddresses implements authority.ChangeSet. It returns the list of -// addresses of the new members. -func (set *RosterChangeSet) GetNewAddresses() []mino.Address { - return append([]mino.Address{}, set.addrs...) -} - -// GetRemoveIndices returns the list of indices to remove from the authority. -func (set *RosterChangeSet) GetRemoveIndices() []uint { - return append([]uint{}, set.remove...) -} - -// Remove appends the index to the list of removals. -func (set *RosterChangeSet) Remove(index uint) { - set.remove = append(set.remove, index) -} - -// Add appends the address and the public key to the list of new participants. -func (set *RosterChangeSet) Add(addr mino.Address, pubkey crypto.PublicKey) { - set.addrs = append(set.addrs, addr) - set.pubkeys = append(set.pubkeys, pubkey) -} - -// NumChanges implements authority.ChangeSet. It returns the number of changes -// that is applied with the change set. -func (set *RosterChangeSet) NumChanges() int { - return len(set.remove) + len(set.addrs) -} - -// Serialize implements serde.Message. It returns the serialized data for this -// change set. -func (set *RosterChangeSet) Serialize(ctx serde.Context) ([]byte, error) { - format := csetFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, set) - if err != nil { - return nil, xerrors.Errorf("couldn't encode change set: %v", err) - } - - return data, nil -} - -// PubKeyFac is the key for the public key factory. -type PubKeyFac struct{} - -// AddrKeyFac is the key for the address factory. -type AddrKeyFac struct{} - -// SimpleChangeSetFactory is a message factory to deserialize a change set. -// -// - roster.ChangeSetFactory -type SimpleChangeSetFactory struct { - addrFactory mino.AddressFactory - pubkeyFactory crypto.PublicKeyFactory -} - -// NewChangeSetFactory returns a new change set factory. -func NewChangeSetFactory(af mino.AddressFactory, pkf crypto.PublicKeyFactory) ChangeSetFactory { - return SimpleChangeSetFactory{ - addrFactory: af, - pubkeyFactory: pkf, - } -} - -// Deserialize implements serde.Factory. It returns the change set from the data -// if appropriate, otherwise an error. -func (f SimpleChangeSetFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.ChangeSetOf(ctx, data) -} - -// ChangeSetOf implements roster.ChangeSetFactory. It returns the change set -// from the data if appropriate, otherwise an error. -func (f SimpleChangeSetFactory) ChangeSetOf(ctx serde.Context, data []byte) (ChangeSet, error) { - format := csetFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, PubKeyFac{}, f.pubkeyFactory) - ctx = serde.WithFactory(ctx, AddrKeyFac{}, f.addrFactory) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode change set: %v", err) - } - - cset, ok := msg.(ChangeSet) - if !ok { - return nil, xerrors.Errorf("invalid message of type '%T'", msg) - } - - return cset, nil -} diff --git a/dela/core/ordering/cosipbft/authority/changeset_test.go b/dela/core/ordering/cosipbft/authority/changeset_test.go deleted file mode 100644 index 349711a..0000000 --- a/dela/core/ordering/cosipbft/authority/changeset_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package authority - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func init() { - RegisterChangeSetFormat(fake.GoodFormat, fake.Format{Msg: NewChangeSet()}) - RegisterChangeSetFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterChangeSetFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestChangeSet_GetPublicKeys(t *testing.T) { - cset := NewChangeSet() - require.Len(t, cset.GetPublicKeys(), 0) - - cset.Add(fake.NewAddress(0), fake.PublicKey{}) - require.Len(t, cset.GetPublicKeys(), 1) -} - -func TestChangeSet_GetNewAddresses(t *testing.T) { - cset := NewChangeSet() - require.Len(t, cset.GetNewAddresses(), 0) - - cset.Add(fake.NewAddress(0), fake.PublicKey{}) - require.Len(t, cset.GetNewAddresses(), 1) -} - -func TestChangeSet_GetRemoveIndices(t *testing.T) { - cset := NewChangeSet() - require.Len(t, cset.GetRemoveIndices(), 0) - - cset.Remove(5) - require.Len(t, cset.GetRemoveIndices(), 1) -} - -func TestChangeSet_NumChanges(t *testing.T) { - cset := NewChangeSet() - require.Equal(t, 0, cset.NumChanges()) - - cset.Remove(5) - require.Equal(t, 1, cset.NumChanges()) - - cset.Add(fake.NewAddress(0), fake.PublicKey{}) - require.Equal(t, 2, cset.NumChanges()) -} - -func TestChangeSet_Serialize(t *testing.T) { - cset := RosterChangeSet{} - - data, err := cset.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = cset.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode change set")) -} - -func TestChangeSetFactory_Deserialize(t *testing.T) { - factory := NewChangeSetFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, NewChangeSet(), msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode change set")) - - _, err = factory.Deserialize(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid message of type 'fake.Message'") -} diff --git a/dela/core/ordering/cosipbft/authority/json/json.go b/dela/core/ordering/cosipbft/authority/json/json.go deleted file mode 100644 index 48fd385..0000000 --- a/dela/core/ordering/cosipbft/authority/json/json.go +++ /dev/null @@ -1,212 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - authority.RegisterChangeSetFormat(serde.FormatJSON, changeSetFormat{}) - authority.RegisterRosterFormat(serde.FormatJSON, rosterFormat{}) -} - -// Player is a JSON message that contains the address and the public key of a -// new participant. -type Player struct { - Address []byte - PublicKey json.RawMessage -} - -// ChangeSet is a JSON message of the change set of an authority. -type ChangeSet struct { - Remove []uint - Addresses [][]byte - PublicKeys []json.RawMessage -} - -// Address is a JSON message for an address. -type Address []byte - -// Roster is a JSON message for a authority. -type Roster []Player - -// ChangeSetFormat is the engine to encode and decode change set messages in -// JSON format. -// -// - implements serde.FormatEngine -type changeSetFormat struct{} - -// Encode implements serde.FormatEngine. It returns the data serialized for the -// change set message if appropriate, otherwise an error. -func (f changeSetFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - cset, ok := msg.(*authority.RosterChangeSet) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - addrs := make([][]byte, 0) - for _, addr := range cset.GetNewAddresses() { - raw, err := addr.MarshalText() - if err != nil { - return nil, xerrors.Errorf("couldn't serialize address: %v", err) - } - - addrs = append(addrs, raw) - } - - pubkeys := make([]json.RawMessage, 0) - for _, pubkey := range cset.GetPublicKeys() { - raw, err := pubkey.Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize public key: %v", err) - } - - pubkeys = append(pubkeys, raw) - } - - m := ChangeSet{ - Remove: cset.GetRemoveIndices(), - Addresses: addrs, - PublicKeys: pubkeys, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the message with the JSON -// data if appropriate, otherwise it returns an error. -func (f changeSetFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := ChangeSet{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize change set: %v", err) - } - - factory := ctx.GetFactory(authority.PubKeyFac{}) - - pkFac, ok := factory.(crypto.PublicKeyFactory) - if !ok { - return nil, xerrors.Errorf("invalid public key factory of type '%T'", factory) - } - - factory = ctx.GetFactory(authority.AddrKeyFac{}) - - addrFac, ok := factory.(mino.AddressFactory) - if !ok { - return nil, xerrors.Errorf("invalid address factory of type '%T'", factory) - } - - cset := authority.NewChangeSet() - - for _, index := range m.Remove { - cset.Remove(index) - } - - for i, rawAddr := range m.Addresses { - addr := addrFac.FromText(rawAddr) - - pubkey, err := pkFac.PublicKeyOf(ctx, m.PublicKeys[i]) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize public key: %v", err) - } - - cset.Add(addr, pubkey) - } - - return cset, nil -} - -// RosterFormat is the engine to encode and decode roster messages in JSON -// format. -// -// - implements serde.FormatEngine -type rosterFormat struct{} - -// Encode implements serde.FormatEngine. It returns the data serialized for the -// roster message if appropriate, otherwise an error. -func (f rosterFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - roster, ok := msg.(authority.Roster) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - players := make([]Player, roster.Len()) - - addrIter := roster.AddressIterator() - pkIter := roster.PublicKeyIterator() - for i := 0; addrIter.HasNext() && pkIter.HasNext(); i++ { - addr, err := addrIter.GetNext().MarshalText() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal address: %v", err) - } - - pubkey, err := pkIter.GetNext().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize public key: %v", err) - } - - players[i] = Player{ - Address: addr, - PublicKey: pubkey, - } - } - - m := Roster(players) - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the roster with the JSON -// data if appropriate, otherwise it returns an error. -func (f rosterFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := Roster{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize roster: %v", err) - } - - factory := ctx.GetFactory(authority.PubKeyFac{}) - - pkFac, ok := factory.(crypto.PublicKeyFactory) - if !ok { - return nil, xerrors.Errorf("invalid public key factory of type '%T'", factory) - } - - factory = ctx.GetFactory(authority.AddrKeyFac{}) - - addrFac, ok := factory.(mino.AddressFactory) - if !ok { - return nil, xerrors.Errorf("invalid address factory of type '%T'", factory) - } - - addrs := make([]mino.Address, len(m)) - pubkeys := make([]crypto.PublicKey, len(m)) - - for i, player := range m { - addrs[i] = addrFac.FromText(player.Address) - - pubkey, err := pkFac.PublicKeyOf(ctx, player.PublicKey) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize public key: %v", err) - } - - pubkeys[i] = pubkey - } - - return authority.New(addrs, pubkeys), nil -} diff --git a/dela/core/ordering/cosipbft/authority/json/json_test.go b/dela/core/ordering/cosipbft/authority/json/json_test.go deleted file mode 100644 index ba9678c..0000000 --- a/dela/core/ordering/cosipbft/authority/json/json_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func TestChangeSetFormat_Encode(t *testing.T) { - cset := authority.NewChangeSet() - cset.Remove(42) - cset.Add(fake.NewAddress(2), fake.PublicKey{}) - - format := changeSetFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - - data, err := format.Encode(ctx, cset) - require.NoError(t, err) - expected := `{"Remove":[42],"Addresses":["AgAAAA=="],"PublicKeys":[{}]}` - require.Equal(t, expected, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), cset) - require.EqualError(t, err, fake.Err("couldn't marshal")) - - cset = authority.NewChangeSet() - cset.Add(fake.NewAddress(0), fake.NewBadPublicKey()) - _, err = format.Encode(ctx, cset) - require.EqualError(t, err, fake.Err("couldn't serialize public key")) - - cset = authority.NewChangeSet() - cset.Add(fake.NewBadAddress(), fake.PublicKey{}) - _, err = format.Encode(ctx, cset) - require.EqualError(t, err, fake.Err("couldn't serialize address")) -} - -func TestChangeSetFormat_Decode(t *testing.T) { - format := changeSetFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - ctx = serde.WithFactory(ctx, authority.AddrKeyFac{}, fake.AddressFactory{}) - ctx = serde.WithFactory(ctx, authority.PubKeyFac{}, fake.PublicKeyFactory{}) - - cset := authority.NewChangeSet() - cset.Add(fake.NewAddress(0), fake.PublicKey{}) - - msg, err := format.Decode(ctx, []byte(`{"Addresses":[[]],"PublicKeys":[{}]}`)) - require.NoError(t, err) - require.Equal(t, cset, msg) - - cset = authority.NewChangeSet() - cset.Remove(1) - cset.Remove(2) - cset.Remove(3) - - msg, err = format.Decode(ctx, []byte(`{"Remove":[1,2,3]}`)) - require.NoError(t, err) - require.Equal(t, cset, msg) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize change set")) - - badCtx := serde.WithFactory(ctx, authority.PubKeyFac{}, fake.NewBadPublicKeyFactory()) - _, err = format.Decode(badCtx, []byte(`{"Addresses":[[]],"PublicKeys":[{}]}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize public key")) - - badCtx = serde.WithFactory(ctx, authority.AddrKeyFac{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Addresses":[[]],"PublicKeys":[{}]}`)) - require.EqualError(t, err, "invalid address factory of type ''") - - badCtx = serde.WithFactory(ctx, authority.PubKeyFac{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Addresses":[[]],"PublicKeys":[{}]}`)) - require.EqualError(t, err, "invalid public key factory of type ''") -} - -func TestRosterFormat_Encode(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(1, fake.NewSigner)) - - format := rosterFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - - data, err := format.Encode(ctx, ro) - require.NoError(t, err) - require.Equal(t, `[{"Address":"AAAAAA==","PublicKey":{}}]`, string(data)) - - _, err = format.Encode(fake.NewContext(), fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), ro) - require.EqualError(t, err, fake.Err("couldn't marshal")) - - ro = authority.New([]mino.Address{fake.NewBadAddress()}, nil) - _, err = format.Encode(ctx, ro) - require.EqualError(t, err, fake.Err("couldn't marshal address")) - - ro = authority.New([]mino.Address{fake.NewAddress(0)}, []crypto.PublicKey{fake.NewBadPublicKey()}) - _, err = format.Encode(ctx, ro) - require.EqualError(t, err, fake.Err("couldn't serialize public key")) -} - -func TestRosterFormat_Decode(t *testing.T) { - format := rosterFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - ctx = serde.WithFactory(ctx, authority.AddrKeyFac{}, fake.AddressFactory{}) - ctx = serde.WithFactory(ctx, authority.PubKeyFac{}, fake.PublicKeyFactory{}) - - ro, err := format.Decode(ctx, []byte(`[{}]`)) - require.NoError(t, err) - require.Equal(t, authority.FromAuthority(fake.NewAuthority(1, fake.NewSigner)), ro) - - _, err = format.Decode(fake.NewBadContext(), []byte(`[]`)) - require.EqualError(t, err, fake.Err("couldn't deserialize roster")) - - badCtx := serde.WithFactory(ctx, authority.PubKeyFac{}, fake.NewBadPublicKeyFactory()) - _, err = format.Decode(badCtx, []byte(`[{}]`)) - require.EqualError(t, err, fake.Err("couldn't deserialize public key")) - - badCtx = serde.WithFactory(ctx, authority.AddrKeyFac{}, nil) - _, err = format.Decode(badCtx, []byte(`[{}]`)) - require.EqualError(t, err, "invalid address factory of type ''") - - badCtx = serde.WithFactory(ctx, authority.PubKeyFac{}, nil) - _, err = format.Decode(badCtx, []byte(`[{}]`)) - require.EqualError(t, err, "invalid public key factory of type ''") -} diff --git a/dela/core/ordering/cosipbft/authority/roster.go b/dela/core/ordering/cosipbft/authority/roster.go deleted file mode 100644 index a060c99..0000000 --- a/dela/core/ordering/cosipbft/authority/roster.go +++ /dev/null @@ -1,303 +0,0 @@ -// This file contains the implementation of a collective authority. -// -// Documentation Last Review: 13.10.2020 -// - -package authority - -import ( - "io" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var rosterFormats = registry.NewSimpleRegistry() - -// RegisterRosterFormat registers the engine for the provided format. -func RegisterRosterFormat(c serde.Format, f serde.FormatEngine) { - rosterFormats.Register(c, f) -} - -// iterator is a generic implementation of an iterator over a list of conodes. -type iterator struct { - index int - roster *Roster -} - -func (i *iterator) Seek(index int) { - i.index = index -} - -func (i *iterator) HasNext() bool { - return i.index < i.roster.Len() -} - -func (i *iterator) GetNext() int { - res := i.index - i.index++ - return res -} - -// addressIterator is an iterator for a list of addresses. -// -// - implements mino.AddressIterator -type addressIterator struct { - *iterator -} - -// GetNext implements mino.AddressIterator. It returns the next address. -func (i *addressIterator) GetNext() mino.Address { - if i.iterator.HasNext() { - return i.roster.addrs[i.iterator.GetNext()] - } - return nil -} - -// publicKeyIterator is an iterator for a list of public keys. -// -// - implements crypto.PublicKeyIterator -type publicKeyIterator struct { - *iterator -} - -// GetNext implements crypto.PublicKeyIterator. It returns the next public key. -func (i *publicKeyIterator) GetNext() crypto.PublicKey { - if i.iterator.HasNext() { - return i.roster.pubkeys[i.iterator.GetNext()] - } - return nil -} - -// Roster contains a list of participants with their addresses and public keys. -// -// - implements authority.Authority -type Roster struct { - addrs []mino.Address - pubkeys []crypto.PublicKey -} - -// New creates a new roster from the list of addresses and public keys. -func New(addrs []mino.Address, pubkeys []crypto.PublicKey) Roster { - return Roster{ - addrs: addrs, - pubkeys: pubkeys, - } -} - -// FromAuthority returns a viewchange roster from a collective authority. -func FromAuthority(authority crypto.CollectiveAuthority) Roster { - addrs := make([]mino.Address, authority.Len()) - pubkeys := make([]crypto.PublicKey, authority.Len()) - - addrIter := authority.AddressIterator() - pubkeyIter := authority.PublicKeyIterator() - for i := 0; addrIter.HasNext() && pubkeyIter.HasNext(); i++ { - addrs[i] = addrIter.GetNext() - pubkeys[i] = pubkeyIter.GetNext() - } - - return New(addrs, pubkeys) -} - -// Fingerprint implements serde.Fingerprinter. It marshals the roster and writes -// the result in the given writer. -func (r Roster) Fingerprint(w io.Writer) error { - for i, addr := range r.addrs { - data, err := addr.MarshalText() - if err != nil { - return xerrors.Errorf("couldn't marshal address: %v", err) - } - - _, err = w.Write(data) - if err != nil { - return xerrors.Errorf("couldn't write address: %v", err) - } - - data, err = r.pubkeys[i].MarshalBinary() - if err != nil { - return xerrors.Errorf("couldn't marshal public key: %v", err) - } - - _, err = w.Write(data) - if err != nil { - return xerrors.Errorf("couldn't write public key: %v", err) - } - } - - return nil -} - -// Take implements mino.Players. It returns a subset of the roster according to -// the filter. -func (r Roster) Take(updaters ...mino.FilterUpdater) mino.Players { - filter := mino.ApplyFilters(updaters) - newRoster := Roster{ - addrs: make([]mino.Address, len(filter.Indices)), - pubkeys: make([]crypto.PublicKey, len(filter.Indices)), - } - - for i, k := range filter.Indices { - newRoster.addrs[i] = r.addrs[k] - newRoster.pubkeys[i] = r.pubkeys[k] - } - - return newRoster -} - -// Apply implements authority.Authority. It returns a new authority after -// applying the change set. The removals must be sorted by descending order and -// unique or the behaviour will be undefined. -func (r Roster) Apply(in ChangeSet) Authority { - changeset, ok := in.(*RosterChangeSet) - if !ok { - dela.Logger.Warn().Msgf("Change set '%T' is not supported. Ignoring.", in) - return r - } - - addrs := make([]mino.Address, r.Len()) - pubkeys := make([]crypto.PublicKey, r.Len()) - - for i, addr := range r.addrs { - addrs[i] = addr - pubkeys[i] = r.pubkeys[i] - } - - for _, i := range changeset.remove { - if int(i) < len(addrs) { - addrs = append(addrs[:i], addrs[i+1:]...) - pubkeys = append(pubkeys[:i], pubkeys[i+1:]...) - } - } - - roster := Roster{ - addrs: append(addrs, changeset.addrs...), - pubkeys: append(pubkeys, changeset.pubkeys...), - } - - return roster -} - -// Diff implements authority.Authority. It returns the change set that must be -// applied to the current authority to get the given one. -func (r Roster) Diff(o Authority) ChangeSet { - changeset := NewChangeSet() - - other, ok := o.(Roster) - if !ok { - return changeset - } - - i := 0 - k := 0 - for i < len(r.addrs) || k < len(other.addrs) { - if i < len(r.addrs) && k < len(other.addrs) { - if r.addrs[i].Equal(other.addrs[k]) { - i++ - k++ - } else { - changeset.remove = append(changeset.remove, uint(i)) - i++ - } - } else if i < len(r.addrs) { - changeset.remove = append(changeset.remove, uint(i)) - i++ - } else { - changeset.addrs = append(changeset.addrs, other.addrs[k]) - changeset.pubkeys = append(changeset.pubkeys, other.pubkeys[k]) - k++ - } - } - - return changeset -} - -// Len implements mino.Players. It returns the length of the authority. -func (r Roster) Len() int { - return len(r.addrs) -} - -// GetPublicKey implements crypto.CollectiveAuthority. It returns the public key -// of the address if it exists, nil otherwise. The second return is the index of -// the public key in the authority. -func (r Roster) GetPublicKey(target mino.Address) (crypto.PublicKey, int) { - for i, addr := range r.addrs { - if addr.Equal(target) { - return r.pubkeys[i], i - } - } - - return nil, -1 -} - -// AddressIterator implements mino.Players. It returns an iterator of the -// addresses of the roster in a deterministic order. -func (r Roster) AddressIterator() mino.AddressIterator { - return &addressIterator{iterator: &iterator{roster: &r}} -} - -// PublicKeyIterator implements crypto.CollectiveAuthority. It returns an -// iterator of the public keys of the roster in a deterministic order. -func (r Roster) PublicKeyIterator() crypto.PublicKeyIterator { - return &publicKeyIterator{iterator: &iterator{roster: &r}} -} - -// Serialize implements serde.Message. It returns the serialized data for this -// roster. -func (r Roster) Serialize(ctx serde.Context) ([]byte, error) { - format := rosterFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, r) - if err != nil { - return nil, xerrors.Errorf("couldn't encode roster: %v", err) - } - - return data, nil -} - -// rosterFac is a factory to deserialize authority. -// -// - implements authority.Factory -type rosterFac struct { - addrFactory mino.AddressFactory - pubkeyFactory crypto.PublicKeyFactory -} - -// NewFactory creates a new instance of the authority factory. -func NewFactory(af mino.AddressFactory, pf crypto.PublicKeyFactory) Factory { - return rosterFac{ - addrFactory: af, - pubkeyFactory: pf, - } -} - -// Deserialize implements serde.Factory. It returns the roster from the data if -// appropriate, otherwise an error. -func (f rosterFac) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.AuthorityOf(ctx, data) -} - -// AuthorityOf implements authority.AuthorityFactory. It returns the roster from -// the data if appropriate, otherwise an error. -func (f rosterFac) AuthorityOf(ctx serde.Context, data []byte) (Authority, error) { - format := rosterFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, PubKeyFac{}, f.pubkeyFactory) - ctx = serde.WithFactory(ctx, AddrKeyFac{}, f.addrFactory) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode roster: %v", err) - } - - roster, ok := msg.(Roster) - if !ok { - return nil, xerrors.Errorf("invalid message of type '%T'", msg) - } - - return roster, nil -} diff --git a/dela/core/ordering/cosipbft/authority/roster_test.go b/dela/core/ordering/cosipbft/authority/roster_test.go deleted file mode 100644 index 0919825..0000000 --- a/dela/core/ordering/cosipbft/authority/roster_test.go +++ /dev/null @@ -1,240 +0,0 @@ -package authority - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func init() { - RegisterRosterFormat(fake.GoodFormat, fake.Format{Msg: Roster{}}) - RegisterRosterFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterRosterFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestIterator_Seek(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - iter := &iterator{ - roster: &roster, - } - - iter.Seek(2) - require.True(t, iter.HasNext()) - iter.Seek(3) - require.False(t, iter.HasNext()) -} - -func TestIterator_HasNext(t *testing.T) { - iter := &iterator{ - roster: &Roster{addrs: make([]mino.Address, 3)}, - } - - require.True(t, iter.HasNext()) - - iter.index = 1 - require.True(t, iter.HasNext()) - - iter.index = 2 - require.True(t, iter.HasNext()) - - iter.index = 3 - require.False(t, iter.HasNext()) - - iter.index = 10 - require.False(t, iter.HasNext()) -} - -func TestIterator_GetNext(t *testing.T) { - iter := &iterator{ - roster: &Roster{addrs: make([]mino.Address, 3)}, - } - - for i := 0; i < 3; i++ { - c := iter.GetNext() - require.NotNil(t, c) - } - - require.Equal(t, 3, iter.GetNext()) -} - -func TestAddressIterator_GetNext(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - iter := &addressIterator{ - iterator: &iterator{ - roster: &roster, - }, - } - - for _, target := range roster.addrs { - addr := iter.GetNext() - require.Equal(t, target, addr) - } - - require.Nil(t, iter.GetNext()) -} - -func TestPublicKeyIterator_GetNext(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - iter := &publicKeyIterator{ - iterator: &iterator{ - roster: &roster, - }, - } - - for _, target := range roster.pubkeys { - pubkey := iter.GetNext() - require.Equal(t, target, pubkey) - } - - require.Nil(t, iter.GetNext()) -} - -func TestRoster_Fingerprint(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(2, fake.NewSigner)) - - out := new(bytes.Buffer) - err := roster.Fingerprint(out) - require.NoError(t, err) - require.Equal(t, "\x00\x00\x00\x00PK\x01\x00\x00\x00PK", out.String()) - - roster.addrs[0] = fake.NewBadAddress() - err = roster.Fingerprint(out) - require.EqualError(t, err, fake.Err("couldn't marshal address")) - - roster.addrs[0] = fake.NewAddress(0) - roster.pubkeys[0] = fake.NewBadPublicKey() - err = roster.Fingerprint(out) - require.EqualError(t, err, fake.Err("couldn't marshal public key")) - - roster.pubkeys[0] = fake.PublicKey{} - err = roster.Fingerprint(fake.NewBadHash()) - require.EqualError(t, err, fake.Err("couldn't write address")) - - err = roster.Fingerprint(fake.NewBadHashWithDelay(1)) - require.EqualError(t, err, fake.Err("couldn't write public key")) -} - -func TestRoster_Take(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - roster2 := roster.Take(mino.RangeFilter(1, 2)) - require.Equal(t, 1, roster2.Len()) - - roster2 = roster.Take(mino.RangeFilter(1, 3)) - require.Equal(t, 2, roster2.Len()) -} - -func TestRoster_Apply(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - require.Equal(t, roster, roster.Apply(nil)) - - cset := NewChangeSet() - cset.Remove(3) - cset.Remove(2) - cset.Remove(0) - - roster2 := roster.Apply(cset) - require.Equal(t, roster.Len()-2, roster2.Len()) - - cset = NewChangeSet() - cset.Add(fake.NewAddress(5), fake.PublicKey{}) - - roster3 := roster2.Apply(cset) - require.Equal(t, roster.Len()-1, roster3.Len()) -} - -func TestRoster_Diff(t *testing.T) { - roster1 := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - roster2 := FromAuthority(fake.NewAuthority(4, fake.NewSigner)) - diff := roster1.Diff(roster2).(*RosterChangeSet) - require.Len(t, diff.addrs, 1) - require.Len(t, diff.pubkeys, 1) - - roster3 := FromAuthority(fake.NewAuthority(2, fake.NewSigner)) - diff = roster1.Diff(roster3).(*RosterChangeSet) - require.Len(t, diff.remove, 1) - - roster4 := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - roster4.addrs[1] = fake.NewAddress(5) - diff = roster1.Diff(roster4).(*RosterChangeSet) - require.Equal(t, []uint{1, 2}, diff.remove) - require.Len(t, diff.addrs, 2) - require.Len(t, diff.pubkeys, 2) - - diff = roster1.Diff((Authority)(nil)).(*RosterChangeSet) - require.Equal(t, NewChangeSet(), diff) -} - -func TestRoster_Len(t *testing.T) { - roster := FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - require.Equal(t, 3, roster.Len()) -} - -func TestRoster_GetPublicKey(t *testing.T) { - authority := fake.NewAuthority(3, fake.NewSigner) - roster := FromAuthority(authority) - - iter := roster.AddressIterator() - i := 0 - for iter.HasNext() { - pubkey, index := roster.GetPublicKey(iter.GetNext()) - require.Equal(t, authority.GetSigner(i).GetPublicKey(), pubkey) - require.Equal(t, i, index) - i++ - } - - pubkey, index := roster.GetPublicKey(fake.NewAddress(999)) - require.Equal(t, -1, index) - require.Nil(t, pubkey) -} - -func TestRoster_AddressIterator(t *testing.T) { - authority := fake.NewAuthority(3, fake.NewSigner) - roster := FromAuthority(authority) - - iter := roster.AddressIterator() - for i := 0; iter.HasNext(); i++ { - require.Equal(t, authority.GetAddress(i), iter.GetNext()) - } -} - -func TestRoster_PublicKeyIterator(t *testing.T) { - authority := fake.NewAuthority(3, bls.Generate) - roster := FromAuthority(authority) - - iter := roster.PublicKeyIterator() - for i := 0; iter.HasNext(); i++ { - require.Equal(t, authority.GetSigner(i).GetPublicKey(), iter.GetNext()) - } -} - -func TestRoster_Serialize(t *testing.T) { - roster := Roster{} - - data, err := roster.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = roster.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode roster")) -} - -func TestFactory_Deserialize(t *testing.T) { - factory := NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, Roster{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode roster")) - - _, err = factory.Deserialize(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid message of type 'fake.Message'") -} diff --git a/dela/core/ordering/cosipbft/blockstore/blockstore.go b/dela/core/ordering/cosipbft/blockstore/blockstore.go deleted file mode 100644 index c002c24..0000000 --- a/dela/core/ordering/cosipbft/blockstore/blockstore.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package blockstore defines the different storage the ordering service is -// using. -// -// The block store defines the primitives to store a block and read one from the -// disk. It also provide an API to read a chain from the genesis block to the -// latest block. It is important to notice that a block is stored alongside the -// link that has been created during the consensus. -// -// The tree cache stores the latest state of the tree, which is modified after -// each new block. -// -// The genesis store allows to set a definitive genesis block and persist it so -// that it can be reloaded later on. -// -// Documentation Last Review: 13.10.2020 -package blockstore - -import ( - "context" - "errors" - - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" -) - -// ErrNoBlock is the error message returned when the block is unknown. -var ErrNoBlock = errors.New("no block") - -// TreeCache is a cache to store a tree that needs to be accessed in different -// places. -type TreeCache interface { - // Get returns the current value of the cache. - Get() hashtree.Tree - - // GetWithLock implements blockstore.TreeCache. It returns the current value - // of the cache alongside a function to unlock the cache. It allows one to - // delay a set while fetching associated data. The function returned must be - // called. - GetWithLock() (tree hashtree.Tree, unlock func()) - - // Set sets a new tree in the cache. - Set(hashtree.Tree) - - // SetWithLock implements blockstore.TreeCache. It sets the tree while - // holding the lock and returns a function to unlock it. It allows one to - // prevent an access until associated data is updated. The function returned - // must be called. - SetWithLock(hashtree.Tree) (unlock func()) -} - -// GenesisStore is the interface to store and get the genesis block. It is left -// to the implementation to persist it. -type GenesisStore interface { - // Get must return the genesis block if it is set, otherwise an error. - Get() (types.Genesis, error) - - // Set must set the genesis block. - Set(types.Genesis) error - - // Exists returns true if the genesis is already set. - Exists() bool -} - -// BlockStore is the interface to store and get blocks. -type BlockStore interface { - // Len must return the length of the store. - Len() uint64 - - // Store must store the block link only if it matches the latest link, - // otherwise it must return an error. - Store(types.BlockLink) error - - // Get must return the block link associated to the digest, or an error. - Get(id types.Digest) (types.BlockLink, error) - - // GetByIndex return the block link associated to the index, or an error. - GetByIndex(index uint64) (types.BlockLink, error) - - // GetChain returns a chain of the blocks. It can be used to prove the - // integrity of the last block from the genesis. - GetChain() (types.Chain, error) - - // Last must return the latest block link in the store. - Last() (types.BlockLink, error) - - // Watch returns a channel that is filled with new block links. The is - // closed as soon as the context is done. - Watch(context.Context) <-chan types.BlockLink - - // WithTx returns a block store that is using the transaction to perform - // operations on the database. - WithTx(store.Transaction) BlockStore -} diff --git a/dela/core/ordering/cosipbft/blockstore/disk.go b/dela/core/ordering/cosipbft/blockstore/disk.go deleted file mode 100644 index d07c503..0000000 --- a/dela/core/ordering/cosipbft/blockstore/disk.go +++ /dev/null @@ -1,312 +0,0 @@ -// This file contains the implementation of a persistent block store. It stores -// the blocks and their links to a key/value database. -// -// Documentation Last Review: 13.10.2020 -// - -package blockstore - -import ( - "context" - "encoding/binary" - "sync" - - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -type cachedData struct { - sync.Mutex - - length uint64 - last types.BlockLink - indices map[types.Digest]uint64 -} - -// InDisk is a persistent storage implementation for the blocks. -// -// - implements blockstore.BlockStore -type InDisk struct { - *cachedData - - db kv.DB - bucket []byte - context serde.Context - fac types.LinkFactory - watcher core.Observable - - txn store.Transaction -} - -// NewDiskStore creates a new persistent storage. -func NewDiskStore(db kv.DB, fac types.LinkFactory) *InDisk { - return &InDisk{ - db: db, - bucket: []byte("blocks"), - context: json.NewContext(), - fac: fac, - watcher: core.NewWatcher(), - cachedData: &cachedData{ - indices: make(map[types.Digest]uint64), - }, - } -} - -// Len implements blockstore.BlockStore. It returns the number of blocks stored -// in the database. -func (s *InDisk) Len() uint64 { - s.Lock() - defer s.Unlock() - - return s.length -} - -// Load reads the database to rebuild the cache. -func (s *InDisk) Load() error { - s.Lock() - defer s.Unlock() - - return s.doView(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(s.bucket) - if bucket == nil { - return nil - } - - err := bucket.Scan([]byte{}, func(key, value []byte) error { - link, err := s.fac.BlockLinkOf(s.context, value) - if err != nil { - return xerrors.Errorf("malformed block: %v", err) - } - - s.length++ - s.last = link - s.indices[link.GetBlock().GetHash()] = link.GetBlock().GetIndex() - - return nil - }) - - if err != nil { - return xerrors.Errorf("while scanning: %v", err) - } - - return nil - }) -} - -// Store implements blockstore.BlockStore. It stores the link in the database if -// it matches the latest link. -func (s *InDisk) Store(link types.BlockLink) error { - s.Lock() - last := s.last - s.Unlock() - - if last != nil && last.GetTo() != link.GetFrom() { - return xerrors.Errorf("mismatch digests '%v' (new) != '%v' (last)", - link.GetFrom(), last.GetTo()) - } - - data, err := link.Serialize(s.context) - if err != nil { - return xerrors.Errorf("failed to serialize: %v", err) - } - - return s.doUpdate(func(tx kv.WritableTx) error { - bucket, err := tx.GetBucketOrCreate(s.bucket) - if err != nil { - return xerrors.Errorf("bucket failed: %v", err) - } - - index := link.GetBlock().GetIndex() - - key := s.makeKey(index) - - err = bucket.Set(key, data) - if err != nil { - return xerrors.Errorf("while writing: %v", err) - } - - tx.OnCommit(func() { - s.Lock() - - s.length++ - s.last = link - s.indices[link.GetBlock().GetHash()] = index - - s.Unlock() - - s.watcher.Notify(link) - }) - - return nil - }) -} - -// Get implements blockstore.BlockStore. It loads the block with the given -// identifier if it exists, otherwise it returns an error. -func (s *InDisk) Get(id types.Digest) (types.BlockLink, error) { - s.Lock() - index, found := s.indices[id] - s.Unlock() - - if !found { - return nil, xerrors.Errorf("'%v' not found: %w", id, ErrNoBlock) - } - - return s.GetByIndex(index) -} - -// GetByIndex implements blockstore.BlockStore. It returns the block associated -// to the index if it exists, otherwise it returns an error. -func (s *InDisk) GetByIndex(index uint64) (link types.BlockLink, err error) { - key := s.makeKey(index) - - err = s.doView(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(s.bucket) - - value := bucket.Get(key) - - if len(value) == 0 { - return xerrors.Errorf("index %d not found: %w", index, ErrNoBlock) - } - - var err error - link, err = s.fac.BlockLinkOf(s.context, value) - if err != nil { - return xerrors.Errorf("malformed block: %v", err) - } - - return nil - }) - - return -} - -// GetChain implements blockstore.Blockstore. It returns a chain to the latest -// block. -func (s *InDisk) GetChain() (types.Chain, error) { - s.Lock() - length := s.length - s.Unlock() - - if length == 0 { - return nil, xerrors.New("store is empty") - } - - prevs := make([]types.Link, length-1) - - var chain types.Chain - - err := s.doView(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(s.bucket) - - i := uint64(0) - err := bucket.Scan([]byte{}, func(key, value []byte) error { - if i >= length-1 { - link, err := s.fac.BlockLinkOf(s.context, value) - if err != nil { - return xerrors.Errorf("block malformed: %v", err) - } - - chain = types.NewChain(link, prevs) - return nil - } - - link, err := s.fac.LinkOf(s.context, value) - if err != nil { - return xerrors.Errorf("link malformed: %v", err) - } - - prevs[i] = link - i++ - - return nil - }) - - if err != nil { - return xerrors.Errorf("while scanning: %v", err) - } - - return nil - }) - - if err != nil { - return nil, xerrors.Errorf("while reading database: %v", err) - } - - return chain, nil -} - -// Last implements blockstore.BlockStore. It returns the last block stored in -// the database. -func (s *InDisk) Last() (types.BlockLink, error) { - s.Lock() - defer s.Unlock() - - if s.length == 0 { - return nil, xerrors.Errorf("store is empty: %w", ErrNoBlock) - } - - return s.last, nil -} - -// Watch implements blockstore.BlockStore. It returns a channel populated with -// new blocks stored. -func (s *InDisk) Watch(ctx context.Context) <-chan types.BlockLink { - obs := newObserver(ctx, s.watcher) - - return obs.ch -} - -// WithTx implements blockstore.BlockStore. It returns a store that will use the -// transaction for the operations on the database. -func (s *InDisk) WithTx(txn store.Transaction) BlockStore { - store := &InDisk{ - db: s.db, - bucket: s.bucket, - context: s.context, - fac: s.fac, - watcher: s.watcher, - cachedData: s.cachedData, - txn: txn, - } - - return store -} - -func (s *InDisk) doUpdate(fn func(tx kv.WritableTx) error) error { - if s.txn != nil { - tx, ok := s.txn.(kv.WritableTx) - if !ok { - return xerrors.Errorf("transaction '%T' is not writable", s.txn) - } - - return fn(tx) - } - - return s.db.Update(fn) -} - -func (s *InDisk) doView(fn func(tx kv.ReadableTx) error) error { - if s.txn != nil { - tx, ok := s.txn.(kv.ReadableTx) - if !ok { - return xerrors.Errorf("transaction '%T' is not readable", s.txn) - } - - return fn(tx) - } - - return s.db.View(fn) -} - -func (s *InDisk) makeKey(index uint64) []byte { - key := make([]byte, 8) - binary.LittleEndian.PutUint64(key, index) - - return key -} diff --git a/dela/core/ordering/cosipbft/blockstore/disk_test.go b/dela/core/ordering/cosipbft/blockstore/disk_test.go deleted file mode 100644 index 6ac6c70..0000000 --- a/dela/core/ordering/cosipbft/blockstore/disk_test.go +++ /dev/null @@ -1,299 +0,0 @@ -package blockstore - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestInDisk_Len(t *testing.T) { - store := NewDiskStore(nil, nil) - require.Equal(t, uint64(0), store.Len()) - - store.length = 5 - require.Equal(t, uint64(5), store.Len()) -} - -func TestInDisk_Load(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - err := store.Load() - require.NoError(t, err) - - err = store.Store(makeLink(t, types.Digest{})) - require.NoError(t, err) - - err = store.Store(makeLink(t, store.last.GetTo())) - require.NoError(t, err) - - newStore := NewDiskStore(db, makeBlockFac()) - - err = newStore.Load() - require.NoError(t, err) - - store.fac = badLinkFac{} - err = store.Load() - require.EqualError(t, err, fake.Err("while scanning: malformed block")) -} - -func TestInDisk_Store(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - err := store.Store(makeLink(t, types.Digest{}, types.WithIndex(0))) - require.NoError(t, err) - require.Equal(t, uint64(1), store.length) - require.NotNil(t, store.last) - require.Len(t, store.indices, 1) - - err = store.Store(makeLink(t, store.last.GetTo(), types.WithIndex(1))) - require.NoError(t, err) - require.Equal(t, uint64(2), store.length) - require.Len(t, store.indices, 2) - - err = store.Store(makeLink(t, types.Digest{})) - require.EqualError(t, err, "mismatch digests '00000000' (new) != 'b68f5931' (last)") - - store = NewDiskStore(db, makeBlockFac()) - err = store.Store(badLink{}) - require.EqualError(t, err, fake.Err("failed to serialize")) - - store.db = badDB{} - err = store.Store(makeLink(t, types.Digest{})) - require.EqualError(t, err, fake.Err("bucket failed")) - - store.db = badDB{bucket: badBucket{}} - err = store.Store(makeLink(t, types.Digest{})) - require.EqualError(t, err, fake.Err("while writing")) -} - -func TestInDisk_Get(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - link, err := store.Get(types.Digest{}) - require.EqualError(t, err, "'00000000' not found: no block") - require.Nil(t, link) - - err = store.Store(makeLink(t, types.Digest{}, types.WithIndex(2))) - require.NoError(t, err) - - link, err = store.Get(store.last.GetTo()) - require.NoError(t, err) - require.Equal(t, uint64(2), link.GetBlock().GetIndex()) -} - -func TestInDisk_GetByIndex(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - err := store.Store(makeLink(t, types.Digest{}, types.WithIndex(0))) - require.NoError(t, err) - - err = store.Store(makeLink(t, store.last.GetTo(), types.WithIndex(1))) - require.NoError(t, err) - - link, err := store.GetByIndex(1) - require.NoError(t, err) - require.Equal(t, uint64(1), link.GetBlock().GetIndex()) - - _, err = store.GetByIndex(3) - require.EqualError(t, err, "index 3 not found: no block") - - store.fac = badLinkFac{} - _, err = store.GetByIndex(0) - require.EqualError(t, err, fake.Err("malformed block")) -} - -func TestInDisk_GetChain(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - _, err := store.GetChain() - require.EqualError(t, err, "store is empty") - - err = store.Store(makeLink(t, types.Digest{}, types.WithIndex(0))) - require.NoError(t, err) - - err = store.Store(makeLink(t, store.last.GetTo(), types.WithIndex(1))) - require.NoError(t, err) - - chain, err := store.GetChain() - require.NoError(t, err) - require.Equal(t, uint64(1), chain.GetBlock().GetIndex()) - - store.fac = badLinkFac{} - _, err = store.GetChain() - require.EqualError(t, err, fake.Err("while reading database: while scanning: link malformed")) -} - -func TestInDisk_GetChain_BadBlockLink(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - err := store.Store(makeLink(t, types.Digest{}, types.WithIndex(0))) - require.NoError(t, err) - - store.fac = badLinkFac{} - _, err = store.GetChain() - require.EqualError(t, err, fake.Err("while reading database: while scanning: block malformed")) -} - -func TestInDisk_Last(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - last, err := store.Last() - require.EqualError(t, err, "store is empty: no block") - require.Nil(t, last) - - store.length = 1 - store.last = makeLink(t, types.Digest{}) - last, err = store.Last() - require.NoError(t, err) - require.NotNil(t, last) -} - -func TestInDisk_Watch(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - links := store.Watch(ctx) - - err := store.Store(makeLink(t, types.Digest{}, types.WithIndex(2))) - require.NoError(t, err) - - link := <-links - require.Equal(t, uint64(2), link.GetBlock().GetIndex()) - - cancel() - _, more := <-links - require.False(t, more) -} - -func TestInDisk_WithTx(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - store := NewDiskStore(db, makeBlockFac()) - - err := db.Update(func(tx kv.WritableTx) error { - next := store.WithTx(tx) - err := next.Store(makeLink(t, types.Digest{})) - require.NoError(t, err) - - link, err := next.GetByIndex(0) - require.NoError(t, err) - require.NotNil(t, link) - - return nil - }) - require.NoError(t, err) - require.Equal(t, uint64(1), store.length) - - next := NewDiskStore(db, makeBlockFac()).WithTx(dummyTx{}) - - err = next.Store(makeLink(t, types.Digest{})) - require.EqualError(t, err, "transaction 'blockstore.dummyTx' is not writable") - - _, err = next.GetByIndex(0) - require.EqualError(t, err, "transaction 'blockstore.dummyTx' is not readable") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeDB(t *testing.T) (kv.DB, func()) { - file, err := os.CreateTemp(os.TempDir(), "dela-blockstore") - require.NoError(t, err) - - db, err := kv.New(file.Name()) - require.NoError(t, err) - - clean := func() { - db.Close() - file.Close() - } - - return db, clean -} - -func makeBlockFac() types.LinkFactory { - blockFac := types.NewBlockFactory(fakeResultFac{}) - - return types.NewLinkFactory(blockFac, fake.SignatureFactory{}, fakeCsFac{}) -} - -type fakeCsFac struct { - authority.ChangeSetFactory -} - -func (fakeCsFac) ChangeSetOf(serde.Context, []byte) (authority.ChangeSet, error) { - return authority.NewChangeSet(), nil -} - -type fakeResultFac struct { - validation.ResultFactory -} - -func (fakeResultFac) ResultOf(serde.Context, []byte) (validation.Result, error) { - return simple.NewResult(nil), nil -} - -type badLinkFac struct { - types.LinkFactory -} - -func (badLinkFac) BlockLinkOf(serde.Context, []byte) (types.BlockLink, error) { - return nil, fake.GetError() -} - -func (badLinkFac) LinkOf(serde.Context, []byte) (types.Link, error) { - return nil, fake.GetError() -} - -type badLink struct { - types.BlockLink -} - -func (badLink) GetFrom() types.Digest { - return types.Digest{} -} - -func (badLink) Serialize(serde.Context) ([]byte, error) { - return nil, fake.GetError() -} - -type dummyTx struct { - store.Transaction -} diff --git a/dela/core/ordering/cosipbft/blockstore/genesis.go b/dela/core/ordering/cosipbft/blockstore/genesis.go deleted file mode 100644 index 3b03c9e..0000000 --- a/dela/core/ordering/cosipbft/blockstore/genesis.go +++ /dev/null @@ -1,145 +0,0 @@ -// This file contains the implementations of a genesis store. An in-memory and a -// persistent implementation are available. -// -// Documentation Last Review: 13.10.2020 -// - -package blockstore - -import ( - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -var genesisBucket = []byte("blockstore-genesis") -var genesisKey = []byte("block") - -// CachedGenesis is a store to keep a genesis block in cache. -// -// - implements blockstore.GenesisStore -type cachedGenesis struct { - genesis types.Genesis - set bool -} - -// NewGenesisStore returns a new empty genesis store. -func NewGenesisStore() GenesisStore { - return &cachedGenesis{} -} - -// Get implements blockstore.GenesisStore. It returns the genesis block if it is -// set, otherwise it returns an error. -func (s *cachedGenesis) Get() (types.Genesis, error) { - if !s.set { - return types.Genesis{}, xerrors.New("missing genesis block") - } - - return s.genesis, nil -} - -// Set implements blockstore.GenesisStore. It sets the genesis block only if the -// cache is empty, otherwise it returns an error. -func (s *cachedGenesis) Set(genesis types.Genesis) error { - if s.set { - return xerrors.New("genesis block is already set") - } - - s.genesis = genesis - s.set = true - return nil -} - -// Exists implements blockstore.GenesisStore. It returns true if the genesis -// block is set. -func (s *cachedGenesis) Exists() bool { - return s.set -} - -// PersistentGenesisCache is a store to set a genesis block on disk. It also -// provides a function to load the block from the disk that will then stay in -// memory. -// -// - implements blockstore.GenesisStore -type PersistentGenesisCache struct { - *cachedGenesis - - db kv.DB - context serde.Context - fac serde.Factory -} - -// NewGenesisDiskStore creates a new store that will load or set the genesis -// block using the given database. -func NewGenesisDiskStore(db kv.DB, fac serde.Factory) PersistentGenesisCache { - return PersistentGenesisCache{ - cachedGenesis: &cachedGenesis{}, - db: db, - context: json.NewContext(), - fac: fac, - } -} - -// Load tries to read the genesis block in the database, and set it to memory -// only if it exists. It returns an error if the database cannot be read, or the -// value is malformed. -func (s PersistentGenesisCache) Load() error { - return s.db.View(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(genesisBucket) - if bucket == nil { - // Nothing in the database, so the load is successful but no genesis - // is set. - return nil - } - - data := bucket.Get(genesisKey) - - msg, err := s.fac.Deserialize(s.context, data) - if err != nil { - return xerrors.Errorf("malformed value: %v", err) - } - - genesis, ok := msg.(types.Genesis) - if !ok { - return xerrors.Errorf("unsupported message '%T'", msg) - } - - s.set = true - s.genesis = genesis - - return nil - }) -} - -// Set implements blockstore.GenesisStore. It writes the genesis block in -// disk, and then in memory for fast access. -func (s PersistentGenesisCache) Set(genesis types.Genesis) error { - if !s.set { - data, err := genesis.Serialize(s.context) - if err != nil { - return xerrors.Errorf("failed to serialize genesis: %v", err) - } - - err = s.db.Update(func(tx kv.WritableTx) error { - bucket, err := tx.GetBucketOrCreate(genesisBucket) - if err != nil { - return xerrors.Errorf("bucket: %v", err) - } - - err = bucket.Set(genesisKey, data) - if err != nil { - return xerrors.Errorf("while writing to bucket: %v", err) - } - - return nil - }) - - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - } - - return s.cachedGenesis.Set(genesis) -} diff --git a/dela/core/ordering/cosipbft/blockstore/genesis_test.go b/dela/core/ordering/cosipbft/blockstore/genesis_test.go deleted file mode 100644 index 9686a82..0000000 --- a/dela/core/ordering/cosipbft/blockstore/genesis_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package blockstore - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestCachedGenesis_Get(t *testing.T) { - store := NewGenesisStore().(*cachedGenesis) - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - _, err := store.Get() - require.EqualError(t, err, "missing genesis block") - - block, err := types.NewGenesis(ro) - require.NoError(t, err) - - store.set = true - store.genesis = block - - genesis, err := store.Get() - require.NoError(t, err) - require.Equal(t, block, genesis) -} - -func TestCachedGenesis_Set(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - block, err := types.NewGenesis(ro) - require.NoError(t, err) - - store := NewGenesisStore().(*cachedGenesis) - - err = store.Set(block) - require.NoError(t, err) - require.True(t, store.set) - - err = store.Set(block) - require.EqualError(t, err, "genesis block is already set") -} - -func TestCachedGenesis_Exists(t *testing.T) { - store := NewGenesisStore() - - require.False(t, store.Exists()) - - store.Set(types.Genesis{}) - require.True(t, store.Exists()) -} - -func TestGenesisDiskStore_Load(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-blockstore-") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - store := NewGenesisDiskStore(db, makeFac()) - - err = store.Load() - require.NoError(t, err) - require.False(t, store.set) - - err = store.Set(makeGenesis(t)) - require.NoError(t, err) - - // Reset the store. - store = NewGenesisDiskStore(db, makeFac()) - - err = store.Load() - require.NoError(t, err) - require.True(t, store.set) - - store.fac = fake.NewBadMessageFactory() - err = store.Load() - require.EqualError(t, err, fake.Err("malformed value")) - - store.fac = fake.MessageFactory{} - err = store.Load() - require.EqualError(t, err, "unsupported message 'fake.Message'") -} - -func TestGenesisDiskStore_Set(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-blockstore-") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - store := NewGenesisDiskStore(db, makeFac()) - - err = store.Set(makeGenesis(t)) - require.NoError(t, err) - - var data []byte - db.View(func(tx kv.ReadableTx) error { - data = tx.GetBucket(genesisBucket).Get(genesisKey) - return nil - }) - - require.NotEmpty(t, data) - - store = NewGenesisDiskStore(badDB{}, makeFac()) - err = store.Set(makeGenesis(t)) - require.EqualError(t, err, fake.Err("store failed: bucket")) - - store.db = badDB{bucket: badBucket{}} - err = store.Set(makeGenesis(t)) - require.EqualError(t, err, fake.Err("store failed: while writing to bucket")) - - store.context = fake.NewBadContext() - err = store.Set(makeGenesis(t)) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to serialize genesis: ") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeFac() types.GenesisFactory { - authFac := authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - - return types.NewGenesisFactory(authFac) -} - -func makeGenesis(t *testing.T) types.Genesis { - ro := authority.FromAuthority(fake.NewAuthority(1, fake.NewSigner)) - - genesis, err := types.NewGenesis(ro) - require.NoError(t, err) - - return genesis -} - -type badBucket struct { - kv.Bucket -} - -func (badBucket) Set(key, value []byte) error { - return fake.GetError() -} - -type badTx struct { - kv.WritableTx - - bucket kv.Bucket -} - -func (tx badTx) GetBucketOrCreate(name []byte) (kv.Bucket, error) { - if tx.bucket != nil { - return tx.bucket, nil - } - - return nil, fake.GetError() -} - -type badDB struct { - kv.DB - - bucket kv.Bucket -} - -func (db badDB) Update(fn func(kv.WritableTx) error) error { - return fn(badTx{bucket: db.bucket}) -} diff --git a/dela/core/ordering/cosipbft/blockstore/mem.go b/dela/core/ordering/cosipbft/blockstore/mem.go deleted file mode 100644 index 57d0cb0..0000000 --- a/dela/core/ordering/cosipbft/blockstore/mem.go +++ /dev/null @@ -1,294 +0,0 @@ -// This file contains an in-memory implementation of a block store. -// -// Documentation Last Review: 13.10.2020 -// - -package blockstore - -import ( - "context" - "sync" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "golang.org/x/xerrors" -) - -// sizeWarnLimit defines the limit after which a warning is emitted every time -// the queue keeps growing. -const sizeWarnLimit = 100 - -// InMemory is a block store that only stores the block in-memory which means -// they won't persist. -// -// - implements blockstore.BlockStore -type InMemory struct { - sync.Mutex - blocks []types.BlockLink - watcher core.Observable - withTx bool -} - -// NewInMemory returns a new empty in-memory block store. -func NewInMemory() *InMemory { - return &InMemory{ - blocks: make([]types.BlockLink, 0), - watcher: core.NewWatcher(), - } -} - -// Len implements blockstore.BlockStore. It returns the length of the store. -func (s *InMemory) Len() uint64 { - s.Lock() - defer s.Unlock() - - return uint64(len(s.blocks)) -} - -// Store implements blockstore.BlockStore. It stores the block only if the link -// matches the latest block. -func (s *InMemory) Store(link types.BlockLink) error { - s.Lock() - defer s.Unlock() - - if len(s.blocks) > 0 { - latest := s.blocks[len(s.blocks)-1] - - if latest.GetTo() != link.GetFrom() { - return xerrors.Errorf("mismatch link '%v' != '%v'", link.GetFrom(), latest.GetTo()) - } - } - - s.blocks = append(s.blocks, link) - - if !s.withTx { - // When the store is using a database transaction, it will delay the - // notification until the commit. - s.watcher.Notify(link) - } - - return nil -} - -// Get implements blockstore.BlockStore. It returns the block link associated to -// the digest if it exists, otherwise it returns an error. -func (s *InMemory) Get(id types.Digest) (types.BlockLink, error) { - s.Lock() - defer s.Unlock() - - for _, link := range s.blocks { - if link.GetTo() == id { - return link, nil - } - } - - return nil, xerrors.Errorf("block not found: %w", ErrNoBlock) -} - -// GetByIndex implements blockstore.BlockStore. It returns the block associated -// to the index if it exists. -func (s *InMemory) GetByIndex(index uint64) (types.BlockLink, error) { - s.Lock() - defer s.Unlock() - - if int(index) >= len(s.blocks) { - return nil, xerrors.Errorf("block not found: %w", ErrNoBlock) - } - - return s.blocks[index], nil -} - -// GetChain implements blockstore.BlockStore. It returns the chain to the latest -// block. -func (s *InMemory) GetChain() (types.Chain, error) { - s.Lock() - defer s.Unlock() - - num := len(s.blocks) - 1 - - if num < 0 { - return nil, xerrors.New("store is empty") - } - - prevs := make([]types.Link, num) - for i, block := range s.blocks[:num] { - prevs[i] = block.Reduce() - } - - return types.NewChain(s.blocks[num], prevs), nil -} - -// Last implements blockstore.BlockStore. It returns the latest block of the -// store. -func (s *InMemory) Last() (types.BlockLink, error) { - s.Lock() - defer s.Unlock() - - if len(s.blocks) == 0 { - return nil, xerrors.Errorf("store empty: %w", ErrNoBlock) - } - - return s.blocks[len(s.blocks)-1], nil -} - -// Watch implements blockstore.BlockStore. It returns a channel populated with -// new blocks. -func (s *InMemory) Watch(ctx context.Context) <-chan types.BlockLink { - obs := newObserver(ctx, s.watcher) - - return obs.ch -} - -// WithTx implements blockstore.BlockStore. It returns a new store that will -// apply the list of blocks at the end of the transaction. -func (s *InMemory) WithTx(txn store.Transaction) BlockStore { - store := &InMemory{ - blocks: append([]types.BlockLink{}, s.blocks...), - watcher: s.watcher, - withTx: true, - } - - from := len(store.blocks) - - txn.OnCommit(func() { - s.Lock() - s.blocks = store.blocks - s.withTx = false - - newBlocks := append([]types.BlockLink{}, s.blocks[from:]...) - s.Unlock() - - for _, link := range newBlocks { - s.watcher.Notify(link) - } - }) - - return store -} - -// Observer is an observer that can be added to store watcher. It will announce -// the blocks in order and without blocking the watcher even if the listener is -// not actively emptying the queue. -// -// - implements core.Observer. -type observer struct { - sync.Mutex - - logger zerolog.Logger - buffer []types.BlockLink - running bool - closed bool - working sync.WaitGroup - ch chan types.BlockLink -} - -func newObserver(ctx context.Context, watcher core.Observable) *observer { - // This channel must have a buffer of size 1, no more no less, in order to - // preserve the ordering of the events but also to prevent the observer - // buffer to be used when the channel is correctly listened to. - ch := make(chan types.BlockLink, 1) - - obs := &observer{ - logger: dela.Logger, - ch: ch, - } - - watcher.Add(obs) - - go func() { - <-ctx.Done() - watcher.Remove(obs) - obs.close() - }() - - return obs -} - -// NotifyCallback implements core.Observer. It pushes the event to the channel -// if it is free, otherwise it fills a buffer and waits for the channel to be -// drained. -func (obs *observer) NotifyCallback(evt interface{}) { - obs.Lock() - defer obs.Unlock() - - if obs.closed { - return - } - - if obs.running { - // We know the channel is busy so it goes directly to the buffer. - obs.buffer = append(obs.buffer, evt.(types.BlockLink)) - obs.checkSize() - return - } - - select { - case obs.ch <- evt.(types.BlockLink): - - default: - // The buffer size is not controlled as we assume the event will be read - // shortly by the caller. - obs.buffer = append(obs.buffer, evt.(types.BlockLink)) - - obs.checkSize() - - obs.running = true - - obs.working.Add(1) - - go obs.pushAndWait() - } -} - -func (obs *observer) checkSize() { - if len(obs.buffer) >= sizeWarnLimit { - obs.logger.Warn(). - Int("size", len(obs.buffer)). - Msg("observer queue is growing unexpectedly") - } -} - -func (obs *observer) pushAndWait() { - defer obs.working.Done() - - for { - obs.Lock() - - if len(obs.buffer) == 0 { - obs.running = false - obs.Unlock() - return - } - - msg := obs.buffer[0] - obs.buffer = obs.buffer[1:] - - obs.Unlock() - - // Wait for the channel to be available to writings. - obs.ch <- msg - } -} - -func (obs *observer) close() { - obs.Lock() - - obs.closed = true - obs.running = false - obs.buffer = nil - - // Drain message in transit to close the channel properly. - select { - case <-obs.ch: - default: - } - - close(obs.ch) - - obs.Unlock() - - obs.working.Wait() -} diff --git a/dela/core/ordering/cosipbft/blockstore/mem_test.go b/dela/core/ordering/cosipbft/blockstore/mem_test.go deleted file mode 100644 index 7893ac0..0000000 --- a/dela/core/ordering/cosipbft/blockstore/mem_test.go +++ /dev/null @@ -1,219 +0,0 @@ -package blockstore - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestInMemory_Len(t *testing.T) { - store := NewInMemory() - require.Equal(t, uint64(0), store.Len()) - - store.blocks = []types.BlockLink{makeLink(t, types.Digest{}), makeLink(t, types.Digest{})} - require.Equal(t, uint64(2), store.Len()) -} - -func TestInMemory_Store(t *testing.T) { - store := NewInMemory() - - err := store.Store(makeLink(t, types.Digest{})) - require.NoError(t, err) - - err = store.Store(makeLink(t, store.blocks[0].GetTo())) - require.NoError(t, err) - - err = store.Store(makeLink(t, types.Digest{})) - require.EqualError(t, err, "mismatch link '00000000' != '2c34ce1d'") -} - -func TestInMemory_Get(t *testing.T) { - store := NewInMemory() - - store.blocks = []types.BlockLink{makeLink(t, types.Digest{})} - - block, err := store.Get(store.blocks[0].GetTo()) - require.NoError(t, err) - require.Equal(t, store.blocks[0], block) - - _, err = store.Get(types.Digest{}) - require.EqualError(t, err, "block not found: no block") -} - -func TestInMemory_GetByIndex(t *testing.T) { - store := NewInMemory() - - store.blocks = []types.BlockLink{ - makeLink(t, types.Digest{}, types.WithIndex(0)), - makeLink(t, types.Digest{}, types.WithIndex(1)), - makeLink(t, types.Digest{}, types.WithIndex(2)), - } - - block, err := store.GetByIndex(1) - require.NoError(t, err) - require.Equal(t, uint64(1), block.GetBlock().GetIndex()) - - block, err = store.GetByIndex(2) - require.NoError(t, err) - require.Equal(t, uint64(2), block.GetBlock().GetIndex()) - - _, err = store.GetByIndex(3) - require.EqualError(t, err, "block not found: no block") -} - -func TestInMemory_GetChain(t *testing.T) { - store := NewInMemory() - - store.blocks = []types.BlockLink{ - makeLink(t, types.Digest{}, types.WithIndex(0)), - makeLink(t, types.Digest{}, types.WithIndex(1)), - makeLink(t, types.Digest{}, types.WithIndex(2)), - } - - chain, err := store.GetChain() - require.NoError(t, err) - require.Len(t, chain.GetLinks(), 3) - - store.blocks = store.blocks[:1] - chain, err = store.GetChain() - require.NoError(t, err) - require.Len(t, chain.GetLinks(), 1) - - store.blocks = nil - _, err = store.GetChain() - require.EqualError(t, err, "store is empty") -} - -func TestInMemory_Last(t *testing.T) { - store := NewInMemory() - - _, err := store.Last() - require.EqualError(t, err, "store empty: no block") - - store.blocks = []types.BlockLink{makeLink(t, types.Digest{})} - block, err := store.Last() - require.NoError(t, err) - require.Equal(t, store.blocks[0], block) -} - -func TestInMemory_Watch(t *testing.T) { - num := 20 - store := NewInMemory() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ch := store.Watch(ctx) - - store.Store(makeLink(t, types.Digest{})) - - for i := 0; i < num; i++ { - store.Store(makeLink(t, store.blocks[i].GetTo(), types.WithIndex(uint64(i+1)))) - } - - for i := 0; i <= num; i++ { - link := <-ch - require.Equal(t, store.blocks[i].GetTo(), link.GetTo(), i) - } -} - -func TestInMemory_WithTx(t *testing.T) { - store := NewInMemory() - - tx := &fakeTx{} - txstore := store.WithTx(tx) - - err := txstore.Store(makeLink(t, types.Digest{})) - require.NoError(t, err) - require.Len(t, store.blocks, 0) - require.Len(t, txstore.(*InMemory).blocks, 1) - - tx.fn() - require.Len(t, store.blocks, 1) -} - -func TestObserver_NotifyCallback(t *testing.T) { - obs := &observer{ - ch: make(chan types.BlockLink, 1), - } - - link := makeLink(t, types.Digest{}) - - // The observer should not block when the event is not drained. - obs.NotifyCallback(link) - obs.NotifyCallback(link) - obs.NotifyCallback(link) - - evt := <-obs.ch - require.NotNil(t, evt) - - evt = <-obs.ch - require.NotNil(t, evt) - - // Closing with events waiting should clean resources. - obs.close() - require.Empty(t, obs.buffer) - require.Empty(t, obs.ch) - require.True(t, obs.closed) - require.False(t, obs.running) - - // Incoming events should now be ignored. - obs.NotifyCallback(link) - require.Empty(t, obs.buffer) - require.Empty(t, obs.ch) -} - -func TestObserver_Flooding_NotifyCallback(t *testing.T) { - logger, check := fake.CheckLog("observer queue is growing unexpectedly") - - obs := &observer{ - logger: logger, - ch: make(chan types.BlockLink), - } - - link := makeLink(t, types.Digest{}) - - for i := 0; i < sizeWarnLimit+1; i++ { - obs.NotifyCallback(link) - } - - check(t) -} - -func TestObserver_WhileEmpty_Close(t *testing.T) { - obs := &observer{ - ch: make(chan types.BlockLink), - running: true, - } - - obs.close() - require.True(t, obs.closed) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeLink(t *testing.T, from types.Digest, opts ...types.BlockOption) types.BlockLink { - to, err := types.NewBlock(simple.NewResult(nil), opts...) - require.NoError(t, err) - - link, err := types.NewBlockLink(from, to, types.WithSignatures(fake.Signature{}, fake.Signature{})) - require.NoError(t, err) - - return link -} - -type fakeTx struct { - store.Transaction - - fn func() -} - -func (tx *fakeTx) OnCommit(fn func()) { - tx.fn = fn -} diff --git a/dela/core/ordering/cosipbft/blockstore/tree.go b/dela/core/ordering/cosipbft/blockstore/tree.go deleted file mode 100644 index c8e385f..0000000 --- a/dela/core/ordering/cosipbft/blockstore/tree.go +++ /dev/null @@ -1,68 +0,0 @@ -// This file contains the implementation of an in-memory tree cache. -// -// Documenation Last Review: 13.10.2020 -// - -package blockstore - -import ( - "sync" - - "go.dedis.ch/dela/core/store/hashtree" -) - -// TreeCache is a storage for a tree. It supports asynchronous calls. -// -// - implements blockstore.TreeCache -type treeCache struct { - sync.Mutex - tree hashtree.Tree -} - -// NewTreeCache creates a new cache with the given tree as the first value. -func NewTreeCache(tree hashtree.Tree) TreeCache { - return &treeCache{ - tree: tree, - } -} - -// Get implements blockstore.TreeCache. It returns the current value of the -// cache. -func (c *treeCache) Get() hashtree.Tree { - c.Lock() - defer c.Unlock() - - return c.tree -} - -// GetWithLock implements blockstore.TreeCache. It returns the current value of -// the cache alongside a function to unlock the cache. It allows one to delay -// a set while fetching associated data. The function returned must be called. -func (c *treeCache) GetWithLock() (hashtree.Tree, func()) { - c.Lock() - - return c.tree, c.unlock -} - -// Set implements blockstore.TreeCache. It stores the new tree as the cache -// value but panic if it is nil. -func (c *treeCache) Set(tree hashtree.Tree) { - c.Lock() - c.tree = tree - c.Unlock() -} - -// SetWithLock implements blockstore.TreeCache. It sets the tree while holding -// the lock and returns a function to unlock it. It allows one to prevent an -// access until associated data is updated. The function returned must be -// called. -func (c *treeCache) SetWithLock(tree hashtree.Tree) func() { - c.Lock() - c.tree = tree - - return c.unlock -} - -func (c *treeCache) unlock() { - c.Unlock() -} diff --git a/dela/core/ordering/cosipbft/blockstore/tree_test.go b/dela/core/ordering/cosipbft/blockstore/tree_test.go deleted file mode 100644 index f92dc8c..0000000 --- a/dela/core/ordering/cosipbft/blockstore/tree_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package blockstore - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/store/hashtree" -) - -func TestTreeCache_Get(t *testing.T) { - cache := NewTreeCache(fakeTree{}) - - require.Equal(t, fakeTree{}, cache.Get()) -} - -func TestTreeCache_GetWithLock(t *testing.T) { - cache := NewTreeCache(fakeTree{}) - - tree, unlock := cache.GetWithLock() - require.NotNil(t, tree) - - unlock() - - tree, unlock = cache.GetWithLock() - require.NotNil(t, tree) - - unlock() -} - -func TestTreeCache_Set(t *testing.T) { - cache := NewTreeCache(fakeTree{}) - - cache.Set(fakeTree{value: 1}) - require.Equal(t, fakeTree{value: 1}, cache.Get()) -} - -func TestTreeCache_SetAndLock(t *testing.T) { - cache := NewTreeCache(fakeTree{}) - - unlock := cache.SetWithLock(fakeTree{}) - - ch := make(chan struct{}) - go func() { - cache.Get() - close(ch) - }() - - time.Sleep(50 * time.Millisecond) - - select { - case <-ch: - t.Fatal("get should be locked") - default: - } - - unlock() - - select { - case <-ch: - case <-time.After(time.Second): - t.Fatal("get should be released") - } -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeTree struct { - hashtree.Tree - value int -} diff --git a/dela/core/ordering/cosipbft/blocksync/blocksync.go b/dela/core/ordering/cosipbft/blocksync/blocksync.go deleted file mode 100644 index 5dc5145..0000000 --- a/dela/core/ordering/cosipbft/blocksync/blocksync.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package blocksync defines a block synchronizer for the ordering service. -// -// The package also implements a default synchronizer that will send an -// announcement with the latest known block, and share the chain to the nodes -// that have fallen behind. -// -// Documentation Last Review: 13.10.2020 -package blocksync - -import ( - "context" - - "go.dedis.ch/dela/mino" -) - -// Config is the configuration to change the behaviour of the synchronization. -type Config struct { - // MinSoft is the number of participants that have soft-synchronized, - // meaning they know the latest index of the leader. - MinSoft int - - // MinHard is the number of participants that have hard-synchronized, - // meaning they have the latest block stored. - MinHard int -} - -// Synchronizer is an interface to synchronize a leader with the participants. -type Synchronizer interface { - // GetLatest returns the latest known synchronization update. It can be used - // to wait for a complete chain update as this index has been proven to - // exist. - GetLatest() uint64 - - // Sync sends a synchronization message to all the participants in order to - // announce the current state of the chain. - Sync(ctx context.Context, players mino.Players, cfg Config) error -} diff --git a/dela/core/ordering/cosipbft/blocksync/default.go b/dela/core/ordering/cosipbft/blocksync/default.go deleted file mode 100644 index 7e99364..0000000 --- a/dela/core/ordering/cosipbft/blocksync/default.go +++ /dev/null @@ -1,335 +0,0 @@ -// This file contains a default implementation of a block synchronizer. -// -// Documentation Last Review: 13.10.2020 -// - -package blocksync - -import ( - "context" - "io" - "sync" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync/types" - "go.dedis.ch/dela/core/ordering/cosipbft/pbft" - otypes "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -var ( - // protocolName denotes the value of the protocol span tag associated with - // the `blocksync` protocol. - protocolName = "blocksync" -) - -// DefaultSync is a block synchronizer that allow soft and hard synchronization -// of the participants. A soft threshold means that a given number of -// participants have updated the latest index, whereas a hard one means that -// they have stored all the blocks up to the latest index. -// -// - implements blocksync.Synchronizer -type defaultSync struct { - logger zerolog.Logger - rpc mino.RPC - pbftsm pbft.StateMachine - blocks blockstore.BlockStore - - latest *uint64 - catchUpLock *sync.Mutex -} - -// SyncParam is the parameter object to create a new synchronizer. -type SyncParam struct { - Mino mino.Mino - PBFT pbft.StateMachine - Blocks blockstore.BlockStore - Genesis blockstore.GenesisStore - LinkFactory otypes.LinkFactory - ChainFactory otypes.ChainFactory - VerifierFactory crypto.VerifierFactory -} - -// NewSynchronizer creates a new block synchronizer. -func NewSynchronizer(param SyncParam) Synchronizer { - latest := param.Blocks.Len() - - logger := dela.Logger.With().Str("addr", param.Mino.GetAddress().String()).Logger() - - h := &handler{ - latest: &latest, - catchUpLock: new(sync.Mutex), - logger: logger, - genesis: param.Genesis, - blocks: param.Blocks, - pbftsm: param.PBFT, - verifierFac: param.VerifierFactory, - } - - fac := types.NewMessageFactory(param.LinkFactory, param.ChainFactory) - - s := defaultSync{ - logger: logger, - rpc: mino.MustCreateRPC(param.Mino, "blocksync", h, fac), - pbftsm: param.PBFT, - blocks: param.Blocks, - latest: &latest, - catchUpLock: h.catchUpLock, - } - - return s -} - -// GetLatest implements blocksync.Synchronizer. It returns the latest index -// known by the instance. -func (s defaultSync) GetLatest() uint64 { - s.catchUpLock.Lock() - defer s.catchUpLock.Unlock() - - return *s.latest -} - -// Sync implements blocksync.Synchronizer. it starts a routine to first -// soft-sync the participants and then send the blocks when necessary. It will -// synchronize other nodes as long as the context is not done. -func (s defaultSync) Sync(ctx context.Context, players mino.Players, cfg Config) error { - ctx = context.WithValue(ctx, tracing.ProtocolKey, protocolName) - - if s.blocks.Len() == 0 { - // When the store is empty, that means that the participants are all - // synchronized anyway as there is no block. - return nil - } - - sender, rcvr, err := s.rpc.Stream(ctx, players) - if err != nil { - return xerrors.Errorf("stream failed: %v", err) - } - - // 1. Send the announcement message to everyone so that they can learn about - // the latest block. - chain, err := s.blocks.GetChain() - if err != nil { - return xerrors.Errorf("failed to read chain: %v", err) - } - - errs := sender.Send(types.NewSyncMessage(chain), iter2arr(players.AddressIterator())...) - for err := range errs { - if err != nil { - s.logger.Warn().Err(err).Msg("announcement failed") - } - } - - // 2. Wait for the hard synchronization to end. It can be interrupted with - // the context. - wg := sync.WaitGroup{} - wg.Add(1) - once := sync.Once{} - - // The synchronization is run in background so that it continues even after - // the threshold is reached, which allow other nodes to complete a catch up - // while the round is performing. - go func() { - defer once.Do(wg.Done) - - soft := map[mino.Address]struct{}{} - hard := map[mino.Address]struct{}{} - - for { - from, msg, err := rcvr.Recv(ctx) - if err == context.Canceled || err == context.DeadlineExceeded || err == io.EOF { - return - } - if err != nil { - s.logger.Warn().Err(err).Msg("sync finished") - return - } - - switch in := msg.(type) { - case types.SyncRequest: - _, found := soft[from] - if found { - s.logger.Warn().Msg("found duplicate request") - continue - } - - soft[from] = struct{}{} - - go s.syncNode(in.GetFrom(), sender, from) - - case types.SyncAck: - soft[from] = struct{}{} - hard[from] = struct{}{} - } - - if len(soft) >= cfg.MinSoft && len(hard) >= cfg.MinHard { - once.Do(wg.Done) - } - } - }() - - wg.Wait() - - return nil -} - -func (s defaultSync) syncNode(from uint64, sender mino.Sender, to mino.Address) { - for i := from; i < s.blocks.Len(); i++ { - link, err := s.blocks.GetByIndex(i) - if err != nil { - s.logger.Err(err).Msgf("while synchronizing %v", to) - return - } - - s.logger.Debug(). - Uint64("index", link.GetBlock().GetIndex()). - Stringer("to", to). - Msg("send block") - - err = <-sender.Send(types.NewSyncReply(link), to) - if err != nil { - s.logger.Err(err).Msgf("while synchronizing %v", to) - return - } - } -} - -// handler is a Mino handler for the synchronization messages. -// -// - implements mino.Handler -type handler struct { - mino.UnsupportedHandler - - latest *uint64 - catchUpLock *sync.Mutex - - logger zerolog.Logger - blocks blockstore.BlockStore - genesis blockstore.GenesisStore - pbftsm pbft.StateMachine - verifierFac crypto.VerifierFactory -} - -// Stream implements mino.Handler. It waits for an announcement message and then -// wait for the block message if any is needed. -func (h *handler) Stream(out mino.Sender, in mino.Receiver) error { - ctx := context.Background() - - m, orch, err := h.waitAnnounce(ctx, in) - if err != nil { - return xerrors.Errorf("no announcement: %v", err) - } - - h.logger.Debug(). - Uint64("index", m.GetLatestIndex()). - Msg("received synchronization message") - - genesis, err := h.genesis.Get() - if err != nil { - return xerrors.Errorf("reading genesis: %v", err) - } - - from := genesis.GetHash() - - // We trust our storage, thus we won't check links on blocks we already - // have. - bl, err := h.blocks.Last() - if err == nil { - from = bl.GetFrom() - } - - err = m.GetChain().Verify(genesis, from, h.verifierFac) - if err != nil { - return xerrors.Errorf("failed to verify chain: %v", err) - } - - if m.GetLatestIndex() < h.blocks.Len() { - // The block storage has already all the block known so far so we can - // send the hard-sync acknowledgement. - return h.ack(out, orch) - } - - // At this point, the synchronization can only happen on one thread, so it - // waits for the lock to be free, which means that in the meantime some - // blocks might have been stored but the request is sent with the most - // up-to-date block index, so it won't catch up twice the same block. - h.catchUpLock.Lock() - defer h.catchUpLock.Unlock() - - // Update the latest index through atomic operations as it can be read - // asynchronously from the getter. - if m.GetLatestIndex() > *h.latest { - *h.latest = m.GetLatestIndex() - } - - err = <-out.Send(types.NewSyncRequest(h.blocks.Len()), orch) - if err != nil { - return xerrors.Errorf("sending request failed: %v", err) - } - - for h.blocks.Len() <= m.GetLatestIndex() { - _, msg, err := in.Recv(ctx) - if err != nil { - return xerrors.Errorf("receiver failed: %v", err) - } - - reply, ok := msg.(types.SyncReply) - if ok { - h.logger.Debug(). - Uint64("index", reply.GetLink().GetBlock().GetIndex()). - Msg("catch up block") - - err = h.pbftsm.CatchUp(reply.GetLink()) - if err != nil { - return xerrors.Errorf("pbft catch up failed: %v", err) - } - } - } - - h.logger.Debug().Msg("catch up done") - - return h.ack(out, orch) -} - -func (h *handler) waitAnnounce(ctx context.Context, - in mino.Receiver) (*types.SyncMessage, mino.Address, error) { - - for { - orch, msg, err := in.Recv(ctx) - if err != nil { - return nil, nil, xerrors.Errorf("receiver failed: %v", err) - } - - // The SyncMessage contains the chain to the latest block known by the - // leader which allows to verify if it is not lying. - m, ok := msg.(types.SyncMessage) - if ok { - return &m, orch, nil - } - } -} - -func (h *handler) ack(out mino.Sender, orch mino.Address) error { - // Send the acknowledgement to the orchestrator that the blocks have been - // caught up. - err := <-out.Send(types.NewSyncAck(), orch) - if err != nil { - return xerrors.Errorf("sending ack failed: %v", err) - } - - return nil -} - -func iter2arr(iter mino.AddressIterator) []mino.Address { - addrs := []mino.Address{} - for iter.HasNext() { - addrs = append(addrs, iter.GetNext()) - } - - return addrs -} diff --git a/dela/core/ordering/cosipbft/blocksync/default_test.go b/dela/core/ordering/cosipbft/blocksync/default_test.go deleted file mode 100644 index 0be0cbe..0000000 --- a/dela/core/ordering/cosipbft/blocksync/default_test.go +++ /dev/null @@ -1,366 +0,0 @@ -package blocksync - -import ( - "context" - "fmt" - "strconv" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync/types" - "go.dedis.ch/dela/core/ordering/cosipbft/pbft" - otypes "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minoch" -) - -func TestDefaultSync_Basic(t *testing.T) { - n := 20 - k := 8 - num := 10 - - syncs, genesis, roster := makeNodes(t, n) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := syncs[0].Sync(ctx, roster, Config{ - MinSoft: roster.Len(), - MinHard: roster.Len(), - }) - require.NoError(t, err) - - storeBlocks(t, syncs[0].blocks, num, genesis.GetHash().Bytes()...) - - // Test only a subset of the roster to prepare for the next test. - err = syncs[0].Sync(ctx, roster.Take(mino.RangeFilter(0, k)), Config{ - MinSoft: k, - MinHard: k, - }) - require.NoError(t, err) - - for i := 0; i < k; i++ { - require.Equal(t, uint64(num), syncs[i].blocks.Len(), strconv.Itoa(i)) - } - - // Test that two parrallel synchronizations for the same latest index don't - // mix each other. Also test that already updated participants won't fail. - wg := sync.WaitGroup{} - wg.Add(2) - - cfg := Config{ - MinSoft: n, - MinHard: n, - } - - go func() { - defer wg.Done() - require.NoError(t, syncs[0].Sync(ctx, roster, cfg)) - }() - go func() { - defer wg.Done() - require.NoError(t, syncs[k-1].Sync(ctx, roster, cfg)) - }() - - wg.Wait() - - for i := 0; i < n; i++ { - require.Equal(t, uint64(num), syncs[i].blocks.Len(), strconv.Itoa(i)) - } -} - -func TestDefaultSync_GetLatest(t *testing.T) { - latest := uint64(5) - - sync := defaultSync{ - latest: &latest, - catchUpLock: new(sync.Mutex), - } - - require.Equal(t, uint64(5), sync.GetLatest()) -} - -func TestDefaultSync_Sync(t *testing.T) { - rcvr := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncRequest(0)), - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncRequest(0)), - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncAck()), - ) - sender := fake.Sender{} - - sync := defaultSync{ - rpc: fake.NewStreamRPC(rcvr, sender), - blocks: blockstore.NewInMemory(), - } - - storeBlocks(t, sync.blocks, 1) - - ctx := context.Background() - - err := sync.Sync(ctx, mino.NewAddresses(), Config{MinSoft: 1, MinHard: 1}) - require.NoError(t, err) - - sync.blocks = badBlockStore{errChain: fake.GetError()} - err = sync.Sync(ctx, mino.NewAddresses(), Config{}) - require.EqualError(t, err, fake.Err("failed to read chain")) - - sync.blocks = blockstore.NewInMemory() - storeBlocks(t, sync.blocks, 1) - sync.rpc = fake.NewBadRPC() - err = sync.Sync(ctx, mino.NewAddresses(), Config{}) - require.EqualError(t, err, fake.Err("stream failed")) - - logger, check := fake.CheckLog("announcement failed") - - sync.logger = logger - sync.rpc = fake.NewStreamRPC(fake.NewReceiver(), fake.NewBadSender()) - err = sync.Sync(ctx, mino.NewAddresses(), Config{}) - require.NoError(t, err) - check(t) - - logger, check = fake.CheckLog("sync finished") - - sync.logger = logger - sync.rpc = fake.NewStreamRPC(fake.NewBadReceiver(), fake.Sender{}) - err = sync.Sync(ctx, mino.NewAddresses(fake.NewAddress(0)), Config{MinSoft: 1}) - require.NoError(t, err) - check(t) - - logger, wait := fake.WaitLog("while synchronizing fake.Address[0]", time.Second) - - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncRequest(0)), - ) - - sync.logger = logger - sync.rpc = fake.NewStreamRPC(recv, sender) - sync.blocks = badBlockStore{} - err = sync.Sync(ctx, mino.NewAddresses(), Config{MinSoft: 1}) - require.NoError(t, err) - wait(t) -} - -func TestDefaultSync_SyncNode(t *testing.T) { - sync := defaultSync{ - blocks: blockstore.NewInMemory(), - } - - storeBlocks(t, sync.blocks, 5) - - logger, check := fake.CheckLog("while synchronizing fake.Address[0]") - - sync.logger = logger - sync.syncNode(0, fake.NewBadSender(), fake.NewAddress(0)) - - check(t) -} - -func TestHandler_Stream(t *testing.T) { - latest := uint64(0) - blocks := blockstore.NewInMemory() - storeBlocks(t, blocks, 3) - - handler := &handler{ - latest: &latest, - catchUpLock: new(sync.Mutex), - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - verifierFac: fake.VerifierFactory{}, - } - handler.genesis.Set(otypes.Genesis{}) - handler.pbftsm = testSM{blocks: handler.blocks} - storeBlocks(t, handler.blocks, 1) - - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(makeChain(t, 0))), - ) - - err := handler.Stream(fake.Sender{}, recv) - require.NoError(t, err) - - msgs := []fake.ReceiverMessage{ - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(makeChain(t, blocks.Len()-1))), - } - for i := uint64(0); i < blocks.Len(); i++ { - link, err := blocks.GetByIndex(i) - require.NoError(t, err) - - msgs = append(msgs, fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncReply(link))) - } - - handler.blocks = blockstore.NewInMemory() - handler.pbftsm = testSM{blocks: handler.blocks} - err = handler.Stream(fake.Sender{}, fake.NewReceiver(msgs...)) - require.NoError(t, err) - require.Equal(t, blocks.Len(), handler.blocks.Len()) - - err = handler.Stream(fake.Sender{}, fake.NewBadReceiver()) - require.EqualError(t, err, fake.Err("no announcement: receiver failed")) - - handler.genesis = blockstore.NewGenesisStore() - err = handler.Stream(fake.Sender{}, fake.NewReceiver(msgs...)) - require.EqualError(t, err, "reading genesis: missing genesis block") - - recv = fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(fakeChain{err: fake.GetError()})), - ) - - handler.genesis.Set(otypes.Genesis{}) - err = handler.Stream(fake.Sender{}, recv) - require.EqualError(t, err, fake.Err("failed to verify chain")) - - recv = fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(makeChain(t, 6))), - ) - - err = handler.Stream(fake.NewBadSender(), recv) - require.EqualError(t, err, fake.Err("sending request failed")) - - recv = fake.NewBadReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(makeChain(t, 6))), - ) - - err = handler.Stream(fake.Sender{}, recv) - require.EqualError(t, err, fake.Err("receiver failed")) - - recv = fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(makeChain(t, 6))), - msgs[1], - ) - - err = handler.Stream(fake.Sender{}, recv) - require.Error(t, err) - require.Regexp(t, "pbft catch up failed: mismatch link '[0]{8}' != '[0-9a-f]{8}'", err.Error()) - - recv = fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), types.NewSyncMessage(makeChain(t, 0))), - ) - - err = handler.Stream(fake.NewBadSender(), recv) - require.EqualError(t, err, fake.Err("sending ack failed")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeChain(t *testing.T, index uint64) otypes.Chain { - block, err := otypes.NewBlock(simple.NewResult(nil), otypes.WithIndex(index)) - require.NoError(t, err) - - return fakeChain{block: block} -} - -func makeNodes(t *testing.T, n int) ([]defaultSync, otypes.Genesis, mino.Players) { - manager := minoch.NewManager() - - syncs := make([]defaultSync, n) - addrs := make([]mino.Address, n) - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := otypes.NewGenesis(ro) - require.NoError(t, err) - - for i := 0; i < n; i++ { - m := minoch.MustCreate(manager, fmt.Sprintf("node%d", i)) - - addrs[i] = m.GetAddress() - - genstore := blockstore.NewGenesisStore() - genstore.Set(genesis) - - blocks := blockstore.NewInMemory() - blockFac := otypes.NewBlockFactory(simple.NewResultFactory(signed.NewTransactionFactory())) - csFac := authority.NewChangeSetFactory(m.GetAddressFactory(), fake.PublicKeyFactory{}) - linkFac := otypes.NewLinkFactory(blockFac, fake.SignatureFactory{}, csFac) - - param := SyncParam{ - Mino: m, - Blocks: blocks, - Genesis: genstore, - LinkFactory: linkFac, - ChainFactory: otypes.NewChainFactory(linkFac), - PBFT: testSM{blocks: blocks}, - VerifierFactory: fake.VerifierFactory{}, - } - - syncs[i] = NewSynchronizer(param).(defaultSync) - } - - return syncs, genesis, mino.NewAddresses(addrs...) -} - -func storeBlocks(t *testing.T, blocks blockstore.BlockStore, n int, from ...byte) { - prev := otypes.Digest{} - copy(prev[:], from) - - for i := 0; i < n; i++ { - block, err := otypes.NewBlock(simple.NewResult(nil), otypes.WithIndex(uint64(i))) - require.NoError(t, err) - - link, err := otypes.NewBlockLink(prev, block, - otypes.WithSignatures(fake.Signature{}, fake.Signature{})) - require.NoError(t, err) - - err = blocks.Store(link) - require.NoError(t, err) - - prev = block.GetHash() - } -} - -type testSM struct { - pbft.StateMachine - - blocks blockstore.BlockStore -} - -func (sm testSM) CatchUp(link otypes.BlockLink) error { - err := sm.blocks.Store(link) - if err != nil { - return err - } - - return nil -} - -type badBlockStore struct { - blockstore.BlockStore - - errChain error -} - -func (s badBlockStore) Len() uint64 { - return 5 -} - -func (s badBlockStore) GetChain() (otypes.Chain, error) { - return nil, s.errChain -} - -func (s badBlockStore) GetByIndex(index uint64) (otypes.BlockLink, error) { - return nil, fake.GetError() -} - -type fakeChain struct { - otypes.Chain - - block otypes.Block - err error -} - -func (c fakeChain) GetBlock() otypes.Block { - return c.block -} - -func (c fakeChain) Verify(otypes.Genesis, otypes.Digest, crypto.VerifierFactory) error { - return c.err -} diff --git a/dela/core/ordering/cosipbft/blocksync/json/json.go b/dela/core/ordering/cosipbft/blocksync/json/json.go deleted file mode 100644 index 492f309..0000000 --- a/dela/core/ordering/cosipbft/blocksync/json/json.go +++ /dev/null @@ -1,145 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync/types" - otypes "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - types.RegisterMessageFormat(serde.FormatJSON, msgFormat{}) -} - -// SyncMessageJSON is the JSON representation of a sync announcement. -type SyncMessageJSON struct { - Chain json.RawMessage -} - -// SyncRequestJSON is the JSON representation of a sync request. -type SyncRequestJSON struct { - From uint64 -} - -// SyncReplyJSON is the JSON representation of a sync reply. -type SyncReplyJSON struct { - Link json.RawMessage -} - -// SyncAckJSON is the JSON representation of a sync acknowledgement. -type SyncAckJSON struct{} - -// MessageJSON is the JSON representation of a sync message. -type MessageJSON struct { - Message *SyncMessageJSON `json:",omitempty"` - Request *SyncRequestJSON `json:",omitempty"` - Reply *SyncReplyJSON `json:",omitempty"` - Ack *SyncAckJSON `json:",omitempty"` -} - -// MsgFormat is the format engine to encode and decode sync messages. -// -// - implements serde.FormatEngine -type msgFormat struct{} - -// Encode implements serde.FormatEngine. It returns the JSON data of the message -// if appropriate, otherwise an error. -func (fmt msgFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - var m MessageJSON - - switch in := msg.(type) { - case types.SyncMessage: - chain, err := in.GetChain().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to encode chain: %v", err) - } - - sm := SyncMessageJSON{ - Chain: chain, - } - - m.Message = &sm - case types.SyncRequest: - req := SyncRequestJSON{ - From: in.GetFrom(), - } - - m.Request = &req - case types.SyncReply: - link, err := in.GetLink().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("link serialization failed: %v", err) - } - - reply := SyncReplyJSON{ - Link: link, - } - - m.Reply = &reply - case types.SyncAck: - m.Ack = &SyncAckJSON{} - default: - return nil, xerrors.Errorf("unsupported message '%T'", msg) - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("marshal failed: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It returns the message associated to -// the data if appropriate, otherwise an error. -func (fmt msgFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := MessageJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("unmarshal failed: %v", err) - } - - if m.Message != nil { - fac := ctx.GetFactory(types.ChainKey{}) - - factory, ok := fac.(otypes.ChainFactory) - if !ok { - return nil, xerrors.Errorf("invalid chain factory '%T'", fac) - } - - chain, err := factory.ChainOf(ctx, m.Message.Chain) - if err != nil { - return nil, xerrors.Errorf("failed to decode chain: %v", err) - } - - return types.NewSyncMessage(chain), nil - } - - if m.Request != nil { - return types.NewSyncRequest(m.Request.From), nil - } - - if m.Reply != nil { - fac := ctx.GetFactory(types.LinkKey{}) - - factory, ok := fac.(otypes.LinkFactory) - if !ok { - return nil, xerrors.Errorf("invalid link factory '%T'", fac) - } - - link, err := factory.BlockLinkOf(ctx, m.Reply.Link) - if err != nil { - return nil, xerrors.Errorf("couldn't decode link: %v", err) - } - - return types.NewSyncReply(link), nil - } - - if m.Ack != nil { - return types.NewSyncAck(), nil - } - - return nil, xerrors.New("message is empty") -} diff --git a/dela/core/ordering/cosipbft/blocksync/json/json_test.go b/dela/core/ordering/cosipbft/blocksync/json/json_test.go deleted file mode 100644 index 4ede970..0000000 --- a/dela/core/ordering/cosipbft/blocksync/json/json_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync/types" - otypes "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestMsgFormat_Encode(t *testing.T) { - format := msgFormat{} - - ctx := fake.NewContext() - - data, err := format.Encode(ctx, types.NewSyncMessage(fakeChain{})) - require.NoError(t, err) - require.Equal(t, `{"Message":{"Chain":{}}}`, string(data)) - - data, err = format.Encode(ctx, types.NewSyncRequest(3)) - require.NoError(t, err) - require.Equal(t, `{"Request":{"From":3}}`, string(data)) - - data, err = format.Encode(ctx, types.NewSyncReply(fakeLink{})) - require.NoError(t, err) - require.Equal(t, `{"Reply":{"Link":{}}}`, string(data)) - - data, err = format.Encode(ctx, types.NewSyncAck()) - require.NoError(t, err) - require.Equal(t, `{"Ack":{}}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message 'fake.Message'") - - _, err = format.Encode(ctx, types.NewSyncMessage(fakeChain{err: fake.GetError()})) - require.EqualError(t, err, fake.Err("failed to encode chain")) - - _, err = format.Encode(ctx, types.NewSyncReply(fakeLink{err: fake.GetError()})) - require.EqualError(t, err, fake.Err("link serialization failed")) - - _, err = format.Encode(fake.NewBadContext(), types.NewSyncAck()) - require.EqualError(t, err, fake.Err("marshal failed")) -} - -func TestMsgFormat_Decode(t *testing.T) { - format := msgFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.LinkKey{}, fakeLinkFac{}) - ctx = serde.WithFactory(ctx, types.ChainKey{}, fakeChainFac{}) - - msg, err := format.Decode(ctx, []byte(`{"Message":{}}`)) - require.NoError(t, err) - require.Equal(t, types.NewSyncMessage(fakeChain{}), msg) - - msg, err = format.Decode(ctx, []byte(`{"Request":{}}`)) - require.NoError(t, err) - require.Equal(t, types.NewSyncRequest(0), msg) - - msg, err = format.Decode(ctx, []byte(`{"Reply":{"Link":{}}}`)) - require.NoError(t, err) - require.Equal(t, types.NewSyncReply(fakeLink{}), msg) - - msg, err = format.Decode(ctx, []byte(`{"Ack":{}}`)) - require.NoError(t, err) - require.Equal(t, types.NewSyncAck(), msg) - - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, "message is empty") - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("unmarshal failed")) - - ctx = serde.WithFactory(ctx, types.ChainKey{}, fakeChainFac{err: fake.GetError()}) - _, err = format.Decode(ctx, []byte(`{"Message":{}}`)) - require.EqualError(t, err, fake.Err("failed to decode chain")) - - ctx = serde.WithFactory(ctx, types.ChainKey{}, fake.MessageFactory{}) - _, err = format.Decode(ctx, []byte(`{"Message":{}}`)) - require.EqualError(t, err, "invalid chain factory 'fake.MessageFactory'") - - ctx = serde.WithFactory(ctx, types.LinkKey{}, fakeLinkFac{err: fake.GetError()}) - _, err = format.Decode(ctx, []byte(`{"Reply":{"Link":{}}}`)) - require.EqualError(t, err, fake.Err("couldn't decode link")) - - ctx = serde.WithFactory(ctx, types.LinkKey{}, fake.MessageFactory{}) - _, err = format.Decode(ctx, []byte(`{"Reply":{"Link":{}}}`)) - require.EqualError(t, err, "invalid link factory 'fake.MessageFactory'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeChain struct { - otypes.Chain - - err error -} - -func (chain fakeChain) Serialize(serde.Context) ([]byte, error) { - return []byte("{}"), chain.err -} - -type fakeChainFac struct { - otypes.ChainFactory - - err error -} - -func (fac fakeChainFac) ChainOf(serde.Context, []byte) (otypes.Chain, error) { - return fakeChain{}, fac.err -} - -type fakeLink struct { - otypes.BlockLink - - err error -} - -func (link fakeLink) Serialize(serde.Context) ([]byte, error) { - return []byte("{}"), link.err -} - -type fakeLinkFac struct { - otypes.LinkFactory - - err error -} - -func (fac fakeLinkFac) BlockLinkOf(serde.Context, []byte) (otypes.BlockLink, error) { - return fakeLink{}, fac.err -} diff --git a/dela/core/ordering/cosipbft/blocksync/types/types.go b/dela/core/ordering/cosipbft/blocksync/types/types.go deleted file mode 100644 index 86ddd14..0000000 --- a/dela/core/ordering/cosipbft/blocksync/types/types.go +++ /dev/null @@ -1,186 +0,0 @@ -// Package types implements the network messages for a synchronization. -// -// The messages are implemented in a different package to prevent cycle imports -// when importing the serde formats. -// -// Documentation Last Review: 13.10.2020 -package types - -import ( - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var msgFormats = registry.NewSimpleRegistry() - -// RegisterMessageFormat registers the engine for the given format. -func RegisterMessageFormat(f serde.Format, e serde.FormatEngine) { - msgFormats.Register(f, e) -} - -// SyncMessage is the announcement sent to the participants with the latest -// index of the leader. The chain is provided to prove the validity of the -// index. -// -// - implements serde.Message -type SyncMessage struct { - chain types.Chain -} - -// NewSyncMessage creates a new announcement message. -func NewSyncMessage(chain types.Chain) SyncMessage { - return SyncMessage{ - chain: chain, - } -} - -// GetChain returns the chain that proves the latest index. -func (m SyncMessage) GetChain() types.Chain { - return m.chain -} - -// GetLatestIndex returns the latest index. -func (m SyncMessage) GetLatestIndex() uint64 { - return m.chain.GetBlock().GetIndex() -} - -// Serialize implements serde.Message. It returns the serialized data for this -// message. -func (m SyncMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// SyncRequest is a message to request missing blocks from a given index. -// -// - implements serde.Message -type SyncRequest struct { - from uint64 -} - -// NewSyncRequest creates a new sync request. -func NewSyncRequest(from uint64) SyncRequest { - return SyncRequest{ - from: from, - } -} - -// GetFrom returns the expected index of the first block when catching up. -func (m SyncRequest) GetFrom() uint64 { - return m.from -} - -// Serialize implements serde.Message. It returns the serialized data for this -// message. -func (m SyncRequest) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// SyncReply is a message to send a block to a participant. -// -// - implements serde.Message -type SyncReply struct { - link types.BlockLink -} - -// NewSyncReply creates a new sync reply. -func NewSyncReply(link types.BlockLink) SyncReply { - return SyncReply{ - link: link, - } -} - -// GetLink returns the link to a block to catch up. -func (m SyncReply) GetLink() types.BlockLink { - return m.link -} - -// Serialize implements serde.Message. It returns the serialized data for this -// message. -func (m SyncReply) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// SyncAck is a message sent to confirm a hard synchronization, which is when -// the node has all the blocks. -// -// - implements serde.Message -type SyncAck struct{} - -// NewSyncAck creates a new sync acknowledgement. -func NewSyncAck() SyncAck { - return SyncAck{} -} - -// Serialize implements serde.Message. It returns the serialized data for this -// message. -func (m SyncAck) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// LinkKey is the key of the block link factory. -type LinkKey struct{} - -// ChainKey is the key of the chain factory. -type ChainKey struct{} - -// MessageFactory is a message factory for sync messages. -// -// - implements serde.Factory -type MessageFactory struct { - linkFac types.LinkFactory - chainFac types.ChainFactory -} - -// NewMessageFactory createsa new message factory. -func NewMessageFactory(fac types.LinkFactory, chainFac types.ChainFactory) MessageFactory { - return MessageFactory{ - linkFac: fac, - chainFac: chainFac, - } -} - -// Deserialize implements serde.Factory. It returns the message associated to -// the data if appropriate, otherwise an error. -func (fac MessageFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := msgFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, LinkKey{}, fac.linkFac) - ctx = serde.WithFactory(ctx, ChainKey{}, fac.chainFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding failed: %v", err) - } - - return msg, nil -} diff --git a/dela/core/ordering/cosipbft/blocksync/types/types_test.go b/dela/core/ordering/cosipbft/blocksync/types/types_test.go deleted file mode 100644 index b32deb7..0000000 --- a/dela/core/ordering/cosipbft/blocksync/types/types_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -var testCalls = &fake.Call{} - -func init() { - RegisterMessageFormat(fake.GoodFormat, fake.Format{Msg: SyncMessage{}, Call: testCalls}) - RegisterMessageFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestSyncMessage_GetChain(t *testing.T) { - m := NewSyncMessage(makeChain(t, 5)) - - require.NotNil(t, m.GetChain()) -} - -func TestSyncMessage_GetLatestIndex(t *testing.T) { - m := NewSyncMessage(makeChain(t, 5)) - - require.Equal(t, uint64(5), m.GetLatestIndex()) -} - -func TestSyncMessage_Serialize(t *testing.T) { - m := NewSyncMessage(makeChain(t, 6)) - - data, err := m.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = m.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestSyncRequest_GetFrom(t *testing.T) { - m := NewSyncRequest(2) - - require.Equal(t, uint64(2), m.GetFrom()) -} - -func TestSyncRequest_Serialize(t *testing.T) { - m := NewSyncRequest(3) - - data, err := m.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = m.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestSyncReply_GetLink(t *testing.T) { - link, err := types.NewBlockLink(types.Digest{1}, types.Block{}) - require.NoError(t, err) - - m := NewSyncReply(link) - - require.Equal(t, link, m.GetLink()) -} - -func TestSyncReply_Serialize(t *testing.T) { - link, err := types.NewBlockLink(types.Digest{}, types.Block{}) - require.NoError(t, err) - - m := NewSyncReply(link) - - data, err := m.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = m.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestSyncAck_Serialize(t *testing.T) { - m := NewSyncAck() - - data, err := m.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = m.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestMessageFactory_Deserialize(t *testing.T) { - testCalls.Clear() - - linkFac := types.NewLinkFactory(nil, nil, nil) - - fac := NewMessageFactory(linkFac, types.NewChainFactory(linkFac)) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, SyncMessage{}, msg) - - factory := testCalls.Get(0, 0).(serde.Context).GetFactory(LinkKey{}) - require.NotNil(t, factory) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding failed")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeChain(t *testing.T, index uint64) types.Chain { - block, err := types.NewBlock(simple.NewResult(nil), types.WithIndex(index)) - require.NoError(t, err) - - link, err := types.NewBlockLink(types.Digest{}, block) - require.NoError(t, err) - - return types.NewChain(link, nil) -} diff --git a/dela/core/ordering/cosipbft/contracts/viewchange/viewchange.go b/dela/core/ordering/cosipbft/contracts/viewchange/viewchange.go deleted file mode 100644 index 5ac9a2c..0000000 --- a/dela/core/ordering/cosipbft/contracts/viewchange/viewchange.go +++ /dev/null @@ -1,177 +0,0 @@ -// Package viewchange implements a native smart contract to update the roster of -// a chain. -// -// Documentation Last Review: 08.10.2020 -package viewchange - -import ( - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -const ( - // ContractName is the name of the contract. - ContractName = "go.dedis.ch/dela.ViewChange" - - // AuthorityArg is the key of the argument for the new authority. - AuthorityArg = "viewchange:authority" - - messageOnlyOne = "only one view change per block is allowed" - messageArgMissing = "authority not found in transaction" - messageStorageEmpty = "authority not found in storage" - messageStorageCorrupted = "invalid authority data in storage" - messageTooManyChanges = "too many changes" - messageStorageFailure = "storage failure" - messageDuplicate = "duplicate in roster" - messageUnauthorized = "unauthorized identity" -) - -// RegisterContract registers the view change contract to the given execution -// service. -func RegisterContract(exec *native.Service, c Contract) { - exec.Set(ContractName, c) -} - -// NewCreds creates new credentials for a view change contract execution. -func NewCreds(id []byte) access.Credential { - return access.NewContractCreds(id, ContractName, "update") -} - -// Manager is an extension of a normal transaction manager to help creating view -// change ones. -type Manager struct { - manager txn.Manager - context serde.Context -} - -// NewManager returns a view change manager from the transaction manager. -func NewManager(mgr txn.Manager) Manager { - return Manager{ - manager: mgr, - context: json.NewContext(), - } -} - -// Make creates a new transaction using the provided manager. It contains the -// new roster that the transaction should apply. -func (mgr Manager) Make(roster authority.Authority) (txn.Transaction, error) { - data, err := roster.Serialize(mgr.context) - if err != nil { - return nil, xerrors.Errorf("failed to serialize roster: %v", err) - } - - tx, err := mgr.manager.Make( - txn.Arg{Key: native.ContractArg, Value: []byte(ContractName)}, - txn.Arg{Key: AuthorityArg, Value: data}, - ) - if err != nil { - return nil, xerrors.Errorf("creating transaction: %v", err) - } - - return tx, nil -} - -// Contract is a contract to update the roster at a given key in the storage. It -// only allows one member change per transaction. -// -// - implements native.Contract -type Contract struct { - rosterKey []byte - rosterFac authority.Factory - accessKey []byte - access access.Service - context serde.Context -} - -// NewContract creates a new viewchange contract. -func NewContract(rKey, aKey []byte, rFac authority.Factory, srvc access.Service) Contract { - return Contract{ - rosterKey: rKey, - rosterFac: rFac, - accessKey: aKey, - access: srvc, - context: json.NewContext(), - } -} - -// Execute implements native.Contract. It looks for the roster in the -// transaction and updates the storage if there is at most one membership -// change. -func (c Contract) Execute(snap store.Snapshot, step execution.Step) error { - for _, tx := range step.Previous { - // Only one view change transaction is allowed per block to prevent - // malicious peers to reach the threshold. - if string(tx.GetArg(native.ContractArg)) == ContractName { - return xerrors.New(messageOnlyOne) - } - } - - roster, err := c.rosterFac.AuthorityOf(c.context, step.Current.GetArg(AuthorityArg)) - if err != nil { - reportErr(step.Current, xerrors.Errorf("incoming roster: %v", err)) - - return xerrors.New(messageArgMissing) - } - - currData, err := snap.Get(c.rosterKey) - if err != nil { - reportErr(step.Current, xerrors.Errorf("reading store: %v", err)) - - return xerrors.New(messageStorageEmpty) - } - - curr, err := c.rosterFac.AuthorityOf(c.context, currData) - if err != nil { - reportErr(step.Current, xerrors.Errorf("stored roster: %v", err)) - - return xerrors.New(messageStorageCorrupted) - } - - changeset := curr.Diff(roster) - - if changeset.NumChanges() > 1 { - return xerrors.New(messageTooManyChanges) - } - - for _, addr := range changeset.GetNewAddresses() { - _, index := curr.GetPublicKey(addr) - if index >= 0 { - return xerrors.Errorf("%s: %v", messageDuplicate, addr) - } - } - - creds := NewCreds(c.accessKey) - - err = c.access.Match(snap, creds, step.Current.GetIdentity()) - if err != nil { - reportErr(step.Current, xerrors.Errorf("access control: %v", err)) - - return xerrors.Errorf("%s: %v", messageUnauthorized, step.Current.GetIdentity()) - } - - err = snap.Set(c.rosterKey, step.Current.GetArg(AuthorityArg)) - if err != nil { - reportErr(step.Current, xerrors.Errorf("writing store: %v", err)) - - return xerrors.New(messageStorageFailure) - } - - return nil -} - -// reportErr prints a log with the actual error while the transaction will -// contain a simplified explanation. -func reportErr(tx txn.Transaction, err error) { - dela.Logger.Warn(). - Hex("ID", tx.GetID()). - Err(err). - Msg("transaction refused") -} diff --git a/dela/core/ordering/cosipbft/contracts/viewchange/viewchange_test.go b/dela/core/ordering/cosipbft/contracts/viewchange/viewchange_test.go deleted file mode 100644 index d030e3d..0000000 --- a/dela/core/ordering/cosipbft/contracts/viewchange/viewchange_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package viewchange - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestRegisterContract(t *testing.T) { - srvc := native.NewExecution() - - RegisterContract(srvc, Contract{}) -} - -func TestNewTransaction(t *testing.T) { - mgr := NewManager(signed.NewManager(fake.NewSigner(), nil)) - - tx, err := mgr.Make(authority.New(nil, nil)) - require.NoError(t, err) - require.NotNil(t, tx) - require.Equal(t, "[]", string(tx.GetArg(AuthorityArg))) - - _, err = mgr.Make(badRoster{}) - require.EqualError(t, err, fake.Err("failed to serialize roster")) - - mgr.manager = badManager{} - _, err = mgr.Make(authority.New(nil, nil)) - require.EqualError(t, err, fake.Err("creating transaction")) -} - -func TestContract_Execute(t *testing.T) { - fac := authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - - contract := NewContract([]byte("roster"), []byte("access"), fac, fakeAccess{}) - - err := contract.Execute(fakeStore{}, makeStep(t, "[]")) - require.NoError(t, err) - - err = contract.Execute(fakeStore{}, execution.Step{Previous: []txn.Transaction{makeTx(t, "")}}) - require.EqualError(t, err, "only one view change per block is allowed") - - contract.rosterFac = badRosterFac{} - err = contract.Execute(fakeStore{}, makeStep(t, "[]")) - require.EqualError(t, err, messageArgMissing) - - contract.rosterFac = fac - err = contract.Execute(fakeStore{errGet: fake.GetError()}, makeStep(t, "[]")) - require.EqualError(t, err, messageStorageEmpty) - - contract.rosterFac = badRosterFac{counter: fake.NewCounter(1)} - err = contract.Execute(fakeStore{}, makeStep(t, "[]")) - require.EqualError(t, err, messageStorageCorrupted) - - contract.rosterFac = fac - err = contract.Execute(fakeStore{}, makeStep(t, "[{},{},{}]")) - require.EqualError(t, err, messageTooManyChanges) - - err = contract.Execute(fakeStore{}, makeStep(t, "[{},{}]")) - require.EqualError(t, err, "duplicate in roster: fake.Address[0]") - - err = contract.Execute(fakeStore{errSet: fake.GetError()}, makeStep(t, "[]")) - require.EqualError(t, err, messageStorageFailure) - - contract.access = fakeAccess{err: fake.GetError()} - err = contract.Execute(fakeStore{}, makeStep(t, "[]")) - require.EqualError(t, err, "unauthorized identity: fake.PublicKey") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeStep(t *testing.T, arg string) execution.Step { - return execution.Step{Current: makeTx(t, arg)} -} - -func makeTx(t *testing.T, arg string) txn.Transaction { - args := []signed.TransactionOption{ - signed.WithArg(AuthorityArg, []byte(arg)), - signed.WithArg(native.ContractArg, []byte(ContractName)), - } - - tx, err := signed.NewTransaction(0, fake.PublicKey{}, args...) - require.NoError(t, err) - - return tx -} - -type fakeStore struct { - store.Snapshot - - errGet error - errSet error -} - -func (snap fakeStore) Get(key []byte) ([]byte, error) { - return []byte("[{}]"), snap.errGet -} - -func (snap fakeStore) Set(key, value []byte) error { - return snap.errSet -} - -type badRosterFac struct { - authority.Factory - counter *fake.Counter -} - -func (fac badRosterFac) AuthorityOf(serde.Context, []byte) (authority.Authority, error) { - if fac.counter.Done() { - return nil, fake.GetError() - } - - fac.counter.Decrease() - return nil, nil -} - -type badRoster struct { - authority.Authority -} - -func (ro badRoster) Serialize(serde.Context) ([]byte, error) { - return nil, fake.GetError() -} - -type badManager struct { - txn.Manager -} - -func (badManager) Make(opts ...txn.Arg) (txn.Transaction, error) { - return nil, fake.GetError() -} - -type fakeAccess struct { - access.Service - - err error -} - -func (srvc fakeAccess) Match(store.Readable, access.Credential, ...access.Identity) error { - return srvc.err -} diff --git a/dela/core/ordering/cosipbft/controller/action.go b/dela/core/ordering/cosipbft/controller/action.go deleted file mode 100644 index 0eb7efa..0000000 --- a/dela/core/ordering/cosipbft/controller/action.go +++ /dev/null @@ -1,282 +0,0 @@ -// This file contains the implementation of the controller actions. -// -// -// Documentation Last Review: 13.10.20202 - -package controller - -import ( - "bytes" - "context" - "encoding/base64" - "fmt" - "strings" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/contracts/viewchange" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -const separator = ":" - -// Service is the expected interface of the ordering service that is extended -// with some additional functions. -type Service interface { - ordering.Service - - GetRoster() (authority.Authority, error) - - Setup(ctx context.Context, ca crypto.CollectiveAuthority) error -} - -// SetupAction is an action to create a new chain with a list of participants. -// -// - implements node.ActionTemplate -type setupAction struct{} - -// Execute implements node.ActionTemplate. It reads the list of members and -// request the setup to the service. -func (a setupAction) Execute(ctx node.Context) error { - roster, err := a.readMembers(ctx) - if err != nil { - return xerrors.Errorf("failed to read roster: %v", err) - } - - var srvc Service - err = ctx.Injector.Resolve(&srvc) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - timeout := ctx.Flags.Duration("timeout") - - setupCtx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - err = srvc.Setup(setupCtx, roster) - if err != nil { - return xerrors.Errorf("failed to setup: %v", err) - } - - return nil -} - -func (a setupAction) readMembers(ctx node.Context) (authority.Authority, error) { - members := ctx.Flags.StringSlice("member") - - addrs := make([]mino.Address, len(members)) - pubkeys := make([]crypto.PublicKey, len(members)) - - for i, member := range members { - addr, pubkey, err := decodeMember(ctx, member) - if err != nil { - return nil, xerrors.Errorf("failed to decode: %v", err) - } - - addrs[i] = addr - pubkeys[i] = pubkey - } - - return authority.New(addrs, pubkeys), nil -} - -// ExportAction is an action to display a base64 string describing the node. It -// can be used to transmit the identity of a node to another one. -// -// - implements node.ActionTemplate -type exportAction struct{} - -// Execute implements node.ActionTemplate. It looks for the node address and -// public key and prints "$ADDR_BASE64:$PUBLIC_KEY_BASE64". -func (a exportAction) Execute(ctx node.Context) error { - var m mino.Mino - err := ctx.Injector.Resolve(&m) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - addr, err := m.GetAddress().MarshalText() - if err != nil { - return xerrors.Errorf("failed to marshal address: %v", err) - } - - var c cosi.CollectiveSigning - err = ctx.Injector.Resolve(&c) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - pubkey, err := c.GetSigner().GetPublicKey().MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to marshal public key: %v", err) - } - - desc := base64.StdEncoding.EncodeToString(addr) + separator + - base64.StdEncoding.EncodeToString(pubkey) - - fmt.Fprint(ctx.Out, desc) - - return nil -} - -// RosterAddAction is an action to require a roster change in the change by -// adding a new member. -// -// - implements node.ActionTemplate -type rosterAddAction struct{} - -// Execute implements node.ActionTemplate. It reads the new member and send a -// transaction to require a roster change. -func (rosterAddAction) Execute(ctx node.Context) error { - var srvc Service - err := ctx.Injector.Resolve(&srvc) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - tx, err := prepareRosterTx(ctx, srvc) - if err != nil { - return xerrors.Errorf("while preparing tx: %v", err) - } - - var p pool.Pool - err = ctx.Injector.Resolve(&p) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - wait := ctx.Flags.Duration("wait") - - // Start listening for new transactions before sending the new one, to - // be sure the event will be received. - watchCtx, cancel := context.WithTimeout(context.Background(), wait) - defer cancel() - - events := srvc.Watch(watchCtx) - - err = p.Add(tx) - if err != nil { - return xerrors.Errorf("failed to add transaction: %v", err) - } - - if wait > 0 { - dela.Logger.Debug(). - Hex("id", tx.GetID()). - Msg("wait for the transaction to be included") - - for event := range events { - for _, res := range event.Transactions { - if !bytes.Equal(res.GetTransaction().GetID(), tx.GetID()) { - continue - } - - dela.Logger.Debug(). - Hex("id", tx.GetID()). - Msg("transaction included in the block") - - accepted, msg := res.GetStatus() - if !accepted { - return xerrors.Errorf("transaction refused: %s", msg) - } - - return nil - } - } - - return xerrors.New("transaction not found after timeout") - } - - return nil -} - -func prepareRosterTx(ctx node.Context, srvc Service) (txn.Transaction, error) { - roster, err := srvc.GetRoster() - if err != nil { - return nil, xerrors.Errorf("failed to read roster: %v", err) - } - - addr, pubkey, err := decodeMember(ctx, ctx.Flags.String("member")) - if err != nil { - return nil, xerrors.Errorf("failed to decode member: %v", err) - } - - cset := authority.NewChangeSet() - cset.Add(addr, pubkey) - - mgr, err := makeManager(ctx) - if err != nil { - return nil, xerrors.Errorf("txn manager: %v", err) - } - - tx, err := viewchange.NewManager(mgr).Make(roster.Apply(cset)) - if err != nil { - return nil, xerrors.Errorf("transaction: %v", err) - } - - return tx, nil -} - -func makeManager(ctx node.Context) (txn.Manager, error) { - var mgr txn.Manager - err := ctx.Injector.Resolve(&mgr) - if err != nil { - return nil, xerrors.Errorf("injector: %v", err) - } - - // Synchronize the manager with the latest state of the chain so that it can - // create valid transactions. - err = mgr.Sync() - if err != nil { - return nil, xerrors.Errorf("sync: %v", err) - } - - return mgr, nil -} - -func decodeMember(ctx node.Context, str string) (mino.Address, crypto.PublicKey, error) { - parts := strings.Split(str, separator) - if len(parts) != 2 { - return nil, nil, xerrors.New("invalid member base64 string") - } - - // 1. Deserialize the address. - var m mino.Mino - err := ctx.Injector.Resolve(&m) - if err != nil { - return nil, nil, xerrors.Errorf("injector: %v", err) - } - - addrBuf, err := base64.StdEncoding.DecodeString(parts[0]) - if err != nil { - return nil, nil, xerrors.Errorf("base64 address: %v", err) - } - - addr := m.GetAddressFactory().FromText(addrBuf) - - // 2. Deserialize the public key. - var c cosi.CollectiveSigning - err = ctx.Injector.Resolve(&c) - if err != nil { - return nil, nil, xerrors.Errorf("injector: %v", err) - } - - pubkeyBuf, err := base64.StdEncoding.DecodeString(parts[1]) - if err != nil { - return nil, nil, xerrors.Errorf("base64 public key: %v", err) - } - - pubkey, err := c.GetPublicKeyFactory().FromBytes(pubkeyBuf) - if err != nil { - return nil, nil, xerrors.Errorf("failed to decode public key: %v", err) - } - - return addr, pubkey, nil -} diff --git a/dela/core/ordering/cosipbft/controller/action_test.go b/dela/core/ordering/cosipbft/controller/action_test.go deleted file mode 100644 index adb6223..0000000 --- a/dela/core/ordering/cosipbft/controller/action_test.go +++ /dev/null @@ -1,291 +0,0 @@ -package controller - -import ( - "bytes" - "context" - "io" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/core/txn/pool/mem" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func TestSetupAction_Execute(t *testing.T) { - action := setupAction{} - - calls := &fake.Call{} - ctx := prepContext(calls) - ctx.Flags.(node.FlagSet)["member"] = []interface{}{"YQ==:YQ==", "YQ==:YQ=="} - - err := action.Execute(ctx) - require.NoError(t, err) - require.Equal(t, 1, calls.Len()) - require.Equal(t, 2, calls.Get(0, 1).(mino.Players).Len()) - - ctx.Flags.(node.FlagSet)["member"] = []interface{}{""} - err = action.Execute(ctx) - require.EqualError(t, err, "failed to read roster: failed to decode: invalid member base64 string") - - ctx.Flags = make(node.FlagSet) - ctx.Injector = node.NewInjector() - err = action.Execute(ctx) - require.EqualError(t, err, "injector: couldn't find dependency for 'controller.Service'") - - ctx.Injector.Inject(fakeService{err: fake.GetError()}) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("failed to setup")) -} - -func TestExportAction_Execute(t *testing.T) { - action := exportAction{} - - ctx := prepContext(nil) - - buffer := new(bytes.Buffer) - ctx.Out = buffer - - err := action.Execute(ctx) - require.NoError(t, err) - require.Equal(t, "AAAAAA==:UEs=", buffer.String()) - - ctx.Injector = node.NewInjector() - err = action.Execute(ctx) - require.EqualError(t, err, "injector: couldn't find dependency for 'mino.Mino'") - - ctx.Injector.Inject(fake.NewBadMino()) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("failed to marshal address")) - - ctx.Injector.Inject(fake.Mino{}) - err = action.Execute(ctx) - require.EqualError(t, err, "injector: couldn't find dependency for 'cosi.CollectiveSigning'") - - ctx.Injector.Inject(fakeCosi{err: true}) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("failed to marshal public key")) -} - -func TestRosterAddAction_Execute(t *testing.T) { - action := rosterAddAction{} - - ctx := prepContext(nil) - ctx.Flags.(node.FlagSet)["member"] = "YQ==:YQ==" - ctx.Flags.(node.FlagSet)["wait"] = float64(time.Second) - - err := action.Execute(ctx) - require.NoError(t, err) - - ctx.Flags.(node.FlagSet)["wait"] = float64(0) - - err = action.Execute(ctx) - require.NoError(t, err) - - var p pool.Pool - require.NoError(t, ctx.Injector.Resolve(&p)) - require.Equal(t, 1, p.Stats().TxCount) - - ctx.Injector = node.NewInjector() - err = action.Execute(ctx) - require.EqualError(t, err, "injector: couldn't find dependency for 'controller.Service'") - - ctx.Injector.Inject(fakeService{err: fake.GetError()}) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("while preparing tx: failed to read roster")) - - ctx.Injector.Inject(fakeService{}) - err = action.Execute(ctx) - require.EqualError(t, err, - "while preparing tx: failed to decode member: injector: couldn't find dependency for 'mino.Mino'") - - ctx.Injector.Inject(fake.Mino{}) - ctx.Injector.Inject(fakeCosi{}) - err = action.Execute(ctx) - require.EqualError(t, err, - "while preparing tx: txn manager: injector: couldn't find dependency for 'txn.Manager'") - - ctx.Injector.Inject(fakeTxManager{errSync: fake.GetError()}) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("while preparing tx: txn manager: sync")) - - ctx.Injector.Inject(fakeTxManager{errMake: fake.GetError()}) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("while preparing tx: transaction: creating transaction")) - - ctx.Injector.Inject(fakeTxManager{}) - err = action.Execute(ctx) - require.EqualError(t, err, "injector: couldn't find dependency for 'pool.Pool'") - - ctx.Injector.Inject(badPool{}) - err = action.Execute(ctx) - require.EqualError(t, err, fake.Err("failed to add transaction")) - - events := []ordering.Event{ - {Transactions: []validation.TransactionResult{fakeResult{refused: true}}}, - } - ctx = prepContext(nil) - ctx.Flags.(node.FlagSet)["member"] = "YQ==:YQ==" - ctx.Flags.(node.FlagSet)["wait"] = float64(time.Second) - ctx.Injector.Inject(fakeService{events: events}) - err = action.Execute(ctx) - require.EqualError(t, err, "transaction refused: message") - - ctx.Injector.Inject(fakeService{events: nil}) - err = action.Execute(ctx) - require.EqualError(t, err, "transaction not found after timeout") -} - -func TestDecodeMember(t *testing.T) { - ctx := prepContext(nil) - - _, _, err := decodeMember(ctx, "a:a") - require.EqualError(t, err, "base64 address: illegal base64 data at input byte 0") - - _, _, err = decodeMember(ctx, ":a") - require.EqualError(t, err, "base64 public key: illegal base64 data at input byte 0") - - ctx.Injector = node.NewInjector() - ctx.Injector.Inject(fake.Mino{}) - _, _, err = decodeMember(ctx, ":") - require.EqualError(t, err, "injector: couldn't find dependency for 'cosi.CollectiveSigning'") - - ctx.Injector.Inject(fakeCosi{err: true}) - _, _, err = decodeMember(ctx, ":") - require.EqualError(t, err, fake.Err("failed to decode public key")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func prepContext(calls *fake.Call) node.Context { - ctx := node.Context{ - Injector: node.NewInjector(), - Flags: make(node.FlagSet), - Out: io.Discard, - } - - events := []ordering.Event{ - {Transactions: []validation.TransactionResult{fakeResult{}}}, - } - - ctx.Injector.Inject(fake.Mino{}) - ctx.Injector.Inject(fakeCosi{}) - ctx.Injector.Inject(fakeService{calls: calls, events: events}) - ctx.Injector.Inject(mem.NewPool()) - ctx.Injector.Inject(fakeTxManager{}) - - return ctx -} - -type fakeService struct { - ordering.Service - calls *fake.Call - events []ordering.Event - err error -} - -func (s fakeService) GetRoster() (authority.Authority, error) { - return authority.New(nil, nil), s.err -} - -func (s fakeService) Setup(ctx context.Context, ca crypto.CollectiveAuthority) error { - s.calls.Add(ctx, ca) - return s.err -} - -func (s fakeService) Watch(context.Context) <-chan ordering.Event { - ch := make(chan ordering.Event, len(s.events)) - for _, evt := range s.events { - ch <- evt - } - close(ch) - - return ch -} - -func (s fakeService) Close() error { - return s.err -} - -type fakeCosi struct { - cosi.CollectiveSigning - err bool -} - -func (c fakeCosi) GetPublicKeyFactory() crypto.PublicKeyFactory { - if c.err { - return fake.NewBadPublicKeyFactory() - } - - return fake.NewPublicKeyFactory(fake.PublicKey{}) -} - -func (c fakeCosi) GetSigner() crypto.Signer { - if c.err { - return fake.NewSignerWithPublicKey(fake.NewBadPublicKey()) - } - - return fake.NewSigner() -} - -type fakeTx struct { - txn.Transaction -} - -func (fakeTx) GetNonce() uint64 { - return 0 -} - -func (fakeTx) GetIdentity() access.Identity { - return fake.PublicKey{} -} - -func (fakeTx) GetID() []byte { - return []byte{0xaa} -} - -type fakeResult struct { - validation.TransactionResult - refused bool -} - -func (fakeResult) GetTransaction() txn.Transaction { - return fakeTx{} -} - -func (res fakeResult) GetStatus() (bool, string) { - return !res.refused, "message" -} - -type fakeTxManager struct { - txn.Manager - errMake error - errSync error -} - -func (mgr fakeTxManager) Make(args ...txn.Arg) (txn.Transaction, error) { - return fakeTx{}, mgr.errMake -} - -func (mgr fakeTxManager) Sync() error { - return mgr.errSync -} - -type badPool struct { - pool.Pool -} - -func (p badPool) Add(txn.Transaction) error { - return fake.GetError() -} diff --git a/dela/core/ordering/cosipbft/controller/controller.go b/dela/core/ordering/cosipbft/controller/controller.go deleted file mode 100644 index eaad146..0000000 --- a/dela/core/ordering/cosipbft/controller/controller.go +++ /dev/null @@ -1,258 +0,0 @@ -// Package controller implements a minimal controller for cosipbft. -// -// Documentation Last Review: 13.10.2020 -package controller - -import ( - "encoding" - "path/filepath" - "time" - - "go.dedis.ch/dela/contracts/value" - "go.dedis.ch/dela/crypto" - - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/access/darc" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/ordering/cosipbft" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store/hashtree/binprefix" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn/pool" - poolimpl "go.dedis.ch/dela/core/txn/pool/gossip" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/cosi/threshold" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/crypto/loader" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/gossip" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -const privateKeyFile = "private.key" - -// valueAccessKey is the access key used for the value contract. -var valueAccessKey = [32]byte{2} - -func blsSigner() encoding.BinaryMarshaler { - return bls.NewSigner() -} - -// miniController is a CLI initializer to inject an ordering service that is -// using collective signatures and PBFT for the consensus. -// -// - implements node.Initializer -type miniController struct { - signerFn func() encoding.BinaryMarshaler -} - -// NewController creates a new minimal controller for cosipbft. -func NewController() node.Initializer { - return miniController{ - signerFn: blsSigner, - } -} - -// SetCommands implements node.Initializer. It sets the command to control the -// service. -func (miniController) SetCommands(builder node.Builder) { - cmd := builder.SetCommand("ordering") - cmd.SetDescription("Ordering service administration") - - sub := cmd.SetSubCommand("setup") - sub.SetDescription("Creates a new chain") - sub.SetFlags( - cli.DurationFlag{ - Name: "timeout", - Usage: "maximum amount of time to setup", - Value: 20 * time.Second, - }, - cli.StringSliceFlag{ - Name: "member", - Required: true, - Usage: "one or several member of the new chain", - }, - ) - sub.SetAction(builder.MakeAction(setupAction{})) - - sub = cmd.SetSubCommand("export") - sub.SetDescription("Export the node information") - sub.SetAction(builder.MakeAction(exportAction{})) - - sub = cmd.SetSubCommand("roster") - sub.SetDescription("Roster administration") - - sub = sub.SetSubCommand("add") - sub.SetDescription("Add a member to the chain") - sub.SetFlags( - cli.StringFlag{ - Name: "member", - Required: true, - Usage: "base64 description of the member to add", - }, - cli.DurationFlag{ - Name: "wait", - Usage: "wait for the transaction to be processed", - }, - ) - sub.SetAction(builder.MakeAction(rosterAddAction{})) -} - -// OnStart implements node.Initializer. It starts the ordering components and -// inject them. -func (m miniController) OnStart(flags cli.Flags, inj node.Injector) error { - var onet mino.Mino - err := inj.Resolve(&onet) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - signer, err := m.getSigner(flags) - if err != nil { - return xerrors.Errorf("signer: %v", err) - } - - cosi := threshold.NewThreshold(onet.WithSegment("cosi"), signer) - cosi.SetThreshold(threshold.ByzantineThreshold) - - exec := native.NewExecution() - access := darc.NewService(json.NewContext()) - - rosterFac := authority.NewFactory(onet.GetAddressFactory(), cosi.GetPublicKeyFactory()) - cosipbft.RegisterRosterContract(exec, rosterFac, access) - - value.RegisterContract(exec, value.NewContract(valueAccessKey[:], access)) - - txFac := signed.NewTransactionFactory() - vs := simple.NewService(exec, txFac) - - pool, err := poolimpl.NewPool(gossip.NewFlat(onet.WithSegment("pool"), txFac)) - if err != nil { - return xerrors.Errorf("pool: %v", err) - } - - var db kv.DB - err = inj.Resolve(&db) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - tree := binprefix.NewMerkleTree(db, binprefix.Nonce{}) - - param := cosipbft.ServiceParam{ - Mino: onet, - Cosi: cosi, - Validation: vs, - Access: access, - Pool: pool, - DB: db, - Tree: tree, - } - - err = tree.Load() - if err != nil { - return xerrors.Errorf("failed to load tree: %v", err) - } - - genstore := blockstore.NewGenesisDiskStore(db, types.NewGenesisFactory(rosterFac)) - - err = genstore.Load() - if err != nil { - return xerrors.Errorf("failed to load genesis: %v", err) - } - - blockFac := types.NewBlockFactory(vs.GetFactory()) - csFac := authority.NewChangeSetFactory(onet.GetAddressFactory(), cosi.GetPublicKeyFactory()) - linkFac := types.NewLinkFactory(blockFac, cosi.GetSignatureFactory(), csFac) - - blocks := blockstore.NewDiskStore(db, linkFac) - - err = blocks.Load() - if err != nil { - return xerrors.Errorf("failed to load blocks: %v", err) - } - - srvc, err := cosipbft.NewService(param, cosipbft.WithGenesisStore(genstore), cosipbft.WithBlockStore(blocks)) - if err != nil { - return xerrors.Errorf("service: %v", err) - } - - inj.Inject(srvc) - inj.Inject(cosi) - inj.Inject(pool) - inj.Inject(vs) - inj.Inject(exec) - inj.Inject(&access) - - return nil -} - -// OnStop implements node.Initializer. It stops the service and the transaction -// pool. -func (miniController) OnStop(inj node.Injector) error { - var srvc ordering.Service - err := inj.Resolve(&srvc) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - err = srvc.Close() - if err != nil { - return xerrors.Errorf("while closing service: %v", err) - } - - var p pool.Pool - err = inj.Resolve(&p) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - err = p.Close() - if err != nil { - return xerrors.Errorf("while closing pool: %v", err) - } - - return nil -} - -func (m miniController) getSigner(flags cli.Flags) (crypto.AggregateSigner, error) { - loader := loader.NewFileLoader(filepath.Join(flags.Path("config"), privateKeyFile)) - - signerdata, err := loader.LoadOrCreate(generator{newFn: m.signerFn}) - if err != nil { - return nil, xerrors.Errorf("while loading: %v", err) - } - - signer, err := bls.NewSignerFromBytes(signerdata) - if err != nil { - return nil, xerrors.Errorf("while unmarshaling: %v", err) - } - - return signer, nil -} - -// generator is an implementation to generate a private key. -// -// - implements loader.Generator -type generator struct { - newFn func() encoding.BinaryMarshaler -} - -// Generate implements loader.Generator. It returns the marshaled data of a -// private key. -func (g generator) Generate() ([]byte, error) { - signer := g.newFn() - - data, err := signer.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("failed to marshal signer: %v", err) - } - - return data, nil -} diff --git a/dela/core/ordering/cosipbft/controller/controller_test.go b/dela/core/ordering/cosipbft/controller/controller_test.go deleted file mode 100644 index 1daf337..0000000 --- a/dela/core/ordering/cosipbft/controller/controller_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package controller - -import ( - "encoding" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestMinimal_SetCommands(t *testing.T) { - m := NewController() - - b := node.NewBuilder() - m.SetCommands(b) -} - -func TestMinimal_OnStart(t *testing.T) { - flags, dir, clean := makeFlags(t) - defer clean() - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - m := NewController().(miniController) - - inj := node.NewInjector() - inj.Inject(fake.Mino{}) - inj.Inject(db) - - err = m.OnStart(flags, inj) - require.NoError(t, err) -} - -func TestMinimal_MissingMino_OnStart(t *testing.T) { - m := NewController() - - err := m.OnStart(make(node.FlagSet), node.NewInjector()) - require.EqualError(t, err, - "injector: couldn't find dependency for 'mino.Mino'") -} - -func TestMinimal_FailLoadKey_OnStart(t *testing.T) { - flags, _, clean := makeFlags(t) - defer clean() - - m := NewController().(miniController) - - inj := node.NewInjector() - inj.Inject(fake.Mino{}) - inj.Inject(fake.NewInMemoryDB()) - - m.signerFn = badFn - - err := m.OnStart(flags, inj) - require.EqualError(t, err, - fake.Err("signer: while loading: generator failed: failed to marshal signer")) -} - -func TestMinimal_MissingDB_OnStart(t *testing.T) { - flags, _, clean := makeFlags(t) - defer clean() - - m := NewController().(miniController) - - inj := node.NewInjector() - inj.Inject(fake.Mino{}) - - err := m.OnStart(flags, inj) - require.EqualError(t, err, "injector: couldn't find dependency for 'kv.DB'") -} - -func TestMinimal_MalformedKey_OnStart(t *testing.T) { - flags, dir, clean := makeFlags(t) - defer clean() - - m := NewController().(miniController) - - inj := node.NewInjector() - inj.Inject(fake.Mino{}) - inj.Inject(fake.NewInMemoryDB()) - - file, err := os.Create(filepath.Join(dir, privateKeyFile)) - require.NoError(t, err) - - file.Close() - - err = m.OnStart(flags, inj) - require.Error(t, err) - require.Contains(t, err.Error(), "signer: while unmarshaling: ") -} - -func TestMinimal_OnStop(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-test-") - require.NoError(t, err) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - m := NewController() - - fset := make(node.FlagSet) - fset["config"] = dir - - inj := node.NewInjector() - inj.Inject(fake.Mino{}) - inj.Inject(db) - - err = m.OnStart(fset, inj) - require.NoError(t, err) - - err = m.OnStop(inj) - require.NoError(t, err) - - inj = node.NewInjector() - err = m.OnStop(inj) - require.EqualError(t, err, - "injector: couldn't find dependency for 'ordering.Service'") - - inj.Inject(fakeService{err: fake.GetError()}) - err = m.OnStop(inj) - require.EqualError(t, err, fake.Err("while closing service")) - - inj.Inject(fakeService{}) - err = m.OnStop(inj) - require.EqualError(t, err, - "injector: couldn't find dependency for 'pool.Pool'") - - inj.Inject(fakePool{err: fake.GetError()}) - err = m.OnStop(inj) - require.EqualError(t, err, fake.Err("while closing pool")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeFlags(t *testing.T) (cli.Flags, string, func()) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-") - require.NoError(t, err) - - fset := make(node.FlagSet) - fset["config"] = dir - - return fset, dir, func() { os.RemoveAll(dir) } -} - -func badFn() encoding.BinaryMarshaler { - return fake.NewBadHash() -} - -type fakePool struct { - pool.Pool - - err error -} - -func (p fakePool) Close() error { - return p.err -} diff --git a/dela/core/ordering/cosipbft/cosipbft.go b/dela/core/ordering/cosipbft/cosipbft.go deleted file mode 100644 index 647877a..0000000 --- a/dela/core/ordering/cosipbft/cosipbft.go +++ /dev/null @@ -1,797 +0,0 @@ -// Package cosipbft implements an ordering service using collective signatures -// for the consensus. -// -// The consensus follows the PBFT algorithm using collective signatures to -// perform the prepare and commit phases. The leader is orchestrating the -// protocol and the followers wait for incoming messages to update their own -// state machines and reply with signatures when the leader candidate is valid. -// If the leader fails to send a candidate, or finalize it, the followers will -// timeout after some time and move to a view change state. -// -// The view change procedure is always waiting on the leader+1 confirmation -// before moving to leader+2, leader+3, etc. It means that if not enough nodes -// are online to create a block, the round will fail until enough wakes up and -// confirm leader+1. If leader+1 fails to create a block within the round -// timeout, a new view change starts for leader+2 and so on until a block is -// created. -// -// Before each PBFT round, a synchronization is run from the leader to allow -// nodes that have fallen behind (or are new) to catch missing blocks. Only a -// PBFT threshold of nodes needs to confirm a hard synchronization (having all -// the blocks) for the round to proceed, but others will keep catching up. -// -// Related Papers: -// -// Enhancing Bitcoin Security and Performance with Strong Consistency via -// Collective Signing (2016) -// https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_kokoris-kogias.pdf -// -// Documentation Last Review: 12.10.2020 -package cosipbft - -import ( - "context" - "fmt" - "math" - "time" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync" - "go.dedis.ch/dela/core/ordering/cosipbft/contracts/viewchange" - "go.dedis.ch/dela/core/ordering/cosipbft/pbft" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/cosi/threshold" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -const ( - // DefaultRoundTimeout is the maximum round time the service waits - // for an event to happen. - DefaultRoundTimeout = 1 * time.Second - - // DefaultFailedRoundTimeout is the maximum round time the service waits - // for an event to happen, after a round has failed, thus letting time - // for a view change to establish a new leader. - // DefaultFailedRoundTimeout is generally bigger than DefaultRoundTimeout - DefaultFailedRoundTimeout = 2 * time.Second - - // DefaultTransactionTimeout is the maximum allowed age of transactions - // before a view change is executed. - DefaultTransactionTimeout = 10 * time.Second - - // RoundWait is the constant value of the exponential backoff use between - // round failures. - RoundWait = 5 * time.Millisecond - - // RoundMaxWait is the maximum amount for the backoff. - RoundMaxWait = 5 * time.Minute - - rpcName = "cosipbft" -) - -// RegisterRosterContract registers the native smart contract to update the -// roster to the given service. -func RegisterRosterContract(exec *native.Service, rFac authority.Factory, srvc access.Service) { - contract := viewchange.NewContract(keyRoster[:], keyAccess[:], rFac, srvc) - - viewchange.RegisterContract(exec, contract) -} - -// Service is an ordering service using collective signatures combined with PBFT -// to create a chain of blocks. -// -// - implements ordering.Service -type Service struct { - *processor - - me mino.Address - rpc mino.RPC - actor cosi.Actor - val validation.Service - verifierFac crypto.VerifierFactory - - timeoutRound time.Duration - timeoutRoundAfterFailure time.Duration - transactionTimeout time.Duration - - events chan ordering.Event - closing chan struct{} - closed chan struct{} - failedRound bool -} - -type serviceTemplate struct { - hashFac crypto.HashFactory - blocks blockstore.BlockStore - genesis blockstore.GenesisStore -} - -// ServiceOption is the type of option to set some fields of the service. -type ServiceOption func(*serviceTemplate) - -// WithGenesisStore is an option to set the genesis store. -func WithGenesisStore(store blockstore.GenesisStore) ServiceOption { - return func(tmpl *serviceTemplate) { - tmpl.genesis = store - } -} - -// WithBlockStore is an option to set the block store. -func WithBlockStore(store blockstore.BlockStore) ServiceOption { - return func(tmpl *serviceTemplate) { - tmpl.blocks = store - } -} - -// WithHashFactory is an option to set the hash factory used by the service. -func WithHashFactory(fac crypto.HashFactory) ServiceOption { - return func(tmpl *serviceTemplate) { - tmpl.hashFac = fac - } -} - -// ServiceParam is the different components to provide to the service. All the -// fields are mandatory and it will panic if any is nil. -type ServiceParam struct { - Mino mino.Mino - Cosi cosi.CollectiveSigning - Validation validation.Service - Access access.Service - Pool pool.Pool - Tree hashtree.Tree - DB kv.DB -} - -// NewService starts a new ordering service. -func NewService(param ServiceParam, opts ...ServiceOption) (*Service, error) { - tmpl := serviceTemplate{ - hashFac: crypto.NewSha256Factory(), - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - } - - for _, opt := range opts { - opt(&tmpl) - } - - proc := newProcessor() - proc.hashFactory = tmpl.hashFac - proc.blocks = tmpl.blocks - proc.genesis = tmpl.genesis - proc.pool = param.Pool - proc.rosterFac = authority.NewFactory(param.Mino.GetAddressFactory(), param.Cosi.GetPublicKeyFactory()) - proc.tree = blockstore.NewTreeCache(param.Tree) - proc.access = param.Access - proc.logger = dela.Logger.With().Str("addr", param.Mino.GetAddress().String()).Logger() - - pcparam := pbft.StateMachineParam{ - Logger: proc.logger, - Validation: param.Validation, - Signer: param.Cosi.GetSigner(), - VerifierFactory: param.Cosi.GetVerifierFactory(), - Blocks: tmpl.blocks, - Genesis: tmpl.genesis, - Tree: proc.tree, - AuthorityReader: proc.readRoster, - DB: param.DB, - } - - proc.pbftsm = pbft.NewStateMachine(pcparam) - - blockFac := types.NewBlockFactory(param.Validation.GetFactory()) - csFac := authority.NewChangeSetFactory(param.Mino.GetAddressFactory(), param.Cosi.GetPublicKeyFactory()) - linkFac := types.NewLinkFactory(blockFac, param.Cosi.GetSignatureFactory(), csFac) - chainFac := types.NewChainFactory(linkFac) - - syncparam := blocksync.SyncParam{ - Mino: param.Mino, - Blocks: tmpl.blocks, - Genesis: tmpl.genesis, - PBFT: proc.pbftsm, - LinkFactory: linkFac, - ChainFactory: chainFac, - VerifierFactory: param.Cosi.GetVerifierFactory(), - } - - bs := blocksync.NewSynchronizer(syncparam) - - proc.sync = bs - - fac := types.NewMessageFactory( - types.NewGenesisFactory(proc.rosterFac), - blockFac, - param.Mino.GetAddressFactory(), - param.Cosi.GetSignatureFactory(), - csFac, - ) - - proc.MessageFactory = fac - - actor, err := param.Cosi.Listen(proc) - if err != nil { - return nil, xerrors.Errorf("creating cosi failed: %v", err) - } - - s := &Service{ - processor: proc, - me: param.Mino.GetAddress(), - rpc: mino.MustCreateRPC(param.Mino, rpcName, proc, fac), - actor: actor, - val: param.Validation, - verifierFac: param.Cosi.GetVerifierFactory(), - timeoutRound: DefaultRoundTimeout, - timeoutRoundAfterFailure: DefaultFailedRoundTimeout, - transactionTimeout: DefaultTransactionTimeout, - events: make(chan ordering.Event, 1), - closing: make(chan struct{}), - closed: make(chan struct{}), - } - - // Pool will filter the transaction that are already accepted by this - // service. - param.Pool.AddFilter(poolFilter{tree: proc.tree, srvc: param.Validation}) - - go s.main() - - go s.watchBlocks() - - if s.genesis.Exists() { - // If the genesis already exists, the service can start right away to - // participate in the chain. - close(s.started) - } - - return s, nil -} - -// Setup creates a genesis block and sends it to the collective authority. -func (s *Service) Setup(ctx context.Context, ca crypto.CollectiveAuthority) error { - err := s.storeGenesis(authority.FromAuthority(ca), nil) - if err != nil { - return xerrors.Errorf("creating genesis: %v", err) - } - - genesis, err := s.genesis.Get() - if err != nil { - return xerrors.Errorf("failed to read genesis: %v", err) - } - - resps, err := s.rpc.Call(ctx, types.NewGenesisMessage(genesis), ca) - if err != nil { - return xerrors.Errorf("sending genesis: %v", err) - } - - for resp := range resps { - _, err := resp.GetMessageOrError() - if err != nil { - return xerrors.Errorf("one request failed: %v", err) - } - } - - s.logger.Info(). - Int("roster", ca.Len()). - Stringer("digest", genesis.GetHash()). - Msg("new chain has been created") - - return nil -} - -// GetProof implements ordering.Service. It returns the proof of absence or -// inclusion for the latest block. The proof integrity is not verified as this -// is assumed the node is acting correctly so the data is anyway consistent. The -// proof must be verified by the caller when leaving the trusted environment, -// for instance when the proof is sent over the network. -func (s *Service) GetProof(key []byte) (ordering.Proof, error) { - tree, unlock := s.tree.GetWithLock() - defer unlock() - - path, err := tree.GetPath(key) - if err != nil { - return nil, xerrors.Errorf("reading path: %v", err) - } - - // The chain is fetched while having the lock of the tree cache so that - // there is no race between the two stores when finalizing a block. - chain, err := s.blocks.GetChain() - if err != nil { - return nil, xerrors.Errorf("reading chain: %v", err) - } - - return newProof(path, chain), nil -} - -// GetStore implements ordering.Service. It returns the current tree as a -// read-only storage. -func (s *Service) GetStore() store.Readable { - return s.tree.Get() -} - -// GetRoster returns the current roster of the service. -func (s *Service) GetRoster() (authority.Authority, error) { - return s.getCurrentRoster() -} - -// Watch implements ordering.Service. It returns a channel that will be -// populated with new incoming blocks and some information about them. The -// channel must be listened at all time and the context must be closed when -// done. -func (s *Service) Watch(ctx context.Context) <-chan ordering.Event { - obs := observer{ch: make(chan ordering.Event, 1)} - - s.watcher.Add(obs) - - go func() { - <-ctx.Done() - s.watcher.Remove(obs) - close(obs.ch) - }() - - return obs.ch -} - -// Close implements ordering.Service. It gracefully closes the service. It will -// announce the closing request and wait for the current to end before -// returning. -func (s *Service) Close() error { - close(s.closing) - <-s.closed - - return nil -} - -func (s *Service) watchBlocks() { - ctx, cancel := context.WithCancel(context.Background()) - - go func() { - <-s.closing - cancel() - }() - - linkCh := s.blocks.Watch(ctx) - - for link := range linkCh { - // 1. Remove the transactions from the pool to avoid duplicates. - for _, res := range link.GetBlock().GetData().GetTransactionResults() { - err := s.pool.Remove(res.GetTransaction()) - if err != nil { - s.logger.Err(err).Msg("removing transaction") - } - } - - // 2. Update the current membership. - err := s.refreshRoster() - if err != nil { - s.logger.Err(err).Msg("roster refresh failed") - } - - event := ordering.Event{ - Index: link.GetBlock().GetIndex(), - Transactions: link.GetBlock().GetData().GetTransactionResults(), - } - - // 3. Notify the main loop that a new block has been created, but ignore - // if the channel is busy. - select { - case s.events <- event: - default: - } - - // 4. Notify the new block to potential listeners. - s.watcher.Notify(event) - - s.logger.Info(). - Uint64("index", link.GetBlock().GetIndex()). - Stringer("root", link.GetBlock().GetTreeRoot()). - Msg("block event") - } -} - -func (s *Service) refreshRoster() error { - roster, err := s.getCurrentRoster() - if err != nil { - return xerrors.Errorf("reading roster: %v", err) - } - - err = s.pool.SetPlayers(roster) - if err != nil { - return xerrors.Errorf("updating tx pool: %v", err) - } - - return nil -} - -func (s *Service) main() error { - defer close(s.closed) - - select { - case <-s.started: - // A genesis block has been set, the node will then follow the chain - // related to it. - s.logger.Info().Msg("node has started following the chain") - - case <-s.closing: - return nil - } - - // Update the components that need to learn about the participants like the - // transaction pool. - err := s.refreshRoster() - if err != nil { - return xerrors.Errorf("refreshing roster: %v", err) - } - - s.logger.Debug().Msg("node has started") - - backoff := float64(0) - - for { - // When a round failure occurs, it sleeps with a given backoff to give a - // chance to the system to recover without exhausting the resources. - time.Sleep(calculateBackoff(backoff)) - - select { - case <-s.closing: - return nil - - default: - ctx, cancel := context.WithCancel(context.Background()) - - go func() { - select { - case <-s.closing: - cancel() - case <-ctx.Done(): - } - }() - - err := s.doRound(ctx) - cancel() - - if err != nil { - if calculateBackoff(backoff+1) < RoundMaxWait { - backoff++ - } - - s.logger.Err(err).Msg("round failed") - } else { - backoff = 0 - } - } - } -} - -func (s *Service) doRound(ctx context.Context) error { - roster, err := s.getCurrentRoster() - if err != nil { - return xerrors.Errorf("reading roster: %v", err) - } - - timeout := s.timeoutRound - if s.failedRound { - timeout = s.timeoutRoundAfterFailure - } - - leader, err := s.pbftsm.GetLeader() - if err != nil { - return xerrors.Errorf("reading leader: %v", err) - } - - if s.me.Equal(leader) { - s.logger.Debug().Msgf("Starting a leader round with a %.1f seconds timeout", timeout.Seconds()) - return s.doLeaderRound(ctx, roster, timeout) - } - - s.logger.Debug().Msgf("Starting a follower round with a %.1f seconds timeout", timeout.Seconds()) - return s.doFollowerRound(ctx, roster) -} - -func (s *Service) doLeaderRound(ctx context.Context, roster authority.Authority, timeout time.Duration) error { - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - s.logger.Debug().Uint64("index", s.blocks.Len()).Msg("round has started") - - // Send a synchronization to the roster so that they can learn about the - // latest block of the chain. - err := s.sync.Sync(ctx, roster, blocksync.Config{MinHard: threshold.ByzantineThreshold(roster.Len())}) - if err != nil { - return xerrors.Errorf("sync failed: %v", err) - } - - s.logger.Debug().Uint64("index", s.blocks.Len()).Msg("pbft has started") - - err = s.doPBFT(ctx) - if err != nil { - return xerrors.Errorf("pbft failed: %v", err) - } - - // The leader can be a new leader coming from a view change, so it resets - // the value as a round has finished. - s.failedRound = false - - return nil -} - -func (s *Service) doFollowerRound(ctx context.Context, roster authority.Authority) error { - // A follower has to wait for the new block, or the round timeout, to proceed. - select { - case <-time.After(s.timeoutRound): - if !s.roundHasFailed() { - return nil - } - - s.logger.Info().Msg("round has failed, do a view change !") - - s.pool.ResetStats() // avoid infinite view change - - view, err := s.pbftsm.Expire(s.me) // start the viewChange - if err != nil { - return xerrors.Errorf("pbft expire failed: %v", err) - } - - viewMsg := types.NewViewMessage(view.GetID(), view.GetLeader(), view.GetSignature()) - - ctx, cancel := context.WithTimeout(ctx, s.timeoutRound) - defer cancel() - - resps, err := s.rpc.Call(ctx, viewMsg, roster) - if err != nil { - return xerrors.Errorf("rpc failed to send views: %v", err) - } - - for resp := range resps { - _, err = resp.GetMessageOrError() - if err != nil { - s.logger.Warn().Err(err).Str("to", resp.GetFrom().String()).Msg("view propagation failure") - } - } - - statesCh := s.pbftsm.Watch(ctx) - - state := s.pbftsm.GetState() - var more bool - - for state == pbft.ViewChangeState { - state, more = <-statesCh - if !more { - return xerrors.New("view change failed") - } - } - - s.logger.Info().Msgf("view change for %d", viewMsg.GetLeader()) - - return nil - - case <-s.events: - // As a child, a block has been committed thus the previous view - // change succeeded. - s.failedRound = false - - // A block has been created meaning that the round is over. - return nil - - case <-s.closing: - return nil - } -} - -func (s *Service) roundHasFailed() bool { - stats := s.pool.Stats() - - if stats.TxCount == 0 { - // When the pool of transactions is empty, the round is aborted - // and everything restart. - return false - } - - if time.Since(stats.OldestTx) > s.transactionTimeout { - s.logger.Warn().Msg("found a rotten transaction") - s.failedRound = true - } - - return s.failedRound -} - -func (s *Service) doPBFT(ctx context.Context) error { - var id types.Digest - var block types.Block - - if s.pbftsm.GetState() >= pbft.CommitState { - // The node is already committed to a block, which means enough nodes - // have accepted, but somehow the finalization failed. - id, block = s.pbftsm.GetCommit() - } else { - txs := s.pool.Gather(ctx, pool.Config{Min: 1}) - if len(txs) == 0 { - s.logger.Debug().Msg("no transaction in pool") - - return nil - } - - s.logger.Debug(). - Int("num", len(txs)). - Msg("transactions have been found") - - if ctx.Err() != nil { - // Don't bother trying PBFT if the context is done. - return ctx.Err() - } - - data, root, err := s.prepareData(txs) - if err != nil { - return xerrors.Errorf("failed to prepare data: %v", err) - } - - block, err = types.NewBlock( - data, - types.WithTreeRoot(root), - types.WithIndex(uint64(s.blocks.Len())), - types.WithHashFactory(s.hashFactory)) - - if err != nil { - return xerrors.Errorf("creating block failed: %v", err) - } - - id, err = s.pbftsm.Prepare(s.me, block) - if err != nil { - return xerrors.Errorf("pbft prepare failed: %v", err) - } - } - - roster, err := s.getCurrentRoster() - if err != nil { - return xerrors.Errorf("read roster failed: %v", err) - } - - // 1. Prepare phase - req := types.NewBlockMessage(block, s.prepareViews()) - - sig, err := s.actor.Sign(ctx, req, roster) - if err != nil { - return xerrors.Errorf("prepare signature failed: %v", err) - } - - s.logger.Debug().Str("signature", fmt.Sprintf("%v", sig)).Msg("prepare done") - - // 2. Commit phase - commit := types.NewCommit(id, sig) - - sig, err = s.actor.Sign(ctx, commit, roster) - if err != nil { - return xerrors.Errorf("commit signature failed: %v", err) - } - - s.logger.Debug().Str("signature", fmt.Sprintf("%v", sig)).Msg("commit done") - - // 3. Propagation phase - done := types.NewDone(id, sig) - - resps, err := s.rpc.Call(ctx, done, roster) - if err != nil { - return xerrors.Errorf("propagation failed: %v", err) - } - - for resp := range resps { - _, err = resp.GetMessageOrError() - if err != nil { - s.logger.Warn().Err(err).Msg("propagation failed") - } - } - - // 4. Wake up new participants so that they can learn about the chain. - err = s.wakeUp(ctx, roster) - if err != nil { - return xerrors.Errorf("wake up failed: %v", err) - } - - return nil -} - -func (s *Service) prepareViews() map[mino.Address]types.ViewMessage { - views := s.pbftsm.GetViews() - msgs := make(map[mino.Address]types.ViewMessage) - - for addr, view := range views { - msgs[addr] = types.NewViewMessage(view.GetID(), view.GetLeader(), view.GetSignature()) - } - - return msgs -} - -func (s *Service) prepareData(txs []txn.Transaction) (data validation.Result, id types.Digest, err error) { - var stageTree hashtree.StagingTree - - stageTree, err = s.tree.Get().Stage(func(snap store.Snapshot) error { - data, err = s.val.Validate(snap, txs) - if err != nil { - return xerrors.Errorf("validation failed: %v", err) - } - - return nil - }) - - if err != nil { - err = xerrors.Errorf("staging tree failed: %v", err) - return - } - - copy(id[:], stageTree.GetRoot()) - - return -} - -func (s *Service) wakeUp(ctx context.Context, ro authority.Authority) error { - newRoster, err := s.getCurrentRoster() - if err != nil { - return xerrors.Errorf("read roster failed: %v", err) - } - - changeset := ro.Diff(newRoster) - - genesis, err := s.genesis.Get() - if err != nil { - return xerrors.Errorf("read genesis failed: %v", err) - } - - resps, err := s.rpc.Call(ctx, types.NewGenesisMessage(genesis), mino.NewAddresses(changeset.GetNewAddresses()...)) - if err != nil { - return xerrors.Errorf("rpc failed: %v", err) - } - - for resp := range resps { - _, err := resp.GetMessageOrError() - if err != nil { - s.logger.Warn().Err(err).Msg("wake up failed") - } - } - - return nil -} - -type observer struct { - ch chan ordering.Event -} - -func (obs observer) NotifyCallback(event interface{}) { - obs.ch <- event.(ordering.Event) -} - -func calculateBackoff(backoff float64) time.Duration { - return time.Duration(math.Pow(2, backoff)) * RoundWait -} - -// PoolFilter is a filter to drop transactions which are already included in the -// block or simply with an invalid nonce. -// -// - implements pool.Filter -type poolFilter struct { - tree blockstore.TreeCache - srvc validation.Service -} - -// Accept implements pool.Filter. It returns an error if the transaction exists -// already or the nonce is invalid. -func (f poolFilter) Accept(tx txn.Transaction, leeway validation.Leeway) error { - s := f.tree.Get() - - err := f.srvc.Accept(s, tx, leeway) - if err != nil { - return xerrors.Errorf("unacceptable transaction: %v", err) - } - - return nil -} diff --git a/dela/core/ordering/cosipbft/cosipbft_test.go b/dela/core/ordering/cosipbft/cosipbft_test.go deleted file mode 100644 index e504565..0000000 --- a/dela/core/ordering/cosipbft/cosipbft_test.go +++ /dev/null @@ -1,1157 +0,0 @@ -package cosipbft - -import ( - "context" - "fmt" - "os" - "path/filepath" - "runtime/debug" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/access/darc" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/contracts/viewchange" - "go.dedis.ch/dela/core/ordering/cosipbft/pbft" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree/binprefix" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - poolgossip "go.dedis.ch/dela/core/txn/pool/gossip" - "go.dedis.ch/dela/core/txn/pool/mem" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/cosi/flatcosi" - "go.dedis.ch/dela/cosi/threshold" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/gossip" - "go.dedis.ch/dela/mino/minoch" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" -) - -func TestService_Scenario_Basic(t *testing.T) { - nodes, ro, clean := makeAuthority(t, 5) - defer clean() - - signer := nodes[0].signer - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - initial := ro.Take(mino.RangeFilter(0, 4)).(crypto.CollectiveAuthority) - - err := nodes[0].service.Setup(ctx, initial) - require.NoError(t, err) - - events := nodes[2].service.Watch(ctx) - - err = nodes[0].pool.Add(makeTx(t, 0, signer)) - require.NoError(t, err) - - evt := waitEvent(t, events, 2*DefaultRoundTimeout) - require.Equal(t, uint64(0), evt.Index) - - err = nodes[1].pool.Add(makeTx(t, 1, signer)) - require.NoError(t, err) - - evt = waitEvent(t, events, 20*DefaultRoundTimeout) - require.Equal(t, uint64(1), evt.Index) - - err = nodes[1].pool.Add(makeRosterTx(t, 2, ro, signer)) - require.NoError(t, err) - - evt = waitEvent(t, events, 20*DefaultRoundTimeout) - require.Equal(t, uint64(2), evt.Index) - for i := 0; i < 3; i++ { - err = nodes[1].pool.Add(makeTx(t, uint64(i+3), signer)) - require.NoError(t, err) - - evt = waitEvent(t, events, 20*DefaultRoundTimeout) - require.Equal(t, uint64(i+3), evt.Index) - } - - proof, err := nodes[0].service.GetProof(keyRoster[:]) - require.NoError(t, err) - require.NotNil(t, proof.GetValue()) - - require.Equal(t, keyRoster[:], proof.GetKey()) - require.NotNil(t, proof.GetValue()) - - checkProof(t, proof.(Proof), nodes[0].service) -} - -func TestService_Scenario_ViewChange(t *testing.T) { - nodes, ro, clean := makeAuthority(t, 4) - defer clean() - - // Simulate an issue with the leader transaction pool so that it does not - // receive any of them. - nodes[0].pool.Close() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := nodes[1].service.Setup(ctx, ro) - require.NoError(t, err) - - events := nodes[2].service.Watch(ctx) - - // Other nodes will detect a transaction but no block incoming => timeout - err = nodes[1].pool.Add(makeTx(t, 0, nodes[1].signer)) - require.NoError(t, err) - - evt := waitEvent(t, events, DefaultTransactionTimeout+2*time.Second) - require.Equal(t, uint64(0), evt.Index) -} - -func TestService_Scenario_ViewChangeRequest(t *testing.T) { - nodes, ro, clean := makeAuthority(t, 4) - defer clean() - nodes[3].service.pool = fakePool{ - Pool: nodes[3].service.pool, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := nodes[1].service.Setup(ctx, ro) - require.NoError(t, err) - - leader, err := nodes[0].service.pbftsm.GetLeader() - require.NoError(t, err) - require.Equal(t, leader, nodes[0].onet.GetAddress()) - - // let enough time for a round to run - time.Sleep(DefaultRoundTimeout + 100*time.Millisecond) - - require.Equal(t, nodes[3].service.pbftsm.GetState(), pbft.ViewChangeState) - require.NotEqual(t, nodes[2].service.pbftsm.GetState(), pbft.ViewChangeState) - require.NotEqual(t, nodes[1].service.pbftsm.GetState(), pbft.ViewChangeState) - require.NotEqual(t, nodes[0].service.pbftsm.GetState(), pbft.ViewChangeState) - - leader, err = nodes[0].service.pbftsm.GetLeader() - require.NoError(t, err) - // assert that node 0 is still the leader - require.Equal(t, leader, nodes[0].onet.GetAddress()) -} - -func TestService_Scenario_NoViewChangeRequest(t *testing.T) { - nodes, ro, clean := makeAuthority(t, 4) - defer clean() - - signer := nodes[0].signer - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - initial := ro.Take(mino.RangeFilter(0, 4)).(crypto.CollectiveAuthority) - - err := nodes[0].service.Setup(ctx, initial) - require.NoError(t, err) - - leader, err := nodes[0].service.pbftsm.GetLeader() - require.NoError(t, err) - require.Equal(t, leader, nodes[0].onet.GetAddress()) - - err = nodes[0].pool.Add(makeTx(t, 0, signer)) - require.NoError(t, err) - - // let enough time for a round to run - time.Sleep(DefaultRoundTimeout + 100*time.Millisecond) - - require.NotEqual(t, nodes[3].service.pbftsm.GetState(), pbft.ViewChangeState) - require.NotEqual(t, nodes[2].service.pbftsm.GetState(), pbft.ViewChangeState) - require.NotEqual(t, nodes[1].service.pbftsm.GetState(), pbft.ViewChangeState) - require.NotEqual(t, nodes[0].service.pbftsm.GetState(), pbft.ViewChangeState) - - leader, err = nodes[0].service.pbftsm.GetLeader() - require.NoError(t, err) - // assert that node 0 is still the leader - require.Equal(t, leader, nodes[0].onet.GetAddress()) -} - -// Test that a block committed will be eventually finalized even if the -// propagation failed. -// -// Expected log warnings and errors: -// - timeout from the followers -// - block not from the leader -// - round failed on node 0 -// - mismatch state viewchange != (initial|prepare) -func TestService_Scenario_FinalizeFailure(t *testing.T) { - nodes, ro, clean := makeAuthority(t, 4) - defer clean() - - filter := func(req mino.Request) bool { - switch req.Message.(type) { - case types.DoneMessage: - // Ignore propagation from node 0 which produces a committed block - // without finalization. Node 1 will take over and finalize it. - return !req.Address.Equal(nodes[0].service.me) - default: - return true - } - } - - for i := 0; i < 4; i++ { - nodes[i].onet.AddFilter(filter) - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := nodes[0].service.Setup(ctx, ro) - require.NoError(t, err) - - events := nodes[1].service.Watch(ctx) - - err = nodes[0].pool.Add(makeTx(t, 0, nodes[0].signer)) - require.NoError(t, err) - - evt := waitEvent(t, events, DefaultTransactionTimeout+2*time.Second) - require.Equal(t, uint64(0), evt.Index) -} - -func TestService_New(t *testing.T) { - param := ServiceParam{ - Mino: fake.Mino{}, - Cosi: flatcosi.NewFlat(fake.Mino{}, fake.NewAggregateSigner()), - Tree: fakeTree{}, - Validation: simple.NewService(nil, nil), - Pool: badPool{}, - } - - genesis := blockstore.NewGenesisStore() - genesis.Set(types.Genesis{}) - - opts := []ServiceOption{ - WithHashFactory(fake.NewHashFactory(&fake.Hash{})), - WithGenesisStore(genesis), - WithBlockStore(blockstore.NewInMemory()), - } - - srvc, err := NewService(param, opts...) - require.NoError(t, err) - require.NotNil(t, srvc) - - <-srvc.closed - - param.Cosi = badCosi{} - _, err = NewService(param) - require.EqualError(t, err, fake.Err("creating cosi failed")) -} - -func TestService_Setup(t *testing.T) { - rpc := fake.NewRPC() - - srvc := &Service{processor: newProcessor()} - srvc.rpc = rpc - srvc.hashFactory = crypto.NewSha256Factory() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.genesis = blockstore.NewGenesisStore() - srvc.access = fakeAccess{} - - rpc.Done() - - a := fake.NewAuthority(3, fake.NewSigner) - ctx := context.Background() - - err := srvc.Setup(ctx, a) - require.NoError(t, err) - - _, more := <-srvc.started - require.False(t, more) - - genesis, err := srvc.genesis.Get() - require.NoError(t, err) - require.Equal(t, 3, genesis.GetRoster().Len()) -} - -func TestService_AlreadySet_Setup(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - } - - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.access = fakeAccess{} - srvc.genesis = blockstore.NewGenesisStore() - srvc.genesis.Set(types.Genesis{}) - - a := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.Setup(ctx, a) - require.EqualError(t, err, - "creating genesis: set genesis failed: genesis block is already set") -} - -func TestService_FailReadGenesis_Setup(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - } - - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.access = fakeAccess{} - srvc.genesis = fakeGenesisStore{errGet: fake.GetError()} - - a := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.Setup(ctx, a) - require.EqualError(t, err, fake.Err("failed to read genesis")) -} - -func TestService_FailPropagate_Setup(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - } - - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.access = fakeAccess{} - srvc.genesis = blockstore.NewGenesisStore() - srvc.rpc = fake.NewBadRPC() - - a := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.Setup(ctx, a) - require.EqualError(t, err, fake.Err("sending genesis")) -} - -func TestService_RequestFailure_Setup(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - } - - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.access = fakeAccess{} - srvc.genesis = blockstore.NewGenesisStore() - - rpc := fake.NewRPC() - rpc.SendResponseWithError(fake.NewAddress(1), fake.GetError()) - srvc.rpc = rpc - - a := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.Setup(ctx, a) - require.EqualError(t, err, fake.Err("one request failed")) -} - -func TestService_Main(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.closing = make(chan struct{}) - srvc.closed = make(chan struct{}) - - close(srvc.closing) - - err := srvc.main() - require.NoError(t, err) - - srvc.tree = blockstore.NewTreeCache(fakeTree{err: fake.GetError()}) - srvc.closing = make(chan struct{}) - srvc.started = make(chan struct{}) - srvc.closed = make(chan struct{}) - close(srvc.started) - err = srvc.main() - require.EqualError(t, err, fake.Err("refreshing roster: reading roster: read from tree")) - - srvc.tree.Set(fakeTree{}) - srvc.pool = badPool{} - srvc.closed = make(chan struct{}) - err = srvc.main() - require.EqualError(t, err, fake.Err("refreshing roster: updating tx pool")) - - logger, wait := fake.WaitLog("round failed", 2*time.Second) - go func() { - wait(t) - close(srvc.closing) - }() - - srvc.logger = logger - srvc.pool = mem.NewPool() - srvc.pbftsm = fakeSM{errLeader: fake.GetError()} - srvc.closed = make(chan struct{}) - err = srvc.main() - require.NoError(t, err) -} - -func TestService_DoRound(t *testing.T) { - rpc := fake.NewRPC() - ch := make(chan pbft.State) - - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(1), - rpc: rpc, - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - closing: make(chan struct{}), - } - srvc.blocks = blockstore.NewInMemory() - srvc.sync = fakeSync{} - srvc.pool = mem.NewPool() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = fakeSM{ - state: pbft.ViewChangeState, - ch: ch, - } - - rpc.SendResponse(fake.NewAddress(3), nil) - rpc.SendResponseWithError(fake.NewAddress(2), fake.GetError()) - rpc.Done() - - ctx := context.Background() - - // Round with timeout but no transaction in the pool. - err := srvc.doRound(ctx) - require.NoError(t, err) - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - go func() { - ch <- pbft.InitialState - close(ch) - }() - - // Round with timeout and a transaction in the pool. - err = srvc.doRound(ctx) - require.NoError(t, err) -} - -func TestService_ViewchangeFailed_DoRound(t *testing.T) { - pbftsm := fakeSM{ - state: pbft.ViewChangeState, - ch: make(chan pbft.State), - } - // Stuck to view change state thus causing the view to fail. - close(pbftsm.ch) - - rpc := fake.NewRPC() - rpc.Done() - - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(1), - rpc: rpc, - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - } - - srvc.blocks = blockstore.NewInMemory() - srvc.pool = mem.NewPool() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = pbftsm - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, "view change failed") -} - -func TestService_FailPBFTExpire_DoRound(t *testing.T) { - rpc := fake.NewRPC() - rpc.Done() - - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(1), - rpc: rpc, - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - } - - srvc.blocks = blockstore.NewInMemory() - srvc.pool = mem.NewPool() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = fakeSM{ - err: fake.GetError(), - state: pbft.InitialState, - } - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, fake.Err("pbft expire failed")) -} - -func TestService_FailSendViews_DoRound(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(1), - rpc: fake.NewBadRPC(), - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - } - - srvc.blocks = blockstore.NewInMemory() - srvc.pool = mem.NewPool() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = fakeSM{} - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, fake.Err("rpc failed to send views")) -} - -func TestService_FailReadRoster_DoRound(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(1), - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - } - - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = badRosterFac{} - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, fake.Err("reading roster: decode failed")) -} - -func TestService_FailReadLeader_DoRound(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(1), - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - } - - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = fakeSM{errLeader: fake.GetError()} - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, fake.Err("reading leader")) -} - -func TestService_FailSync_DoRound(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(0), - timeoutRound: time.Millisecond, - timeoutRoundAfterFailure: time.Millisecond, - } - - srvc.blocks = blockstore.NewInMemory() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = fakeSM{} - srvc.sync = fakeSync{err: fake.GetError()} - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, fake.Err("sync failed")) -} - -func TestService_FailPBFT_DoRound(t *testing.T) { - srvc := &Service{ - processor: newProcessor(), - me: fake.NewAddress(0), - timeoutRound: DefaultRoundTimeout, - timeoutRoundAfterFailure: DefaultFailedRoundTimeout, - val: fakeValidation{err: fake.GetError()}, - } - - srvc.blocks = blockstore.NewInMemory() - srvc.pool = mem.NewPool() - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.pbftsm = fakeSM{} - srvc.sync = fakeSync{} - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doRound(ctx) - require.EqualError(t, err, - fake.Err("pbft failed: failed to prepare data: staging tree failed: validation failed")) -} - -func TestService_DoPBFT(t *testing.T) { - rpc := fake.NewRPC() - - srvc := &Service{processor: newProcessor()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.val = fakeValidation{} - srvc.blocks = blockstore.NewInMemory() - srvc.genesis = blockstore.NewGenesisStore() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.pbftsm = fakeSM{} - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.actor = fakeCosiActor{} - srvc.pool = mem.NewPool() - srvc.rpc = rpc - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - rpc.SendResponseWithError(fake.NewAddress(5), fake.GetError()) - rpc.Done() - srvc.genesis.Set(types.Genesis{}) - - // Context timed out and no transaction are in the pool. - err := srvc.doPBFT(ctx) - require.NoError(t, err) - - // This time the gathering succeeds. - ctx = context.Background() - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - err = srvc.doPBFT(ctx) - require.NoError(t, err) -} - -func TestService_ContextCanceld_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{err: fake.GetError()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, "context canceled") -} - -func TestService_FailValidation_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{err: fake.GetError()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - srvc.val = fakeValidation{err: fake.GetError()} - err := srvc.doPBFT(ctx) - require.EqualError(t, err, - fake.Err("failed to prepare data: staging tree failed: validation failed")) -} - -func TestService_FailCreateBlock_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - srvc.hashFactory = fake.NewHashFactory(fake.NewBadHash()) - srvc.blocks = blockstore.NewInMemory() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, - fake.Err("creating block failed: fingerprint failed: couldn't write index")) -} - -func TestService_FailPrepare_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{err: fake.GetError()} - srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.blocks = blockstore.NewInMemory() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, fake.Err("pbft prepare failed")) -} - -func TestService_FailReadRoster_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{err: fake.GetError()}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.blocks = blockstore.NewInMemory() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, fake.Err("read roster failed: read from tree")) -} - -func TestService_FailPrepareSig_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.blocks = blockstore.NewInMemory() - srvc.actor = fakeCosiActor{err: fake.GetError()} - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, fake.Err("prepare signature failed")) -} - -func TestService_FailCommitSign_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.blocks = blockstore.NewInMemory() - srvc.actor = fakeCosiActor{ - err: fake.GetError(), - counter: fake.NewCounter(1), - } - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, fake.Err("commit signature failed")) -} - -func TestService_FailPropagation_DoPBFT(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.blocks = blockstore.NewInMemory() - srvc.actor = fakeCosiActor{} - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.rpc = fake.NewBadRPC() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, fake.Err("propagation failed")) -} - -func TestService_FailWakeUp_DoPBFT(t *testing.T) { - rpc := fake.NewRPC() - rpc.Done() - - srvc := &Service{processor: newProcessor()} - srvc.val = fakeValidation{} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.pbftsm = fakeSM{} - srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() - srvc.blocks = blockstore.NewInMemory() - srvc.actor = fakeCosiActor{} - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.rpc = rpc - srvc.genesis = blockstore.NewGenesisStore() - - srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err := srvc.doPBFT(ctx) - require.EqualError(t, err, "wake up failed: read genesis failed: missing genesis block") -} - -func TestService_WakeUp(t *testing.T) { - rpc := fake.NewRPC() - - srvc := &Service{processor: newProcessor()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.genesis = blockstore.NewGenesisStore() - srvc.genesis.Set(types.Genesis{}) - srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - srvc.rpc = rpc - - ctx := context.Background() - - rpc.SendResponseWithError(fake.NewAddress(5), fake.GetError()) - rpc.Done() - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - err := srvc.wakeUp(ctx, ro) - require.NoError(t, err) - - srvc.tree.Set(fakeTree{err: fake.GetError()}) - err = srvc.wakeUp(ctx, ro) - require.EqualError(t, err, fake.Err("read roster failed: read from tree")) - - srvc.tree.Set(fakeTree{}) - srvc.rpc = fake.NewBadRPC() - err = srvc.wakeUp(ctx, ro) - require.EqualError(t, err, fake.Err("rpc failed")) -} - -func TestService_GetProof(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.blocks = blockstore.NewInMemory() - srvc.blocks.Store(makeBlock(t, types.Digest{})) - - proof, err := srvc.GetProof([]byte("A")) - require.NoError(t, err) - require.NotNil(t, proof) - - srvc.tree.Set(fakeTree{err: fake.GetError()}) - _, err = srvc.GetProof([]byte("A")) - require.EqualError(t, err, fake.Err("reading path")) - - srvc.tree.Set(fakeTree{}) - srvc.blocks = blockstore.NewInMemory() - _, err = srvc.GetProof([]byte("A")) - require.EqualError(t, err, "reading chain: store is empty") -} - -func TestService_GetStore(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - - require.IsType(t, fakeTree{}, srvc.GetStore()) -} - -func TestService_GetRoster(t *testing.T) { - srvc := &Service{processor: newProcessor()} - srvc.tree = blockstore.NewTreeCache(fakeTree{}) - srvc.rosterFac = fakeRosterFac{} - - roster, err := srvc.GetRoster() - require.NoError(t, err) - require.Equal(t, 3, roster.Len()) -} - -func TestService_PoolFilter(t *testing.T) { - filter := poolFilter{ - tree: blockstore.NewTreeCache(fakeTree{}), - srvc: fakeValidation{}, - } - - err := filter.Accept(makeTx(t, 0, fake.NewSigner()), validation.Leeway{}) - require.NoError(t, err) - - filter.srvc = fakeValidation{err: fake.GetError()} - err = filter.Accept(makeTx(t, 0, fake.NewSigner()), validation.Leeway{}) - require.EqualError(t, err, fake.Err("unacceptable transaction")) -} - -// ----------------------------------------------------------------------------- -// Utility functions -func checkProof(t *testing.T, p Proof, s *Service) { - genesis, err := s.genesis.Get() - require.NoError(t, err) - - err = p.Verify(genesis, s.verifierFac) - require.NoError(t, err) -} - -type testNode struct { - onet *minoch.Minoch - service *Service - pool *poolgossip.Pool - db kv.DB - dbpath string - signer crypto.Signer -} - -const testContractName = "abc" - -type testExec struct { - err error -} - -func (e testExec) Execute(store.Snapshot, execution.Step) error { - return e.err -} - -func makeTx(t *testing.T, nonce uint64, signer crypto.Signer) txn.Transaction { - opts := []signed.TransactionOption{ - signed.WithArg(native.ContractArg, []byte(testContractName)), - } - - tx, err := signed.NewTransaction(nonce, signer.GetPublicKey(), opts...) - require.NoError(t, err) - - require.NoError(t, tx.Sign(signer)) - - return tx -} - -func makeRosterTx(t *testing.T, nonce uint64, roster authority.Authority, signer crypto.Signer) txn.Transaction { - data, err := roster.Serialize(json.NewContext()) - require.NoError(t, err) - - tx, err := signed.NewTransaction( - nonce, - signer.GetPublicKey(), - signed.WithArg(native.ContractArg, []byte(viewchange.ContractName)), - signed.WithArg(viewchange.AuthorityArg, data), - ) - require.NoError(t, err) - - require.NoError(t, tx.Sign(signer)) - - return tx -} - -func waitEvent(t *testing.T, events <-chan ordering.Event, timeout time.Duration) ordering.Event { - select { - case <-time.After(timeout): - t.Log(string(debug.Stack())) - t.Fatal("no event received before the timeout") - return ordering.Event{} - case evt := <-events: - return evt - } -} - -func makeAuthority(t *testing.T, n int) ([]testNode, authority.Authority, func()) { - manager := minoch.NewManager() - - addrs := make([]mino.Address, n) - pubkeys := make([]crypto.PublicKey, n) - nodes := make([]testNode, n) - - for i := 0; i < n; i++ { - m := minoch.MustCreate(manager, fmt.Sprintf("node%d", i)) - - addrs[i] = m.GetAddress() - - signer := bls.NewSigner() - pubkeys[i] = signer.GetPublicKey() - - c := threshold.NewThreshold(m, signer) - c.SetThreshold(threshold.ByzantineThreshold) - - dir, err := os.MkdirTemp(os.TempDir(), "cosipbft") - require.NoError(t, err) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - txFac := signed.NewTransactionFactory() - - p, err := poolgossip.NewPool(gossip.NewFlat(m, txFac)) - require.NoError(t, err) - - tree := binprefix.NewMerkleTree(db, binprefix.Nonce{}) - - exec := native.NewExecution() - exec.Set(testContractName, testExec{}) - - accessSrvc := darc.NewService(json.NewContext()) - - rosterFac := authority.NewFactory(m.GetAddressFactory(), c.GetPublicKeyFactory()) - RegisterRosterContract(exec, rosterFac, accessSrvc) - - vs := simple.NewService(exec, txFac) - - param := ServiceParam{ - Mino: m, - Cosi: c, - Validation: vs, - Access: accessSrvc, - Pool: p, - Tree: tree, - DB: db, - } - - srv, err := NewService(param) - require.NoError(t, err) - - nodes[i] = testNode{ - onet: m, - service: srv, - pool: p, - db: db, - dbpath: dir, - signer: c.GetSigner(), - } - } - - ro := authority.New(addrs, pubkeys) - - clean := func() { - for _, node := range nodes { - require.NoError(t, node.service.Close()) - require.NoError(t, node.db.Close()) - require.NoError(t, os.RemoveAll(node.dbpath)) - } - } - - return nodes, ro, clean -} - -type badRosterFac struct { - authority.Factory -} - -func (fac badRosterFac) AuthorityOf(serde.Context, []byte) (authority.Authority, error) { - return nil, fake.GetError() -} - -type fakePool struct { - pool.Pool -} - -func (f fakePool) Stats() pool.Stats { - return pool.Stats{ - OldestTx: time.Now().Add(-100 * time.Hour), - TxCount: 1, - } -} - -type badPool struct { - pool.Pool -} - -func (p badPool) SetPlayers(mino.Players) error { - return fake.GetError() -} - -func (p badPool) AddFilter(pool.Filter) {} - -type badCosi struct { - cosi.CollectiveSigning -} - -func (c badCosi) GetSigner() crypto.Signer { - return fake.NewBadSigner() -} - -func (c badCosi) GetPublicKeyFactory() crypto.PublicKeyFactory { - return fake.NewPublicKeyFactory(fake.PublicKey{}) -} - -func (c badCosi) GetSignatureFactory() crypto.SignatureFactory { - return fake.NewSignatureFactory(fake.Signature{}) -} - -func (c badCosi) GetVerifierFactory() crypto.VerifierFactory { - return fake.NewVerifierFactory(fake.Verifier{}) -} - -func (c badCosi) Listen(cosi.Reactor) (cosi.Actor, error) { - return nil, fake.GetError() -} - -type fakeValidation struct { - validation.Service - - err error -} - -func (val fakeValidation) Accept(store.Readable, txn.Transaction, validation.Leeway) error { - return val.err -} - -func (val fakeValidation) Validate(store.Snapshot, []txn.Transaction) (validation.Result, error) { - return simple.NewResult(nil), val.err -} - -type fakeCosiActor struct { - cosi.Actor - - counter *fake.Counter - err error -} - -func (c fakeCosiActor) Sign(ctx context.Context, msg serde.Message, - ca crypto.CollectiveAuthority) (crypto.Signature, error) { - - if c.counter.Done() { - return fake.Signature{}, c.err - } - - c.counter.Decrease() - return fake.Signature{}, nil -} - -type fakeRosterFac struct { - authority.Factory -} - -func (fakeRosterFac) AuthorityOf(serde.Context, []byte) (authority.Authority, error) { - return authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)), nil -} - -type fakeAccess struct { - access.Service - - err error -} - -func (srvc fakeAccess) Grant(store.Snapshot, access.Credential, ...access.Identity) error { - return srvc.err -} diff --git a/dela/core/ordering/cosipbft/json/chain.go b/dela/core/ordering/cosipbft/json/chain.go deleted file mode 100644 index 515d19a..0000000 --- a/dela/core/ordering/cosipbft/json/chain.go +++ /dev/null @@ -1,84 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// ChainFormat is the JSON format to encode and decode chains. -// -// - implements serde.FormatEngine -type chainFormat struct{} - -// Encode implements serde.FormatEngine. It serializes the chain if appropriate, -// otherwise it returns an error. -func (fmt chainFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - chain, ok := msg.(types.Chain) - if !ok { - return nil, xerrors.Errorf("unsupported message '%T'", msg) - } - - links := chain.GetLinks() - raws := make([]json.RawMessage, len(links)) - - for i, link := range links { - raw, err := link.Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize link: %v", err) - } - - raws[i] = raw - } - - m := ChainJSON{ - Links: raws, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It deserializes the chain if -// appropriate, otherwise it returns an error. -func (fmt chainFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := ChainJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - if len(m.Links) == 0 { - return nil, xerrors.New("chain cannot be empty") - } - - fac := ctx.GetFactory(types.LinkKey{}) - - factory, ok := fac.(types.LinkFactory) - if !ok { - return nil, xerrors.Errorf("invalid link factory '%T'", fac) - } - - prevs := make([]types.Link, len(m.Links)-1) - for i, raw := range m.Links[:len(m.Links)-1] { - link, err := factory.LinkOf(ctx, raw) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize link: %v", err) - } - - prevs[i] = link - } - - last, err := factory.BlockLinkOf(ctx, m.Links[len(m.Links)-1]) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize block link: %v", err) - } - - return types.NewChain(last, prevs), nil -} diff --git a/dela/core/ordering/cosipbft/json/chain_test.go b/dela/core/ordering/cosipbft/json/chain_test.go deleted file mode 100644 index 8784768..0000000 --- a/dela/core/ordering/cosipbft/json/chain_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestChainFormat_Encode(t *testing.T) { - format := chainFormat{} - - ctx := fake.NewContext() - - data, err := format.Encode(ctx, types.NewChain(fakeLink{}, nil)) - require.NoError(t, err) - require.Equal(t, `{"Links":[{}]}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message 'fake.Message'") - - _, err = format.Encode(ctx, types.NewChain(fakeLink{err: fake.GetError()}, nil)) - require.EqualError(t, err, fake.Err("couldn't serialize link")) - - _, err = format.Encode(fake.NewBadContext(), types.NewChain(fakeLink{}, nil)) - require.EqualError(t, err, fake.Err("failed to marshal")) -} - -func TestChainFormat_Decode(t *testing.T) { - format := chainFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.LinkKey{}, fakeLinkFac{}) - - chain, err := format.Decode(ctx, []byte(`{"Links":[{}, {}, {}]}`)) - require.NoError(t, err) - require.Equal(t, types.NewChain(fakeLink{}, []types.Link{fakeLink{}, fakeLink{}}), chain) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, "chain cannot be empty") - - badCtx := serde.WithFactory(ctx, types.LinkKey{}, fake.MessageFactory{}) - _, err = format.Decode(badCtx, []byte(`{"Links":[{}]}`)) - require.EqualError(t, err, "invalid link factory 'fake.MessageFactory'") - - badCtx = serde.WithFactory(ctx, types.LinkKey{}, fakeLinkFac{errLink: fake.GetError()}) - _, err = format.Decode(badCtx, []byte(`{"Links":[{}, {}]}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize link")) - - badCtx = serde.WithFactory(ctx, types.LinkKey{}, fakeLinkFac{errBlockLink: fake.GetError()}) - _, err = format.Decode(badCtx, []byte(`{"Links": [{}]}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize block link")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeLink struct { - types.BlockLink - - err error -} - -func (link fakeLink) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), link.err -} - -type fakeLinkFac struct { - types.LinkFactory - - errLink error - errBlockLink error -} - -func (link fakeLinkFac) LinkOf(serde.Context, []byte) (types.Link, error) { - return fakeLink{}, link.errLink -} - -func (link fakeLinkFac) BlockLinkOf(serde.Context, []byte) (types.BlockLink, error) { - return fakeLink{}, link.errBlockLink -} diff --git a/dela/core/ordering/cosipbft/json/json.go b/dela/core/ordering/cosipbft/json/json.go deleted file mode 100644 index 4d5ecd4..0000000 --- a/dela/core/ordering/cosipbft/json/json.go +++ /dev/null @@ -1,467 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - types.RegisterGenesisFormat(serde.FormatJSON, genesisFormat{}) - types.RegisterMessageFormat(serde.FormatJSON, msgFormat{}) - types.RegisterBlockFormat(serde.FormatJSON, blockFormat{}) - types.RegisterLinkFormat(serde.FormatJSON, linkFormat{}) - types.RegisterChainFormat(serde.FormatJSON, chainFormat{}) -} - -// GenesisJSON is the JSON message for a genesis block. -type GenesisJSON struct { - Roster json.RawMessage - TreeRoot []byte -} - -// BlockJSON is the JSON message for a block. -type BlockJSON struct { - Index uint64 - TreeRoot []byte - Data json.RawMessage -} - -// LinkJSON is the JSON message for a link. -type LinkJSON struct { - From []byte - To []byte `json:",omitempty"` - PrepareSignature json.RawMessage - CommitSignature json.RawMessage - ChangeSet json.RawMessage - Block json.RawMessage `json:",omitempty"` -} - -// ChainJSON is the JSON message for a chain. -type ChainJSON struct { - Links []json.RawMessage -} - -// GenesisMessageJSON is the JSON message to send a genesis block. -type GenesisMessageJSON struct { - Genesis json.RawMessage -} - -// BlockMessageJSON is the JSON message to send a block. -type BlockMessageJSON struct { - Block json.RawMessage - Views map[string]ViewMessageJSON -} - -// CommitMessageJSON is the JSON message to send a commit request. -type CommitMessageJSON struct { - ID []byte - Signature json.RawMessage -} - -// DoneMessageJSON is the JSON message to send a block confirmation. -type DoneMessageJSON struct { - ID []byte - Signature json.RawMessage -} - -// ViewMessageJSON is the JSON message to send a view change request. -type ViewMessageJSON struct { - Leader uint16 - ID []byte - Signature json.RawMessage -} - -// MessageJSON is the JSON message that wraps the different kinds of messages. -type MessageJSON struct { - Genesis *GenesisMessageJSON `json:",omitempty"` - Block *BlockMessageJSON `json:",omitempty"` - Commit *CommitMessageJSON `json:",omitempty"` - Done *DoneMessageJSON `json:",omitempty"` - View *ViewMessageJSON `json:",omitempty"` -} - -// GenesisFormat is a format engine to serialize and deserialize the genesis -// blocks. -// -// - implements serde.FormatEngine -type genesisFormat struct { - hashFac crypto.HashFactory -} - -// Encode implements serde.FormatEngine. It returns the serialized data of the -// genesis if appropriate, otherwise it returns an error. -func (f genesisFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - genesis, ok := msg.(types.Genesis) - if !ok { - return nil, xerrors.Errorf("invalid genesis '%T'", msg) - } - - roster, err := genesis.GetRoster().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to serialize roster: %v", err) - } - - m := GenesisJSON{ - Roster: roster, - TreeRoot: genesis.GetRoot().Bytes(), - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the genesis block if -// appropriate, otherwise it returns an error. -func (f genesisFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := GenesisJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - factory := ctx.GetFactory(types.RosterKey{}) - - fac, ok := factory.(authority.Factory) - if !ok { - return nil, xerrors.Errorf("invalid roster factory '%T'", factory) - } - - roster, err := fac.AuthorityOf(ctx, m.Roster) - if err != nil { - return nil, xerrors.Errorf("authority factory failed: %v", err) - } - - root := types.Digest{} - copy(root[:], m.TreeRoot) - - opts := []types.GenesisOption{types.WithGenesisRoot(root)} - - if f.hashFac != nil { - opts = append(opts, types.WithGenesisHashFactory(f.hashFac)) - } - - genesis, err := types.NewGenesis(roster, opts...) - if err != nil { - return nil, xerrors.Errorf("creating genesis: %v", err) - } - - return genesis, nil -} - -// BlockFormat is the format engine to serialize and deserialize the blocks. -// -// - implements serde.FormatEngine -type blockFormat struct { - hashFac crypto.HashFactory -} - -// Encode implements serde.FormatEngine. It returns the serialized data of the -// block if appropritate, otherwise it returns an error. -func (f blockFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - block, ok := msg.(types.Block) - if !ok { - return nil, xerrors.Errorf("invalid block '%T'", msg) - } - - blockdata, err := block.GetData().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to serialize data: %v", err) - } - - m := BlockJSON{ - Index: block.GetIndex(), - TreeRoot: block.GetTreeRoot().Bytes(), - Data: blockdata, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the block if appropriate, -// otherwise it returns an error. -func (f blockFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := BlockJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - factory := ctx.GetFactory(types.DataKey{}) - - fac, ok := factory.(validation.ResultFactory) - if !ok { - return nil, xerrors.Errorf("invalid data factory '%T'", factory) - } - - blockdata, err := fac.ResultOf(ctx, m.Data) - if err != nil { - return nil, xerrors.Errorf("data factory failed: %v", err) - } - - root := types.Digest{} - copy(root[:], m.TreeRoot) - - opts := []types.BlockOption{ - types.WithTreeRoot(root), - types.WithIndex(m.Index), - } - - if f.hashFac != nil { - opts = append(opts, types.WithHashFactory(f.hashFac)) - } - - block, err := types.NewBlock(blockdata, opts...) - if err != nil { - return nil, xerrors.Errorf("creating block: %v", err) - } - - return block, nil -} - -// MsgFormat is the format engine to serialize and deserialize the messages. -// -// - implements serde.FormatEngine -type msgFormat struct{} - -// Encode implements serde.FormatEngine. It returns the serialized data of the -// message if appropriate, otherwise it returns an error. -func (f msgFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - var m MessageJSON - - switch in := msg.(type) { - case types.GenesisMessage: - genesis, err := in.GetGenesis().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to serialize genesis: %v", err) - } - - gm := GenesisMessageJSON{ - Genesis: genesis, - } - - m = MessageJSON{Genesis: &gm} - case types.BlockMessage: - block, err := in.GetBlock().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("block: %v", err) - } - - views := make(map[string]ViewMessageJSON) - for addr, view := range in.GetViews() { - key, err := addr.MarshalText() - if err != nil { - return nil, xerrors.Errorf("failed to serialize address: %v", err) - } - - rawView, err := encodeView(view, ctx) - if err != nil { - return nil, xerrors.Errorf("view: %v", err) - } - - views[string(key)] = *rawView - } - - bm := BlockMessageJSON{ - Block: block, - Views: views, - } - - m = MessageJSON{Block: &bm} - case types.CommitMessage: - sig, err := in.GetSignature().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to serialize signature: %v", err) - } - - cm := CommitMessageJSON{ - ID: in.GetID().Bytes(), - Signature: sig, - } - - m = MessageJSON{Commit: &cm} - case types.DoneMessage: - sig, err := in.GetSignature().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to serialize signature: %v", err) - } - - dm := DoneMessageJSON{ - ID: in.GetID().Bytes(), - Signature: sig, - } - - m = MessageJSON{Done: &dm} - case types.ViewMessage: - vm, err := encodeView(in, ctx) - if err != nil { - return nil, xerrors.Errorf("view: %v", err) - } - - m = MessageJSON{View: vm} - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -func encodeView(in types.ViewMessage, ctx serde.Context) (*ViewMessageJSON, error) { - sig, err := in.GetSignature().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to serialize signature: %v", err) - } - - vm := &ViewMessageJSON{ - ID: in.GetID().Bytes(), - Leader: in.GetLeader(), - Signature: sig, - } - - return vm, nil -} - -// Decode implements serde.FormatEngine. It populates the message if -// appropriate, otherwise it returns an error. -func (f msgFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := MessageJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - if m.Genesis != nil { - factory := ctx.GetFactory(types.GenesisKey{}) - if factory == nil { - return nil, xerrors.New("missing genesis factory") - } - - msg, err := factory.Deserialize(ctx, m.Genesis.Genesis) - if err != nil { - return nil, xerrors.Errorf("failed to deserialize genesis: %v", err) - } - - genesis, ok := msg.(types.Genesis) - if !ok { - return nil, xerrors.Errorf("invalid genesis '%T'", msg) - } - - return types.NewGenesisMessage(genesis), nil - } - - if m.Block != nil { - // 1. Decode the block. - factory := ctx.GetFactory(types.BlockKey{}) - if factory == nil { - return nil, xerrors.New("missing block factory") - } - - msg, err := factory.Deserialize(ctx, m.Block.Block) - if err != nil { - return nil, xerrors.Errorf("failed to deserialize block: %v", err) - } - - block, ok := msg.(types.Block) - if !ok { - return nil, xerrors.Errorf("invalid block '%T'", msg) - } - - // 2. Decode the view messages if any. - factory = ctx.GetFactory(types.AddressKey{}) - - fac, ok := factory.(mino.AddressFactory) - if !ok { - return nil, xerrors.Errorf("invalid address factory '%T'", factory) - } - - views := make(map[mino.Address]types.ViewMessage) - for key, rawView := range m.Block.Views { - addr := fac.FromText([]byte(key)) - - view, err := decodeView(ctx, &rawView) - if err != nil { - return nil, xerrors.Errorf("view: %v", err) - } - - views[addr] = view - } - - return types.NewBlockMessage(block, views), nil - } - - if m.Commit != nil { - sig, err := decodeSignature(ctx, m.Commit.Signature, types.AggregateKey{}) - if err != nil { - return nil, xerrors.Errorf("commit failed: %v", err) - } - - id := types.Digest{} - copy(id[:], m.Commit.ID) - - return types.NewCommit(id, sig), nil - } - - if m.Done != nil { - sig, err := decodeSignature(ctx, m.Done.Signature, types.AggregateKey{}) - if err != nil { - return nil, xerrors.Errorf("done failed: %v", err) - } - - id := types.Digest{} - copy(id[:], m.Done.ID) - - return types.NewDone(id, sig), nil - } - - if m.View != nil { - return decodeView(ctx, m.View) - } - - return nil, xerrors.New("message is empty") -} - -func decodeView(ctx serde.Context, view *ViewMessageJSON) (types.ViewMessage, error) { - sig, err := decodeSignature(ctx, view.Signature, types.SignatureKey{}) - if err != nil { - return types.ViewMessage{}, xerrors.Errorf("signature: %v", err) - } - - id := types.Digest{} - copy(id[:], view.ID) - - return types.NewViewMessage(id, view.Leader, sig), nil -} - -func decodeSignature(ctx serde.Context, data []byte, key interface{}) (crypto.Signature, error) { - factory := ctx.GetFactory(key) - - fac, ok := factory.(crypto.SignatureFactory) - if !ok { - return nil, xerrors.Errorf("invalid signature factory '%T'", factory) - } - - sig, err := fac.SignatureOf(ctx, data) - if err != nil { - return nil, xerrors.Errorf("factory failed: %v", err) - } - - return sig, nil -} diff --git a/dela/core/ordering/cosipbft/json/json_test.go b/dela/core/ordering/cosipbft/json/json_test.go deleted file mode 100644 index cdb8e3e..0000000 --- a/dela/core/ordering/cosipbft/json/json_test.go +++ /dev/null @@ -1,316 +0,0 @@ -package json - -import ( - "io" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func init() { - types.RegisterGenesisFormat(fake.GoodFormat, fakeGenesisFormat{}) - types.RegisterGenesisFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestGenesisFormat_Encode(t *testing.T) { - format := genesisFormat{} - - ctx := fake.NewContext() - - genesis, err := types.NewGenesis(fakeRoster{}, types.WithGenesisRoot(types.Digest{1})) - require.NoError(t, err) - - data, err := format.Encode(ctx, genesis) - require.NoError(t, err) - require.Regexp(t, `{"Roster":{},"TreeRoot":"[^"]+"}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "invalid genesis 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), genesis) - require.EqualError(t, err, fake.Err("failed to marshal")) - - genesis, err = types.NewGenesis(fakeRoster{err: fake.GetError()}) - require.NoError(t, err) - - _, err = format.Encode(ctx, genesis) - require.EqualError(t, err, fake.Err("failed to serialize roster")) -} - -func TestGenesisFormat_Decode(t *testing.T) { - format := genesisFormat{} - - genesis, err := types.NewGenesis(fakeRoster{}) - require.NoError(t, err) - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.RosterKey{}, fakeRosterFac{}) - - msg, err := format.Decode(ctx, []byte(`{}`)) - require.NoError(t, err) - require.NotNil(t, msg, genesis) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - badCtx := serde.WithFactory(ctx, types.RosterKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "invalid roster factory ''") - - badCtx = serde.WithFactory(ctx, types.RosterKey{}, fakeRosterFac{err: fake.GetError()}) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("authority factory failed")) - - format.hashFac = fake.NewHashFactory(fake.NewBadHash()) - _, err = format.Decode(ctx, []byte(`{}`)) - require.Error(t, err) - require.Contains(t, err.Error(), "creating genesis: fingerprint failed: ") -} - -func TestBlockFormat_Encode(t *testing.T) { - format := blockFormat{} - - ctx := fake.NewContext() - - block, err := types.NewBlock(fakeResult{}) - require.NoError(t, err) - - data, err := format.Encode(ctx, block) - require.NoError(t, err) - require.Regexp(t, `{"Index":0,"TreeRoot":"[^"]+","Data":{}}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "invalid block 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), block) - require.EqualError(t, err, fake.Err("failed to marshal")) - - block, err = types.NewBlock(fakeResult{err: fake.GetError()}) - require.NoError(t, err) - - _, err = format.Encode(ctx, block) - require.EqualError(t, err, fake.Err("failed to serialize data")) -} - -func TestBlockFormat_Decode(t *testing.T) { - format := blockFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.DataKey{}, fakeResultFac{}) - - block, err := types.NewBlock(fakeResult{}) - require.NoError(t, err) - - msg, err := format.Decode(ctx, []byte(`{}`)) - require.NoError(t, err) - require.Equal(t, block, msg) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - badCtx := serde.WithFactory(ctx, types.DataKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "invalid data factory ''") - - badCtx = serde.WithFactory(ctx, types.DataKey{}, fakeResultFac{err: fake.GetError()}) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("data factory failed")) - - format.hashFac = fake.NewHashFactory(fake.NewBadHash()) - _, err = format.Decode(ctx, []byte(`{}`)) - require.Error(t, err) - require.Contains(t, err.Error(), "creating block: fingerprint failed: ") -} - -func TestMsgFormat_Encode(t *testing.T) { - format := msgFormat{} - - genesis, err := types.NewGenesis(fakeRoster{}) - require.NoError(t, err) - - block, err := types.NewBlock(fakeResult{}) - require.NoError(t, err) - - ctx := fake.NewContext() - - data, err := format.Encode(ctx, types.NewGenesisMessage(genesis)) - require.NoError(t, err) - require.Equal(t, `{"Genesis":{"Genesis":{}}}`, string(data)) - - _, err = format.Encode(fake.NewBadContext(), types.NewGenesisMessage(genesis)) - require.EqualError(t, err, fake.Err("failed to serialize genesis: encoding failed")) - - views := map[mino.Address]types.ViewMessage{ - fake.NewAddress(0): types.NewViewMessage(types.Digest{1}, 5, fake.Signature{}), - } - data, err = format.Encode(ctx, types.NewBlockMessage(block, views)) - require.NoError(t, err) - require.Regexp(t, - `{"Block":{"Block":{},"Views":{"[^"]+":{"Leader":5,"ID":"[^"]+","Signature":{}}}}}`, string(data)) - - views[fake.NewAddress(0)] = types.NewViewMessage(types.Digest{}, 0, fake.NewBadSignature()) - _, err = format.Encode(ctx, types.NewBlockMessage(block, views)) - require.EqualError(t, err, fake.Err("view: failed to serialize signature")) - - delete(views, fake.NewAddress(0)) - views[fake.NewBadAddress()] = types.NewViewMessage(types.Digest{}, 0, fake.Signature{}) - _, err = format.Encode(ctx, types.NewBlockMessage(block, views)) - require.EqualError(t, err, fake.Err("failed to serialize address")) - - _, err = format.Encode(fake.NewBadContext(), types.NewBlockMessage(block, nil)) - require.EqualError(t, err, fake.Err("block: encoding failed")) - - data, err = format.Encode(ctx, types.NewCommit(types.Digest{}, fake.Signature{})) - require.NoError(t, err) - require.Regexp(t, `{"Commit":{"ID":"[^"]+","Signature":{}}}`, string(data)) - - _, err = format.Encode(ctx, types.NewCommit(types.Digest{}, fake.NewBadSignature())) - require.EqualError(t, err, fake.Err("failed to serialize signature")) - - data, err = format.Encode(ctx, types.NewDone(types.Digest{}, fake.Signature{})) - require.NoError(t, err) - require.Regexp(t, `{"Done":{"ID":"[^"]+","Signature":{}}}`, string(data)) - - _, err = format.Encode(ctx, types.NewDone(types.Digest{}, fake.NewBadSignature())) - require.EqualError(t, err, fake.Err("failed to serialize signature")) - - data, err = format.Encode(ctx, types.NewViewMessage(types.Digest{}, 5, fake.Signature{})) - require.NoError(t, err) - require.Regexp(t, `{"View":{"Leader":5,"ID":"[^"]+","Signature":{}}}`, string(data)) - - _, err = format.Encode(ctx, types.NewViewMessage(types.Digest{}, 0, fake.NewBadSignature())) - require.EqualError(t, err, fake.Err("view: failed to serialize signature")) - - _, err = format.Encode(fake.NewBadContext(), types.NewViewMessage(types.Digest{}, 0, fake.Signature{})) - require.EqualError(t, err, fake.Err("failed to marshal")) -} - -func TestMsgFormat_Decode(t *testing.T) { - format := msgFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.GenesisKey{}, types.GenesisFactory{}) - ctx = serde.WithFactory(ctx, types.BlockKey{}, types.BlockFactory{}) - ctx = serde.WithFactory(ctx, types.AggregateKey{}, fake.SignatureFactory{}) - ctx = serde.WithFactory(ctx, types.SignatureKey{}, fake.SignatureFactory{}) - ctx = serde.WithFactory(ctx, types.AddressKey{}, fake.AddressFactory{}) - - msg, err := format.Decode(ctx, []byte(`{"Genesis":{}}`)) - require.NoError(t, err) - require.IsType(t, types.GenesisMessage{}, msg) - - badCtx := serde.WithFactory(ctx, types.GenesisKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Genesis":{}}`)) - require.EqualError(t, err, "missing genesis factory") - - badCtx = serde.WithFactory(ctx, types.GenesisKey{}, fake.NewBadMessageFactory()) - _, err = format.Decode(badCtx, []byte(`{"Genesis":{}}`)) - require.EqualError(t, err, fake.Err("failed to deserialize genesis")) - - badCtx = serde.WithFactory(ctx, types.GenesisKey{}, fake.MessageFactory{}) - _, err = format.Decode(badCtx, []byte(`{"Genesis":{}}`)) - require.EqualError(t, err, "invalid genesis 'fake.Message'") - - msg, err = format.Decode(ctx, []byte(`{"Block":{"Views":{"":{}}}}`)) - require.NoError(t, err) - require.IsType(t, types.BlockMessage{}, msg) - require.Len(t, msg.(types.BlockMessage).GetViews(), 1) - - badCtx = serde.WithFactory(ctx, types.BlockKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Block":{}}`)) - require.EqualError(t, err, "missing block factory") - - badCtx = serde.WithFactory(ctx, types.BlockKey{}, fake.NewBadMessageFactory()) - _, err = format.Decode(badCtx, []byte(`{"Block":{}}`)) - require.EqualError(t, err, fake.Err("failed to deserialize block")) - - badCtx = serde.WithFactory(ctx, types.BlockKey{}, fake.MessageFactory{}) - _, err = format.Decode(badCtx, []byte(`{"Block":{}}`)) - require.EqualError(t, err, "invalid block 'fake.Message'") - - badCtx = serde.WithFactory(ctx, types.AddressKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Block":{"Views":{"":{}}}}`)) - require.EqualError(t, err, "invalid address factory ''") - - badCtx = serde.WithFactory(ctx, types.SignatureKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Block":{"Views":{"":{}}}}`)) - require.EqualError(t, err, "view: signature: invalid signature factory ''") - - msg, err = format.Decode(ctx, []byte(`{"Commit":{}}`)) - require.NoError(t, err) - require.IsType(t, types.CommitMessage{}, msg) - - badCtx = serde.WithFactory(ctx, types.AggregateKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Commit":{}}`)) - require.EqualError(t, err, "commit failed: invalid signature factory ''") - - msg, err = format.Decode(ctx, []byte(`{"Done":{}}`)) - require.NoError(t, err) - require.IsType(t, types.DoneMessage{}, msg) - - _, err = format.Decode(badCtx, []byte(`{"Done":{}}`)) - require.EqualError(t, err, "done failed: invalid signature factory ''") - - msg, err = format.Decode(ctx, []byte(`{"View":{}}`)) - require.NoError(t, err) - require.IsType(t, types.ViewMessage{}, msg) - - badCtx = serde.WithFactory(ctx, types.SignatureKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"View":{}}`)) - require.EqualError(t, err, "signature: invalid signature factory ''") - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, "message is empty") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeRoster struct { - authority.Authority - - err error -} - -func (ro fakeRoster) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), ro.err -} - -func (fakeRoster) Fingerprint(io.Writer) error { - return nil -} - -type fakeRosterFac struct { - authority.Factory - - err error -} - -func (fac fakeRosterFac) AuthorityOf(serde.Context, []byte) (authority.Authority, error) { - return fakeRoster{}, fac.err -} - -type fakeGenesisFormat struct { - serde.FormatEngine -} - -func (fakeGenesisFormat) Encode(serde.Context, serde.Message) ([]byte, error) { - return []byte(`{}`), nil -} - -func (fakeGenesisFormat) Decode(serde.Context, []byte) (serde.Message, error) { - genesis, err := types.NewGenesis(authority.New(nil, nil)) - if err != nil { - return nil, err - } - - return genesis, nil -} diff --git a/dela/core/ordering/cosipbft/json/link.go b/dela/core/ordering/cosipbft/json/link.go deleted file mode 100644 index d505c90..0000000 --- a/dela/core/ordering/cosipbft/json/link.go +++ /dev/null @@ -1,166 +0,0 @@ -package json - -import ( - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// LinkFormat is the JSON format engine to serialize and deserialize the links. -// -// - implements serde.FormatEngine -type linkFormat struct { - hashFac crypto.HashFactory -} - -// Encode implements serde.FormatEngine. It serializes the link or the block -// link if appropriate, otherwise it returns an error. -func (fmt linkFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - var m LinkJSON - - switch link := msg.(type) { - case types.BlockLink: - err := fmt.encodeLink(ctx, link, &m) - if err != nil { - return nil, err - } - - block, err := link.GetBlock().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize block: %v", err) - } - - m.Block = block - case types.Link: - err := fmt.encodeLink(ctx, link, &m) - if err != nil { - return nil, err - } - - to := link.GetTo() - - m.To = to.Bytes() - default: - return nil, xerrors.Errorf("unsupported message '%T'", msg) - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -func (fmt linkFormat) encodeLink(ctx serde.Context, link types.Link, m *LinkJSON) error { - prepare, err := link.GetPrepareSignature().Serialize(ctx) - if err != nil { - return xerrors.Errorf("couldn't serialize prepare: %v", err) - } - - commit, err := link.GetCommitSignature().Serialize(ctx) - if err != nil { - return xerrors.Errorf("couldn't serialize commit: %v", err) - } - - changeset, err := link.GetChangeSet().Serialize(ctx) - if err != nil { - return xerrors.Errorf("couldn't serialize change set: %v", err) - } - - m.From = link.GetFrom().Bytes() - m.PrepareSignature = prepare - m.CommitSignature = commit - m.ChangeSet = changeset - - return nil -} - -// Decode implements serde.FormatEngine. It populates the link or the block link -// if appropriate, otherwise it returns an error. -func (fmt linkFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := LinkJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - prepare, err := decodeSignature(ctx, m.PrepareSignature, types.AggregateKey{}) - if err != nil { - return nil, xerrors.Errorf("failed to decode prepare: %v", err) - } - - commit, err := decodeSignature(ctx, m.CommitSignature, types.AggregateKey{}) - if err != nil { - return nil, xerrors.Errorf("failed to decode commit: %v", err) - } - - changeset, err := decodeChangeSet(ctx, m.ChangeSet) - if err != nil { - return nil, xerrors.Errorf("failed to decode change set: %v", err) - } - - from := types.Digest{} - copy(from[:], m.From) - - opts := []types.LinkOption{ - types.WithSignatures(prepare, commit), - types.WithChangeSet(changeset), - } - - if fmt.hashFac != nil { - opts = append(opts, types.WithLinkHashFactory(fmt.hashFac)) - } - - if len(m.Block) > 0 { - factory := ctx.GetFactory(types.BlockKey{}) - if factory == nil { - return nil, xerrors.New("missing block factory") - } - - msg, err := factory.Deserialize(ctx, m.Block) - if err != nil { - return nil, xerrors.Errorf("failed to decode block: %v", err) - } - - block, ok := msg.(types.Block) - if !ok { - return nil, xerrors.Errorf("invalid block '%T'", msg) - } - - link, err := types.NewBlockLink(from, block, opts...) - if err != nil { - return nil, xerrors.Errorf("creating block link: %v", err) - } - - return link, nil - } - - to := types.Digest{} - copy(to[:], m.To) - - link, err := types.NewForwardLink(from, to, opts...) - if err != nil { - return nil, xerrors.Errorf("creating forward link: %v", err) - } - - return link, nil -} - -func decodeChangeSet(ctx serde.Context, data []byte) (authority.ChangeSet, error) { - factory := ctx.GetFactory(types.ChangeSetKey{}) - - fac, ok := factory.(authority.ChangeSetFactory) - if !ok { - return nil, xerrors.Errorf("invalid factory '%T'", factory) - } - - changeset, err := fac.ChangeSetOf(ctx, data) - if err != nil { - return nil, xerrors.Errorf("factory failed: %v", err) - } - - return changeset, nil -} diff --git a/dela/core/ordering/cosipbft/json/link_test.go b/dela/core/ordering/cosipbft/json/link_test.go deleted file mode 100644 index f71f90c..0000000 --- a/dela/core/ordering/cosipbft/json/link_test.go +++ /dev/null @@ -1,205 +0,0 @@ -package json - -import ( - "io" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func init() { - types.RegisterBlockFormat(fake.GoodFormat, fakeBlockFormat{}) - types.RegisterBlockFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestLinkFormat_Encode(t *testing.T) { - format := linkFormat{} - - ctx := fake.NewContext() - - data, err := format.Encode(ctx, makeLink(t)) - require.NoError(t, err) - re := `{"From":"[^"]+","To":"[^"]+",` + - `"PrepareSignature":{},"CommitSignature":{},"ChangeSet":{}}` - require.Regexp(t, re, string(data)) - - data, err = format.Encode(ctx, makeBlockLink(t)) - require.NoError(t, err) - re = `{"From":"[^"]+","PrepareSignature":{},` + - `"CommitSignature":{},"ChangeSet":{},"Block":{}}` - require.Regexp(t, re, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message 'fake.Message'") - - opt := types.WithSignatures(fake.NewBadSignature(), fake.Signature{}) - _, err = format.Encode(ctx, makeLink(t, opt)) - require.EqualError(t, err, fake.Err("couldn't serialize prepare")) - - opt = types.WithSignatures(fake.Signature{}, fake.NewBadSignature()) - _, err = format.Encode(ctx, makeLink(t, opt)) - require.EqualError(t, err, fake.Err("couldn't serialize commit")) - - opt = types.WithChangeSet(fakeChangeSet{err: fake.GetError()}) - _, err = format.Encode(ctx, makeBlockLink(t, opt)) - require.EqualError(t, err, fake.Err("couldn't serialize change set")) - - _, err = format.Encode(fake.NewBadContext(), makeBlockLink(t)) - require.EqualError(t, err, fake.Err("couldn't serialize block: encoding failed")) - - _, err = format.Encode(fake.NewBadContext(), makeLink(t)) - require.EqualError(t, err, fake.Err("failed to marshal")) -} - -func TestLinkFormat_Decode(t *testing.T) { - format := linkFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.AggregateKey{}, fake.SignatureFactory{}) - ctx = serde.WithFactory(ctx, types.ChangeSetKey{}, fakeChangeSetFac{}) - ctx = serde.WithFactory(ctx, types.BlockKey{}, types.BlockFactory{}) - - msg, err := format.Decode(ctx, []byte(`{"From":[1],"To":[2]}`)) - require.NoError(t, err) - require.Equal(t, makeLink(t), msg) - - msg, err = format.Decode(ctx, []byte(`{"From":[1],"Block":{}}`)) - require.NoError(t, err) - require.Equal(t, makeBlockLink(t), msg) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - badCtx := serde.WithFactory(ctx, types.AggregateKey{}, fake.NewBadSignatureFactory()) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to decode prepare: factory failed")) - - badCtx = serde.WithFactory(ctx, types.AggregateKey{}, fake.NewBadSignatureFactoryWithDelay(1)) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to decode commit: factory failed")) - - badCtx = serde.WithFactory(ctx, types.ChangeSetKey{}, fake.MessageFactory{}) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "failed to decode change set: invalid factory 'fake.MessageFactory'") - - badCtx = serde.WithFactory(ctx, types.ChangeSetKey{}, fakeChangeSetFac{err: fake.GetError()}) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to decode change set: factory failed")) - - badCtx = serde.WithFactory(ctx, types.BlockKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Block":{}}`)) - require.EqualError(t, err, "missing block factory") - - badCtx = serde.WithFactory(ctx, types.BlockKey{}, fake.NewBadMessageFactory()) - _, err = format.Decode(badCtx, []byte(`{"Block":{}}`)) - require.EqualError(t, err, fake.Err("failed to decode block")) - - badCtx = serde.WithFactory(ctx, types.BlockKey{}, fake.MessageFactory{}) - _, err = format.Decode(badCtx, []byte(`{"Block":{}}`)) - require.EqualError(t, err, "invalid block 'fake.Message'") - - format.hashFac = fake.NewHashFactory(fake.NewBadHash()) - _, err = format.Decode(ctx, []byte(`{}`)) - require.Error(t, err) - require.Contains(t, err.Error(), "creating forward link: failed to fingerprint: ") - - _, err = format.Decode(ctx, []byte(`{"Block":{}}`)) - require.Error(t, err) - require.Contains(t, err.Error(), "creating block link: creating forward link: failed to fingerprint: ") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeLink(t *testing.T, opts ...types.LinkOption) types.Link { - sigs := types.WithSignatures(fake.Signature{}, fake.Signature{}) - cs := types.WithChangeSet(fakeChangeSet{}) - - opts = append([]types.LinkOption{sigs, cs}, opts...) - - link, err := types.NewForwardLink(types.Digest{1}, types.Digest{2}, opts...) - require.NoError(t, err) - - return link -} - -func makeBlockLink(t *testing.T, opts ...types.LinkOption) types.BlockLink { - block, err := types.NewBlock(fakeResult{}) - require.NoError(t, err) - - sigs := types.WithSignatures(fake.Signature{}, fake.Signature{}) - cs := types.WithChangeSet(fakeChangeSet{}) - - opts = append([]types.LinkOption{sigs, cs}, opts...) - - link, err := types.NewBlockLink(types.Digest{1}, block, opts...) - require.NoError(t, err) - - return link -} - -type fakeChangeSet struct { - authority.ChangeSet - - err error -} - -func (cs fakeChangeSet) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), cs.err -} - -type fakeChangeSetFac struct { - authority.ChangeSetFactory - - err error -} - -func (fac fakeChangeSetFac) ChangeSetOf(serde.Context, []byte) (authority.ChangeSet, error) { - return fakeChangeSet{}, fac.err -} - -type fakeResult struct { - validation.Result - - err error -} - -func (data fakeResult) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), data.err -} - -func (fakeResult) Fingerprint(io.Writer) error { - return nil -} - -type fakeResultFac struct { - validation.ResultFactory - - err error -} - -func (fac fakeResultFac) ResultOf(serde.Context, []byte) (validation.Result, error) { - return fakeResult{}, fac.err -} - -type fakeBlockFormat struct { - serde.FormatEngine -} - -func (fakeBlockFormat) Encode(serde.Context, serde.Message) ([]byte, error) { - return []byte(`{}`), nil -} - -func (fakeBlockFormat) Decode(serde.Context, []byte) (serde.Message, error) { - block, err := types.NewBlock(fakeResult{}) - if err != nil { - return nil, err - } - - return block, nil -} diff --git a/dela/core/ordering/cosipbft/pbft/pbft.go b/dela/core/ordering/cosipbft/pbft/pbft.go deleted file mode 100644 index bf31fe5..0000000 --- a/dela/core/ordering/cosipbft/pbft/pbft.go +++ /dev/null @@ -1,856 +0,0 @@ -// Package pbft defines a state machine to perform PBFT using collective -// signatures. -// -// The package also implements a default state machine that allows only one -// block candidate per leader so that after a successful prepare phase, it -// expects the block to be committed and finalized. The only other state allowed -// is the view change if the round has expired. -// -// The view change can be fixed only by providing enough valid views from unique -// participants to comply to the 2f threshold, or if a catch up that provides a -// proof of acceptance of the block. -// -// Documentation Last Review: 13.10.2020 -package pbft - -import ( - "context" - "sync" - - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -// State is the type of the different possible states for the PBFT state -// machine. -type State byte - -func (s State) String() string { - switch s { - case NoneState: - return "none" - case InitialState: - return "initial" - case PrepareState: - return "prepare" - case CommitState: - return "commit" - case ViewChangeState: - return "viewchange" - default: - return "unknown" - } -} - -// defines prometheus metrics -var ( - promBlocks = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "dela_cosipbft_blocks_total", - Help: "total number of blocks", - }) - - promTxs = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "dela_cosipbft_transactions_block", - Help: "total number of transactions in the last block", - Buckets: []float64{0, 1, 2, 3, 5, 8, 13, 20, 30, 50, 100}, - }) - - promRejectedTxs = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "dela_cosipbft_transactions_rejected_block", - Help: "total number of rejected transactions in the last block", - Buckets: []float64{0, 1, 2, 3, 5, 8, 13, 20, 30, 50, 100}, - }) - - promLeader = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "dela_cosipbft_leader", - Help: "leader index from the roster", - }) -) - -const ( - // NoneState is the very first state of the machine where nothing is set. - NoneState State = iota - - // InitialState is the entry state which means the beginning of the PBFT - // protocol. - InitialState - - // PrepareState is the state to indicate that a proposal has been received - // and the machine is waiting for confirmations. - PrepareState - - // CommitState is the state to indicate that the participants have accepted - // the proposal and the machine is waiting for the second confirmation. - CommitState - - // ViewChangeState is the state to indicate that the protocol is failing and - // the machine is waiting for view change requests. - ViewChangeState -) - -func init() { - dela.PromCollectors = append(dela.PromCollectors, promBlocks, promTxs, - promRejectedTxs, promLeader) -} - -// StateMachine is the interface to implement to support a PBFT protocol. -type StateMachine interface { - // GetState returns the current state. - GetState() State - - // GetLeader returns the address of the round leader. - GetLeader() (mino.Address, error) - - // GetViews returns the list of views for which the round has been accepted, - // after a successful view change. Otherwise it is empty. - GetViews() map[mino.Address]View - - // GetCommit returns the candidate digest and the associated block if the - // state machine is committed to a candidate, otherwise the behaviour is - // undefined. - GetCommit() (types.Digest, types.Block) - - // Prepare processes the candidate block and moves the state machine if it - // is valid and from the correct leader. - Prepare(from mino.Address, block types.Block) (types.Digest, error) - - // Commit moves the state machine to the next state if the signature is - // valid for the candidate. - Commit(types.Digest, crypto.Signature) error - - // Finalize finalizes a round if the signature is a valid commit signature. - Finalize(types.Digest, crypto.Signature) error - - // Accept processes the view during a view change state, and moves to a new - // round if enough have been received. - Accept(View) error - - // AcceptAll processes the list of views so that it may proceed to a future - // round if the list contains enough valid views. - AcceptAll([]View) error - - // Expire announces that the round has expired and moves the state machine - // to a view change state. - Expire(addr mino.Address) (View, error) - - // CatchUp forces a valid block to be processed by the state machine without - // doing the intermediate phases. - CatchUp(types.BlockLink) error - - // Watch returns a channel that is populated with the changes of states from - // the state machine. - Watch(context.Context) <-chan State -} - -type round struct { - leader uint16 - threshold int - id types.Digest - block types.Block - tree hashtree.StagingTree - prepareSig crypto.Signature - changeset authority.ChangeSet - committed bool - prevViews map[mino.Address]View - views map[mino.Address]View - - // allows a node to catch up on a new leader - tentativeRound types.Digest - tentativeLeader uint16 -} - -// AuthorityReader is a function to help the state machine to read the current -// authority for a given tree. -type AuthorityReader func(tree hashtree.Tree) (authority.Authority, error) - -// pbftsm is an implementation of a state machine to perform PBFT rounds. -// -// - implements pbft.Statemachine -type pbftsm struct { - sync.Mutex - - logger zerolog.Logger - watcher core.Observable - hashFac crypto.HashFactory - val validation.Service - blocks blockstore.BlockStore - genesis blockstore.GenesisStore - tree blockstore.TreeCache - authReader AuthorityReader - db kv.DB - - // verifierFac creates a verifier for the aggregated signature. - verifierFac crypto.VerifierFactory - // signer signs and verify single signature for the view change. - signer crypto.Signer - - state State - round round -} - -// StateMachineParam is a structure to pass the different components of the PBFT -// state machine. -type StateMachineParam struct { - Logger zerolog.Logger - Validation validation.Service - VerifierFactory crypto.VerifierFactory - Signer crypto.Signer - Blocks blockstore.BlockStore - Genesis blockstore.GenesisStore - Tree blockstore.TreeCache - AuthorityReader AuthorityReader - DB kv.DB -} - -// NewStateMachine returns a new state machine. -func NewStateMachine(param StateMachineParam) StateMachine { - return &pbftsm{ - logger: param.Logger, - watcher: core.NewWatcher(), - hashFac: crypto.NewSha256Factory(), - val: param.Validation, - verifierFac: param.VerifierFactory, - signer: param.Signer, - blocks: param.Blocks, - genesis: param.Genesis, - tree: param.Tree, - db: param.DB, - state: NoneState, - authReader: param.AuthorityReader, - } -} - -// GetState implements pbft.StateMachine. It returns the current state of the -// machine. -func (m *pbftsm) GetState() State { - m.Lock() - defer m.Unlock() - - return m.state -} - -// GetLeader implements pbft.StateMachine. It returns the current leader of the -// round, or nil if the roster is not yet defined. -func (m *pbftsm) GetLeader() (mino.Address, error) { - m.Lock() - defer m.Unlock() - - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return nil, xerrors.Errorf("failed to read roster: %v", err) - } - - iter := roster.AddressIterator() - iter.Seek(int(m.round.leader)) - - return iter.GetNext(), nil -} - -// GetViews implements pbft.StateMachine. It returns the views for which the -// current round has been accepted. -func (m *pbftsm) GetViews() map[mino.Address]View { - m.Lock() - - views := make(map[mino.Address]View) - for key, value := range m.round.prevViews { - views[key] = value - } - - m.Unlock() - - return views -} - -// GetCommit implements pbft.StateMachine. It returns the proposal identifier -// and the block that have been proposed to the state machine. The values are -// valid only if the state is at least PrepareState. -func (m *pbftsm) GetCommit() (types.Digest, types.Block) { - m.Lock() - defer m.Unlock() - - return m.round.id, m.round.block -} - -// Prepare implements pbft.StateMachine. It receives the proposal from the -// leader and the current tree, and produces the next tree alongside the ID of -// the proposal that will be signed. -func (m *pbftsm) Prepare(from mino.Address, block types.Block) (types.Digest, error) { - m.Lock() - defer m.Unlock() - - id := m.round.id - - if m.state == ViewChangeState { - // When in view change mode, it must refuse any proposal incoming until - // the node leaves the state. - return id, xerrors.New("cannot be in view change state during prepare") - } - - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return id, xerrors.Errorf("failed to read roster: %v", err) - } - - _, index := roster.GetPublicKey(from) - - if uint16(index) != m.round.leader { - // Allows the node to catchup on the leader. It rejects the proposal, - // but will accept this leader later if the block is finalized and - // synced to us. - m.round.tentativeRound = block.GetHash() - m.round.tentativeLeader = uint16(index) - return id, xerrors.Errorf("'%v' is not the leader", from) - } - - // Check the state after verifying that the proposal comes from the right - // leader. - if m.state == PrepareState || m.state == CommitState { - // The leader should only propose one block, therefore the accepted - // proposal identifier is sent back, whatever the input is. - return id, nil - } - - m.round.threshold = calculateThreshold(roster.Len()) - - err = m.verifyPrepare(m.tree.Get(), block, &m.round, roster) - if err != nil { - return id, err - } - - m.setState(PrepareState) - - return m.round.id, nil -} - -// Commit implements pbft.StateMachine. It commits the state machine to the -// proposal if the signature is verified. -func (m *pbftsm) Commit(id types.Digest, sig crypto.Signature) error { - m.Lock() - defer m.Unlock() - - if m.state != PrepareState && m.state != CommitState { - return xerrors.Errorf("cannot commit from %v state", m.state) - } - - if id != m.round.id { - return xerrors.Errorf("mismatch id '%v' != '%v'", id, m.round.id) - } - - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return xerrors.Errorf("failed to read roster: %v", err) - } - - err = m.verifyCommit(&m.round, sig, roster) - if err != nil { - return err - } - - // At this point, the proposal must be finalized whatever happens. - m.round.committed = true - - m.setState(CommitState) - - return nil -} - -// Finalize implements pbft.StateMachine. It makes sure the commit signature is -// correct and then moves to the initial state. -func (m *pbftsm) Finalize(id types.Digest, sig crypto.Signature) error { - m.Lock() - defer m.Unlock() - - if m.state != CommitState { - return xerrors.Errorf("mismatch state %v != %v", m.state, CommitState) - } - - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return xerrors.Errorf("failed to read roster: %v", err) - } - - err = m.verifyFinalize(&m.round, sig, roster) - if err != nil { - return err - } - - dela.Logger.Info().Msgf("finalize round with leader: %d", m.round.leader) - - m.round.prevViews = nil - m.round.views = nil - m.round.committed = false - - m.setState(InitialState) - - return nil -} - -// Accept implements pbft.StateMachine. It processes view change messages and -// reset the current PBFT if it receives enough of them. -func (m *pbftsm) Accept(view View) error { - m.Lock() - defer m.Unlock() - - _, err := m.init() - if err != nil { - return xerrors.Errorf("init: %v", err) - } - - if view.leader == m.round.leader { - // Ignore view coming for the current leader as we already accepted this - // leader. - return nil - } - - err = m.verifyViews(false, view) - if err != nil { - return xerrors.Errorf("invalid view: %v", err) - } - - if m.round.views == nil { - m.round.views = make(map[mino.Address]View) - } - - m.logger.Trace(). - Str("from", view.from.String()). - Msg("view accepted") - - m.round.views[view.from] = view - - m.checkViewChange(view) - - return nil -} - -// AcceptAll implements pbft.StateMachine. It accepts a list of views which -// allows a node falling behind to catch up. The list must contain enough views -// to reach the threshold, otherwise it will be ignored. -func (m *pbftsm) AcceptAll(views []View) error { - m.Lock() - defer m.Unlock() - - _, err := m.init() - if err != nil { - return xerrors.Errorf("init: %v", err) - } - - if len(views) <= m.round.threshold { - return xerrors.Errorf("not enough views: %d <= %d", - len(views), m.round.threshold) - } - - if views[0].leader == m.round.leader { - // Skip verifying the views if the leader will anyway be the same. - return nil - } - - err = m.verifyViews(true, views...) - if err != nil { - return xerrors.Errorf("invalid view: %v", err) - } - - set := make(map[mino.Address]View) - for _, view := range views { - set[view.from] = view - } - - m.round.views = set - m.state = ViewChangeState - m.checkViewChange(views[0]) - - return nil -} - -func (m *pbftsm) verifyViews(skip bool, views ...View) error { - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return xerrors.Errorf("failed to read roster: %v", err) - } - - for _, view := range views { - pubkey, _ := roster.GetPublicKey(view.from) - if pubkey == nil { - return xerrors.Errorf("unknown peer: %v", view.from) - } - - err := view.Verify(pubkey) - if err != nil { - return xerrors.Errorf("invalid signature: %v", err) - } - - nextLeader := (m.round.leader + 1) % uint16(roster.Len()) - if !skip && view.leader != nextLeader { - // The state machine ignore view messages from different rounds. It only - // accepts views for the next leader even if the state machine is not in - // ViewChange state. - // - // This check can be skipped when enough views are received for a - // given leader index. - return xerrors.Errorf("mismatch leader %d != %d", view.leader, nextLeader) - } - - latestID, err := m.getLatestID() - if err != nil { - return xerrors.Errorf("failed to read latest id: %v", err) - } - - if view.id != latestID { - return xerrors.Errorf("mismatch id %v != %v", view.id, latestID) - } - } - - return nil -} - -// Expire implements pbft.StateMachine. It moves the state machine to the -// ViewChange state. If it has already received enough messages, it will move to -// the next leader. -func (m *pbftsm) Expire(addr mino.Address) (View, error) { - m.Lock() - defer m.Unlock() - - m.logger.Warn().Msgf("expire: current leader is %d", m.round.leader) - - roster, err := m.init() - if err != nil { - return View{}, xerrors.Errorf("init: %v", err) - } - - lastID, err := m.getLatestID() - if err != nil { - return View{}, xerrors.Errorf("couldn't get latest digest: %v", err) - } - - newLeader := (m.round.leader + 1) % uint16(roster.Len()) - - param := ViewParam{ - From: addr, - ID: lastID, - Leader: newLeader, - } - - view, err := NewViewAndSign(param, m.signer) - if err != nil { - return view, xerrors.Errorf("create view: %v", err) - } - - m.setState(ViewChangeState) - - if m.round.views == nil { - m.round.views = make(map[mino.Address]View) - } - - m.round.views[addr] = view - - m.checkViewChange(view) - - return view, nil -} - -// CatchUp implements pbft.StateMachine. It can force a block link to be -// inserted to reset the state machine to a initial state. -func (m *pbftsm) CatchUp(link types.BlockLink) error { - m.Lock() - defer m.Unlock() - - if m.state == CommitState && m.round.id != link.GetHash() { - return xerrors.Errorf("already committed to '%v'", m.round.id) - } - - r := round{ - threshold: m.round.threshold, - } - - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return xerrors.Errorf("failed to read roster: %v", err) - } - - err = m.verifyPrepare(m.tree.Get(), link.GetBlock(), &r, roster) - if err != nil { - return xerrors.Errorf("prepare failed: %v", err) - } - - err = m.verifyCommit(&r, link.GetPrepareSignature(), roster) - if err != nil { - return xerrors.Errorf("commit failed: %v", err) - } - - err = m.verifyFinalize(&r, link.GetCommitSignature(), roster) - if err != nil { - return xerrors.Errorf("finalize failed: %v", err) - } - - if link.GetTo() == m.round.tentativeRound { - m.round.leader = m.round.tentativeLeader - dela.Logger.Info().Msgf("accepting to set leader to: %d", m.round.leader) - } - - m.round.views = nil - m.round.prevViews = nil - m.setState(InitialState) - - return nil -} - -// Watch implements pbft.StateMachine. It returns a channel that will be -// populated with stage changes. -func (m *pbftsm) Watch(ctx context.Context) <-chan State { - ch := make(chan State, 100) - - obs := observer{ch: ch} - m.watcher.Add(obs) - - go func() { - <-ctx.Done() - m.watcher.Remove(obs) - close(ch) - }() - - return ch -} - -func (m *pbftsm) verifyPrepare(tree hashtree.Tree, block types.Block, r *round, ro authority.Authority) error { - stageTree, err := tree.Stage(func(snap store.Snapshot) error { - txs := block.GetTransactions() - rejected := 0 - - res, err := m.val.Validate(snap, txs) - if err != nil { - return xerrors.Errorf("validation failed: %v", err) - } - - for _, r := range res.GetTransactionResults() { - accepted, reason := r.GetStatus() - if !accepted { - m.logger.Warn().Str("reason", reason).Msg("transaction not accepted") - rejected++ - } - } - - promTxs.Observe(float64(len(txs))) - promRejectedTxs.Observe(float64(rejected)) - - return nil - }) - - if err != nil { - return xerrors.Errorf("while updating tree: %v", err) - } - - root := types.Digest{} - copy(root[:], stageTree.GetRoot()) - - if root != block.GetTreeRoot() { - return xerrors.Errorf("mismatch tree root '%v' != '%v'", root, block.GetTreeRoot()) - } - - if m.blocks.Len() != block.GetIndex() { - return xerrors.Errorf("mismatch index %d != %d", block.GetIndex(), m.blocks.Len()) - } - - lastID, err := m.getLatestID() - if err != nil { - return xerrors.Errorf("couldn't get latest digest: %v", err) - } - - // The roster will be used to find the differential with the previous one so - // that the forward link can be populated. - roster, err := m.authReader(stageTree) - if err != nil { - return xerrors.Errorf("failed to read next roster: %v", err) - } - - changeset := ro.Diff(roster) - opts := []types.LinkOption{ - types.WithChangeSet(changeset), - types.WithLinkHashFactory(m.hashFac), - } - - link, err := types.NewForwardLink(lastID, block.GetHash(), opts...) - if err != nil { - return xerrors.Errorf("failed to create link: %v", err) - } - - r.id = link.GetHash() - r.tree = stageTree - r.block = block - r.changeset = changeset - - return nil -} - -func (m *pbftsm) verifyCommit(r *round, sig crypto.Signature, ro authority.Authority) error { - verifier, err := m.verifierFac.FromAuthority(ro) - if err != nil { - return xerrors.Errorf("couldn't make verifier: %v", err) - } - - err = verifier.Verify(r.id[:], sig) - if err != nil { - return xerrors.Errorf("verifier failed: %v", err) - } - - r.prepareSig = sig - - return nil -} - -func (m *pbftsm) verifyFinalize(r *round, sig crypto.Signature, ro authority.Authority) error { - verifier, err := m.verifierFac.FromAuthority(ro) - if err != nil { - return xerrors.Errorf("couldn't make verifier: %v", err) - } - - buffer, err := r.prepareSig.MarshalBinary() - if err != nil { - return xerrors.Errorf("couldn't marshal signature: %v", err) - } - - err = verifier.Verify(buffer, sig) - if err != nil { - return xerrors.Errorf("verifier failed: %v", err) - } - - lastID, err := m.getLatestID() - if err != nil { - return xerrors.Errorf("couldn't get latest digest: %v", err) - } - - // Persist to the database in a transaction so that it can revert to the - // previous state for either the tree or the block if something goes wrong. - err = m.db.Update(func(txn kv.WritableTx) error { - // 1. Persist the tree through the transaction and update the cache. - err := r.tree.WithTx(txn).Commit() - if err != nil { - return xerrors.Errorf("while committing tree: %v", err) - } - - var unlock func() - - txn.OnCommit(func() { - // The cache is updated only after both are committed with the tree - // using the database as the transaction is done. - unlock = m.tree.SetWithLock(r.tree) - }) - - // 2. Persist the block and its forward link. - opts := []types.LinkOption{ - types.WithSignatures(r.prepareSig, sig), - types.WithChangeSet(r.changeset), - types.WithLinkHashFactory(m.hashFac), - } - - link, err := types.NewBlockLink(lastID, r.block, opts...) - if err != nil { - return xerrors.Errorf("creating link: %v", err) - } - - err = m.blocks.WithTx(txn).Store(link) - if err != nil { - return xerrors.Errorf("store block: %v", err) - } - - // Only release the tree cache at the very end of the transaction, so - // that a call to get the tree will hold until the block is stored. - txn.OnCommit(func() { - promBlocks.Set(float64(m.blocks.Len())) - promLeader.Set(float64(m.round.leader)) - unlock() - }) - - return nil - }) - - if err != nil { - return xerrors.Errorf("database failed: %v", err) - } - - return nil -} - -func (m *pbftsm) init() (authority.Authority, error) { - roster, err := m.authReader(m.tree.Get()) - if err != nil { - return nil, xerrors.Errorf("failed to read roster: %v", err) - } - - if m.state != NoneState { - return roster, nil - } - - m.round.threshold = calculateThreshold(roster.Len()) - - m.setState(InitialState) - - return roster, nil -} - -func (m *pbftsm) setState(s State) { - m.state = s - m.watcher.Notify(s) -} - -func (m *pbftsm) checkViewChange(view View) { - if m.state == ViewChangeState && len(m.round.views) > m.round.threshold { - m.round.prevViews = m.round.views - m.round.views = nil - m.round.leader = view.leader - - if m.round.committed { - m.setState(CommitState) - } else { - m.setState(InitialState) - } - } -} - -func (m *pbftsm) getLatestID() (types.Digest, error) { - if m.blocks.Len() == 0 { - genesis, err := m.genesis.Get() - if err != nil { - return types.Digest{}, err - } - - return genesis.GetHash(), nil - } - - last, err := m.blocks.Last() - if err != nil { - return types.Digest{}, err - } - - return last.GetTo(), nil -} - -type observer struct { - ch chan State -} - -func (obs observer) NotifyCallback(event interface{}) { - obs.ch <- event.(State) -} - -// CalculateThreshold returns the number of messages that a node needs to -// receive before confirming the view change. The threshold is 2*f where f can -// be found with n = 3*f+1 where n is the number of participants. -func calculateThreshold(n int) int { - f := (n - 1) / 3 - if f == 0 { - return n - } - - return 2 * f -} diff --git a/dela/core/ordering/cosipbft/pbft/pbft_test.go b/dela/core/ordering/cosipbft/pbft/pbft_test.go deleted file mode 100644 index 6c453d6..0000000 --- a/dela/core/ordering/cosipbft/pbft/pbft_test.go +++ /dev/null @@ -1,975 +0,0 @@ -package pbft - -import ( - "context" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/store/hashtree/binprefix" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func TestState_String(t *testing.T) { - var state State = 99 - require.Equal(t, "unknown", state.String()) - - state = PrepareState - require.Equal(t, "prepare", state.String()) - - state = 0 - require.Equal(t, "none", state.String()) -} - -func TestStateMachine_GetState(t *testing.T) { - sm := &pbftsm{} - require.Equal(t, NoneState, sm.GetState()) - - sm.state = CommitState - require.Equal(t, CommitState, sm.GetState()) -} - -func TestStateMachine_GetLeader(t *testing.T) { - roster := fake.NewAuthority(3, fake.NewSigner) - - sm := &pbftsm{ - tree: blockstore.NewTreeCache(badTree{}), - authReader: func(hashtree.Tree) (authority.Authority, error) { - return authority.FromAuthority(roster), nil - }, - } - - leader, err := sm.GetLeader() - require.NoError(t, err) - require.Equal(t, roster.GetAddress(0), leader) - - sm.round.leader = 2 - leader, err = sm.GetLeader() - require.NoError(t, err) - require.Equal(t, roster.GetAddress(2), leader) - - sm.authReader = badReader - _, err = sm.GetLeader() - require.EqualError(t, err, fake.Err("failed to read roster")) -} - -func TestStateMachine_GetViews(t *testing.T) { - sm := &pbftsm{} - require.Len(t, sm.GetViews(), 0) - - sm.round.prevViews = map[mino.Address]View{ - fake.NewAddress(0): {}, - fake.NewAddress(1): {}, - } - require.Len(t, sm.GetViews(), 2) -} - -func TestStateMachine_GetCommit(t *testing.T) { - sm := &pbftsm{} - - id, block := sm.GetCommit() - require.Equal(t, types.Digest{}, id) - require.Equal(t, types.Block{}, block) - - block, err := types.NewBlock(simple.Result{}, types.WithIndex(1)) - require.NoError(t, err) - - sm.round.id = types.Digest{1} - sm.round.block = block - id, block = sm.GetCommit() - require.Equal(t, types.Digest{1}, id) - require.Equal(t, block, block) -} - -func TestStateMachine_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - param := StateMachineParam{ - Validation: simple.NewService(fakeExec{}, nil), - Blocks: blockstore.NewInMemory(), - Genesis: blockstore.NewGenesisStore(), - Tree: blockstore.NewTreeCache(tree), - AuthorityReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - DB: db, - } - - param.Genesis.Set(types.Genesis{}) - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root), types.WithIndex(0)) - require.NoError(t, err) - - from := fake.NewAddress(0) - - sm := NewStateMachine(param).(*pbftsm) - sm.state = InitialState - - id, err := sm.Prepare(from, block) - require.NoError(t, err) - require.NotEqual(t, types.Digest{}, id) - require.Equal(t, PrepareState, sm.state) - require.Equal(t, id, sm.round.id) - - id, err = sm.Prepare(from, block) - require.NoError(t, err) - require.Equal(t, sm.round.id, id) -} - -func TestStateMachine_WhileViewChange_Prepare(t *testing.T) { - sm := &pbftsm{ - state: ViewChangeState, - } - - _, err := sm.Prepare(fake.NewAddress(0), types.Block{}) - require.EqualError(t, err, "cannot be in view change state during prepare") -} - -func TestStateMachine_WrongLeader_Prepare(t *testing.T) { - tree, _, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: InitialState, - authReader: goodReader, - tree: blockstore.NewTreeCache(tree), - } - - link := makeLink(t) - - _, err := sm.Prepare(fake.NewAddress(1), link.GetBlock()) - require.EqualError(t, err, "'fake.Address[1]' is not the leader") -} - -func TestStateMachine_FailedValidation_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: InitialState, - val: badValidation{}, - tree: blockstore.NewTreeCache(tree), - db: db, - authReader: goodReader, - } - - link := makeLink(t) - - _, err := sm.Prepare(fake.NewAddress(0), link.GetBlock()) - require.EqualError(t, err, fake.Err("while updating tree: callback failed: validation failed")) -} - -func TestStateMachine_MismatchTreeRoot_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: InitialState, - val: badValidation{}, - tree: blockstore.NewTreeCache(tree), - db: db, - authReader: goodReader, - } - - other, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(types.Digest{})) - require.NoError(t, err) - - sm.val = unacceptedTxsValidation{} - _, err = sm.Prepare(fake.NewAddress(0), other) - require.EqualError(t, err, "mismatch tree root '71b6c1d5' != '00000000'") -} - -func TestStateMachine_MissingGenesis_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: InitialState, - val: simple.NewService(fakeExec{}, nil), - tree: blockstore.NewTreeCache(tree), - db: db, - authReader: goodReader, - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - } - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root)) - require.NoError(t, err) - - _, err = sm.Prepare(fake.NewAddress(0), block) - require.EqualError(t, err, "couldn't get latest digest: missing genesis block") -} - -func TestStateMachine_FailReadCurrentRoster_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: InitialState, - val: simple.NewService(fakeExec{}, nil), - tree: blockstore.NewTreeCache(tree), - db: db, - authReader: badReader, - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - } - - sm.genesis.Set(types.Genesis{}) - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root)) - require.NoError(t, err) - - _, err = sm.Prepare(fake.NewAddress(0), block) - require.EqualError(t, err, fake.Err("failed to read roster")) -} - -func TestStateMachine_FailReadRosterInStageTree_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - counter := fake.NewCounter(1) - - sm := &pbftsm{ - state: InitialState, - val: simple.NewService(fakeExec{}, nil), - tree: blockstore.NewTreeCache(tree), - db: db, - authReader: func(hashtree.Tree) (authority.Authority, error) { - if !counter.Done() { - counter.Decrease() - return authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)), nil - } - - return nil, fake.GetError() - }, - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - hashFac: crypto.NewSha256Factory(), - watcher: core.NewWatcher(), - } - - sm.genesis.Set(types.Genesis{}) - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root)) - require.NoError(t, err) - - // Failure to read the roster of the staging tree. - _, err = sm.Prepare(fake.NewAddress(0), block) - require.EqualError(t, err, fake.Err("failed to read next roster")) -} - -func TestStateMachine_FailCreateLink_Prepare(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: InitialState, - val: simple.NewService(fakeExec{}, nil), - tree: blockstore.NewTreeCache(tree), - db: db, - authReader: goodReader, - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - hashFac: fake.NewHashFactory(fake.NewBadHash()), - watcher: core.NewWatcher(), - } - - sm.genesis.Set(types.Genesis{}) - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root)) - require.NoError(t, err) - - _, err = sm.Prepare(fake.NewAddress(0), block) - require.EqualError(t, err, - fake.Err("failed to create link: failed to fingerprint: couldn't write from")) -} - -func TestStateMachine_Commit(t *testing.T) { - sm := &pbftsm{ - state: PrepareState, - verifierFac: fake.NewVerifierFactory(fake.Verifier{}), - watcher: core.NewWatcher(), - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - round: round{ - id: types.Digest{1}, - }, - } - - err := sm.Commit(types.Digest{1}, fake.Signature{}) - require.NoError(t, err) - require.Equal(t, CommitState, sm.state) - require.True(t, sm.round.committed) - require.NotNil(t, sm.round.prepareSig) -} - -func TestStateMachine_WhileViewChange_Commit(t *testing.T) { - sm := &pbftsm{ - state: ViewChangeState, - } - - err := sm.Commit(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, "cannot commit from viewchange state") -} - -func TestStateMachine_MismatchCandidate_Commit(t *testing.T) { - sm := &pbftsm{ - state: PrepareState, - round: round{ - id: types.Digest{1}, - }, - } - - err := sm.Commit(types.Digest{2}, fake.Signature{}) - require.EqualError(t, err, "mismatch id '02000000' != '01000000'") -} - -func TestStateMachine_FailCreateVerifier_Commit(t *testing.T) { - sm := &pbftsm{ - state: PrepareState, - verifierFac: fake.NewBadVerifierFactory(), - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - } - - err := sm.Commit(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("couldn't make verifier")) -} - -func TestStateMachine_WrongSignature_Commit(t *testing.T) { - sm := &pbftsm{ - state: PrepareState, - verifierFac: fake.NewVerifierFactory(fake.NewBadVerifier()), - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - } - - err := sm.Commit(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("verifier failed")) -} - -func TestStateMachine_FailReadCurrentRoster_Commit(t *testing.T) { - sm := &pbftsm{ - state: PrepareState, - tree: blockstore.NewTreeCache(badTree{}), - authReader: badReader, - } - - err := sm.Commit(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("failed to read roster")) -} - -func TestStateMachine_Finalize(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - param := StateMachineParam{ - VerifierFactory: fake.NewVerifierFactory(fake.Verifier{}), - Blocks: blockstore.NewInMemory(), - Genesis: blockstore.NewGenesisStore(), - Tree: blockstore.NewTreeCache(tree), - AuthorityReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - DB: db, - } - - param.Genesis.Set(types.Genesis{}) - - sm := NewStateMachine(param).(*pbftsm) - sm.state = CommitState - sm.round.tree = tree.(hashtree.StagingTree) - sm.round.prepareSig = fake.Signature{} - - err := sm.Finalize(types.Digest{1}, fake.Signature{}) - require.NoError(t, err) -} - -func TestStateMachine_NotCommitted_Finalize(t *testing.T) { - sm := &pbftsm{ - state: InitialState, - } - - err := sm.Finalize(types.Digest{1}, fake.Signature{}) - require.EqualError(t, err, "mismatch state initial != commit") -} - -func TestStateMachine_FailReadCurrentRoster_Finalize(t *testing.T) { - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(badTree{}), - authReader: badReader, - } - - err := sm.Finalize(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("failed to read roster")) -} - -func TestStateMachine_FailCreateVerifier_Finalize(t *testing.T) { - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - verifierFac: fake.NewBadVerifierFactory(), - } - - err := sm.Finalize(types.Digest{1}, fake.Signature{}) - require.EqualError(t, err, fake.Err("couldn't make verifier")) -} - -func TestStateMachine_WrongSignature_Finalize(t *testing.T) { - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - verifierFac: fake.NewVerifierFactory(fake.NewBadVerifier()), - round: round{ - prepareSig: fake.Signature{}, - }, - } - - err := sm.Finalize(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("verifier failed")) -} - -func TestStateMachine_MissingGenesis_Finalize(t *testing.T) { - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - verifierFac: fake.NewVerifierFactory(fake.Verifier{}), - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - round: round{ - prepareSig: fake.Signature{}, - }, - } - - err := sm.Finalize(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, "couldn't get latest digest: missing genesis block") -} - -func TestStateMachine_BadBlockStore_Finalize(t *testing.T) { - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(badTree{}), - authReader: goodReader, - verifierFac: fake.NewVerifierFactory(fake.Verifier{}), - genesis: blockstore.NewGenesisStore(), - blocks: badBlockStore{length: 1}, - round: round{ - prepareSig: fake.Signature{}, - }, - } - - err := sm.Finalize(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("couldn't get latest digest")) -} - -func TestStateMachine_FailCommitTree_Finalize(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(tree), - authReader: goodReader, - verifierFac: fake.NewVerifierFactory(fake.Verifier{}), - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - db: db, - round: round{ - prepareSig: fake.Signature{}, - tree: badTree{}, - }, - } - - sm.blocks.Store(makeLink(t)) - - err := sm.Finalize(types.Digest{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("database failed: while committing tree")) -} - -func TestStateMachine_FailCreateLink_Finalize(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(tree), - authReader: goodReader, - verifierFac: fake.NewVerifierFactory(fake.Verifier{}), - genesis: blockstore.NewGenesisStore(), - blocks: blockstore.NewInMemory(), - db: db, - hashFac: fake.NewHashFactory(fake.NewBadHash()), - round: round{ - prepareSig: fake.Signature{}, - tree: tree.(hashtree.StagingTree), - }, - } - - sm.blocks.Store(makeLink(t)) - - err := sm.Finalize(types.Digest{1}, fake.Signature{}) - require.Error(t, err) - require.Contains(t, err.Error(), "database failed: creating link:") -} - -func TestStateMachine_FailStoreBlock_Finalize(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - sm := &pbftsm{ - state: CommitState, - tree: blockstore.NewTreeCache(tree), - authReader: goodReader, - verifierFac: fake.NewVerifierFactory(fake.Verifier{}), - genesis: blockstore.NewGenesisStore(), - blocks: badBlockStore{}, - db: db, - hashFac: crypto.NewSha256Factory(), - round: round{ - prepareSig: fake.Signature{}, - tree: tree.(hashtree.StagingTree), - }, - } - - sm.genesis.Set(types.Genesis{}) - - err := sm.Finalize(types.Digest{1}, fake.Signature{}) - require.EqualError(t, err, fake.Err("database failed: store block")) -} - -func TestStateMachine_Accept(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(4, fake.NewSigner)) - - sm := &pbftsm{ - state: ViewChangeState, - blocks: blockstore.NewInMemory(), - genesis: blockstore.NewGenesisStore(), - watcher: core.NewWatcher(), - signer: fake.NewSigner(), - tree: blockstore.NewTreeCache(badTree{}), - authReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - } - - sm.genesis.Set(types.Genesis{}) - sm.round.threshold = 2 - - err := sm.Accept(View{from: fake.NewAddress(0), leader: 1}) - require.NoError(t, err) - require.Equal(t, 2, sm.round.threshold) - require.Len(t, sm.round.views, 1) - - err = sm.Accept(View{from: fake.NewAddress(1), leader: 1}) - require.NoError(t, err) - require.Len(t, sm.round.views, 2) - - // Ignore view for the same leader. - err = sm.Accept(View{from: fake.NewAddress(2), leader: 0}) - require.NoError(t, err) - require.Len(t, sm.round.views, 2) - - // Ignore duplicate. - err = sm.Accept(View{from: fake.NewAddress(0), leader: 1}) - require.NoError(t, err) - require.Len(t, sm.round.views, 2) - - // Finalize view change from a commit state - sm.round.committed = true - err = sm.Accept(View{from: fake.NewAddress(3), leader: 1}) - require.NoError(t, err) - require.Equal(t, CommitState, sm.state) - - // Ignore views for a different leader than the next one. - err = sm.Accept(View{from: fake.NewAddress(2), leader: 5}) - require.EqualError(t, err, "invalid view: mismatch leader 5 != 2") - require.Len(t, sm.round.views, 0) - - sm.genesis = blockstore.NewGenesisStore() - err = sm.Accept(View{from: fake.NewAddress(0), leader: 2}) - require.EqualError(t, err, "invalid view: failed to read latest id: missing genesis block") - - // Only accept views for the current round ID. - sm.genesis.Set(types.Genesis{}) - err = sm.Accept(View{from: fake.NewAddress(3), leader: 2, id: types.Digest{1}}) - require.EqualError(t, err, "invalid view: mismatch id 01000000 != 00000000") - - sm.authReader = badReader - err = sm.Accept(View{leader: 2}) - require.EqualError(t, err, fake.Err("init: failed to read roster")) - - // Ignore view with an invalid signature. - sm.state = InitialState - sm.authReader = func(hashtree.Tree) (authority.Authority, error) { - ro := authority.New( - []mino.Address{fake.NewAddress(0)}, - []crypto.PublicKey{fake.NewBadPublicKey()}, - ) - return ro, nil - } - err = sm.Accept(View{from: fake.NewAddress(0), leader: 2}) - require.EqualError(t, err, fake.Err("invalid view: invalid signature: verify")) -} - -func TestStateMachine_verifyViews(t *testing.T) { - sm := &pbftsm{ - tree: blockstore.NewTreeCache(badTree{}), - authReader: badReader, - } - - err := sm.verifyViews(false) - require.EqualError(t, err, fake.Err("failed to read roster")) -} - -func TestStateMachine_AcceptAll(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(4, fake.NewSigner)) - - sm := &pbftsm{ - blocks: blockstore.NewInMemory(), - genesis: blockstore.NewGenesisStore(), - watcher: core.NewWatcher(), - signer: fake.NewSigner(), - tree: blockstore.NewTreeCache(badTree{}), - authReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - } - - sm.genesis.Set(types.Genesis{}) - - err := sm.AcceptAll([]View{ - {from: fake.NewAddress(0), leader: 5}, - {from: fake.NewAddress(1), leader: 5}, - {from: fake.NewAddress(2), leader: 5}, - }) - require.NoError(t, err) - require.Equal(t, 2, sm.round.threshold) - require.Equal(t, uint16(5), sm.round.leader) - require.Equal(t, InitialState, sm.state) - require.Nil(t, sm.round.views) - require.Len(t, sm.round.prevViews, 3) - - sm.round.threshold = 0 - err = sm.AcceptAll([]View{{leader: 5}}) - require.NoError(t, err) - - // Only accept if there are enough views. - err = sm.AcceptAll([]View{}) - require.EqualError(t, err, "not enough views: 0 <= 0") - - err = sm.AcceptAll([]View{{from: fake.NewAddress(4), leader: 6}}) - require.EqualError(t, err, "invalid view: unknown peer: fake.Address[4]") - - sm.state = NoneState - sm.authReader = badReader - err = sm.AcceptAll([]View{{}}) - require.EqualError(t, err, fake.Err("init: failed to read roster")) -} - -func TestStateMachine_Expire(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(4, fake.NewSigner)) - - sm := &pbftsm{ - watcher: core.NewWatcher(), - blocks: blockstore.NewInMemory(), - genesis: blockstore.NewGenesisStore(), - signer: bls.NewSigner(), - tree: blockstore.NewTreeCache(badTree{}), - authReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - } - - sm.genesis.Set(types.Genesis{}) - - view, err := sm.Expire(fake.NewAddress(0)) - require.NoError(t, err) - require.Equal(t, 2, sm.round.threshold) - require.Equal(t, uint16(1), view.leader) - require.NoError(t, view.Verify(sm.signer.GetPublicKey())) - - sm.signer = fake.NewBadSigner() - _, err = sm.Expire(fake.NewAddress(0)) - require.EqualError(t, err, fake.Err("create view: signer")) - - sm.signer = fake.NewSigner() - sm.genesis = blockstore.NewGenesisStore() - _, err = sm.Expire(fake.NewAddress(0)) - require.EqualError(t, err, "couldn't get latest digest: missing genesis block") - - sm.authReader = badReader - sm.state = NoneState - _, err = sm.Expire(fake.NewAddress(0)) - require.EqualError(t, err, fake.Err("init: failed to read roster")) -} - -func TestStateMachine_CatchUp(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - param := StateMachineParam{ - Validation: simple.NewService(fakeExec{}, nil), - VerifierFactory: fake.VerifierFactory{}, - Blocks: blockstore.NewInMemory(), - Genesis: blockstore.NewGenesisStore(), - Tree: blockstore.NewTreeCache(tree), - AuthorityReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - DB: db, - } - - param.Genesis.Set(types.Genesis{}) - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root), types.WithIndex(0)) - require.NoError(t, err) - - sm := NewStateMachine(param).(*pbftsm) - - opts := []types.LinkOption{ - types.WithSignatures(fake.Signature{}, fake.Signature{}), - types.WithChangeSet(authority.NewChangeSet()), - } - - link, err := types.NewBlockLink(types.Digest{}, block, opts...) - require.NoError(t, err) - - err = sm.CatchUp(link) - require.NoError(t, err) - - sm.state = CommitState - sm.round.id = types.Digest{} - err = sm.CatchUp(link) - require.EqualError(t, err, "already committed to '00000000'") - - sm.state = InitialState - sm.round.id = link.GetHash() - err = sm.CatchUp(link) - require.EqualError(t, err, "prepare failed: mismatch index 0 != 1") - - sm.authReader = badReader - err = sm.CatchUp(link) - require.EqualError(t, err, fake.Err("failed to read roster")) - - sm.authReader = param.AuthorityReader - sm.blocks = blockstore.NewInMemory() - sm.verifierFac = fake.NewVerifierFactory(fake.NewBadVerifier()) - err = sm.CatchUp(link) - require.EqualError(t, err, fake.Err("commit failed: verifier failed")) - - opts = []types.LinkOption{ - types.WithSignatures(fake.NewBadSignature(), fake.Signature{}), - types.WithChangeSet(authority.NewChangeSet()), - } - - link, err = types.NewBlockLink(types.Digest{}, block, opts...) - require.NoError(t, err) - sm.verifierFac = fake.VerifierFactory{} - err = sm.CatchUp(link) - require.EqualError(t, err, fake.Err("finalize failed: couldn't marshal signature")) -} - -// checks that the tentative leader is set in case the tentative round is equal -// to the proposed block. -func TestStateMachine_CatchUp_Tentative_Leader_Accept(t *testing.T) { - tree, db, clean := makeTree(t) - defer clean() - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - param := StateMachineParam{ - Validation: simple.NewService(fakeExec{}, nil), - VerifierFactory: fake.VerifierFactory{}, - Blocks: blockstore.NewInMemory(), - Genesis: blockstore.NewGenesisStore(), - Tree: blockstore.NewTreeCache(tree), - AuthorityReader: func(hashtree.Tree) (authority.Authority, error) { - return ro, nil - }, - DB: db, - } - - param.Genesis.Set(types.Genesis{}) - - root := types.Digest{} - copy(root[:], tree.GetRoot()) - - block, err := types.NewBlock(simple.NewResult(nil), types.WithTreeRoot(root), types.WithIndex(0)) - require.NoError(t, err) - - sm := NewStateMachine(param).(*pbftsm) - - opts := []types.LinkOption{ - types.WithSignatures(fake.Signature{}, fake.Signature{}), - types.WithChangeSet(authority.NewChangeSet()), - } - - link, err := types.NewBlockLink(types.Digest{}, block, opts...) - require.NoError(t, err) - - tentativeLeader := uint16(9) - sm.round.tentativeRound = link.GetTo() - sm.round.tentativeLeader = tentativeLeader - - err = sm.CatchUp(link) - require.NoError(t, err) - - require.Equal(t, tentativeLeader, sm.round.leader) -} - -func TestStateMachine_Watch(t *testing.T) { - sm := &pbftsm{ - watcher: core.NewWatcher(), - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - statesCh := sm.Watch(ctx) - - sm.setState(ViewChangeState) - state := <-statesCh - require.Equal(t, ViewChangeState, state) - - cancel() - _, more := <-statesCh - require.False(t, more) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeTree(t *testing.T) (hashtree.Tree, kv.DB, func()) { - dir, err := os.MkdirTemp(os.TempDir(), "pbft") - require.NoError(t, err) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - tree := binprefix.NewMerkleTree(db, binprefix.Nonce{}) - stage, err := tree.Stage(func(store.Snapshot) error { return nil }) - require.NoError(t, err) - - return stage, db, func() { os.RemoveAll(dir) } -} - -func makeLink(t *testing.T) types.BlockLink { - block, err := types.NewBlock(simple.NewResult(nil)) - require.NoError(t, err) - - link, err := types.NewBlockLink(types.Digest{}, block) - require.NoError(t, err) - - return link -} - -type fakeExec struct { - err error -} - -func (e fakeExec) Execute(store.Snapshot, execution.Step) (execution.Result, error) { - return execution.Result{}, e.err -} - -type badValidation struct { - validation.Service -} - -func (v badValidation) Validate(store.Snapshot, []txn.Transaction) (validation.Result, error) { - return nil, fake.GetError() -} - -type unacceptedTxsValidation struct { - validation.Service -} - -func (v unacceptedTxsValidation) Validate(store.Snapshot, []txn.Transaction) (validation.Result, error) { - return simple.NewResult([]simple.TransactionResult{ - simple.NewTransactionResult(nil, false, "unaccepted"), - }), nil -} - -type badBlockStore struct { - blockstore.BlockStore - length uint64 -} - -func (s badBlockStore) WithTx(store.Transaction) blockstore.BlockStore { - return s -} - -func (s badBlockStore) Len() uint64 { - return s.length -} - -func (s badBlockStore) Last() (types.BlockLink, error) { - return nil, fake.GetError() -} - -func (s badBlockStore) Store(types.BlockLink) error { - return fake.GetError() -} - -type badTree struct { - hashtree.StagingTree -} - -func (t badTree) WithTx(store.Transaction) hashtree.StagingTree { - return t -} - -func (t badTree) Commit() error { - return fake.GetError() -} - -func goodReader(hashtree.Tree) (authority.Authority, error) { - return authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)), nil -} - -func badReader(hashtree.Tree) (authority.Authority, error) { - return nil, fake.GetError() -} diff --git a/dela/core/ordering/cosipbft/pbft/view.go b/dela/core/ordering/cosipbft/pbft/view.go deleted file mode 100644 index bf1249d..0000000 --- a/dela/core/ordering/cosipbft/pbft/view.go +++ /dev/null @@ -1,97 +0,0 @@ -// This file contains the implementation of the views sent by the nodes during a -// view change. -// -// Documentation Last Review: 13.10.2020 -// - -package pbft - -import ( - "encoding/binary" - - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -// View is the view change request sent to other participants. -type View struct { - from mino.Address - id types.Digest - leader uint16 - signature crypto.Signature -} - -// ViewParam contains the parameters to create a view. -type ViewParam struct { - From mino.Address - ID types.Digest - Leader uint16 -} - -// NewView creates a new view. -func NewView(param ViewParam, sig crypto.Signature) View { - return View{ - from: param.From, - id: param.ID, - leader: param.Leader, - signature: sig, - } -} - -// NewViewAndSign creates a new view and uses the signer to make the signature -// that will be verified by other participants. -func NewViewAndSign(param ViewParam, signer crypto.Signer) (View, error) { - view := View{ - from: param.From, - id: param.ID, - leader: param.Leader, - } - - sig, err := signer.Sign(view.bytes()) - if err != nil { - return view, xerrors.Errorf("signer: %v", err) - } - - view.signature = sig - - return view, nil -} - -// GetFrom returns the address the view is coming from. -func (v View) GetFrom() mino.Address { - return v.from -} - -// GetID returns the block digest the view is targeting. -func (v View) GetID() types.Digest { - return v.id -} - -// GetLeader returns the index of the leader proposed by the view. -func (v View) GetLeader() uint16 { - return v.leader -} - -// GetSignature returns the signature of the view. -func (v View) GetSignature() crypto.Signature { - return v.signature -} - -// Verify takes the public key to verify the signature of the view. -func (v View) Verify(pubkey crypto.PublicKey) error { - err := pubkey.Verify(v.bytes(), v.signature) - if err != nil { - return xerrors.Errorf("verify: %v", err) - } - - return nil -} - -func (v View) bytes() []byte { - buffer := make([]byte, 2) - binary.LittleEndian.PutUint16(buffer, v.leader) - - return append(buffer, v.id.Bytes()...) -} diff --git a/dela/core/ordering/cosipbft/pbft/view_test.go b/dela/core/ordering/cosipbft/pbft/view_test.go deleted file mode 100644 index 236f60f..0000000 --- a/dela/core/ordering/cosipbft/pbft/view_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package pbft - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestView_Getters(t *testing.T) { - param := ViewParam{ - From: fake.NewAddress(0), - ID: types.Digest{1}, - Leader: 5, - } - - view := NewView(param, fake.Signature{}) - - require.Equal(t, fake.NewAddress(0), view.GetFrom()) - require.Equal(t, types.Digest{1}, view.GetID()) - require.Equal(t, uint16(5), view.GetLeader()) - require.Equal(t, fake.Signature{}, view.GetSignature()) -} - -func TestView_Verify(t *testing.T) { - param := ViewParam{ - From: fake.NewAddress(0), - ID: types.Digest{2}, - Leader: 3, - } - - signer := bls.NewSigner() - - view, err := NewViewAndSign(param, signer) - require.NoError(t, err) - require.NoError(t, view.Verify(signer.GetPublicKey())) - - _, err = NewViewAndSign(param, fake.NewBadSigner()) - require.EqualError(t, err, fake.Err("signer")) - - err = view.Verify(fake.NewBadPublicKey()) - require.EqualError(t, err, fake.Err("verify")) -} diff --git a/dela/core/ordering/cosipbft/proc.go b/dela/core/ordering/cosipbft/proc.go deleted file mode 100644 index 5c24cae..0000000 --- a/dela/core/ordering/cosipbft/proc.go +++ /dev/null @@ -1,257 +0,0 @@ -// Theis file contains the network message handler implementations for the -// collective signing reactor and the module rpc. -// -// Documentation Last Review: 12.10.2020 -// - -package cosipbft - -import ( - "context" - - "github.com/rs/zerolog" - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync" - "go.dedis.ch/dela/core/ordering/cosipbft/contracts/viewchange" - "go.dedis.ch/dela/core/ordering/cosipbft/pbft" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -var ( - keyRoster = [32]byte{} - keyAccess = [32]byte{1} -) - -// Processor processes the messages to run a collective signing PBFT consensus. -// -// - implements cosi.Reactor -// - implements mino.Handler -type processor struct { - mino.UnsupportedHandler - types.MessageFactory - - logger zerolog.Logger - pbftsm pbft.StateMachine - sync blocksync.Synchronizer - tree blockstore.TreeCache - pool pool.Pool - watcher core.Observable - rosterFac authority.Factory - hashFactory crypto.HashFactory - access access.Service - - context serde.Context - genesis blockstore.GenesisStore - blocks blockstore.BlockStore - - started chan struct{} -} - -func newProcessor() *processor { - return &processor{ - watcher: core.NewWatcher(), - context: json.NewContext(), - started: make(chan struct{}), - } -} - -// Invoke implements cosi.Reactor. It processes the messages from the collective -// signature module. The messages are either from the the prepare or the commit -// phase. -func (h *processor) Invoke(from mino.Address, msg serde.Message) ([]byte, error) { - switch in := msg.(type) { - case types.BlockMessage: - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - blocks := h.blocks.Watch(ctx) - - // In case the node is falling behind the chain, it gives it a chance to - // catch up before moving forward. - latest := h.sync.GetLatest() - - if latest > h.blocks.Len() { - for link := range blocks { - if link.GetBlock().GetIndex() >= latest { - cancel() - } - } - } - - viewMsgs := in.GetViews() - if len(viewMsgs) > 0 { - h.logger.Debug().Int("num", len(viewMsgs)).Msg("process views") - - views := make([]pbft.View, 0, len(viewMsgs)) - for addr, view := range viewMsgs { - param := pbft.ViewParam{ - From: addr, - ID: view.GetID(), - Leader: view.GetLeader(), - } - - views = append(views, pbft.NewView(param, view.GetSignature())) - } - - // Force a view change if enough views are provided in the situation - // where the current node is falling behind the others. - err := h.pbftsm.AcceptAll(views) - if err != nil { - return nil, xerrors.Errorf("accept all: %v", err) - } - } - - digest, err := h.pbftsm.Prepare(from, in.GetBlock()) - if err != nil { - return nil, xerrors.Errorf("pbft prepare failed: %v", err) - } - - return digest[:], nil - case types.CommitMessage: - err := h.pbftsm.Commit(in.GetID(), in.GetSignature()) - if err != nil { - h.logger.Debug().Msg("commit failed") - - return nil, xerrors.Errorf("pbft commit failed: %v", err) - } - - buffer, err := in.GetSignature().MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal signature: %v", err) - } - - return buffer, nil - default: - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } -} - -// Process implements mino.Handler. It processes the messages from the RPC. -func (h *processor) Process(req mino.Request) (serde.Message, error) { - switch msg := req.Message.(type) { - case types.GenesisMessage: - if h.genesis.Exists() { - return nil, nil - } - - root := msg.GetGenesis().GetRoot() - - return nil, h.storeGenesis(msg.GetGenesis().GetRoster(), &root) - case types.DoneMessage: - err := h.pbftsm.Finalize(msg.GetID(), msg.GetSignature()) - if err != nil { - return nil, xerrors.Errorf("pbftsm finalized failed: %v", err) - } - case types.ViewMessage: - param := pbft.ViewParam{ - From: req.Address, - ID: msg.GetID(), - Leader: msg.GetLeader(), - } - - err := h.pbftsm.Accept(pbft.NewView(param, msg.GetSignature())) - if err != nil { - h.logger.Warn().Err(err).Msg("view message refused") - } - default: - return nil, xerrors.Errorf("unsupported message of type '%T'", req.Message) - } - - return nil, nil -} - -func (h *processor) getCurrentRoster() (authority.Authority, error) { - return h.readRoster(h.tree.Get()) -} - -func (h *processor) readRoster(tree hashtree.Tree) (authority.Authority, error) { - data, err := tree.Get(keyRoster[:]) - if err != nil { - return nil, xerrors.Errorf("read from tree: %v", err) - } - - roster, err := h.rosterFac.AuthorityOf(h.context, data) - if err != nil { - return nil, xerrors.Errorf("decode failed: %v", err) - } - - return roster, nil -} - -func (h *processor) storeGenesis(roster authority.Authority, match *types.Digest) error { - value, err := roster.Serialize(h.context) - if err != nil { - return xerrors.Errorf("failed to serialize roster: %v", err) - } - - stageTree, err := h.tree.Get().Stage(func(snap store.Snapshot) error { - err := h.makeAccess(snap, roster) - if err != nil { - return xerrors.Errorf("failed to set access: %v", err) - } - - err = snap.Set(keyRoster[:], value) - if err != nil { - return xerrors.Errorf("failed to store roster: %v", err) - } - - return nil - }) - if err != nil { - return xerrors.Errorf("while updating tree: %v", err) - } - - root := types.Digest{} - copy(root[:], stageTree.GetRoot()) - - if match != nil && *match != root { - return xerrors.Errorf("mismatch tree root '%v' != '%v'", match, root) - } - - genesis, err := types.NewGenesis(roster, types.WithGenesisRoot(root)) - if err != nil { - return xerrors.Errorf("creating genesis: %v", err) - } - - err = stageTree.Commit() - if err != nil { - return xerrors.Errorf("tree commit failed: %v", err) - } - - h.tree.Set(stageTree) - - err = h.genesis.Set(genesis) - if err != nil { - return xerrors.Errorf("set genesis failed: %v", err) - } - - close(h.started) - - return nil -} - -func (h *processor) makeAccess(store store.Snapshot, roster authority.Authority) error { - creds := viewchange.NewCreds(keyAccess[:]) - - iter := roster.PublicKeyIterator() - for iter.HasNext() { - // Grant each member of the roster an access to change the roster. - err := h.access.Grant(store, creds, iter.GetNext()) - if err != nil { - return err - } - } - - return nil -} diff --git a/dela/core/ordering/cosipbft/proc_test.go b/dela/core/ordering/cosipbft/proc_test.go deleted file mode 100644 index df4b3c3..0000000 --- a/dela/core/ordering/cosipbft/proc_test.go +++ /dev/null @@ -1,343 +0,0 @@ -package cosipbft - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/blocksync" - "go.dedis.ch/dela/core/ordering/cosipbft/pbft" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde/json" -) - -func TestProcessor_BlockMessage_Invoke(t *testing.T) { - expected := types.Digest{1} - - proc := newProcessor() - proc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - proc.sync = fakeSync{latest: 1} - proc.blocks = fakeStore{} - proc.pbftsm = fakeSM{ - state: pbft.InitialState, - id: expected, - } - - msg := types.NewBlockMessage(types.Block{}, nil) - - id, err := proc.Invoke(fake.NewAddress(0), msg) - require.NoError(t, err) - require.Equal(t, expected[:], id) - - proc.pbftsm = fakeSM{state: pbft.InitialState, err: fake.GetError()} - _, err = proc.Invoke(fake.NewAddress(0), msg) - require.EqualError(t, err, fake.Err("pbft prepare failed")) - - views := map[mino.Address]types.ViewMessage{fake.NewAddress(0): {}} - msg = types.NewBlockMessage(types.Block{}, views) - proc.pbftsm = fakeSM{err: fake.GetError()} - _, err = proc.Invoke(fake.NewAddress(0), msg) - require.EqualError(t, err, fake.Err("accept all")) -} - -func TestProcessor_CommitMessage_Invoke(t *testing.T) { - proc := newProcessor() - proc.pbftsm = fakeSM{} - - msg := types.NewCommit(types.Digest{1}, fake.Signature{}) - - id, err := proc.Invoke(fake.NewAddress(0), msg) - require.NoError(t, err) - require.Equal(t, []byte{0xfe}, id) - - proc.pbftsm = fakeSM{err: fake.GetError()} - _, err = proc.Invoke(fake.NewAddress(0), msg) - require.EqualError(t, err, fake.Err("pbft commit failed")) - - proc.pbftsm = fakeSM{} - msg = types.NewCommit(types.Digest{}, fake.NewBadSignature()) - _, err = proc.Invoke(fake.NewAddress(0), msg) - require.EqualError(t, err, fake.Err("couldn't marshal signature")) - - _, err = proc.Invoke(fake.NewAddress(0), fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") -} - -func TestProcessor_GenesisMessage_Process(t *testing.T) { - proc := newProcessor() - proc.tree = blockstore.NewTreeCache(fakeTree{}) - proc.genesis = blockstore.NewGenesisStore() - proc.access = fakeAccess{} - - root := types.Digest{} - copy(root[:], []byte("root")) - - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := types.NewGenesis(ro, types.WithGenesisRoot(root)) - require.NoError(t, err) - - req := mino.Request{ - Message: types.NewGenesisMessage(genesis), - } - - msg, err := proc.Process(req) - require.NoError(t, err) - require.Nil(t, msg) - - proc.genesis = blockstore.NewGenesisStore() - proc.context = fake.NewContext() - _, err = proc.Process(req) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to serialize roster: couldn't encode roster: ") - - wrongGenesis, err := types.NewGenesis(ro) - require.NoError(t, err) - - proc.context = json.NewContext() - _, err = proc.Process(mino.Request{Message: types.NewGenesisMessage(wrongGenesis)}) - require.EqualError(t, err, "mismatch tree root '00000000' != '726f6f74'") - - proc.access = fakeAccess{err: fake.GetError()} - _, err = proc.Process(req) - require.EqualError(t, err, fake.Err("while updating tree: failed to set access")) - - proc.access = fakeAccess{} - proc.tree = blockstore.NewTreeCache(fakeTree{errStore: fake.GetError()}) - _, err = proc.Process(req) - require.EqualError(t, err, fake.Err("while updating tree: failed to store roster")) - - proc.tree = blockstore.NewTreeCache(fakeTree{errCommit: fake.GetError()}) - _, err = proc.Process(req) - require.EqualError(t, err, fake.Err("tree commit failed")) - - proc.tree = blockstore.NewTreeCache(fakeTree{}) - proc.genesis = fakeGenesisStore{errSet: fake.GetError()} - _, err = proc.Process(req) - require.EqualError(t, err, fake.Err("set genesis failed")) -} - -func TestProcessor_DoneMessage_Process(t *testing.T) { - proc := newProcessor() - proc.pbftsm = fakeSM{} - proc.blocks = blockstore.NewInMemory() - proc.blocks.Store(makeBlock(t, types.Digest{})) - - req := mino.Request{ - Message: types.NewDone(types.Digest{}, fake.Signature{}), - } - - resp, err := proc.Process(req) - require.NoError(t, err) - require.Nil(t, resp) - - proc.pbftsm = fakeSM{err: fake.GetError()} - _, err = proc.Process(req) - require.EqualError(t, err, fake.Err("pbftsm finalized failed")) -} - -func TestProcessor_ViewMessage_Process(t *testing.T) { - proc := newProcessor() - proc.pbftsm = fakeSM{} - - req := mino.Request{ - Message: types.NewViewMessage(types.Digest{}, 0, fake.Signature{}), - } - - resp, err := proc.Process(req) - require.NoError(t, err) - require.Nil(t, resp) - - proc.pbftsm = fakeSM{err: fake.GetError()} - _, err = proc.Process(req) - require.NoError(t, err) -} - -func TestProcessor_Unsupported_Process(t *testing.T) { - proc := newProcessor() - - req := mino.Request{Message: fake.Message{}} - - _, err := proc.Process(req) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeBlock(t *testing.T, from types.Digest, opts ...types.LinkOption) types.BlockLink { - block, err := types.NewBlock(simple.NewResult(nil)) - require.NoError(t, err) - - link, err := types.NewBlockLink(from, block, opts...) - require.NoError(t, err) - - return link -} - -type fakeSM struct { - pbft.StateMachine - - err error - errLeader error - state pbft.State - id types.Digest - ch chan pbft.State -} - -func (sm fakeSM) GetState() pbft.State { - return sm.state -} - -func (sm fakeSM) GetLeader() (mino.Address, error) { - return fake.NewAddress(0), sm.errLeader -} - -func (sm fakeSM) GetViews() map[mino.Address]pbft.View { - return nil -} - -func (sm fakeSM) PrePrepare(authority.Authority) error { - return sm.err -} - -func (sm fakeSM) Prepare(mino.Address, types.Block) (types.Digest, error) { - return sm.id, sm.err -} - -func (sm fakeSM) Commit(types.Digest, crypto.Signature) error { - return sm.err -} - -func (sm fakeSM) Finalize(types.Digest, crypto.Signature) error { - return sm.err -} - -func (sm fakeSM) Expire(mino.Address) (pbft.View, error) { - return pbft.View{}, sm.err -} - -func (sm fakeSM) Accept(pbft.View) error { - return sm.err -} - -func (sm fakeSM) AcceptAll([]pbft.View) error { - return sm.err -} - -func (sm fakeSM) Watch(context.Context) <-chan pbft.State { - return sm.ch -} - -type fakeSync struct { - blocksync.Synchronizer - - latest uint64 - err error -} - -func (sync fakeSync) GetLatest() uint64 { - return sync.latest -} - -func (sync fakeSync) Sync(ctx context.Context, players mino.Players, cfg blocksync.Config) error { - return sync.err -} - -type fakeSnapshot struct { - store.Snapshot - - err error -} - -func (snap fakeSnapshot) Get(key []byte) ([]byte, error) { - return []byte{}, snap.err -} - -func (snap fakeSnapshot) Set(key []byte, value []byte) error { - return snap.err -} - -func (snap fakeSnapshot) Delete(key []byte) error { - return snap.err -} - -type fakeTree struct { - hashtree.StagingTree - - err error - errStage error - errCommit error - errStore error -} - -func (t fakeTree) GetRoot() []byte { - return []byte("root") -} - -func (t fakeTree) GetPath(key []byte) (hashtree.Path, error) { - return nil, t.err -} - -func (t fakeTree) Get(key []byte) ([]byte, error) { - return []byte("[]"), t.err -} - -func (t fakeTree) Stage(fn func(store.Snapshot) error) (hashtree.StagingTree, error) { - err := fn(fakeSnapshot{err: t.errStore}) - if err != nil { - return nil, err - } - - return t, t.errStage -} - -func (t fakeTree) Commit() error { - return t.errCommit -} - -type fakeGenesisStore struct { - blockstore.GenesisStore - - errGet error - errSet error -} - -func (s fakeGenesisStore) Exists() bool { - return false -} - -func (s fakeGenesisStore) Get() (types.Genesis, error) { - return types.Genesis{}, s.errGet -} - -func (s fakeGenesisStore) Set(types.Genesis) error { - return s.errSet -} - -type fakeStore struct { - blockstore.BlockStore -} - -func (fakeStore) Len() uint64 { - return 0 -} - -func (fakeStore) Watch(context.Context) <-chan types.BlockLink { - ch := make(chan types.BlockLink, 1) - - block, _ := types.NewBlock(simple.NewResult(nil), types.WithIndex(1)) - link, _ := types.NewBlockLink(types.Digest{}, block) - ch <- link - close(ch) - - return ch -} diff --git a/dela/core/ordering/cosipbft/proof.go b/dela/core/ordering/cosipbft/proof.go deleted file mode 100644 index 41ef6ed..0000000 --- a/dela/core/ordering/cosipbft/proof.go +++ /dev/null @@ -1,66 +0,0 @@ -// This file contains the implementation of a proof for this ordering service. -// -// Documentation Last Review: 12.10.2020 -// - -package cosipbft - -import ( - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -// Proof is a combination of elements that will prove the inclusion or the -// absence of a key/value pair in the given block. -// -// - implements ordering.Proof -type Proof struct { - path hashtree.Path - chain types.Chain -} - -func newProof(path hashtree.Path, chain types.Chain) Proof { - return Proof{ - path: path, - chain: chain, - } -} - -// GetKey implements ordering.Proof. It returns the key associated to the proof. -func (p Proof) GetKey() []byte { - return p.path.GetKey() -} - -// GetValue implements ordering.Proof. It returns the value associated to the -// proof if the key exists, otherwise it returns nil. -func (p Proof) GetValue() []byte { - return p.path.GetValue() -} - -// Verify takes the genesis block and the verifier factory to verify the chain -// up to the latest block. It verifies the whole chain. -func (p Proof) Verify(genesis types.Genesis, fac crypto.VerifierFactory) error { - err := p.chain.Verify(genesis, genesis.GetHash(), fac) - if err != nil { - return xerrors.Errorf("failed to verify chain: %v", err) - } - - last := p.chain.GetBlock() - - // The path object is transmitted with enough information so that when it is - // instanciated, it can calculate the Merkle root. It is therefore - // unnecessary to do it again here. - root := types.Digest{} - copy(root[:], p.path.GetRoot()) - - // The Merkle root must match the one stored in the block to prove that the - // chain is correct. - if last.GetTreeRoot() != root { - return xerrors.Errorf("mismatch tree root: '%v' != '%v'", - last.GetTreeRoot(), root) - } - - return nil -} diff --git a/dela/core/ordering/cosipbft/proof_test.go b/dela/core/ordering/cosipbft/proof_test.go deleted file mode 100644 index 6bd3cea..0000000 --- a/dela/core/ordering/cosipbft/proof_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package cosipbft - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestProof_GetKey(t *testing.T) { - p := Proof{ - path: fakePath{}, - } - - require.Equal(t, []byte("key"), p.GetKey()) -} - -func TestProof_GetValue(t *testing.T) { - p := Proof{ - path: fakePath{}, - } - - require.Equal(t, []byte("value"), p.GetValue()) -} - -func TestProof_Verify(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := types.NewGenesis(ro) - require.NoError(t, err) - - block, err := types.NewBlock(simple.NewResult(nil)) - require.NoError(t, err) - - p := Proof{ - path: fakePath{}, - chain: fakeChain{block: block}, - } - - err = p.Verify(genesis, fake.VerifierFactory{}) - require.EqualError(t, err, "mismatch tree root: '00000000' != '01020300'") - - p.chain = fakeChain{err: fake.GetError()} - err = p.Verify(genesis, fake.VerifierFactory{}) - require.EqualError(t, err, fake.Err("failed to verify chain")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakePath struct { - hashtree.Path -} - -func (p fakePath) GetKey() []byte { - return []byte("key") -} - -func (p fakePath) GetValue() []byte { - return []byte("value") -} - -func (p fakePath) GetRoot() []byte { - return types.Digest{1, 2, 3}.Bytes() -} - -type fakeChain struct { - types.Chain - - block types.Block - err error -} - -func (c fakeChain) GetBlock() types.Block { - return c.block -} - -func (c fakeChain) Verify(types.Genesis, types.Digest, crypto.VerifierFactory) error { - return c.err -} diff --git a/dela/core/ordering/cosipbft/types/block.go b/dela/core/ordering/cosipbft/types/block.go deleted file mode 100644 index d7a5912..0000000 --- a/dela/core/ordering/cosipbft/types/block.go +++ /dev/null @@ -1,351 +0,0 @@ -// This file implements the block and the link that will form a chain. -// -// Documentation Last review: 13.10.2020 -// - -package types - -import ( - "encoding/binary" - "fmt" - "io" - - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var ( - genesisFormats = registry.NewSimpleRegistry() - blockFormats = registry.NewSimpleRegistry() -) - -// RegisterGenesisFormat registers the engine for the provided format. -func RegisterGenesisFormat(f serde.Format, e serde.FormatEngine) { - genesisFormats.Register(f, e) -} - -// RegisterBlockFormat registers the engine for the provided format. -func RegisterBlockFormat(f serde.Format, e serde.FormatEngine) { - blockFormats.Register(f, e) -} - -// Digest defines the result of a fingerprint. It expects a digest of 256 bits. -// -// - implements fmt.Stringer -type Digest [32]byte - -// String implements fmt.Stringer. It returns a short representation of the -// digest. -func (d Digest) String() string { - return fmt.Sprintf("%x", d[:])[:8] -} - -// Bytes return the bytes of the digest. -func (d Digest) Bytes() []byte { - return d[:] -} - -// Genesis is the very first block of a chain. It contains the initial roster -// and tree root. -// -// - implements serde.Message -type Genesis struct { - digest Digest - roster authority.Authority - treeRoot Digest -} - -type genesisTemplate struct { - Genesis - hashFactory crypto.HashFactory -} - -// GenesisOption is the option type to set some fields of a genesis block. -type GenesisOption func(*genesisTemplate) - -// WithGenesisRoot is an option to set the tree root of the genesis block. -func WithGenesisRoot(root Digest) GenesisOption { - return func(tmpl *genesisTemplate) { - tmpl.treeRoot = root - } -} - -// WithGenesisHashFactory is an option to set the hash factory. -func WithGenesisHashFactory(fac crypto.HashFactory) GenesisOption { - return func(tmpl *genesisTemplate) { - tmpl.hashFactory = fac - } -} - -// NewGenesis creates a new genesis block with the provided roster. -func NewGenesis(ro authority.Authority, opts ...GenesisOption) (Genesis, error) { - tmpl := genesisTemplate{ - Genesis: Genesis{ - roster: ro, - treeRoot: Digest{}, - }, - hashFactory: crypto.NewSha256Factory(), - } - - for _, opt := range opts { - opt(&tmpl) - } - - h := tmpl.hashFactory.New() - err := tmpl.Fingerprint(h) - if err != nil { - return tmpl.Genesis, xerrors.Errorf("fingerprint failed: %v", err) - } - - copy(tmpl.digest[:], h.Sum(nil)) - - return tmpl.Genesis, nil -} - -// GetHash returns the digest of the block. -func (g Genesis) GetHash() Digest { - return g.digest -} - -// GetRoster returns the roster of the genesis block. -func (g Genesis) GetRoster() authority.Authority { - return g.roster -} - -// GetRoot returns the tree root. -func (g Genesis) GetRoot() Digest { - return g.treeRoot -} - -// Serialize implements serde.Message. It returns the serialized data for this -// genesis block. -func (g Genesis) Serialize(ctx serde.Context) ([]byte, error) { - format := genesisFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, g) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// Fingerprint implements serde.Fingerprinter. It deterministically writes a -// binary representation of the genesis block into the writer. -func (g Genesis) Fingerprint(w io.Writer) error { - _, err := w.Write(g.treeRoot[:]) - if err != nil { - return xerrors.Errorf("couldn't write root: %v", err) - } - - err = g.roster.Fingerprint(w) - if err != nil { - return xerrors.Errorf("roster fingerprint failed: %v", err) - } - - return nil -} - -// RosterKey is the key of the roster factory. -type RosterKey struct{} - -// GenesisFactory is a factory to deserialize the genesis messages. -// -// - implements serde.Factory -type GenesisFactory struct { - rosterFac authority.Factory -} - -// NewGenesisFactory creates a new genesis factory. -func NewGenesisFactory(rf authority.Factory) GenesisFactory { - return GenesisFactory{ - rosterFac: rf, - } -} - -// Deserialize implements serde.Factory. It populates the genesis block if -// appropriate, otherwise it returns an error. -func (f GenesisFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := genesisFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, RosterKey{}, f.rosterFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding failed: %v", err) - } - - return msg, nil -} - -// Block is a block of a chain. It holds an index which is the height of the -// block from the genesis block, the Merkle tree root and the validation result -// of the transactions. -// -// - implements serde.Message -type Block struct { - digest Digest - index uint64 - data validation.Result - treeRoot Digest -} - -type blockTemplate struct { - Block - hashFactory crypto.HashFactory -} - -// BlockOption is the type of option to set some fields of a block. -type BlockOption func(*blockTemplate) - -// WithIndex is an option to set the index of the block. -func WithIndex(index uint64) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.index = index - } -} - -// WithTreeRoot is an option to set the tree root for the block. -func WithTreeRoot(root Digest) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.treeRoot = root - } -} - -// WithHashFactory is an option to set the hash factory for the block. -func WithHashFactory(fac crypto.HashFactory) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.hashFactory = fac - } -} - -// NewBlock creates a new block. -func NewBlock(data validation.Result, opts ...BlockOption) (Block, error) { - tmpl := blockTemplate{ - Block: Block{ - data: data, - treeRoot: Digest{}, - }, - hashFactory: crypto.NewSha256Factory(), - } - - for _, opt := range opts { - opt(&tmpl) - } - - h := tmpl.hashFactory.New() - err := tmpl.Fingerprint(h) - if err != nil { - return tmpl.Block, xerrors.Errorf("fingerprint failed: %v", err) - } - - copy(tmpl.digest[:], h.Sum(nil)) - - return tmpl.Block, nil -} - -// GetHash returns the digest of the block. -func (b Block) GetHash() Digest { - return b.digest -} - -// GetIndex returns the index of the block. -func (b Block) GetIndex() uint64 { - return b.index -} - -// GetData returns the validated data of the block. -func (b Block) GetData() validation.Result { - return b.data -} - -// GetTransactions is a helper to extract the transactions from the validation -// result. -func (b Block) GetTransactions() []txn.Transaction { - results := b.data.GetTransactionResults() - txs := make([]txn.Transaction, len(results)) - - for i, res := range results { - txs[i] = res.GetTransaction() - } - - return txs -} - -// GetTreeRoot returns the tree root of the block. -func (b Block) GetTreeRoot() Digest { - return b.treeRoot -} - -// Fingerprint implements serde.Fingerprinter. It deterministically writes a -// binary representation of the block into the writer. -func (b Block) Fingerprint(w io.Writer) error { - buffer := make([]byte, 8) - binary.LittleEndian.PutUint64(buffer, b.index) - _, err := w.Write(buffer) - if err != nil { - return xerrors.Errorf("couldn't write index: %v", err) - } - - _, err = w.Write(b.treeRoot[:]) - if err != nil { - return xerrors.Errorf("couldn't write root: %v", err) - } - - err = b.data.Fingerprint(w) - if err != nil { - return xerrors.Errorf("data fingerprint failed: %v", err) - } - - return nil -} - -// Serialize implements serde.Message. It returns the serialized data of the -// block. -func (b Block) Serialize(ctx serde.Context) ([]byte, error) { - format := blockFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, b) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// DataKey is the key for the validated data factory. -type DataKey struct{} - -// BlockFactory is a factory to deserialize block messages. -// -// - implements serde.Factory -type BlockFactory struct { - dataFac validation.ResultFactory -} - -// NewBlockFactory creates a new block factory. -func NewBlockFactory(fac validation.ResultFactory) BlockFactory { - return BlockFactory{ - dataFac: fac, - } -} - -// Deserialize implements serde.Factory. It populates the block from the data if -// appropriate, otherwise it returns an error. -func (f BlockFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := blockFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, DataKey{}, f.dataFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding block failed: %v", err) - } - - return msg, nil -} diff --git a/dela/core/ordering/cosipbft/types/block_test.go b/dela/core/ordering/cosipbft/types/block_test.go deleted file mode 100644 index 1bcd6a8..0000000 --- a/dela/core/ordering/cosipbft/types/block_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package types - -import ( - "bytes" - "io" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/internal/testing/fake" -) - -func init() { - RegisterGenesisFormat(fake.GoodFormat, fake.Format{Msg: Genesis{}}) - RegisterGenesisFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterBlockFormat(fake.GoodFormat, fake.Format{Msg: Block{}}) - RegisterBlockFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestDigest_String(t *testing.T) { - digest := Digest{1, 2, 3, 4} - - require.Equal(t, "01020304", digest.String()) -} - -func TestDigest_Bytes(t *testing.T) { - digest := Digest{1, 2, 3, 4} - - require.Equal(t, digest[:], digest.Bytes()) -} - -func TestGenesis_GetHash(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - require.NotEqual(t, Digest{}, genesis.GetHash()) - - id := Digest{1, 2, 3} - genesis.digest = id - require.Equal(t, id, genesis.GetHash()) -} - -func TestGenesis_GetRoster(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - require.Equal(t, 3, genesis.GetRoster().Len()) -} - -func TestGenesis_GetRoot(t *testing.T) { - genesis := Genesis{treeRoot: Digest{5}} - - require.Equal(t, Digest{5}, genesis.GetRoot()) -} - -func TestGenesis_Serialize(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - data, err := genesis.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = genesis.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestGenesis_Fingerprint(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(1, fake.NewSigner)) - - genesis, err := NewGenesis(ro, WithGenesisRoot(Digest{5})) - require.NoError(t, err) - - buffer := new(bytes.Buffer) - err = genesis.Fingerprint(buffer) - require.NoError(t, err) - require.Regexp(t, "^\x05(\x00){35,}PK", buffer.String()) - - _, err = NewGenesis(ro, WithGenesisHashFactory(fake.NewHashFactory(fake.NewBadHash()))) - require.EqualError(t, err, fake.Err("fingerprint failed: couldn't write root")) - - genesis.roster = badRoster{} - err = genesis.Fingerprint(buffer) - require.EqualError(t, err, fake.Err("roster fingerprint failed")) -} - -func TestGenesisFactory_Deserialize(t *testing.T) { - fac := NewGenesisFactory(authority.NewFactory(nil, nil)) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.IsType(t, Genesis{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding failed")) -} - -func TestBlock_GetHash(t *testing.T) { - block, err := NewBlock(simple.NewResult(nil), WithTreeRoot(Digest{2})) - require.NoError(t, err) - require.NotEqual(t, Digest{}, block.GetHash()) -} - -func TestBlock_GetIndex(t *testing.T) { - block, err := NewBlock(simple.NewResult(nil), WithIndex(2)) - require.NoError(t, err) - require.Equal(t, uint64(2), block.GetIndex()) -} - -func TestBlock_GetData(t *testing.T) { - block := Block{data: simple.NewResult(nil)} - - require.Equal(t, simple.NewResult(nil), block.GetData()) -} - -func TestBlock_GetTransactions(t *testing.T) { - block := Block{data: simple.NewResult(nil)} - require.Len(t, block.GetTransactions(), 0) - - block.data = simple.NewResult([]simple.TransactionResult{{}}) - require.Len(t, block.GetTransactions(), 1) -} - -func TestBlock_GetTreeRoot(t *testing.T) { - block := Block{treeRoot: Digest{3}} - - require.Equal(t, Digest{3}, block.GetTreeRoot()) -} - -func TestBlock_Fingerprint(t *testing.T) { - block := Block{ - index: 3, - treeRoot: Digest{4}, - data: simple.NewResult(nil), - } - - buffer := new(bytes.Buffer) - - err := block.Fingerprint(buffer) - require.NoError(t, err) - require.Regexp(t, "^\x03(\x00){7}\x04(\x00){31}$", buffer.String()) - - err = block.Fingerprint(fake.NewBadHash()) - require.EqualError(t, err, fake.Err("couldn't write index")) - - err = block.Fingerprint(fake.NewBadHashWithDelay(1)) - require.EqualError(t, err, fake.Err("couldn't write root")) - - block.data = badData{} - err = block.Fingerprint(io.Discard) - require.EqualError(t, err, fake.Err("data fingerprint failed")) - - _, err = NewBlock(block.data, WithHashFactory(fake.NewHashFactory(fake.NewBadHash()))) - require.EqualError(t, err, fake.Err("fingerprint failed: couldn't write index")) -} - -func TestBlock_Serialize(t *testing.T) { - block, err := NewBlock(simple.NewResult(nil)) - require.NoError(t, err) - - data, err := block.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = block.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestBlockFactory_Deserialize(t *testing.T) { - txFac := signed.NewTransactionFactory() - fac := NewBlockFactory(simple.NewResultFactory(txFac)) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.IsType(t, Block{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding block failed")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badRoster struct { - authority.Authority -} - -func (r badRoster) Fingerprint(io.Writer) error { - return fake.GetError() -} - -type badData struct { - validation.Result -} - -func (d badData) Fingerprint(io.Writer) error { - return fake.GetError() -} diff --git a/dela/core/ordering/cosipbft/types/chain.go b/dela/core/ordering/cosipbft/types/chain.go deleted file mode 100644 index 20b486f..0000000 --- a/dela/core/ordering/cosipbft/types/chain.go +++ /dev/null @@ -1,447 +0,0 @@ -// This file contains the implementation of a chain of block links. -// -// Documentation Last Review: 13.10.2020 -// - -package types - -import ( - "io" - - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var ( - chainFormats = registry.NewSimpleRegistry() - linkFormats = registry.NewSimpleRegistry() -) - -// RegisterLinkFormat registers the engine for the provided format. -func RegisterLinkFormat(f serde.Format, e serde.FormatEngine) { - linkFormats.Register(f, e) -} - -// RegisterChainFormat registers the engine for the provided format. -func RegisterChainFormat(f serde.Format, e serde.FormatEngine) { - chainFormats.Register(f, e) -} - -// ForwardLink is a link between two blocks that is only using their different -// digests to reduce the serialization footprint. -// -// - implements types.Link -// - implements serde.Fingerprinter -type forwardLink struct { - digest Digest - from Digest - to Digest - changeset authority.ChangeSet - prepareSig crypto.Signature - commitSig crypto.Signature -} - -type linkTemplate struct { - forwardLink - - hashFac crypto.HashFactory -} - -// LinkOption is the type of option to set some optional fields of the -// link. -type LinkOption func(*linkTemplate) - -// WithSignatures is the option to set the signatures of the link. -func WithSignatures(prep, commit crypto.Signature) LinkOption { - return func(tmpl *linkTemplate) { - tmpl.prepareSig = prep - tmpl.commitSig = commit - } -} - -// WithChangeSet is the option to set the change set of the roster for this -// link. -func WithChangeSet(cs authority.ChangeSet) LinkOption { - return func(tmpl *linkTemplate) { - tmpl.changeset = cs - } -} - -// WithLinkHashFactory is the option to set the hash factory for the link. -func WithLinkHashFactory(fac crypto.HashFactory) LinkOption { - return func(tmpl *linkTemplate) { - tmpl.hashFac = fac - } -} - -// NewForwardLink creates a new forward link between the two block digests. -func NewForwardLink(from, to Digest, opts ...LinkOption) (Link, error) { - tmpl := linkTemplate{ - forwardLink: forwardLink{ - from: from, - to: to, - changeset: authority.NewChangeSet(), - }, - hashFac: crypto.NewSha256Factory(), - } - - for _, opt := range opts { - opt(&tmpl) - } - - h := tmpl.hashFac.New() - err := tmpl.Fingerprint(h) - if err != nil { - return nil, xerrors.Errorf("failed to fingerprint: %v", err) - } - - copy(tmpl.digest[:], h.Sum(nil)) - - return tmpl.forwardLink, nil -} - -// GetHash implements types.Link. It returns the digest of the link. -func (link forwardLink) GetHash() Digest { - return link.digest -} - -// GetFrom implements types.Link. It returns the digest of the source block. -func (link forwardLink) GetFrom() Digest { - return link.from -} - -// GetTo implements types.Link. It returns the block the link is pointing to. -func (link forwardLink) GetTo() Digest { - return link.to -} - -// GetPrepareSignature implements types.Link. It returns the prepare signature -// if it is set, otherwise it returns nil. -func (link forwardLink) GetPrepareSignature() crypto.Signature { - return link.prepareSig -} - -// GetCommitSignature implements types.Link. It returns the commit signature if -// it is set, otherwise it returns nil. -func (link forwardLink) GetCommitSignature() crypto.Signature { - return link.commitSig -} - -// GetChangeSet implements types.Link. It returns the change set of the roster -// for this link. -func (link forwardLink) GetChangeSet() authority.ChangeSet { - return link.changeset -} - -// Fingerprint implements serde.Fingerprinter. It deterministically writes a -// binary representation of the block link. -func (link forwardLink) Fingerprint(w io.Writer) error { - _, err := w.Write(link.from[:]) - if err != nil { - return xerrors.Errorf("couldn't write from: %v", err) - } - - id := link.GetTo() - - _, err = w.Write(id[:]) - if err != nil { - return xerrors.Errorf("couldn't write to: %v", err) - } - - return nil -} - -// Serialize implements serde.Message. It returns the data of the serialized -// forward link. -func (link forwardLink) Serialize(ctx serde.Context) ([]byte, error) { - format := linkFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, link) - if err != nil { - return nil, xerrors.Errorf("encoding link failed: %v", err) - } - - return data, nil -} - -// BlockLink is a link between two blocks but only keep the previous block -// digest. -// -// - implements types.BlockLink -type blockLink struct { - forwardLink - - block Block -} - -// NewBlockLink creates a new block link between from and to. -func NewBlockLink(from Digest, to Block, opts ...LinkOption) (BlockLink, error) { - link, err := NewForwardLink(from, to.digest, opts...) - if err != nil { - return nil, xerrors.Errorf("creating forward link: %v", err) - } - - bl := blockLink{ - forwardLink: link.(forwardLink), - block: to, - } - - return bl, nil -} - -// GetBlock implements types.BlockLink. It returns the block that the link is -// pointing at. -func (link blockLink) GetBlock() Block { - return link.block -} - -// Reduce implements types.BlockLink. It reduces the block link to its -// minimalistic shape. -func (link blockLink) Reduce() Link { - return link.forwardLink -} - -// Serialize implements serde.Message. It returns the serialized data for this -// block link. -func (link blockLink) Serialize(ctx serde.Context) ([]byte, error) { - format := linkFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, link) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// ChangeSetKey is the key of the change set factory. -type ChangeSetKey struct{} - -// BlockLinkFac is the factory to deserialize block link messages. -// -// - implements types.LinkFactory -type linkFac struct { - blockFac serde.Factory - sigFac crypto.SignatureFactory - csFac authority.ChangeSetFactory -} - -// NewLinkFactory creates a new block link factory. -func NewLinkFactory(blockFac serde.Factory, - sigFac crypto.SignatureFactory, csFac authority.ChangeSetFactory) LinkFactory { - - return linkFac{ - blockFac: blockFac, - sigFac: sigFac, - csFac: csFac, - } -} - -// Deserialize implements serde.Factory. It populates the block link if -// appropriate, otherwise it returns an error. -func (fac linkFac) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := linkFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, BlockKey{}, fac.blockFac) - ctx = serde.WithFactory(ctx, AggregateKey{}, fac.sigFac) - ctx = serde.WithFactory(ctx, ChangeSetKey{}, fac.csFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding link failed: %v", err) - } - - return msg, nil -} - -// LinkOf implements types.LinkFactory. It populates the link if appropriate, -// otherwise it returns an error. -func (fac linkFac) LinkOf(ctx serde.Context, data []byte) (Link, error) { - msg, err := fac.Deserialize(ctx, data) - if err != nil { - return nil, err - } - - link, ok := msg.(Link) - if !ok { - return nil, xerrors.Errorf("invalid forward link '%T'", msg) - } - - return link, nil -} - -// BlockLinkOf implements types.LinkFactory. It populates the block link if -// appropriate, otherwise it returns an error. -func (fac linkFac) BlockLinkOf(ctx serde.Context, data []byte) (BlockLink, error) { - msg, err := fac.Deserialize(ctx, data) - if err != nil { - return nil, err - } - - link, ok := msg.(BlockLink) - if !ok { - return nil, xerrors.Errorf("invalid block link '%T'", msg) - } - - return link, nil -} - -// Chain is a combination of ordered links that will define a proof of existence -// for a block. It does not include the genesis block which is assumed to be -// known beforehands. -// -// - implements types.Chain -type chain struct { - last BlockLink - prevs []Link -} - -// NewChain creates a new chain from the block link and the previous forward -// links. -func NewChain(last BlockLink, prevs []Link) Chain { - return chain{ - last: last, - prevs: prevs, - } -} - -// GetLinks implements types.Chain. It returns all the links of the chain in -// order. -func (c chain) GetLinks() []Link { - return append(append([]Link{}, c.prevs...), c.last) -} - -// GetBlock implements types.Chain. It returns the block the chain is pointing -// at. -func (c chain) GetBlock() Block { - return c.last.GetBlock() -} - -// Verify implements types.Chain. It verifies the integrity of the chain using -// the genesis block and the verifier factory. It starts the verification at the -// link whose previous block equals the given Digest. If the genesis hash is -// provided, the whole chain is going to be validated. -func (c chain) Verify(genesis Genesis, from Digest, fac crypto.VerifierFactory) error { - authority := genesis.GetRoster() - - prev := genesis.GetHash() - - toProcess := false - - for _, link := range c.GetLinks() { - // Skip the verification until we reach the provided Digest. We still - // have to update the roster though. - if !toProcess && link.GetFrom() != from { - prev = link.GetTo() - authority = authority.Apply(link.GetChangeSet()) - continue - } - - toProcess = true - - // It makes sure that the chain of links is consistent. - if prev != link.GetFrom() { - return xerrors.Errorf("mismatch from: '%v' != '%v'", link.GetFrom(), prev) - } - - // The verifier can be used to verify the signature of the link, but it - // needs to be created for every link as the roster can change. - verifier, err := fac.FromAuthority(authority) - if err != nil { - return xerrors.Errorf("verifier factory failed: %v", err) - } - - if link.GetPrepareSignature() == nil { - return xerrors.New("unexpected nil prepare signature in link") - } - - if link.GetCommitSignature() == nil { - return xerrors.New("unexpected nil commit signature in link") - } - - // 1. Verify the prepare signature that signs the integrity of the - // forward link. - err = verifier.Verify(link.GetHash().Bytes(), link.GetPrepareSignature()) - if err != nil { - return xerrors.Errorf("invalid prepare signature: %v", err) - } - - // 2. Verify the commit signature that signs the binary representation - // of the prepare signature. - msg, err := link.GetPrepareSignature().MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to marshal signature: %v", err) - } - - err = verifier.Verify(msg, link.GetCommitSignature()) - if err != nil { - return xerrors.Errorf("invalid commit signature: %v", err) - } - - prev = link.GetTo() - - authority = authority.Apply(link.GetChangeSet()) - } - - if !toProcess { - return xerrors.Errorf("no verification made (from Digest %v)", from) - } - - return nil -} - -// Serialize implements serde.Message. It returns the data of the serialized -// chain. -func (c chain) Serialize(ctx serde.Context) ([]byte, error) { - format := chainFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, c) - if err != nil { - return nil, xerrors.Errorf("encoding chain failed: %v", err) - } - - return data, nil -} - -// ChainFactory is a factory to serialize and deserialize a chain. -// -// - implements types.ChainFactory -type chainFactory struct { - linkFac LinkFactory -} - -// NewChainFactory creates a new factory from the link factory. -func NewChainFactory(fac LinkFactory) ChainFactory { - return chainFactory{ - linkFac: fac, - } -} - -// Deserialize implements serde.Factory. It returns the chain from the data if -// appropriate, otherwise it returns an error. -func (fac chainFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return fac.ChainOf(ctx, data) -} - -// ChainOf implements types.ChainFactory. It returns the chain from the data if -// appropriate, otherwise it returns an error. -func (fac chainFactory) ChainOf(ctx serde.Context, data []byte) (Chain, error) { - format := chainFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, LinkKey{}, fac.linkFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding chain failed: %v", err) - } - - chain, ok := msg.(Chain) - if !ok { - return nil, xerrors.Errorf("invalid chain '%T'", msg) - } - - return chain, nil -} diff --git a/dela/core/ordering/cosipbft/types/chain_test.go b/dela/core/ordering/cosipbft/types/chain_test.go deleted file mode 100644 index 93d6139..0000000 --- a/dela/core/ordering/cosipbft/types/chain_test.go +++ /dev/null @@ -1,347 +0,0 @@ -package types - -import ( - "bytes" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func init() { - RegisterLinkFormat(fake.GoodFormat, fake.Format{Msg: blockLink{}}) - RegisterLinkFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterLinkFormat(serde.Format("badtype"), fake.Format{Msg: fake.Message{}}) - RegisterChainFormat(fake.GoodFormat, fake.Format{Msg: chain{}}) - RegisterChainFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterChainFormat(serde.Format("badtype"), fake.Format{Msg: fake.Message{}}) -} - -func TestForwardLink_New(t *testing.T) { - link, err := NewForwardLink(Digest{1}, Digest{2}) - require.NoError(t, err) - require.Equal(t, Digest{1}, link.GetFrom()) - - opts := []LinkOption{ - WithSignatures(fake.Signature{}, fake.Signature{}), - WithChangeSet(authority.NewChangeSet()), - } - - link, err = NewForwardLink(Digest{1}, Digest{2}, opts...) - require.NoError(t, err) - require.Equal(t, fake.Signature{}, link.GetPrepareSignature()) - require.Equal(t, fake.Signature{}, link.GetCommitSignature()) - - opts = []LinkOption{ - WithLinkHashFactory(fake.NewHashFactory(fake.NewBadHash())), - } - - _, err = NewForwardLink(Digest{1}, Digest{2}, opts...) - require.EqualError(t, err, fake.Err("failed to fingerprint: couldn't write from")) -} - -func TestForwardLink_GetHash(t *testing.T) { - link := forwardLink{digest: Digest{1}} - - require.Equal(t, Digest{1}, link.GetHash()) -} - -func TestForwardLink_GetFrom(t *testing.T) { - link := forwardLink{from: Digest{2}} - - require.Equal(t, Digest{2}, link.GetFrom()) -} - -func TestForwardLink_GetTo(t *testing.T) { - link := forwardLink{to: Digest{3}} - - require.Equal(t, Digest{3}, link.GetTo()) -} - -func TestForwardLink_GetPrepareSignature(t *testing.T) { - link := forwardLink{prepareSig: fake.Signature{}} - - require.NotNil(t, link.GetPrepareSignature()) - require.Nil(t, link.GetCommitSignature()) -} - -func TestForwardLink_GetCommitSignature(t *testing.T) { - link := forwardLink{commitSig: fake.Signature{}} - - require.NotNil(t, link.GetCommitSignature()) - require.Nil(t, link.GetPrepareSignature()) -} - -func TestForwardLink_GetChangeSet(t *testing.T) { - link := forwardLink{ - changeset: authority.NewChangeSet(), - } - - require.Equal(t, authority.NewChangeSet(), link.GetChangeSet()) -} - -func TestForwardLink_Serialize(t *testing.T) { - link := forwardLink{} - - data, err := link.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = link.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding link failed")) -} - -func TestForwardLink_Fingerprint(t *testing.T) { - link, err := NewForwardLink(Digest{1}, Digest{2}) - require.NoError(t, err) - - buffer := new(bytes.Buffer) - - err = link.Fingerprint(buffer) - require.NoError(t, err) - require.Regexp(t, "^\x01\x00{31}\x02\x00{31}$", buffer.String()) - - err = link.Fingerprint(fake.NewBadHash()) - require.EqualError(t, err, fake.Err("couldn't write from")) - - err = link.Fingerprint(fake.NewBadHashWithDelay(1)) - require.EqualError(t, err, fake.Err("couldn't write to")) -} - -func TestBlockLink_New(t *testing.T) { - link, err := NewBlockLink(Digest{}, Block{}) - require.NoError(t, err) - require.Equal(t, Digest{}, link.GetFrom()) - - opt := WithLinkHashFactory(fake.NewHashFactory(fake.NewBadHash())) - - _, err = NewBlockLink(Digest{}, Block{}, opt) - require.EqualError(t, err, - fake.Err("creating forward link: failed to fingerprint: couldn't write from")) -} - -func TestBlockLink_GetBlock(t *testing.T) { - link := blockLink{ - block: Block{index: 1}, - } - - require.Equal(t, uint64(1), link.GetBlock().GetIndex()) -} - -func TestBlockLink_Reduce(t *testing.T) { - link := blockLink{ - forwardLink: forwardLink{ - from: Digest{1}, - to: Digest{2}, - prepareSig: fake.Signature{}, - commitSig: fake.Signature{}, - }, - } - - require.Equal(t, link.forwardLink, link.Reduce()) -} - -func TestBlockLink_Serialize(t *testing.T) { - link := blockLink{} - - data, err := link.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = link.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestLinkFac_LinkOf(t *testing.T) { - csFac := authority.NewChangeSetFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - fac := NewLinkFactory(BlockFactory{}, fake.SignatureFactory{}, csFac) - - msg, err := fac.LinkOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, blockLink{}, msg) - - _, err = fac.LinkOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding link failed")) - - _, err = fac.LinkOf(fake.NewContextWithFormat(serde.Format("badtype")), nil) - require.EqualError(t, err, "invalid forward link 'fake.Message'") -} - -func TestLinkFac_BlockLinkOf(t *testing.T) { - csFac := authority.NewChangeSetFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) - fac := NewLinkFactory(BlockFactory{}, fake.SignatureFactory{}, csFac) - - msg, err := fac.BlockLinkOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, blockLink{}, msg) - - _, err = fac.BlockLinkOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding link failed")) - - _, err = fac.BlockLinkOf(fake.NewContextWithFormat(serde.Format("badtype")), nil) - require.EqualError(t, err, "invalid block link 'fake.Message'") -} - -func TestChain_GetLinks(t *testing.T) { - chain := NewChain(blockLink{}, []Link{forwardLink{}, forwardLink{}}) - - require.Len(t, chain.GetLinks(), 3) -} - -func TestChain_GetBlock(t *testing.T) { - chain := NewChain(blockLink{block: Block{index: 2}}, nil) - - require.Equal(t, uint64(2), chain.GetBlock().GetIndex()) -} - -func TestChain_Verify(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - c := NewChain(makeLink(t, genesis.digest, Digest{}), nil) - - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.NoError(t, err) - - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.NoError(t, err) - - l := makeLink(t, Digest{}, Digest{}) - c = NewChain(l, nil) - err = c.Verify(genesis, l.GetFrom(), fake.VerifierFactory{}) - require.EqualError(t, err, fmt.Sprintf("mismatch from: '00000000' != '%v'", genesis.GetHash())) - - c = NewChain(makeLink(t, genesis.digest, Digest{}), nil) - err = c.Verify(genesis, genesis.GetHash(), fake.NewBadVerifierFactory()) - require.EqualError(t, err, fake.Err("verifier factory failed")) - - err = c.Verify(genesis, genesis.GetHash(), fake.NewVerifierFactory(fake.NewBadVerifier())) - require.EqualError(t, err, fake.Err("invalid prepare signature")) - - link := makeLink(t, genesis.digest, Digest{}).(blockLink) - link.prepareSig = nil - c = NewChain(link, nil) - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.EqualError(t, err, "unexpected nil prepare signature in link") - - link.prepareSig = fake.Signature{} - link.commitSig = nil - c = NewChain(link, nil) - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.EqualError(t, err, "unexpected nil commit signature in link") - - link.prepareSig = fake.NewBadSignature() - link.commitSig = fake.Signature{} - c = NewChain(link, nil) - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.EqualError(t, err, fake.Err("failed to marshal signature")) - - c = NewChain(makeLink(t, genesis.digest, Digest{}), nil) - err = c.Verify(genesis, genesis.GetHash(), fake.NewVerifierFactory(fake.NewBadVerifierWithDelay(1))) - require.EqualError(t, err, fake.Err("invalid commit signature")) -} - -func TestChain_Verify_Skip(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - link1 := makeLink(t, genesis.digest, digest(0x1)) - link2 := makeLink(t, digest(0x1), digest(0x2)) - link3 := makeLink(t, digest(0x2), digest(0x3)) - - c := NewChain(link3, []Link{link1, link2}) - - // Check the whole chain - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.NoError(t, err) - - // Check from link2 - err = c.Verify(genesis, digest(0x2), fake.VerifierFactory{}) - require.NoError(t, err) -} - -func TestChain_Verify_Skip_Invalid(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - link1 := makeLink(t, genesis.digest, digest(0x10)) - link2 := makeLink(t, digest(0x1), digest(0x2)) - link3 := makeLink(t, digest(0x2), digest(0x3)) - - c := NewChain(link3, []Link{link1, link2}) - - // Check the whole chain, should be an error with invalid link 1 - err = c.Verify(genesis, genesis.GetHash(), fake.VerifierFactory{}) - require.Error(t, err) - - // Check from link3, no error since we skip - err = c.Verify(genesis, digest(0x2), fake.VerifierFactory{}) - require.NoError(t, err) -} - -func TestChain_Verify_Skip_NoCheck(t *testing.T) { - ro := authority.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - - genesis, err := NewGenesis(ro) - require.NoError(t, err) - - link1 := makeLink(t, genesis.digest, digest(0x10)) - link2 := makeLink(t, digest(0x1), digest(0x2)) - link3 := makeLink(t, digest(0x2), digest(0x3)) - - c := NewChain(link3, []Link{link1, link2}) - - // Check with an inexistent digest - err = c.Verify(genesis, digest(0x33), fake.VerifierFactory{}) - require.EqualError(t, err, "no verification made (from Digest 33000000)") -} - -func TestChain_Serialize(t *testing.T) { - chain := chain{} - - data, err := chain.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = chain.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding chain failed")) -} - -func TestChainFactory_Deserialize(t *testing.T) { - fac := NewChainFactory(linkFac{}) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, chain{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding chain failed")) - - _, err = fac.Deserialize(fake.NewContextWithFormat(serde.Format("badtype")), nil) - require.EqualError(t, err, "invalid chain 'fake.Message'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeLink(t *testing.T, from, to Digest) BlockLink { - link, err := NewForwardLink(from, to, WithSignatures(fake.Signature{}, fake.Signature{})) - require.NoError(t, err) - - return blockLink{forwardLink: link.(forwardLink)} -} - -func digest(b byte) Digest { - var d Digest - d[0] = b - return d -} diff --git a/dela/core/ordering/cosipbft/types/messages.go b/dela/core/ordering/cosipbft/types/messages.go deleted file mode 100644 index ac8e5d3..0000000 --- a/dela/core/ordering/cosipbft/types/messages.go +++ /dev/null @@ -1,284 +0,0 @@ -// This file contains the implementation of the wrapper messages. -// -// Documentation Last Review: 13.10.2020 -// - -package types - -import ( - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var msgFormats = registry.NewSimpleRegistry() - -// RegisterMessageFormat registers the engine for the provided format. -func RegisterMessageFormat(f serde.Format, e serde.FormatEngine) { - msgFormats.Register(f, e) -} - -// GenesisMessage is a message to send a genesis to distant participants. -// -// - implements serde.Message -type GenesisMessage struct { - genesis *Genesis -} - -// NewGenesisMessage creates a new genesis message. -func NewGenesisMessage(genesis Genesis) GenesisMessage { - return GenesisMessage{ - genesis: &genesis, - } -} - -// GetGenesis returns the genesis block contained in the message. -func (m GenesisMessage) GetGenesis() *Genesis { - return m.genesis -} - -// Serialize implements serde.Message. It returns the serialized data for this -// message. -func (m GenesisMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// BlockMessage is a message sent to participants to share a block. -// -// - implements serde.Message -type BlockMessage struct { - block Block - views map[mino.Address]ViewMessage -} - -// NewBlockMessage creates a new block message with the provided block. -func NewBlockMessage(block Block, views map[mino.Address]ViewMessage) BlockMessage { - return BlockMessage{ - block: block, - views: views, - } -} - -// GetBlock returns the block of the message. -func (m BlockMessage) GetBlock() Block { - return m.block -} - -// GetViews returns the view messages if any. -func (m BlockMessage) GetViews() map[mino.Address]ViewMessage { - return m.views -} - -// Serialize implements serde.Message. It returns the serialized data of the -// block. -func (m BlockMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// CommitMessage is a message containing the signature of the prepare phase of a -// PBFT execution. -// -// - implements serde.Message -type CommitMessage struct { - id Digest - signature crypto.Signature -} - -// NewCommit creates a new commit message. -func NewCommit(id Digest, sig crypto.Signature) CommitMessage { - return CommitMessage{ - id: id, - signature: sig, - } -} - -// GetID returns the block digest to commit. -func (m CommitMessage) GetID() Digest { - return m.id -} - -// GetSignature returns the prepare signature. -func (m CommitMessage) GetSignature() crypto.Signature { - return m.signature -} - -// Serialize implements serde.Message. It returns the serialized data of the -// commit message. -func (m CommitMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// DoneMessage is a message containing the signature of the commit phase of a -// PBFT execution. -// -// - implements serde.Message -type DoneMessage struct { - id Digest - signature crypto.Signature -} - -// NewDone creates a new done message. -func NewDone(id Digest, sig crypto.Signature) DoneMessage { - return DoneMessage{ - id: id, - signature: sig, - } -} - -// GetID returns the digest of the block that has been accepted. -func (m DoneMessage) GetID() Digest { - return m.id -} - -// GetSignature returns the commit signature that proves the commitment of the -// block. -func (m DoneMessage) GetSignature() crypto.Signature { - return m.signature -} - -// Serialize implements serde.Message. It returns the serialized data of the -// done message. -func (m DoneMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// ViewMessage is a message to announce a view change request. -// -// - implements serde.Message -type ViewMessage struct { - id Digest - leader uint16 - signature crypto.Signature -} - -// NewViewMessage creates a new view message. -func NewViewMessage(id Digest, leader uint16, sig crypto.Signature) ViewMessage { - return ViewMessage{ - id: id, - leader: leader, - signature: sig, - } -} - -// GetID returns the digest of the latest block. -func (m ViewMessage) GetID() Digest { - return m.id -} - -// GetLeader returns the leader index of the view change. -func (m ViewMessage) GetLeader() uint16 { - return m.leader -} - -// GetSignature returns the signature of the view. -func (m ViewMessage) GetSignature() crypto.Signature { - return m.signature -} - -// Serialize implements serde.Message. It returns the serialized data for this -// view message. -func (m ViewMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// GenesisKey is the key of the genesis factory. -type GenesisKey struct{} - -// BlockKey is the key of the block factory. -type BlockKey struct{} - -// LinkKey is the key of the link factory. -type LinkKey struct{} - -// AggregateKey is the key of the collective signature factory. -type AggregateKey struct{} - -// SignatureKey is the key of the view signature factory. -type SignatureKey struct{} - -// AddressKey is the key of the address factory. -type AddressKey struct{} - -// MessageFactory is the factory to deserialize messages. -// -// - implements serde.Factory -type MessageFactory struct { - genesisFac serde.Factory - blockFac serde.Factory - aggFac crypto.SignatureFactory - sigFac crypto.SignatureFactory - csFac authority.ChangeSetFactory - addrFac mino.AddressFactory -} - -// NewMessageFactory creates a new message factory. -func NewMessageFactory(gf, bf serde.Factory, addrFac mino.AddressFactory, - aggFac crypto.SignatureFactory, csf authority.ChangeSetFactory) MessageFactory { - return MessageFactory{ - genesisFac: gf, - blockFac: bf, - aggFac: aggFac, - sigFac: common.NewSignatureFactory(), - csFac: csf, - addrFac: addrFac, - } -} - -// Deserialize implements serde.Factory. It populates the message if -// appropriate, otherwise it returns an error. -func (f MessageFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := msgFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, GenesisKey{}, f.genesisFac) - ctx = serde.WithFactory(ctx, BlockKey{}, f.blockFac) - ctx = serde.WithFactory(ctx, AggregateKey{}, f.aggFac) - ctx = serde.WithFactory(ctx, SignatureKey{}, f.sigFac) - ctx = serde.WithFactory(ctx, LinkKey{}, NewLinkFactory(f.blockFac, f.aggFac, f.csFac)) - ctx = serde.WithFactory(ctx, AddressKey{}, f.addrFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding failed: %v", err) - } - - return msg, nil -} diff --git a/dela/core/ordering/cosipbft/types/messages_test.go b/dela/core/ordering/cosipbft/types/messages_test.go deleted file mode 100644 index 0661dc7..0000000 --- a/dela/core/ordering/cosipbft/types/messages_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func init() { - RegisterMessageFormat(fake.GoodFormat, fake.Format{Msg: GenesisMessage{}}) - RegisterMessageFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestGenesisMessage_GetGenesis(t *testing.T) { - msg := NewGenesisMessage(Genesis{}) - - require.NotNil(t, msg.GetGenesis()) -} - -func TestGenesisMessage_Serialize(t *testing.T) { - msg := NewGenesisMessage(Genesis{}) - - data, err := msg.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = msg.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestBlockMessage_GetBlock(t *testing.T) { - expected := Block{index: 1} - msg := NewBlockMessage(expected, nil) - - block := msg.GetBlock() - require.Equal(t, expected, block) -} - -func TestBlockMessage_GetViews(t *testing.T) { - msg := NewBlockMessage(Block{}, nil) - require.Len(t, msg.GetViews(), 0) - - msg = NewBlockMessage(Block{}, map[mino.Address]ViewMessage{fake.NewAddress(0): {}}) - require.Len(t, msg.GetViews(), 1) -} - -func TestBlockMessage_Serialize(t *testing.T) { - msg := NewBlockMessage(Block{}, nil) - - data, err := msg.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = msg.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestCommitMessage_GetID(t *testing.T) { - msg := NewCommit(Digest{1}, fake.Signature{}) - - require.Equal(t, Digest{1}, msg.GetID()) -} - -func TestCommitMessage_GetSignature(t *testing.T) { - msg := NewCommit(Digest{}, fake.Signature{}) - - require.Equal(t, fake.Signature{}, msg.GetSignature()) -} - -func TestCommitMessage_Serialize(t *testing.T) { - msg := NewCommit(Digest{}, fake.Signature{}) - - data, err := msg.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = msg.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestDoneMessage_GetID(t *testing.T) { - msg := NewDone(Digest{1}, fake.Signature{}) - - require.Equal(t, Digest{1}, msg.GetID()) -} - -func TestDoneMessage_GetSignature(t *testing.T) { - msg := NewDone(Digest{}, fake.Signature{}) - - require.Equal(t, fake.Signature{}, msg.GetSignature()) -} - -func TestDoneMessage_Serialize(t *testing.T) { - msg := NewDone(Digest{}, fake.Signature{}) - - data, err := msg.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = msg.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestViewMessage_GetID(t *testing.T) { - msg := NewViewMessage(Digest{1}, 0, nil) - - require.Equal(t, Digest{1}, msg.GetID()) -} - -func TestViewMessage_GetLeader(t *testing.T) { - msg := NewViewMessage(Digest{}, 2, nil) - - require.Equal(t, uint16(2), msg.GetLeader()) -} - -func TestViewMessage_GetSignature(t *testing.T) { - msg := NewViewMessage(Digest{}, 0, fake.Signature{}) - - require.Equal(t, fake.Signature{}, msg.GetSignature()) -} - -func TestViewMessage_Serialize(t *testing.T) { - msg := NewViewMessage(Digest{}, 3, fake.Signature{}) - - data, err := msg.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = msg.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestMessageFactory_Deserialize(t *testing.T) { - fac := NewMessageFactory( - GenesisFactory{}, - BlockFactory{}, - fake.AddressFactory{}, - fake.SignatureFactory{}, - authority.NewChangeSetFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}), - ) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, GenesisMessage{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding failed")) -} diff --git a/dela/core/ordering/cosipbft/types/types.go b/dela/core/ordering/cosipbft/types/types.go deleted file mode 100644 index f36e8c8..0000000 --- a/dela/core/ordering/cosipbft/types/types.go +++ /dev/null @@ -1,88 +0,0 @@ -// Package types implements the network messages for cosipbft. -// -// The messages are implemented in a different package to prevent cycle imports -// when importing the serde formats. -// -// Documentation Last Review: 13.10.2020 -package types - -import ( - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" -) - -// Link is the interface of a link between two blocks. -type Link interface { - serde.Message - serde.Fingerprinter - - // GetHash returns the digest of the forward link that is signed with the - // prepare signature. - GetHash() Digest - - // GetFrom returns the digest of the previous block. - GetFrom() Digest - - // GetTo returns the digest of the block the link is pointing at. - GetTo() Digest - - // GetPrepareSignature returns the signature that proves the integrity of - // the link. - GetPrepareSignature() crypto.Signature - - // GetCommitSignature returns the signature that proves the block has been - // committed. - GetCommitSignature() crypto.Signature - - // GetChangeSet returns the roster change set for this link. - GetChangeSet() authority.ChangeSet -} - -// BlockLink is an extension of the Link interface to include the block the link -// is pointing at. It also provides a function to get a lighter link without the -// block. -type BlockLink interface { - Link - - // GetBlock returns the block the link is pointing at. - GetBlock() Block - - // Reduce returns the forward link equivalent to this block link but without - // the block to allow a lighter serialization. - Reduce() Link -} - -// LinkFactory is the interface of the block link factory. -type LinkFactory interface { - serde.Factory - - LinkOf(serde.Context, []byte) (Link, error) - - BlockLinkOf(serde.Context, []byte) (BlockLink, error) -} - -// Chain is the interface to combine several links in order to create a chain -// that can prove the integrity of the blocks from the genesis block. -type Chain interface { - serde.Message - - // GetLinks returns all the links that defines the chain in order. - GetLinks() []Link - - // GetBlock returns the latest block that the chain is pointing at. - GetBlock() Block - - // Verify takes the genesis block and the verifier factory that should - // verify the chain. Performs the verification starting at the link Digest. - Verify(genesis Genesis, from Digest, fac crypto.VerifierFactory) error -} - -// ChainFactory is the interface to serialize and deserialize chains. -type ChainFactory interface { - serde.Factory - - // ChainOf returns the chain from the data if appropriate, otherwise it - // returns an error. - ChainOf(serde.Context, []byte) (Chain, error) -} diff --git a/dela/core/ordering/ordering.go b/dela/core/ordering/ordering.go deleted file mode 100644 index de63bef..0000000 --- a/dela/core/ordering/ordering.go +++ /dev/null @@ -1,48 +0,0 @@ -// Package ordering defines the interface of the ordering service. The -// high-level purpose of this service is to order the transactions from the -// pool. -// -// Depending on the implementation, the service can be composed of multiple -// sub-components. For instance, an ordering service using CoSiPBFT will need to -// elect a leader every round but one running PoW will only do an ordering -// locally and creates a block with the proof of work. -package ordering - -import ( - "context" - - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/validation" -) - -// Proof contains the value of a specific key. -type Proof interface { - // GetKey returns the key of the proof. - GetKey() []byte - - // GetValue returns the value of the key. - GetValue() []byte -} - -// Event describes the current state of the service after an update. -type Event struct { - Index uint64 - Transactions []validation.TransactionResult -} - -// Service is the interface of an ordering service. It provides the primitives -// to order transactions from a pool. -type Service interface { - // GetProof must return a proof of the value at the provided key. - GetProof(key []byte) (Proof, error) - - // GetStore returns the store used by the service. - GetStore() store.Readable - - // Watch returns a channel populated with events when transactions are - // accepted. - Watch(ctx context.Context) <-chan Event - - // Close closes the service and cleans the resources. - Close() error -} diff --git a/dela/core/ordering/pow/block.go b/dela/core/ordering/pow/block.go deleted file mode 100644 index 282a5d6..0000000 --- a/dela/core/ordering/pow/block.go +++ /dev/null @@ -1,164 +0,0 @@ -package pow - -import ( - "context" - "encoding" - "encoding/binary" - "math/big" - - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -// Difficulty is the default difficulty. -const Difficulty = 1 - -// Block is a representation of a batch of transactions for a Proof-of-Work -// consensus. Each block has a fingerprint as a proof of correctness. -type Block struct { - index uint64 - nonce uint64 - root []byte - data validation.Result - hash []byte -} - -type blockTemplate struct { - Block - - hashFactory crypto.HashFactory - difficulty uint -} - -// BlockOption is the type of options to create a block. -type BlockOption func(*blockTemplate) - -// WithIndex is an option to set the block index. -func WithIndex(index uint64) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.index = index - } -} - -// WithNonce is an option to set the nonce of a block. -func WithNonce(nonce uint64) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.nonce = nonce - tmpl.difficulty = 0 - } -} - -// WithRoot is an option to set the root of a block. -func WithRoot(root []byte) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.root = root - } -} - -// WithDifficulty is an option to set the difficulty of the proof of work. If -// the difficulty is set to 0, the hash will be calculated according to the -// current nonce. -func WithDifficulty(diff uint) BlockOption { - return func(tmpl *blockTemplate) { - tmpl.difficulty = diff - } -} - -// NewBlock creates a new block. -func NewBlock(ctx context.Context, data validation.Result, opts ...BlockOption) (Block, error) { - tmpl := blockTemplate{ - Block: Block{ - data: data, - }, - hashFactory: crypto.NewSha256Factory(), - difficulty: Difficulty, - } - - for _, opt := range opts { - opt(&tmpl) - } - - err := tmpl.Block.prepare(ctx, tmpl.hashFactory, tmpl.difficulty) - if err != nil { - return tmpl.Block, xerrors.Errorf("couldn't prepare block: %v", err) - } - - return tmpl.Block, nil -} - -// Prepare is the actual proof of work on the block. It will find the nonce to -// match the difficulty level. -func (b *Block) prepare(ctx context.Context, fac crypto.HashFactory, diff uint) error { - h := fac.New() - - buffer := make([]byte, 8) - binary.LittleEndian.PutUint64(buffer, b.index) - _, err := h.Write(buffer) - if err != nil { - return xerrors.Errorf("failed to write index: %v", err) - } - - _, err = h.Write(b.root) - if err != nil { - return xerrors.Errorf("failed to write root: %v", err) - } - - err = b.data.Fingerprint(h) - if err != nil { - return xerrors.Errorf("failed to fingerprint data: %v", err) - } - - // The state before writing the nonce is saved so it does not need to be - // computed all the time. - inter, err := h.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { - return xerrors.Errorf("couldn't marshal digest: %v", err) - } - - bitstring := make([]byte, h.Size()) - for i := range bitstring { - bitstring[i] = 0xff - } - - target := new(big.Int) - target.SetBytes(bitstring) - target.Rsh(target, diff) - - for { - // Allow the proof of work to be aborted at any time if the context is - // cancelled earlier. - if ctx.Err() != nil { - return xerrors.Errorf("context error: %v", ctx.Err()) - } - - // Copy h to get the state before the nonce is written. - err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary(inter) - if err != nil { - return xerrors.Errorf("couldn't unmarshal digest: %v", err) - } - - binary.LittleEndian.PutUint64(buffer, b.nonce) - _, err = h.Write(buffer) - if err != nil { - return xerrors.Errorf("failed to write nonce: %v", err) - } - - res := h.Sum(nil) - // If no difficulty is set, the provided nonce defines the hash, - // otherwise it looks for a hash that matches the difficulty. - if diff == 0 || matchDifficulty(res, target) { - b.hash = res - return nil - } - - b.nonce++ - } -} - -func matchDifficulty(hash []byte, limit *big.Int) bool { - value := new(big.Int) - value.SetBytes(hash) - - return value.Cmp(limit) == -1 -} diff --git a/dela/core/ordering/pow/block_test.go b/dela/core/ordering/pow/block_test.go deleted file mode 100644 index b24f155..0000000 --- a/dela/core/ordering/pow/block_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package pow - -import ( - "context" - "io" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestBlock_New(t *testing.T) { - block, err := NewBlock(context.Background(), fakeData{}, WithIndex(1), WithNonce(2), WithRoot([]byte{3})) - require.NoError(t, err) - require.Equal(t, uint64(1), block.index) - require.Equal(t, uint64(2), block.nonce) - require.Equal(t, []byte{3}, block.root) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - _, err = NewBlock(ctx, fakeData{}) - require.EqualError(t, err, "couldn't prepare block: context error: context canceled") -} - -func TestBlock_Prepare(t *testing.T) { - block := &Block{ - data: fakeData{}, - } - - ctx := context.Background() - - err := block.prepare(ctx, crypto.NewSha256Factory(), 1) - require.NoError(t, err) - require.Len(t, block.hash, 32) - - err = block.prepare(ctx, crypto.NewSha256Factory(), 0) - require.NoError(t, err) - require.Len(t, block.hash, 32) - - err = block.prepare(ctx, fake.NewHashFactory(fake.NewBadHash()), 0) - require.EqualError(t, err, fake.Err("failed to write index")) - - err = block.prepare(ctx, fake.NewHashFactory(fake.NewBadHashWithDelay(1)), 0) - require.EqualError(t, err, fake.Err("failed to write root")) - - block.data = fakeData{err: fake.GetError()} - err = block.prepare(ctx, crypto.NewSha256Factory(), 0) - require.EqualError(t, err, fake.Err("failed to fingerprint data")) - - block.data = fakeData{} - err = block.prepare(ctx, fake.NewHashFactory(fake.NewBadHashWithDelay(2)), 0) - require.EqualError(t, err, fake.Err("couldn't marshal digest")) - - err = block.prepare(ctx, fake.NewHashFactory(fake.NewBadHashWithDelay(3)), 0) - require.EqualError(t, err, fake.Err("couldn't unmarshal digest")) - - err = block.prepare(ctx, fake.NewHashFactory(fake.NewBadHashWithDelay(4)), 0) - require.EqualError(t, err, fake.Err("failed to write nonce")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeData struct { - validation.Result - err error -} - -func (d fakeData) GetTransactionResults() []validation.TransactionResult { - return nil -} - -func (d fakeData) Fingerprint(io.Writer) error { - return d.err -} diff --git a/dela/core/ordering/pow/observer.go b/dela/core/ordering/pow/observer.go deleted file mode 100644 index 219953b..0000000 --- a/dela/core/ordering/pow/observer.go +++ /dev/null @@ -1,11 +0,0 @@ -package pow - -import "go.dedis.ch/dela/core/ordering" - -type observer struct { - events chan ordering.Event -} - -func (o observer) NotifyCallback(event interface{}) { - o.events <- event.(ordering.Event) -} diff --git a/dela/core/ordering/pow/pow.go b/dela/core/ordering/pow/pow.go deleted file mode 100644 index 91c8487..0000000 --- a/dela/core/ordering/pow/pow.go +++ /dev/null @@ -1,201 +0,0 @@ -// Package pow implements a Proof-of-Work ordering service. This implementation -// very naive and only support one single node. It demonstrates a permissionless -// blockchain. -// -// TODO: later improve or remove -package pow - -import ( - "context" - "sync" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -type epoch struct { - block Block - store hashtree.Tree -} - -// Service is an ordering service powered by a Proof-of-Work consensus -// algorithm. -// -// - implements ordering.Service -type Service struct { - pool pool.Pool - validation validation.Service - epochs []epoch - hashFactory crypto.HashFactory - difficulty uint - watcher core.Observable - closing chan struct{} - closed sync.WaitGroup -} - -// NewService creates a new service. -func NewService(pool pool.Pool, val validation.Service, trie hashtree.Tree) *Service { - return &Service{ - pool: pool, - validation: val, - epochs: []epoch{{store: trie}}, - hashFactory: crypto.NewSha256Factory(), - difficulty: 1, - watcher: core.NewWatcher(), - } -} - -// Listen implements ordering.Service. -func (s *Service) Listen() error { - if s.closing != nil { - return xerrors.New("service already started") - } - - s.closing = make(chan struct{}) - s.closed = sync.WaitGroup{} - s.closed.Add(1) - - go func() { - defer s.closed.Done() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // This is the main loop to create a block. It will wait for - // transactions at first and then try to create a new block. If another - // peer successfully finds a correct block, it will abort and restart. - for { - errCh := make(chan error, 1) - go func() { - errCh <- s.createBlock(ctx) - }() - - select { - case <-s.closing: - // This will cancel the context thus stopping the go routine. - return - case err := <-errCh: - if err != nil { - // Something went wrong when creating the block. The main - // loop is stopped as this is a critical error. - dela.Logger.Err(err).Msg("failed to create block") - return - } - - } - } - }() - - return nil -} - -// Stop implements ordering.Service. -func (s *Service) Stop() error { - if s.closing == nil { - return xerrors.New("service not started") - } - - close(s.closing) - s.closed.Wait() - - s.closing = nil - - return nil -} - -// GetProof implements ordering.Service. -func (s *Service) GetProof(key []byte) (ordering.Proof, error) { - last := s.epochs[len(s.epochs)-1] - - path, err := last.store.GetPath(key) - if err != nil { - return nil, xerrors.Errorf("couldn't read share: %v", err) - } - - blocks := make([]Block, len(s.epochs)) - for i, epoch := range s.epochs { - blocks[i] = epoch.block - } - - pr, err := NewProof(blocks, path) - if err != nil { - return nil, xerrors.Errorf("couldn't create proof: %v", err) - } - - return pr, nil -} - -// Watch implements ordering.Service. -func (s *Service) Watch(ctx context.Context) <-chan ordering.Event { - events := make(chan ordering.Event, 1) - - obs := observer{events: events} - s.watcher.Add(obs) - - go func() { - <-ctx.Done() - s.watcher.Remove(obs) - }() - - return events -} - -func (s *Service) createBlock(ctx context.Context) error { - // Wait for at least one transaction before creating a block. - txs := s.pool.Gather(ctx, pool.Config{Min: 1}) - - if ctx.Err() != nil { - // Context is closed so we don't proceed in the block creation. - return nil - } - - latestEpoch := s.epochs[len(s.epochs)-1] - - var data validation.Result - newTrie, err := latestEpoch.store.Stage(func(rwt store.Snapshot) error { - var err error - data, err = s.validation.Validate(rwt, txs) - if err != nil { - return xerrors.Errorf("failed to validate: %v", err) - } - - return nil - }) - - if err != nil { - return xerrors.Errorf("couldn't stage store: %v", err) - } - - block, err := NewBlock( - ctx, - data, - WithIndex(uint64(len(s.epochs))), - WithRoot(newTrie.GetRoot()), - WithDifficulty(s.difficulty), - ) - if err != nil { - return xerrors.Errorf("couldn't create block: %v", err) - } - - s.epochs = append(s.epochs, epoch{ - block: block, - store: newTrie, - }) - - for _, txres := range block.data.GetTransactionResults() { - s.pool.Remove(txres.GetTransaction()) - } - - s.watcher.Notify(ordering.Event{Index: block.index}) - - dela.Logger.Trace().Uint64("index", block.index).Msg("block append") - - return nil -} diff --git a/dela/core/ordering/pow/pow_test.go b/dela/core/ordering/pow/pow_test.go deleted file mode 100644 index 34f9a41..0000000 --- a/dela/core/ordering/pow/pow_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package pow - -import ( - "context" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - tree "go.dedis.ch/dela/core/store/hashtree/binprefix" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn" - pool "go.dedis.ch/dela/core/txn/pool/mem" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation" - val "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "golang.org/x/xerrors" -) - -func TestService_Basic(t *testing.T) { - tree, clean := makeTree(t) - defer clean() - - exec := native.NewExecution() - exec.Set(testContractName, testExec{}) - - pool := pool.NewPool() - srvc := NewService( - pool, - val.NewService(exec, signed.NewTransactionFactory()), - tree, - ) - - // 1. Start the ordering service. - require.NoError(t, srvc.Listen()) - defer srvc.Stop() - - // 2. Watch for new events before sending a transaction. - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - evts := srvc.Watch(ctx) - - signer := bls.NewSigner() - - // 3. Send a transaction to the pool. It should be detected by the ordering - // service and start a new block. - require.NoError(t, pool.Add(makeTx(t, 0, signer))) - - evt := <-evts - require.Equal(t, uint64(1), evt.Index) - - pr, err := srvc.GetProof([]byte("ping")) - require.NoError(t, err) - require.Equal(t, []byte("pong"), pr.GetValue()) - - // 4. Send another transaction to the pool. This time it should creates a - // block appended to the previous one. - require.NoError(t, pool.Add(makeTx(t, 1, signer))) - - evt = <-evts - require.Equal(t, uint64(2), evt.Index) -} - -func TestService_Listen(t *testing.T) { - tree, clean := makeTree(t) - defer clean() - - vs := val.NewService(native.NewExecution(), signed.NewTransactionFactory()) - - pool := pool.NewPool() - srvc := NewService(pool, vs, tree) - - err := srvc.Listen() - require.NoError(t, err) - - err = srvc.Listen() - require.EqualError(t, err, "service already started") - - err = srvc.Stop() - require.NoError(t, err) - - err = srvc.Stop() - require.EqualError(t, err, "service not started") - - pool.Add(makeTx(t, 0, fake.NewSigner())) - srvc = NewService(pool, badValidation{}, tree) - err = srvc.Listen() - require.NoError(t, err) - - srvc.closed.Wait() -} - -func TestService_GetProof(t *testing.T) { - tree, clean := makeTree(t) - defer clean() - - srvc := &Service{ - epochs: []epoch{{store: tree}}, - } - - pr, err := srvc.GetProof([]byte("A")) - require.NoError(t, err) - require.Equal(t, []byte("A"), pr.GetKey()) - - srvc.epochs[0].block.root = []byte{1} - _, err = srvc.GetProof([]byte("A")) - require.EqualError(t, err, - "couldn't create proof: mismatch block and share store root 0x01 != ") - - srvc.epochs[0].store = badTrie{} - _, err = srvc.GetProof([]byte("A")) - require.EqualError(t, err, fake.Err("couldn't read share")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -const testContractName = "abc" - -func makeTree(t *testing.T) (hashtree.Tree, func()) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-pow") - require.NoError(t, err) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - tree := tree.NewMerkleTree(db, tree.Nonce{}) - tree.Stage(func(store.Snapshot) error { return nil }) - - return tree, func() { os.RemoveAll(dir) } -} - -func makeTx(t *testing.T, nonce uint64, signer crypto.Signer) txn.Transaction { - tx, err := signed.NewTransaction( - nonce, - signer.GetPublicKey(), - signed.WithArg("key", []byte("ping")), - signed.WithArg("value", []byte("pong")), - signed.WithArg(native.ContractArg, []byte(testContractName)), - ) - require.NoError(t, err) - - return tx -} - -type testExec struct{} - -func (e testExec) Execute(store store.Snapshot, step execution.Step) error { - key := step.Current.GetArg("key") - value := step.Current.GetArg("value") - - if len(key) == 0 || len(value) == 0 { - return xerrors.New("key or value is nil") - } - - err := store.Set(key, value) - if err != nil { - return err - } - - return nil -} - -type badValidation struct { - validation.Service -} - -func (v badValidation) Validate(store.Snapshot, []txn.Transaction) (validation.Result, error) { - return nil, fake.GetError() -} - -type badTrie struct { - hashtree.Tree -} - -func (s badTrie) GetPath([]byte) (hashtree.Path, error) { - return nil, fake.GetError() -} diff --git a/dela/core/ordering/pow/proof.go b/dela/core/ordering/pow/proof.go deleted file mode 100644 index 0530ced..0000000 --- a/dela/core/ordering/pow/proof.go +++ /dev/null @@ -1,46 +0,0 @@ -package pow - -import ( - "bytes" - - "go.dedis.ch/dela/core/store/hashtree" - "golang.org/x/xerrors" -) - -// Proof is a proof that the chain of blocks has or has not the key in the -// store. If the key exists, the proof also contains the value. -// -// - implements ordering.Proof -type Proof struct { - blocks []Block - path hashtree.Path -} - -// NewProof creates a new valid proof. -func NewProof(blocks []Block, path hashtree.Path) (Proof, error) { - pr := Proof{ - blocks: blocks, - path: path, - } - - if len(blocks) == 0 { - return pr, xerrors.New("empty list of blocks") - } - - last := blocks[len(blocks)-1] - if !bytes.Equal(last.root, path.GetRoot()) { - return pr, xerrors.Errorf("mismatch block and share store root %#x != %#x", last.root, path.GetRoot()) - } - - return pr, nil -} - -// GetKey implements ordering.Proof. -func (p Proof) GetKey() []byte { - return p.path.GetKey() -} - -// GetValue implements ordering.Proof. -func (p Proof) GetValue() []byte { - return p.path.GetValue() -} diff --git a/dela/core/store/hashtree/binprefix/binprefix.go b/dela/core/store/hashtree/binprefix/binprefix.go deleted file mode 100644 index fb053d2..0000000 --- a/dela/core/store/hashtree/binprefix/binprefix.go +++ /dev/null @@ -1,286 +0,0 @@ -// Package binprefix implements the hash tree interface by following the merkle -// binary prefix tree algorithm. -// -// https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-melara.pdf -// -// The merkle tree is stored in-memory until it reaches a certain threshold of -// depth where it will write the nodes in disk. The leaf are always stored in -// disk because of the value it holds. -// -// Interior (Root) -// / \ -// 0 / \ 1 -// / \ -// Interior Interior -// / \ / \ -// 0 / \ 1 0 / \ 1 -// / \ / \ -// DiskNode Interior Empty Interior -// / \ / \ -// - - - - - -/- - -\- - - - - - / - - -\- - - - - - - - - - - Memory Depth -// 0 / \ 1 0 / \ 1 -// / \ / \ -// DiskNode DiskNode DiskNode DiskNode -// -// The drawing above demonstrates an example of a tree. Here the memory depth is -// set at 3 which means that every node after this level will be a disk node. It -// will be loaded to its in-memory type when traversing the tree. The node at -// prefix 00 is an example of a leaf node which is a disk node even above the -// memory depth level. -// -// Documentation Last Review: 08.10.2020 -package binprefix - -import ( - "sync" - - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -// MerkleTree is an implementation of a Merkle prefix binary tree. This -// particular implementation assumes the keys will have the same length so that -// only the longest unique prefix along the path can be a leaf node. -// -// The leafs of the tree will be stored on the disk when committing the tree. -// Modifications on a staged tree are done in-memory. -// -// - implements hashtree.Tree -type MerkleTree struct { - sync.Mutex - - tree *Tree - db kv.DB - tx store.Transaction - bucket []byte - hashFactory crypto.HashFactory -} - -// NewMerkleTree creates a new Merkle tree-based storage. -func NewMerkleTree(db kv.DB, nonce Nonce) *MerkleTree { - return &MerkleTree{ - tree: NewTree(nonce), - db: db, - bucket: []byte("hashtree"), - hashFactory: crypto.NewSha256Factory(), - } -} - -// Load tries to read the bucket and scan it for existing leafs and populate the -// tree with them. -func (t *MerkleTree) Load() error { - t.Lock() - defer t.Unlock() - - return t.doUpdate(func(tx kv.WritableTx) error { - bucket := tx.GetBucket(t.bucket) - - err := t.tree.FillFromBucket(bucket) - if err != nil { - return xerrors.Errorf("failed to load: %v", err) - } - - err = t.tree.CalculateRoot(t.hashFactory, bucket) - if err != nil { - return xerrors.Errorf("while updating: %v", err) - } - - return nil - }) -} - -// Get implements store.Readable. It returns the value associated with the key -// if it exists, otherwise it returns nil. -func (t *MerkleTree) Get(key []byte) ([]byte, error) { - t.Lock() - defer t.Unlock() - - var value []byte - - err := t.doView(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(t.bucket) - - var err error - value, err = t.tree.Search(key, nil, bucket) - - return err - }) - - if err != nil { - return nil, xerrors.Errorf("couldn't search key: %v", err) - } - - return value, nil -} - -// GetRoot implements hashtree.Tree. It returns the root hash of the tree. -func (t *MerkleTree) GetRoot() []byte { - t.Lock() - defer t.Unlock() - - return t.tree.root.GetHash() -} - -// GetPath implements hashtree.Tree. It returns a path to a given key that can -// be used to prove the inclusion or the absence of a key. -func (t *MerkleTree) GetPath(key []byte) (hashtree.Path, error) { - t.Lock() - defer t.Unlock() - - path := newPath(t.tree.nonce[:], key) - - err := t.doView(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(t.bucket) - - _, err := t.tree.Search(key, &path, bucket) - return err - }) - - if err != nil { - return nil, xerrors.Errorf("couldn't search key: %v", err) - } - - return path, nil -} - -// Stage implements hashtree.Tree. It executes the callback over a clone of the -// current tree and return the clone with the root calculated. -func (t *MerkleTree) Stage(fn func(store.Snapshot) error) (hashtree.StagingTree, error) { - clone := t.clone() - - err := t.doUpdate(func(tx kv.WritableTx) error { - b, err := tx.GetBucketOrCreate(t.bucket) - if err != nil { - return xerrors.Errorf("read bucket failed: %v", err) - } - - err = fn(writableMerkleTree{ - MerkleTree: clone, - bucket: b, - }) - - if err != nil { - return xerrors.Errorf("callback failed: %v", err) - } - - err = clone.tree.CalculateRoot(t.hashFactory, b) - if err != nil { - return xerrors.Errorf("couldn't update tree: %v", err) - } - - return nil - }) - - return clone, err -} - -// Commit implements hashtree.StagingTree. It writes the leaf nodes to the disk -// and a trade-off of other nodes. -func (t *MerkleTree) Commit() error { - t.Lock() - defer t.Unlock() - - err := t.doUpdate(func(tx kv.WritableTx) error { - bucket, err := tx.GetBucketOrCreate(t.bucket) - if err != nil { - return xerrors.Errorf("read bucket failed: %v", err) - } - - return t.tree.Persist(bucket) - }) - - if err != nil { - return xerrors.Errorf("failed to persist tree: %v", err) - } - - return nil -} - -// WithTx implements hashtree.StagingTree. It returns a tree that will share the -// same underlying data but it will perform operations on the database through -// the transaction. -func (t *MerkleTree) WithTx(tx store.Transaction) hashtree.StagingTree { - return &MerkleTree{ - tree: t.tree, - db: t.db, - tx: tx, - bucket: t.bucket, - hashFactory: t.hashFactory, - } -} - -func (t *MerkleTree) clone() *MerkleTree { - return &MerkleTree{ - tree: t.tree.Clone(), - db: t.db, - tx: t.tx, - bucket: t.bucket, - hashFactory: t.hashFactory, - } -} - -func (t *MerkleTree) doUpdate(fn func(kv.WritableTx) error) error { - if t.tx != nil { - tx, ok := t.tx.(kv.WritableTx) - if !ok { - return xerrors.Errorf("transaction '%T' is not writable", t.tx) - } - - return fn(tx) - } - - return t.db.Update(fn) -} - -func (t *MerkleTree) doView(fn func(kv.ReadableTx) error) error { - if t.tx != nil { - tx, ok := t.tx.(kv.ReadableTx) - if !ok { - return xerrors.Errorf("transaction '%T' is not readable", t.tx) - } - - return fn(tx) - } - - return t.db.View(fn) -} - -// WritableMerkleTree is a wrapper around the merkle tree implementation so that -// it can be written into but the tree is not updated at every operation. -// -// - implements store.Writable -type writableMerkleTree struct { - *MerkleTree - - bucket kv.Bucket -} - -// Set implements store.Writable. It adds or updates the key in the internal -// tree. -func (t writableMerkleTree) Set(key, value []byte) error { - t.Lock() - defer t.Unlock() - - err := t.tree.Insert(key, value, t.bucket) - if err != nil { - return xerrors.Errorf("couldn't insert pair: %v", err) - } - - return nil -} - -// Delete implements store.Writable. It removes the key from the tree. -func (t writableMerkleTree) Delete(key []byte) error { - t.Lock() - defer t.Unlock() - - err := t.tree.Delete(key, t.bucket) - if err != nil { - return xerrors.Errorf("couldn't delete key: %v", err) - } - - return nil -} diff --git a/dela/core/store/hashtree/binprefix/binprefix_test.go b/dela/core/store/hashtree/binprefix/binprefix_test.go deleted file mode 100644 index 3dc232c..0000000 --- a/dela/core/store/hashtree/binprefix/binprefix_test.go +++ /dev/null @@ -1,339 +0,0 @@ -package binprefix - -import ( - "bytes" - "crypto/rand" - "os" - "path/filepath" - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestMerkleTree_IntegrationTest(t *testing.T) { - db, clean := makeDB(t) - defer clean() - - tree := NewMerkleTree(db, Nonce{}) - tree.tree.memDepth = 5 - fac := tree.hashFactory - values := map[[MaxDepth]byte][]byte{} - - next, err := tree.Stage(func(snap store.Snapshot) error { - for i := 0; i < 1000; i++ { - key := [MaxDepth]byte{} - rand.Read(key[:]) - - value := make([]byte, 8) - rand.Read(value) - - err := snap.Set(key[:], value) - require.NoError(t, err) - - values[key] = value - } - return nil - }) - - require.NoError(t, err) - require.NotEmpty(t, values) - tree = next.(*MerkleTree) - - // Test read and write in a transaction. - err = db.Update(func(txn kv.WritableTx) error { - txtree := tree.WithTx(txn) - - err := txtree.Commit() - require.NoError(t, err) - - for key := range values { - path, err := txtree.GetPath(key[:]) - require.NoError(t, err) - - root, err := path.(Path).computeRoot(fac) - require.NoError(t, err) - require.Equal(t, root, tree.GetRoot()) - require.Equal(t, values[key], path.GetValue()) - } - - return nil - }) - require.NoError(t, err) - - next, err = tree.Stage(func(snap store.Snapshot) error { - for key := range values { - err = snap.Delete(key[:]) - require.NoError(t, err) - } - - return nil - }) - require.NoError(t, err) - tree = next.(*MerkleTree) - - require.Len(t, tree.GetRoot(), 32) - require.IsType(t, (*EmptyNode)(nil), tree.tree.root) - - err = tree.Commit() - require.NoError(t, err) - - db.View(func(txn kv.ReadableTx) error { - b := txn.GetBucket(tree.bucket) - require.NotNil(t, b) - - return b.ForEach(func(k, v []byte) error { - t.Fatal("database should be empty") - return nil - }) - }) -} - -func TestMerkleTree_Random_IntegrationTest(t *testing.T) { - f := func(nonce Nonce, n uint8, mem uint8) bool { - t.Logf("Step nonce:%x n:%d mem:%d", nonce, n, mem%32) - - db, clean := makeDB(t) - defer clean() - - tree := NewMerkleTree(db, nonce) - tree.tree.memDepth = int(mem) % 32 - - values := map[[4]byte][]byte{} - - var err error - var stage hashtree.StagingTree = tree - for k := 0; k < 2; k++ { - stage, err = stage.Stage(func(snap store.Snapshot) error { - for i := 0; i < int(n); i++ { - key := [4]byte{} - rand.Read(key[:]) - - value := make([]byte, 4) - rand.Read(value) - - values[key] = value - - snap.Set(key[:], value) - } - return nil - }) - require.NoError(t, err) - } - - // Test that all keys are present in-memory. - for key, value := range values { - v, err := stage.Get(key[:]) - require.NoError(t, err) - require.Equal(t, value, v) - } - - err = stage.Commit() - require.NoError(t, err) - - tree = stage.(*MerkleTree) - - // Test that the keys are still present after persiting to the disk, - // alongside with the computed hash for the nodes. - for key, value := range values { - path, err := tree.GetPath(key[:]) - require.NoError(t, err) - require.Equal(t, value, path.GetValue()) - - root, err := path.(Path).computeRoot(crypto.NewSha256Factory()) - require.NoError(t, err) - require.Equal(t, tree.GetRoot(), root) - } - - newTree := NewMerkleTree(db, nonce) - err = newTree.Load() - require.NoError(t, err) - - // Test that the keys are still present after loading the tree from the - // disk. - for key, value := range values { - path, err := newTree.GetPath(key[:]) - require.NoError(t, err) - require.Equal(t, value, path.GetValue()) - - root, err := path.(Path).computeRoot(crypto.NewSha256Factory()) - require.NoError(t, err) - require.Equal(t, newTree.GetRoot(), root) - } - - return true - } - - err := quick.Check(f, &quick.Config{MaxCount: 20}) - require.NoError(t, err) -} - -func TestMerkleTree_Load(t *testing.T) { - tree := NewMerkleTree(fakeDB{}, Nonce{}) - - err := tree.Load() - require.NoError(t, err) - - tree.tx = fakeTx{bucket: &fakeBucket{errScan: fake.GetError()}} - err = tree.Load() - require.EqualError(t, err, fake.Err("failed to load: while scanning")) - - tree.tx = nil - tree.hashFactory = fake.NewHashFactory(fake.NewBadHash()) - err = tree.Load() - require.EqualError(t, err, fake.Err("while updating: failed to prepare: empty node failed")) -} - -func TestMerkleTree_Get(t *testing.T) { - tree := NewMerkleTree(fakeDB{}, Nonce{}) - - err := tree.tree.Insert([]byte("ping"), []byte("pong"), &fakeBucket{}) - require.NoError(t, err) - - value, err := tree.Get([]byte("ping")) - require.NoError(t, err) - require.Equal(t, []byte("pong"), value) - - value, err = tree.Get([]byte("pong")) - require.NoError(t, err) - require.Nil(t, value) - - _, err = tree.Get(make([]byte, MaxDepth+1)) - require.EqualError(t, err, "couldn't search key: mismatch key length 33 > 32") - - tree.tx = wrongTx{} - _, err = tree.Get([]byte{}) - require.EqualError(t, err, - "couldn't search key: transaction 'binprefix.wrongTx' is not readable") -} - -func TestMerkleTree_GetRoot(t *testing.T) { - tree := NewMerkleTree(fakeDB{}, Nonce{}) - // Tree is not yet updated. - require.Empty(t, tree.GetRoot()) - - empty, err := tree.Stage(func(store.Snapshot) error { return nil }) - require.NoError(t, err) - // Tree is updated even without any leafs. - require.NotEmpty(t, empty.GetRoot()) - - filled, err := tree.Stage(func(snap store.Snapshot) error { - return snap.Set([]byte("ping"), []byte("pong")) - }) - require.NoError(t, err) - require.NotEmpty(t, filled.GetRoot()) - require.NotEqual(t, empty.GetRoot(), filled.GetRoot()) -} - -func TestMerkleTree_GetPath(t *testing.T) { - tree := NewMerkleTree(fakeDB{}, Nonce{}) - - f := func(key [8]byte, value []byte) bool { - err := tree.tree.Insert(key[:], value, &fakeBucket{}) - require.NoError(t, err) - - err = tree.tree.CalculateRoot(tree.hashFactory, &fakeBucket{}) - require.NoError(t, err) - - path, err := tree.GetPath(key[:]) - require.NoError(t, err) - - root, err := path.(Path).computeRoot(tree.hashFactory) - require.NoError(t, err) - require.Equal(t, root, path.GetRoot()) - - return bytes.Equal(value, path.GetValue()) - } - - err := quick.Check(f, nil) - require.NoError(t, err) - - _, err = tree.GetPath(make([]byte, MaxDepth+1)) - require.EqualError(t, err, "couldn't search key: mismatch key length 33 > 32") -} - -func TestMerkleTree_Stage(t *testing.T) { - tree := NewMerkleTree(fakeDB{}, Nonce{}) - - next, err := tree.Stage(func(snap store.Snapshot) error { return nil }) - require.NoError(t, err) - require.NotEmpty(t, next.GetRoot()) - - _, err = tree.Stage(func(store.Snapshot) error { return fake.GetError() }) - require.EqualError(t, err, fake.Err("callback failed")) - - tree.hashFactory = fake.NewHashFactory(fake.NewBadHash()) - _, err = tree.Stage(func(store.Snapshot) error { return nil }) - require.EqualError(t, err, - fake.Err("couldn't update tree: failed to prepare: empty node failed")) - - tree.tx = badTx{} - _, err = tree.Stage(func(store.Snapshot) error { return nil }) - require.EqualError(t, err, fake.Err("read bucket failed")) - - tree.tx = wrongTx{} - _, err = tree.Stage(func(store.Snapshot) error { return nil }) - require.EqualError(t, err, "transaction 'binprefix.wrongTx' is not writable") -} - -func TestMerkleTree_Commit(t *testing.T) { - tree := NewMerkleTree(fakeDB{err: fake.GetError()}, Nonce{}) - - err := tree.Commit() - require.EqualError(t, err, fake.Err("failed to persist tree")) - - tree.tx = badTx{} - err = tree.Commit() - require.EqualError(t, err, fake.Err("failed to persist tree: read bucket failed")) -} - -func TestWritableMerkleTree_Set(t *testing.T) { - tree := writableMerkleTree{MerkleTree: NewMerkleTree(fakeDB{}, Nonce{})} - - err := tree.Set([]byte("ping"), []byte("pong")) - require.NoError(t, err) - require.Equal(t, 1, tree.tree.Len()) - - err = tree.Set(make([]byte, MaxDepth+1), nil) - require.EqualError(t, err, "couldn't insert pair: mismatch key length 33 > 32") -} - -func TestWritableMerkleTree_Delete(t *testing.T) { - tree := writableMerkleTree{MerkleTree: NewMerkleTree(fakeDB{}, Nonce{})} - - err := tree.Delete([]byte("ping")) - require.NoError(t, err) - - err = tree.Delete(make([]byte, MaxDepth+1)) - require.EqualError(t, err, "couldn't delete key: mismatch key length 33 > 32") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeDB(t *testing.T) (kv.DB, func()) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-pow") - require.NoError(t, err) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - return db, func() { os.RemoveAll(dir) } -} - -type badTx struct { - kv.WritableTx -} - -func (tx badTx) GetBucketOrCreate([]byte) (kv.Bucket, error) { - return nil, fake.GetError() -} - -type wrongTx struct { - store.Transaction -} diff --git a/dela/core/store/hashtree/binprefix/disk.go b/dela/core/store/hashtree/binprefix/disk.go deleted file mode 100644 index 5e45ef2..0000000 --- a/dela/core/store/hashtree/binprefix/disk.go +++ /dev/null @@ -1,237 +0,0 @@ -// -// Documentation Last Review: 08.10.2020 -// - -package binprefix - -import ( - "math/big" - - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// DiskNode is an implementation of a tree node which is stored on the -// disk. -// -// - implements binprefix.TreeNode -type DiskNode struct { - depth uint16 - hash []byte - context serde.Context - factory serde.Factory -} - -// NewDiskNode creates a new disk node. -func NewDiskNode(depth uint16, hash []byte, ctx serde.Context, factory serde.Factory) *DiskNode { - return &DiskNode{ - depth: depth, - hash: hash, - context: ctx, - factory: factory, - } -} - -// GetHash implements binprefix.TreeNode. It returns the hash of the disk node -// if it is set, otherwise it returns nil. -func (n *DiskNode) GetHash() []byte { - return n.hash -} - -// GetType returns the type of the node. -func (n *DiskNode) GetType() byte { - return diskNodeType -} - -// Search implements binprefix.TreeNode. It loads the disk node and then search -// for the key. -func (n *DiskNode) Search(key *big.Int, path *Path, bucket kv.Bucket) ([]byte, error) { - if bucket == nil { - return nil, xerrors.New("bucket is nil") - } - - node, err := n.load(key, bucket) - if err != nil { - return nil, xerrors.Errorf("failed to load node: %v", err) - } - - value, err := node.Search(key, path, bucket) - if err != nil { - // No wrapping to prevent very long error message from recursive calls. - return nil, err - } - - return value, nil -} - -// Insert implements binprefix.TreeNode. It loads the node and inserts the -// key/value pair using in-memory operations. The whole path to the key will be -// loaded and kept in-memory until the tree is persisted. -func (n *DiskNode) Insert(key *big.Int, value []byte, bucket kv.Bucket) (TreeNode, error) { - node, err := n.load(key, bucket) - if err != nil { - return nil, xerrors.Errorf("failed to load node: %v", err) - } - - next, err := node.Insert(key, value, bucket) - if err != nil { - // No wrapping to prevent very long error message from recursive calls. - return nil, err - } - - return next, nil -} - -// Delete implements binprefix.TreeNode. It loads the node and deletes the key -// if it exists. The whole path to the key is loaded in-memory until the tree is -// persisted. -func (n *DiskNode) Delete(key *big.Int, bucket kv.Bucket) (TreeNode, error) { - node, err := n.load(key, bucket) - if err != nil { - return nil, xerrors.Errorf("failed to load node: %v", err) - } - - next, err := node.Delete(key, bucket) - if err != nil { - return nil, err - } - - return next, nil -} - -// Prepare implements binprefix.TreeNode. It loads the node and calculates its -// hash. The subtree might be loaded in-memory if deeper hashes have not been -// computed yet. -func (n *DiskNode) Prepare(nonce []byte, prefix *big.Int, - bucket kv.Bucket, fac crypto.HashFactory) ([]byte, error) { - - if len(n.hash) > 0 { - // Hash is already calculated so we can skip and return. - return n.hash, nil - } - - node, err := n.load(prefix, bucket) - if err != nil { - return nil, xerrors.Errorf("failed to load node: %v", err) - } - - digest, err := node.Prepare(nonce, prefix, bucket, fac) - if err != nil { - // No wrapping to prevent very long error message from recursive calls. - return nil, err - } - - err = n.store(prefix, node, bucket) - if err != nil { - return nil, xerrors.Errorf("failed to store node: %v", err) - } - - n.hash = digest - - return digest, nil -} - -// Visit implements binprefix.TreeNode. -func (n *DiskNode) Visit(fn func(TreeNode) error) error { - return fn(n) -} - -// Clone implements binprefix.TreeNode. It clones the disk node but both the old -// and the new will read the same bucket. -func (n *DiskNode) Clone() TreeNode { - return NewDiskNode(n.depth, n.hash, n.context, n.factory) -} - -// Serialize implements serde.Message. It always returns an error as a disk node -// cannot be serialized. -func (n *DiskNode) Serialize(ctx serde.Context) ([]byte, error) { - return nil, xerrors.New("not implemented") -} - -func (n *DiskNode) load(index *big.Int, bucket kv.Bucket) (TreeNode, error) { - key := n.prepareKey(index) - - data := bucket.Get(key) - if len(data) == 0 { - return nil, xerrors.Errorf("prefix %b (depth %d) not in database", index, n.depth) - } - - msg, err := n.factory.Deserialize(n.context, data) - if err != nil { - return nil, xerrors.Errorf("failed to deserialize: %v", err) - } - - node, ok := msg.(TreeNode) - if !ok { - return nil, xerrors.Errorf("invalid node of type '%T'", msg) - } - - return node, nil -} - -func (n *DiskNode) store(index *big.Int, node TreeNode, b kv.Bucket) error { - data, err := node.Serialize(n.context) - if err != nil { - return xerrors.Errorf("failed to serialize: %v", err) - } - - key := n.prepareKey(index) - - err = b.Set(key, data) - if err != nil { - return xerrors.Errorf("failed to set key: %v", err) - } - - return nil -} - -func (n *DiskNode) cleanSubtree(depth uint16, index *big.Int, b kv.Bucket) error { - prefix := n.prepareKey(index) - if len(prefix) > 0 { - // It needs to scan a bitwise prefix thus it removes the last byte. - prefix = prefix[:len(prefix)-1] - } - - // The database can scan over prefix at the *byte* level but the node keys - // are bitwise so it manually compares the bits of the last byte of the - // prefix. - return b.Scan(prefix, func(k, _ []byte) error { - key := new(big.Int) - key.SetBytes(reverse(k)) - - for i := len(prefix) * 8; i < int(depth); i++ { - if key.Bit(i) != index.Bit(i) { - return nil - } - } - - return b.Delete(k) - }) -} - -func (n *DiskNode) prepareKey(index *big.Int) []byte { - prefix := new(big.Int) - - // First fill the prefix until the depth bit which will create a unique key - // for the node... - for i := 0; i < int(n.depth); i++ { - prefix.SetBit(prefix, i, index.Bit(i)) - } - - // ... but we set the bit at _depth_ to one to differentiate prefixes that - // end with 0s. - prefix.SetBit(prefix, int(n.depth), 1) - - return reverse(prefix.Bytes()) -} - -func reverse(buffer []byte) []byte { - buffer = append([]byte{}, buffer...) - for i, j := 0, len(buffer)-1; i < j; i, j = i+1, j-1 { - buffer[i], buffer[j] = buffer[j], buffer[i] - } - - return buffer -} diff --git a/dela/core/store/hashtree/binprefix/disk_test.go b/dela/core/store/hashtree/binprefix/disk_test.go deleted file mode 100644 index 2edb913..0000000 --- a/dela/core/store/hashtree/binprefix/disk_test.go +++ /dev/null @@ -1,238 +0,0 @@ -package binprefix - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde/json" -) - -var testCtx = json.NewContext() - -func TestDiskNode_GetHash(t *testing.T) { - node := &DiskNode{hash: []byte{0xaa}} - - require.Equal(t, []byte{0xaa}, node.GetHash()) -} - -func TestDiskNode_GetType(t *testing.T) { - node := &DiskNode{} - - require.Equal(t, diskNodeType, node.GetType()) -} - -func TestDiskNode_Search(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - // Test if a leaf node can be loaded and searched for. - leaf := NewLeafNode(0, big.NewInt(0), []byte("pong")) - data, err := leaf.Serialize(testCtx) - require.NoError(t, err) - - bucketKey := node.prepareKey(big.NewInt(0)) - - value, err := node.Search(big.NewInt(0), nil, newFakeBucket(bucketKey, data)) - require.NoError(t, err) - require.Equal(t, []byte("pong"), value) - - // Error if the node is not stored in the database. - _, err = node.Search(big.NewInt(0), nil, &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 0 (depth 0) not in database") - - inter := NewInteriorNode(0, big.NewInt(0)) - data, err = inter.Serialize(testCtx) - require.NoError(t, err) - - // Error deeper in the tree. - _, err = node.Search(big.NewInt(0), nil, newFakeBucket(bucketKey, data)) - require.EqualError(t, err, "failed to load node: prefix 0 (depth 1) not in database") - - _, err = node.Search(big.NewInt(0), nil, nil) - require.EqualError(t, err, "bucket is nil") -} - -func TestDiskNode_Insert(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - empty := NewEmptyNode(0, big.NewInt(0)) - data, err := empty.Serialize(testCtx) - require.NoError(t, err) - - bucket := newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - next, err := node.Insert(big.NewInt(2), []byte("pong"), bucket) - require.NoError(t, err) - require.IsType(t, (*LeafNode)(nil), next) - - _, err = node.Insert(big.NewInt(2), nil, &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 10 (depth 0) not in database") - - inter := NewInteriorNode(0, big.NewInt(0)) - data, err = inter.Serialize(testCtx) - require.NoError(t, err) - - bucket = newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - _, err = node.Insert(big.NewInt(2), []byte("pong"), bucket) - require.EqualError(t, err, "failed to load node: prefix 10 (depth 1) not in database") -} - -func TestDiskNode_Delete(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - empty := NewEmptyNode(0, big.NewInt(0)) - data, err := empty.Serialize(testCtx) - require.NoError(t, err) - - bucket := newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - next, err := node.Delete(big.NewInt(2), bucket) - require.NoError(t, err) - require.IsType(t, (*EmptyNode)(nil), next) - - _, err = node.Delete(big.NewInt(2), &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 10 (depth 0) not in database") - - inter := NewInteriorNode(0, big.NewInt(0)) - data, err = inter.Serialize(testCtx) - require.NoError(t, err) - - bucket = newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - _, err = node.Delete(big.NewInt(2), bucket) - require.EqualError(t, err, "failed to load node: prefix 10 (depth 1) not in database") - - _, err = node.Delete(big.NewInt(3), bucket) - require.EqualError(t, err, "failed to load node: prefix 11 (depth 1) not in database") -} - -func TestDiskNode_Prepare(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - empty := NewEmptyNode(0, big.NewInt(0)) - data, err := empty.Serialize(testCtx) - require.NoError(t, err) - - bucket := newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - hash, err := node.Prepare([]byte{1}, big.NewInt(0), bucket, crypto.NewSha256Factory()) - require.NoError(t, err) - require.Len(t, hash, 32) - require.Equal(t, hash, node.hash) - - node.hash = nil - _, err = node.Prepare([]byte{}, big.NewInt(0), &fakeBucket{}, nil) - require.EqualError(t, err, "failed to load node: prefix 0 (depth 0) not in database") - - bucket.errSet = fake.GetError() - _, err = node.Prepare([]byte{}, big.NewInt(0), bucket, crypto.NewSha256Factory()) - require.EqualError(t, err, fake.Err("failed to store node: failed to set key")) - - inter := NewInteriorNode(0, big.NewInt(0)) - data, err = inter.Serialize(testCtx) - require.NoError(t, err) - - bucket = newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - _, err = node.Prepare([]byte{}, big.NewInt(0), bucket, crypto.NewSha256Factory()) - require.EqualError(t, err, "failed to load node: prefix 0 (depth 1) not in database") -} - -func TestDiskNode_Visit(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - counter := 0 - node.Visit(func(n TreeNode) error { - require.Same(t, node, n) - counter++ - return nil - }) - - require.Equal(t, 1, counter) -} - -func TestDiskNode_Clone(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - clone := node.Clone() - require.Equal(t, node, clone) -} - -func TestDiskNode_Serialize(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - _, err := node.Serialize(testCtx) - require.Error(t, err) -} - -func TestDiskNode_Load(t *testing.T) { - node := NewDiskNode(0, nil, testCtx, NodeFactory{}) - - empty := NewEmptyNode(0, big.NewInt(0)) - data, err := empty.Serialize(testCtx) - require.NoError(t, err) - - bucket := newFakeBucket(node.prepareKey(big.NewInt(0)), data) - - n, err := node.load(big.NewInt(0), bucket) - require.NoError(t, err) - require.IsType(t, empty, n) - - node.factory = fake.NewBadMessageFactory() - _, err = node.load(big.NewInt(0), bucket) - require.EqualError(t, err, fake.Err("failed to deserialize")) - - node.factory = fake.MessageFactory{} - _, err = node.load(big.NewInt(0), bucket) - require.EqualError(t, err, "invalid node of type 'fake.Message'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeBucket struct { - kv.Bucket - values map[string][]byte - errSet error - errScan error -} - -func newFakeBucket(key, value []byte) *fakeBucket { - return &fakeBucket{ - values: map[string][]byte{ - string(key): value, - }, - } -} - -func (b *fakeBucket) Get(key []byte) []byte { - return b.values[string(key)] -} - -func (b *fakeBucket) Set(key, value []byte) error { - if b.values == nil { - b.values = make(map[string][]byte) - } - - b.values[string(key)] = value - return b.errSet -} - -func (b *fakeBucket) Delete(key []byte) error { - return nil -} - -func (b *fakeBucket) Scan(prefix []byte, fn func(k, v []byte) error) error { - for key, value := range b.values { - err := fn([]byte(key), value) - if err != nil { - return err - } - } - - return b.errScan -} diff --git a/dela/core/store/hashtree/binprefix/format.go b/dela/core/store/hashtree/binprefix/format.go deleted file mode 100644 index 6152aa8..0000000 --- a/dela/core/store/hashtree/binprefix/format.go +++ /dev/null @@ -1,123 +0,0 @@ -package binprefix - -import ( - "math/big" - - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// LeafNodeJSON is the JSON representation of a leaf node. -type LeafNodeJSON struct { - Digest []byte - Depth uint16 - Prefix []byte - Value []byte -} - -// InteriorNodeJSON is the JSON representation of an interior node. -type InteriorNodeJSON struct { - Digest []byte - Depth uint16 - Prefix []byte -} - -// EmptyNodeJSON is the JSON representation of an empty node. -type EmptyNodeJSON struct { - Digest []byte - Depth uint16 - Prefix []byte -} - -// NodeJSON is the wrapper around the different types of a tree node. -type NodeJSON struct { - Leaf *LeafNodeJSON `json:",omitempty"` - Interior *InteriorNodeJSON `json:",omitempty"` - Empty *EmptyNodeJSON `json:",omitempty"` -} - -type nodeFormat struct{} - -func (f nodeFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - var m NodeJSON - - switch node := msg.(type) { - case *LeafNode: - leaf := LeafNodeJSON{ - Digest: node.GetHash(), - Depth: node.GetDepth(), - Prefix: node.GetKey(), - Value: node.GetValue(), - } - - m = NodeJSON{Leaf: &leaf} - case *EmptyNode: - empty := EmptyNodeJSON{ - Digest: node.GetHash(), - Depth: node.GetDepth(), - Prefix: node.GetPrefix().Bytes(), - } - - m = NodeJSON{Empty: &empty} - case *InteriorNode: - inter := InteriorNodeJSON{ - Digest: node.GetHash(), - Depth: node.GetDepth(), - Prefix: node.GetPrefix().Bytes(), - } - - m = NodeJSON{Interior: &inter} - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -func (f nodeFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := NodeJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - if m.Leaf != nil { - key := new(big.Int) - key.SetBytes(m.Leaf.Prefix) - - node := NewLeafNodeWithDigest(m.Leaf.Depth, key, m.Leaf.Value, m.Leaf.Digest) - - return node, nil - } - - if m.Empty != nil { - prefix := new(big.Int) - prefix.SetBytes(m.Empty.Prefix) - - node := NewEmptyNodeWithDigest(m.Empty.Depth, prefix, m.Empty.Digest) - - return node, nil - } - - if m.Interior != nil { - factory := ctx.GetFactory(NodeKey{}) - - prefix := new(big.Int) - prefix.SetBytes(m.Interior.Prefix) - - node := NewInteriorNodeWithChildren( - m.Interior.Depth, - prefix, - m.Interior.Digest, - NewDiskNode(m.Interior.Depth+1, nil, ctx, factory), - NewDiskNode(m.Interior.Depth+1, nil, ctx, factory), - ) - - return node, nil - } - - return nil, xerrors.New("message is empty") -} diff --git a/dela/core/store/hashtree/binprefix/path.go b/dela/core/store/hashtree/binprefix/path.go deleted file mode 100644 index bbbb78e..0000000 --- a/dela/core/store/hashtree/binprefix/path.go +++ /dev/null @@ -1,91 +0,0 @@ -// -// Documentation Last Review: 08.10.2020 -// - -package binprefix - -import ( - "math/big" - - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -// Path is a path from the root to a leaf, represented as a series of interior -// nodes hashes. The end of the path is either a leaf with a key holding a -// value, or an empty node. -// -// - implements hashtree.Path -type Path struct { - nonce []byte - key []byte - value []byte - // Root is the root of the hash tree. This value is not serialized and - // reproduced from the leaf and the interior nodes when deserializing. - root []byte - interiors [][]byte -} - -// newPath creates an empty path for the provided key. It must be filled to be -// valid. -func newPath(nonce, key []byte) Path { - return Path{ - nonce: nonce, - key: key, - } -} - -// GetKey implements hashtree.Path. It returns the key associated to the path. -func (s Path) GetKey() []byte { - return s.key -} - -// GetValue implements hashtree.Path. It returns the value pointed by the path. -func (s Path) GetValue() []byte { - return s.value -} - -// GetRoot implements hashtree.Path. It returns the hash of the root node -// calculated from the leaf up to the root. -func (s Path) GetRoot() []byte { - return s.root -} - -func (s Path) computeRoot(fac crypto.HashFactory) ([]byte, error) { - key := new(big.Int) - key.SetBytes(s.key) - - var node TreeNode - if s.value != nil { - node = NewLeafNode(uint16(len(s.interiors)), key, s.value) - } else { - node = NewEmptyNode(uint16(len(s.interiors)), key) - } - - // Reproduce the shortest unique prefix for the key. - prefix := new(big.Int) - for i := 0; i < len(s.interiors); i++ { - prefix.SetBit(prefix, i, key.Bit(i)) - } - - curr, err := node.Prepare(s.nonce, prefix, nil, fac) - if err != nil { - return nil, xerrors.Errorf("while preparing: %v", err) - } - - for i := len(s.interiors) - 1; i >= 0; i-- { - h := fac.New() - - if key.Bit(i) == 0 { - h.Write(curr) - h.Write(s.interiors[i]) - } else { - h.Write(s.interiors[i]) - h.Write(curr) - } - - curr = h.Sum(nil) - } - - return curr, nil -} diff --git a/dela/core/store/hashtree/binprefix/path_test.go b/dela/core/store/hashtree/binprefix/path_test.go deleted file mode 100644 index 462cb15..0000000 --- a/dela/core/store/hashtree/binprefix/path_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package binprefix - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestPath_GetKey(t *testing.T) { - path := newPath([]byte{}, []byte("ping")) - - require.Equal(t, []byte("ping"), path.GetKey()) -} - -func TestPath_GetValue(t *testing.T) { - path := newPath([]byte{}, []byte("ping")) - - require.Nil(t, path.GetValue()) - - path.value = []byte("pong") - require.Equal(t, []byte("pong"), path.GetValue()) -} - -func TestPath_GetRoot(t *testing.T) { - path := newPath([]byte{}, []byte("ping")) - - require.Nil(t, path.GetRoot()) - - path.root = []byte("pong") - require.Equal(t, []byte("pong"), path.GetRoot()) -} - -func TestPath_ComputeRoot(t *testing.T) { - path := newPath([]byte{1, 2, 3}, []byte("A")) - - root, err := path.computeRoot(fake.NewHashFactory(&fake.Hash{})) - require.NoError(t, err) - require.NotEmpty(t, root) - - _, err = path.computeRoot(fake.NewHashFactory(fake.NewBadHash())) - require.EqualError(t, err, fake.Err("while preparing: empty node failed")) -} diff --git a/dela/core/store/hashtree/binprefix/tree.go b/dela/core/store/hashtree/binprefix/tree.go deleted file mode 100644 index 2948996..0000000 --- a/dela/core/store/hashtree/binprefix/tree.go +++ /dev/null @@ -1,860 +0,0 @@ -// -// Documentation Last Review: 08.10.2020 -// - -package binprefix - -import ( - "encoding/binary" - "math" - "math/big" - - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -func init() { - nodeFormats.Register(serde.FormatJSON, nodeFormat{}) -} - -// Nonce is the type of the tree nonce. -type Nonce [8]byte - -const ( - // DepthLength is the length in bytes of the binary representation of the - // depth. - DepthLength = 2 - - // MaxDepth is the maximum depth the tree should reach. It is equivalent to - // the maximum key length in bytes. - MaxDepth = 32 -) - -const ( - emptyNodeType byte = iota - interiorNodeType - leafNodeType - diskNodeType -) - -var nodeFormats = registry.NewSimpleRegistry() - -// TreeNode is the interface for the different types of nodes that a Merkle tree -// could have. -type TreeNode interface { - serde.Message - - GetHash() []byte - - GetType() byte - - Search(key *big.Int, path *Path, bucket kv.Bucket) ([]byte, error) - - Insert(key *big.Int, value []byte, bucket kv.Bucket) (TreeNode, error) - - Delete(key *big.Int, bucket kv.Bucket) (TreeNode, error) - - Prepare(nonce []byte, prefix *big.Int, bucket kv.Bucket, fac crypto.HashFactory) ([]byte, error) - - Visit(func(node TreeNode) error) error - - Clone() TreeNode -} - -// Tree is an implementation of a Merkle binary prefix tree. Due to the -// structure of the tree, any prefix of a longer prefix is overridden which -// means that the key should have the same length. -// -// Mutable operations on the tree don't update the hash root. It can be done -// after a batch of operations or a single one by using the CalculateRoot -// function. -type Tree struct { - nonce Nonce - maxDepth int - memDepth int - root TreeNode - context serde.Context - factory serde.Factory -} - -// NewTree creates a new empty tree. -func NewTree(nonce Nonce) *Tree { - return &Tree{ - nonce: nonce, - maxDepth: MaxDepth, - memDepth: MaxDepth * 8, - root: NewEmptyNode(0, big.NewInt(0)), - factory: NodeFactory{}, - context: json.NewContext(), - } -} - -// Len returns the number of leaves in the tree. -func (t *Tree) Len() int { - counter := 0 - - t.root.Visit(func(n TreeNode) error { - if n.GetType() == leafNodeType { - counter++ - } - - return nil - }) - - return counter -} - -// FillFromBucket scans the bucket for leafs to insert them in the tree. It will -// then persist the tree to restore the memory depth limit. -func (t *Tree) FillFromBucket(bucket kv.Bucket) error { - if bucket == nil { - return nil - } - - t.root = NewInteriorNode(0, big.NewInt(0)) - - err := bucket.Scan([]byte{}, func(key, value []byte) error { - msg, err := t.factory.Deserialize(t.context, value) - if err != nil { - return xerrors.Errorf("tree node malformed: %v", err) - } - - var diskNode *DiskNode - var prefix *big.Int - - switch node := msg.(type) { - case *InteriorNode: - diskNode = NewDiskNode(node.depth, node.hash, t.context, t.factory) - prefix = node.prefix - case *EmptyNode: - diskNode = NewDiskNode(node.depth, node.hash, t.context, t.factory) - prefix = node.prefix - case *LeafNode: - diskNode = NewDiskNode(node.depth, node.hash, t.context, t.factory) - prefix = node.key - } - - t.restore(t.root, prefix, diskNode) - - return nil - }) - - if err != nil { - return xerrors.Errorf("while scanning: %v", err) - } - - return nil -} - -// Restore is a recursive function that will append the node to the tree by -// recreating the interior nodes from the root to its natural position defined -// by the prefix. -func (t *Tree) restore(curr TreeNode, prefix *big.Int, node *DiskNode) TreeNode { - var interior *InteriorNode - - switch n := curr.(type) { - case *InteriorNode: - interior = n - case *DiskNode: - return curr - case *EmptyNode: - if node.depth == n.depth { - return node - } - - interior = NewInteriorNode(n.depth, n.prefix) - } - - if prefix.Bit(int(interior.depth)) == 0 { - interior.left = t.restore(interior.left, prefix, node) - } else { - interior.right = t.restore(interior.right, prefix, node) - } - - return interior -} - -// Search returns the value associated to the key if it exists, otherwise nil. -// When path is defined, it will be filled with the interior nodes and the leaf -// node so that it can prove the inclusion or the absence of the key. -func (t *Tree) Search(key []byte, path *Path, b kv.Bucket) ([]byte, error) { - if len(key) > t.maxDepth { - return nil, xerrors.Errorf("mismatch key length %d > %d", len(key), t.maxDepth) - } - - value, err := t.root.Search(makeKey(key), path, b) - if err != nil { - return nil, xerrors.Errorf("failed to search: %v", err) - } - - if path != nil { - path.root = t.root.GetHash() - } - - return value, nil -} - -// Insert inserts the key in the tree. -func (t *Tree) Insert(key, value []byte, b kv.Bucket) error { - if len(key) > t.maxDepth { - return xerrors.Errorf("mismatch key length %d > %d", len(key), t.maxDepth) - } - - var err error - t.root, err = t.root.Insert(makeKey(key), value, b) - if err != nil { - return xerrors.Errorf("failed to insert: %v", err) - } - - return nil -} - -// Delete removes a key from the tree. -func (t *Tree) Delete(key []byte, b kv.Bucket) error { - if len(key) > t.maxDepth { - return xerrors.Errorf("mismatch key length %d > %d", len(key), t.maxDepth) - } - - var err error - t.root, err = t.root.Delete(makeKey(key), b) - if err != nil { - return xerrors.Errorf("failed to delete: %v", err) - } - - return nil -} - -// CalculateRoot updates the hashes of the tree. -func (t *Tree) CalculateRoot(fac crypto.HashFactory, b kv.Bucket) error { - prefix := new(big.Int) - - _, err := t.root.Prepare(t.nonce[:], prefix, b, fac) - if err != nil { - return xerrors.Errorf("failed to prepare: %v", err) - } - - return nil -} - -// Persist visits the whole tree and stores the leaf node in the database and -// replaces the node with disk nodes. Depending of the parameter, it also stores -// intermediate nodes on the disk. -func (t *Tree) Persist(b kv.Bucket) error { - return t.root.Visit(func(n TreeNode) error { - switch node := n.(type) { - case *InteriorNode: - if int(node.depth) > t.memDepth { - return t.toDisk(node.depth, node.prefix, node, b, false, true) - } - - _, ok := node.left.(*LeafNode) - if ok || int(node.depth) == t.memDepth { - node.left = NewDiskNode(node.depth+1, node.left.GetHash(), t.context, t.factory) - } - - _, ok = node.right.(*LeafNode) - if ok || int(node.depth) == t.memDepth { - node.right = NewDiskNode(node.depth+1, node.right.GetHash(), t.context, t.factory) - } - case *LeafNode: - return t.toDisk(node.depth, node.key, node, b, true, true) - case *EmptyNode: - shouldStore := int(node.depth) >= t.memDepth - - return t.toDisk(node.depth, node.prefix, node, b, true, shouldStore) - } - - return nil - }) -} - -func (t *Tree) toDisk(depth uint16, prefix *big.Int, node TreeNode, b kv.Bucket, clean, store bool) error { - disknode := NewDiskNode(depth, nil, t.context, t.factory) - - if clean { - err := disknode.cleanSubtree(depth, prefix, b) - if err != nil { - return xerrors.Errorf("failed to clean subtree: %v", err) - } - } - - if store { - err := disknode.store(prefix, node, b) - if err != nil { - return xerrors.Errorf("failed to store node: %v", err) - } - } - - return nil -} - -// Clone returns a deep copy of the tree. -func (t *Tree) Clone() *Tree { - return &Tree{ - nonce: t.nonce, - maxDepth: t.maxDepth, - memDepth: t.memDepth, - root: t.root.Clone(), - context: t.context, - factory: t.factory, - } -} - -// EmptyNode is leaf node with no value. -// -// - implements binprefix.TreeNode -type EmptyNode struct { - depth uint16 - prefix *big.Int - hash []byte -} - -// NewEmptyNode creates a new empty node. -func NewEmptyNode(depth uint16, key *big.Int) *EmptyNode { - return NewEmptyNodeWithDigest(depth, key, nil) -} - -// NewEmptyNodeWithDigest creates a new empty node with its digest. -func NewEmptyNodeWithDigest(depth uint16, key *big.Int, hash []byte) *EmptyNode { - return &EmptyNode{ - depth: depth, - prefix: key, - hash: hash, - } -} - -// GetHash implements binprefix.TreeNode. It returns the hash of the node. -func (n *EmptyNode) GetHash() []byte { - return append([]byte{}, n.hash...) -} - -// GetType implements binprefix.TreeNode. It returns the empty node type. -func (n *EmptyNode) GetType() byte { - return emptyNodeType -} - -// GetDepth returns the depth of the node. -func (n *EmptyNode) GetDepth() uint16 { - return n.depth -} - -// GetPrefix returns the prefix of the node. -func (n *EmptyNode) GetPrefix() *big.Int { - return n.prefix -} - -// Search implements binprefix.TreeNode. It always return a empty value. -func (n *EmptyNode) Search(key *big.Int, path *Path, b kv.Bucket) ([]byte, error) { - if path != nil { - path.value = nil - } - - return nil, nil -} - -// Insert implements binprefix.TreeNode. It replaces the empty node by a leaf -// node that contains the key and the value. -func (n *EmptyNode) Insert(key *big.Int, value []byte, b kv.Bucket) (TreeNode, error) { - return NewLeafNode(n.depth, key, value), nil -} - -// Delete implements binprefix.TreeNode. It ignores the delete as an empty node -// already means the key is missing. -func (n *EmptyNode) Delete(key *big.Int, b kv.Bucket) (TreeNode, error) { - return n, nil -} - -// Prepare implements binprefix.TreeNode. It updates the hash of the node if not -// already set and returns the digest. -func (n *EmptyNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { - - if len(n.hash) > 0 { - // Hash is already calculated so we can skip and return. - return n.hash, nil - } - - h := fac.New() - - data := make([]byte, 1+len(nonce)+bilen(prefix)+DepthLength) - cursor := 1 - data[0] = emptyNodeType - copy(data[cursor:], nonce) - cursor += len(nonce) - copy(data[cursor:], prefix.Bytes()) - cursor += bilen(prefix) - copy(data[cursor:], int2buffer(n.depth)) - - _, err := h.Write(data) - if err != nil { - return nil, xerrors.Errorf("empty node failed: %v", err) - } - - n.hash = h.Sum(nil) - - return n.GetHash(), nil -} - -// Visit implements binprefix.TreeNode. It executes the callback with the node. -func (n *EmptyNode) Visit(fn func(node TreeNode) error) error { - err := fn(n) - if err != nil { - return xerrors.Errorf("visiting empty: %v", err) - } - - return nil -} - -// Clone implements binprefix.TreeNode. It returns a deep copy of the empty -// node. -func (n *EmptyNode) Clone() TreeNode { - return NewEmptyNode(n.depth, n.prefix) -} - -// Serialize implements serde.Message. It returns the JSON data of the empty -// node. -func (n *EmptyNode) Serialize(ctx serde.Context) ([]byte, error) { - format := nodeFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, n) - if err != nil { - return nil, xerrors.Errorf("failed to encode empty node: %v", err) - } - - return data, nil -} - -// InteriorNode is a node with two children. -// -// - implements binprefix.TreeNode -type InteriorNode struct { - hash []byte - depth uint16 - prefix *big.Int - left TreeNode - right TreeNode -} - -// NewInteriorNode creates a new interior node with two empty nodes as children. -func NewInteriorNode(depth uint16, prefix *big.Int) *InteriorNode { - return NewInteriorNodeWithChildren( - depth, - prefix, - nil, - NewEmptyNode(depth+1, new(big.Int).SetBit(prefix, int(depth), 0)), - NewEmptyNode(depth+1, new(big.Int).SetBit(prefix, int(depth), 1)), - ) -} - -// NewInteriorNodeWithChildren creates a new interior node with the two given -// children. -func NewInteriorNodeWithChildren(depth uint16, prefix *big.Int, hash []byte, left, right TreeNode) *InteriorNode { - return &InteriorNode{ - depth: depth, - prefix: prefix, - hash: hash, - left: left, - right: right, - } -} - -// GetHash implements binprefix.TreeNode. It returns the hash of the node. -func (n *InteriorNode) GetHash() []byte { - return append([]byte{}, n.hash...) -} - -// GetType implements binprefix.TreeNode. It returns the interior node type. -func (n *InteriorNode) GetType() byte { - return interiorNodeType -} - -// GetDepth returns the depth of the node. -func (n *InteriorNode) GetDepth() uint16 { - return n.depth -} - -// GetPrefix returns the prefix of the node. -func (n *InteriorNode) GetPrefix() *big.Int { - return n.prefix -} - -// Search implements binprefix.TreeNode. It recursively search for the value in -// the correct child. -func (n *InteriorNode) Search(key *big.Int, path *Path, b kv.Bucket) ([]byte, error) { - if key.Bit(int(n.depth)) == 0 { - n.right, _ = n.load(n.right, key, 1, b) - - if path != nil { - path.interiors = append(path.interiors, n.right.GetHash()) - } - - return n.left.Search(key, path, b) - } - - n.left, _ = n.load(n.left, key, 0, b) - - if path != nil { - path.interiors = append(path.interiors, n.left.GetHash()) - } - - return n.right.Search(key, path, b) -} - -// Insert implements binprefix.TreeNode. It inserts the key/value pair to the -// right path. -func (n *InteriorNode) Insert(key *big.Int, value []byte, b kv.Bucket) (TreeNode, error) { - var err error - if key.Bit(int(n.depth)) == 0 { - n.left, err = n.left.Insert(key, value, b) - } else { - n.right, err = n.right.Insert(key, value, b) - } - - // Reset the hash as the subtree will change and thus invalidate this - // current value. - n.hash = nil - - return n, err -} - -// Delete implements binprefix.TreeNode. It deletes the leaf node associated to -// the key if it exists, otherwise nothing will change. -func (n *InteriorNode) Delete(key *big.Int, b kv.Bucket) (TreeNode, error) { - var err error - - // Depending on the path to follow, it will delete the key from the correct - // path and it will load the first node of the opposite path if it is a disk - // node so that the type can be compared. Errors are not wrapper to prevent - // very long error message. - if key.Bit(int(n.depth)) == 0 { - n.left, err = n.left.Delete(key, b) - if err != nil { - return nil, err - } - - n.right, err = n.load(n.right, key, 1, b) - if err != nil { - return nil, err - } - } else { - n.right, err = n.right.Delete(key, b) - if err != nil { - return nil, err - } - - n.left, err = n.load(n.left, key, 0, b) - if err != nil { - return nil, err - } - } - - if n.left.GetType() == emptyNodeType && n.right.GetType() == emptyNodeType { - // If an interior node points to two empty nodes, it is itself an empty - // one. - return NewEmptyNode(n.depth, n.prefix), nil - } - - return n, nil -} - -func (n *InteriorNode) load(node TreeNode, key *big.Int, bit uint, b kv.Bucket) (TreeNode, error) { - diskn, ok := node.(*DiskNode) - if !ok { - return node, nil - } - - node, err := diskn.load(new(big.Int).SetBit(key, int(n.depth), bit), b) - if err != nil { - return nil, xerrors.Errorf("failed to load node: %v", err) - } - - return node, nil -} - -// Prepare implements binprefix.TreeNode. It updates the hash of the node if not -// already set and returns the digest. -func (n *InteriorNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { - - if len(n.hash) > 0 { - // Hash is already calculated so we can skip and return. - return n.hash, nil - } - - h := fac.New() - - left, err := n.left.Prepare(nonce, new(big.Int).SetBit(prefix, int(n.depth), 0), b, fac) - if err != nil { - // No wrapping to prevent recursive calls to create huge error messages. - return nil, err - } - - right, err := n.right.Prepare(nonce, new(big.Int).SetBit(prefix, int(n.depth), 1), b, fac) - if err != nil { - // No wrapping to prevent recursive calls to create huge error messages. - return nil, err - } - - _, err = h.Write(append(left, right...)) - if err != nil { - return nil, xerrors.Errorf("interior node failed: %v", err) - } - - n.hash = h.Sum(nil) - - return n.GetHash(), nil -} - -// Visit implements binprefix.TreeNode. It executes the callback with the node -// and recursively with the children. -func (n *InteriorNode) Visit(fn func(TreeNode) error) error { - err := n.left.Visit(fn) - if err != nil { - // No wrapping to prevent long error message from recursive calls. - return err - } - - err = n.right.Visit(fn) - if err != nil { - // No wrapping to prevent long error message from recursive calls. - return err - } - - err = fn(n) - if err != nil { - return xerrors.Errorf("visiting interior: %v", err) - } - - return nil -} - -// Clone implements binprefix.TreeNode. It returns a deep copy of the interior -// node. -func (n *InteriorNode) Clone() TreeNode { - return &InteriorNode{ - depth: n.depth, - prefix: n.prefix, - left: n.left.Clone(), - right: n.right.Clone(), - } -} - -// Serialize implements serde.Message. It returns the JSON data of the interior -// node. -func (n *InteriorNode) Serialize(ctx serde.Context) ([]byte, error) { - format := nodeFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, n) - if err != nil { - return nil, xerrors.Errorf("failed to encode interior node: %v", err) - } - - return data, nil -} - -// LeafNode is a leaf node with a key and a value. -// -// - implements binprefix.TreeNode -type LeafNode struct { - hash []byte - depth uint16 - key *big.Int - value []byte -} - -// NewLeafNode creates a new leaf node. -func NewLeafNode(depth uint16, key *big.Int, value []byte) *LeafNode { - return NewLeafNodeWithDigest(depth, key, value, nil) -} - -// NewLeafNodeWithDigest creates a new leaf node with its digest. -func NewLeafNodeWithDigest(depth uint16, key *big.Int, value, hash []byte) *LeafNode { - return &LeafNode{ - hash: hash, - depth: depth, - key: key, - value: value, - } -} - -// GetHash implements binprefix.TreeNode. It returns the hash of the node. -func (n *LeafNode) GetHash() []byte { - return append([]byte{}, n.hash...) -} - -// GetDepth returns the depth of the node. -func (n *LeafNode) GetDepth() uint16 { - return n.depth -} - -// GetKey returns the key of the leaf node. -func (n *LeafNode) GetKey() []byte { - return n.key.Bytes() -} - -// GetValue returns the value of the leaf node. -func (n *LeafNode) GetValue() []byte { - return n.value -} - -// GetType implements binprefix.TreeNode. It returns the leaf node type. -func (n *LeafNode) GetType() byte { - return leafNodeType -} - -// Search implements binprefix.TreeNode. It returns the value if the key -// matches. -func (n *LeafNode) Search(key *big.Int, path *Path, b kv.Bucket) ([]byte, error) { - if path != nil { - path.value = n.value - } - - if n.key.Cmp(key) == 0 { - return n.value, nil - } - - return nil, nil -} - -// Insert implements binprefix.TreeNode. It replaces the leaf node by an -// interior node that contains both the current pair and the new one to insert. -func (n *LeafNode) Insert(key *big.Int, value []byte, b kv.Bucket) (TreeNode, error) { - if n.key.Cmp(key) == 0 { - n.hash = nil - n.value = value - return n, nil - } - - prefix := new(big.Int) - for i := 0; i < int(n.depth); i++ { - prefix.SetBit(prefix, i, key.Bit(i)) - } - - node := NewInteriorNode(n.depth, prefix) - - // As the node is freshly created, the operations are in-memory and thus it - // doesn't trigger any error. - node.Insert(n.key, n.value, b) - node.Insert(key, value, b) - - return node, nil -} - -// Delete implements binprefix.TreeNode. It removes the leaf if the key matches. -func (n *LeafNode) Delete(key *big.Int, b kv.Bucket) (TreeNode, error) { - if n.key.Cmp(key) == 0 { - return NewEmptyNode(n.depth, key), nil - } - - return n, nil -} - -// Prepare implements binprefix.TreeNode. It updates the hash of the node if not -// already set and returns the digest. -func (n *LeafNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { - - if len(n.hash) > 0 { - // Hash is already calculated so we can skip and return. - return n.hash, nil - } - - h := fac.New() - - data := make([]byte, 1+len(nonce)+DepthLength+bilen(prefix)+bilen(n.key)+len(n.value)) - data[0] = leafNodeType - cursor := 1 - copy(data[cursor:], nonce) - cursor += len(nonce) - copy(data[cursor:], int2buffer(n.depth)) - cursor += DepthLength - copy(data[cursor:], prefix.Bytes()) - cursor += bilen(prefix) - copy(data[cursor:], n.key.Bytes()) - cursor += bilen(n.key) - copy(data[cursor:], n.value) - - _, err := h.Write(data) - if err != nil { - return nil, xerrors.Errorf("leaf node failed: %v", err) - } - - n.hash = h.Sum(nil) - - return n.GetHash(), nil -} - -// Visit implements binprefix.TreeNode. It executes the callback with the node. -func (n *LeafNode) Visit(fn func(TreeNode) error) error { - err := fn(n) - if err != nil { - return xerrors.Errorf("visiting leaf: %v", err) - } - - return nil -} - -// Clone implements binprefix.TreeNode. It returns a copy of the leaf node. -func (n *LeafNode) Clone() TreeNode { - return NewLeafNode(n.depth, n.key, n.value) -} - -// Serialize implements serde.Message. It returns the JSON data of the leaf -// node. -func (n *LeafNode) Serialize(ctx serde.Context) ([]byte, error) { - format := nodeFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, n) - if err != nil { - return nil, xerrors.Errorf("failed to encode leaf node: %v", err) - } - - return data, nil -} - -// NodeKey is the key for the node factory. -type NodeKey struct{} - -// NodeFactory is the factory to deserialize tree nodes. -// -// - implements serde.Factory -type NodeFactory struct{} - -// Deserialize implements serde.Factory. It populates the tree node associated -// to the data if appropriate, otherwise it returns an error. -func (f NodeFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := nodeFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, NodeKey{}, f) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("format failed: %v", err) - } - - return msg, nil -} - -func int2buffer(depth uint16) []byte { - buffer := make([]byte, 2) - binary.LittleEndian.PutUint16(buffer, depth) - - return buffer -} - -// makeKey is a helper to transform a key in bytes to its big number -// equivalence. -func makeKey(key []byte) *big.Int { - bi := new(big.Int) - bi.SetBytes(key) - - return bi -} - -func bilen(n *big.Int) int { - return int(math.Ceil(float64(n.BitLen()) / 8.0)) -} diff --git a/dela/core/store/hashtree/binprefix/tree_test.go b/dela/core/store/hashtree/binprefix/tree_test.go deleted file mode 100644 index 05c02b4..0000000 --- a/dela/core/store/hashtree/binprefix/tree_test.go +++ /dev/null @@ -1,702 +0,0 @@ -package binprefix - -import ( - "math" - "math/big" - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "golang.org/x/xerrors" -) - -func init() { - nodeFormats.Register(fake.BadFormat, fake.NewBadFormat()) -} - -func TestTree_Len(t *testing.T) { - tree := NewTree(Nonce{}) - require.Equal(t, 0, tree.Len()) - - tree.root = NewInteriorNode(0, big.NewInt(0)) - require.Equal(t, 0, tree.Len()) - - tree.root.(*InteriorNode).left = NewLeafNode(1, nil, nil) - require.Equal(t, 1, tree.Len()) - - tree.root.(*InteriorNode).right = NewLeafNode(1, nil, nil) - require.Equal(t, 2, tree.Len()) -} - -func TestTree_Load(t *testing.T) { - tree := NewTree(Nonce{}) - - bucket := makeBucket(t) - - err := tree.FillFromBucket(bucket) - require.NoError(t, err) - - tree.factory = fake.NewBadMessageFactory() - err = tree.FillFromBucket(bucket) - require.EqualError(t, err, fake.Err("while scanning: tree node malformed")) -} - -func TestTree_Search(t *testing.T) { - tree := NewTree(Nonce{}) - - value, err := tree.Search(nil, nil, &fakeBucket{}) - require.NoError(t, err) - require.Nil(t, value) - - tree.root = NewLeafNode(0, makeKey([]byte("A")), []byte("B")) - value, err = tree.Search([]byte("A"), nil, &fakeBucket{}) - require.NoError(t, err) - require.Equal(t, []byte("B"), value) - - _, err = tree.Search(make([]byte, MaxDepth+1), nil, &fakeBucket{}) - require.EqualError(t, err, "mismatch key length 33 > 32") - - tree.root = fakeNode{err: fake.GetError()} - _, err = tree.Search([]byte("A"), nil, nil) - require.EqualError(t, err, fake.Err("failed to search")) -} - -func TestTree_Insert(t *testing.T) { - tree := NewTree(Nonce{}) - - err := tree.Insert([]byte("ping"), []byte("pong"), &fakeBucket{}) - require.NoError(t, err) - require.Equal(t, 1, tree.Len()) - - err = tree.Insert(make([]byte, MaxDepth+1), nil, &fakeBucket{}) - require.EqualError(t, err, "mismatch key length 33 > 32") - - tree.root = fakeNode{err: fake.GetError()} - err = tree.Insert([]byte("A"), []byte("B"), nil) - require.EqualError(t, err, fake.Err("failed to insert")) -} - -func TestTree_Insert_UpdateLeaf(t *testing.T) { - bucket := &fakeBucket{} - hashFactory := crypto.NewSha256Factory() - tree := NewTree(Nonce{}) - - for i := 0; i <= math.MaxUint8; i++ { - key := []byte{byte(i)} - err := tree.Insert(key[:], key[:], bucket) - require.NoError(t, err) - } - - err := tree.CalculateRoot(hashFactory, bucket) - require.NoError(t, err) - - updateKey := makeKey([]byte{byte(math.MaxUint8 - 1)}) - - var existingHash []byte - tree.root.Visit(func(node TreeNode) error { - lnode, ok := node.(*LeafNode) - if ok && lnode.key.Cmp(updateKey) == 0 { - existingHash = lnode.GetHash() - } - - return nil - }) - - require.NotEmpty(t, existingHash) - - err = tree.Persist(bucket) - require.NoError(t, err) - - clone := tree.Clone() - - err = clone.Insert([]byte{byte(math.MaxUint8 - 1)}, []byte{0}, bucket) - require.NoError(t, err) - - err = clone.CalculateRoot(hashFactory, bucket) - require.NoError(t, err) - - var updatedHash []byte - clone.root.Visit(func(node TreeNode) error { - lnode, ok := node.(*LeafNode) - if ok && lnode.key.Cmp(updateKey) == 0 { - updatedHash = lnode.GetHash() - } - return nil - }) - - require.NotEmpty(t, updatedHash) - require.NotEqual(t, existingHash, updatedHash) -} - -func TestTree_Delete(t *testing.T) { - tree := NewTree(Nonce{}) - - err := tree.Delete(nil, &fakeBucket{}) - require.NoError(t, err) - - err = tree.Delete([]byte("ping"), &fakeBucket{}) - require.NoError(t, err) - - tree.root = NewLeafNode(0, makeKey([]byte("ping")), []byte("pong")) - err = tree.Delete([]byte("ping"), &fakeBucket{}) - require.NoError(t, err) - require.IsType(t, (*EmptyNode)(nil), tree.root) - - err = tree.Delete(make([]byte, MaxDepth+1), &fakeBucket{}) - require.EqualError(t, err, "mismatch key length 33 > 32") - - tree.root = fakeNode{err: fake.GetError()} - err = tree.Delete([]byte("A"), nil) - require.EqualError(t, err, fake.Err("failed to delete")) -} - -func TestTree_Persist(t *testing.T) { - bucket := &fakeBucket{} - - tree := NewTree(Nonce{}) - - // 1. Test if the tree can be stored in disk with the default parameter to - // only store leaf nodes. - for i := 0; i <= math.MaxUint8; i++ { - key := []byte{byte(i)} - - err := tree.Insert(key[:], key[:], bucket) - require.NoError(t, err) - } - - err := tree.Persist(bucket) - require.NoError(t, err) - - count := 0 - for _, value := range bucket.values { - require.Regexp(t, `{"Leaf":{[^}]+}}`, string(value)) - count++ - } - require.Equal(t, 256, count) - - // 2. Test with a memory depth of 6 which means that only disk nodes should - // exist after this level. - tree.memDepth = 6 - err = tree.Persist(bucket) - require.NoError(t, err) - - tree.root.Visit(func(n TreeNode) error { - switch node := n.(type) { - case *InteriorNode: - require.LessOrEqual(t, int(node.depth), 6) - case *EmptyNode: - require.LessOrEqual(t, int(node.depth), 6) - case *LeafNode: - t.Fatal("no leaf node expected in-memory") - } - return nil - }) - - count = 0 - for _, value := range bucket.values { - require.Regexp(t, `{"(Leaf|Interior)":{[^}]+}}`, string(value)) - count++ - } - // 2^9-1 - (2^7-1) = 384 - require.Equal(t, 384, count) - - // 3. Test with only the root in-memory. - tree.memDepth = 0 - err = tree.Persist(bucket) - require.NoError(t, err) - // 2^9-1 = 511 (root is not on disk) - require.Equal(t, 510, len(bucket.values)) - - tree.root.Visit(func(n TreeNode) error { - switch node := n.(type) { - case *InteriorNode: - // Only the root is allow as an interior node. - require.Equal(t, uint16(0), node.depth) - case *EmptyNode: - t.Fatal("expect only disk nodes") - case *LeafNode: - t.Fatal("expect only disk nodes") - } - return nil - }) - - tree.root = NewInteriorNode(0, big.NewInt(0)) - - err = tree.Persist(&fakeBucket{errSet: fake.GetError()}) - require.EqualError(t, err, - fake.Err("visiting empty: failed to store node: failed to set key")) - - err = tree.Persist(&fakeBucket{errScan: fake.GetError()}) - require.EqualError(t, err, - fake.Err("visiting empty: failed to clean subtree")) -} - -func TestTree_Clone(t *testing.T) { - tree := NewTree(Nonce{}) - - f := func(key [8]byte, value []byte) bool { - return tree.Insert(key[:], value, &fakeBucket{}) == nil - } - - err := quick.Check(f, nil) - require.NoError(t, err) - - clone := tree.Clone() - require.Equal(t, tree.Len(), clone.Len()) - - require.NoError(t, tree.CalculateRoot(crypto.NewSha256Factory(), &fakeBucket{})) - require.NoError(t, clone.CalculateRoot(crypto.NewSha256Factory(), &fakeBucket{})) - require.Equal(t, tree.root.GetHash(), clone.root.GetHash()) -} - -func TestEmptyNode_GetHash(t *testing.T) { - node := NewEmptyNode(0, big.NewInt(0)) - require.Empty(t, node.GetHash()) - - node.hash = []byte("ping") - require.Equal(t, []byte("ping"), node.GetHash()) -} - -func TestEmptyNode_GetType(t *testing.T) { - node := NewEmptyNode(0, big.NewInt(0)) - require.Equal(t, emptyNodeType, node.GetType()) -} - -func TestEmptyNode_Search(t *testing.T) { - node := NewEmptyNode(0, big.NewInt(0)) - path := newPath([]byte{}, nil) - - value, err := node.Search(new(big.Int), &path, nil) - require.NoError(t, err) - require.Nil(t, value) - require.Nil(t, path.value) -} - -func TestEmptyNode_Insert(t *testing.T) { - node := NewEmptyNode(0, big.NewInt(0)) - - next, err := node.Insert(big.NewInt(0), []byte("pong"), nil) - require.NoError(t, err) - require.IsType(t, (*LeafNode)(nil), next) -} - -func TestEmptyNode_Delete(t *testing.T) { - node := NewEmptyNode(0, big.NewInt(0)) - - next, err := node.Delete(nil, nil) - require.NoError(t, err) - require.Same(t, node, next) -} - -func TestEmptyNode_Prepare(t *testing.T) { - node := NewEmptyNode(3, big.NewInt(0)) - - fac := crypto.NewSha256Factory() - - hash, err := node.Prepare([]byte("nonce"), new(big.Int), nil, fac) - require.NoError(t, err) - require.Equal(t, hash, node.hash) - - calls := &fake.Call{} - node.hash = nil - _, err = node.Prepare([]byte{1}, new(big.Int).SetBytes([]byte{2}), nil, fake.NewHashFactory(&fake.Hash{Call: calls})) - require.NoError(t, err) - require.Equal(t, 1, calls.Len()) - require.Equal(t, "\x00\x01\x02\x03\x00", string(calls.Get(0, 0).([]byte))) - - node.hash = nil - _, err = node.Prepare([]byte{1}, new(big.Int), nil, fake.NewHashFactory(fake.NewBadHash())) - require.EqualError(t, err, fake.Err("empty node failed")) -} - -func TestEmptyNode_Visit(t *testing.T) { - node := NewEmptyNode(3, big.NewInt(0)) - count := 0 - - node.Visit(func(tn TreeNode) error { - require.Same(t, node, tn) - count++ - - return nil - }) - - require.Equal(t, 1, count) -} - -func TestEmptyNode_Clone(t *testing.T) { - node := NewEmptyNode(3, big.NewInt(0)) - - clone := node.Clone() - require.Equal(t, node, clone) -} - -func TestEmptyNode_Serialize(t *testing.T) { - node := NewEmptyNode(3, big.NewInt(1)) - - data, err := node.Serialize(testCtx) - require.Equal(t, `{"Empty":{"Digest":"","Depth":3,"Prefix":"AQ=="}}`, string(data)) - require.NoError(t, err) - - _, err = node.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("failed to encode empty node")) -} - -func TestInteriorNode_GetHash(t *testing.T) { - node := NewInteriorNode(3, big.NewInt(0)) - - require.Empty(t, node.GetHash()) - - node.hash = []byte{1, 2, 3} - require.Equal(t, []byte{1, 2, 3}, node.GetHash()) -} - -func TestInteriorNode_GetType(t *testing.T) { - node := NewInteriorNode(3, big.NewInt(0)) - - require.Equal(t, interiorNodeType, node.GetType()) -} - -func TestInteriorNode_Search(t *testing.T) { - node := NewInteriorNode(0, big.NewInt(0)) - node.left = NewLeafNode(1, big.NewInt(0), []byte("ping")) - node.right = NewLeafNode(1, big.NewInt(1), []byte("pong")) - - path := newPath([]byte{}, nil) - value, err := node.Search(big.NewInt(0), &path, nil) - require.NoError(t, err) - require.Equal(t, "ping", string(value)) - require.Len(t, path.interiors, 1) - - path = newPath([]byte{}, nil) - value, err = node.Search(big.NewInt(1), &path, nil) - require.NoError(t, err) - require.Equal(t, "pong", string(value)) - require.Len(t, path.interiors, 1) -} - -func TestInteriorNode_Search_Error(t *testing.T) { - node := NewInteriorNode(0, big.NewInt(0)) - node.left = NewDiskNode(0, nil, testCtx, NodeFactory{}) - - _, err := node.Search(big.NewInt(0), nil, &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 0 (depth 0) not in database") - - node.left = nil - node.right = NewDiskNode(0, nil, testCtx, NodeFactory{}) - - _, err = node.Search(big.NewInt(1), nil, &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 1 (depth 0) not in database") -} - -func TestInteriorNode_Insert(t *testing.T) { - node := NewInteriorNode(0, big.NewInt(0)) - - next, err := node.Insert(big.NewInt(0), []byte("ping"), nil) - require.NoError(t, err) - require.Same(t, node, next) - - next, err = node.Insert(big.NewInt(1), []byte("pong"), nil) - require.NoError(t, err) - require.Same(t, node, next) -} - -func TestInteriorNode_Delete(t *testing.T) { - node := NewInteriorNode(0, big.NewInt(0)) - node.left = NewLeafNode(1, big.NewInt(0), []byte("ping")) - node.right = NewLeafNode(1, big.NewInt(1), []byte("pong")) - - next, err := node.Delete(big.NewInt(0), nil) - require.NoError(t, err) - require.Same(t, node, next) - - next, err = node.Delete(big.NewInt(1), nil) - require.NoError(t, err) - require.IsType(t, (*EmptyNode)(nil), next) - - node.right = NewDiskNode(1, nil, testCtx, NodeFactory{}) - _, err = node.Delete(big.NewInt(0), &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 1 (depth 1) not in database") - - node.right = NewEmptyNode(1, big.NewInt(2)) - node.left = NewDiskNode(1, nil, testCtx, NodeFactory{}) - _, err = node.Delete(big.NewInt(1), &fakeBucket{}) - require.EqualError(t, err, "failed to load node: prefix 0 (depth 1) not in database") -} - -func TestInteriorNode_Prepare(t *testing.T) { - node := NewInteriorNode(1, big.NewInt(0)) - node.left = fakeNode{data: []byte{0xaa}} - node.right = fakeNode{data: []byte{0xbb}} - calls := &fake.Call{} - - _, err := node.Prepare([]byte{1}, big.NewInt(1), nil, fake.NewHashFactory(&fake.Hash{Call: calls})) - require.NoError(t, err) - require.Equal(t, 1, calls.Len()) - require.Equal(t, "\xaa\xbb", string(calls.Get(0, 0).([]byte))) - - node.hash = nil - node.left = fakeNode{err: xerrors.New("bad node error")} - _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewSha256Factory()) - require.EqualError(t, err, "bad node error") - - node.left = fakeNode{} - node.right = fakeNode{err: xerrors.New("bad node error")} - _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewSha256Factory()) - require.EqualError(t, err, "bad node error") - - node.right = fakeNode{} - _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, fake.NewHashFactory(fake.NewBadHash())) - require.EqualError(t, err, fake.Err("interior node failed")) -} - -func TestInteriorNode_Visit(t *testing.T) { - node := NewInteriorNode(0, big.NewInt(0)) - - counter := 0 - err := node.Visit(func(n TreeNode) error { - if counter == 2 { - require.IsType(t, node, n) - } else { - require.IsType(t, (*EmptyNode)(nil), n) - } - counter++ - - return nil - }) - require.NoError(t, err) - require.Equal(t, 3, counter) - - node.left = fakeNode{} - node.right = fakeNode{} - err = node.Visit(func(TreeNode) error { return fake.GetError() }) - require.EqualError(t, err, fake.Err("visiting interior")) - - node.right = NewEmptyNode(1, big.NewInt(2)) - err = node.Visit(func(TreeNode) error { return fake.GetError() }) - require.EqualError(t, err, fake.Err("visiting empty")) -} - -func TestInteriorNode_Clone(t *testing.T) { - node := NewInteriorNode(0, big.NewInt(0)) - - clone := node.Clone() - require.Equal(t, node, clone) -} - -func TestInteriorNode_Serialize(t *testing.T) { - node := NewInteriorNode(2, big.NewInt(3)) - - data, err := node.Serialize(testCtx) - require.NoError(t, err) - require.Equal(t, `{"Interior":{"Digest":"","Depth":2,"Prefix":"Aw=="}}`, string(data)) - - _, err = node.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("failed to encode interior node")) -} - -func TestLeafNode_GetHash(t *testing.T) { - node := NewLeafNode(0, makeKey([]byte("ping")), []byte("pong")) - - require.Empty(t, node.GetHash()) - - _, err := node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewSha256Factory()) - require.NoError(t, err) - require.Len(t, node.GetHash(), 32) -} - -func TestLeafNode_GetType(t *testing.T) { - node := NewLeafNode(0, makeKey([]byte("ping")), []byte("pong")) - - require.Equal(t, leafNodeType, node.GetType()) -} - -func TestLeafNode_Search(t *testing.T) { - node := NewLeafNode(0, makeKey([]byte("ping")), []byte("pong")) - path := newPath([]byte{}, []byte("ping")) - - value, err := node.Search(makeKey([]byte("ping")), &path, nil) - require.NoError(t, err) - require.Equal(t, []byte("pong"), value) - require.Equal(t, []byte("pong"), path.value) - - value, err = node.Search(makeKey([]byte("pong")), nil, nil) - require.NoError(t, err) - require.Nil(t, value) -} - -func TestLeafNode_Insert(t *testing.T) { - node := NewLeafNode(0, makeKey([]byte("ping")), []byte("pong")) - - next, err := node.Insert(makeKey([]byte("ping")), []byte("abc"), nil) - require.NoError(t, err) - require.Same(t, node, next) - require.Equal(t, []byte("abc"), next.(*LeafNode).value) - - node = NewLeafNode(0, makeKey([]byte{0}), []byte{0xaa}) - next, err = node.Insert(makeKey([]byte{1}), []byte{0xbb}, nil) - require.NoError(t, err) - require.IsType(t, (*InteriorNode)(nil), next) - require.IsType(t, (*LeafNode)(nil), next.(*InteriorNode).left) - require.IsType(t, (*LeafNode)(nil), next.(*InteriorNode).right) - - node = NewLeafNode(0, makeKey([]byte{1}), []byte{0xaa}) - next, err = node.Insert(makeKey([]byte{0}), []byte{0xbb}, nil) - require.NoError(t, err) - require.IsType(t, (*InteriorNode)(nil), next) - require.IsType(t, (*LeafNode)(nil), next.(*InteriorNode).left) - require.IsType(t, (*LeafNode)(nil), next.(*InteriorNode).right) -} - -func TestLeafNode_Delete(t *testing.T) { - node := NewLeafNode(0, makeKey([]byte("ping")), []byte("pong")) - - next, err := node.Delete(makeKey([]byte("pong")), nil) - require.NoError(t, err) - require.Same(t, node, next) - - next, err = node.Delete(makeKey([]byte("ping")), nil) - require.NoError(t, err) - require.IsType(t, (*EmptyNode)(nil), next) -} - -func TestLeafNode_Prepare(t *testing.T) { - node := NewLeafNode(3, makeKey([]byte("ping")), []byte("pong")) - calls := &fake.Call{} - - _, err := node.Prepare([]byte{1}, big.NewInt(2), nil, fake.NewHashFactory(&fake.Hash{Call: calls})) - require.NoError(t, err) - require.Equal(t, 1, calls.Len()) - require.Equal(t, "\x02\x01\x03\x00\x02pingpong", string(calls.Get(0, 0).([]byte))) - - node.hash = nil - _, err = node.Prepare(nil, big.NewInt(0), nil, fake.NewHashFactory(fake.NewBadHash())) - require.EqualError(t, err, fake.Err("leaf node failed")) -} - -func TestLeafNode_Visit(t *testing.T) { - node := NewLeafNode(3, makeKey([]byte("ping")), []byte("pong")) - - counter := 0 - err := node.Visit(func(n TreeNode) error { - counter++ - require.IsType(t, node, n) - - return nil - }) - require.NoError(t, err) - require.Equal(t, 1, counter) - - err = node.Visit(func(n TreeNode) error { return fake.GetError() }) - require.EqualError(t, err, fake.Err("visiting leaf")) -} - -func TestLeafNode_Clone(t *testing.T) { - node := NewLeafNode(3, makeKey([]byte("ping")), []byte("pong")) - - clone := node.Clone() - require.Equal(t, node, clone) -} - -func TestLeafNode_Serialize(t *testing.T) { - node := NewLeafNode(2, big.NewInt(2), []byte{0xaa}) - - data, err := node.Serialize(testCtx) - require.NoError(t, err) - require.Equal(t, `{"Leaf":{"Digest":"","Depth":2,"Prefix":"Ag==","Value":"qg=="}}`, string(data)) - - _, err = node.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("failed to encode leaf node")) -} - -func TestNodeFactory_Deserialize(t *testing.T) { - fac := NodeFactory{} - - msg, err := fac.Deserialize(testCtx, []byte(`{"Empty":{}}`)) - require.NoError(t, err) - require.IsType(t, &EmptyNode{}, msg) - - _, err = fac.Deserialize(testCtx, []byte(`{}`)) - require.EqualError(t, err, "format failed: message is empty") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeBucket(t *testing.T) *fakeBucket { - emptyNode, err := NewEmptyNode(1, big.NewInt(0)).Serialize(testCtx) - require.NoError(t, err) - - leafNode, err := NewLeafNode(1, big.NewInt(1), []byte{1, 2, 3}).Serialize(testCtx) - require.NoError(t, err) - - bucket := &fakeBucket{ - values: map[string][]byte{ - string([]byte{0}): emptyNode, - string([]byte{1}): leafNode, - }, - } - - return bucket -} - -type fakeTx struct { - kv.WritableTx - - bucket kv.Bucket -} - -func (tx fakeTx) GetBucket(name []byte) kv.Bucket { - return tx.bucket -} - -func (tx fakeTx) GetBucketOrCreate(name []byte) (kv.Bucket, error) { - return nil, nil -} - -type fakeDB struct { - kv.DB - err error -} - -func (db fakeDB) View(fn func(kv.ReadableTx) error) error { - return fn(fakeTx{}) -} - -func (db fakeDB) Update(fn func(kv.WritableTx) error) error { - if db.err != nil { - return db.err - } - return fn(fakeTx{}) -} - -type fakeNode struct { - TreeNode - - data []byte - err error -} - -func (n fakeNode) Search(key *big.Int, path *Path, bucket kv.Bucket) ([]byte, error) { - return nil, n.err -} - -func (n fakeNode) Insert(key *big.Int, value []byte, b kv.Bucket) (TreeNode, error) { - return n, n.err -} - -func (n fakeNode) Delete(key *big.Int, b kv.Bucket) (TreeNode, error) { - return nil, n.err -} - -func (n fakeNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { - - return n.data, n.err -} - -func (n fakeNode) Visit(func(TreeNode) error) error { - return n.err -} diff --git a/dela/core/store/hashtree/hashtree.go b/dela/core/store/hashtree/hashtree.go deleted file mode 100644 index 59295d5..0000000 --- a/dela/core/store/hashtree/hashtree.go +++ /dev/null @@ -1,54 +0,0 @@ -// Package hashtree defines the specialization of the store as a Merkle tree. -// -// Merkle trees allow the creation of proofs to demonstrate if a key/value pair -// is stored in the tree, or if it is not. -// -// Documentation Last Review: 08.10.2020 -package hashtree - -import "go.dedis.ch/dela/core/store" - -// Path is a path along the tree to a key and its value, or none if the key is -// not set. -type Path interface { - // GetKey returns the key of the path. - GetKey() []byte - - // GetValue returns the value of the path, or nil if it is not set. - GetValue() []byte - - // GetRoot returns the store root calculated from the key. It should match - // the tree root for the path to be valid. - GetRoot() []byte -} - -// Tree is a specialization of a store. It uses the Merkle tree structure to -// create a root hash that represents the state of the tree and can be used to -// create proof of inclusion/proof of absence. -type Tree interface { - store.Readable - - // GetRoot returns the root hash of this tree. - GetRoot() []byte - - // GetPath returns a path to a key and its value in the tree. It can be used - // as a proof of inclusion or a proof of absence in the contrary. - GetPath(key []byte) (Path, error) - - // Stage must create a writable tree from the current one that will be - // passed to the callback, then return it. - Stage(func(store.Snapshot) error) (StagingTree, error) -} - -// StagingTree is a tree that has been modified in-memory but is yet to be -// committed to the disk. -type StagingTree interface { - Tree - - // WithTx decorates the staging tree to use a transaction to perform - // operations on the database while using the same underlying data. - WithTx(store.Transaction) StagingTree - - // Commit writes the tree to a persistent storage. - Commit() error -} diff --git a/dela/core/store/kv/controller/controller.go b/dela/core/store/kv/controller/controller.go deleted file mode 100644 index b0b3d8c..0000000 --- a/dela/core/store/kv/controller/controller.go +++ /dev/null @@ -1,56 +0,0 @@ -// Package controller implements a CLI controller for the key/value database. -// -// Documentation Last Review: 08.10.2020 -package controller - -import ( - "path/filepath" - - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/store/kv" - "golang.org/x/xerrors" -) - -// MinimalController is a CLI controller to inject a key/value database. -// -// - implements node.Initializer -type minimalController struct{} - -// NewController returns a minimal controller that will inject a key/value -// database. -func NewController() node.Initializer { - return minimalController{} -} - -// SetCommands implements node.Initializer. It does not register any command. -func (m minimalController) SetCommands(builder node.Builder) {} - -// OnStart implements node.Initializer. It opens the database in a file using -// the config path as the base. -func (m minimalController) OnStart(flags cli.Flags, inj node.Injector) error { - db, err := kv.New(filepath.Join(flags.String("config"), "dela.db")) - if err != nil { - return xerrors.Errorf("db: %v", err) - } - - inj.Inject(db) - - return nil -} - -// OnStop implements node.Initializer. It closes the database. -func (m minimalController) OnStop(inj node.Injector) error { - var db kv.DB - err := inj.Resolve(&db) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - err = db.Close() - if err != nil { - return xerrors.Errorf("while closing db: %v", err) - } - - return nil -} diff --git a/dela/core/store/kv/default.go b/dela/core/store/kv/default.go deleted file mode 100644 index 5fa4ad6..0000000 --- a/dela/core/store/kv/default.go +++ /dev/null @@ -1,143 +0,0 @@ -// This file contains the implementation of a key/value database using bbolt. -// -// See https://github.com/etcd-io/bbolt. -// -// Documentation Last Review: 08.10.2020 -// - -package kv - -import ( - "bytes" - - "go.etcd.io/bbolt" - "golang.org/x/xerrors" -) - -// BoltDB is an adapter of the KV database using bboltdb. -// -// - implements kv.DB -type boltDB struct { - bolt *bbolt.DB -} - -// New opens a new database to the given file. -func New(path string) (DB, error) { - db, err := bbolt.Open(path, 0666, &bbolt.Options{}) - if err != nil { - return nil, xerrors.Errorf("failed to open db: %v", err) - } - - bdb := boltDB{ - bolt: db, - } - - return bdb, nil -} - -// View implements kv.DB. It executes the read-only transaction in the context -// of the database. -func (db boltDB) View(fn func(ReadableTx) error) error { - return db.bolt.View(func(txn *bbolt.Tx) error { - return fn(boltTx{txn: txn}) - }) -} - -// Update implements kv.DB. It executes the writable transaction in the context -// of the database. -func (db boltDB) Update(fn func(WritableTx) error) error { - return db.bolt.Update(func(txn *bbolt.Tx) error { - return fn(boltTx{txn: txn}) - }) -} - -// Close implements kv.DB. It closes the database. Any view or update call will -// result in an error after this function is called. -func (db boltDB) Close() error { - return db.bolt.Close() -} - -// BoltTx is the adapter of a bbolt transaction for the key/value database. -// -// - implements kv.ReadableTx -// - implements kv.WritableTx -type boltTx struct { - txn *bbolt.Tx -} - -// GetBucket implements kv.ReadableTx. It returns the bucket with the given name -// or nil if it does not exist. -func (tx boltTx) GetBucket(name []byte) Bucket { - bucket := tx.txn.Bucket(name) - if bucket == nil { - return nil - } - - return boltBucket{bucket: bucket} -} - -// GetBucketOrCreate implements kv.WritableTx. It creates the bucket if it does -// not exist and then return it. -func (tx boltTx) GetBucketOrCreate(name []byte) (Bucket, error) { - bucket, err := tx.txn.CreateBucketIfNotExists(name) - if err != nil { - return nil, xerrors.Errorf("create bucket failed: %v", err) - } - - return boltBucket{bucket: bucket}, nil -} - -// OnCommit implements store.Transaction. It registers a callback that is called -// after the transaction is successful. -func (tx boltTx) OnCommit(fn func()) { - tx.txn.OnCommit(fn) -} - -// BoltBucket is the adapter of a bbolt bucket for the key/value database. -// -// - implements kv.Bucket -type boltBucket struct { - bucket *bbolt.Bucket -} - -// Get implements kv.Bucket. It returns the value associated to the key, or nil -// if it does not exist. -func (txn boltBucket) Get(key []byte) []byte { - return txn.bucket.Get(key) -} - -// Set implements kv.Bucket. It sets the provided key to the value. -func (txn boltBucket) Set(key, value []byte) error { - return txn.bucket.Put(key, value) -} - -// Delete implements kv.Bucket. It deletes the key from the bucket. -func (txn boltBucket) Delete(key []byte) error { - return txn.bucket.Delete(key) -} - -// ForEach implements kv.Bucket. It iterates over the whole bucket in an -// unspecified order. If the callback returns an error, the iteration is stopped -// and the error returned to the caller. -func (txn boltBucket) ForEach(fn func(k, v []byte) error) error { - return txn.bucket.ForEach(fn) -} - -// Scan implements kv.Bucket. It iterates over the keys matching the prefix in a -// sorted order. If the callback returns an error, the iteration is stopped and -// the error returned to the caller. -func (txn boltBucket) Scan(prefix []byte, fn func(k, v []byte) error) error { - cursor := txn.bucket.Cursor() - cursor.Seek(prefix) - - for k, v := cursor.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = cursor.Next() { - err := fn(k, v) - if err != nil { - // The caller is responsible for wrapping the errors inside the - // callback, as it returns the exact error to allow comparison. - return err - } - } - - return nil -} diff --git a/dela/core/store/kv/default_test.go b/dela/core/store/kv/default_test.go deleted file mode 100644 index 2f8b084..0000000 --- a/dela/core/store/kv/default_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package kv - -import ( - "os" - "path/filepath" - "testing" - "time" - - "github.com/stretchr/testify/require" - "golang.org/x/xerrors" -) - -const delaTestDir = "dela-core-kv" - -func TestBoltDB_UpdateAndView(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - ch := make(chan struct{}) - err = db.Update(func(txn WritableTx) error { - txn.OnCommit(func() { close(ch) }) - - bucket, err := txn.GetBucketOrCreate([]byte("bucket")) - require.NoError(t, err) - - return bucket.Set([]byte("ping"), []byte("pong")) - }) - require.NoError(t, err) - - select { - case <-ch: - case <-time.After(time.Second): - t.Fatal("timeout") - } - - err = db.View(func(txn ReadableTx) error { - bucket := txn.GetBucket([]byte("bucket")) - require.NotNil(t, bucket) - - value := bucket.Get([]byte("ping")) - require.Equal(t, []byte("pong"), value) - - return nil - }) - require.NoError(t, err) -} - -func TestBoltDB_Close(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - err = db.Close() - require.NoError(t, err) - require.Error(t, db.(boltDB).bolt.Sync()) -} - -func TestBoltTx_GetBucket(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - err = db.Update(func(tx WritableTx) error { - require.Nil(t, tx.GetBucket([]byte("unknown"))) - - _, err := tx.GetBucketOrCreate([]byte("A")) - require.NoError(t, err) - require.NotNil(t, tx.GetBucket([]byte("A"))) - - _, err = tx.GetBucketOrCreate(nil) - require.EqualError(t, err, "create bucket failed: bucket name required") - - return nil - }) - require.NoError(t, err) -} - -func TestBoltBucket_Get_Set_Delete(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - err = db.Update(func(txn WritableTx) error { - b, err := txn.GetBucketOrCreate([]byte("bucket")) - require.NoError(t, err) - - require.NoError(t, b.Set([]byte("ping"), []byte("pong"))) - - value := b.Get([]byte("ping")) - require.Equal(t, []byte("pong"), value) - - value = b.Get([]byte("pong")) - require.Nil(t, value) - - require.NoError(t, b.Delete([]byte("ping"))) - - value = b.Get([]byte("ping")) - require.Nil(t, value) - - return nil - }) - - require.NoError(t, err) -} - -func TestBoltBucket_ForEach(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - err = db.Update(func(txn WritableTx) error { - b, err := txn.GetBucketOrCreate([]byte("test")) - require.NoError(t, err) - - require.NoError(t, b.Set([]byte{2}, []byte{2})) - require.NoError(t, b.Set([]byte{1}, []byte{1})) - require.NoError(t, b.Set([]byte{0}, []byte{0})) - - var i byte = 0 - return b.ForEach(func(k, v []byte) error { - require.Equal(t, []byte{i}, k) - require.Equal(t, []byte{i}, v) - i++ - return nil - }) - }) - require.NoError(t, err) -} - -func TestBoltBucket_Scan(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), delaTestDir) - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - err = db.Update(func(txn WritableTx) error { - b, err := txn.GetBucketOrCreate([]byte("bucket")) - require.NoError(t, err) - - require.NoError(t, b.Set([]byte{7}, []byte{7})) - require.NoError(t, b.Set([]byte{0}, []byte{0})) - - var i byte = 0 - b.Scan(nil, func(k, v []byte) error { - require.Equal(t, []byte{i}, k) - require.Equal(t, []byte{i}, v) - i += 7 - return nil - }) - require.Equal(t, byte(14), i) - - err = b.Scan([]byte{1}, func(k, v []byte) error { - return xerrors.New("callback error") - }) - require.NoError(t, err) - - err = b.Scan([]byte{}, func(k, v []byte) error { - return xerrors.New("callback error") - }) - require.EqualError(t, err, "callback error") - - return nil - }) - require.NoError(t, err) -} diff --git a/dela/core/store/kv/default_unix_test.go b/dela/core/store/kv/default_unix_test.go deleted file mode 100644 index 564e59c..0000000 --- a/dela/core/store/kv/default_unix_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build linux darwin - -package kv - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBoltDB_New(t *testing.T) { - db, err := New("") - require.Nil(t, db) - require.EqualError(t, err, "failed to open db: open : no such file or directory") -} diff --git a/dela/core/store/kv/default_windows_test.go b/dela/core/store/kv/default_windows_test.go deleted file mode 100644 index aa3a244..0000000 --- a/dela/core/store/kv/default_windows_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package kv - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBoltDB_New(t *testing.T) { - db, err := New("") - require.Nil(t, db) - require.EqualError(t, err, - "failed to open db: open : The system cannot find the file specified.") -} diff --git a/dela/core/store/kv/example_test.go b/dela/core/store/kv/example_test.go deleted file mode 100644 index be9113f..0000000 --- a/dela/core/store/kv/example_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package kv - -import ( - "fmt" - "os" - "path/filepath" -) - -func ExampleBucket_Scan() { - dir, err := os.MkdirTemp(os.TempDir(), "example") - if err != nil { - panic("failed to create folder: " + err.Error()) - } - - defer os.RemoveAll(dir) - - db, err := New(filepath.Join(dir, "example.db")) - if err != nil { - panic("failed to open db: " + err.Error()) - } - - pairs := [][]byte{ - {0b0101}, - {0b1111}, - {0b0000}, - {0b1110}, - } - - err = db.Update(func(tx WritableTx) error { - bucket, err := tx.GetBucketOrCreate([]byte("example_bucket")) - if err != nil { - return err - } - - for _, pair := range pairs { - err = bucket.Set(pair, pair) - if err != nil { - return err - } - } - - return nil - }) - if err != nil { - panic("database write failed: " + err.Error()) - } - - err = db.View(func(tx ReadableTx) error { - bucket := tx.GetBucket([]byte("example_bucket")) - if bucket == nil { - return nil - } - - return bucket.Scan(nil, func(key, value []byte) error { - fmt.Printf("%04b", key) - fmt.Println("") - return nil - }) - }) - if err != nil { - panic("database read failed: " + err.Error()) - } - - // Output: [0000] - // [0101] - // [1110] - // [1111] -} diff --git a/dela/core/store/kv/kv.go b/dela/core/store/kv/kv.go deleted file mode 100644 index f4d1cbd..0000000 --- a/dela/core/store/kv/kv.go +++ /dev/null @@ -1,63 +0,0 @@ -// Package kv defines the abstraction for a key/value database. -// -// The package also implements a default database implementation that is using -// bbolt as the engine (https://github.com/etcd-io/bbolt). -// -// Documentation Last Review: 08.10.2020 -package kv - -import "go.dedis.ch/dela/core/store" - -// Bucket is a general interface to operate on a database bucket. -type Bucket interface { - // Get reads the key from the bucket and returns the value, or nil if the - // key does not exist. - Get(key []byte) []byte - - // Set assigns the value to the provided key. - Set(key, value []byte) error - - // Delete deletes the key from the bucket. - Delete(key []byte) error - - // ForEach iterates over all the items in the bucket in a unspecified order. - // The iteration stops when the callback returns an error. - ForEach(func(k, v []byte) error) error - - // Scan iterates over every key that matches the prefix in an order - // determined by the implementation. The iteration stops when the callback - // returns an error. - Scan(prefix []byte, fn func(k, v []byte) error) error -} - -// ReadableTx allows one to perform read-only atomic operations on the database. -type ReadableTx interface { - // GetBucket returns the bucket of the given name if it exists, otherwise it - // returns nil. - GetBucket(name []byte) Bucket -} - -// WritableTx allows one to perform atomic operations on the database. -type WritableTx interface { - store.Transaction - - ReadableTx - - // GetBucketOrCreate returns the bucket of the given name if it exists, or - // it creates it. - GetBucketOrCreate(name []byte) (Bucket, error) -} - -// DB is a general interface to operate over a key/value database. -type DB interface { - // View executes the provided read-only transaction in the context of the - // database. - View(fn func(ReadableTx) error) error - - // Update executes the provided writable transaction in the context of the - // database. - Update(fn func(WritableTx) error) error - - // Close closes the database and free the resources. - Close() error -} diff --git a/dela/core/store/store.go b/dela/core/store/store.go deleted file mode 100644 index a1e20de..0000000 --- a/dela/core/store/store.go +++ /dev/null @@ -1,31 +0,0 @@ -// Package store defines the primitives of a simple key/value storage. -// -// Documentation Last Review: 08.10.2020 -package store - -// Readable is the interface for a readable store. -type Readable interface { - Get(key []byte) ([]byte, error) -} - -// Writable is the interface for a writable store. -type Writable interface { - Set(key []byte, value []byte) error - - Delete(key []byte) error -} - -// Snapshot is a state of the store that can be read and write independently. A -// write is applied only to the snapshot reference. -type Snapshot interface { - Readable - Writable -} - -// Transaction is a generic interface that store implementations can use to -// provide atomicity. -type Transaction interface { - // OnCommit adds a callback to be executed after the transaction - // successfully commits. - OnCommit(func()) -} diff --git a/dela/core/txn/pool/controller/action.go b/dela/core/txn/pool/controller/action.go deleted file mode 100644 index 1f9762d..0000000 --- a/dela/core/txn/pool/controller/action.go +++ /dev/null @@ -1,116 +0,0 @@ -// This file implements the actions of the controller -// -// Documentation Last Review: 02.02.2021 -// - -package controller - -import ( - "sync" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/crypto/loader" - - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/core/txn/signed" - "golang.org/x/xerrors" -) - -// getManager is the function called when we need a transaction manager. It -// allows us to use a different manager for the tests. -var getManager = func(signer crypto.Signer, s signed.Client) txn.Manager { - return signed.NewManager(signer, s) -} - -// addAction describes an action to add an new transaction to the pool. -// -// - implements node.ActionTemplate -type addAction struct { - sync.Mutex - - client *client -} - -// Execute implements node.ActionTemplate -func (a *addAction) Execute(ctx node.Context) error { - a.Lock() - defer a.Unlock() - - var p pool.Pool - err := ctx.Injector.Resolve(&p) - if err != nil { - return xerrors.Errorf("injector: %v", err) - } - - args, err := getArgs(ctx) - if err != nil { - return xerrors.Errorf("failed to get args: %v", err) - } - - signer, err := getSigner(ctx) - if err != nil { - return xerrors.Errorf("failed to get signer: %v", err) - } - - nonce := ctx.Flags.Int(nonceFlag) - if nonce != -1 { - a.client.nonce = uint64(nonce) - } - - manager := getManager(signer, a.client) - - err = manager.Sync() - if err != nil { - return xerrors.Errorf("failed to sync manager: %v", err) - } - - tx, err := manager.Make(args...) - if err != nil { - return xerrors.Errorf("creating transaction: %v", err) - } - - err = p.Add(tx) - if err != nil { - return xerrors.Errorf("failed to include tx: %v", err) - } - - return nil -} - -// getArgs extracts and parses arguments from the context. -func getArgs(ctx node.Context) ([]txn.Arg, error) { - inArgs := ctx.Flags.StringSlice("args") - if len(inArgs)%2 != 0 { - return nil, xerrors.New("number of args should be even") - } - - args := make([]txn.Arg, len(inArgs)/2) - for i := 0; i < len(args); i++ { - args[i] = txn.Arg{ - Key: inArgs[i*2], - Value: []byte(inArgs[i*2+1]), - } - } - - return args, nil -} - -// getSigner creates a signer from the signerFlag flag in context. -func getSigner(ctx node.Context) (crypto.Signer, error) { - l := loader.NewFileLoader(ctx.Flags.Path(signerFlag)) - - signerdata, err := l.Load() - if err != nil { - return nil, xerrors.Errorf("failed to load signer: %v", err) - } - - signer, err := bls.NewSignerFromBytes(signerdata) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal signer: %v", err) - } - - return signer, nil -} diff --git a/dela/core/txn/pool/controller/action_test.go b/dela/core/txn/pool/controller/action_test.go deleted file mode 100644 index bdf4c56..0000000 --- a/dela/core/txn/pool/controller/action_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package controller - -import ( - "errors" - "io" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/core/txn/pool/mem" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestExecute(t *testing.T) { - ctx := node.Context{ - Injector: node.NewInjector(), - Flags: make(node.FlagSet), - Out: io.Discard, - } - - ctx.Flags.(node.FlagSet)["args"] = []interface{}{"1", "2"} - - action := addAction{client: &client{}} - ctx.Injector.Inject(mem.NewPool()) - - buf, err := bls.NewSigner().MarshalBinary() - require.NoError(t, err) - - keyFile := filepath.Join(os.TempDir(), "key.buf") - ctx.Flags.(node.FlagSet)[signerFlag] = keyFile - - err = os.WriteFile(keyFile, buf, os.ModePerm) - require.NoError(t, err) - defer os.RemoveAll(keyFile) - - err = action.Execute(ctx) - require.NoError(t, err) - - ctx.Injector = node.NewInjector() - ctx.Injector.Inject(&badPool{}) - err = action.Execute(ctx) - require.EqualError(t, err, "failed to include tx: "+fake.Err("failed to add")) - - getManager = func(c crypto.Signer, s signed.Client) txn.Manager { - return badManager{} - } - - err = action.Execute(ctx) - require.EqualError(t, err, "creating transaction: "+fake.Err("make fail")) - - getManager = func(c crypto.Signer, s signed.Client) txn.Manager { - return badManager{failSync: true} - } - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to sync manager: "+fake.Err("sync fail")) - - err = os.WriteFile(keyFile, []byte("bad signer"), os.ModePerm) - require.NoError(t, err) - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to get signer: failed to unmarshal signer: while unmarshaling scalar: UnmarshalBinary: wrong size buffer") - - ctx.Flags.(node.FlagSet)[signerFlag] = "/not/exist" - - err = action.Execute(ctx) - // the error message can be different based on the platform - require.Regexp(t, "^failed to get signer: failed to load signer: while opening file: open /not/exist:", err.Error()) - - ctx.Flags.(node.FlagSet)["args"] = []interface{}{"1"} - - err = action.Execute(ctx) - require.EqualError(t, err, "failed to get args: number of args should be even") - - ctx.Injector = node.NewInjector() - err = action.Execute(ctx) - require.EqualError(t, err, "injector: couldn't find dependency for 'pool.Pool'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badPool struct { - pool.Pool -} - -func (p *badPool) Add(txn.Transaction) error { - return errors.New(fake.Err("failed to add")) -} - -type badManager struct { - txn.Manager - failSync bool -} - -func (m badManager) Sync() error { - if m.failSync { - return errors.New(fake.Err("sync fail")) - } - - return nil -} - -func (m badManager) Make(args ...txn.Arg) (txn.Transaction, error) { - return nil, errors.New(fake.Err("make fail")) -} diff --git a/dela/core/txn/pool/controller/controller.go b/dela/core/txn/pool/controller/controller.go deleted file mode 100644 index 9d70373..0000000 --- a/dela/core/txn/pool/controller/controller.go +++ /dev/null @@ -1,78 +0,0 @@ -// Package controller implements a controller for the pool -// -// Documentation Last Review: 02.02.2021 -package controller - -import ( - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/access" -) - -const ( - // signerFlag is the flag name containing the path to the private keyfile. - signerFlag = "key" - - // nonceFlag is the flag name containing the nonce. - nonceFlag = "nonce" -) - -type miniController struct { -} - -// NewController creates a new minimal controller for the pool -// -// - implements node.Initializer -func NewController() node.Initializer { - return miniController{} -} - -// SetCommands implements mode.Initializer. It sets the command to interact with -// the pool. -func (miniController) SetCommands(builder node.Builder) { - cmd := builder.SetCommand("pool") - cmd.SetDescription("interact with the pool") - - sub := cmd.SetSubCommand("add") - sub.SetDescription("add a transaction to the pool") - sub.SetFlags(cli.StringSliceFlag{ - Name: "args", - Usage: "list of key-value pairs", - }, cli.IntFlag{ - Name: nonceFlag, - Usage: "nonce to use", - Required: false, - Value: -1, - }, cli.StringFlag{ - Name: signerFlag, - Usage: "path to the private keyfile", - Required: true, - }) - sub.SetAction(builder.MakeAction(&addAction{ - client: &client{}, - })) -} - -// OnStart implements node.Initializer -func (m miniController) OnStart(flags cli.Flags, inj node.Injector) error { - return nil -} - -// OnStop implements node.Initializer -func (miniController) OnStop(inj node.Injector) error { - return nil -} - -// client return monotically increasing nonce -// -// - implements signed.Client -type client struct { - nonce uint64 -} - -// GetNonce implements signed.Client -func (c *client) GetNonce(access.Identity) (uint64, error) { - res := c.nonce - c.nonce++ - return res, nil -} diff --git a/dela/core/txn/pool/controller/controller_test.go b/dela/core/txn/pool/controller/controller_test.go deleted file mode 100644 index a4d8dbd..0000000 --- a/dela/core/txn/pool/controller/controller_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package controller - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestMiniController_Build(t *testing.T) { - ctrl := NewController() - - call := &fake.Call{} - ctrl.SetCommands(fakeBuilder{call: call}) - - require.Equal(t, 7, call.Len()) - require.Equal(t, "pool", call.Get(0, 0)) - require.Equal(t, "interact with the pool", call.Get(1, 0)) - require.Equal(t, "add", call.Get(2, 0)) - require.Equal(t, "add a transaction to the pool", call.Get(3, 0)) - require.Len(t, call.Get(4, 0), 3) - require.IsType(t, &addAction{}, call.Get(5, 0)) - require.Nil(t, call.Get(6, 0)) // our fake MakeAction() returns nil -} - -func TestMiniController_OnStart(t *testing.T) { - res := NewController().OnStart(node.FlagSet{}, nil) - require.Nil(t, res) -} - -func TestMiniController_OnStop(t *testing.T) { - res := NewController().OnStop(nil) - require.Nil(t, res) -} - -func TestClient(t *testing.T) { - c := client{} - - n, err := c.GetNonce(nil) - require.NoError(t, err) - require.Equal(t, uint64(0), n) - - n, err = c.GetNonce(nil) - require.NoError(t, err) - require.Equal(t, uint64(1), n) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeCommandBuilder struct { - call *fake.Call -} - -func (b fakeCommandBuilder) SetSubCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return b -} - -func (b fakeCommandBuilder) SetDescription(value string) { - b.call.Add(value) -} - -func (b fakeCommandBuilder) SetFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeCommandBuilder) SetAction(a cli.Action) { - b.call.Add(a) -} - -type fakeBuilder struct { - call *fake.Call -} - -func (b fakeBuilder) SetCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return fakeCommandBuilder(b) -} - -func (b fakeBuilder) SetStartFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeBuilder) MakeAction(tmpl node.ActionTemplate) cli.Action { - b.call.Add(tmpl) - return nil -} diff --git a/dela/core/txn/pool/gatherer.go b/dela/core/txn/pool/gatherer.go deleted file mode 100644 index 5b0fcac..0000000 --- a/dela/core/txn/pool/gatherer.go +++ /dev/null @@ -1,263 +0,0 @@ -package pool - -import ( - "context" - "sync" - "time" - - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "golang.org/x/xerrors" -) - -// DefaultIdentitySize is the default size defined for each identity to store -// transactions. -const DefaultIdentitySize = 100 - -// Gatherer is a common tool to the pool implementations that helps to implement -// the gathering process. -type Gatherer interface { - // AddFilter adds the filter to the list that a transaction will go through - // before being accepted by the gatherer. - AddFilter(Filter) - - // Add adds the transaction to the list of pending transactions. - Add(tx txn.Transaction) error - - // Remove removes a transaction from the list of pending ones. - Remove(tx txn.Transaction) error - - // Wait waits for a notification with sufficient transactions to return the - // array, or nil if the context ends. - Wait(ctx context.Context, cfg Config) []txn.Transaction - - // Close closes current operations and cleans the resources. - Close() - - // Stats gets the transaction statistics. - Stats() Stats - - // ResetStats resets the transaction statistics. - ResetStats() -} - -type item struct { - cfg Config - ch chan []txn.Transaction -} - -// SimpleGatherer is a gatherer of transactions that will use filters to drop -// invalid transactions. It limits the size for each identity, *as long as* a -// filter is set, otherwise it can grow indefinitely. -// -// - implements pool.Gatherer -type simpleGatherer struct { - sync.Mutex - - limit int - queue []item - validators []Filter - - // A string key is generated for each unique identity, which will have its - // own list of transactions, so that a limited size can be enforced - // independently of each other. - txs map[string]transactions -} - -// NewSimpleGatherer creates a new gatherer. -func NewSimpleGatherer() Gatherer { - return &simpleGatherer{ - limit: DefaultIdentitySize, - txs: make(map[string]transactions), - } -} - -// AddFilter implements pool.Gatherer. It adds the filter to the list that a -// transaction will go through before being accepted by the gatherer. -func (g *simpleGatherer) AddFilter(filter Filter) { - if filter == nil { - return - } - - g.validators = append(g.validators, filter) -} - -// Add implements pool.Gatherer. It adds the transaction to the set of available -// transactions and notify the queue of the new length. -func (g *simpleGatherer) Add(tx txn.Transaction) error { - - for _, val := range g.validators { - // Make sure the transaction is not already known, or that is not in a - // distant future to limit the pool storage size. - err := val.Accept(tx, validation.Leeway{MaxSequenceDifference: g.limit}) - if err != nil { - return xerrors.Errorf("invalid transaction: %v", err) - } - } - - key, err := makeKey(tx.GetIdentity()) - if err != nil { - return xerrors.Errorf("identity key failed: %v", err) - } - - g.Lock() - - g.txs[key] = g.txs[key].Add(transactionStats{ - tx, - time.Now(), - }) - - g.notify(g.calculateLength()) - - g.Unlock() - - return nil -} - -// Remove implements pool.Gatherer. It removes the transaction from the set of -// available transactions and add the key in the history to prevent duplicates. -func (g *simpleGatherer) Remove(tx txn.Transaction) error { - key, err := makeKey(tx.GetIdentity()) - if err != nil { - return xerrors.Errorf("identity key failed: %v", err) - } - - g.Lock() - - g.txs[key] = g.txs[key].Remove(tx) - - g.Unlock() - - return nil -} - -// Wait implements pool.Gatherer. It waits for enough transactions before -// returning the list, or it returns nil if the context ends. -func (g *simpleGatherer) Wait(ctx context.Context, cfg Config) []txn.Transaction { - ch := make(chan []txn.Transaction, 1) - - g.Lock() - - if g.calculateLength() >= cfg.Min { - txs := g.makeArray() - g.Unlock() - - return txs - } - - g.queue = append(g.queue, item{cfg: cfg, ch: ch}) - - g.Unlock() - - if cfg.Callback != nil { - cfg.Callback() - } - - select { - case txs := <-ch: - return txs - case <-ctx.Done(): - return nil - } -} - -// Stats implements pool.Gatherer. It gets the transaction statistics. -func (g *simpleGatherer) Stats() Stats { - g.Lock() - defer g.Unlock() - - txs := g.makeStatsArray() - stats := Stats{ - TxCount: len(txs), - OldestTx: time.Now(), - } - - for _, tx := range txs { - if tx.insertionTime.Before(stats.OldestTx) { - stats.OldestTx = tx.insertionTime - } - } - - return stats -} - -// ResetStats implements pool.Gatherer. It resets the transactions statistics. -func (g *simpleGatherer) ResetStats() { - g.Lock() - defer g.Unlock() - - txs := g.makeStatsArray() - for _, tx := range txs { - tx.ResetStats() - } -} - -// Close implements pool.Gatherer. It closes the operations and cleans the -// resources. -func (g *simpleGatherer) Close() { - g.Lock() - - g.txs = make(map[string]transactions) - - for _, item := range g.queue { - close(item.ch) - } - - g.queue = nil - - g.Unlock() -} - -// Notify triggers the elements of the queue that are waiting for at least the -// length in parameter and remove them from the queue. -func (g *simpleGatherer) notify(length int) { - // Iterating by descending order to allow the deletion of the element inside - // the loop. - for i := len(g.queue) - 1; i >= 0; i-- { - item := g.queue[i] - - if item.cfg.Min <= length { - item.ch <- g.makeArray() - g.queue = append(g.queue[:i], g.queue[i+1:]...) - } - } -} - -func (g *simpleGatherer) calculateLength() int { - num := 0 - for _, list := range g.txs { - num += len(list) - } - - return num -} - -func (g *simpleGatherer) makeStatsArray() []transactionStats { - txs := make([]transactionStats, 0, g.calculateLength()) - for _, list := range g.txs { - txs = append(txs, list...) - } - - return txs -} - -func (g *simpleGatherer) makeArray() []txn.Transaction { - stxs := g.makeStatsArray() - txs := make([]txn.Transaction, 0, len(stxs)) - - for _, t := range stxs { - txs = append(txs, t.Transaction) - } - - return txs -} - -func makeKey(id access.Identity) (string, error) { - data, err := id.MarshalText() - if err != nil { - return "", err - } - - return string(data), nil -} diff --git a/dela/core/txn/pool/gatherer_test.go b/dela/core/txn/pool/gatherer_test.go deleted file mode 100644 index 5726671..0000000 --- a/dela/core/txn/pool/gatherer_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package pool - -import ( - "context" - "sync" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestSimpleGatherer_Len(t *testing.T) { - gatherer := NewSimpleGatherer().(*simpleGatherer) - require.Equal(t, 0, gatherer.Stats().TxCount) - - gatherer.txs["Alice"] = transactions{emptyTx()} - require.Equal(t, 1, gatherer.Stats().TxCount) - - gatherer.txs["Bob"] = transactions{emptyTx(), emptyTx()} - require.Equal(t, 3, gatherer.Stats().TxCount) -} - -func TestSimpleGatherer_Add(t *testing.T) { - gatherer := NewSimpleGatherer().(*simpleGatherer) - gatherer.AddFilter(nil) - gatherer.AddFilter(fakeFilter{}) - - for i := 0; i < DefaultIdentitySize; i++ { - err := gatherer.Add(newTx(uint64(i), "Alice")) - require.NoError(t, err) - } - - require.Equal(t, DefaultIdentitySize, gatherer.Stats().TxCount) - - err := gatherer.Add(newTx(DefaultIdentitySize-1, "Alice")) - require.NoError(t, err) - - err = gatherer.Add(newTx(5, "Bob")) - require.NoError(t, err) - - err = gatherer.Add(newTx(2, "Bob")) - require.NoError(t, err) - - require.Len(t, gatherer.txs["Bob"], 2) - require.Equal(t, uint64(2), gatherer.txs["Bob"][0].GetNonce()) - - err = gatherer.Add(newTx(DefaultIdentitySize+1, "Alice")) - require.EqualError(t, err, fake.Err("invalid transaction")) - - err = gatherer.Add(fakeTx{identity: fake.NewBadPublicKey()}) - require.EqualError(t, err, fake.Err("identity key failed")) -} - -func TestSimpleGatherer_Remove(t *testing.T) { - gatherer := NewSimpleGatherer().(*simpleGatherer) - gatherer.txs["Alice"] = transactions{newTx(0, "Alice"), newTx(1, "Alice")} - - err := gatherer.Remove(newTx(0, "Alice")) - require.NoError(t, err) - require.Len(t, gatherer.txs["Alice"], 1) - - err = gatherer.Remove(newTx(0, "Alice")) - require.NoError(t, err) - require.Len(t, gatherer.txs["Alice"], 1) - - err = gatherer.Remove(newTx(1, "Alice")) - require.NoError(t, err) - require.Len(t, gatherer.txs["Alice"], 0) - - err = gatherer.Remove(fakeTx{identity: fake.NewBadPublicKey()}) - require.EqualError(t, err, fake.Err("identity key failed")) -} - -func TestSimpleGatherer_Wait(t *testing.T) { - gatherer := NewSimpleGatherer().(*simpleGatherer) - - ctx := context.Background() - - cb := func() { - gatherer.Lock() - require.Len(t, gatherer.queue, 1) - gatherer.Unlock() - - require.NoError(t, gatherer.Add(newTx(0xa, "Alice"))) - } - - txs := gatherer.Wait(ctx, Config{Min: 1, Callback: cb}) - require.Len(t, txs, 1) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - txs = gatherer.Wait(ctx, Config{Min: 1}) - require.Len(t, txs, 1) - - txs = gatherer.Wait(ctx, Config{Min: 2}) - require.Nil(t, txs) -} - -func TestSimpleGatherer_Close(t *testing.T) { - gatherer := NewSimpleGatherer().(*simpleGatherer) - - require.NoError(t, gatherer.Add(newTx(0, "Alice"))) - require.NoError(t, gatherer.Add(newTx(1, "Alice"))) - require.NoError(t, gatherer.Remove(newTx(0, "Alice"))) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - wg := sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() - - txs := gatherer.Wait(ctx, Config{Min: 100, Callback: wg.Done}) - require.Empty(t, txs) - }() - - wg.Wait() - wg.Add(1) - - gatherer.Close() - - require.Empty(t, gatherer.queue) - require.Empty(t, gatherer.txs) - - wg.Wait() -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeTx struct { - txn.Transaction - id uint64 - identity access.Identity -} - -func emptyTx() transactionStats { - return transactionStats{ - Transaction: fakeTx{}, - } -} - -func newTx(nonce uint64, identity string) transactionStats { - return transactionStats{ - Transaction: fakeTx{ - id: nonce, - identity: fakeIdentity{text: identity}, - }, - } -} - -func (tx fakeTx) GetID() []byte { - return []byte{byte(tx.id)} -} - -func (tx fakeTx) GetNonce() uint64 { - return tx.id -} - -func (tx fakeTx) GetIdentity() access.Identity { - return tx.identity -} - -type fakeIdentity struct { - access.Identity - text string -} - -func (id fakeIdentity) MarshalText() ([]byte, error) { - return []byte(id.text), nil -} - -type fakeFilter struct{} - -func (fakeFilter) Accept(tx txn.Transaction, leeway validation.Leeway) error { - if tx.GetNonce() >= uint64(leeway.MaxSequenceDifference) { - return fake.GetError() - } - - return nil -} diff --git a/dela/core/txn/pool/gossip/gossip.go b/dela/core/txn/pool/gossip/gossip.go deleted file mode 100644 index c14c14e..0000000 --- a/dela/core/txn/pool/gossip/gossip.go +++ /dev/null @@ -1,130 +0,0 @@ -// Package gossip implements a transaction pool that is using a gossip protocol -// to spread the transactions to other participants. -package gossip - -import ( - "context" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/gossip" - "golang.org/x/xerrors" -) - -// Pool is a transaction pool that is using gossip to send the transactions to -// the other participants. -// -// - implements pool.Pool -type Pool struct { - logger zerolog.Logger - actor gossip.Actor - gatherer pool.Gatherer - closing chan struct{} -} - -// NewPool creates a new empty pool and starts to gossip incoming transaction. -func NewPool(gossiper gossip.Gossiper) (*Pool, error) { - actor, err := gossiper.Listen() - if err != nil { - return nil, xerrors.Errorf("failed to listen: %v", err) - } - - p := &Pool{ - logger: dela.Logger, - actor: actor, - gatherer: pool.NewSimpleGatherer(), - closing: make(chan struct{}), - } - - go p.listenRumors(gossiper.Rumors()) - - return p, nil -} - -// SetPlayers implements pool.Pool. It sets the list of participants the -// transactions should be gossiped to. -func (p *Pool) SetPlayers(players mino.Players) error { - p.actor.SetPlayers(players) - return nil -} - -// AddFilter implements pool.Pool. It adds the filter to the gatherer. -func (p *Pool) AddFilter(filter pool.Filter) { - p.gatherer.AddFilter(filter) -} - -// Add implements pool.Pool. It adds the transaction to the pool and gossips it -// to other participants. -func (p *Pool) Add(tx txn.Transaction) error { - err := p.gatherer.Add(tx) - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - - err = p.actor.Add(tx) - if err != nil { - return xerrors.Errorf("failed to gossip tx: %v", err) - } - - return nil -} - -// Remove implements pool.Pool. It removes the transaction from the pool. -func (p *Pool) Remove(tx txn.Transaction) error { - err := p.gatherer.Remove(tx) - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - - return nil -} - -// Gather implements pool.Pool. It blocks until the pool has enough transactions -// according to the configuration and then returns the transactions. -func (p *Pool) Gather(ctx context.Context, cfg pool.Config) []txn.Transaction { - return p.gatherer.Wait(ctx, cfg) -} - -// Stats implements pool.Pool. It gets the transaction statistics. -func (p *Pool) Stats() pool.Stats { - return p.gatherer.Stats() -} - -// ResetStats implements pool.Pool. It resets the transaction statistics. -func (p *Pool) ResetStats() { - p.gatherer.ResetStats() -} - -// Close stops the gossiper and terminate the routine that listens for rumors. -func (p *Pool) Close() error { - p.gatherer.Close() - - close(p.closing) - - err := p.actor.Close() - if err != nil { - return xerrors.Errorf("failed to close gossiper: %v", err) - } - - return nil -} - -func (p *Pool) listenRumors(ch <-chan gossip.Rumor) { - for { - select { - case rumor := <-ch: - tx, ok := rumor.(txn.Transaction) - if ok { - err := p.gatherer.Add(tx) - if err != nil { - p.logger.Debug().Err(err).Msg("failed to add transaction") - } - } - case <-p.closing: - return - } - } -} diff --git a/dela/core/txn/pool/gossip/gossip_test.go b/dela/core/txn/pool/gossip/gossip_test.go deleted file mode 100644 index a56d0aa..0000000 --- a/dela/core/txn/pool/gossip/gossip_test.go +++ /dev/null @@ -1,300 +0,0 @@ -package gossip - -import ( - "bytes" - "context" - "fmt" - "testing" - "time" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/gossip" - "go.dedis.ch/dela/mino/minoch" - "go.dedis.ch/dela/serde" -) - -func TestPool_Basic(t *testing.T) { - _, pools := makeRoster(t, 10) - defer func() { - for _, p := range pools { - require.NoError(t, p.Close()) - } - }() - - go func() { - for i := 0; i < 50; i++ { - err := pools[0].Add(makeFakeTx(uint64(i))) - require.NoError(t, err) - } - }() - - go func() { - for i := 0; i < 50; i++ { - err := pools[2].Add(makeFakeTx(uint64(i + 50))) - require.NoError(t, err) - } - }() - - go func() { - for i := 0; i < 50; i++ { - err := pools[7].Add(makeFakeTx(uint64(i + 100))) - require.NoError(t, err) - } - }() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - txs := pools[0].Gather(ctx, pool.Config{Min: 150}) - require.Len(t, txs, 150) -} - -func TestPool_New(t *testing.T) { - p, err := NewPool(fakeGossiper{}) - require.NoError(t, err) - require.NotNil(t, p) - - err = p.Close() - require.NoError(t, err) - - _, err = NewPool(fakeGossiper{err: fake.GetError()}) - require.EqualError(t, err, fake.Err("failed to listen")) -} - -func TestPool_Len(t *testing.T) { - p := &Pool{ - gatherer: pool.NewSimpleGatherer(), - } - - require.Equal(t, 0, p.Stats().TxCount) - - p.gatherer.Add(makeFakeTx(0)) - require.Equal(t, 1, p.Stats().TxCount) -} - -func TestPool_AddFilter(t *testing.T) { - p := &Pool{ - gatherer: pool.NewSimpleGatherer(), - } - - p.AddFilter(nil) -} - -func TestPool_Add(t *testing.T) { - p := &Pool{ - actor: fakeActor{}, - gatherer: pool.NewSimpleGatherer(), - } - - err := p.Add(makeFakeTx(0)) - require.NoError(t, err) - - p.gatherer = badGatherer{} - err = p.Add(makeFakeTx(0)) - require.EqualError(t, err, fake.Err("store failed")) - - p.gatherer = pool.NewSimpleGatherer() - p.actor = fakeActor{err: fake.GetError()} - err = p.Add(makeFakeTx(0)) - require.EqualError(t, err, fake.Err("failed to gossip tx")) -} - -func TestPool_Remove(t *testing.T) { - p := &Pool{ - actor: fakeActor{}, - gatherer: pool.NewSimpleGatherer(), - } - - tx := makeFakeTx(0) - - require.NoError(t, p.gatherer.Add(tx)) - - err := p.Remove(tx) - require.NoError(t, err) - - p.gatherer = badGatherer{} - err = p.Remove(tx) - require.EqualError(t, err, fake.Err("store failed")) -} - -func TestPool_Gather(t *testing.T) { - p := &Pool{ - actor: fakeActor{}, - gatherer: pool.NewSimpleGatherer(), - } - - ctx := context.Background() - - cb := func() { - require.NoError(t, p.Add(makeFakeTx(0))) - require.NoError(t, p.Add(makeFakeTx(1))) - } - - txs := p.Gather(ctx, pool.Config{Min: 2, Callback: cb}) - require.Len(t, txs, 2) - - txs = p.Gather(ctx, pool.Config{Min: 2}) - require.Len(t, txs, 2) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - txs = p.Gather(ctx, pool.Config{Min: 3}) - require.Len(t, txs, 0) -} - -func TestPool_Close(t *testing.T) { - p := &Pool{ - gatherer: pool.NewSimpleGatherer(), - closing: make(chan struct{}), - actor: fakeActor{}, - } - - err := p.Close() - require.NoError(t, err) - - p.closing = make(chan struct{}) - p.actor = fakeActor{err: fake.GetError()} - err = p.Close() - require.EqualError(t, err, fake.Err("failed to close gossiper")) -} - -func TestPool_ListenRumors(t *testing.T) { - buffer := new(bytes.Buffer) - - p := &Pool{ - logger: zerolog.New(buffer), - closing: make(chan struct{}), - gatherer: pool.NewSimpleGatherer(), - } - - ch := make(chan gossip.Rumor) - go func() { - ch <- makeFakeTx(0) - close(p.closing) - }() - - p.listenRumors(ch) - require.Empty(t, buffer.String()) - - p.gatherer = badGatherer{} - p.closing = make(chan struct{}) - - ch = make(chan gossip.Rumor) - go func() { - ch <- makeFakeTx(0) - close(p.closing) - }() - - p.listenRumors(ch) - require.NotEmpty(t, buffer.String()) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeFakeTx(nonce uint64) txn.Transaction { - return fakeTx{nonce: nonce} -} - -func makeRoster(t *testing.T, n int) (mino.Players, []*Pool) { - manager := minoch.NewManager() - - pools := make([]*Pool, n) - addrs := make([]mino.Address, n) - - for i := 0; i < n; i++ { - m := minoch.MustCreate(manager, fmt.Sprintf("node%d", i)) - - addrs[i] = m.GetAddress() - - g := gossip.NewFlat(m, fakeTxFac{}) - - p, err := NewPool(g) - require.NoError(t, err) - - pools[i] = p - } - - players := mino.NewAddresses(addrs...) - for _, p := range pools { - p.SetPlayers(players) - } - - return players, pools -} - -type fakeTx struct { - txn.Transaction - - nonce uint64 -} - -func (tx fakeTx) GetNonce() uint64 { - return tx.nonce -} - -func (tx fakeTx) GetIdentity() access.Identity { - return fake.PublicKey{} -} - -func (tx fakeTx) GetID() []byte { - return []byte{byte(tx.nonce)} -} - -func (tx fakeTx) Serialize(serde.Context) ([]byte, error) { - return tx.GetID(), nil -} - -type fakeTxFac struct { - txn.Factory -} - -func (fakeTxFac) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return fakeTx{nonce: uint64(data[0])}, nil -} - -type fakeActor struct { - gossip.Actor - call *fake.Call - err error -} - -func (a fakeActor) Add(r gossip.Rumor) error { - a.call.Add(r) - return a.err -} - -func (a fakeActor) Close() error { - return a.err -} - -type fakeGossiper struct { - err error -} - -func (g fakeGossiper) Listen() (gossip.Actor, error) { - return fakeActor{}, g.err -} - -func (g fakeGossiper) Rumors() <-chan gossip.Rumor { - return nil -} - -type badGatherer struct { - pool.Gatherer -} - -func (g badGatherer) Add(tx txn.Transaction) error { - return fake.GetError() -} - -func (g badGatherer) Remove(tx txn.Transaction) error { - return fake.GetError() -} diff --git a/dela/core/txn/pool/mem/mem.go b/dela/core/txn/pool/mem/mem.go deleted file mode 100644 index 4f377cd..0000000 --- a/dela/core/txn/pool/mem/mem.go +++ /dev/null @@ -1,81 +0,0 @@ -package mem - -import ( - "context" - - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -// Pool is a in-memory transaction pool. It only accepts transactions from a -// local client and it does not support asynchronous calls. -// -// - implements pool.Pool -type Pool struct { - gatherer pool.Gatherer -} - -// NewPool creates a new service. -func NewPool() *Pool { - return &Pool{ - gatherer: pool.NewSimpleGatherer(), - } -} - -// AddFilter implements pool.Pool. It adds the filter to the gatherer. -func (p *Pool) AddFilter(filter pool.Filter) { - p.gatherer.AddFilter(filter) -} - -// Add implements pool.Pool. It adds the transaction to the pool of waiting -// transactions. -func (p *Pool) Add(tx txn.Transaction) error { - err := p.gatherer.Add(tx) - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - - return nil -} - -// Remove implements pool.Pool. It removes the transaction from the pool if it -// exists, otherwise it returns an error. -func (p *Pool) Remove(tx txn.Transaction) error { - err := p.gatherer.Remove(tx) - if err != nil { - return xerrors.Errorf("store failed: %v", err) - } - - return nil -} - -// SetPlayers implements pool.Pool. It does nothing as the pool is in-memory and -// only shares the transactions to the host. -func (p *Pool) SetPlayers(mino.Players) error { - return nil -} - -// Gather implements pool.Pool. It gathers the transactions of the pool and -// return them. -func (p *Pool) Gather(ctx context.Context, cfg pool.Config) []txn.Transaction { - return p.gatherer.Wait(ctx, cfg) -} - -// Close implements pool.Pool. It cleans the resources of the gatherer. -func (p *Pool) Close() error { - p.gatherer.Close() - - return nil -} - -// Stats implements pool.Pool. It gets the transaction statistics. -func (p *Pool) Stats() pool.Stats { - return p.gatherer.Stats() -} - -// ResetStats implements pool.Pool. It resets the transaction statistics. -func (p *Pool) ResetStats() { - p.gatherer.ResetStats() -} diff --git a/dela/core/txn/pool/mem/mem_test.go b/dela/core/txn/pool/mem/mem_test.go deleted file mode 100644 index 4de2a78..0000000 --- a/dela/core/txn/pool/mem/mem_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package mem - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestPool_Len(t *testing.T) { - p := NewPool() - require.Equal(t, 0, p.Stats().TxCount) - - p.gatherer.Add(fakeTx{}) - require.Equal(t, 1, p.Stats().TxCount) -} - -func TestPool_AddFilter(t *testing.T) { - p := NewPool() - - p.AddFilter(nil) -} - -func TestPool_Add(t *testing.T) { - p := NewPool() - - err := p.Add(fakeTx{id: []byte{1}}) - require.NoError(t, err) - - err = p.Add(fakeTx{id: []byte{2}}) - require.NoError(t, err) - - // A transaction that exists in the active queue can simply be overwritten - // thus no error is expected. - err = p.Add(fakeTx{id: []byte{1}}) - require.NoError(t, err) - - p.gatherer = badGatherer{} - err = p.Add(fakeTx{}) - require.EqualError(t, err, fake.Err("store failed")) -} - -func TestPool_Remove(t *testing.T) { - p := NewPool() - - require.NoError(t, p.gatherer.Add(fakeTx{id: []byte{1}})) - - err := p.Remove(fakeTx{id: []byte{1}}) - require.NoError(t, err) - - p.gatherer = badGatherer{} - err = p.Remove(fakeTx{id: []byte{1}}) - require.EqualError(t, err, fake.Err("store failed")) -} - -func TestPool_SetPlayers(t *testing.T) { - p := NewPool() - - require.NoError(t, p.SetPlayers(nil)) -} - -func TestPool_Gather(t *testing.T) { - p := NewPool() - - ctx := context.Background() - - cb := func() { - require.NoError(t, p.Add(fakeTx{})) - } - - txs := p.Gather(ctx, pool.Config{Min: 1, Callback: cb}) - require.Len(t, txs, 1) - - txs = p.Gather(ctx, pool.Config{Min: 1}) - require.Len(t, txs, 1) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - txs = p.Gather(ctx, pool.Config{Min: 2}) - require.Len(t, txs, 0) -} - -func TestPool_Close(t *testing.T) { - p := NewPool() - - err := p.Close() - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeTx struct { - txn.Transaction - - id []byte -} - -func (tx fakeTx) GetNonce() uint64 { - return 0 -} - -func (tx fakeTx) GetIdentity() access.Identity { - return fake.PublicKey{} -} - -func (tx fakeTx) GetID() []byte { - return tx.id -} - -type badGatherer struct { - pool.Gatherer -} - -func (g badGatherer) Add(tx txn.Transaction) error { - return fake.GetError() -} - -func (g badGatherer) Remove(tx txn.Transaction) error { - return fake.GetError() -} diff --git a/dela/core/txn/pool/pool.go b/dela/core/txn/pool/pool.go deleted file mode 100644 index 15cd72a..0000000 --- a/dela/core/txn/pool/pool.go +++ /dev/null @@ -1,71 +0,0 @@ -// Package pool defines the interface for a transaction pool. It will hold the -// transactions of the clients until an ordering service read them and it will -// broadcast the state of the pool to other known participants. -package pool - -import ( - "context" - "time" - - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/mino" -) - -// Config is the set of parameters that allows one to change the behavior of the -// gathering process. -type Config struct { - // Min indicates what is minimum number of transactions that is required - // before returning. - Min int - - // Callback is a function called when the pool doesn't have enough - // transactions at the moment of calling and must therefore wait for new - // transactions to come. It allows one to take action to stop the gathering - // if necessary. - Callback func() -} - -// Filter is the interface to implement to validate if a transaction will be -// accepted and thus is allowed to be pushed in the pool. -type Filter interface { - // Accept returns an error when the transaction is going to be rejected. - Accept(tx txn.Transaction, leeway validation.Leeway) error -} - -// Pool is the maintainer of the list of transactions. -type Pool interface { - // SetPlayers updates the list of participants that should eventually - // receive the transactions. - SetPlayers(mino.Players) error - - AddFilter(Filter) - - // Add adds the transaction to the pool. - Add(txn.Transaction) error - - // Remove removes the transaction from the pool. - Remove(txn.Transaction) error - - // Gather is a blocking function to gather transactions from the pool. The - // configuration allows one to specify criterion before returning. - Gather(context.Context, Config) []txn.Transaction - - // Stats gets the transactions statistics - Stats() Stats - - // ResetStats resets the transaction statistics. - ResetStats() - - // Close closes the pool and cleans the resources. - Close() error -} - -// Stats groups statistics used to manage the pool -type Stats struct { - // OldestTx is the time at which the oldest transaction was added to the pool. - OldestTx time.Time - - // TxCount is the number of transactions available in the pool. - TxCount int -} diff --git a/dela/core/txn/pool/stats.go b/dela/core/txn/pool/stats.go deleted file mode 100644 index d96c6ab..0000000 --- a/dela/core/txn/pool/stats.go +++ /dev/null @@ -1,20 +0,0 @@ -package pool - -import ( - "time" - - "go.dedis.ch/dela/core/txn" -) - -// transactionStats enhances a transaction with some statistics -// to allow the detection of rotten transactions in a pool. -type transactionStats struct { - txn.Transaction - insertionTime time.Time -} - -// ResetStats resets the insertion time to now. -// It is used when a leader view change is initiated. -func (t *transactionStats) ResetStats() { - t.insertionTime = time.Now() -} diff --git a/dela/core/txn/pool/stats_test.go b/dela/core/txn/pool/stats_test.go deleted file mode 100644 index 1ab4286..0000000 --- a/dela/core/txn/pool/stats_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package pool - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestStats_Reset(t *testing.T) { - stats := transactionStats{ - insertionTime: time.Now().Add(-time.Hour), - } - - isRotten := time.Since(stats.insertionTime) > time.Minute - require.True(t, isRotten) - stats.ResetStats() - isRotten = time.Since(stats.insertionTime) > time.Minute - require.False(t, isRotten) -} diff --git a/dela/core/txn/pool/transactions.go b/dela/core/txn/pool/transactions.go deleted file mode 100644 index 416de01..0000000 --- a/dela/core/txn/pool/transactions.go +++ /dev/null @@ -1,57 +0,0 @@ -package pool - -import ( - "bytes" - "sort" - - "go.dedis.ch/dela/core/txn" -) - -// Transactions is a sortable list of transactions. -// -// - implements sort.Interface -type transactions []transactionStats - -// Len implements sort.Interface. It returns the length of the list. -func (txs transactions) Len() int { - return len(txs) -} - -// Less implements sort.Interface. It returns true if the nonce of the ith -// transaction is smaller than the jth. -func (txs transactions) Less(i, j int) bool { - return txs[i].GetNonce() < txs[j].GetNonce() -} - -// Swap implements sort.Interface. It swaps the ith and the jth transactions. -func (txs transactions) Swap(i, j int) { - txs[i], txs[j] = txs[j], txs[i] -} - -// Add adds the transaction to the list if and only if the nonce is unique. The -// resulting list will be sorted by nonce. -func (txs transactions) Add(other transactionStats) transactions { - for _, tx := range txs { - if tx.GetNonce() == other.GetNonce() { - return txs - } - } - - list := append(txs, other) - sort.Sort(list) - - return list -} - -// Remove removes the transaction from the list if it exists, while preserving -// the order of the transactions. -func (txs transactions) Remove(other txn.Transaction) transactions { - for i, tx := range txs { - if bytes.Equal(tx.GetID(), other.GetID()) { - txs = append(txs[:i], txs[i+1:]...) - break - } - } - - return txs -} diff --git a/dela/core/txn/signed/controller/controller.go b/dela/core/txn/signed/controller/controller.go deleted file mode 100644 index 0873c4e..0000000 --- a/dela/core/txn/signed/controller/controller.go +++ /dev/null @@ -1,88 +0,0 @@ -// Package controller implements a CLI controller to inject a transaction -// manager. -// -// Documentation Last Review: 08.10.2020 -package controller - -import ( - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/cosi" -) - -// MgrController is a CLI controller that will inject a transaction manager -// using the signer of the collective signing component. -// -// - implements node.Initializer -type mgrController struct{} - -// NewManagerController creates a new controller that will inject a transaction -// manager in the context. -func NewManagerController() node.Initializer { - return mgrController{} -} - -// SetCommands implements node.Initializer. It does not register any command. -func (mgrController) SetCommands(node.Builder) {} - -// OnStart implements node.Initializer. It creates a transaction manager using -// the signer of the collective signing component and injects it. -func (mgrController) OnStart(flags cli.Flags, inj node.Injector) error { - var srvc ordering.Service - err := inj.Resolve(&srvc) - if err != nil { - return err - } - - var nonceMgr validation.Service - err = inj.Resolve(&nonceMgr) - if err != nil { - return err - } - - var c cosi.CollectiveSigning - err = inj.Resolve(&c) - if err != nil { - return err - } - - mgr := signed.NewManager(c.GetSigner(), client{ - srvc: srvc, - mgr: nonceMgr, - }) - - inj.Inject(mgr) - - return nil -} - -// OnStop implements node.initializer. It does nothing. -func (mgrController) OnStop(node.Injector) error { - return nil -} - -// Client is a local client for the manager to read the current identity's nonce -// from the ordering service. -// -// - implements signed.Client -type client struct { - srvc ordering.Service - mgr validation.Service -} - -// GetNonce implements signed.Client. It reads the store of the ordering service -// to get the next nonce of the identity and returns it. -func (c client) GetNonce(ident access.Identity) (uint64, error) { - store := c.srvc.GetStore() - - nonce, err := c.mgr.GetNonce(store, ident) - if err != nil { - return 0, err - } - - return nonce, nil -} diff --git a/dela/core/txn/signed/example_test.go b/dela/core/txn/signed/example_test.go deleted file mode 100644 index 51e6fae..0000000 --- a/dela/core/txn/signed/example_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package signed - -import ( - "fmt" - - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/crypto/bls" -) - -func ExampleTransactionManager_Make() { - signer := bls.NewSigner() - - manager := NewManager(signer, exampleClient{nonce: 5}) - - tx, err := manager.Make() - if err != nil { - panic("failed to create first transaction: " + err.Error()) - } - - fmt.Println(tx.GetNonce()) - - err = manager.Sync() - if err != nil { - panic("failed to synchronize: " + err.Error()) - } - - tx, err = manager.Make() - if err != nil { - panic("failed to create second transaction: " + err.Error()) - } - - fmt.Println(tx.GetNonce()) - - // Output: 0 - // 5 -} - -// exampleClient is an example of a manager client. It always synchronize the -// manager to the nonce value. -// -// - implements signed.Client -type exampleClient struct { - nonce uint64 -} - -// GetNonce implements signed.Client. It always return the same nonce for -// simplicity. -func (cl exampleClient) GetNonce(identity access.Identity) (uint64, error) { - return cl.nonce, nil -} diff --git a/dela/core/txn/signed/json/json.go b/dela/core/txn/signed/json/json.go deleted file mode 100644 index 91ac9e4..0000000 --- a/dela/core/txn/signed/json/json.go +++ /dev/null @@ -1,142 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - signed.RegisterTransactionFormat(serde.FormatJSON, txFormat{}) -} - -// TransactionJSON is the JSON message of a transaction. -type TransactionJSON struct { - Nonce uint64 - Args map[string][]byte - PublicKey json.RawMessage - Signature json.RawMessage -} - -// TxFormat is the JSON format engine for transactions. -// -// - implements serde.FormatEngine -type txFormat struct { - hashFactory crypto.HashFactory -} - -// Encode implements serde.FormatEngine. It returns the JSON data of the -// provided transaction if appropriate, otherwise it returns an error. -func (fmt txFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - tx, ok := msg.(*signed.Transaction) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - if tx.GetSignature() == nil { - return nil, xerrors.New("signature is missing") - } - - args := map[string][]byte{} - for _, arg := range tx.GetArgs() { - args[arg] = tx.GetArg(arg) - } - - pubkey, err := tx.GetIdentity().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to encode public key: %v", err) - } - - sig, err := tx.GetSignature().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("failed to encode signature: %v", err) - } - - m := TransactionJSON{ - Nonce: tx.GetNonce(), - Args: args, - PublicKey: pubkey, - Signature: sig, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It returns the transaction from the -// JSON data if appropriate, otherwise it returns an error. -func (fmt txFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := TransactionJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - pubkey, err := decodeIdentity(ctx, m.PublicKey) - if err != nil { - return nil, xerrors.Errorf("public key: %v", err) - } - - sig, err := decodeSignature(ctx, m.Signature) - if err != nil { - return nil, xerrors.Errorf("signature: %v", err) - } - - args := make([]signed.TransactionOption, 0, len(m.Args)+2) - for key, value := range m.Args { - args = append(args, signed.WithArg(key, value)) - } - - args = append(args, signed.WithSignature(sig)) - - if fmt.hashFactory != nil { - args = append(args, signed.WithHashFactory(fmt.hashFactory)) - } - - tx, err := signed.NewTransaction(m.Nonce, pubkey, args...) - if err != nil { - return nil, xerrors.Errorf("failed to create tx: %v", err) - } - - return tx, nil -} - -func decodeIdentity(ctx serde.Context, data []byte) (crypto.PublicKey, error) { - fac := ctx.GetFactory(signed.PublicKeyFac{}) - - factory, ok := fac.(common.PublicKeyFactory) - if !ok { - return nil, xerrors.Errorf("invalid factory '%T'", fac) - } - - pubkey, err := factory.PublicKeyOf(ctx, data) - if err != nil { - return nil, xerrors.Errorf("malformed: %v", err) - } - - return pubkey, nil -} - -func decodeSignature(ctx serde.Context, data []byte) (crypto.Signature, error) { - fac := ctx.GetFactory(signed.SignatureFac{}) - - factory, ok := fac.(crypto.SignatureFactory) - if !ok { - return nil, xerrors.Errorf("invalid factory '%T'", fac) - } - - sig, err := factory.SignatureOf(ctx, data) - if err != nil { - return nil, xerrors.Errorf("malformed: %v", err) - } - - return sig, nil -} diff --git a/dela/core/txn/signed/json/json_test.go b/dela/core/txn/signed/json/json_test.go deleted file mode 100644 index c8fbe89..0000000 --- a/dela/core/txn/signed/json/json_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestTxFormat_Encode(t *testing.T) { - format := txFormat{} - - ctx := fake.NewContext() - - tx := makeTx(t, 1, fake.PublicKey{}, signed.WithArg("A", []byte{1})) - - data, err := format.Encode(ctx, tx) - require.NoError(t, err) - require.Equal(t, `{"Nonce":1,"Args":{"A":"AQ=="},"PublicKey":{},"Signature":{}}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(ctx, &signed.Transaction{}) - require.EqualError(t, err, "signature is missing") - - badTx := makeTx(t, 0, fake.PublicKey{}, signed.WithSignature(fake.NewBadSignature())) - _, err = format.Encode(ctx, badTx) - require.EqualError(t, err, fake.Err("failed to encode signature")) - - _, err = format.Encode(fake.NewBadContext(), tx) - require.EqualError(t, err, fake.Err("failed to marshal")) - - tx = makeTx(t, 0, badPublicKey{}) - _, err = format.Encode(fake.NewBadContextWithDelay(1), tx) - require.EqualError(t, err, fake.Err("failed to encode public key")) -} - -func TestTxFormat_Decode(t *testing.T) { - format := txFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, signed.PublicKeyFac{}, fake.PublicKeyFactory{}) - ctx = serde.WithFactory(ctx, signed.SignatureFac{}, fake.SignatureFactory{}) - - msg, err := format.Decode(ctx, []byte(`{"Nonce":2,"Args":{"B":"AQ=="}}`)) - require.NoError(t, err) - expected := makeTx(t, 2, fake.PublicKey{}, signed.WithArg("B", []byte{1})) - require.Equal(t, expected, msg) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - format.hashFactory = fake.NewHashFactory(fake.NewBadHash()) - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, - fake.Err("failed to create tx: couldn't fingerprint tx: couldn't write nonce")) - - badCtx := serde.WithFactory(ctx, signed.PublicKeyFac{}, nil) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "public key: invalid factory ''") - - badCtx = serde.WithFactory(ctx, signed.PublicKeyFac{}, fake.NewBadPublicKeyFactory()) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("public key: malformed")) - - badCtx = serde.WithFactory(ctx, signed.SignatureFac{}, nil) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "signature: invalid factory ''") - - badCtx = serde.WithFactory(ctx, signed.SignatureFac{}, fake.NewBadSignatureFactory()) - _, err = format.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("signature: malformed")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeTx(t *testing.T, nonce uint64, - pk crypto.PublicKey, opts ...signed.TransactionOption) txn.Transaction { - - opts = append([]signed.TransactionOption{signed.WithSignature(fake.Signature{})}, opts...) - - tx, err := signed.NewTransaction(nonce, pk, opts...) - require.NoError(t, err) - - return tx -} - -type badPublicKey struct { - crypto.PublicKey -} - -func (badPublicKey) Serialize(serde.Context) ([]byte, error) { - return nil, fake.GetError() -} - -func (badPublicKey) MarshalBinary() ([]byte, error) { - return []byte{}, nil -} - -func (badPublicKey) Verify([]byte, crypto.Signature) error { - return nil -} diff --git a/dela/core/txn/signed/signed.go b/dela/core/txn/signed/signed.go deleted file mode 100644 index 6578789..0000000 --- a/dela/core/txn/signed/signed.go +++ /dev/null @@ -1,334 +0,0 @@ -// Package signed is an implementation of the transaction abstraction. -// -// It uses a signature to make sure the identity owns the transaction. The nonce -// is a monotonically increasing number that is used to prevent a replay attack -// of an existing transaction. -// -// Documentation Last Review: 08.10.2020 -package signed - -import ( - "encoding/binary" - "io" - "sort" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var txFormats = registry.NewSimpleRegistry() - -// RegisterTransactionFormat registers the engine for the provided format. -func RegisterTransactionFormat(f serde.Format, e serde.FormatEngine) { - txFormats.Register(f, e) -} - -// Transaction is a signed transaction using a nonce to protect itself against -// replay attack. -// -// - implements txn.Transaction -type Transaction struct { - nonce uint64 - args map[string][]byte - pubkey crypto.PublicKey - sig crypto.Signature - hash []byte -} - -type template struct { - Transaction - - hashFactory crypto.HashFactory -} - -// TransactionOption is the type of options to create a transaction. -type TransactionOption func(*template) - -// WithArg is an option to set an argument with the key and the value. -func WithArg(key string, value []byte) TransactionOption { - return func(tmpl *template) { - tmpl.args[key] = value - } -} - -// WithSignature is an option to set a valid signature. The signature will be -// verified against the identity. -func WithSignature(sig crypto.Signature) TransactionOption { - return func(tmpl *template) { - tmpl.sig = sig - } -} - -// WithHashFactory is an option to set a different hash factory when creating a -// transaction. -func WithHashFactory(f crypto.HashFactory) TransactionOption { - return func(tmpl *template) { - tmpl.hashFactory = f - } -} - -// NewTransaction creates a new transaction with the provided nonce. -func NewTransaction(nonce uint64, pk crypto.PublicKey, opts ...TransactionOption) (*Transaction, error) { - tmpl := template{ - Transaction: Transaction{ - nonce: nonce, - pubkey: pk, - args: make(map[string][]byte), - }, - hashFactory: crypto.NewSha256Factory(), - } - - for _, opt := range opts { - opt(&tmpl) - } - - h := tmpl.hashFactory.New() - err := tmpl.Fingerprint(h) - if err != nil { - return nil, xerrors.Errorf("couldn't fingerprint tx: %v", err) - } - - tmpl.hash = h.Sum(nil) - - if tmpl.sig != nil { - err := tmpl.pubkey.Verify(tmpl.hash, tmpl.sig) - if err != nil { - return nil, xerrors.Errorf("invalid signature: %v", err) - } - } - - return &tmpl.Transaction, nil -} - -// GetID implements txn.Transaction. It returns the ID of the transaction. -func (t *Transaction) GetID() []byte { - return t.hash -} - -// GetNonce implements txn.Transaction. It returns the nonce of the transaction. -func (t *Transaction) GetNonce() uint64 { - return t.nonce -} - -// GetIdentity implements txn.Transaction. It returns nil. -func (t *Transaction) GetIdentity() access.Identity { - return t.pubkey -} - -// GetSignature returns the signature of the transaction. -func (t *Transaction) GetSignature() crypto.Signature { - return t.sig -} - -// GetArgs returns the list of arguments available. -func (t *Transaction) GetArgs() []string { - args := make([]string, 0, len(t.args)) - for key := range t.args { - args = append(args, key) - } - - return args -} - -// GetArg implements txn.Transaction. It returns the value of the argument if it -// is set, otherwise nil. -func (t *Transaction) GetArg(key string) []byte { - return t.args[key] -} - -// Sign signs the transaction and stores the signature. -func (t *Transaction) Sign(signer crypto.Signer) error { - if len(t.hash) == 0 { - return xerrors.New("missing digest in transaction") - } - - if !signer.GetPublicKey().Equal(t.pubkey) { - return xerrors.New("mismatch signer and identity") - } - - sig, err := signer.Sign(t.hash) - if err != nil { - return xerrors.Errorf("signer: %v", err) - } - - t.sig = sig - - return nil -} - -// Fingerprint implements serde.Fingerprinter. It writes a deterministic binary -// representation of the transaction. -func (t *Transaction) Fingerprint(w io.Writer) error { - buffer := make([]byte, 8) - binary.LittleEndian.PutUint64(buffer, t.nonce) - - _, err := w.Write(buffer) - if err != nil { - return xerrors.Errorf("couldn't write nonce: %v", err) - } - - // Sort the argument to deterministically write them to the hash. - args := make(sort.StringSlice, 0, len(t.args)) - for key := range t.args { - args = append(args, key) - } - - sort.Sort(args) - - for _, key := range args { - _, err = w.Write(append([]byte(key), t.args[key]...)) - if err != nil { - return xerrors.Errorf("couldn't write arg: %v", err) - } - } - - buffer, err = t.pubkey.MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to marshal public key: %v", err) - } - - _, err = w.Write(buffer) - if err != nil { - return xerrors.Errorf("couldn't write public key: %v", err) - } - - return nil -} - -// Serialize implements serde.Message. It returns the serialized data of the -// transaction. -func (t *Transaction) Serialize(ctx serde.Context) ([]byte, error) { - format := txFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, t) - if err != nil { - return nil, xerrors.Errorf("failed to encode: %v", err) - } - - return data, nil -} - -// PublicKeyFac is the key of the public key factory. -type PublicKeyFac struct{} - -// SignatureFac is the key of the signature factory. -type SignatureFac struct{} - -// TransactionFactory is a factory to deserialize transactions. -// -// - implements serde.Factory -type TransactionFactory struct { - pubkeyFac common.PublicKeyFactory - sigFac common.SignatureFactory -} - -// NewTransactionFactory returns a new factory. -func NewTransactionFactory() TransactionFactory { - return TransactionFactory{ - pubkeyFac: common.NewPublicKeyFactory(), - sigFac: common.NewSignatureFactory(), - } -} - -// Deserialize implements serde.Factory. It populates the transaction from the -// data if appropriate, otherwise it returns an error. -func (f TransactionFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.TransactionOf(ctx, data) -} - -// TransactionOf implements txn.TransactionFactory. It populates the transaction -// from the data if appropriate, otherwise it returns an error. -func (f TransactionFactory) TransactionOf(ctx serde.Context, data []byte) (txn.Transaction, error) { - format := txFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, PublicKeyFac{}, f.pubkeyFac) - ctx = serde.WithFactory(ctx, SignatureFac{}, f.sigFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("failed to decode: %v", err) - } - - tx, ok := msg.(*Transaction) - if !ok { - return nil, xerrors.Errorf("invalid transaction of type '%T'", msg) - } - - return tx, nil -} - -// Client is the interface the manager is using to get the nonce of an identity. -// It allows a local implementation, or through a network client. -type Client interface { - GetNonce(access.Identity) (uint64, error) -} - -// TransactionManager is a manager to create signed transactions. It manages the -// nonce by itself, except if the transaction is refused by the ledger. In that -// case the manager should be synchronized before creating a new one. -// -// - implements txn.Manager -type TransactionManager struct { - client Client - signer crypto.Signer - nonce uint64 - hashFac crypto.HashFactory -} - -// NewManager creates a new transaction manager. -// -// - implements txn.Manager -func NewManager(signer crypto.Signer, client Client) *TransactionManager { - return &TransactionManager{ - client: client, - signer: signer, - nonce: 0, - hashFac: crypto.NewSha256Factory(), - } -} - -// Make implements txn.Manager. It creates a transaction populated with the -// arguments. -func (mgr *TransactionManager) Make(args ...txn.Arg) (txn.Transaction, error) { - opts := make([]TransactionOption, len(args), len(args)+1) - for i, arg := range args { - opts[i] = WithArg(arg.Key, arg.Value) - } - - opts = append(opts, WithHashFactory(mgr.hashFac)) - - tx, err := NewTransaction(mgr.nonce, mgr.signer.GetPublicKey(), opts...) - if err != nil { - return nil, xerrors.Errorf("failed to create tx: %v", err) - } - - err = tx.Sign(mgr.signer) - if err != nil { - return nil, xerrors.Errorf("failed to sign: %v", err) - } - - mgr.nonce++ - - return tx, nil -} - -// Sync implements txn.Manager. It fetches the latest nonce of the signer to -// create valid transactions. -func (mgr *TransactionManager) Sync() error { - nonce, err := mgr.client.GetNonce(mgr.signer.GetPublicKey()) - if err != nil { - return xerrors.Errorf("client: %v", err) - } - - mgr.nonce = nonce - - dela.Logger.Debug().Uint64("nonce", nonce).Msg("manager synchronized") - - return nil -} diff --git a/dela/core/txn/signed/signed_test.go b/dela/core/txn/signed/signed_test.go deleted file mode 100644 index b47f796..0000000 --- a/dela/core/txn/signed/signed_test.go +++ /dev/null @@ -1,199 +0,0 @@ -package signed - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func init() { - RegisterTransactionFormat(fake.GoodFormat, fake.Format{Msg: &Transaction{}}) - RegisterTransactionFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterTransactionFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) -} - -func TestTransaction_New(t *testing.T) { - signer := bls.NewSigner() - - tx, err := NewTransaction(0, signer.GetPublicKey()) - require.NoError(t, err) - require.NotNil(t, tx) - - require.NoError(t, tx.Sign(signer)) - - tx, err = NewTransaction(0, signer.GetPublicKey(), WithSignature(tx.GetSignature())) - require.NoError(t, err) - require.NotNil(t, tx.GetSignature()) - - _, err = NewTransaction(0, fake.PublicKey{}, WithHashFactory(fake.NewHashFactory(fake.NewBadHash()))) - require.EqualError(t, err, fake.Err("couldn't fingerprint tx: couldn't write nonce")) - - _, err = NewTransaction(1, signer.GetPublicKey(), WithSignature(tx.GetSignature())) - require.EqualError(t, err, "invalid signature: bls verify failed: bls: invalid signature") -} - -func TestTransaction_GetID(t *testing.T) { - tx, err := NewTransaction(0, fake.PublicKey{}) - require.NoError(t, err) - - id := tx.GetID() - require.Len(t, id, 32) -} - -func TestTransaction_GetNonce(t *testing.T) { - tx, err := NewTransaction(123, fake.PublicKey{}) - require.NoError(t, err) - - nonce := tx.GetNonce() - require.Equal(t, uint64(123), nonce) -} - -func TestTransaction_GetIdentity(t *testing.T) { - tx, err := NewTransaction(1, fake.PublicKey{}) - require.NoError(t, err) - require.Equal(t, fake.PublicKey{}, tx.GetIdentity()) -} - -func TestTransaction_GetArgs(t *testing.T) { - tx, err := NewTransaction(5, fake.PublicKey{}, WithArg("A", []byte{1}), WithArg("B", []byte{2})) - require.NoError(t, err) - - args := tx.GetArgs() - require.Contains(t, args, "A") - require.Contains(t, args, "B") -} - -func TestTransaction_GetArg(t *testing.T) { - tx, err := NewTransaction(5, fake.PublicKey{}, WithArg("A", []byte{1}), WithArg("B", []byte{2})) - require.NoError(t, err) - - value := tx.GetArg("A") - require.Equal(t, []byte{1}, value) - - value = tx.GetArg("B") - require.Equal(t, []byte{2}, value) - - value = tx.GetArg("C") - require.Nil(t, value) -} - -func TestTransaction_Sign(t *testing.T) { - signer := bls.NewSigner() - - tx, err := NewTransaction(2, signer.GetPublicKey(), WithArg("A", []byte{123})) - require.NoError(t, err) - - err = tx.Sign(signer) - require.NoError(t, err) - require.NoError(t, signer.GetPublicKey().Verify(tx.hash, tx.GetSignature())) - - tx.hash = nil - err = tx.Sign(signer) - require.EqualError(t, err, "missing digest in transaction") - - tx.hash = []byte{1} - err = tx.Sign(fake.Signer{}) - require.EqualError(t, err, "mismatch signer and identity") - - tx.pubkey = fake.PublicKey{} - err = tx.Sign(fake.NewBadSigner()) - require.EqualError(t, err, fake.Err("signer")) -} - -func TestTransaction_Fingerprint(t *testing.T) { - tx, err := NewTransaction(2, fake.PublicKey{}, WithArg("A", []byte{1, 2, 3})) - require.NoError(t, err) - - buffer := new(bytes.Buffer) - err = tx.Fingerprint(buffer) - require.NoError(t, err) - require.Equal(t, "\x02\x00\x00\x00\x00\x00\x00\x00A\x01\x02\x03PK", buffer.String()) - - err = tx.Fingerprint(fake.NewBadHash()) - require.EqualError(t, err, fake.Err("couldn't write nonce")) - - err = tx.Fingerprint(fake.NewBadHashWithDelay(1)) - require.EqualError(t, err, fake.Err("couldn't write arg")) - - err = tx.Fingerprint(fake.NewBadHashWithDelay(2)) - require.EqualError(t, err, fake.Err("couldn't write public key")) - - tx.pubkey = fake.NewBadPublicKey() - err = tx.Fingerprint(buffer) - require.EqualError(t, err, fake.Err("failed to marshal public key")) -} - -func TestTransaction_Serialize(t *testing.T) { - tx, err := NewTransaction(0, fake.PublicKey{}) - require.NoError(t, err) - - data, err := tx.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = tx.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("failed to encode")) -} - -func TestTransactionFactory_Deserialize(t *testing.T) { - factory := NewTransactionFactory() - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.IsType(t, &Transaction{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("failed to decode")) - - _, err = factory.Deserialize(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid transaction of type 'fake.Message'") -} - -func TestManager_Make(t *testing.T) { - mgr := NewManager(fake.NewSigner(), nil) - - tx, err := mgr.Make(txn.Arg{Key: "a", Value: []byte{1, 2, 3}}) - require.NoError(t, err) - require.Equal(t, uint64(0), tx.(*Transaction).nonce) - require.Equal(t, []byte{1, 2, 3}, tx.GetArg("a")) - - mgr.hashFac = fake.NewHashFactory(fake.NewBadHash()) - _, err = mgr.Make() - require.Error(t, err) - require.Contains(t, err.Error(), "failed to create tx: ") - - mgr.hashFac = crypto.NewSha256Factory() - mgr.signer = fake.NewBadSigner() - _, err = mgr.Make() - require.EqualError(t, err, fake.Err("failed to sign: signer")) -} - -func TestManager_Sync(t *testing.T) { - mgr := NewManager(fake.NewSigner(), fakeClient{}) - - err := mgr.Sync() - require.NoError(t, err) - require.Equal(t, uint64(42), mgr.nonce) - - mgr = NewManager(fake.NewSigner(), fakeClient{err: fake.GetError()}) - err = mgr.Sync() - require.EqualError(t, err, fake.Err("client")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeClient struct { - err error -} - -func (c fakeClient) GetNonce(access.Identity) (uint64, error) { - return 42, c.err -} diff --git a/dela/core/txn/txn.go b/dela/core/txn/txn.go deleted file mode 100644 index bf2d955..0000000 --- a/dela/core/txn/txn.go +++ /dev/null @@ -1,59 +0,0 @@ -// Package txn defines the abstraction of transactions. -// -// A transaction is a smart contract input. It is uniquely identifiable via a -// digest and it can be sorted with the nonce that acts as a sequence number. -// The transaction is also created by an identity that can be used for access -// control for instance. -// -// The manager helps to create transactions as the nonce needs to be correct for -// the transaction to be valid. -// -// Documentation Last Review: 08.10.2020 -package txn - -import ( - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/serde" -) - -// Transaction is what triggers a smart contract execution by passing it as part -// of the input. -type Transaction interface { - serde.Message - serde.Fingerprinter - - // GetID returns the unique identifier for the transaction. - GetID() []byte - - // GetNonce returns the nonce of the transaction which corresponds to the - // sequence number of a unique identity. - GetNonce() uint64 - - // GetIdentity returns the identity that created the transaction. - GetIdentity() access.Identity - - // GetArg is a getter for the arguments of the transaction. - GetArg(key string) []byte -} - -// Factory is the definition of a factory to deserialize transaction -// messages. -type Factory interface { - serde.Factory - - TransactionOf(serde.Context, []byte) (Transaction, error) -} - -// Arg is a generic argument that can be stored in a transaction. -type Arg struct { - Key string - Value []byte -} - -// Manager is a manager to create transaction. It can help creating -// transactions when some information is required like the current nonce. -type Manager interface { - Make(args ...Arg) (Transaction, error) - - Sync() error -} diff --git a/dela/core/validation/simple/example_test.go b/dela/core/validation/simple/example_test.go deleted file mode 100644 index 93afd92..0000000 --- a/dela/core/validation/simple/example_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package simple - -import ( - "fmt" - "sync" - - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto/bls" -) - -func ExampleService_GetNonce() { - - exec := native.NewExecution() - - srvc := NewService(exec, signed.NewTransactionFactory()) - - signer := bls.NewSigner() - store := newStore() - - nonce, err := srvc.GetNonce(store, signer.GetPublicKey()) - if err != nil { - panic("cannot get nonce: " + err.Error()) - } - - fmt.Println(nonce) - - // Output: 0 -} - -func ExampleService_Validate() { - - exec := native.NewExecution() - exec.Set("example", exampleContract{}) - - srvc := NewService(exec, signed.NewTransactionFactory()) - - signer := bls.NewSigner() - arg := signed.WithArg(native.ContractArg, []byte("example")) - - txA, err := signed.NewTransaction(0, signer.GetPublicKey(), arg) - if err != nil { - panic("cannot create transaction A: " + err.Error()) - } - txB, err := signed.NewTransaction(1, signer.GetPublicKey(), arg) - if err != nil { - panic("cannot create transaction B: " + err.Error()) - } - txC, err := signed.NewTransaction(3, signer.GetPublicKey(), arg) - if err != nil { - panic("cannot create transaction C: " + err.Error()) - } - - store := newStore() - - res, err := srvc.Validate(store, []txn.Transaction{txA, txB, txC}) - if err != nil { - panic("validation failed: " + err.Error()) - } - - for _, txRes := range res.GetTransactionResults() { - accepted, reason := txRes.GetStatus() - - if accepted { - fmt.Println("accepted") - } else { - fmt.Println("refused", reason) - } - } - - // Output: accepted - // accepted - // refused nonce is invalid, expected 2, got 3 -} - -// exampleContract is an example of a contract doing nothing. -// -// - implements baremetal.Contract -type exampleContract struct{} - -// Execute implements baremetal.Contract -func (exampleContract) Execute(store.Snapshot, execution.Step) error { - return nil -} - -// inMemoryStore in a simple implementation of a store using an in-memory -// map. -// -// - implements store.Snapshot -type inMemoryStore struct { - sync.Mutex - - entries map[string][]byte -} - -func newStore() *inMemoryStore { - return &inMemoryStore{ - entries: make(map[string][]byte), - } -} - -// Get implements store.Readable. It returns the value associated to the key. -func (s *inMemoryStore) Get(key []byte) ([]byte, error) { - s.Lock() - defer s.Unlock() - - return s.entries[string(key)], nil -} - -// Set implements store.Writable. It sets the value for the key. -func (s *inMemoryStore) Set(key, value []byte) error { - s.Lock() - s.entries[string(key)] = value - s.Unlock() - - return nil -} - -// Delete implements store.Writable. It deletes the key from the store. -func (s *inMemoryStore) Delete(key []byte) error { - s.Lock() - delete(s.entries, string(key)) - s.Unlock() - - return nil -} diff --git a/dela/core/validation/simple/json/json.go b/dela/core/validation/simple/json/json.go deleted file mode 100644 index 1d09926..0000000 --- a/dela/core/validation/simple/json/json.go +++ /dev/null @@ -1,141 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - simple.RegisterTransactionResultFormat(serde.FormatJSON, txResFormat{}) - simple.RegisterResultFormat(serde.FormatJSON, resFormat{}) -} - -// TransactionResultJSON is the JSON message for transaction results. -type TransactionResultJSON struct { - Transaction json.RawMessage - Accepted bool - Reason string -} - -// ResultJSON is the JSON message for results. -type ResultJSON struct { - Results []json.RawMessage -} - -type txResFormat struct{} - -func (f txResFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - txres, ok := msg.(simple.TransactionResult) - if !ok { - return nil, xerrors.Errorf("unsupported message") - } - - tx, err := txres.GetTransaction().Serialize(ctx) - if err != nil { - return nil, err - } - - accepted, reason := txres.GetStatus() - - m := TransactionResultJSON{ - Transaction: tx, - Accepted: accepted, - Reason: reason, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, err - } - - return data, nil -} - -func (f txResFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := TransactionResultJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, err - } - - factory := ctx.GetFactory(simple.TransactionKey{}) - - fac, ok := factory.(txn.Factory) - if !ok { - return nil, xerrors.Errorf("invalid transaction factory") - } - - tx, err := fac.TransactionOf(ctx, m.Transaction) - if err != nil { - return nil, err - } - - res := simple.NewTransactionResult(tx, m.Accepted, m.Reason) - - return res, nil -} - -type resFormat struct{} - -func (f resFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - res, ok := msg.(simple.Result) - if !ok { - return nil, xerrors.Errorf("unsupported message") - } - - results := res.GetTransactionResults() - raws := make([]json.RawMessage, len(results)) - - for i, res := range results { - buffer, err := res.Serialize(ctx) - if err != nil { - return nil, err - } - - raws[i] = buffer - } - - m := ResultJSON{ - Results: raws, - } - - buffer, err := ctx.Marshal(m) - if err != nil { - return nil, err - } - - return buffer, nil -} - -func (f resFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := ResultJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, err - } - - factory := ctx.GetFactory(simple.ResultKey{}) - - results := make([]simple.TransactionResult, len(m.Results)) - for i, raw := range m.Results { - msg, err := factory.Deserialize(ctx, raw) - if err != nil { - return nil, err - } - - res, ok := msg.(simple.TransactionResult) - if !ok { - return nil, xerrors.Errorf("invalid transaction result") - } - - results[i] = res - } - - res := simple.NewResult(results) - - return res, nil -} diff --git a/dela/core/validation/simple/result.go b/dela/core/validation/simple/result.go deleted file mode 100644 index 988aa8d..0000000 --- a/dela/core/validation/simple/result.go +++ /dev/null @@ -1,212 +0,0 @@ -// This file contains the implementation of the result returned by the service. -// -// Documentation Last Review: 08.10.2020 -// - -package simple - -import ( - "io" - - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var ( - txResFormats = registry.NewSimpleRegistry() - resFormats = registry.NewSimpleRegistry() -) - -// RegisterTransactionResultFormat registers the engine for the provided format. -func RegisterTransactionResultFormat(f serde.Format, e serde.FormatEngine) { - txResFormats.Register(f, e) -} - -// RegisterResultFormat registers the engine for the provided format. -func RegisterResultFormat(f serde.Format, e serde.FormatEngine) { - resFormats.Register(f, e) -} - -// TransactionResult is the result of a transaction processing. It contains the -// transaction and its state of success. -// -// - implements validation.TransactionResult -type TransactionResult struct { - tx txn.Transaction - accepted bool - reason string -} - -// NewTransactionResult creates a new transaction result for the provided -// transaction. -func NewTransactionResult(tx txn.Transaction, accepted bool, reason string) TransactionResult { - return TransactionResult{ - tx: tx, - accepted: accepted, - reason: reason, - } -} - -// GetTransaction implements validation.TransactionResult. It returns the -// transaction associated to the result. -func (res TransactionResult) GetTransaction() txn.Transaction { - return res.tx -} - -// GetStatus implements validation.TransactionResult. It returns true if the -// transaction has been accepted, otherwise false with the reason. -func (res TransactionResult) GetStatus() (bool, string) { - return res.accepted, res.reason -} - -// Serialize implements serde.Message. It returns the transaction result -// serialized. -func (res TransactionResult) Serialize(ctx serde.Context) ([]byte, error) { - format := txResFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, res) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// TransactionKey is the key of the transaction factory. -type TransactionKey struct{} - -// TransactionResultFactory is the factory to deserialize transaction results. -// -// - implements serde.Factory -type TransactionResultFactory struct { - fac txn.Factory -} - -// NewTransactionResultFactory creates a new transaction result factory. -func NewTransactionResultFactory(f txn.Factory) TransactionResultFactory { - return TransactionResultFactory{ - fac: f, - } -} - -// Deserialize implements serde.Factory. It populates the transaction result if -// appropriate, otherwise it returns an error. -func (f TransactionResultFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := txResFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, TransactionKey{}, f.fac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding failed: %v", err) - } - - return msg, nil -} - -// Result is the result of a standard validation. -// -// - implements validation.Result -type Result struct { - txs []TransactionResult -} - -// NewResult creates a new result from a list of transaction results. -func NewResult(results []TransactionResult) Result { - return Result{ - txs: results, - } -} - -// GetTransactionResults implements validation.Result. It returns the -// transaction results. -func (d Result) GetTransactionResults() []validation.TransactionResult { - res := make([]validation.TransactionResult, len(d.txs)) - for i, r := range d.txs { - res[i] = r - } - - return res -} - -// Fingerprint implements serde.Fingerprinter. It writes a deterministic binary -// representation of the result. -func (d Result) Fingerprint(w io.Writer) error { - for _, res := range d.txs { - err := res.tx.Fingerprint(w) - if err != nil { - return xerrors.Errorf("couldn't fingerprint tx: %v", err) - } - - bit := []byte{0} - if res.accepted { - bit[0] = 1 - } - - _, err = w.Write(bit) - if err != nil { - return xerrors.Errorf("couldn't write accepted: %v", err) - } - } - - return nil -} - -// Serialize implements serde.Message. It returns the serialized data of the -// result. -func (d Result) Serialize(ctx serde.Context) ([]byte, error) { - format := resFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, d) - if err != nil { - return nil, xerrors.Errorf("encoding failed: %v", err) - } - - return data, nil -} - -// ResultKey is the key of the transaction result factory. -type ResultKey struct{} - -// ResultFactory is the factory to deserialize results. -// -// - implements validation.ResultFactory -type ResultFactory struct { - fac serde.Factory -} - -// NewResultFactory creates a new result factory. -func NewResultFactory(f txn.Factory) ResultFactory { - return ResultFactory{ - fac: NewTransactionResultFactory(f), - } -} - -// Deserialize implements serde.Factory. It populates the result if appropriate, -// otherwise it returns an error. -func (f ResultFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.ResultOf(ctx, data) -} - -// ResultOf implements validation.ResultFactory. It returns the result from the -// serialized data if appropriate, otherwise it returns an error. -func (f ResultFactory) ResultOf(ctx serde.Context, data []byte) (validation.Result, error) { - format := resFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, ResultKey{}, f.fac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decoding failed: %v", err) - } - - res, ok := msg.(Result) - if !ok { - return nil, xerrors.Errorf("invalid result type '%T'", msg) - } - - return res, nil -} diff --git a/dela/core/validation/simple/result_test.go b/dela/core/validation/simple/result_test.go deleted file mode 100644 index bcb836f..0000000 --- a/dela/core/validation/simple/result_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package simple - -import ( - "bytes" - "io" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func init() { - RegisterResultFormat(fake.GoodFormat, fake.Format{Msg: Result{}}) - RegisterResultFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterResultFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterTransactionResultFormat(fake.GoodFormat, fake.Format{Msg: TransactionResult{}}) - RegisterTransactionResultFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestTransactionResult_GetTransaction(t *testing.T) { - res := NewTransactionResult(fakeTx{}, true, "") - require.Equal(t, fakeTx{}, res.GetTransaction()) -} - -func TestTransactionResult_GetStatus(t *testing.T) { - res := NewTransactionResult(fakeTx{}, true, "") - - accepted, reason := res.GetStatus() - require.True(t, accepted) - require.Equal(t, "", reason) -} - -func TestTransactionResult_Serialize(t *testing.T) { - res := NewTransactionResult(fakeTx{}, true, "") - - data, err := res.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = res.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestTransactionResultFactory_Deserialize(t *testing.T) { - fac := NewTransactionResultFactory(nil) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, TransactionResult{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding failed")) -} - -func TestResult_GetTransactionResults(t *testing.T) { - res := Result{ - txs: []TransactionResult{{}, {}}, - } - - require.Len(t, res.GetTransactionResults(), 2) -} - -func TestResult_Fingerprint(t *testing.T) { - res := Result{ - txs: []TransactionResult{ - {tx: fakeTx{}}, - {tx: fakeTx{}, accepted: true}, - }, - } - - buffer := new(bytes.Buffer) - err := res.Fingerprint(buffer) - require.NoError(t, err) - - err = res.Fingerprint(fake.NewBadHash()) - require.EqualError(t, err, fake.Err("couldn't write accepted")) - - res.txs[0].tx = fakeTx{err: fake.GetError()} - err = res.Fingerprint(buffer) - require.EqualError(t, err, fake.Err("couldn't fingerprint tx")) -} - -func TestResult_Serialize(t *testing.T) { - res := NewResult(nil) - - data, err := res.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = res.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encoding failed")) -} - -func TestResultFactory_Deserialize(t *testing.T) { - fac := NewResultFactory(nil) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, Result{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decoding failed")) - - _, err = fac.Deserialize(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid result type 'fake.Message'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeTx struct { - txn.Transaction - - nonce uint64 - pubkey crypto.PublicKey - err error -} - -func newTx() fakeTx { - return fakeTx{ - pubkey: fake.PublicKey{}, - } -} - -func (tx fakeTx) GetID() []byte { - return []byte{0xa, 0xb, 0xc, 0xd} -} - -func (tx fakeTx) GetIdentity() access.Identity { - return tx.pubkey -} - -func (tx fakeTx) GetNonce() uint64 { - return tx.nonce -} - -func (tx fakeTx) Fingerprint(io.Writer) error { - return tx.err -} diff --git a/dela/core/validation/simple/simple.go b/dela/core/validation/simple/simple.go deleted file mode 100644 index 5f901a5..0000000 --- a/dela/core/validation/simple/simple.go +++ /dev/null @@ -1,187 +0,0 @@ -// Package simple implements a validation service that executes a batch -// of transactions sequentially. -// -// Documentation Last Review: 08.10.2020 -package simple - -import ( - "encoding/binary" - "fmt" - - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/crypto" - "golang.org/x/xerrors" -) - -// Service is a standard validation service that will process the batch and -// update the snapshot accordingly. -// -// - implements validation.Service -type Service struct { - execution execution.Service - fac validation.ResultFactory - hashFac crypto.HashFactory -} - -// NewService creates a new validation service. -func NewService(exec execution.Service, f txn.Factory) Service { - return Service{ - execution: exec, - fac: NewResultFactory(f), - hashFac: crypto.NewSha256Factory(), - } -} - -// GetFactory implements validation.Service. It returns the result factory. -func (s Service) GetFactory() validation.ResultFactory { - return s.fac -} - -// GetNonce implements validation.Service. It reads the latest nonce in the -// storage for the given identity and returns the next valid nonce. -func (s Service) GetNonce(store store.Readable, ident access.Identity) (uint64, error) { - if ident == nil { - return 0, xerrors.New("missing identity in transaction") - } - - key, err := s.keyFromIdentity(ident) - if err != nil { - return 0, xerrors.Errorf("key: %v", err) - } - - value, err := store.Get(key) - if err != nil { - return 0, xerrors.Errorf("store: %v", err) - } - - if value == nil || len(value) != 8 { - return 0, nil - } - - return binary.LittleEndian.Uint64(value) + 1, nil -} - -// Accept implements validation.Service. It returns nil if the transaction would -// be accepted by the service given some leeway and a snapshot of the storage. -func (s Service) Accept(store store.Readable, tx txn.Transaction, leeway validation.Leeway) error { - nonce, err := s.GetNonce(store, tx.GetIdentity()) - if err != nil { - return xerrors.Errorf("while reading nonce: %v", err) - } - - if tx.GetNonce() < nonce { - return xerrors.Errorf("nonce '%d' < '%d'", tx.GetNonce(), nonce) - } - - limit := nonce + uint64(leeway.MaxSequenceDifference) - - if tx.GetNonce() > limit { - return xerrors.Errorf("nonce '%d' above the limit '%d'", tx.GetNonce(), limit) - } - - return nil -} - -// Validate implements validation.Service. It processes the list of transactions -// while updating the snapshot then returns a bundle of the transaction results. -func (s Service) Validate(store store.Snapshot, txs []txn.Transaction) (validation.Result, error) { - results := make([]TransactionResult, len(txs)) - - step := execution.Step{ - Previous: make([]txn.Transaction, 0, len(txs)), - } - - for i, tx := range txs { - res := TransactionResult{tx: tx} - - step.Current = tx - - err := s.validateTx(store, step, &res) - if err != nil { - return nil, xerrors.Errorf("tx %#x: %v", tx.GetID()[:4], err) - } - - if res.accepted { - step.Previous = append(step.Previous, tx) - } - - results[i] = res - } - - res := Result{ - txs: results, - } - - return res, nil -} - -func (s Service) validateTx(store store.Snapshot, step execution.Step, r *TransactionResult) error { - expectedNonce, err := s.GetNonce(store, step.Current.GetIdentity()) - if err != nil { - return xerrors.Errorf("nonce: %v", err) - } - - if expectedNonce != step.Current.GetNonce() { - r.reason = fmt.Sprintf("nonce is invalid, expected %d, got %d", - expectedNonce, step.Current.GetNonce()) - r.accepted = false - - return nil - } - - res, err := s.execution.Execute(store, step) - // if the execution fail, we don't return an error, but we take it as an - // invalid transaction. - if err != nil { - r.reason = xerrors.Errorf("failed to execute transaction: %v", err).Error() - r.accepted = false - } else { - r.reason = res.Message - r.accepted = res.Accepted - } - - // Update the nonce associated to the identity so that this transaction - // cannot be applied again. - err = s.set(store, step.Current.GetIdentity(), step.Current.GetNonce()) - if err != nil { - return xerrors.Errorf("failed to set nonce: %v", err) - } - - return nil -} - -func (s Service) set(store store.Snapshot, ident access.Identity, nonce uint64) error { - key, err := s.keyFromIdentity(ident) - if err != nil { - return xerrors.Errorf("key: %v", err) - } - - buffer := make([]byte, 8) - binary.LittleEndian.PutUint64(buffer, nonce) - - err = store.Set(key, buffer) - if err != nil { - return xerrors.Errorf("store: %v", err) - } - - return nil -} - -func (s Service) keyFromIdentity(ident access.Identity) ([]byte, error) { - data, err := ident.MarshalText() - if err != nil { - return nil, xerrors.Errorf("failed to marshal identity: %v", err) - } - - h := s.hashFac.New() - _, err = h.Write(data) - if err != nil { - return nil, xerrors.Errorf("failed to write identity: %v", err) - } - - return h.Sum(nil), nil -} diff --git a/dela/core/validation/simple/simple_test.go b/dela/core/validation/simple/simple_test.go deleted file mode 100644 index b8e81ee..0000000 --- a/dela/core/validation/simple/simple_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package simple - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/execution" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/internal/testing/fake" - "golang.org/x/xerrors" -) - -func TestService_GetFactory(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - require.NotNil(t, srvc.GetFactory()) -} - -func TestService_GetNonce(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - nonce, err := srvc.GetNonce(fakeSnapshot{}, fake.PublicKey{}) - require.NoError(t, err) - require.Equal(t, uint64(0), nonce) - - buffer := make([]byte, 8) - buffer[0] = 2 - nonce, err = srvc.GetNonce(fakeSnapshot{value: buffer}, fake.PublicKey{}) - require.NoError(t, err) - require.Equal(t, uint64(3), nonce) - - _, err = srvc.GetNonce(fakeSnapshot{}, fake.NewBadPublicKey()) - require.EqualError(t, err, fake.Err("key: failed to marshal identity")) - - _, err = srvc.GetNonce(fakeSnapshot{errGet: fake.GetError()}, fake.PublicKey{}) - require.EqualError(t, err, fake.Err("store")) -} - -func TestService_Accept(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - tx := newTx() - tx.nonce = 5 - - err := srvc.Accept(fakeSnapshot{}, tx, validation.Leeway{MaxSequenceDifference: 5}) - require.NoError(t, err) -} - -func TestService_NilIdentity_Accept(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - err := srvc.Accept(fakeSnapshot{}, fakeTx{}, validation.Leeway{}) - require.EqualError(t, err, "while reading nonce: missing identity in transaction") -} - -func TestService_OlderNonce_Accept(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - value := make([]byte, 8) - value[0] = 5 - - err := srvc.Accept(fakeSnapshot{value: value}, newTx(), validation.Leeway{}) - require.EqualError(t, err, "nonce '0' < '6'") -} - -func TestService_FutureNonce_Accept(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - tx := newTx() - tx.nonce = 5 - - err := srvc.Accept(fakeSnapshot{}, tx, validation.Leeway{MaxSequenceDifference: 1}) - require.EqualError(t, err, "nonce '5' above the limit '1'") -} - -func TestService_Validate(t *testing.T) { - exec := &fakeExec{check: true} - srvc := NewService(exec, nil) - - res, err := srvc.Validate(fakeSnapshot{}, []txn.Transaction{newTx(), newTx(), newTx()}) - require.NoError(t, err) - require.NotNil(t, res) - require.Equal(t, 3, exec.count) - - tx := newTx() - tx.nonce = 1 - res, err = srvc.Validate(fakeSnapshot{}, []txn.Transaction{tx}) - require.NoError(t, err) - - status, _ := res.GetTransactionResults()[0].GetStatus() - require.False(t, status) -} - -func TestService_NilIdentity_Validate(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - _, err := srvc.Validate(fakeSnapshot{}, []txn.Transaction{fakeTx{}}) - require.EqualError(t, err, "tx 0x0a0b0c0d: nonce: missing identity in transaction") -} - -func TestService_FailStore_Validate(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - - store := fakeSnapshot{errSet: fake.GetError()} - - _, err := srvc.Validate(store, []txn.Transaction{newTx()}) - require.EqualError(t, err, fake.Err("tx 0x0a0b0c0d: failed to set nonce: store")) -} - -func TestService_FailIdentityToKey_Validate(t *testing.T) { - srvc := NewService(&fakeExec{}, nil) - srvc.hashFac = fake.NewHashFactory(fake.NewBadHash()) - - err := srvc.set(fakeSnapshot{}, fake.PublicKey{}, 0) - require.EqualError(t, err, fake.Err("key: failed to write identity")) -} - -func TestService_FailExecuteTx_Validate(t *testing.T) { - srvc := NewService(&fakeExec{err: fake.GetError()}, nil) - - res, err := srvc.Validate(fakeSnapshot{}, []txn.Transaction{newTx()}) - require.NoError(t, err) - - status, msg := res.GetTransactionResults()[0].GetStatus() - require.False(t, status) - require.Equal(t, fake.Err("failed to execute transaction"), msg) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeExec struct { - err error - count int - check bool -} - -func (e *fakeExec) Execute(store store.Snapshot, step execution.Step) (execution.Result, error) { - if e.check && e.count != len(step.Previous) { - return execution.Result{}, xerrors.New("missing previous txs") - } - - e.count++ - return execution.Result{Accepted: true}, e.err -} - -type fakeSnapshot struct { - store.Snapshot - - value []byte - errGet error - errSet error -} - -func (s fakeSnapshot) Get(key []byte) ([]byte, error) { - return s.value, s.errGet -} - -func (s fakeSnapshot) Set(key, value []byte) error { - return s.errSet -} diff --git a/dela/core/validation/validation.go b/dela/core/validation/validation.go deleted file mode 100644 index 1054bb8..0000000 --- a/dela/core/validation/validation.go +++ /dev/null @@ -1,68 +0,0 @@ -// Package validation defines a validation service that will apply a batch of -// transactions to a store snapshot. -// -// Documentation Last Review: 08.10.2020 -package validation - -import ( - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/serde" -) - -// TransactionResult is the result of a transaction execution. -type TransactionResult interface { - serde.Message - - // GetTransaction returns the transaction associated to the result. - GetTransaction() txn.Transaction - - // GetStatus returns the status of the execution. It returns true if the - // transaction has been accepted, otherwise false with a message to explain - // the reason. - GetStatus() (bool, string) -} - -// Result is the result of a validation. -type Result interface { - serde.Message - serde.Fingerprinter - - // GetTransactionResults returns the results. - GetTransactionResults() []TransactionResult -} - -// ResultFactory is the factory for results. -type ResultFactory interface { - serde.Factory - - ResultOf(serde.Context, []byte) (Result, error) -} - -// Leeway is the configuration when asserting if a transaction will be accepted -// to lighten some of the constraints. -type Leeway struct { - // MaxSequenceDifference defines how much from the current sequence the - // transaction can differ. - MaxSequenceDifference int -} - -// Service is the validation service that will process a batch of transactions -// into a result that can be used as a payload of a block. -type Service interface { - // GetFactory returns the result factory. - GetFactory() ResultFactory - - // GetNonce returns the nonce associated with the identity. The value - // returned should be used for the next transaction to be valid. - GetNonce(store.Readable, access.Identity) (uint64, error) - - // Accept returns nil if the transaction will be accepted by the service. - // The leeway parameter allows to reduce some constraints. - Accept(store.Readable, txn.Transaction, Leeway) error - - // Validate takes a snapshot and a list of transactions and returns a - // result. - Validate(store.Snapshot, []txn.Transaction) (Result, error) -} diff --git a/dela/core/watcher.go b/dela/core/watcher.go deleted file mode 100644 index ccba401..0000000 --- a/dela/core/watcher.go +++ /dev/null @@ -1,70 +0,0 @@ -// Package core implements commonly used tools. -// -// Documentation Last Review: 08.10.2020 -// -package core - -import "sync" - -// Observer is the interface to implement to watch events. -type Observer interface { - NotifyCallback(event interface{}) -} - -// Observable provides primitives to add and remove observers and to notify -// them of new events. -type Observable interface { - // Add adds the observer to the list of observers that will be notified of - // new events. - Add(observer Observer) - - // Remove removes the observer from the list thus stopping it from receiving - // new events. - Remove(observer Observer) - - // Notify notifies the observers of a new event. - Notify(event interface{}) -} - -// Watcher is an implementation of the Observable interface. -// -// - implements core.Observable -type Watcher struct { - sync.RWMutex - - observers map[Observer]struct{} -} - -// NewWatcher creates a new empty watcher. -func NewWatcher() *Watcher { - return &Watcher{ - observers: make(map[Observer]struct{}), - } -} - -// Add implements core.Observable. It adds the observer to the list of observers -// that will be notified of new events. -func (w *Watcher) Add(observer Observer) { - w.Lock() - w.observers[observer] = struct{}{} - w.Unlock() -} - -// Remove implements core.Observable. It removes the observer from the list thus -// stopping it from receiving new events. -func (w *Watcher) Remove(observer Observer) { - w.Lock() - delete(w.observers, observer) - w.Unlock() -} - -// Notify implements core.Observable. It notifies the whole list of observers -// one after each other. -func (w *Watcher) Notify(event interface{}) { - w.RLock() - defer w.RUnlock() - - for w := range w.observers { - w.NotifyCallback(event) - } -} diff --git a/dela/core/watcher_test.go b/dela/core/watcher_test.go deleted file mode 100644 index e37a039..0000000 --- a/dela/core/watcher_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package core - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestWatcher_Add(t *testing.T) { - watcher := NewWatcher() - - watcher.Add(fakeObserver{ch: make(chan interface{})}) - require.Len(t, watcher.observers, 1) - - obs := fakeObserver{ch: make(chan interface{})} - watcher.Add(obs) - require.Len(t, watcher.observers, 2) - - watcher.Add(obs) - require.Len(t, watcher.observers, 2) -} - -func TestWatcher_Remove(t *testing.T) { - watcher := NewWatcher() - watcher.observers[newFakeObserver()] = struct{}{} - - obs := newFakeObserver() - watcher.observers[obs] = struct{}{} - require.Len(t, watcher.observers, 2) - - watcher.Remove(obs) - require.Len(t, watcher.observers, 1) - - watcher.Remove(obs) - require.Len(t, watcher.observers, 1) -} - -func TestWatcher_Notify(t *testing.T) { - watcher := NewWatcher() - - obs := newFakeObserver() - watcher.observers[obs] = struct{}{} - - watcher.Notify(struct{}{}) - evt := <-obs.ch - require.NotNil(t, evt) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeObserver struct { - ch chan interface{} -} - -func (o fakeObserver) NotifyCallback(evt interface{}) { - o.ch <- evt -} - -func newFakeObserver() fakeObserver { - return fakeObserver{ - ch: make(chan interface{}, 1), - } -} diff --git a/dela/cosi/cosi.go b/dela/cosi/cosi.go deleted file mode 100644 index bf36f14..0000000 --- a/dela/cosi/cosi.go +++ /dev/null @@ -1,73 +0,0 @@ -// Package cosi defines a collective signing protocol abstraction. A set of -// participants will work with each others to sign a unique message collectively -// in the sense that the protocol produces a single signature that will verify -// the full, or partial, aggregated public key. -// -// Related Papers: -// -// Enhancing Bitcoin Security and Performance with Strong Consistency via -// Collective Signing (2016) -// https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_kokoris-kogias.pdf -// -// On the Security of Two-Round Multi-Signatures (2019) -// https://eprint.iacr.org/2018/417.pdf -// -// Documentation Last Review: 05.10.2020 -package cosi - -import ( - "context" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// Reactor is a collective signature event handler. Every participant must react -// to an incoming signature request from the leader, and this abstraction -// provides the primitive that allows to do so. -type Reactor interface { - serde.Factory - - // Invoke is provided with the message and the address of the sender and it - // should return the unique hash for this message, or an error for malformed - // messages. - Invoke(addr mino.Address, in serde.Message) ([]byte, error) -} - -// Actor provides a primitive to sign a message. -type Actor interface { - // Sign collects the signature of the collective authority and creates an - // aggregated signature. - Sign(ctx context.Context, msg serde.Message, - ca crypto.CollectiveAuthority) (crypto.Signature, error) -} - -// Threshold is a function that returns the threshold to reach for a given n, -// which means it is always positive and below or equal to n. -type Threshold func(int) int - -// CollectiveSigning is the interface that provides the primitives to sign a -// message by members of a network. -type CollectiveSigning interface { - // GetSigner returns the individual signer assigned to the instance. One - // should not use it to verify a collective signature but only for identity - // verification. - GetSigner() crypto.Signer - - // GetPublicKeyFactory returns the aggregate public key factory. - GetPublicKeyFactory() crypto.PublicKeyFactory - - // GetSignatureFactory returns the aggregate signature factory. - GetSignatureFactory() crypto.SignatureFactory - - // GetVerifierFactory returns a factory that can create a verifier to check - // the validity of a signature. - GetVerifierFactory() crypto.VerifierFactory - - // SetThreshold updates the threshold required by a collective signature. - SetThreshold(Threshold) - - // Listen starts the collective signing so that it will answer to requests. - Listen(Reactor) (Actor, error) -} diff --git a/dela/cosi/flatcosi/example_test.go b/dela/cosi/flatcosi/example_test.go deleted file mode 100644 index df02fc0..0000000 --- a/dela/cosi/flatcosi/example_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package flatcosi - -import ( - "context" - "errors" - "fmt" - "time" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minoch" - "go.dedis.ch/dela/serde" -) - -func Example() { - // Create the network overlay instances. It uses channels to communicate. - manager := minoch.NewManager() - - mA := minoch.MustCreate(manager, "A") - mB := minoch.MustCreate(manager, "B") - - // The list of participants to the signature. - roster := fake.NewAuthorityFromMino(bls.Generate, mA, mB) - - // Create the collective signing endpoints for both A and B. - cosiA := NewFlat(mA, roster.GetSigner(0).(crypto.AggregateSigner)) - - actor, err := cosiA.Listen(exampleReactor{}) - if err != nil { - panic(fmt.Sprintf("failed to listen on root: %+v", err)) - } - - cosiB := NewFlat(mB, roster.GetSigner(1).(crypto.AggregateSigner)) - _, err = cosiB.Listen(exampleReactor{}) - if err != nil { - panic(fmt.Sprintf("failed to listen on child: %+v", err)) - } - - // Context to timeout after 30 seconds if no signature is received. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - msg := exampleMessage{Value: "42"} - - signature, err := actor.Sign(ctx, msg, roster) - if err != nil { - panic(fmt.Sprintf("failed to sign: %+v", err)) - } - - // We need a verifier implementation to support collective signatures. - verifier, err := cosiA.GetVerifierFactory().FromAuthority(roster) - if err != nil { - panic(fmt.Sprintf("verifier failed: %+v", err)) - } - - err = verifier.Verify([]byte(msg.Value), signature) - if err != nil { - panic(fmt.Sprintf("signature is invalid: %+v", err)) - } - - fmt.Println("Success", err == nil) - - // Output: Signing value 42 - // Signing value 42 - // Signing value 42 - // Success true -} - -type exampleMessage struct { - Value string -} - -func (msg exampleMessage) Serialize(ctx serde.Context) ([]byte, error) { - return ctx.Marshal(msg) -} - -type exampleReactor struct{} - -func (exampleReactor) Invoke(from mino.Address, msg serde.Message) ([]byte, error) { - example, ok := msg.(exampleMessage) - if !ok { - return nil, errors.New("unsupported message") - } - - fmt.Println("Signing value", example.Value) - - return []byte(example.Value), nil -} - -func (exampleReactor) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - var msg exampleMessage - err := ctx.Unmarshal(data, &msg) - - return msg, err -} diff --git a/dela/cosi/flatcosi/flatcosi.go b/dela/cosi/flatcosi/flatcosi.go deleted file mode 100644 index 8d9b5b5..0000000 --- a/dela/cosi/flatcosi/flatcosi.go +++ /dev/null @@ -1,170 +0,0 @@ -// Package flatcosi is a flat implementation of a collective signing so that the -// orchestrator will contact all the participants to require their signatures -// and then aggregate them to the final one. -// -// Documentation Last Review: 05.10.2020 -package flatcosi - -import ( - "context" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -const ( - rpcName = "cosi" -) - -// Flat is an implementation of the collective signing interface by -// using BLS signatures. It ignores the threshold and always requests a -// signature from every participant. -// -// - implements cosi.CollectiveSigning -type Flat struct { - mino mino.Mino - signer crypto.AggregateSigner -} - -// NewFlat returns a new collective signing instance. -func NewFlat(o mino.Mino, signer crypto.AggregateSigner) *Flat { - return &Flat{ - mino: o, - signer: signer, - } -} - -// GetSigner implements cosi.CollectiveSigning. It returns the signer of the -// instance. -func (flat *Flat) GetSigner() crypto.Signer { - return flat.signer -} - -// GetPublicKeyFactory implements cosi.CollectiveSigning. It returns the public -// key factory. -func (flat *Flat) GetPublicKeyFactory() crypto.PublicKeyFactory { - return flat.signer.GetPublicKeyFactory() -} - -// GetSignatureFactory implements cosi.CollectiveSigning. It returns the -// signature factory. -func (flat *Flat) GetSignatureFactory() crypto.SignatureFactory { - return flat.signer.GetSignatureFactory() -} - -// GetVerifierFactory implements cosi.CollectiveSigning. It returns the verifier -// factory. -func (flat *Flat) GetVerifierFactory() crypto.VerifierFactory { - return flat.signer.GetVerifierFactory() -} - -// SetThreshold implements cosi.CollectiveSigning. It ignores the new threshold -// as this implementation only accepts full participation. -func (flat *Flat) SetThreshold(fn cosi.Threshold) {} - -// Listen implements cosi.CollectiveSigning. It creates an actor that starts an -// RPC called cosi and respond to signing requests. The actor can also be used -// to sign a message. -func (flat *Flat) Listen(r cosi.Reactor) (cosi.Actor, error) { - actor := flatActor{ - logger: dela.Logger, - me: flat.mino.GetAddress(), - signer: flat.signer, - reactor: r, - } - - factory := cosi.NewMessageFactory(r, flat.signer.GetSignatureFactory()) - - actor.rpc = mino.MustCreateRPC(flat.mino, rpcName, newHandler(flat.signer, r), factory) - - return actor, nil -} - -// FlatActor is the active component of the flat collective signing. It provides -// a primitive to trigger a request for signatures from the participants. -// -// - implements cosi.Actor -type flatActor struct { - logger zerolog.Logger - me mino.Address - rpc mino.RPC - signer crypto.AggregateSigner - reactor cosi.Reactor -} - -// Sign implements cosi.Actor. It returns the collective signature of the -// message if every participant returns its signature. -func (a flatActor) Sign(ctx context.Context, msg serde.Message, - ca crypto.CollectiveAuthority) (crypto.Signature, error) { - - verifier, err := a.signer.GetVerifierFactory().FromAuthority(ca) - if err != nil { - return nil, xerrors.Errorf("couldn't make verifier: %v", err) - } - - req := cosi.SignatureRequest{ - Value: msg, - } - - msgs, err := a.rpc.Call(ctx, req, ca) - if err != nil { - return nil, xerrors.Errorf("call aborted: %v", err) - } - - digest, err := a.reactor.Invoke(a.me, msg) - if err != nil { - return nil, xerrors.Errorf("couldn't react to message: %v", err) - } - - var agg crypto.Signature - for { - resp, more := <-msgs - if !more { - if agg == nil { - return nil, xerrors.New("signature is nil") - } - - err = verifier.Verify(digest, agg) - if err != nil { - return nil, xerrors.Errorf("couldn't verify the aggregation: %v", err) - } - - return agg, nil - } - - reply, err := resp.GetMessageOrError() - if err != nil { - return nil, xerrors.Errorf("one request has failed: %v", err) - } - - agg, err = a.processResponse(reply, agg) - if err != nil { - return nil, xerrors.Errorf("couldn't process response: %v", err) - } - } -} - -func (a flatActor) processResponse(resp serde.Message, agg crypto.Signature) (crypto.Signature, error) { - reply, ok := resp.(cosi.SignatureResponse) - if !ok { - return nil, xerrors.Errorf("invalid response type '%T'", resp) - } - - var err error - - if agg == nil { - agg = reply.Signature - } else { - agg, err = a.signer.Aggregate(agg, reply.Signature) - if err != nil { - return nil, xerrors.Errorf("couldn't aggregate: %v", err) - } - } - - return agg, nil -} diff --git a/dela/cosi/flatcosi/flatcosi_test.go b/dela/cosi/flatcosi/flatcosi_test.go deleted file mode 100644 index 5988c34..0000000 --- a/dela/cosi/flatcosi/flatcosi_test.go +++ /dev/null @@ -1,194 +0,0 @@ -package flatcosi - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestFlat_GetSigner(t *testing.T) { - flat := NewFlat(nil, fake.NewAggregateSigner()) - require.NotNil(t, flat.GetSigner()) -} - -func TestFlat_GetPublicKeyFactory(t *testing.T) { - flat := NewFlat(nil, fake.NewAggregateSigner()) - require.NotNil(t, flat.GetPublicKeyFactory()) -} - -func TestFlat_GetSignatureFactory(t *testing.T) { - flat := NewFlat(nil, fake.NewAggregateSigner()) - require.NotNil(t, flat.GetSignatureFactory()) -} - -func TestFlat_GetVerifierFactory(t *testing.T) { - flat := NewFlat(nil, fake.NewAggregateSigner()) - require.NotNil(t, flat.GetVerifierFactory()) -} - -func TestFlat_Listen(t *testing.T) { - flat := NewFlat(fake.Mino{}, bls.NewSigner()) - - a, err := flat.Listen(fakeReactor{}) - require.NoError(t, err) - actor := a.(flatActor) - require.NotNil(t, actor.signer) - require.NotNil(t, actor.rpc) -} - -func TestActor_Sign(t *testing.T) { - message := fake.Message{} - ca := fake.NewAuthority(1, fake.NewSigner) - - rpc := fake.NewRPC() - actor := flatActor{ - signer: fake.NewAggregateSigner(), - rpc: rpc, - reactor: fakeReactor{}, - } - - rpc.SendResponse(nil, cosi.SignatureResponse{Signature: fake.Signature{}}) - rpc.SendResponse(nil, cosi.SignatureResponse{Signature: fake.Signature{}}) - rpc.Done() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sig, err := actor.Sign(ctx, message, ca) - require.NoError(t, err) - require.NotNil(t, sig) -} - -func TestActor_NetworkError_Sign(t *testing.T) { - actor := flatActor{ - signer: fake.NewAggregateSigner(), - rpc: fake.NewBadRPC(), - reactor: fakeReactor{}, - } - - ctx := context.Background() - message := fake.Message{} - roster := fake.NewAuthority(3, fake.NewSigner) - - _, err := actor.Sign(ctx, message, roster) - require.EqualError(t, err, fake.Err("call aborted")) -} - -func TestActor_FailVerifier_Sign(t *testing.T) { - actor := flatActor{ - signer: fake.NewSignerWithVerifierFactory(fake.NewBadVerifierFactory()), - rpc: fake.NewBadRPC(), - reactor: fakeReactor{}, - } - - ctx := context.Background() - message := fake.Message{} - roster := fake.NewAuthority(3, fake.NewSigner) - - _, err := actor.Sign(ctx, message, roster) - require.EqualError(t, err, fake.Err("couldn't make verifier")) -} - -func TestActor_DenyingReactor_Sign(t *testing.T) { - actor := flatActor{ - signer: fake.NewAggregateSigner(), - rpc: fake.NewRPC(), - reactor: fakeReactor{err: fake.GetError()}, - } - - ctx := context.Background() - message := fake.Message{} - roster := fake.NewAuthority(3, fake.NewSigner) - - _, err := actor.Sign(ctx, message, roster) - require.EqualError(t, err, fake.Err("couldn't react to message")) -} - -func TestActor_SignWrongSignature(t *testing.T) { - message := fake.Message{} - ca := fake.NewAuthority(1, fake.NewSigner) - - rpc := fake.NewRPC() - actor := flatActor{ - signer: fake.NewSignerWithVerifierFactory(fake.NewVerifierFactory(fake.NewBadVerifier())), - rpc: rpc, - reactor: fakeReactor{}, - } - - rpc.SendResponse(nil, cosi.SignatureResponse{Signature: fake.Signature{}}) - rpc.Done() - - ctx := context.Background() - - _, err := actor.Sign(ctx, message, ca) - require.EqualError(t, err, fake.Err("couldn't verify the aggregation")) -} - -func TestActor_RPCError_Sign(t *testing.T) { - message := fake.Message{} - ca := fake.NewAuthority(1, fake.NewSigner) - - rpc := fake.NewRPC() - actor := flatActor{ - signer: ca.GetSigner(0).(crypto.AggregateSigner), - rpc: rpc, - reactor: fakeReactor{}, - } - - rpc.Done() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sig, err := actor.Sign(ctx, message, ca) - require.EqualError(t, err, "signature is nil") - require.Nil(t, sig) -} - -func TestActor_Context_Sign(t *testing.T) { - message := fake.Message{} - ca := fake.NewAuthority(1, fake.NewSigner) - rpc := fake.NewRPC() - - actor := flatActor{ - signer: ca.GetSigner(0).(crypto.AggregateSigner), - rpc: rpc, - reactor: fakeReactor{}, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - rpc.SendResponseWithError(nil, fake.GetError()) - rpc.Done() - - sig, err := actor.Sign(ctx, message, ca) - require.EqualError(t, err, fake.Err("one request has failed")) - require.Nil(t, sig) -} - -func TestActor_SignProcessError(t *testing.T) { - ca := fake.NewAuthority(1, fake.NewSigner) - - rpc := fake.NewRPC() - actor := flatActor{ - signer: ca.GetSigner(0).(crypto.AggregateSigner), - reactor: fakeReactor{}, - rpc: rpc, - } - - rpc.SendResponse(nil, fake.Message{}) - rpc.Done() - _, err := actor.Sign(context.Background(), fake.Message{}, ca) - require.EqualError(t, err, - "couldn't process response: invalid response type 'fake.Message'") - - actor.signer = fake.NewBadSigner() - _, err = actor.processResponse(cosi.SignatureResponse{}, fake.Signature{}) - require.EqualError(t, err, fake.Err("couldn't aggregate")) -} diff --git a/dela/cosi/flatcosi/handler.go b/dela/cosi/flatcosi/handler.go deleted file mode 100644 index d685367..0000000 --- a/dela/cosi/flatcosi/handler.go +++ /dev/null @@ -1,55 +0,0 @@ -package flatcosi - -import ( - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// Handler is the RPC callback when a participant of a collective signing -// receives a request. It will invoke the reactor and sign the unique value, or -// return an error if the reactor refuses the message. -// -// - implements mino.Handler -type handler struct { - mino.UnsupportedHandler - signer crypto.Signer - reactor cosi.Reactor -} - -func newHandler(s crypto.Signer, r cosi.Reactor) handler { - return handler{ - signer: s, - reactor: r, - } -} - -// Process implements mino.Handler. It sends the message to the reactor and -// sends back the signature if the message is correctly processed, otherwise it -// returns an error. -func (h handler) Process(req mino.Request) (serde.Message, error) { - switch msg := req.Message.(type) { - - case cosi.SignatureRequest: - buf, err := h.reactor.Invoke(req.Address, msg.Value) - if err != nil { - return nil, xerrors.Errorf("couldn't hash message: %v", err) - } - - sig, err := h.signer.Sign(buf) - if err != nil { - return nil, xerrors.Errorf("couldn't sign: %v", err) - } - - resp := cosi.SignatureResponse{ - Signature: sig, - } - - return resp, nil - - default: - return nil, xerrors.Errorf("invalid message type '%T'", msg) - } -} diff --git a/dela/cosi/flatcosi/handler_test.go b/dela/cosi/flatcosi/handler_test.go deleted file mode 100644 index e62469f..0000000 --- a/dela/cosi/flatcosi/handler_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package flatcosi - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func TestHandler_Process(t *testing.T) { - signer := bls.NewSigner() - - h := newHandler(signer, fakeReactor{}) - req := mino.Request{ - Message: cosi.SignatureRequest{Value: fake.Message{}}, - } - - msg, err := h.Process(req) - require.NoError(t, err) - - resp, ok := msg.(cosi.SignatureResponse) - require.True(t, ok) - - err = signer.GetPublicKey().Verify(testValue, resp.Signature) - require.NoError(t, err) -} - -func TestHandler_InvalidMessage_Process(t *testing.T) { - h := newHandler(fake.NewSigner(), fakeReactor{}) - - resp, err := h.Process(mino.Request{Message: fake.Message{}}) - require.EqualError(t, err, "invalid message type 'fake.Message'") - require.Nil(t, resp) -} - -func TestHandler_DenyingReactor_Process(t *testing.T) { - h := newHandler(fake.NewSigner(), fakeReactor{err: fake.GetError()}) - - req := mino.Request{ - Message: cosi.SignatureRequest{Value: fake.Message{}}, - } - - _, err := h.Process(req) - require.EqualError(t, err, fake.Err("couldn't hash message")) -} - -func TestHandler_FailSign_Process(t *testing.T) { - h := newHandler(fake.NewBadSigner(), fakeReactor{}) - - req := mino.Request{ - Message: cosi.SignatureRequest{Value: fake.Message{}}, - } - - _, err := h.Process(req) - require.EqualError(t, err, fake.Err("couldn't sign")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -var testValue = []byte{0xab} - -type fakeReactor struct { - err error -} - -func (h fakeReactor) Invoke(mino.Address, serde.Message) ([]byte, error) { - return testValue, h.err -} - -func (h fakeReactor) Deserialize(serde.Context, []byte) (serde.Message, error) { - return fake.Message{}, h.err -} diff --git a/dela/cosi/json/json.go b/dela/cosi/json/json.go deleted file mode 100644 index 7243c18..0000000 --- a/dela/cosi/json/json.go +++ /dev/null @@ -1,113 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - cosi.RegisterMessageFormat(serde.FormatJSON, msgFormat{}) -} - -// Request is the JSON message sent to request signature. -type Request struct { - Value json.RawMessage -} - -// Response is the JSON message sent to respond with a signature. -type Response struct { - Signature json.RawMessage -} - -// Message is a JSON container to differentiate the different messages of flat -// collective signing. -type Message struct { - Request *Request `json:",omitempty"` - Response *Response `json:",omitempty"` -} - -// MsgFormat is the engine to encode and decode collective signing messages in -// JSON format. -// -// - implements serde.FormatEngine -type msgFormat struct{} - -// Decode implements serde.FormatEngine. It returns the serialized data of a -// message in JSON format. -func (f msgFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - m := Message{} - - switch message := msg.(type) { - case cosi.SignatureRequest: - value, err := message.Value.Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize message: %v", err) - } - - m.Request = &Request{ - Value: value, - } - case cosi.SignatureResponse: - sig, err := message.Signature.Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize signature: %v", err) - } - - m.Response = &Response{ - Signature: sig, - } - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the message with the JSON -// data if appropriate, otherwise it returns an error. -func (f msgFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := Message{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't unmarshal message: %v", err) - } - - if m.Request != nil { - factory := ctx.GetFactory(cosi.MsgKey{}) - if factory == nil { - return nil, xerrors.New("factory is nil") - } - - value, err := factory.Deserialize(ctx, m.Request.Value) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize value: %v", err) - } - - return cosi.SignatureRequest{Value: value}, nil - } - - if m.Response != nil { - factory := ctx.GetFactory(cosi.SigKey{}) - - fac, ok := factory.(crypto.SignatureFactory) - if !ok { - return nil, xerrors.Errorf("invalid factory of type '%T'", factory) - } - - sig, err := fac.SignatureOf(ctx, m.Response.Signature) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize signature: %v", err) - } - - return cosi.SignatureResponse{Signature: sig}, nil - } - - return nil, xerrors.Errorf("message is empty") -} diff --git a/dela/cosi/json/json_test.go b/dela/cosi/json/json_test.go deleted file mode 100644 index cd99570..0000000 --- a/dela/cosi/json/json_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestMsgFormat_Encode(t *testing.T) { - req := cosi.SignatureRequest{ - Value: fake.Message{}, - } - - format := msgFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - data, err := format.Encode(ctx, req) - require.NoError(t, err) - require.Equal(t, `{"Request":{"Value":{}}}`, string(data)) - - req.Value = fake.NewBadPublicKey() - _, err = format.Encode(ctx, req) - require.EqualError(t, err, fake.Err("couldn't serialize message")) - - req.Value = fake.PublicKey{} - _, err = format.Encode(fake.NewBadContext(), req) - require.EqualError(t, err, fake.Err("couldn't marshal")) -} - -func TestMsgFormat_SignatureResponse_Encode(t *testing.T) { - resp := cosi.SignatureResponse{ - Signature: fake.Signature{}, - } - - format := msgFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - data, err := format.Encode(ctx, resp) - require.NoError(t, err) - require.Equal(t, `{"Response":{"Signature":{}}}`, string(data)) - - resp.Signature = fake.NewBadSignature() - _, err = format.Encode(ctx, resp) - require.EqualError(t, err, fake.Err("couldn't serialize signature")) -} - -func TestMsgFormat_Decode(t *testing.T) { - format := msgFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - ctx = serde.WithFactory(ctx, cosi.MsgKey{}, fake.MessageFactory{}) - ctx = serde.WithFactory(ctx, cosi.SigKey{}, fake.SignatureFactory{}) - - msg, err := format.Decode(ctx, []byte(`{"Request":{}}`)) - require.NoError(t, err) - require.Equal(t, cosi.SignatureRequest{Value: fake.Message{}}, msg) - - badCtx := serde.WithFactory(ctx, cosi.MsgKey{}, fake.NewBadMessageFactory()) - _, err = format.Decode(badCtx, []byte(`{"Request":{}}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize value")) - - badCtx = serde.WithFactory(ctx, cosi.MsgKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Request":{}}`)) - require.EqualError(t, err, "factory is nil") - - msg, err = format.Decode(ctx, []byte(`{"Response":{}}`)) - require.NoError(t, err) - require.Equal(t, cosi.SignatureResponse{Signature: fake.Signature{}}, msg) - - badCtx = serde.WithFactory(ctx, cosi.SigKey{}, fake.NewBadSignatureFactory()) - _, err = format.Decode(badCtx, []byte(`{"Response":{}}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize signature")) - - badCtx = serde.WithFactory(ctx, cosi.SigKey{}, nil) - _, err = format.Decode(badCtx, []byte(`{"Response":{}}`)) - require.EqualError(t, err, "invalid factory of type ''") - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't unmarshal message")) - - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, "message is empty") -} diff --git a/dela/cosi/messages.go b/dela/cosi/messages.go deleted file mode 100644 index 1826f0e..0000000 --- a/dela/cosi/messages.go +++ /dev/null @@ -1,98 +0,0 @@ -// -// Documentation Last Review: 05.10.2020 -// - -package cosi - -import ( - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var msgFormats = registry.NewSimpleRegistry() - -// RegisterMessageFormat registers the format for the given format name. -func RegisterMessageFormat(name serde.Format, f serde.FormatEngine) { - msgFormats.Register(name, f) -} - -// SignatureRequest is the message sent to require a signature from the other -// participants. -// -// - implements serde.Message -type SignatureRequest struct { - Value serde.Message -} - -// Serialize implements serde.Message. It looks up the format and returns the -// serialized data if appropriate, otherwise an error. -func (req SignatureRequest) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, req) - if err != nil { - return nil, xerrors.Errorf("couldn't encode request: %v", err) - } - - return data, nil -} - -// SignatureResponse is the message sent by the participants. -// -// - implements serde.Message -type SignatureResponse struct { - Signature crypto.Signature -} - -// Serialize implements serde.Message. It looks up the format and returns the -// serialized data if appropriate, otherwise an error. -func (resp SignatureResponse) Serialize(ctx serde.Context) ([]byte, error) { - format := msgFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, resp) - if err != nil { - return nil, xerrors.Errorf("couldn't encode response: %v", err) - } - - return data, nil -} - -// MsgKey is the key of the message factory. -type MsgKey struct{} - -// SigKey is the key of the signature factory. -type SigKey struct{} - -// MessageFactory is the message factory for the flat collective signing RPC. -// -// - implements serde.Factory -type MessageFactory struct { - msgFactory serde.Factory - sigFactory crypto.SignatureFactory -} - -// NewMessageFactory returns a new message factory that uses the message and -// signature factories. -func NewMessageFactory(msg serde.Factory, sig crypto.SignatureFactory) MessageFactory { - return MessageFactory{ - msgFactory: msg, - sigFactory: sig, - } -} - -// Deserialize implements serde.Factory. -func (f MessageFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := msgFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, MsgKey{}, f.msgFactory) - ctx = serde.WithFactory(ctx, SigKey{}, f.sigFactory) - - m, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode message: %v", err) - } - - return m, nil -} diff --git a/dela/cosi/messages_test.go b/dela/cosi/messages_test.go deleted file mode 100644 index f523f00..0000000 --- a/dela/cosi/messages_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package cosi - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -var testCalls = &fake.Call{} - -func init() { - RegisterMessageFormat(fake.GoodFormat, fake.Format{Msg: SignatureRequest{}, Call: testCalls}) - RegisterMessageFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestSignatureRequest(t *testing.T) { - req := SignatureRequest{} - - data, err := req.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = req.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode request")) -} - -func TestSignatureResponse(t *testing.T) { - resp := SignatureResponse{} - - data, err := resp.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = resp.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode response")) -} - -func TestMessageFactory_Deserialize(t *testing.T) { - factory := NewMessageFactory(fake.MessageFactory{}, fake.SignatureFactory{}) - - testCalls.Clear() - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, SignatureRequest{}, msg) - - require.Equal(t, 1, testCalls.Len()) - ctx := testCalls.Get(0, 0).(serde.Context) - require.Equal(t, fake.MessageFactory{}, ctx.GetFactory(MsgKey{})) - require.Equal(t, fake.SignatureFactory{}, ctx.GetFactory(SigKey{})) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode message")) -} diff --git a/dela/cosi/threshold/actor.go b/dela/cosi/threshold/actor.go deleted file mode 100644 index 7d044b0..0000000 --- a/dela/cosi/threshold/actor.go +++ /dev/null @@ -1,137 +0,0 @@ -// -// This file contains the implementation of the collective signing actor that -// provides a primitive to send a signature request to participants. -// -// Document Last Review: 05.10.2020 -// - -package threshold - -import ( - "context" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/cosi/threshold/types" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// thresholdActor is an implementation of a collective signing actor. -// -// - implements cosi.Actor -type thresholdActor struct { - *Threshold - - me mino.Address - rpc mino.RPC - reactor cosi.Reactor -} - -// Sign implements cosi.Actor. It returns the collective signature from the -// collective authority, or an error if it failed. The signature may be composed -// of only a subset of the participants, depending on the threshold. The -// function will return as soon as a valid signature is available. -// The context must be cancel at some point, and it will interrupt the protocol -// if it is not done yet. -func (a thresholdActor) Sign(ctx context.Context, msg serde.Message, - ca crypto.CollectiveAuthority) (crypto.Signature, error) { - - ctx = context.WithValue(ctx, tracing.ProtocolKey, protocolName) - - sender, rcvr, err := a.rpc.Stream(ctx, ca) - if err != nil { - return nil, xerrors.Errorf("couldn't open stream: %v", err) - } - - digest, err := a.reactor.Invoke(a.me, msg) - if err != nil { - return nil, xerrors.Errorf("couldn't react to message: %v", err) - } - - // The aggregated signature needs to include at least a threshold number of - // signatures. - thres := a.thresholdFn.Load().(cosi.Threshold)(ca.Len()) - - req := cosi.SignatureRequest{ - Value: msg, - } - - errs := sender.Send(req, iter2slice(ca)...) - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - go a.waitResp(errs, ca.Len()-thres, cancel) - - count := 0 - signature := new(types.Signature) - for count < thres { - addr, resp, err := rcvr.Recv(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't receive more messages: %v", err) - } - - pubkey, index := ca.GetPublicKey(addr) - if index >= 0 { - err = a.merge(signature, resp, index, pubkey, digest) - if err != nil { - a.logger.Warn().Err(err).Msg("failed to process signature response") - } else { - count++ - } - } - } - - // Each signature is individually verified so we can assume the aggregated - // signature is correct. - return signature, nil -} - -func (a thresholdActor) waitResp(errs <-chan error, maxErrs int, cancel func()) { - errCount := 0 - for err := range errs { - a.logger.Warn().Err(err).Msg("signature request to a peer failed") - errCount++ - - if errCount > maxErrs { - dela.Logger.Warn().Msg("aborting collective signing due to too many errors") - cancel() - return - } - } -} - -func (a thresholdActor) merge(signature *types.Signature, m serde.Message, - index int, pubkey crypto.PublicKey, digest []byte) error { - - resp, ok := m.(cosi.SignatureResponse) - if !ok { - return xerrors.Errorf("invalid message type '%T'", m) - } - - err := pubkey.Verify(digest, resp.Signature) - if err != nil { - return xerrors.Errorf("couldn't verify: %v", err) - } - - err = signature.Merge(a.signer, index, resp.Signature) - if err != nil { - return xerrors.Errorf("couldn't merge signature: %v", err) - } - - return nil -} - -func iter2slice(players mino.Players) []mino.Address { - addrs := make([]mino.Address, 0, players.Len()) - iter := players.AddressIterator() - for iter.HasNext() { - addrs = append(addrs, iter.GetNext()) - } - - return addrs -} diff --git a/dela/cosi/threshold/actor_test.go b/dela/cosi/threshold/actor_test.go deleted file mode 100644 index d287d66..0000000 --- a/dela/cosi/threshold/actor_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package threshold - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestActor_Sign(t *testing.T) { - roster := fake.NewAuthority(3, fake.NewSigner) - - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), cosi.SignatureResponse{Signature: fake.Signature{}}), - fake.NewRecvMsg(fake.NewAddress(0), cosi.SignatureResponse{Signature: fake.Signature{}}), - fake.NewRecvMsg(fake.NewAddress(1), cosi.SignatureResponse{Signature: fake.Signature{}}), - ) - rpc := fake.NewStreamRPC(recv, fake.Sender{}) - - actor := thresholdActor{ - Threshold: &Threshold{ - signer: roster.GetSigner(0).(crypto.AggregateSigner), - }, - rpc: rpc, - reactor: fakeReactor{}, - } - - actor.SetThreshold(OneThreshold) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sig, err := actor.Sign(ctx, fake.Message{}, roster) - require.NoError(t, err) - require.NotNil(t, sig) -} - -func TestActor_BadNetwork_Sign(t *testing.T) { - actor := thresholdActor{ - Threshold: &Threshold{}, - rpc: fake.NewBadRPC(), - reactor: fakeReactor{err: fake.GetError()}, - } - - roster := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := actor.Sign(ctx, fake.Message{}, roster) - require.EqualError(t, err, fake.Err("couldn't open stream")) -} - -func TestActor_BadReactor_Sign(t *testing.T) { - actor := thresholdActor{ - Threshold: &Threshold{}, - rpc: fake.NewRPC(), - reactor: fakeReactor{err: fake.GetError()}, - } - - roster := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := actor.Sign(ctx, fake.Message{}, roster) - require.EqualError(t, err, fake.Err("couldn't react to message")) -} - -func TestActor_MalformedResponse_Sign(t *testing.T) { - logger, check := fake.CheckLog("failed to process signature response") - - recv := fake.NewReceiver(fake.NewRecvMsg(fake.NewAddress(0), fake.Message{})) - rpc := fake.NewStreamRPC(recv, fake.Sender{}) - rpc.Done() - - actor := thresholdActor{ - Threshold: &Threshold{ - logger: logger, - }, - rpc: rpc, - reactor: fakeReactor{}, - } - actor.thresholdFn.Store(cosi.Threshold(defaultThreshold)) - - roster := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := actor.Sign(ctx, fake.Message{}, roster) - require.EqualError(t, err, "couldn't receive more messages: EOF") - check(t) -} - -func TestActor_CanceledContext_Sign(t *testing.T) { - actor := thresholdActor{ - Threshold: &Threshold{}, - rpc: fake.NewStreamRPC(fake.NewBlockingReceiver(), fake.Sender{}), - reactor: fakeReactor{}, - } - actor.thresholdFn.Store(cosi.Threshold(defaultThreshold)) - - roster := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - _, err := actor.Sign(ctx, fake.Message{}, roster) - require.EqualError(t, err, "couldn't receive more messages: context canceled") -} - -func TestActor_TooManyErrors_Sign(t *testing.T) { - rpc := fake.NewStreamRPC(fake.NewBlockingReceiver(), fake.NewBadSender()) - - logger, check := fake.CheckLog("signature request to a peer failed") - - actor := thresholdActor{ - Threshold: &Threshold{ - logger: logger, - }, - rpc: rpc, - reactor: fakeReactor{}, - } - actor.thresholdFn.Store(cosi.Threshold(defaultThreshold)) - - roster := fake.NewAuthority(3, fake.NewSigner) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := actor.Sign(ctx, fake.Message{}, roster) - require.EqualError(t, err, "couldn't receive more messages: context canceled") - check(t) -} - -func TestActor_InvalidSignature_Sign(t *testing.T) { - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), cosi.SignatureResponse{Signature: fake.Signature{}}), - ) - rpc := fake.NewStreamRPC(recv, fake.Sender{}) - - logger, check := fake.CheckLog("failed to process signature response") - - actor := thresholdActor{ - Threshold: &Threshold{ - logger: logger, - }, - rpc: rpc, - reactor: fakeReactor{}, - } - actor.thresholdFn.Store(cosi.Threshold(defaultThreshold)) - - roster := fake.NewAuthority(3, func() crypto.Signer { - return fake.NewSignerWithPublicKey(fake.NewBadPublicKey()) - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := actor.Sign(ctx, fake.Message{}, roster) - require.EqualError(t, err, "couldn't receive more messages: EOF") - check(t) -} diff --git a/dela/cosi/threshold/handler.go b/dela/cosi/threshold/handler.go deleted file mode 100644 index ec6966b..0000000 --- a/dela/cosi/threshold/handler.go +++ /dev/null @@ -1,82 +0,0 @@ -// This file contains the implementation of the RPC handler. -// -// Documentation Last Review: 05.10.2020 -// - -package threshold - -import ( - "context" - "io" - - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// thresholdHandler is an implementation of mino.Handler for a threshold -// collective signing. -type thresholdHandler struct { - *Threshold - mino.UnsupportedHandler - - reactor cosi.Reactor -} - -func newHandler(c *Threshold, hasher cosi.Reactor) thresholdHandler { - return thresholdHandler{ - Threshold: c, - reactor: hasher, - } -} - -// Stream implements mino.RPC. It listens for incoming messages and tries to -// send back the signature. If the message is malformed, it is ignored. -func (h thresholdHandler) Stream(out mino.Sender, in mino.Receiver) error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - for { - addr, msg, err := in.Recv(ctx) - if err == io.EOF { - return nil - } - if err != nil { - return xerrors.Errorf("failed to receive: %v", err) - } - - err = h.processRequest(out, msg, addr) - if err != nil { - h.logger.Warn().Err(err).Send() - } - } -} - -func (h thresholdHandler) processRequest(sender mino.Sender, msg serde.Message, addr mino.Address) error { - req, ok := msg.(cosi.SignatureRequest) - if !ok { - return xerrors.Errorf("invalid request type '%T'", msg) - } - - buffer, err := h.reactor.Invoke(addr, req.Value) - if err != nil { - return xerrors.Errorf("couldn't hash message: %v", err) - } - - signature, err := h.signer.Sign(buffer) - if err != nil { - return xerrors.Errorf("couldn't sign: %v", err) - } - - resp := cosi.SignatureResponse{ - Signature: signature, - } - - err = <-sender.Send(resp, addr) - if err != nil { - return xerrors.Errorf("couldn't send the response: %v", err) - } - - return nil -} diff --git a/dela/cosi/threshold/handler_test.go b/dela/cosi/threshold/handler_test.go deleted file mode 100644 index 277f472..0000000 --- a/dela/cosi/threshold/handler_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package threshold - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestThresholdHandler_Stream(t *testing.T) { - handler := newHandler( - &Threshold{signer: fake.NewAggregateSigner()}, - fakeReactor{}, - ) - - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), cosi.SignatureRequest{Value: fake.Message{}}), - ) - sender := fake.NewBadSender() - - err := handler.Stream(sender, recv) - require.NoError(t, err) -} - -func TestThresholdHandler_BadStream_Stream(t *testing.T) { - handler := newHandler( - &Threshold{}, - fakeReactor{}, - ) - - recv := fake.NewBadReceiver() - - err := handler.Stream(fake.Sender{}, recv) - require.EqualError(t, err, fake.Err("failed to receive")) -} - -func TestThresholdHandler_UnsupportedMessage_Stream(t *testing.T) { - logger, check := fake.CheckLog("invalid request type 'fake.Message'") - - handler := newHandler( - &Threshold{ - logger: logger, - }, - fakeReactor{}, - ) - - recv := fake.NewReceiver(fake.NewRecvMsg(fake.NewAddress(0), fake.Message{})) - - err := handler.Stream(fake.Sender{}, recv) - require.NoError(t, err) - check(t) -} - -func TestThresholdHandler_BadReactor_Stream(t *testing.T) { - logger, check := fake.CheckLog(fake.Err("couldn't hash message")) - - handler := newHandler( - &Threshold{ - logger: logger, - }, - fakeReactor{err: fake.GetError()}, - ) - - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), cosi.SignatureRequest{Value: fake.Message{}}), - ) - - err := handler.Stream(fake.Sender{}, recv) - require.NoError(t, err) - check(t) -} - -func TestThresholdHandler_BadSigner_Stream(t *testing.T) { - logger, check := fake.CheckLog(fake.Err("couldn't sign")) - - handler := newHandler( - &Threshold{ - logger: logger, - }, - fakeReactor{}, - ) - - handler.signer = fake.NewBadSigner() - recv := fake.NewReceiver( - fake.NewRecvMsg(fake.NewAddress(0), cosi.SignatureRequest{Value: fake.Message{}}), - ) - - err := handler.Stream(fake.Sender{}, recv) - require.NoError(t, err) - check(t) -} diff --git a/dela/cosi/threshold/json/json.go b/dela/cosi/threshold/json/json.go deleted file mode 100644 index 6bea157..0000000 --- a/dela/cosi/threshold/json/json.go +++ /dev/null @@ -1,78 +0,0 @@ -package json - -import ( - "encoding/json" - - "go.dedis.ch/dela/cosi/threshold/types" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - types.RegisterSignatureFormat(serde.FormatJSON, sigFormat{}) -} - -// Signature is the JSON message for the signature. -type Signature struct { - Mask []byte - Aggregate json.RawMessage -} - -// SigFormat is the engine to encode and decode collective signature messages in -// JSON format. -// -// - implements serde.FormatEngine -type sigFormat struct{} - -// Encode implements serde.FormatEngine. It returns the serialized data of the -// signature message if appropriate, otherwise an error. -func (f sigFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - sig, ok := msg.(*types.Signature) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - agg, err := sig.GetAggregate().Serialize(ctx) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize aggregate: %v", err) - } - - m := Signature{ - Mask: sig.GetMask(), - Aggregate: json.RawMessage(agg), - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the signature of the JSON -// data if appropriate, otherwise it returns an error. -func (f sigFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := Signature{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't unmarshal message: %v", err) - } - - factory := ctx.GetFactory(types.AggKey{}) - - fac, ok := factory.(crypto.SignatureFactory) - if !ok { - return nil, xerrors.Errorf("invalid factory of type '%T'", factory) - } - - agg, err := fac.SignatureOf(ctx, m.Aggregate) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize signature: %v", err) - } - - s := types.NewSignature(agg, m.Mask) - - return s, nil -} diff --git a/dela/cosi/threshold/json/json_test.go b/dela/cosi/threshold/json/json_test.go deleted file mode 100644 index ea30108..0000000 --- a/dela/cosi/threshold/json/json_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi/threshold/types" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestFormat_Encode(t *testing.T) { - format := sigFormat{} - sig := types.NewSignature(fake.Signature{}, []byte{0xab}) - - ctx := fake.NewContext() - - data, err := format.Encode(ctx, sig) - require.NoError(t, err) - require.Equal(t, `{"Mask":"qw==","Aggregate":{}}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), sig) - require.EqualError(t, err, fake.Err("couldn't marshal")) - - sig = types.NewSignature(fake.NewBadSignature(), nil) - _, err = format.Encode(ctx, sig) - require.EqualError(t, err, fake.Err("couldn't serialize aggregate")) -} - -func TestFormat_Decode(t *testing.T) { - format := sigFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.AggKey{}, fake.SignatureFactory{}) - - sig, err := format.Decode(ctx, []byte(`{"Mask":[1],"Aggregate":{}}`)) - require.NoError(t, err) - require.Equal(t, []byte{1}, sig.(*types.Signature).GetMask()) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't unmarshal message")) - - ctx = serde.WithFactory(ctx, types.AggKey{}, fake.NewBadSignatureFactory()) - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize signature")) - - ctx = serde.WithFactory(ctx, types.AggKey{}, nil) - _, err = format.Decode(ctx, []byte(`{}`)) - require.EqualError(t, err, "invalid factory of type ''") -} diff --git a/dela/cosi/threshold/threshold.go b/dela/cosi/threshold/threshold.go deleted file mode 100644 index c24bd1c..0000000 --- a/dela/cosi/threshold/threshold.go +++ /dev/null @@ -1,124 +0,0 @@ -// Package threshold is a stream-based implementation of a collective signing so -// that the orchestrator contacts only a subset of the participants. The -// collective signature allows a given threshold to be valid, which means that -// not all the participants need to return their signature for the protocol to -// end. -// -// Documentation Last Review: 05.10.2020 -package threshold - -import ( - "sync/atomic" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/cosi/threshold/types" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" -) - -var ( - // protocolName denotes the value of the protocol span tag associated with - // the `sign` protocol. - protocolName = "sign" -) - -func defaultThreshold(n int) int { - return n -} - -// OneThreshold is a threshold function to allow one failure. -func OneThreshold(n int) int { - if n <= 0 { - return 0 - } - - return n - 1 -} - -// ByzantineThreshold returns the minimum number of honest nodes required given -// `n` total nodes in a Byzantine Fault Tolerant system. -func ByzantineThreshold(n int) int { - if n <= 0 { - return 0 - } - - f := (n - 1) / 3 - - return n - f -} - -// Threshold is an implementation of the cosi.CollectiveSigning interface that -// is using streams to parallelize the work. -type Threshold struct { - logger zerolog.Logger - mino mino.Mino - signer crypto.AggregateSigner - // Stores the cosi.Threshold function. It will always contain a valid - // function by construction. - thresholdFn atomic.Value -} - -// NewThreshold returns a new instance of a threshold collective signature. -func NewThreshold(m mino.Mino, signer crypto.AggregateSigner) *Threshold { - c := &Threshold{ - logger: dela.Logger.With().Str("addr", m.GetAddress().String()).Logger(), - mino: m, - signer: signer, - } - - // Force the cosi.Threshold type to allow later updates of the same type. - c.thresholdFn.Store(cosi.Threshold(defaultThreshold)) - - return c -} - -// GetSigner implements cosi.CollectiveSigning. It returns the signer of the -// instance. -func (c *Threshold) GetSigner() crypto.Signer { - return c.signer -} - -// GetPublicKeyFactory implements cosi.CollectiveSigning. It returns the public -// key factory. -func (c *Threshold) GetPublicKeyFactory() crypto.PublicKeyFactory { - return c.signer.GetPublicKeyFactory() -} - -// GetSignatureFactory implements cosi.CollectiveSigning. It returns the -// signature factory. -func (c *Threshold) GetSignatureFactory() crypto.SignatureFactory { - return types.NewSignatureFactory(c.signer.GetSignatureFactory()) -} - -// GetVerifierFactory implements cosi.CollectiveSigning. It returns the verifier -// factory. -func (c *Threshold) GetVerifierFactory() crypto.VerifierFactory { - return types.NewThresholdVerifierFactory(c.signer.GetVerifierFactory()) -} - -// SetThreshold implements cosi.CollectiveSigning. It sets a new threshold -// function. -func (c *Threshold) SetThreshold(fn cosi.Threshold) { - if fn == nil { - return - } - - c.thresholdFn.Store(fn) -} - -// Listen implements cosi.CollectiveSigning. It creates the rpc endpoint and -// returns the actor that can trigger a collective signature. -func (c *Threshold) Listen(r cosi.Reactor) (cosi.Actor, error) { - factory := cosi.NewMessageFactory(r, c.signer.GetSignatureFactory()) - - actor := thresholdActor{ - Threshold: c, - me: c.mino.GetAddress(), - rpc: mino.MustCreateRPC(c.mino, "cosi", newHandler(c, r), factory), - reactor: r, - } - - return actor, nil -} diff --git a/dela/cosi/threshold/threshold_test.go b/dela/cosi/threshold/threshold_test.go deleted file mode 100644 index ea7a195..0000000 --- a/dela/cosi/threshold/threshold_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package threshold - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cosi" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minoch" - "go.dedis.ch/dela/serde" -) - -func TestThreshold_Scenario_Basic(t *testing.T) { - manager := minoch.NewManager() - - m1 := minoch.MustCreate(manager, "A") - m2 := minoch.MustCreate(manager, "B") - - ca := fake.NewAuthorityFromMino(bls.Generate, m1, m2) - c1 := NewThreshold(m1, ca.GetSigner(0).(crypto.AggregateSigner)) - c1.SetThreshold(OneThreshold) - - actor, err := c1.Listen(fakeReactor{}) - require.NoError(t, err) - - c2 := NewThreshold(m2, ca.GetSigner(1).(crypto.AggregateSigner)) - _, err = c2.Listen(fakeReactor{err: fake.GetError()}) - require.NoError(t, err) - - ctx := context.Background() - sig, err := actor.Sign(ctx, fake.Message{}, ca) - require.NoError(t, err) - require.NotNil(t, sig) - - verifier, err := c1.GetVerifierFactory().FromAuthority(ca) - require.NoError(t, err) - require.NoError(t, verifier.Verify([]byte{0xff}, sig)) -} - -func TestDefaultThreshold(t *testing.T) { - require.Equal(t, 2, defaultThreshold(2)) - require.Equal(t, 5, defaultThreshold(5)) -} - -func TestOneThreshold(t *testing.T) { - require.Equal(t, 0, OneThreshold(-10)) - require.Equal(t, 0, OneThreshold(0)) - require.Equal(t, 5, OneThreshold(6)) -} - -func TestByzantineThreshold(t *testing.T) { - require.Equal(t, 0, ByzantineThreshold(-10)) - require.Equal(t, 0, ByzantineThreshold(0)) - require.Equal(t, 2, ByzantineThreshold(2)) - require.Equal(t, 3, ByzantineThreshold(4)) - require.Equal(t, 5, ByzantineThreshold(7)) -} - -func TestThreshold_GetSigner(t *testing.T) { - c := &Threshold{signer: fake.NewAggregateSigner()} - require.NotNil(t, c.GetSigner()) -} - -func TestThreshold_GetPublicKeyFactory(t *testing.T) { - c := &Threshold{signer: fake.NewAggregateSigner()} - require.NotNil(t, c.GetPublicKeyFactory()) -} - -func TestThreshold_GetSignatureFactory(t *testing.T) { - c := &Threshold{signer: fake.NewAggregateSigner()} - require.NotNil(t, c.GetSignatureFactory()) -} - -func TestThreshold_SetThreshold(t *testing.T) { - c := NewThreshold(fake.Mino{}, nil) - - c.SetThreshold(nil) - require.NotNil(t, c.thresholdFn.Load()) - - c.SetThreshold(ByzantineThreshold) - require.Equal(t, ByzantineThreshold(999), c.thresholdFn.Load().(cosi.Threshold)(999)) -} - -func TestThreshold_Listen(t *testing.T) { - c := &Threshold{ - mino: fake.Mino{}, - signer: fake.NewAggregateSigner(), - } - - actor, err := c.Listen(fakeReactor{}) - require.NoError(t, err) - require.NotNil(t, actor) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeReactor struct { - fake.MessageFactory - - err error -} - -func (h fakeReactor) Invoke(addr mino.Address, in serde.Message) ([]byte, error) { - return []byte{0xff}, h.err -} diff --git a/dela/cosi/threshold/types/types.go b/dela/cosi/threshold/types/types.go deleted file mode 100644 index 1dc214d..0000000 --- a/dela/cosi/threshold/types/types.go +++ /dev/null @@ -1,293 +0,0 @@ -// Package types implements the threshold collective signature and its verifier. -// -// It wraps a signature implementation in order to extract the correct -// aggregated public key. -// -// The messages have been implemented in this isolated package so that it does -// not create cycle imports when importing the serde formats. -// -// Documentation Last Review: 05.10.2020 -package types - -import ( - "bytes" - "fmt" - "math/big" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -const ( - wordlength = 8 - // shift is used to divide by 8. - shift = 3 - // mask is used to get the remainder of a division by 8. - mask = 0x7 -) - -var formats = registry.NewSimpleRegistry() - -// RegisterSignatureFormat saves the format to be used when -// serializing/deserializing signature messages for the given codec. -func RegisterSignatureFormat(c serde.Format, f serde.FormatEngine) { - formats.Register(c, f) -} - -// Signature is a threshold signature which includes an aggregated signature and -// the mask of signers from the associated collective authority. -// -// - implements crypto.Signature -type Signature struct { - agg crypto.Signature - mask []byte -} - -// NewSignature returns a new threshold signature. -func NewSignature(agg crypto.Signature, mask []byte) *Signature { - return &Signature{ - agg: agg, - mask: mask, - } -} - -// GetAggregate returns the aggregate of the signature which corresponds to the -// addition of the public keys enabled in the mask. -func (s *Signature) GetAggregate() crypto.Signature { - return s.agg -} - -// GetMask returns a bit mask of which public key is enabled. -func (s *Signature) GetMask() []byte { - return append([]byte{}, s.mask...) -} - -// HasBit returns true when the bit at the given index is set to 1. -func (s *Signature) HasBit(index int) bool { - if index < 0 { - return false - } - - i := index >> shift - if i >= len(s.mask) { - return false - } - - return s.mask[i]&(1<> shift - for i >= len(s.mask) { - s.mask = append(s.mask, 0) - } - - s.mask[i] |= 1 << uint(index&mask) -} - -// Serialize implements serde.Message. It serializes the signature into JSON -// format. -func (s *Signature) Serialize(ctx serde.Context) ([]byte, error) { - format := formats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, s) - if err != nil { - return nil, xerrors.Errorf("couldn't encode signature: %v", err) - } - - return data, nil -} - -// MarshalBinary implements encoding.BinaryMarshaler. -func (s *Signature) MarshalBinary() ([]byte, error) { - buffer, err := s.agg.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal signature: %v", err) - } - - buffer = append(buffer, s.mask...) - - return buffer, nil -} - -// Equal implements crypto.Signature. -func (s *Signature) Equal(o crypto.Signature) bool { - other, ok := o.(*Signature) - return ok && other.agg.Equal(s.agg) && bytes.Equal(s.mask, other.mask) -} - -// String implements fmt.Stringer. It returns a string representation of the -// signature. -func (s *Signature) String() string { - mask := new(big.Int).SetBytes(s.mask) - - return fmt.Sprintf("thres[%b]:%s", mask, s.agg) -} - -// AggKey is the key for the aggregate signature factory. -type AggKey struct{} - -// SignatureFactory is the factory to deserialize collective signature. -// -// - implements crypto.SignatureFactory -// - implements serde.Factory -type SignatureFactory struct { - aggFactory crypto.SignatureFactory -} - -// NewSignatureFactory returns a new signature factory. -func NewSignatureFactory(f crypto.SignatureFactory) SignatureFactory { - return SignatureFactory{ - aggFactory: f, - } -} - -// Deserialize implements serde.Factory. It populates the signature from the -// data if appropriate, otherwise it returns an error. -func (f SignatureFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.SignatureOf(ctx, data) -} - -// SignatureOf implements crypto.SignatureFactory. It populates the signature -// from the data if appropriate, otherwise it returns an error. -func (f SignatureFactory) SignatureOf(ctx serde.Context, data []byte) (crypto.Signature, error) { - format := formats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, AggKey{}, f.aggFactory) - - m, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode signature: %v", err) - } - - sig, ok := m.(*Signature) - if !ok { - return nil, xerrors.Errorf("invalid signature of type '%T'", m) - } - - return sig, nil -} - -// Verifier is a threshold verifier which can verify threshold signatures by -// aggregating public keys according to the mask. -// -// - implements crypto.Verifier -type Verifier struct { - pubkeys []crypto.PublicKey - factory crypto.VerifierFactory -} - -func newVerifier(ca crypto.CollectiveAuthority, f crypto.VerifierFactory) Verifier { - pubkeys := make([]crypto.PublicKey, 0, ca.Len()) - iter := ca.PublicKeyIterator() - for iter.HasNext() { - pubkeys = append(pubkeys, iter.GetNext()) - } - - return newVerifierArr(pubkeys, f) -} - -func newVerifierArr(pubkeys []crypto.PublicKey, f crypto.VerifierFactory) Verifier { - return Verifier{ - pubkeys: pubkeys, - factory: f, - } -} - -// Verify implements crypto.Verifier. It returns nil if the signature matches -// the aggregate public key for the mask associated to the signature. -func (v Verifier) Verify(msg []byte, s crypto.Signature) error { - signature, ok := s.(*Signature) - if !ok { - return xerrors.Errorf("invalid signature type '%T' != '%T'", s, signature) - } - - pubkeys := make([]crypto.PublicKey, 0, len(v.pubkeys)) - for _, index := range signature.GetIndices() { - pubkeys = append(pubkeys, v.pubkeys[index]) - } - - verifier, err := v.factory.FromArray(pubkeys) - if err != nil { - return xerrors.Errorf("couldn't make verifier: %v", err) - } - - err = verifier.Verify(msg, signature.agg) - if err != nil { - return xerrors.Errorf("invalid signature: %v", err) - } - - return nil -} - -// VerifierFactory is a factory to create a verifier from a list of -// participants. -type verifierFactory struct { - factory crypto.VerifierFactory -} - -// NewThresholdVerifierFactory creates a new verifier factory from the -// underlying verifier factory. -func NewThresholdVerifierFactory(fac crypto.VerifierFactory) crypto.VerifierFactory { - return verifierFactory{ - factory: fac, - } -} - -// FromAuthority implements crypto.VerifierFactory. It creates a verifier from -// the authority so that the mask's signature will pick the participants that -// have participated. The ordering of the authority must be the same. -func (f verifierFactory) FromAuthority(authority crypto.CollectiveAuthority) (crypto.Verifier, error) { - return newVerifier(authority, f.factory), nil -} - -// FromArray implements crypto.VerifierFactory. It creates a verifier from the -// list of public keys so that the mask's signature will pick the participants -// that have participated. The ordering of the keys must be the same. -func (f verifierFactory) FromArray(pubkeys []crypto.PublicKey) (crypto.Verifier, error) { - return newVerifierArr(pubkeys, f.factory), nil -} diff --git a/dela/cosi/threshold/types/types_test.go b/dela/cosi/threshold/types/types_test.go deleted file mode 100644 index 59a4f31..0000000 --- a/dela/cosi/threshold/types/types_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func init() { - RegisterSignatureFormat(fake.GoodFormat, fake.Format{Msg: &Signature{}}) - RegisterSignatureFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterSignatureFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestSignature_GetAggregate(t *testing.T) { - sig := NewSignature(fake.Signature{}, nil) - - require.Equal(t, fake.Signature{}, sig.GetAggregate()) -} - -func TestSignature_GetMask(t *testing.T) { - sig := NewSignature(nil, []byte{1}) - - require.Equal(t, []byte{1}, sig.GetMask()) -} - -func TestSignature_HasBit(t *testing.T) { - sig := &Signature{mask: []byte{0b00000010, 0b10000001}} - - require.True(t, sig.HasBit(1)) - require.True(t, sig.HasBit(8)) - require.True(t, sig.HasBit(15)) - require.False(t, sig.HasBit(16)) - require.False(t, sig.HasBit(-1)) -} - -func TestSignature_GetIndices(t *testing.T) { - sig := &Signature{mask: []byte{0x0c, 0x01}} - - require.Equal(t, []int{2, 3, 8}, sig.GetIndices()) -} - -func TestSignature_Merge(t *testing.T) { - sig := &Signature{} - - err := sig.Merge(fake.NewAggregateSigner(), 2, fake.Signature{}) - require.NoError(t, err) - require.NotNil(t, sig.agg) - - err = sig.Merge(fake.NewAggregateSigner(), 1, fake.Signature{}) - require.NoError(t, err) - - err = sig.Merge(fake.NewAggregateSigner(), 1, fake.Signature{}) - require.EqualError(t, err, "index 1 already merged") - - err = sig.Merge(fake.NewBadSigner(), 0, fake.Signature{}) - require.EqualError(t, err, fake.Err("couldn't aggregate")) - require.Equal(t, []byte{0b00000110}, sig.mask) -} - -func TestSignature_SetBit(t *testing.T) { - sig := &Signature{} - - sig.setBit(-1) - require.Nil(t, sig.mask) - - sig.setBit(8) - require.Len(t, sig.mask, 2) - require.Equal(t, sig.mask[1], uint8(1)) - - sig.setBit(9) - require.Len(t, sig.mask, 2) - require.Equal(t, sig.mask[1], uint8(3)) -} - -func TestSignature_Serialize(t *testing.T) { - sig := Signature{} - - data, err := sig.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = sig.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode signature")) -} - -func TestSignature_MarshalBinary(t *testing.T) { - sig := &Signature{ - agg: fake.Signature{}, - mask: []byte{0xff}, - } - - buffer, err := sig.MarshalBinary() - require.NoError(t, err) - require.Equal(t, []byte{fake.SignatureByte, 0xff}, buffer) - - sig.agg = fake.NewBadSignature() - _, err = sig.MarshalBinary() - require.EqualError(t, err, fake.Err("couldn't marshal signature")) -} - -func TestSignature_Equal(t *testing.T) { - sig := &Signature{ - agg: fake.Signature{}, - mask: []byte{0xff}, - } - - require.True(t, sig.Equal(sig)) - require.False(t, sig.Equal(nil)) -} - -func TestSignature_String(t *testing.T) { - sig := NewSignature(fake.Signature{}, []byte{0xa}) - - require.Equal(t, "thres[1010]:fakeSignature", sig.String()) -} - -func TestSignatureFactory_Deserialize(t *testing.T) { - factory := NewSignatureFactory(fake.SignatureFactory{}) - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, &Signature{}, msg) -} - -func TestSignatureFactory_SignatureOf(t *testing.T) { - factory := NewSignatureFactory(fake.SignatureFactory{}) - - sig, err := factory.SignatureOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, &Signature{}, sig) - - _, err = factory.SignatureOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode signature")) - - _, err = factory.SignatureOf(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid signature of type 'fake.Message'") -} - -func TestVerifier_Verify(t *testing.T) { - call := &fake.Call{} - - verifier := newVerifier( - fake.NewAuthority(3, fake.NewSigner), - fake.NewVerifierFactoryWithCalls(call)) - - err := verifier.Verify([]byte{0xff}, &Signature{mask: []byte{0x3}}) - require.NoError(t, err) - require.Equal(t, 1, call.Len()) - require.Len(t, call.Get(0, 0), 2) - - err = verifier.Verify([]byte{}, nil) - require.EqualError(t, err, "invalid signature type '' != '*types.Signature'") - - verifier.factory = fake.NewBadVerifierFactory() - err = verifier.Verify([]byte{}, &Signature{}) - require.EqualError(t, err, fake.Err("couldn't make verifier")) - - verifier.factory = fake.NewVerifierFactory(fake.NewBadVerifier()) - err = verifier.Verify([]byte{}, &Signature{}) - require.EqualError(t, err, fake.Err("invalid signature")) -} - -func TestVerifierFactory_FromArray(t *testing.T) { - fac := NewThresholdVerifierFactory(fake.NewVerifierFactory(fake.Verifier{})) - - verifier, err := fac.FromArray([]crypto.PublicKey{fake.PublicKey{}}) - require.NoError(t, err) - require.Len(t, verifier.(Verifier).pubkeys, 1) - - verifier, err = fac.FromAuthority(fake.NewAuthority(3, fake.NewSigner)) - require.NoError(t, err) - require.Len(t, verifier.(Verifier).pubkeys, 3) -} diff --git a/dela/crypto/bls/bls.go b/dela/crypto/bls/bls.go deleted file mode 100644 index db1c488..0000000 --- a/dela/crypto/bls/bls.go +++ /dev/null @@ -1,464 +0,0 @@ -// Package bls implements the cryptographic primitives using the BLS signature -// scheme and the BN256 elliptic curve. -// -// Related Papers: -// -// https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html -// -// Documentation Last Review: 05.10.2020 -package bls - -import ( - "bytes" - "fmt" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "go.dedis.ch/kyber/v3" - "go.dedis.ch/kyber/v3/pairing" - - //lint:ignore SA1019 we need to fix this, issues opened in #166 - "go.dedis.ch/kyber/v3/sign/bls" - "go.dedis.ch/kyber/v3/util/key" - "golang.org/x/xerrors" -) - -const ( - // Algorithm is the name of the curve used for the BLS signature. - Algorithm = "BLS-CURVE-BN256" -) - -var ( - suite = pairing.NewSuiteBn256() - - pubkeyFormats = registry.NewSimpleRegistry() - sigFormats = registry.NewSimpleRegistry() -) - -// RegisterPublicKeyFormat registers the engine for the provided format. -func RegisterPublicKeyFormat(c serde.Format, f serde.FormatEngine) { - pubkeyFormats.Register(c, f) -} - -// RegisterSignatureFormat registers the engine for the provided format. -func RegisterSignatureFormat(c serde.Format, f serde.FormatEngine) { - sigFormats.Register(c, f) -} - -// PublicKey is the adapter a of BN256 public key. -// -// - implements crypto.PublicKey -type PublicKey struct { - point kyber.Point -} - -// NewPublicKey creates a new public key by unmarshaling the data into BN256 -// point. -func NewPublicKey(data []byte) (PublicKey, error) { - point := suite.Point() - err := point.UnmarshalBinary(data) - if err != nil { - return PublicKey{}, err - } - - return PublicKey{point: point}, nil -} - -// NewPublicKeyFromPoint creates a new public key from an existing point. -func NewPublicKeyFromPoint(point kyber.Point) PublicKey { - return PublicKey{ - point: point, - } -} - -// MarshalBinary implements encoding.BinaryMarshaler. It produces a slice of -// bytes representing the public key. -func (pk PublicKey) MarshalBinary() ([]byte, error) { - return pk.point.MarshalBinary() -} - -// Serialize implements serde.Message. It returns the serialized data of the -// public key. -func (pk PublicKey) Serialize(ctx serde.Context) ([]byte, error) { - format := pubkeyFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, pk) - if err != nil { - return nil, xerrors.Errorf("couldn't encode public key: %v", err) - } - - return data, nil -} - -// Verify implements crypto.PublicKey. It returns nil if the signature matches -// the message for this public key. -func (pk PublicKey) Verify(msg []byte, sig crypto.Signature) error { - signature, ok := sig.(Signature) - if !ok { - return xerrors.Errorf("invalid signature type '%T'", sig) - } - - err := bls.Verify(suite, pk.point, msg, signature.data) - if err != nil { - return xerrors.Errorf("bls verify failed: %v", err) - } - - return nil -} - -// Equal implements crypto.PublicKey. It returns true if the other public key -// is the same. -func (pk PublicKey) Equal(other interface{}) bool { - pubkey, ok := other.(PublicKey) - if !ok { - return false - } - - return pubkey.point.Equal(pk.point) -} - -// MarshalText implements encoding.TextMarshaler. It returns a text -// representation of the public key. -func (pk PublicKey) MarshalText() ([]byte, error) { - buffer, err := pk.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return []byte(fmt.Sprintf("bls:%x", buffer)), nil -} - -// GetPoint returns the kyber.Point. -func (pk PublicKey) GetPoint() kyber.Point { - return pk.point -} - -// String implements fmt.String. It returns a string representation of the -// point. -func (pk PublicKey) String() string { - buffer, err := pk.MarshalText() - if err != nil { - return "bls:malformed_point" - } - - // Output only the prefix and 16 characters of the buffer in hexadecimal. - return string(buffer)[:4+16] -} - -// Signature is the adapter of a BN256 signature. -// -// - implements crypto.Signature -type Signature struct { - data []byte -} - -// NewSignature creates a new signature from the provided data. -func NewSignature(data []byte) Signature { - return Signature{ - data: data, - } -} - -// MarshalBinary implements encoding.BinaryMarshaler. It returns a slice of -// bytes representing the signature. -func (sig Signature) MarshalBinary() ([]byte, error) { - return sig.data, nil -} - -// Serialize implements serde.Message. It returns the serialized data of the -// signature. -func (sig Signature) Serialize(ctx serde.Context) ([]byte, error) { - format := sigFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, sig) - if err != nil { - return nil, xerrors.Errorf("couldn't encode signature: %v", err) - } - - return data, nil -} - -// Equal implements crypto.Signature. It returns true if both signatures are the -// same. -func (sig Signature) Equal(other crypto.Signature) bool { - otherSig, ok := other.(Signature) - if !ok { - return false - } - - return bytes.Equal(sig.data, otherSig.data) -} - -// String implements fmt.Stringer. It returns a string representation of the -// signature. -func (sig Signature) String() string { - return fmt.Sprintf("bls:%x", sig.data) -} - -// publicKeyFactory is a factory to deserialize public keys of the BN256 -// elliptic curve. -// -// - implements crypto.PublicKeyFactory -type publicKeyFactory struct{} - -// NewPublicKeyFactory returns a new instance of the factory. -func NewPublicKeyFactory() crypto.PublicKeyFactory { - return publicKeyFactory{} -} - -// Deserialize implements serde.Factory. It returns the public key of the data -// if appropriate, otherwise an error. -func (f publicKeyFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := pubkeyFormats.Get(ctx.GetFormat()) - - m, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode public key: %v", err) - } - - return m, nil -} - -// PublicKeyOf implements crypto.PublicKeyFactory. It returns the public key of -// the data if appropriate, otherwise an error. -func (f publicKeyFactory) PublicKeyOf(ctx serde.Context, data []byte) (crypto.PublicKey, error) { - m, err := f.Deserialize(ctx, data) - if err != nil { - return nil, err - } - - pubkey, ok := m.(crypto.PublicKey) - if !ok { - return nil, xerrors.Errorf("invalid public key of type '%T'", m) - } - - return pubkey, nil -} - -// FromBytes implements crypto.PublicKeyFactory. It returns the public key -// unmarshaled from the bytes. -func (f publicKeyFactory) FromBytes(data []byte) (crypto.PublicKey, error) { - pubkey, err := NewPublicKey(data) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal key: %v", err) - } - - return pubkey, nil -} - -// signatureFactory is a factory to deserialize signatures of the BN256 elliptic -// curve. -// -// - implements crypto.SignatureFactory -type signatureFactory struct{} - -// NewSignatureFactory returns a new instance of the factory. -func NewSignatureFactory() crypto.SignatureFactory { - return signatureFactory{} -} - -// Deserialize implements serde.Factory. It returns the signature of the data if -// appropriate, otherwise an error. -func (f signatureFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := sigFormats.Get(ctx.GetFormat()) - - m, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode signature: %v", err) - } - - return m, nil -} - -// SignatureOf implements crypto.SignatureFactory. It returns the signature of -// the data if appropriate, otherwise an error. -func (f signatureFactory) SignatureOf(ctx serde.Context, data []byte) (crypto.Signature, error) { - m, err := f.Deserialize(ctx, data) - if err != nil { - return nil, err - } - - sig, ok := m.(Signature) - if !ok { - return nil, xerrors.Errorf("invalid signature of type '%T'", m) - } - - return sig, nil -} - -// blsVerifier is a verifier for BLS signatures to match against a message and -// the public key associated to one or several identities. -// -// - implements crypto.Verifier -type blsVerifier struct { - points []kyber.Point -} - -// NewVerifier returns a new verifier that can verify BLS signatures. -func newVerifier(points []kyber.Point) crypto.Verifier { - return blsVerifier{points: points} -} - -// Verify implements crypto.Verifier. It returns nil if the signature matches -// the message, or an error otherwise. -func (v blsVerifier) Verify(msg []byte, sig crypto.Signature) error { - aggKey := bls.AggregatePublicKeys(suite, v.points...) - - err := bls.Verify(suite, aggKey, msg, sig.(Signature).data) - if err != nil { - return err - } - - return nil -} - -// verifierFactory is a factory to create verifiers from an authority or a list -// of public keys. -// -// - implements crypto.VerifierFactory -type verifierFactory struct{} - -// FromIterator implements crypto.VerifierFactory. It returns a verifier that -// will verify the signatures collectively signed by all the signers associated -// with the public keys. -func (v verifierFactory) FromAuthority(ca crypto.CollectiveAuthority) (crypto.Verifier, error) { - if ca == nil { - return nil, xerrors.New("authority is nil") - } - - points := make([]kyber.Point, 0, ca.Len()) - iter := ca.PublicKeyIterator() - for iter.HasNext() { - next := iter.GetNext() - pk, ok := next.(PublicKey) - if !ok { - return nil, xerrors.Errorf("invalid public key type: %T", next) - } - - points = append(points, pk.point) - } - - return newVerifier(points), nil -} - -// FromArray implements crypto.VerifierFactory. It returns a verifier that will -// verify the signatures collectively signed by all the signers associated with -// the public keys. -func (v verifierFactory) FromArray(publicKeys []crypto.PublicKey) (crypto.Verifier, error) { - points := make([]kyber.Point, len(publicKeys)) - for i, pubkey := range publicKeys { - pk, ok := pubkey.(PublicKey) - if !ok { - return nil, xerrors.Errorf("invalid public key type: %T", pubkey) - } - - points[i] = pk.point - } - - return newVerifier(points), nil -} - -// Signer is the adapter of a private key from the Kyber package for the BN256 -// elliptic curve. -// -// - implements crypto.AggregateSigner -// - implements encoding.BinaryMarshaler -type Signer struct { - public kyber.Point - private kyber.Scalar -} - -// NewSigner generates and returns a new random signer. -func NewSigner() Signer { - return Generate().(Signer) -} - -// NewSignerFromBytes restores a signer from a marshalling. -func NewSignerFromBytes(data []byte) (crypto.AggregateSigner, error) { - scalar := suite.Scalar() - err := scalar.UnmarshalBinary(data) - if err != nil { - return nil, xerrors.Errorf("while unmarshaling scalar: %v", err) - } - - pubkey := suite.Point().Mul(scalar, nil) - - signer := Signer{ - public: pubkey, - private: scalar, - } - - return signer, nil -} - -// Generate returns a new random BLS signer that supports aggregation. -func Generate() crypto.Signer { - kp := key.NewKeyPair(suite) - return Signer{ - private: kp.Private, - public: kp.Public, - } -} - -// GetVerifierFactory implements crypto.Signer. It returns the verifier factory -// for BLS signatures. -func (s Signer) GetVerifierFactory() crypto.VerifierFactory { - return verifierFactory{} -} - -// GetPublicKeyFactory implements crypto.Signer. It returns the public key -// factory for BLS signatures. -func (s Signer) GetPublicKeyFactory() crypto.PublicKeyFactory { - return publicKeyFactory{} -} - -// GetSignatureFactory implements crypto.Signer. It returns the signature -// factory for BLS signatures. -func (s Signer) GetSignatureFactory() crypto.SignatureFactory { - return signatureFactory{} -} - -// GetPublicKey implements crypto.Signer. It returns the public key of the -// signer that can be used to verify signatures. -func (s Signer) GetPublicKey() crypto.PublicKey { - return PublicKey{point: s.public} -} - -// Sign implements crypto.Signer. It signs the message in parameter and returns -// the signature, or an error if it cannot sign. -func (s Signer) Sign(msg []byte) (crypto.Signature, error) { - sig, err := bls.Sign(suite, s.private, msg) - if err != nil { - return nil, xerrors.Errorf("couldn't make bls signature: %v", err) - } - - return Signature{data: sig}, nil -} - -// Aggregate implements crypto.Signer. It aggregates the signatures into a -// single one that can be verifier with the aggregated public key associated. -func (s Signer) Aggregate(signatures ...crypto.Signature) (crypto.Signature, error) { - buffers := make([][]byte, len(signatures)) - for i, sig := range signatures { - buffers[i] = sig.(Signature).data - } - - agg, err := bls.AggregateSignatures(suite, buffers...) - if err != nil { - return nil, xerrors.Errorf("couldn't aggregate: %v", err) - } - - return Signature{data: agg}, nil -} - -// MarshalBinary implements encoding.BinaryMarshaler. It returns a binary -// representation of the signer. -func (s Signer) MarshalBinary() ([]byte, error) { - data, err := s.private.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("while marshaling scalar: %v", err) - } - - return data, nil -} diff --git a/dela/crypto/bls/bls_test.go b/dela/crypto/bls/bls_test.go deleted file mode 100644 index c3656a1..0000000 --- a/dela/crypto/bls/bls_test.go +++ /dev/null @@ -1,386 +0,0 @@ -package bls - -import ( - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" - "go.dedis.ch/kyber/v3" -) - -func init() { - RegisterPublicKeyFormat(fake.GoodFormat, fake.Format{Msg: PublicKey{}}) - RegisterPublicKeyFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterPublicKeyFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterSignatureFormat(fake.GoodFormat, fake.Format{Msg: Signature{}}) - RegisterSignatureFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterSignatureFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestPublicKey_New(t *testing.T) { - signer := Generate() - data, err := signer.GetPublicKey().MarshalBinary() - require.NoError(t, err) - - pk, err := NewPublicKey(data) - require.NoError(t, err) - require.True(t, signer.GetPublicKey().Equal(pk)) - - _, err = NewPublicKey(nil) - require.Error(t, err) -} - -func TestPublicKey_MarshalBinary(t *testing.T) { - signer := Generate() - - buffer, err := signer.GetPublicKey().MarshalBinary() - require.NoError(t, err) - require.NotEmpty(t, buffer) -} - -func TestPublicKey_Serialize(t *testing.T) { - pubkey := NewPublicKeyFromPoint(nil) - - data, err := pubkey.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = pubkey.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode public key")) -} - -func TestPublicKey_Verify(t *testing.T) { - msg := []byte("deadbeef") - signer := Generate() - sig, err := signer.Sign(msg) - require.NoError(t, err) - - err = signer.GetPublicKey().Verify(msg, sig) - require.NoError(t, err) - - err = signer.GetPublicKey().Verify([]byte{}, sig) - require.EqualError(t, err, "bls verify failed: bls: invalid signature") - - err = signer.GetPublicKey().Verify(msg, fake.Signature{}) - require.EqualError(t, err, "invalid signature type 'fake.Signature'") -} - -func TestPublicKey_Equal(t *testing.T) { - signerA := Generate() - signerB := Generate() - require.True(t, signerA.GetPublicKey().Equal(signerA.GetPublicKey())) - require.True(t, signerB.GetPublicKey().Equal(signerB.GetPublicKey())) - require.False(t, signerA.GetPublicKey().Equal(signerB.GetPublicKey())) - require.False(t, signerA.GetPublicKey().Equal(fake.PublicKey{})) -} - -func TestPublicKey_MarshalText(t *testing.T) { - signer := Generate() - text, err := signer.GetPublicKey().MarshalText() - require.NoError(t, err) - require.Regexp(t, "^bls:", string(text)) - - pk := PublicKey{point: badPoint{}} - _, err = pk.MarshalText() - require.EqualError(t, err, fake.Err("couldn't marshal")) -} - -func TestPublicKey_GetPoint(t *testing.T) { - point := suite.Point() - pk := PublicKey{point: point} - - require.True(t, point.Equal(pk.GetPoint())) -} - -func TestPublicKey_String(t *testing.T) { - signer := Generate() - str := signer.GetPublicKey().(PublicKey).String() - require.Contains(t, str, "bls:") - - pk := PublicKey{point: badPoint{}} - str = pk.String() - require.Equal(t, "bls:malformed_point", str) -} - -func TestPublicKeyFactory_Deserialize(t *testing.T) { - factory := NewPublicKeyFactory() - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, PublicKey{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode public key")) -} - -func TestPublicKeyFactory_PublicKeyOf(t *testing.T) { - factory := NewPublicKeyFactory() - - pk, err := factory.PublicKeyOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, PublicKey{}, pk) - - _, err = factory.PublicKeyOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode public key")) - - _, err = factory.PublicKeyOf(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid public key of type 'fake.Message'") -} - -func TestPublicKeyFactory_FromBytes(t *testing.T) { - factory := NewPublicKeyFactory() - - point := suite.Point() - data, err := point.MarshalBinary() - require.NoError(t, err) - - pk, err := factory.FromBytes(data) - require.NoError(t, err) - require.True(t, pk.(PublicKey).point.Equal(point)) - - _, err = factory.FromBytes(nil) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to unmarshal key: ") -} - -func TestSignature_MarshalBinary(t *testing.T) { - f := func(data []byte) bool { - sig := NewSignature(data) - buffer, err := sig.MarshalBinary() - require.NoError(t, err) - require.Equal(t, data, buffer) - - return true - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestSignature_Serialize(t *testing.T) { - sig := Signature{} - - data, err := sig.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = sig.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode signature")) -} - -func TestSignature_Equal(t *testing.T) { - f := func(data []byte) bool { - sig := Signature{data: data} - require.True(t, sig.Equal(Signature{data: data})) - - buffer := append(append([]byte{}, data...), 0xaa) - require.False(t, sig.Equal(Signature{data: buffer})) - - require.False(t, sig.Equal(fake.Signature{})) - - return true - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestSignature_String(t *testing.T) { - sig := Signature{data: []byte{1, 2, 3}} - require.Equal(t, "bls:010203", sig.String()) -} - -func TestSignatureFactory_Deserialize(t *testing.T) { - factory := NewSignatureFactory() - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, Signature{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode signature")) -} - -func TestSignatureFactory_SignatureOf(t *testing.T) { - factory := NewSignatureFactory() - - sig, err := factory.SignatureOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, Signature{}, sig) - - _, err = factory.SignatureOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode signature")) - - _, err = factory.SignatureOf(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid signature of type 'fake.Message'") -} - -func TestVerifier_Verify(t *testing.T) { - f := func(msg []byte) bool { - signer := Generate() - sig, err := signer.Sign(msg) - require.NoError(t, err) - - verifier := newVerifier( - []kyber.Point{signer.GetPublicKey().(PublicKey).point}, - ) - err = verifier.Verify(msg, sig) - require.NoError(t, err) - - err = verifier.Verify(append([]byte{1}, msg...), sig) - require.Error(t, err) - - return true - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestVerifierFactory_FromAuthority(t *testing.T) { - factory := verifierFactory{} - - verifier, err := factory.FromAuthority(fake.NewAuthority(2, Generate)) - require.NoError(t, err) - require.IsType(t, blsVerifier{}, verifier) - require.Len(t, verifier.(blsVerifier).points, 2) - require.NotNil(t, verifier.(blsVerifier).points[0]) - require.NotNil(t, verifier.(blsVerifier).points[1]) - - _, err = factory.FromAuthority(nil) - require.EqualError(t, err, "authority is nil") - - _, err = factory.FromAuthority(fake.NewAuthority(2, fake.NewSigner)) - require.EqualError(t, err, "invalid public key type: fake.PublicKey") -} - -func TestVerifierFactory_FromArray(t *testing.T) { - factory := verifierFactory{} - - verifier, err := factory.FromArray([]crypto.PublicKey{PublicKey{}}) - require.NoError(t, err) - require.Len(t, verifier.(blsVerifier).points, 1) - - verifier, err = factory.FromArray(nil) - require.NoError(t, err) - require.Empty(t, verifier.(blsVerifier).points) - - _, err = factory.FromArray([]crypto.PublicKey{fake.PublicKey{}}) - require.EqualError(t, err, "invalid public key type: fake.PublicKey") -} - -func TestSigner_GetVerifierFactory(t *testing.T) { - signer := NewSigner() - - factory := signer.GetVerifierFactory() - require.NotNil(t, factory) - require.IsType(t, verifierFactory{}, factory) -} - -func TestSigner_GetPublicKeyFactory(t *testing.T) { - signer := Generate() - - factory := signer.GetPublicKeyFactory() - require.NotNil(t, factory) - require.IsType(t, publicKeyFactory{}, factory) -} - -func TestSigner_GetSignatureFactory(t *testing.T) { - signer := Generate() - - factory := signer.GetSignatureFactory() - require.NotNil(t, factory) - require.IsType(t, signatureFactory{}, factory) -} - -func TestSigner_Sign(t *testing.T) { - signer := NewSigner() - f := func(msg []byte) bool { - sig, err := signer.Sign(msg) - require.NoError(t, err) - - verifier, err := signer.GetVerifierFactory().FromArray( - []crypto.PublicKey{signer.GetPublicKey()}, - ) - require.NoError(t, err) - require.NoError(t, verifier.Verify(msg, sig)) - - return true - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestSigner_Aggregate(t *testing.T) { - N := 3 - - f := func(msg []byte) bool { - signatures := make([]crypto.Signature, N) - pubkeys := make([]crypto.PublicKey, N) - for i := 0; i < N; i++ { - signer := Generate() - pubkeys[i] = signer.GetPublicKey() - sig, err := signer.Sign(msg) - require.NoError(t, err) - signatures[i] = sig - } - - signer := NewSigner() - agg, err := signer.Aggregate(signatures...) - require.NoError(t, err) - - verifier, err := signer.GetVerifierFactory().FromArray(pubkeys) - require.NoError(t, err) - err = verifier.Verify(msg, agg) - require.NoError(t, err) - - return agg != nil - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestSigner_MarshalBinary(t *testing.T) { - signer := NewSigner() - - sig, err := signer.Sign([]byte{1, 2, 3}) - require.NoError(t, err) - - data, err := signer.MarshalBinary() - require.NoError(t, err) - - next, err := NewSignerFromBytes(data) - require.NoError(t, err) - require.NoError(t, next.GetPublicKey().Verify([]byte{1, 2, 3}, sig)) - - signer.private = badScalar{} - _, err = signer.MarshalBinary() - require.EqualError(t, err, fake.Err("while marshaling scalar")) - - _, err = NewSignerFromBytes(nil) - require.EqualError(t, err, "while unmarshaling scalar: UnmarshalBinary: wrong size buffer") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badPoint struct { - kyber.Point -} - -func (p badPoint) MarshalBinary() ([]byte, error) { - return nil, fake.GetError() -} - -type badScalar struct { - kyber.Scalar -} - -func (s badScalar) MarshalBinary() ([]byte, error) { - return nil, fake.GetError() -} diff --git a/dela/crypto/bls/command/action.go b/dela/crypto/bls/command/action.go deleted file mode 100644 index a77c794..0000000 --- a/dela/crypto/bls/command/action.go +++ /dev/null @@ -1,118 +0,0 @@ -package command - -import ( - "encoding/base64" - "fmt" - "io" - "os" - - "go.dedis.ch/dela/crypto" - - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/crypto/bls" - "golang.org/x/xerrors" -) - -// action defines the different cli actions of the BLS commands. Defining -// functions and printer helps in testing the commands. -type action struct { - printer io.Writer - - genSigner func() ([]byte, error) - getPubKey func([]byte) (crypto.PublicKey, error) - - readFile func(filename string) ([]byte, error) - saveFile func(path string, force bool, data []byte) error -} - -func (a action) newSignerAction(flags cli.Flags) error { - data, err := a.genSigner() - if err != nil { - return xerrors.Errorf("failed to marshal signer: %v", err) - } - - switch flags.String("save") { - case "": - fmt.Fprintln(a.printer, string(data)) - default: - err := a.saveFile(flags.String("save"), flags.Bool("force"), data) - if err != nil { - return xerrors.Errorf("failed to save files: %v", err) - } - } - - return nil -} - -func (a action) loadSignerAction(flags cli.Flags) error { - data, err := a.readFile(flags.Path("path")) - if err != nil { - return xerrors.Errorf("failed to read data: %v", err) - } - - var out []byte - - switch flags.String("format") { - case "PUBKEY": - pubkey, err := a.getPubKey(data) - if err != nil { - return xerrors.Errorf("failed to get PUBKEY: %v", err) - } - - out, err = pubkey.MarshalText() - if err != nil { - return xerrors.Errorf("failed to marshal pubkey: %v", err) - } - - case "BASE64_PUBKEY": - pubkey, err := a.getPubKey(data) - if err != nil { - return xerrors.Errorf("failed to get PUBKEY: %v", err) - } - - buf, err := pubkey.MarshalBinary() - if err != nil { - return xerrors.Errorf("failed to marshal pubkey: %v", err) - } - - out = []byte(base64.StdEncoding.EncodeToString(buf)) - - case "BASE64": - out = []byte(base64.StdEncoding.EncodeToString(data)) - - default: - return xerrors.Errorf("unknown format '%s'", flags.String("format")) - } - - fmt.Fprintln(a.printer, string(out)) - - return nil -} - -func saveToFile(path string, force bool, data []byte) error { - if !force && fileExist(path) { - return xerrors.Errorf("file '%s' already exist, use --force if you "+ - "want to overwrite", path) - } - - err := os.WriteFile(path, data, os.ModePerm) - if err != nil { - return xerrors.Errorf("failed to write file: %v", err) - } - - return nil -} - -func fileExist(path string) bool { - _, err := os.Stat(path) - return !os.IsNotExist(err) -} - -func getPubkey(data []byte) (crypto.PublicKey, error) { - signer, err := bls.NewSignerFromBytes(data) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal signer: %v", err) - } - - return signer.GetPublicKey(), nil -} diff --git a/dela/crypto/bls/command/action_test.go b/dela/crypto/bls/command/action_test.go deleted file mode 100644 index a0a0bff..0000000 --- a/dela/crypto/bls/command/action_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package command - -import ( - "io" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestNewSignerAction(t *testing.T) { - action := action{ - printer: io.Discard, - genSigner: badGenSigner, - saveFile: fakeSaveFile, - getPubKey: getPubkey, - } - - set := node.FlagSet{} - err := action.newSignerAction(set) - require.EqualError(t, err, fake.Err("failed to marshal signer")) - - action.genSigner = bls.NewSigner().MarshalBinary - err = action.newSignerAction(set) - require.NoError(t, err) - - set["save"] = "/do/not/exist" - action.saveFile = badSaveFile - - err = action.newSignerAction(set) - require.EqualError(t, err, fake.Err("failed to save files")) -} - -func TestLoadSignerAction(t *testing.T) { - action := action{ - printer: io.Discard, - readFile: badReadFile, - } - - set := node.FlagSet{} - err := action.loadSignerAction(set) - require.EqualError(t, err, fake.Err("failed to read data")) - - action.readFile = fakeReadFile - err = action.loadSignerAction(set) - require.EqualError(t, err, "unknown format ''") - - set["format"] = "PUBKEY" - action.getPubKey = badGetPubKey - err = action.loadSignerAction(set) - require.EqualError(t, err, fake.Err("failed to get PUBKEY")) - - action.getPubKey = wrongGetPubKey - err = action.loadSignerAction(set) - require.EqualError(t, err, fake.Err("failed to marshal pubkey")) - - set["format"] = "BASE64_PUBKEY" - action.getPubKey = badGetPubKey - err = action.loadSignerAction(set) - require.EqualError(t, err, fake.Err("failed to get PUBKEY")) - - action.getPubKey = wrongGetPubKey - err = action.loadSignerAction(set) - require.EqualError(t, err, fake.Err("failed to marshal pubkey")) - - set["format"] = "BASE64_PUBKEY" - action.getPubKey = fakeGetPubKey - err = action.loadSignerAction(set) - require.NoError(t, err) - - set["format"] = "BASE64" - action.getPubKey = badGetPubKey - err = action.loadSignerAction(set) - require.NoError(t, err) -} - -func TestSaveToFile(t *testing.T) { - path, err := os.MkdirTemp("", "dela-test-") - require.NoError(t, err) - - defer os.RemoveAll(path) - - file := filepath.Join(path, "test") - err = saveToFile(file, false, []byte{1}) - require.NoError(t, err) - - res, err := os.ReadFile(file) - require.NoError(t, err) - require.Equal(t, []byte{1}, res) - - err = saveToFile(file, false, nil) - require.Regexp(t, "^file '.*' already exist, use --force if you want to overwrite$", err) - - err = saveToFile("/not/exist", true, nil) - require.Regexp(t, "^failed to write file:", err) - - err = saveToFile(file, true, []byte{2}) - require.NoError(t, err) - - res, err = os.ReadFile(file) - require.NoError(t, err) - require.Equal(t, []byte{2}, res) -} - -func TestGetPUBKEY_Happy(t *testing.T) { - buf, err := bls.NewSigner().MarshalBinary() - require.NoError(t, err) - - _, err = getPubkey(buf) - require.NoError(t, err) -} - -func TestGetPUBKEY_Error(t *testing.T) { - _, err := getPubkey(nil) - require.EqualError(t, err, "failed to unmarshal signer: while unmarshaling scalar: UnmarshalBinary: wrong size buffer") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func badGenSigner() ([]byte, error) { - return nil, fake.GetError() -} - -func badReadFile(path string) ([]byte, error) { - return nil, fake.GetError() -} - -func badSaveFile(path string, force bool, data []byte) error { - return fake.GetError() -} - -func fakeReadFile(path string) ([]byte, error) { - return nil, nil -} - -func fakeSaveFile(path string, force bool, data []byte) error { - return nil -} - -func badGetPubKey([]byte) (crypto.PublicKey, error) { - return nil, fake.GetError() -} - -func wrongGetPubKey([]byte) (crypto.PublicKey, error) { - return fake.NewBadPublicKey(), nil -} - -func fakeGetPubKey([]byte) (crypto.PublicKey, error) { - return bls.Generate().GetPublicKey(), nil -} diff --git a/dela/crypto/bls/command/command.go b/dela/crypto/bls/command/command.go deleted file mode 100644 index 15da38f..0000000 --- a/dela/crypto/bls/command/command.go +++ /dev/null @@ -1,57 +0,0 @@ -// Package command defines cli commands for the bls package. -package command - -import ( - "os" - - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/crypto/bls" -) - -// Initializer implements the BLS initializer for the crypto CLI. -// -// - implements cli.Initializer -type Initializer struct { -} - -// SetCommands implements cli.Initializer. -func (i Initializer) SetCommands(provider cli.Provider) { - action := action{ - printer: os.Stdout, - - genSigner: bls.NewSigner().MarshalBinary, - getPubKey: getPubkey, - readFile: os.ReadFile, - saveFile: saveToFile, - } - - cmd := provider.SetCommand("bls") - signer := cmd.SetSubCommand("signer") - - new := signer.SetSubCommand("new") - new.SetDescription("create a new bls signer") - new.SetFlags(cli.StringFlag{ - Name: "save", - Usage: "if provided, save the signer to that file", - Required: false, - }, cli.BoolFlag{ - Name: "force", - Usage: "in the case it saves the signer, will overwrite if needed", - Required: false, - }) - new.SetAction(action.newSignerAction) - - read := signer.SetSubCommand("read") - read.SetDescription("read a signer") - read.SetFlags(cli.StringFlag{ - Name: "path", - Usage: "path to the signer's file", - Required: true, - }, cli.StringFlag{ - Name: "format", - Usage: "output format: [PUBKEY | BASE64 | BASE64_PUBKEY]", - Value: "PUBKEY", - Required: false, - }) - read.SetAction(action.loadSignerAction) -} diff --git a/dela/crypto/bls/command/command_test.go b/dela/crypto/bls/command/command_test.go deleted file mode 100644 index 90d3b96..0000000 --- a/dela/crypto/bls/command/command_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package command - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestSetCommands(t *testing.T) { - init := Initializer{} - - call := &fake.Call{} - provider := fakeBuilder{call: call} - init.SetCommands(provider) - - require.Equal(t, 10, call.Len()) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeCommandBuilder struct { - call *fake.Call -} - -func (b fakeCommandBuilder) SetSubCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return b -} - -func (b fakeCommandBuilder) SetDescription(value string) { - b.call.Add(value) -} - -func (b fakeCommandBuilder) SetFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeCommandBuilder) SetAction(a cli.Action) { - b.call.Add(a) -} - -type fakeBuilder struct { - call *fake.Call -} - -func (b fakeBuilder) SetCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return fakeCommandBuilder(b) -} diff --git a/dela/crypto/bls/example_test.go b/dela/crypto/bls/example_test.go deleted file mode 100644 index 924b3c2..0000000 --- a/dela/crypto/bls/example_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package bls - -import ( - "fmt" - - "go.dedis.ch/dela/crypto" -) - -func ExampleSigner_Sign() { - signerA := NewSigner() - signerB := NewSigner() - - publicKeys := []crypto.PublicKey{ - signerA.GetPublicKey(), - signerB.GetPublicKey(), - } - - message := []byte("42") - - signatureA, err := signerA.Sign(message) - if err != nil { - panic("signer A failed: " + err.Error()) - } - - signatureB, err := signerB.Sign(message) - if err != nil { - panic("signer B failed: " + err.Error()) - } - - aggregate, err := signerA.Aggregate(signatureA, signatureB) - if err != nil { - panic("aggregate failed: " + err.Error()) - } - - verifier, err := signerA.GetVerifierFactory().FromArray(publicKeys) - if err != nil { - panic("verifier failed: " + err.Error()) - } - - err = verifier.Verify(message, aggregate) - if err != nil { - panic("invalid signature: " + err.Error()) - } - - fmt.Println("Success") - - // Output: Success -} diff --git a/dela/crypto/bls/json/json.go b/dela/crypto/bls/json/json.go deleted file mode 100644 index 839d5ab..0000000 --- a/dela/crypto/bls/json/json.go +++ /dev/null @@ -1,113 +0,0 @@ -package json - -import ( - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/crypto/common/json" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - bls.RegisterPublicKeyFormat(serde.FormatJSON, pubkeyFormat{}) - bls.RegisterSignatureFormat(serde.FormatJSON, sigFormat{}) -} - -// PubkeyFormat is the engine to encode and decode BLS-BN256 public keys in JSON -// format. -// -// - implements serde.FormatEngine -type pubkeyFormat struct{} - -// Encode implements serde.FormatEngine. It serialized the public key message in -// JSON if appropriate, otherwise it returns an error. -func (f pubkeyFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - pubkey, ok := msg.(bls.PublicKey) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - buffer, err := pubkey.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal point: %v", err) - } - - m := json.PublicKey{ - Algorithm: json.Algorithm{Name: bls.Algorithm}, - Data: buffer, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the public key with JSON -// data if appropriate, otherwise it returns an error. -func (f pubkeyFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := json.PublicKey{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize data: %v", err) - } - - pubkey, err := bls.NewPublicKey(m.Data) - if err != nil { - return nil, xerrors.Errorf("couldn't unmarshal point: %v", err) - } - - return pubkey, nil -} - -// SigFormat is the engine to encode and decode signature messages in JSON -// format. -// -// - implements serde.FormatEngine -type sigFormat struct{} - -// Encode implements serde.FormatEngine. It returns the serialized data of the -// signature message if appropriate, otherwise an error. -func (f sigFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - sig, ok := msg.(bls.Signature) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - buffer, err := sig.MarshalBinary() - assert(err) - - m := json.Signature{ - Algorithm: json.Algorithm{Name: bls.Algorithm}, - Data: buffer, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the signature with the -// JSON data if appropriate, otherwise it returns an error. -func (f sigFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := json.Signature{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize data: %v", err) - } - - return bls.NewSignature(m.Data), nil -} - -// Current implementation cannot return an error but it might change in the -// future therefore an assertion is made to detect if it changes. -func assert(err error) { - if err != nil { - panic("Implementation of the BLS signature is expected " + - "to return a nil when marshaling but an error has been found: " + err.Error()) - } -} diff --git a/dela/crypto/bls/json/json_test.go b/dela/crypto/bls/json/json_test.go deleted file mode 100644 index 7fd9900..0000000 --- a/dela/crypto/bls/json/json_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package json - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" - "go.dedis.ch/kyber/v3" -) - -func TestPubkeyFormat_Encode(t *testing.T) { - signer := bls.Generate() - format := pubkeyFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - data, err := format.Encode(ctx, signer.GetPublicKey()) - require.NoError(t, err) - require.Contains(t, string(data), fmt.Sprintf(`{"Name":"%s","Data":`, bls.Algorithm)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(ctx, bls.NewPublicKeyFromPoint(badPoint{})) - require.EqualError(t, err, fake.Err("couldn't marshal point")) - - _, err = format.Encode(fake.NewBadContext(), signer.GetPublicKey()) - require.EqualError(t, err, fake.Err("couldn't marshal")) -} - -func TestPubkeyFormat_Decode(t *testing.T) { - signer := bls.Generate() - format := pubkeyFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - data, err := signer.GetPublicKey().Serialize(ctx) - require.NoError(t, err) - - pubkey, err := format.Decode(ctx, data) - require.NoError(t, err) - require.True(t, signer.GetPublicKey().Equal(pubkey.(bls.PublicKey))) - - _, err = format.Decode(ctx, []byte(`{"Data":[]}`)) - require.EqualError(t, err, - "couldn't unmarshal point: bn256.G2: not enough data") - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize data")) -} - -func TestSigFormat_Encode(t *testing.T) { - sig := bls.NewSignature([]byte("deadbeef")) - format := sigFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - data, err := format.Encode(ctx, sig) - require.NoError(t, err) - require.Contains(t, string(data), fmt.Sprintf(`{"Name":"%s","Data":`, bls.Algorithm)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), sig) - require.EqualError(t, err, fake.Err("couldn't marshal")) -} - -func TestSigFormat_Decode(t *testing.T) { - format := sigFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - - sig, err := format.Decode(ctx, []byte(`{"Data":"QQ=="}`)) - require.NoError(t, err) - require.Equal(t, bls.NewSignature([]byte("A")), sig) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{"Data":"QQ=="}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize data")) -} - -func TestAssert(t *testing.T) { - defer func() { - r := recover() - require.Contains(t, r, fake.GetError().Error()) - }() - - assert(fake.GetError()) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badPoint struct { - kyber.Point -} - -func (p badPoint) MarshalBinary() ([]byte, error) { - return nil, fake.GetError() -} diff --git a/dela/crypto/common/common.go b/dela/crypto/common/common.go deleted file mode 100644 index 4d429fe..0000000 --- a/dela/crypto/common/common.go +++ /dev/null @@ -1,177 +0,0 @@ -// Package common implements the factories of the crypto primitives to allow the -// use of multiple algorithms over the same communication channel. -// -// Documentation Last Review: 05.10.2020 -package common - -import ( - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var algFormats = registry.NewSimpleRegistry() - -// RegisterAlgorithmFormat registers the engine for the provided format. -func RegisterAlgorithmFormat(c serde.Format, f serde.FormatEngine) { - algFormats.Register(c, f) -} - -// Algorithm contains information about a signature algorithm. -// -// - implements serde.Message -type Algorithm struct { - name string -} - -// NewAlgorithm returns a new algorithm from the provided name. -func NewAlgorithm(name string) Algorithm { - return Algorithm{name: name} -} - -// GetName returns the name of the algorithm. -func (alg Algorithm) GetName() string { - return alg.name -} - -// Serialize implements serde.Message. It returns the serialized for the -// algorithm. -func (alg Algorithm) Serialize(ctx serde.Context) ([]byte, error) { - format := algFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, alg) - if err != nil { - return nil, xerrors.Errorf("couldn't encode algorithm: %v", err) - } - - return data, nil -} - -// PublicKeyFactory is a redefinition of the crypto public key factory to -// exclude some functions that are incompatible with the logic of a common -// factory which requires specific serialization. -type PublicKeyFactory interface { - serde.Factory - - // PublicKeyOf returns the public key associated to the data if appropriate, - // otherwise an error. - PublicKeyOf(serde.Context, []byte) (crypto.PublicKey, error) -} - -// PublicKeyFac is a public key factory for commonly known algorithms. -// -// - implements common.PublicKeyFactory -type PublicKeyFac struct { - factories map[string]crypto.PublicKeyFactory -} - -// NewPublicKeyFactory returns a new instance of the common public key factory. -func NewPublicKeyFactory() PublicKeyFac { - factory := PublicKeyFac{ - factories: make(map[string]crypto.PublicKeyFactory), - } - - factory.RegisterAlgorithm(bls.Algorithm, bls.NewPublicKeyFactory()) - - return factory -} - -// RegisterAlgorithm registers the factory for the algorithm. It will override -// an already existing key. -func (f PublicKeyFac) RegisterAlgorithm(algo string, factory crypto.PublicKeyFactory) { - f.factories[algo] = factory -} - -// Deserialize implements serde.Factory. It looks up the format and returns the -// public key of the data if appropriate, otherwise an error. -func (f PublicKeyFac) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := algFormats.Get(ctx.GetFormat()) - - m, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode algorithm: %v", err) - } - - alg, ok := m.(Algorithm) - if !ok { - return nil, xerrors.Errorf("invalid message of type '%T'", m) - } - - factory := f.factories[alg.name] - if factory == nil { - return nil, xerrors.Errorf("unknown algorithm '%s'", alg.name) - } - - return factory.PublicKeyOf(ctx, data) -} - -// PublicKeyOf implements crypto.PublicKeyFactory. It returns the public key of -// the data if appropriate, otherwise an error. -func (f PublicKeyFac) PublicKeyOf(ctx serde.Context, data []byte) (crypto.PublicKey, error) { - msg, err := f.Deserialize(ctx, data) - if err != nil { - return nil, err - } - - return msg.(crypto.PublicKey), nil -} - -// SignatureFactory is a factory for commonly known algorithms. -// -// - implements crypto.SignatureFactory -type SignatureFactory struct { - factories map[string]crypto.SignatureFactory -} - -// NewSignatureFactory returns a new instance of the common signature factory. -// It registers the BLS algorithm by default. -func NewSignatureFactory() SignatureFactory { - factory := SignatureFactory{ - factories: make(map[string]crypto.SignatureFactory), - } - - factory.RegisterAlgorithm(bls.Algorithm, bls.NewSignatureFactory()) - - return factory -} - -// RegisterAlgorithm register the factory for the algorithm. -func (f SignatureFactory) RegisterAlgorithm(name string, factory crypto.SignatureFactory) { - f.factories[name] = factory -} - -// Deserialize implements serde.Factory. It returns the signature associated to -// the data if appropriate, otherwise it returns an error. -func (f SignatureFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := algFormats.Get(ctx.GetFormat()) - - m, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode algorithm: %v", err) - } - - alg, ok := m.(Algorithm) - if !ok { - return nil, xerrors.Errorf("invalid message of type '%T'", m) - } - - factory := f.factories[alg.name] - if factory == nil { - return nil, xerrors.Errorf("unknown algorithm '%s'", alg.name) - } - - return factory.SignatureOf(ctx, data) -} - -// SignatureOf implements crypto.SignatureFactory. It returns the signature -// associated to the data if appropriate, otherwise it returns an error. -func (f SignatureFactory) SignatureOf(ctx serde.Context, data []byte) (crypto.Signature, error) { - msg, err := f.Deserialize(ctx, data) - if err != nil { - return nil, err - } - - return msg.(crypto.Signature), nil -} diff --git a/dela/crypto/common/common_test.go b/dela/crypto/common/common_test.go deleted file mode 100644 index a7f7577..0000000 --- a/dela/crypto/common/common_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package common - -import ( - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -const testAlgorithm = "fake" - -func init() { - RegisterAlgorithmFormat(fake.GoodFormat, fake.Format{ - Msg: Algorithm{name: testAlgorithm}, - }) - RegisterAlgorithmFormat(serde.Format("BAD_TYPE"), fake.Format{Msg: fake.Message{}}) - RegisterAlgorithmFormat(fake.BadFormat, fake.NewBadFormat()) -} - -func TestAlgorithm_GetName(t *testing.T) { - f := func(name string) bool { - algo := NewAlgorithm(name) - - return name == algo.GetName() - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestAlgorithm_Serialize(t *testing.T) { - algo := NewAlgorithm(testAlgorithm) - - data, err := algo.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = algo.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode algorithm")) -} - -func TestPublicKeyFactory_RegisterAlgorithm(t *testing.T) { - factory := NewPublicKeyFactory() - - // Check passive registrations. - require.Len(t, factory.factories, 1) - - factory.RegisterAlgorithm(testAlgorithm, fake.PublicKeyFactory{}) - require.Len(t, factory.factories, 2) -} - -func TestPublicKeyFactory_Deserialize(t *testing.T) { - factory := NewPublicKeyFactory() - factory.RegisterAlgorithm(testAlgorithm, fake.PublicKeyFactory{}) - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, fake.PublicKey{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode algorithm")) - - _, err = factory.Deserialize(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid message of type 'fake.Message'") - - factory = NewPublicKeyFactory() - _, err = factory.Deserialize(fake.NewContext(), nil) - require.EqualError(t, err, "unknown algorithm 'fake'") -} - -func TestPublicKeyFactory_PublicKeyOf(t *testing.T) { - factory := NewPublicKeyFactory() - factory.RegisterAlgorithm(testAlgorithm, fake.PublicKeyFactory{}) - - pk, err := factory.PublicKeyOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, fake.PublicKey{}, pk) - - _, err = factory.PublicKeyOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode algorithm")) -} - -func TestSignatureFactory_RegisterAlgorithm(t *testing.T) { - factory := NewSignatureFactory() - - require.Len(t, factory.factories, 1) - - factory.RegisterAlgorithm("fake", fake.SignatureFactory{}) - require.Len(t, factory.factories, 2) -} - -func TestSignatureFactory_Deserialize(t *testing.T) { - factory := NewSignatureFactory() - factory.RegisterAlgorithm(testAlgorithm, fake.SignatureFactory{}) - - msg, err := factory.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, fake.Signature{}, msg) - - _, err = factory.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode algorithm")) - - _, err = factory.Deserialize(fake.NewContextWithFormat(serde.Format("BAD_TYPE")), nil) - require.EqualError(t, err, "invalid message of type 'fake.Message'") - - factory = NewSignatureFactory() - _, err = factory.Deserialize(fake.NewContext(), nil) - require.EqualError(t, err, "unknown algorithm 'fake'") -} - -func TestSignatureFactory_SignatureOf(t *testing.T) { - factory := NewSignatureFactory() - factory.RegisterAlgorithm(testAlgorithm, fake.SignatureFactory{}) - - sig, err := factory.SignatureOf(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, fake.Signature{}, sig) - - _, err = factory.SignatureOf(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("couldn't decode algorithm")) -} diff --git a/dela/crypto/common/example_test.go b/dela/crypto/common/example_test.go deleted file mode 100644 index 1b63ad6..0000000 --- a/dela/crypto/common/example_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package common_test - -import ( - "fmt" - - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/crypto/ed25519" - "go.dedis.ch/dela/serde/json" -) - -func ExamplePublicKeyFactory_PublicKeyOf_bls() { - // BLS is already registered by default - factory := common.NewPublicKeyFactory() - - ctx := json.NewContext() - - message := []byte("42") - - signer := bls.NewSigner() - publicKey := signer.GetPublicKey() - - signature, err := signer.Sign(message) - if err != nil { - panic("signing failed: " + err.Error()) - } - - data, err := publicKey.Serialize(ctx) - if err != nil { - panic("serialization failed: " + err.Error()) - } - - // Transmit the data over a physical communication channel... - - result, err := factory.PublicKeyOf(ctx, data) - if err != nil { - panic("factory failed: " + err.Error()) - } - - err = result.Verify(message, signature) - if err != nil { - fmt.Println("public key is invalid") - } else { - fmt.Println("signature is verified") - } - - // Output: signature is verified -} - -func ExamplePublicKeyFactory_PublicKeyOf_ed25519() { - factory := common.NewPublicKeyFactory() - factory.RegisterAlgorithm(ed25519.Algorithm, ed25519.NewPublicKeyFactory()) - - ctx := json.NewContext() - - message := []byte("42") - - signer := ed25519.NewSigner() - publicKey := signer.GetPublicKey() - - signature, err := signer.Sign(message) - if err != nil { - panic("signature failed: " + err.Error()) - } - - data, err := publicKey.Serialize(ctx) - if err != nil { - panic("serialization failed: " + err.Error()) - } - - // Transmit the data over a physical communication channel... - - result, err := factory.PublicKeyOf(ctx, data) - if err != nil { - panic("factory failed: " + err.Error()) - } - - err = result.Verify(message, signature) - if err != nil { - fmt.Println("public key is invalid") - } else { - fmt.Println("signature is verified") - } - - // Output: signature is verified -} diff --git a/dela/crypto/common/json/json.go b/dela/crypto/common/json/json.go deleted file mode 100644 index 2e28243..0000000 --- a/dela/crypto/common/json/json.go +++ /dev/null @@ -1,70 +0,0 @@ -package json - -import ( - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - common.RegisterAlgorithmFormat(serde.FormatJSON, algoFormat{}) -} - -// Algorithm is a common JSON message to identify which algorithm is used in a -// message. -type Algorithm struct { - Name string -} - -// PublicKey is the common JSON message for a public key. It contains the -// algorithm and the data to deserialize. -type PublicKey struct { - Algorithm - Data []byte -} - -// Signature is the common JSON message for a signature. It contains the -// algorithm and the data to deserialize. -type Signature struct { - Algorithm - Data []byte -} - -// AlgoFormat is the engine to encode and decode algorithm data in JSON format. -// -// - implements serde.FormatEngine -type algoFormat struct{} - -// Encode implements serde.FormatEngine. It returns the JSON representation of -// an algorithm message. -func (f algoFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - algo, ok := msg.(common.Algorithm) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - m := Algorithm{ - Name: algo.GetName(), - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the algorithm message from -// the JSON data if appropriate, otherwise it returns an error. -func (f algoFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := Algorithm{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize algorithm: %v", err) - } - - alg := common.NewAlgorithm(m.Name) - - return alg, nil -} diff --git a/dela/crypto/common/json/json_test.go b/dela/crypto/common/json/json_test.go deleted file mode 100644 index 46c8de5..0000000 --- a/dela/crypto/common/json/json_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto/common" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestAlgoFormat_Encode(t *testing.T) { - algo := common.NewAlgorithm("fake") - - format := algoFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - - data, err := format.Encode(ctx, algo) - require.NoError(t, err) - require.Equal(t, `{"Name":"fake"}`, string(data)) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") - - _, err = format.Encode(fake.NewBadContext(), algo) - require.EqualError(t, err, fake.Err("couldn't marshal")) -} - -func TestFormat_Decode(t *testing.T) { - format := algoFormat{} - ctx := serde.NewContext(fake.ContextEngine{}) - - algo, err := format.Decode(ctx, []byte(`{"Name": "fake","Data":[]}`)) - require.NoError(t, err) - require.Equal(t, common.NewAlgorithm("fake"), algo) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't deserialize algorithm")) -} diff --git a/dela/crypto/ed25519/ed25519.go b/dela/crypto/ed25519/ed25519.go deleted file mode 100644 index ea56b6c..0000000 --- a/dela/crypto/ed25519/ed25519.go +++ /dev/null @@ -1,330 +0,0 @@ -// Package ed25519 implements the cryptographic primitives for the Edwards 25519 -// elliptic curve. -// -// The signatures are created using the Schnorr algorithm which allows the -// aggregation of multiple signatures and public keys. -// -// Related Papers: -// -// Efficient Identification and Signatures for Smart Cards (1989) -// https://link.springer.com/chapter/10.1007/0-387-34805-0_22 -// -// Documentation Last Review: 05.10.2020 -package ed25519 - -import ( - "bytes" - "fmt" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "go.dedis.ch/kyber/v3" - "go.dedis.ch/kyber/v3/sign/schnorr" - "go.dedis.ch/kyber/v3/suites" - "go.dedis.ch/kyber/v3/util/key" - "golang.org/x/xerrors" -) - -const ( - // Algorithm is the name of the curve used for the schnorr signature. - Algorithm = "CURVE-ED25519" -) - -var ( - suite = suites.MustFind("Ed25519") - - pubkeyFormats = registry.NewSimpleRegistry() - - sigFormats = registry.NewSimpleRegistry() -) - -// RegisterPublicKeyFormat register the engine for the provided format. -func RegisterPublicKeyFormat(format serde.Format, engine serde.FormatEngine) { - pubkeyFormats.Register(format, engine) -} - -// RegisterSignatureFormat register the engine for the provided format. -func RegisterSignatureFormat(format serde.Format, engine serde.FormatEngine) { - sigFormats.Register(format, engine) -} - -// PublicKey is the public key adapter to the Kyber Ed25519 public key. -// -// - implements crypto.PublicKey -type PublicKey struct { - point kyber.Point -} - -// NewPublicKey returns a new public key from the data. -func NewPublicKey(data []byte) (PublicKey, error) { - point := suite.Point() - err := point.UnmarshalBinary(data) - if err != nil { - return PublicKey{}, xerrors.Errorf("couldn't unmarshal point: %v", err) - } - - pk := PublicKey{ - point: point, - } - - return pk, nil -} - -// NewPublicKeyFromPoint creates a new public key from an existing point. -func NewPublicKeyFromPoint(point kyber.Point) PublicKey { - return PublicKey{ - point: point, - } -} - -// MarshalBinary implements encoding.BinaryMarshaler. It produces a slice of -// bytes representing the public key. -func (pk PublicKey) MarshalBinary() ([]byte, error) { - return pk.point.MarshalBinary() -} - -// Serialize implements serde.Message. It returns the serialized data of the -// public key. -func (pk PublicKey) Serialize(ctx serde.Context) ([]byte, error) { - format := pubkeyFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, pk) - if err != nil { - return nil, xerrors.Errorf("couldn't encode public key: %v", err) - } - - return data, nil -} - -// Verify implements crypto.PublicKey. It returns nil if the signature matches -// the message for this public key. -func (pk PublicKey) Verify(msg []byte, sig crypto.Signature) error { - signature, ok := sig.(Signature) - if !ok { - return xerrors.Errorf("invalid signature type '%T'", sig) - } - - err := schnorr.Verify(suite, pk.point, msg, signature.data) - if err != nil { - return xerrors.Errorf("schnorr verify failed: %v", err) - } - - return nil -} - -// Equal implements crypto.PublicKey. It returns true if the other public key -// is the same. -func (pk PublicKey) Equal(other interface{}) bool { - pubkey, ok := other.(PublicKey) - if !ok { - return false - } - - return pubkey.point.Equal(pk.point) -} - -// MarshalText implements encoding.TextMarshaler. It returns a text -// representation of the public key. -func (pk PublicKey) MarshalText() ([]byte, error) { - buffer, err := pk.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return []byte(fmt.Sprintf("schnorr:%x", buffer)), nil -} - -// GetPoint returns the kyber.point. -func (pk PublicKey) GetPoint() kyber.Point { - return pk.point -} - -// String implements fmt.Stringer. It returns a string representation of the -// point. -func (pk PublicKey) String() string { - buffer, err := pk.MarshalText() - if err != nil { - return "schnorr:malformed_point" - } - - // Output only the prefix and 16 characters of the buffer in hexadecimal. - return string(buffer)[:8+16] -} - -// Signature is the adapter of the Kyber Schnorr signature. -// -// - implements crypto.Signature -type Signature struct { - data []byte -} - -// NewSignature returns a new signature from the data. -func NewSignature(data []byte) Signature { - return Signature{ - data: data, - } -} - -// MarshalBinary implements encoding.BinaryMarshaler. It returns a slice of -// bytes representing the signature. -func (sig Signature) MarshalBinary() ([]byte, error) { - return sig.data, nil -} - -// Serialize implements serde.Message. It returns the serialized data of the -// signature. -func (sig Signature) Serialize(ctx serde.Context) ([]byte, error) { - format := sigFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, sig) - if err != nil { - return nil, xerrors.Errorf("couldn't encode signature: %v", err) - } - - return data, nil -} - -// Equal implements crypto.Signature. It returns true if both signatures are the -// same. -func (sig Signature) Equal(other crypto.Signature) bool { - otherSig, ok := other.(Signature) - if !ok { - return false - } - - return bytes.Equal(sig.data, otherSig.data) -} - -// publicKeyFactory is a factory to deserialize public keys for the Ed25519 -// curve. -// -// - implements crypto.PublicKeyFactory -// - implements serde.Factory -type publicKeyFactory struct{} - -// NewPublicKeyFactory returns a new instance of the factory. -func NewPublicKeyFactory() crypto.PublicKeyFactory { - return publicKeyFactory{} -} - -// Deserialize implements serde.Factory. It returns the public key deserialized -// if appropriate, otherwise an error. -func (f publicKeyFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.PublicKeyOf(ctx, data) -} - -// PublicKeyOf implements crypto.PublicKeyFactory. It returns the public key -// deserialized if appropriate, otherwise an error. -func (f publicKeyFactory) PublicKeyOf(ctx serde.Context, data []byte) (crypto.PublicKey, error) { - format := pubkeyFormats.Get(ctx.GetFormat()) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode public key: %v", err) - } - - pubkey, ok := msg.(PublicKey) - if !ok { - return nil, xerrors.Errorf("invalid public key of type '%T'", msg) - } - - return pubkey, nil -} - -// FromBytes implements crypto.PublicKeyFactory. It returns the public key -// unmarshaled from the bytes. -func (f publicKeyFactory) FromBytes(data []byte) (crypto.PublicKey, error) { - pubkey, err := NewPublicKey(data) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal the key: %v", err) - } - - return pubkey, nil -} - -// signatureFactory is a factory to deserialize signatures of the Ed25519 -// elliptic curve. -// -// - implements crypto.SignatureFactory -// - implements serde.Factory -type signatureFactory struct{} - -// NewSignatureFactory returns a new instance of the factory. -func NewSignatureFactory() crypto.SignatureFactory { - return signatureFactory{} -} - -// Deserialize implements serde.Factory. It returns the signature associated to -// the data if appropriate, otherwise an error. -func (f signatureFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.SignatureOf(ctx, data) -} - -// SignatureOf implements crypto.SignatureFactory. It returns the signature -// associated to the data if appropriate, otherwise an error. -func (f signatureFactory) SignatureOf(ctx serde.Context, data []byte) (crypto.Signature, error) { - format := sigFormats.Get(ctx.GetFormat()) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("couldn't decode signature: %v", err) - } - - signature, ok := msg.(Signature) - if !ok { - return nil, xerrors.Errorf("invalid signature of type '%T'", msg) - } - - return signature, nil -} - -// Signer implements a signer that is creating Schnorr signatures using the -// private key of the Ed25519 elliptic curve. -// -// - implements crypto.Signer -type Signer struct { - keyPair *key.Pair -} - -// NewSigner returns a new random schnorr signer. -func NewSigner() crypto.Signer { - kp := key.NewKeyPair(suite) - return Signer{ - keyPair: kp, - } -} - -// GetPublicKeyFactory implements crypto.Signer. It returns the public key -// factory for schnorr signatures. -func (s Signer) GetPublicKeyFactory() crypto.PublicKeyFactory { - return publicKeyFactory{} -} - -// GetSignatureFactory implements crypto.Signer. It returns the signature -// factory for schnorr signatures. -func (s Signer) GetSignatureFactory() crypto.SignatureFactory { - return signatureFactory{} -} - -// GetPublicKey implements crypto.Signer. It returns the public key of the -// signer that can be used to verify signatures. -func (s Signer) GetPublicKey() crypto.PublicKey { - return PublicKey{point: s.keyPair.Public} -} - -// GetPrivateKey returns the signer's private key. -func (s Signer) GetPrivateKey() kyber.Scalar { - return s.keyPair.Private -} - -// Sign implements crypto.Signer. It signs the message in parameter and returns -// the signature, or an error if it cannot sign. -func (s Signer) Sign(msg []byte) (crypto.Signature, error) { - sig, err := schnorr.Sign(suite, s.keyPair.Private, msg) - if err != nil { - return nil, xerrors.Errorf("couldn't make schnorr signature: %v", err) - } - - return Signature{data: sig}, nil -} diff --git a/dela/crypto/ed25519/ed25519_test.go b/dela/crypto/ed25519/ed25519_test.go deleted file mode 100644 index 237f320..0000000 --- a/dela/crypto/ed25519/ed25519_test.go +++ /dev/null @@ -1,320 +0,0 @@ -package ed25519 - -import ( - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/kyber/v3" - "go.dedis.ch/kyber/v3/sign/schnorr" - "go.dedis.ch/kyber/v3/util/key" -) - -func init() { - RegisterPublicKeyFormat(fake.GoodFormat, fake.Format{Msg: PublicKey{}}) - RegisterPublicKeyFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterPublicKeyFormat("BAD_POINT", fake.Format{Msg: fake.Message{}}) - - RegisterSignatureFormat(fake.GoodFormat, fake.Format{Msg: Signature{}}) - RegisterSignatureFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterSignatureFormat("BAD_SIG", fake.Format{Msg: fake.Message{}}) -} - -func TestPublicKey_New(t *testing.T) { - point := suite.Point() - pointBuf, err := point.MarshalBinary() - require.NoError(t, err) - - pubKey, err := NewPublicKey(pointBuf) - require.NoError(t, err) - - require.True(t, pubKey.GetPoint().Equal(point)) - - _, err = NewPublicKey([]byte{}) - require.EqualError(t, err, "couldn't unmarshal point: invalid Ed25519 curve point") -} - -func TestPublicKey_NewFromPoint(t *testing.T) { - point := suite.Point() - pk := NewPublicKeyFromPoint(point) - require.True(t, pk.GetPoint().Equal(point)) -} - -func TestPublicKey_MarshalBinary(t *testing.T) { - point := suite.Point() - pointBuf, err := point.MarshalBinary() - require.NoError(t, err) - - pk := PublicKey{point: point} - pointBuf2, err := pk.MarshalBinary() - require.NoError(t, err) - - require.Equal(t, pointBuf, pointBuf2) -} - -func TestPublicKey_Serialize(t *testing.T) { - pk := PublicKey{} - ctx := fake.NewContext() - - pkBuf, err := pk.Serialize(ctx) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), pkBuf) - - _, err = pk.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("couldn't encode public key")) -} - -func TestPublicKey_Verify(t *testing.T) { - privKey := suite.Scalar().Pick(suite.RandomStream()) - pubKey := suite.Point().Mul(privKey, nil) - pk := PublicKey{point: pubKey} - - msg := []byte("hello") - signature, err := schnorr.Sign(suite, privKey, msg) - require.NoError(t, err) - - err = pk.Verify(msg, Signature{data: signature}) - require.NoError(t, err) - - err = pk.Verify(msg, fake.NewBadSignature()) - require.EqualError(t, err, "invalid signature type 'fake.Signature'") - - err = pk.Verify(msg, Signature{data: []byte{}}) - // the second error part depends on kyber implementation - require.Regexp(t, "^schnorr verify failed: ", err) -} - -func TestPublicKey_Equal(t *testing.T) { - point := suite.Point() - pk := PublicKey{point: point} - pk2 := PublicKey{point: point} - - require.True(t, pk.Equal(pk2)) - require.False(t, pk.Equal(fake.NewBadPublicKey())) - - point2 := suite.Point().Pick(suite.RandomStream()) - pk2 = PublicKey{point: point2} - - require.False(t, pk.Equal(pk2)) -} - -func TestPublicKey_MarshalText(t *testing.T) { - point := suite.Point() - pk := PublicKey{point: point} - - res, err := pk.MarshalText() - require.NoError(t, err) - require.Regexp(t, "^schnorr:", string(res)) - - pk.point = badPoint{} - _, err = pk.MarshalText() - require.EqualError(t, err, fake.Err("couldn't marshal")) -} - -func TestPublicKey_GetPoint(t *testing.T) { - point := suite.Point() - pk := PublicKey{point: point} - - require.True(t, point.Equal(pk.GetPoint())) -} - -func TestPublicKey_String(t *testing.T) { - point := suite.Point() - pk := PublicKey{point: point} - - res := pk.String() - require.Regexp(t, "^schnorr:[a-f0-9]{16}$", res) - - pk.point = badPoint{} - res = pk.String() - require.Equal(t, "schnorr:malformed_point", res) -} - -func TestSignature_New(t *testing.T) { - data := []byte("hello") - sig := NewSignature(data) - require.Equal(t, data, sig.data) -} - -func TestSignature_MarshalBinary(t *testing.T) { - data := []byte("hello") - sig := NewSignature(data) - - buf, err := sig.MarshalBinary() - require.NoError(t, err) - require.Equal(t, data, buf) -} - -func TestSignature_Serialize(t *testing.T) { - data := []byte("hello") - sig := NewSignature(data) - - ctx := fake.NewContext() - buf, err := sig.Serialize(ctx) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), buf) - - ctx = fake.NewBadContext() - _, err = sig.Serialize(ctx) - require.EqualError(t, err, fake.Err("couldn't encode signature")) -} - -func TestSignature_Equal(t *testing.T) { - data := []byte("hello") - sig := NewSignature(data) - sig2 := NewSignature(data) - - require.True(t, sig.Equal(sig2)) - require.False(t, sig.Equal(fake.NewBadSignature())) - - data2 := []byte("world") - sig2 = NewSignature(data2) - - require.False(t, sig.Equal(sig2)) -} - -func TestPublicKeyFactory_New(t *testing.T) { - pkf := NewPublicKeyFactory() - require.IsType(t, publicKeyFactory{}, pkf) -} - -func TestPublicKeyFactory_Deserialize(t *testing.T) { - pkf := NewPublicKeyFactory() - ctx := fake.NewContext() - - msg, err := pkf.Deserialize(ctx, nil) - require.NoError(t, err) - require.IsType(t, PublicKey{}, msg) -} - -func TestPublicKeyFactory_PublicKeyOf(t *testing.T) { - pkf := NewPublicKeyFactory() - ctx := fake.NewContext() - - msg, err := pkf.PublicKeyOf(ctx, nil) - require.NoError(t, err) - require.IsType(t, PublicKey{}, msg) - - ctx = fake.NewBadContext() - _, err = pkf.PublicKeyOf(ctx, nil) - require.EqualError(t, err, fake.Err("couldn't decode public key")) - - ctx = fake.NewContextWithFormat("BAD_POINT") - _, err = pkf.PublicKeyOf(ctx, nil) - require.EqualError(t, err, "invalid public key of type 'fake.Message'") -} - -func TestPublicKeyFactory_FromBytes(t *testing.T) { - fac := NewPublicKeyFactory() - - point := suite.Point() - data, err := point.MarshalBinary() - require.NoError(t, err) - - pk, err := fac.FromBytes(data) - require.NoError(t, err) - require.True(t, pk.(PublicKey).point.Equal(point)) - - _, err = fac.FromBytes(nil) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to unmarshal the key: ") -} - -func TestSignatureFactory_New(t *testing.T) { - sf := NewSignatureFactory() - require.IsType(t, signatureFactory{}, sf) -} - -func TestSignatureFactory_Deserialize(t *testing.T) { - sf := NewSignatureFactory() - ctx := fake.NewContext() - - msg, err := sf.Deserialize(ctx, nil) - require.NoError(t, err) - require.IsType(t, Signature{}, msg) -} - -func TestSignatureFactory_SignatureOf(t *testing.T) { - sf := NewSignatureFactory() - ctx := fake.NewContext() - - msg, err := sf.SignatureOf(ctx, nil) - require.NoError(t, err) - require.IsType(t, Signature{}, msg) - - ctx = fake.NewBadContext() - _, err = sf.SignatureOf(ctx, nil) - require.EqualError(t, err, fake.Err("couldn't decode signature")) - - ctx = fake.NewContextWithFormat("BAD_SIG") - _, err = sf.SignatureOf(ctx, nil) - require.EqualError(t, err, "invalid signature of type 'fake.Message'") -} - -func TestSigner_New(t *testing.T) { - signer := NewSigner() - require.IsType(t, Signer{}, signer) -} - -func TestSigner_GetPublicKeyFactory(t *testing.T) { - signer := NewSigner() - factory := signer.GetPublicKeyFactory() - require.IsType(t, publicKeyFactory{}, factory) -} - -func TestSigner_GetSignatureFactory(t *testing.T) { - signer := NewSigner() - factory := signer.GetSignatureFactory() - require.IsType(t, signatureFactory{}, factory) -} - -func TestSigner_GetPublicKey(t *testing.T) { - kp := key.NewKeyPair(suite) - signer := Signer{keyPair: kp} - - pk := NewPublicKeyFromPoint(kp.Public) - - pk2 := signer.GetPublicKey() - require.True(t, pk.Equal(pk2)) -} - -func TestSigner_GetPrivateKey(t *testing.T) { - kp := key.NewKeyPair(suite) - signer := Signer{keyPair: kp} - - secret := signer.GetPrivateKey() - require.True(t, secret.Equal(kp.Private)) -} - -func TestSigner_Sign(t *testing.T) { - kp := key.NewKeyPair(suite) - signer := Signer{keyPair: kp} - - f := func(msg []byte) bool { - signature, err := signer.Sign(msg) - require.NoError(t, err) - - signData, err := signature.MarshalBinary() - require.NoError(t, err) - - err = schnorr.Verify(suite, kp.Public, msg, signData) - require.NoError(t, err) - - return true - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badPoint struct { - kyber.Point -} - -func (p badPoint) MarshalBinary() ([]byte, error) { - return nil, fake.GetError() -} diff --git a/dela/crypto/ed25519/example_test.go b/dela/crypto/ed25519/example_test.go deleted file mode 100644 index 5289bf9..0000000 --- a/dela/crypto/ed25519/example_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package ed25519 - -import "fmt" - -func ExampleSigner_Sign() { - signerA := NewSigner() - - message := []byte("42") - - signature, err := signerA.Sign(message) - if err != nil { - panic("signer failed: " + err.Error()) - } - - err = signerA.GetPublicKey().Verify(message, signature) - if err != nil { - panic("invalid signature: " + err.Error()) - } - - fmt.Println("Success") - - // Output: Success -} diff --git a/dela/crypto/ed25519/json/json.go b/dela/crypto/ed25519/json/json.go deleted file mode 100644 index 999e3fe..0000000 --- a/dela/crypto/ed25519/json/json.go +++ /dev/null @@ -1,90 +0,0 @@ -package json - -import ( - "go.dedis.ch/dela/crypto/common/json" - "go.dedis.ch/dela/crypto/ed25519" - "golang.org/x/xerrors" - - "go.dedis.ch/dela/serde" -) - -func init() { - ed25519.RegisterPublicKeyFormat(serde.FormatJSON, pubkeyFormat{}) - ed25519.RegisterSignatureFormat(serde.FormatJSON, sigFormat{}) -} - -type pubkeyFormat struct{} - -func (f pubkeyFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - pubkey, ok := msg.(ed25519.PublicKey) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - buffer, err := pubkey.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal point: %v", err) - } - - m := json.PublicKey{ - Algorithm: json.Algorithm{Name: ed25519.Algorithm}, - Data: buffer, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -func (f pubkeyFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := json.PublicKey{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't unmarshal public key: %v", err) - } - - pubkey, err := ed25519.NewPublicKey(m.Data) - if err != nil { - return nil, xerrors.Errorf("couldn't create public key: %v", err) - } - - return pubkey, nil -} - -type sigFormat struct{} - -func (f sigFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - signature, ok := msg.(ed25519.Signature) - if !ok { - return nil, xerrors.Errorf("unsupported message of type '%T'", msg) - } - - data, _ := signature.MarshalBinary() - - m := json.Signature{ - Algorithm: json.Algorithm{Name: ed25519.Algorithm}, - Data: data, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("couldn't marshal: %v", err) - } - - return data, nil -} - -func (f sigFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := json.Signature{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("couldn't unmarshal signature: %v", err) - } - - signature := ed25519.NewSignature(m.Data) - - return signature, nil -} diff --git a/dela/crypto/ed25519/json/json_test.go b/dela/crypto/ed25519/json/json_test.go deleted file mode 100644 index 25324df..0000000 --- a/dela/crypto/ed25519/json/json_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto/ed25519" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" - "go.dedis.ch/kyber/v3" -) - -func TestPubKkeyFormat_Encode(t *testing.T) { - format := pubkeyFormat{} - signer := ed25519.NewSigner() - - msg := signer.GetPublicKey() - - ctx := serde.NewContext(fake.ContextEngine{}) - - data, err := format.Encode(ctx, msg) - require.NoError(t, err) - require.Regexp(t, `{"Name":"CURVE-ED25519","Data":"[^"]+"}`, string(data)) - - _, err = format.Encode(fake.NewBadContext(), msg) - require.EqualError(t, err, fake.Err("couldn't marshal")) - - _, err = format.Encode(fake.NewBadContext(), ed25519.NewPublicKeyFromPoint(badPoint{})) - require.EqualError(t, err, fake.Err("couldn't marshal point")) - - _, err = format.Encode(fake.NewBadContext(), fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") -} - -func TestPubKkeyFormat_Decode(t *testing.T) { - format := pubkeyFormat{} - signer := ed25519.NewSigner() - - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - data, err := signer.GetPublicKey().Serialize(ctx) - require.NoError(t, err) - - pubkey, err := format.Decode(ctx, data) - require.NoError(t, err) - require.True(t, signer.GetPublicKey().Equal(pubkey.(ed25519.PublicKey))) - - _, err = format.Decode(ctx, []byte(`{"Data":[]}`)) - require.EqualError(t, err, "couldn't create public key: couldn't unmarshal point: invalid Ed25519 curve point") - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't unmarshal public key")) -} - -func TestSigFormat_Encode(t *testing.T) { - format := sigFormat{} - ctx := fake.NewContext() - - signer := ed25519.NewSigner() - sign, err := signer.Sign([]byte("hello")) - require.NoError(t, err) - - data, err := format.Encode(ctx, sign) - require.NoError(t, err) - require.Regexp(t, `{"Name":"CURVE-ED25519","Data":"[^"]+"}`, string(data)) - - _, err = format.Encode(fake.NewBadContext(), sign) - require.EqualError(t, err, fake.Err("couldn't marshal")) - - _, err = format.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message of type 'fake.Message'") -} - -func TestSigFormat_Decode(t *testing.T) { - format := sigFormat{} - ctx := fake.NewContextWithFormat(serde.FormatJSON) - - signer := ed25519.NewSigner() - signatue, err := signer.Sign([]byte("hello")) - require.NoError(t, err) - - data, err := signatue.Serialize(ctx) - require.NoError(t, err) - - msg, err := format.Decode(ctx, data) - require.NoError(t, err) - require.True(t, signatue.Equal(msg.(ed25519.Signature))) - - _, err = format.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("couldn't unmarshal signature")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badPoint struct { - kyber.Point -} - -func (p badPoint) MarshalBinary() ([]byte, error) { - return nil, fake.GetError() -} diff --git a/dela/crypto/hash.go b/dela/crypto/hash.go deleted file mode 100644 index 9d8940e..0000000 --- a/dela/crypto/hash.go +++ /dev/null @@ -1,25 +0,0 @@ -// -// Documentation Last Review: 05.10.2020 -// - -package crypto - -import ( - "crypto/sha256" - "hash" -) - -// Sha256Factory is a hash factory that is using SHA256. -// -// - implements crypto.HashFactory -type Sha256Factory struct{} - -// NewSha256Factory returns a new instance of the factory. -func NewSha256Factory() Sha256Factory { - return Sha256Factory{} -} - -// New implements crypto.HashFactory. It returns a new SHA256 instance. -func (f Sha256Factory) New() hash.Hash { - return sha256.New() -} diff --git a/dela/crypto/hash_test.go b/dela/crypto/hash_test.go deleted file mode 100644 index bf17ea1..0000000 --- a/dela/crypto/hash_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package crypto - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestSha256Factory_New(t *testing.T) { - factory := NewSha256Factory() - require.NotNil(t, factory.New()) -} diff --git a/dela/crypto/loader/example_test.go b/dela/crypto/loader/example_test.go deleted file mode 100644 index a50f405..0000000 --- a/dela/crypto/loader/example_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package loader - -import ( - "fmt" - "os" - "path/filepath" -) - -func ExampleLoader_LoadOrCreate() { - dir, err := os.MkdirTemp(os.TempDir(), "example") - if err != nil { - panic("no folder: " + err.Error()) - } - - defer os.RemoveAll(dir) - - loader := NewFileLoader(filepath.Join(dir, "private.key")) - - data, err := loader.LoadOrCreate(exampleGenerator{}) - if err != nil { - panic("loading key failed: " + err.Error()) - } - - fmt.Println(string(data)) - - // Output: a marshaled private key -} - -// Example of dummy generator which generates a key and returns its -// serialization. -// -// - implements loader.Generator -type exampleGenerator struct{} - -// Generate implements loader.Generator. It returns a dummy byte slice for the -// example. -func (exampleGenerator) Generate() ([]byte, error) { - return []byte("a marshaled private key"), nil -} diff --git a/dela/crypto/loader/file.go b/dela/crypto/loader/file.go deleted file mode 100644 index 8bc26b8..0000000 --- a/dela/crypto/loader/file.go +++ /dev/null @@ -1,85 +0,0 @@ -// -// Documentation Last Review: 06.10.2020 -// - -package loader - -import ( - "io" - "os" - - "golang.org/x/xerrors" -) - -// FileLoader is loader that is storing the new keys to a file. -// -// - implements loader.Loader -type fileLoader struct { - path string - - openFn func(path string) (*os.File, error) - openFileFn func(path string, flags int, perms os.FileMode) (*os.File, error) - statFn func(path string) (os.FileInfo, error) -} - -// NewFileLoader creates a new loader that is using the file given in parameter. -func NewFileLoader(path string) Loader { - return fileLoader{ - path: path, - openFn: os.Open, - openFileFn: os.OpenFile, - statFn: os.Stat, - } -} - -// LoadOrCreate implements loader.Loader. It either loads the key from the file -// if it exists, or it generates a new one and stores it in the file. The file -// created has minimal read permission for the current user (0400). -func (l fileLoader) LoadOrCreate(g Generator) ([]byte, error) { - _, err := l.statFn(l.path) - if os.IsNotExist(err) { - data, err := g.Generate() - if err != nil { - return nil, xerrors.Errorf("generator failed: %v", err) - } - - file, err := l.openFileFn(l.path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0400) - if err != nil { - return nil, xerrors.Errorf("while creating file: %v", err) - } - - defer file.Close() - - _, err = file.Write(data) - if err != nil { - return nil, xerrors.Errorf("while writing: %v", err) - } - - return data, nil - } - - data, err := l.Load() - if err != nil { - return nil, xerrors.Errorf("failed to load file: %v", err) - } - - return data, nil -} - -// Load implements loader.Loader. It loads the key from the file if it exists, -// otherwise it returns an error. -func (l fileLoader) Load() ([]byte, error) { - file, err := l.openFn(l.path) - if err != nil { - return nil, xerrors.Errorf("while opening file: %v", err) - } - - defer file.Close() - - data, err := io.ReadAll(file) - if err != nil { - return nil, xerrors.Errorf("while reading file: %v", err) - } - - return data, nil -} diff --git a/dela/crypto/loader/file_test.go b/dela/crypto/loader/file_test.go deleted file mode 100644 index 9e0c5dc..0000000 --- a/dela/crypto/loader/file_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package loader - -import ( - "os" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestFileLoader_LoadOrCreate(t *testing.T) { - file, err := os.CreateTemp(os.TempDir(), "dela") - require.NoError(t, err) - - file.Close() - - defer os.Remove(file.Name()) - require.NoError(t, os.Remove(file.Name())) - - generator := fakeGenerator{ - calls: fake.NewCall(), - } - - loader := NewFileLoader(file.Name()).(fileLoader) - - // Generate.. - data, err := loader.LoadOrCreate(generator) - require.NoError(t, err) - require.Equal(t, []byte{1, 2, 3}, data) - require.Equal(t, 1, generator.calls.Len()) - - // Read from the file.. - data, err = loader.LoadOrCreate(generator) - require.NoError(t, err) - require.Equal(t, []byte{1, 2, 3}, data) - require.Equal(t, 1, generator.calls.Len()) -} - -func TestFileLoader_BadGenerator_LoadOrCreate(t *testing.T) { - loader := fileLoader{ - path: "", - statFn: statNotExists, - } - - _, err := loader.LoadOrCreate(fakeGenerator{err: fake.GetError()}) - require.EqualError(t, err, fake.Err("generator failed")) -} - -func TestFileLoader_FailCreateFile_LoadOrCreate(t *testing.T) { - loader := fileLoader{ - path: "", - statFn: statNotExists, - openFileFn: func(path string, flags int, perms os.FileMode) (*os.File, error) { - return nil, fake.GetError() - }, - } - - _, err := loader.LoadOrCreate(fakeGenerator{}) - require.EqualError(t, err, fake.Err("while creating file")) -} - -func TestFileLoader_FailWriteFile_LoadOrCreate(t *testing.T) { - loader := fileLoader{ - path: "", - statFn: statNotExists, - openFileFn: func(path string, flags int, perms os.FileMode) (*os.File, error) { - return os.NewFile(0, ""), nil - }, - } - - _, err := loader.LoadOrCreate(fakeGenerator{}) - require.Error(t, err) - require.Contains(t, err.Error(), "while writing: write : ") -} - -func TestFileLoader_FailOpenFile_LoadOrCreate(t *testing.T) { - loader := fileLoader{ - path: "", - statFn: statExists, - openFn: func(path string) (*os.File, error) { - return nil, fake.GetError() - }, - } - - _, err := loader.LoadOrCreate(fakeGenerator{}) - require.EqualError(t, err, fake.Err("failed to load file: while opening file")) -} - -func TestFileLoader_FailReadFile_LoadOrCreate(t *testing.T) { - loader := fileLoader{ - path: "", - statFn: statExists, - openFn: func(path string) (*os.File, error) { - return os.Open(os.TempDir()) - }, - } - - _, err := loader.LoadOrCreate(fakeGenerator{}) - require.Error(t, err) - require.Contains(t, err.Error(), "while reading file: ") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func statNotExists(path string) (os.FileInfo, error) { - return nil, os.ErrNotExist -} - -func statExists(path string) (os.FileInfo, error) { - return nil, nil -} - -type fakeGenerator struct { - calls *fake.Call - err error -} - -func (g fakeGenerator) Generate() ([]byte, error) { - g.calls.Add("Generate") - - return []byte{1, 2, 3}, g.err -} diff --git a/dela/crypto/loader/mod.go b/dela/crypto/loader/mod.go deleted file mode 100644 index 93f93a6..0000000 --- a/dela/crypto/loader/mod.go +++ /dev/null @@ -1,25 +0,0 @@ -// Package loader defines an abstraction to store a private, or a public, key in -// a storage. -// -// When the key does not exist, it will generate a new one using a generator -// implemented by the caller and stores it for the next time. -// -// Documentation Last Review: 06.10.2020 -// -package loader - -// Generator is the interface to implement to generate a key. -type Generator interface { - Generate() ([]byte, error) -} - -// Loader is an abstraction to load a key from a storage. It allows for instance -// to load a private key from the disk, or generate it if it doesn't exist. -type Loader interface { - // LoadOrCreate tries to load the key and returns it if found, otherwise it - // generates a new one using the generator and stores it. - LoadOrCreate(Generator) ([]byte, error) - - // Load loads the key and returns an error if it doesn't find it. - Load() ([]byte, error) -} diff --git a/dela/crypto/mod.go b/dela/crypto/mod.go deleted file mode 100644 index ee31839..0000000 --- a/dela/crypto/mod.go +++ /dev/null @@ -1,161 +0,0 @@ -// Package crypto defines cryptographic primitives shared by the different -// modules of Dela. -// -// It defines the abstraction of a public and a private key and their -// combination where a private key can create a signature for a given message, -// while the public key can verify the association of those two elements. -// -// A signer is a unique entity that possesses both a public and an associated -// private key. It is assumed that the combination is a unique identity. This -// abstraction hides the logic of a private key. -// -// For the aggregation of those primitives, a verifier abstraction is defined to -// provide the primitives to verify using the aggregate instead of a single one. -// -// Documentation Last Review: 05.10.2020 -// -package crypto - -import ( - "encoding" - "hash" - - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// HashFactory is an interface to produce a hash digest. -type HashFactory interface { - New() hash.Hash -} - -// RandGenerator is an interface to generate random values with a fully seeded -// random source. -type RandGenerator interface { - // Read populates the buffer with random bytes up to a size that is - // returned alongside an error if something goes wrong. - Read([]byte) (int, error) -} - -// PublicKey is a public identity that can be used to verify a signature. -type PublicKey interface { - encoding.BinaryMarshaler - encoding.TextMarshaler - serde.Message - - // Verify returns nil if the signature matches the message, otherwise an - // error is returned. - Verify(msg []byte, signature Signature) error - - // Equal returns true when both objects are similar. - Equal(other interface{}) bool -} - -// PublicKeyFactory is a factory to decode public keys. -type PublicKeyFactory interface { - serde.Factory - - // PublicKeyOf populates the public key associated to the data if - // appropriate, otherwise it returns an error. - PublicKeyOf(ctx serde.Context, data []byte) (PublicKey, error) - - // FromBytes returns the public key associated to the data if appropriate, - // otherwise it returns an error. - FromBytes(data []byte) (PublicKey, error) -} - -// PublicKeyIterator is an iterator over the list of public keys of a -// collective authority. -type PublicKeyIterator interface { - // Seek moves the iterator to a specific index. - Seek(int) - - // HasNext returns true if a public key is available, false if the iterator - // is exhausted. - HasNext() bool - - // GetNext returns the next public key in case HasNext returns true, - // otherwise no assumption can be done. - GetNext() PublicKey -} - -// Signature is a verifiable element for a unique message. -type Signature interface { - encoding.BinaryMarshaler - serde.Message - - // Equal returns true if both objects are similar. - Equal(other Signature) bool -} - -// SignatureFactory is a factory to decode signatures. -type SignatureFactory interface { - serde.Factory - - // SignatureOf returns a signature associated with the data if appropriate, - // otherwise it returns an error. - SignatureOf(ctx serde.Context, data []byte) (Signature, error) -} - -// Verifier provides the primitive to verify a signature w.r.t. a message. -type Verifier interface { - // Verify returns nil if the signature matches the message for the public - // key internal to the verifier. - Verify(msg []byte, signature Signature) error -} - -// VerifierFactory provides the primitives to create a verifier. -type VerifierFactory interface { - // FromAuthority returns a verifier that will use the authority to generate - // a public key. - FromAuthority(ca CollectiveAuthority) (Verifier, error) - - // FromArray returns a verifier that will use the list of public keys to - // generate one. - FromArray(keys []PublicKey) (Verifier, error) -} - -// Signer provides the primitives to sign and verify signatures. -type Signer interface { - // GetPublicKeyFactory returns a factory that can deserialize public keys of - // the same type as the signer. - GetPublicKeyFactory() PublicKeyFactory - - // GetSignatureFactory returns a factory that can deserialize signatures of - // the same type as the signer. - GetSignatureFactory() SignatureFactory - - // GetPublicKey returns the public key of the signer. - GetPublicKey() PublicKey - - // Sign returns a signature that will match the message for the signer - // public key. - Sign(msg []byte) (Signature, error) -} - -// AggregateSigner offers the same primitives as the Signer interface but -// also includes a primitive to aggregate signatures into one. -type AggregateSigner interface { - Signer - - // GetVerifierFactory returns the factory that can deserialize verifiers of - // the same type as the signer. - GetVerifierFactory() VerifierFactory - - // Aggregate returns the aggregate signature of the ones in parameter. - Aggregate(signatures ...Signature) (Signature, error) -} - -// CollectiveAuthority is a set of participants with each of them being -// associated to a Mino address and a public key. -type CollectiveAuthority interface { - mino.Players - - // GetPublicKey returns the public key and its index of the corresponding - // address if any matches. An index < 0 means no correspondance found. - GetPublicKey(addr mino.Address) (PublicKey, int) - - // PublicKeyIterator creates a public key iterator that iterates over the - // list of public keys and is consistent with the address iterator. - PublicKeyIterator() PublicKeyIterator -} diff --git a/dela/crypto/rand.go b/dela/crypto/rand.go deleted file mode 100644 index 7493b80..0000000 --- a/dela/crypto/rand.go +++ /dev/null @@ -1,18 +0,0 @@ -// -// Documentation Last Review: 05.10.2020 -// - -package crypto - -import "crypto/rand" - -// CryptographicRandomGenerator is cryptographically secure random generator. -// -// - implements crypto.RandGenerator -type CryptographicRandomGenerator struct{} - -// Read implements crypto.RandGenerator. It fills the given buffer at its -// capacity as long as no error occurred. -func (crg CryptographicRandomGenerator) Read(buffer []byte) (int, error) { - return rand.Read(buffer) -} diff --git a/dela/crypto/rand_test.go b/dela/crypto/rand_test.go deleted file mode 100644 index 596ceac..0000000 --- a/dela/crypto/rand_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package crypto - -import ( - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" -) - -func TestCryptographicRandomGenerator_Read(t *testing.T) { - rand := CryptographicRandomGenerator{} - - f := func(buffer []byte) bool { - n, err := rand.Read(buffer) - require.NoError(t, err) - require.Equal(t, len(buffer), n) - - return true - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} diff --git a/dela/demo.sh b/dela/demo.sh deleted file mode 100755 index 2bfc1f4..0000000 --- a/dela/demo.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -set -e - -export TEMPDIR=$(mktemp -d /tmp/dkgcli.XXXXXXXXXXXXX) -rm_tempdir () { - rm -rf "$TEMPDIR" -} -trap rm_tempdir EXIT - -n=16 -t=9 - -for i in $(seq $n); do - tmux new-window -d "LLVL=info dkgcli --config $TEMPDIR/node$i start --listen tcp://127.0.0.1:$((2000+i)); read" -done - -sleep 3 - -# Exchange certificates -for i in $(seq 2 $n); do - dkgcli --config $TEMPDIR/node$i minogrpc join --address //127.0.0.1:2001 $(dkgcli --config $TEMPDIR/node1 minogrpc token) -done - -# Initialize DKG on each node. Do that in a 4th session. -for i in $(seq $n); do - dkgcli --config $TEMPDIR/node$i dkg listen -done - -# Do the setup in one of the node: -cmd=(dkgcli --config $TEMPDIR/node1 dkg setup --threshold $t) -for i in $(seq $n); do - cmd+=(--authority $(cat $TEMPDIR/node$i/dkgauthority)) -done -"${cmd[@]}" - -verbs=(buy sell) -animals=(penguin lion monkey cat dog kangaroo) -formats=(jpeg png gif) -coins=(poptokens byzcoins efranks) - -function pick { - shift $((RANDOM%$#)) - echo "$1" -} - - -echo ✅ generated committee pubkey: $(dkgcli --config $TEMPDIR/node1 dkg get-public-key) - -for ((i = 0; i < 12; i++)); do - sleep 1 - label=$(printf "%02x" $i) - echo ⌚ block $i decryption key: $(dkgcli --config $TEMPDIR/node1 dkg sign -message $label) - case $i in - ( 5 ) - msg="$(pick ${verbs[@]}) a $(pick ${animals[@]}) $(pick ${formats[@]}) in exchange for $(pick ${coins[@]})" - ct=$(dkgcli --config $TEMPDIR/node1 dkg encrypt -label 0a -message $(xxd -p -c0 <<<$msg)) - echo 🔐 encrypted message for block 10: $ct - ;; - ( 10 ) - echo 🔓 decrypted message: $(dkgcli --config $TEMPDIR/node1 dkg decrypt -label $label -ciphertext $ct) - echo 🔓 decrypted message: $(dkgcli --config $TEMPDIR/node1 dkg decrypt -label $label -ciphertext $ct | xxd -r -p) - ;; - esac -done - -echo ⚠️ NOT FINANCIAL ADVICE⚠️ - -read diff --git a/dela/dkg/ibe/ibe.go b/dela/dkg/ibe/ibe.go deleted file mode 100644 index 29887f1..0000000 --- a/dela/dkg/ibe/ibe.go +++ /dev/null @@ -1,113 +0,0 @@ -// This code is (c) by DEDIS/EPFL 2017 under the MPL v2 or later version. - -package ibe - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/sha512" - "errors" - "fmt" - - kyber "go.dedis.ch/kyber/v3" - "go.dedis.ch/kyber/v3/pairing" - "go.dedis.ch/kyber/v3/util/random" - - "golang.org/x/crypto/hkdf" -) - -// Based on https://github.com/drand/kyber/blob/master/encrypt/ibe/ibe.go -// with G1 and G2 flipped and AES-CTR instead of Blake2s - -type CiphertextCPA struct { - U kyber.Point - V []byte -} - -const pointMarshalledSize = 128 - -func (ct *CiphertextCPA) Serialize(suite pairing.Suite) ([]byte, error) { - marshalledU, err := ct.U.MarshalBinary() - if err != nil { - return nil, err - } - if len(marshalledU) != pointMarshalledSize { - return nil, fmt.Errorf("unexpected point marshalled size: %v", len(marshalledU)) - } - buf := make([]byte, 0, pointMarshalledSize+len(ct.V)) - buf = append(buf, marshalledU...) - buf = append(buf, ct.V...) - return buf, nil -} -func (ct *CiphertextCPA) Deserialize(suite pairing.Suite, data []byte) error { - marshalledU := data[:pointMarshalledSize] - U := suite.G2().Point() - U.UnmarshalBinary(marshalledU) - ct.U = U - ct.V = bytes.Clone(data[pointMarshalledSize:]) - return nil -} - -type hashablePoint interface { - Hash([]byte) kyber.Point -} - -func gtToStream(GidT kyber.Point) (cipher.Stream, error) { - seed, err := GidT.MarshalBinary() - if err != nil { - return nil, err - } - key := make([]byte, 32) - kdf := hkdf.New(sha512.New, seed, nil, nil) - kdf.Read(key) - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - iv := make([]byte, 16) //NOTE: this is allowed because the key is used only once - return cipher.NewCTR(block, iv), nil -} - -func DeriveEncryptionKeyOnG2(suite pairing.Suite, X kyber.Point, label []byte) (kyber.Point, error) { - hashable, ok := suite.G1().Point().(hashablePoint) - if !ok { - return nil, errors.New("point needs to implement hashablePoint") - } - P := suite.Pair(hashable.Hash(label), X) - return P, nil -} - -func EncryptCPAonG2(suite pairing.Suite, P kyber.Point, msg []byte) (*CiphertextCPA, error) { - r := suite.G2().Scalar().Pick(random.New()) - U := suite.G2().Point().Mul(r, suite.G2().Point().Base()) - xof, err := gtToStream(suite.GT().Point().Mul(r, P)) - if err != nil { - return nil, err - } - V := make([]byte, len(msg)) - xof.XORKeyStream(V, msg) - - return &CiphertextCPA{U, V}, nil -} - -func DecryptCPAonG2(suite pairing.Suite, decKey kyber.Point, ct *CiphertextCPA) ([]byte, error) { - xof, err := gtToStream(suite.Pair(decKey, ct.U)) - if err != nil { - return nil, err - } - msg := make([]byte, len(ct.V)) - xof.XORKeyStream(msg, ct.V) - return msg, nil -} - -func VerifyIdentityOnG2(suite pairing.Suite, pk, identity kyber.Point, label []byte) (bool, error) { - hashable, ok := suite.G1().Point().(hashablePoint) - if !ok { - return false, errors.New("point needs to implement hashablePoint") - } - - lhs := suite.Pair(identity, suite.G2().Point().Base()) - rhs := suite.Pair(hashable.Hash(label), pk) - return lhs.Equal(rhs), nil -} diff --git a/dela/dkg/pedersen_bn256/dkgcli/README.md b/dela/dkg/pedersen_bn256/dkgcli/README.md deleted file mode 100644 index c7268d4..0000000 --- a/dela/dkg/pedersen_bn256/dkgcli/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# DKGCLI - -DKGCLI is a CLI tool for using the DKG protocol. Here is a complete scenario: - -```sh -# Install the CLI -go install . - -# Run 3 nodes. Do that in 3 different sessions -LLVL=info dkgcli --config /tmp/node1 start --routing tree --listen tcp://127.0.0.1:2001 -LLVL=info dkgcli --config /tmp/node2 start --routing tree --listen tcp://127.0.0.1:2002 -LLVL=info dkgcli --config /tmp/node3 start --routing tree --listen tcp://127.0.0.1:2003 - -# Exchange certificates -dkgcli --config /tmp/node2 minogrpc join --address //127.0.0.1:2001 $(dkgcli --config /tmp/node1 minogrpc token) -dkgcli --config /tmp/node3 minogrpc join --address //127.0.0.1:2001 $(dkgcli --config /tmp/node1 minogrpc token) - -# Initialize DKG on each node. Do that in a 4th session. -dkgcli --config /tmp/node1 dkg listen -dkgcli --config /tmp/node2 dkg listen -dkgcli --config /tmp/node3 dkg listen - -# Do the setup in one of the node: -dkgcli --config /tmp/node1 dkg setup \ - --authority $(cat /tmp/node1/dkgauthority) \ - --authority $(cat /tmp/node2/dkgauthority) \ - --authority $(cat /tmp/node3/dkgauthority) - -# Encrypt a message: -dkgcli --config /tmp/node2 dkg encrypt --message deadbeef - -# Decrypt a message -dkgcli --config /tmp/node3 dkg decrypt --encrypted <...> \ No newline at end of file diff --git a/dela/dkg/pedersen_bn256/dkgcli/dockerfile b/dela/dkg/pedersen_bn256/dkgcli/dockerfile deleted file mode 100644 index ceee9ac..0000000 --- a/dela/dkg/pedersen_bn256/dkgcli/dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Defines a docker container to use dkgcli -# -# Build from the dela folder: -# docker build -t dela/dkg:latest -f dkg/pedersen/dkgcli/dockerfile . -# Run with: -# docker run --rm -e LLVL=info dela/dkg -# -FROM golang:1.14 AS build -ADD . /dela -WORKDIR /dela -RUN CGO_ENABLED=0 GOOS=linux go build -o dkgcli ./dkg/pedersen/dkgcli - -FROM alpine:latest -COPY --from=build /dela/dkgcli /usr/local/bin - -ENTRYPOINT [ "dkgcli" ] -CMD ["--config", "/config", "start", "--routing", "tree", "--listen", "tcp://0.0.0.0:2000"] \ No newline at end of file diff --git a/dela/docker-compose.yml b/dela/docker-compose.yml deleted file mode 100644 index a8f32cd..0000000 --- a/dela/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - demo: - build: . - volumes: - - ./:/app - stdin_open: true # docker run -i - tty: true # docker run -t - command: tmux new-session ./demo.sh diff --git a/dela/docs/.nojekyll b/dela/docs/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/dela/docs/README.md b/dela/docs/README.md deleted file mode 100644 index 787566e..0000000 --- a/dela/docs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -To manually browse the doc start with [sidebar.md](sidebar.md). Otherwise you -can still go to -[https://dedis.github.io/dela/](https://dedis.github.io/dela/). \ No newline at end of file diff --git a/dela/docs/architecture.md b/dela/docs/architecture.md deleted file mode 100644 index 8d2c353..0000000 --- a/dela/docs/architecture.md +++ /dev/null @@ -1,37 +0,0 @@ -# Architecture - -Dela has been built with modularization and testability in mind. As such, the -system is made of multiple interoperable modules based on a set of well-defined -abstractions. - -## Stacks - -The stacks display the role of each module and its relation to others. Each -module can rely on zero or more of the modules below it. From the client's -perspective the stack is simpler since it doesn't need to know about the complex -ordering mechanism. - -![stacks](assets/stacks.png) - -## Execution flow - -The execution flow displays how the system uses the modules in order to fulfill -its goals. Since a client and a node have different goals, we display the -perspective from those two separately. - -![flow](assets/dela-flow.png) - -## CoSiPBFT flow - -CoSiPBFT is the actual implementation of the ordering module. This is the core -part that orchestrates the addition of new blocks based on Collective -Signatures. This diagrams helps to understand how the leader and its followers -work together to verify and include new blocks. - -![cosi flow](assets/cosipbft.png) - -## Package dependencies - -Here is a simplified diagram of package dependencies, for reference: - -![package dep](assets/packages.png) \ No newline at end of file diff --git a/dela/docs/assets/cosipbft.png b/dela/docs/assets/cosipbft.png deleted file mode 100644 index 11afbde..0000000 Binary files a/dela/docs/assets/cosipbft.png and /dev/null differ diff --git a/dela/docs/assets/dela-flow.png b/dela/docs/assets/dela-flow.png deleted file mode 100644 index 84806b4..0000000 Binary files a/dela/docs/assets/dela-flow.png and /dev/null differ diff --git a/dela/docs/assets/infograph.png b/dela/docs/assets/infograph.png deleted file mode 100644 index 97dc907..0000000 Binary files a/dela/docs/assets/infograph.png and /dev/null differ diff --git a/dela/docs/assets/logo.png b/dela/docs/assets/logo.png deleted file mode 100644 index cf81b62..0000000 Binary files a/dela/docs/assets/logo.png and /dev/null differ diff --git a/dela/docs/assets/logotype.png b/dela/docs/assets/logotype.png deleted file mode 100644 index 703edc9..0000000 Binary files a/dela/docs/assets/logotype.png and /dev/null differ diff --git a/dela/docs/assets/modules.png b/dela/docs/assets/modules.png deleted file mode 100644 index be1b4f2..0000000 Binary files a/dela/docs/assets/modules.png and /dev/null differ diff --git a/dela/docs/assets/overview.png b/dela/docs/assets/overview.png deleted file mode 100644 index 40fb120..0000000 Binary files a/dela/docs/assets/overview.png and /dev/null differ diff --git a/dela/docs/assets/packages.png b/dela/docs/assets/packages.png deleted file mode 100644 index f214c9a..0000000 Binary files a/dela/docs/assets/packages.png and /dev/null differ diff --git a/dela/docs/assets/serde.png b/dela/docs/assets/serde.png deleted file mode 100644 index 9c08223..0000000 Binary files a/dela/docs/assets/serde.png and /dev/null differ diff --git a/dela/docs/assets/stacks.png b/dela/docs/assets/stacks.png deleted file mode 100644 index 403f767..0000000 Binary files a/dela/docs/assets/stacks.png and /dev/null differ diff --git a/dela/docs/cosipbft.md b/dela/docs/cosipbft.md deleted file mode 100644 index bede15b..0000000 --- a/dela/docs/cosipbft.md +++ /dev/null @@ -1,89 +0,0 @@ -# Collective Signing Practical Byzantine Fault Tolerance - -This section describes the implementation of an ordering service based on PBFT, -and using collective signatures for the implementation [1]. - -## Chain - -At the very beginning of the chain, a genesis block defines the basic settings -like the roster and the authorizations (i.e. to update the roster). The special -block is not counted as a block and is formatted differently, therefore the -first block will have an index of zero. - -The chain is not exactly an ordered list of blocks, but an ordered list of -**links**. Each link has a reference to the block it is pointing at, and it can -be reduced to be sent over a communication channel with the minimal piece of -information to prove its correctness. - -## Leader - -The consensus will assign a leader each round that will be responsible for -orchestrating the protocols. As of now, it is always the first participant that -is assigned the role of leader but it should be changed to a more random and -dynamic approach to reduce the chance of transactions being ignored. - -There are plenty of research papers that offer solutions for this problem, but a -quick change could be to use the previous block signature to create a seed and -shuffle the list of participants. - -## PBFT State Machine - -The service is designed so that the network messages and the handlers are -implemented independently from the actual logic of PBFT. The subpackage _pbft_ -contains the definition and the implementation of the state machine which has -five states: -- **None** (N): default state, only at the very beginning -- **Initial** (I): indicate the beginning of a round -- **Prepare** (P): indicate a candidate has been accepted -- **Commit** (C): indicate a threshold of participants have accepted the candidate -- **ViewChange** (V): indicate the round has expired and leader is probably - faulty - -The following schema shows the transitions allowed by the state machine: - -``` -PBFT State Machine - - ┌────────────── 3 ──────────────┐ - │ │ - v │ - ┌───┐ ┌───┐ ┌─┴─┐ -┌─>│ I ├───── 4 ─────>│ P │── 2 ──>│ C │<──┐ -│ └─┬─┘ └─┬─┘ └─┬─┘ │ -│ │ ^ │ │ │ -│ │ ┌───┐ │ 5 │ │ -│ │ │ N ├── 1 ──┘ │ │ │ -│ │ └─┬─┘ v │ │ -│ │ │ ┌─────┐ │ │ -│ │ └─ 5 ─>│ │ │ │ -│ │ │ V │<─── 5 ────┘ │ -│ └───── 5 ────>│ │ │ -│ └─┬─┬─┘ │ -│ │ │ │ -└───────── 6 ────────┘ └──────── 6 ────────┘ -``` - -1. The state machine is instantiated with an empty state (_None_) and waits for - a candidate to transition to _Prepare_. -2. A _Prepare_ state will transition to the _Commit_ state if a valid signature - is provided. -3. After a round is finalized, the state machine gets back to _Initial_. -4. the _Initial_ state waits for a new candidate to transition to the _Prepare_ - state. -5. Any of the state can transition to _ViewChange_ (other than (V) itself) when - the round expires. -6. After a view change is accepted, it either transition to _Initial_ if no - candidate has been comitted, otherwise to _Commit_. - -The point (6.) is explained because we may be in a _ViewChange_ state while a -threshold of participants accept a new block. Furthermore, reaching a threshold -is a prerequisite to a candidate being committed on any participant. As such, if -a candidate has been committed on at least one of the participants, we know that -the signature exists. The system will have to finalize this candidate, even if a -new leader tries a different one. A different candidate will be refused anyway -by the participants committed to the other. - -## Papers - -[1] Enhancing Bitcoin Security and Performance with Strong Consistency via -Collective Signing (2016) diff --git a/dela/docs/dela.md b/dela/docs/dela.md deleted file mode 100644 index 53448fa..0000000 --- a/dela/docs/dela.md +++ /dev/null @@ -1,126 +0,0 @@ -![Infography](assets/infograph.png) - -# Dela - -Dela stands for DEDIS Ledger Architecture. It is both a set of abstractions and -an implementation of a distributed ledger architecture. - -Dela has 2 main purposes: - -- Provide a modular, global-purpose, and universal framework that describes a - minimal and extended set of abstractions for a distributed ledger - architecture. -- Provide multiple modules implementations that can be combined to run a - distributed ledger. - -With Dela you can: - -- Learn the architecture of a distributed ledger -- Run your blockchain / distributed ledger -- Implement and test your new idea that will revolutionize the blockchain world - by adding your new module's implementation to the Dela ecosystem - -## Distributed ledger - -A distributed ledger aims to solve the following problem: how can a set of -entities agree on a state without any central entity? In other words: how can a -group of computers (or *nodes*) cooperate to maintain a database? This problem -is not new and has been solved with, for example, distributed databases. With -distributed databases, multiple replicas of a database are kept in different -locations and constantly maintained to have the same view on the data. If a -replica fails or stops responding, another copy is ready to take the relay. -Distributed ledgers are not that different from distributed databases except -with one major point: parties - or *nodes* - don't trust each other. Where a -distributed database can use a set of trusted *cooperative* nodes, a distributed -ledger is rather a set of untrusted *foreigners* entities that must find a way -to agree on a state. Distributed databases can be compared to a set of friends -that keep collectively the amounts that everybody owes to each other, while a -distributed ledger would be doing the same but with everyone in your city (do -you trust everyone in your city to tell you how much you owe to your friend?). - -A notorious application of a distributed ledger is Bitcoin, which uses a -blockchain data structure and a mining protocol (*Proof-of-Work*) to maintain a -consistent and decentralized list of coin transactions. This global list of -transactions (or *ledger*) is what allows the existence of a such decentralized -crypto-currency. - -## Architecture - -Dela has 6 core modules that you will find in `core/`: - -- access -- execution -- ordering -- store -- txn (and its sub-module txn/pool) -- validation - -The following diagram simplifies a lot the system to offer a general and -informal overview of the components' interactions, from both the client's side -and the node's side: - -![global overview](assets/overview.png) - -More documentations and diagrams can be found in [architecture](architecture.md). - -## Terminologies - -- **actor** - An actor is a player of a protocol or a module. It is intended to - be accessible only after the initialization and it provides the primitives to - start the underlying protocol logic. - -- **arc** - Arc stands for Access Rights Control. It is the abstraction that - controls the access to the instances. - -- **blockchain** - A blockchain is a distributed and immutable storage - abstraction. A well-defined threshold of participants work together to reach a - consensus on every block. - -- **cosi** - CoSi stands for *Collective Signature*. It represents an aggregate - of signature from multiple key pairs and it can be verified by the - corresponding aggregate of public keys. - -- **fingerprint** - Fingerprint defines a digest commonly produced by a hash - algorithm that can be used to verify the integrity of some data. One example - is the inventory page integrity to prove which instances are stored. - -- **governance** - Governance is a black box that gives the ability to act on - the participants of a consensus. - -- **instance** - An instance is the smallest unit of storage in a ledger. It is - identified by a unique key and stores a generic piece of data. - -- **inventory** - An inventory is the storage abstraction of a ledger. The - ledger evolves alongside with the blocks and that is represented by pages in - an inventory where the index matches the block index. - -- **ledger** - A ledger is a book of records of transactions. Similarly, a - public distributed ledger can be implemented on top of a blockchain. - -- **message** - A message is a serialized data structure that can be transmitted - over a physical channel and decoded on the other side. - -- **mino** - Mino stands for *Minimalist Network Overlay*, it is the abstraction - that defines how to register and use RPCs over a distributed set of nodes. - -- **node** - A node is a server participating in a protocol. - -- **payload** - A payload is the data that a block will store. The blockchain - implementation does not know the data structure thus requires a - *PayloadProcessor* that will validate during the consensus. - -- **proof** - A proof is a cryptographic tool that can provide integrity to a - piece of data. - -- **protobuf** - https://developers.google.com/protocol-buffers/ - -- **roster** - A roster is a set of participants to a protocol. - -- **RPC** - RPC stands for *Remote Procedure Call*. It represents a procedure - that an authorized external actor can call to get a specific result. - -- **skipchain** - A skipchain is a specific implementation of the blockchain - that is using collective signings to create shortcuts between blocks. - -- **task** - A task is an order of execution that is stored inside a - transaction. It will define how the transaction will update the inventory. diff --git a/dela/docs/diagrams/dela.drawio b/dela/docs/diagrams/dela.drawio deleted file mode 100644 index cfd1e69..0000000 --- a/dela/docs/diagrams/dela.drawio +++ /dev/null @@ -1 +0,0 @@ -7R1pc6NG9te4ytkqq6CbQ/poe47dTZx4Y9dMJl9SWEISMaIVhK/8+u3mEvR7QkhuoO3MfPCIBhp499WvT+jl6vlz7K2XV2zmhyfEmD2f0A8nhJim4fD/xMhLPmIYNBtZxMEsH9sO3AR/+8WF+ehDMPM3tQsTxsIkWNcHpyyK/GlSG/PimD3VL5uzsP7UtbfwwcDN1Avh6NdgliyLD3Mm2xP/9oPFMn/0mLjZiZVXXJx/yWbpzdhTZYh+PKGXMWNJ9mv1fOmHAnoFXLL7Pu04W75Y7EdJmxusx9s/noJfL15++/wrDT9+O7u6J2c5ejbJS/HB/ox/f37I4mTJFizywo/b0YuYPUQzX8xq8KPtNT8xtuaDJh/800+SlxyZ3kPC+NAyWYX5Wf7C8ctv+f3pwTdxMLKLww/P1ZMfXvKj6UP8mD5XTAI/P4fIhj3EU7/hm0lORl688JOG6+zsOgGQygNy4H722crnL8cviP3QS4LHOsF4Od0tyuu2qOE/cuwcgKlxNu+jFz6UT6Ln4jue+Z90xLjy7v3b59MfUKT+5N1x5qwhwguDRcR/TzkM/ZgPPPpxEnDiP89PrILZLMO5vwn+9u7S+QQq1iyIkvQT7YsT+wOKjyaCEw/yn08QDs4fUmOSGrjzu86MkWmZOY5y6ZJP3hoh+eTX4mMql7D5fMMpQ8ZY+Q7HI9E9gt1qVD8Y73XPblQrdjNNwG8k47fz2ew0edaZx1xVPGaMjAmhNRYzdWcxk37nsd3AIXoxmQWYjGZM9tVLpkuNWaygMhU8Zo4JqfGYrTuPEYC282nCYoAvbvKuxc+HVZhdsEVNisZrtgmSgAkU3bEkYSsEd4lguiq2d3IJwMVOmBcmeQ5vOs6Pn7YWPs2HlhXb3jE64gMK4HlySU7OLwJBz3OPsz4/vOBf7oT8VS7uOKSdRZLCIh8pBm45FbCwGOYvc7e9VEKOWpCaYxmmNoCpaSJAtbsCqg2AerPyYsEgl4yLXW+a9AwSMoFk1i9ITMi4xxLaL/HMj4NooQGpWQj79gxXF8C1dM6i0jv77CVLP97hnb1v+6Tw0/YbKI5WBgqxviOr4cKxXsiCEj932fhIMPMS/zRlx8sUod7Cv419X2c/rqA+JUYmNaya1NTJkYu+/H4b/RVZP3rh4g+Lnn3588ovqUsL3tuy27cat/XBeyh0ChO2f97DkTUBvOdkvDdlq1WQFCwnPo1znR5M10h2KpiOllZwznSlpXAs2xUv5NZ5eUQm9Sk6jK9AKfsGbUhqa2ebO8rgOpgT6Ji6QbX8BhBh8uO4tMw/PvvTU5FHaWEa9Kp/noOkon740bfKma3yEQeF7hlAZ41b2ovD6azG91bAcV8yA1OEsobnOuoOznUwoGVnXMdB4q1EQC8FUux7M/6ApzhI/PIMDrK3wHqtWAg3Ooy2LET0MvvM4RG1wz8m+xzkAsNmBb/Fba9DcaPw22vZm3pZ9ohXbeUa9ACtqYltv0u2HuNQO2qLD4rURN1LPzMp9yLqk3SY1zGUaURhUj1oohCt4RUiUQbYm4TFvgZAtYcHKvSYXCnmcBey6f3wKqq0JdyqMWE0apqKHU8GDD41XqhORaW3nsex91K5IJfzO2WlReu1KQWpfdpxPXXHTdfzH9kbqJWpMDcECTKanYtaVX4UsUgQ4MzbLEsCrJCSGL/2Ei40onSEGNydvdgkMbsv61NJExHsRVmFne0Gbn6lmqNSCt5xJSmREWt+VxNOpYlMOUmfETOYSBl6oRN5GQZ+BJPLXM4mdWRmaLtkoaiMKDA/D8JQGmpvKT0tuSd1s/ZSNn+KvTUQXwo0QVkHUYAcyb9aCOmQzhQBDD7/zGYwyPxuMEDpWC8MUBhm2wZ3JSzUoYHBS3G5gDup02u5tqACLReBltUZtKA1WA0gaQcvG7H0jF4BBuNJWYR7aFCNnf2khWnR7iAFKyorntjQ4JrI+QEEXONewXWUcVY17cO79EyhD96A6QWIlppH2l7mZLJnpo6NLwoV/6vRh4UFFYR9G7lVG8oARZzKKENm4Y4po3jcd8Y+Cn1Ar016Rh+0J2+foQKDBr3KoLII5GyymI4yBSh5qwQJovVquFtI6flBfKI9U8jWLJBEb0emQdvuQJmGpsrKM4fGE/ev6CPvlBSIIZUPWX2TAkzKnU+n/mbz9kUkVGJwIQlGJN2JSJhm+MAJMQWzJ7ILxjxmKzHLas3i5D2ggNQzmyYWAOhXTUE/7Xy9BpAeIDiChd4IFhyhCmBzfXM1IdcPxu3X4Fv09ffHxX8/LM4mUAn02cLhsCqzY8pqDm/7gMKJQK2FXjfYogb0bQoTHi4tehJLZHXp9dBEmUp6PVhusVhYixULOK70rYZSslroVWzlasVWhTTWoiqAaFLd+yr8Dta+A38bGEA4DX1v5sc/wBqbfJXY5iWaai1PC6JVIlCJMdGpXQ7+wYiDO5RANesC1R1eorYNHvfEcTCStJvj8jUYi6YV0ZrwHFHFc8bINOTqTvo6Dizep14GJTpjTWr/uiggxaF1TD+5XtRq22I77bhc+YL49NZDS+3sosamcHuJbVcJCN5gkFfeYOalVaqq83CCheVbu6WWJa0m11puKWu0Z4wMw6rjRqcGRfjXHxMU6WMB0ZE1v0fH6JWJIT0qfm1bWvi8V6rI4Trphm6kCpJ93i1V8pVxN1wS6C1RlIVzjJFljR2VEiWf2B1JgeuR4/Zl+hCA8inbBOu7eddtpqCmdXtsvdUUC6jAIl2IIRzsjoFBXQkYdEz6Awa66EyDnq/oSsfmhY5HL6usrVw4Xi9ZLfUSUa2XXkX4MEO/xupUFXcztGUBMBlaABT1OwMS/fGk142pA5UVkdFWZPXVFxE0faeksbrWVo7s5tlDEyvM7z/ursbvGBpGnx1TmvI0VREWs67VtmPJvIDYMFjFQWdggHGBzITJVt/2a9D1asPgmc0WhdY18V2pQ5uG3mYTTFtKZ01qxaBwHkvCuW2t2P6Zdoj5Q31jF/gBk2bfGN6wx5l2AZf24UwjTQn+Ez2ye73jb6ZCb5lrBe0jbkijrcd/mIigVJWIADN1JSKodaCIoMaBIkK6oSMRQQDxXXOjBauH1UdC7DQM3quE0Kifq5TMb9sqSnkzMBxQefjsjVUqIqvw0141WqtporDbq0kKpV82bzJ1Z0pLo91ylDBlc4KtB6YcrIE9jl8YkSzT1/8qe7ZlLdyS50hnVrVUbrkj5wkUJaCk+h5TMuM6VK4w5dJD6mlC61A0iTNw2IZAeockvcfx2Jn62CeQxNG1Hwf8UwRPNEscXbwYkDywnfocbb0YSAyu9DYdx7ORTpXr2Beg19sEUbbLg6jqpVJXyldaIGWKaWTXE/b8UcSo/nOs3mQd0ildFY8fX/CnKz9TeY7j+XkyHvWboUJ6530KuG3JTUm9WVrlLreqWboHpwKGYypNiIx5yPmSGGwubM6l2BKElb3CjBWbPYRiLFitQ3/F4Zzl4dIaqUt2E1xffLoFqNejs9ucRUnhOZkKzCtCJA40DJgdLLvxqV6Ii6MW2f3ju311kH0lW0XHy+OxZKl1LI2RhfBZo12tZXFJsTrKYh3NK2St/Xfzard5RalsFB3N0NTs12GyYJGD6CKyTk4fA//pculFC13a+TdTqo6s3QOjwoD7KogYQNjgDUSxDWmwhqudWS02LCGYxi/rRANYjV3dYAUT+WhZYP/NQ23dIAXdnI0fI32nB2dAC/Eb+gUVDE/jlXSD81+foML32wGQghp4n/V1zBLYncDcmxprm/Hqx0IDK76MY5uYwpm66/KGksJhvRlRUqitxlDePERZMrQf0nDkbVMBQtuShmPIpCFntDomDRiG8dMIm4icedGM/31YiyTvSRFia9x9V4/wmQJxbhcsUuIF6TBudtU+EN9zTKMKiwObQtWEB9x5xmqrNVpveYhfSFoKkZ62PIQZ5lO2FjFqL0RW916JLm0oBWiyJZ6yqgrRdsSWwlg6+dCNVFgtWlt5sXj4JePM4E0bayjaLXZsL7/Gkl6xCSK+el29WnCfgp3rig61vW9dJzduHh6qpZT8rhTqcEGUwlvYB5cgXFLb6ZgbWNxU37TY5FgPtaCuOF307qwHVs8KM/y1WROCT9u91kCWJN1idrXi3gZSAKzXHThxOGCVODmFR3ebdfrdhjy03RnMOD1fr2GG4aAgUMEcoT9P1MDZNFv1eDYhoFXsU4QDGubkdNoxTAYYQRTspFd4wcyWHhuGkbG5F1KYeyrHsNRBCiaRNNowDBCWMzBhIRuGSZsBbHxupcXiR9BqC0rN9wIo+9YXstBC2jVQhGI7C6ggm35pknaRqRXLJaB6Q8XeACiskEZkuiQ+ZWCVm7EMByxdM59lXUjBg0iTkF5zVEiBpx6VB4CoEBO5Z6LSNvNJ6X5TBKstNbsjKxhaxXZ9699os+uQoohc72rbUhxQrbd/Gp4DEVg5fVpsSNUq4qNiuwkd5YaWZpwKYJbbimnjiCIlg3o4VjKoMGnm9gkppOZM8hSmYeCn8az34SlYbn31+/CeAlLKpomnIFMrRai1XzsFKWbTxVMArI0EAXoGFjRU9LB/AagQT6FnUEH1q4VNZ7cIxGHWb3f6AmrWbaB8cGiZ+6GFeaCdQaswITVMoO5d44LWVu3E0t6cqZ2rXX136MIxSDTAoLxZMh/5FIjvaF8S+55wUrx2RQYt+EM34lnCYF160VQUEc455Yq/Ycie/FicFnaCMc1qnYyHddZQJhCrfbPyw6m3SRf6zvOpkiW3UZcsnKXQXIlzEZulT1qlLe0iJizjOz+fLntAVtEoUc1m6a39FIWJv18a3nHbe5FSzi8PSRhEhQU88+L7X/hdQZKy6ciwMYu7MJnFyWo15Amh8/mcTKeAvfmZmXPncDXISbSc31ZU70hMyUGkLhm1NLudSVecjQWd/awYVeA+baR7UtSpFu2IxKyw1moI5JqaIJeCVtlINHNCEK1rd4VZaPfmPi1c37/XnX0PK/RdqdwRq8jo1eV1YGTwRoQb2uxK/h4QYlKrHoVAImY9YwRpL891ZsYqqT40rZEp9uMsXkFrJBkqtBaRBJtBETe1q74WOJZgXl/CkvMPQ5IjiTZsY4KuFlLM7//+8tfE/DRd/fzj55//RxkN7TMYzJMw5P7DMGRSWpd1JpJrUYUjfhgzbpNX6ij5Ny6vuNEurvg/ \ No newline at end of file diff --git a/dela/docs/diagrams/ledger.drawio b/dela/docs/diagrams/ledger.drawio deleted file mode 100644 index 4d445d3..0000000 --- a/dela/docs/diagrams/ledger.drawio +++ /dev/null @@ -1 +0,0 @@ -7VpLc+I4EP41VGUP2cIvIEcIySaHrUlN2JnJ3gQSRrXC8siC2Pvrt4UlY9mmeNQAYcMF3K1Wq9X+9KklaHn38/QPgeLZnxwT1nLbOG15w5brOsFdG76UJss1Xa+XK0JBsTZaK17pv0Qrdb9wQTFJLEPJOZM0tpUTHkVkIi0dEoK/22ZTzuxRYxSSmuJ1glhd+51iOcu1vaC91j8RGs7MyE5bt8yRMdaKZIYwfy+pvIeWdy84l/nTPL0nTCXP5CXv97ihtQhMkEju0qHfG7G7l4fBlH/9W85+fvvylNFb7WWJ2EJPuOV2GPgbxCpkmek8dH4uVJyDORIhjVpeH1rbcQqfoFzNVulvJY/zNr/UJkkqbxGjoe43gYCJWPuEp1B/r0amJQWag8MBq0vPyscUTUihtrtYHiEptDrKWNQ0RjESKErQRFIevQDWSl7G1T6gi6u6mVCpM0g2s3Q2T/igVDMylaVcb/Jdn+YHDlnZIoxvRulvuR2BFSyapqIsQyL7jN1o01YwaAXDUbrrxF1rBu6SCElh2fdznA5XSB5o1A7zyAccrKZstYinFBaXB4spkpq0HFfLj2hOmaK7J8KWRHlVGZZzpoyKsdWAJN24lp2CIYBaCZ8TKTIw0R18zSmaVF1DOu9riip0szI9GTJCmhbDwvWaOeBBk8ceROJdiaROJPc8SkiULBLo90VgImgUXiibKNtYkeHKaDND1uI/Xjh4nJsMkURjlJCLpTxGE0mimybOO21GgVEHjE/+SWxSXek+Ba96HZtYnV6wI7EGxyJW/0qsdWL9BsFipOjnQun0Qplqmeed3KwoYTth/a/IwPfPXmV1G8igkmMS4b4694KEKZrzCI9m6rUPoOFxlc9hO5d0Sl3fzhxkR2Q/ysKb6vN7YMRhWvhQUmaklMofpedSL5DWnZRg+uTRE1w7g1feEcyQL8SEbC9AJaCZyG0n3vo7L71Tx7x3QRhwzNKOremdancvnELUBX56Ffh0KqjIp6Q7lQ/uFT/Var/mKJ9zzRHAAGUls1gZJDUMFkk4HJZ3p4ClgVcBqbdSSzO8CiiX0PtmgbcZymeApX8yWAZdG05e70Bcdr0tjjbg8lehzlwRXkujcmlkFevXquhEx6bnCJPUnJoWAPiOf7Yj3AvKGEfYRKPFs4XzjGHJUJmZeIx8toBeYTEjuRDERFQoPsUZt1pNePWq1rlrqmr9Y1W1Tv1XiL/Udc7mEoLHJLKqhVUCMUpmBOtMNZUOe1Smh1bBv7B08HetaNvN7/sItUOnUtN2Dq0dev4WR8euHdxr7VCvHao3qRdWPijbKSUMO/qWOosbKb2wc+t2H3x6sCa+ci7t+9lxJj/H1lWln2KfOuPe1fTD17HOvuZ5rw2sEE6+ge18JeOebAPrOhuqn703sO4WR8fewJp+F/hYuLPvXJzT4W7nwul0uOtU4BIcjDtvi6O9LwM3LJRuYI/Ttv5dBA+5xx1BDeL6n065+fr/Yt7Dfw== \ No newline at end of file diff --git a/dela/docs/diagrams/serde.drawio b/dela/docs/diagrams/serde.drawio deleted file mode 100644 index f066e9d..0000000 --- a/dela/docs/diagrams/serde.drawio +++ /dev/null @@ -1 +0,0 @@ -7VpZc6M4EP41VHkenOIwNn70Ee/MVmVqd2a3dvI0RYyMVQuIFXJiz6/fFohLYBMT4yOVFxu1pFZ3091fS0IxZv72N2qH6wfiIE/RVWerGHNF1zVzrMIfp+wSysiwEoJLsSMG5YTv+BcSRDHP3WAHRaWBjBCP4bBMXJIgQEtWotmUkpfysBXxyquGtosqhO9L26tS/8EOWydUy1Rz+meE3XW6sqaKHt9OBwtCtLYd8lIgGfeKMaOEsOTJ386Qx42X2iWZt9jTmwlGUcBeM0H//af78G369YFO/lx+mYU/52u9L7g8295GKKzoQw/4TUMuMtsJOwz/23A5p75NXRwoxgR61XALv0CMteX0PiNh0jco9DG0ZX3bw66YtwSBEc15wpMr/uOVcYFg+8Bw6lVbXziPlb1EGbk8pcQRjILlVZ5ohZISZiTgMt8HoBIq8HiSZwAtlGlryg2X+nGqo7Zf3VaG9tCKFSxd5c3Huoh9tX3U+5SMXBAKTlmnv2AMHur1cGbY0VQZzcXcnmJOFXP+tGNgkBkQEMQW/XTIEN3ptQlSYWWp6oWPZd3/3svC6yWx9WdEGYZ0MEn8dx57+FR48zwRd0pg1MqLg3uFIeiM6QpcSCQzTRfthe1jj6fBz8h7Rpwrdxfme3xQtjZfEG33xriWZQ5IuYj4iNEdDBETLJFrRLI1BqL9kqcuPaWti2krzVq2SJduxjrPKPAgksoRCUa/wgQjB/oNhzj4OiMUc4ScxFNDCAkpCiBCFvGw3Wtj4OrUhEwmVOhFjG4A54vxfaR2Nx3hxlAOcbMa4mpNiI+7inCjJsIlE6PAmfBaDFpPHln+C6Zw7GiNHGEX6F/EVp2rSato2IL90BazH+koeH7kz3emaM23ha75LuMGSv7IFoJGYRZv5tPiVjovUQI5lfJQelOgKNnQJWrOgQwcHrGmYqz65gtvVkvfPkWezfBzWba6VyvY/UEgKeReNC47kTmUnCNRSUwq1pQSH91sYJToXGEUO1qmYnvfM68QXS5eviZ13s1XryhYwm6yl2E0r+8eUBTBjut1Ralc4TqoyjDnkPIUSzQxfHfAMlDLoTyqKR2tc+LKsBLbf0foELSQEAVV+CgBTR2WZPjxWOhpwJICfDyW0KNzLDFfiSX6wZet3hnWyKq84FPBiynBQuZMx+LLSGtgtAdfwCXsXWFYyAdEBwQeSOsY6kG5BsOD4+EhkUCanYpDVqsIdQKIow9ArAJiltJvFgtXMaSLTd435OKI3fBuLkIUg//8yrD4eDx/d/A7lDKQYVT3ddq4Bn81rSsAts4EwMpNbeZGbwPgDtBWdh297W7OUhsYdbybG5/H4bQ2Dnf9Fd/ofA4n+4nVtryTyyiZ0YnKu1GaTHdpzjT3l2un8uY0L3/UYsVaTDqr/ajFLqyNgyrV2MfJyMFLNa3uZGR41spMq17bXxNS6peCytTgbzwd6at36kCCjH7NPcup8NSQCzizJZ6aMjDLjE6EpwP5WEY/gKfV2WXLps3uD0c0/QORq4gsQ9gNQjKNVUC0l37iwhGqeA1yoQ9VINaESAJDy5cy7xo1jWE5zE29Cpu1BxqdXShox91UO9j2SeD8tcYCOWuvqPVBPXS2ula4/lMN7YzHGnoZZMzWu0yrgVFHu0xzeI5d5uAcTp3Vdsc5aJsjvkuUg2f0ak2q9Vp/emGoDYxOVesdLPWahr8xBqCZfyCdDM8/Mzfu/wc= \ No newline at end of file diff --git a/dela/docs/guideline.md b/dela/docs/guideline.md deleted file mode 100644 index d4e1b03..0000000 --- a/dela/docs/guideline.md +++ /dev/null @@ -1,235 +0,0 @@ -# Programming guideline - -This page covers some opinionated rules that are not (or not enough) covered by -the golang best practices. We first stick to the official and common golang -best practices, using this as a supplement. - -## Comments - -Any comments should be formatted at 80 chars (in VS code the *Rewrap* does the -job pretty well). - ---- - -The implementation of an abstraction should start with the comment ` implements . It ...` - -```go -// FromText implements mino.AddressFactory. It returns an instance of an -// address from a byte slice. -func (f addressFactory) FromText(text []byte) mino.Address { - return address{id: string(text)} -} -``` - ---- - -A struct that implements an interface should have a comment that ends with `- implements `: - -```go -// RPC represents an RPC that has been registered by a client, which allows -// clients to call an RPC that will execute the provided handler. -// -// - implements mino.RPC -type RPC struct { - handler mino.Handler - srv Server - uri string -} -``` - -## Tests - -Tests should be named according to their specific scope of testing, following -the syntax `Test(_)_`. For instance, a unit test of a function -should follow that convention: - -```go -// dummy.go - -type Dummy struct{} - -func (d Dummy) String() string {...} -``` - -```go -// dummy_test.go - -func TestDummy_String(t *testing.T) {} - -func TestDummy_Failures_String(t *testing.T) {} -``` - -In test files, all the utility stuff should be grouped at the end of the file, -preceded by - -```go -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeNeededStruct{} -// ... -``` - -Test a function following a "bottom-top" path, ie. by first testing the entire -execution with the happy path, then by checking each possible case from the last -to the first one. Here is an example of a function and its test cases: - -```go -func Dummy() error { - if x { - return error.New("error 1") - } - if y { - return error.New("error 2") - } - - return nil -} - -func TestDummy(t *testing.T) { - err := dummy() - require.NoError(t, err) - - err = dummy() - require.EqualError(t, err, "error 2") - - err = dummy() - require.EqualError(t, err, "error 1") -} -``` - -## Error - -Don't feel guilty providing too much information in an error message. You never -really know you needed it until you experience it. - -```go -// DON'T -neighbour, ok := srv.neighbours[addr] -if !ok { - return nil, xerrors.Errorf("finding neighbour") -} -// This could result in a BAD error message like: -// > Failed to start server: finding neighbour - -// DO -neighbour, ok := srv.neighbours[addr] -if !ok { - return nil, xerrors.Errorf("couldn't find neighbour [%s]", addr) -} -// This could result in a GOOD error message like: -// > Failed to start server: couldn't find neighbour [127.0.0.1] -``` - -## Misc - -A "constructor" function should be named `New` and placed right after the struct's declaration: - -```go -type Dummy struct{ - // ... -} - -func NewDummy(...) Dummy { - // ... -} -``` - -Try to use blank lines between groups of instructions. Don't spare lines: - -```go -// DON'T -func (g *simpleGatherer) Wait(ctx context.Context, cfg Config) []txn.Transaction { - ch := make(chan []txn.Transaction, 1) - g.Lock() - g.queue = append(g.queue, item{cfg: cfg, ch: ch}) - g.Unlock() - if cfg.Callback != nil { - cfg.Callback() - } - select { - case txs := <-ch: - return txs - case <-ctx.Done(): - return nil - } -} - -// DO -func (g *simpleGatherer) Wait(ctx context.Context, cfg Config) []txn.Transaction { - ch := make(chan []txn.Transaction, 1) - - g.Lock() - g.queue = append(g.queue, item{cfg: cfg, ch: ch}) - g.Unlock() - - if cfg.Callback != nil { - cfg.Callback() - } - - select { - case txs := <-ch: - return txs - case <-ctx.Done(): - return nil - } -} -``` - -Don't use `if` with initialization statements. It makes the code harder to read. -Seriously, don't spare the extra line. - -```go -// DON'T -if err := dummy(); err != nil { - // ... -} - -// DO -err := dummy() -if err != nil { - // ... -} -``` - -Try at all costs not to use initialized return variables. It makes the code harder to read. - -```go -// DON'T -func dummy() (els []int, err error) { - // ... - return // <- what is returned exactly? -} - -// DO -func dummy() ([]int, error) { - // ... - return result, nil -} -``` - -If you initialize a struct, try to first gather your needed elements and then set it up once: - -```go -// DON'T -func NewPedersen(m mino.Mino) (*Pedersen) { - p := new(Pedersen) - p.factory = types.NewMessageFactory(m.GetAddressFactory()) - p.privkey = suite.Scalar().Pick(suite.RandomStream()) - p.mino = m - - return p -} - -// DO -func NewPedersen(m mino.Mino) (*Pedersen) { - factory := types.NewMessageFactory(m.GetAddressFactory()) - privkey := suite.Scalar().Pick(suite.RandomStream()) - - return &Pedersen{ - privKey: privkey, - mino: m, - factory: factory, - } -} -``` diff --git a/dela/docs/index.html b/dela/docs/index.html deleted file mode 100644 index bee6aa4..0000000 --- a/dela/docs/index.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Dela - Dedis Ledger Architecture - - - - - - -
- - - - - - - - - - - - - - - diff --git a/dela/docs/ledger.md b/dela/docs/ledger.md deleted file mode 100644 index 832b581..0000000 --- a/dela/docs/ledger.md +++ /dev/null @@ -1,75 +0,0 @@ -# Ledger - -This section describes the design of the distributed public ledger abstraction -described in the core module. - -The system is built around three abstractions that offer an API to clients so -that one can propagate transactions, create and validate that it will be -accepted, and finally wait for a confirmation that it is included, or rejected, -by the public ledger. - -A transaction is what triggers a change in the ledger set of values, where it is -left to the ledger implementation to define how values are stored and -identified. The transaction is created with a targeted execution environment -alongside the input parameters. - -The first abstraction which allows one to propagate transactions is defined as a -pool that can work independetly from the other abstractions. In fact, you could -run a distributed system that only shares transactions. - -The second is a service that accepts a transaction and can return if it will be -included in the next block, or if it will be rejected. Furthermore, it defines -the validity of the transaction so that it cannot be reused in a _replay attack_ -for instance. - -Finally, the third abstraction will collect the transactions from the pool and -create a chain of blocks, block after block according to a consensus algorithm. -One can register to the service to receive notifications when a new block is -created and learn about the transactions included in the block, and which of -them have been accepted. - -## Transaction Pool - -Each participant of a distributed ledger needs to collect a bunch of -transactions when it is trying to create a block. The pool is there to provide -that service, so that clients can send their transactions to one of the pool of -the distributed system while the ordering services wait for enough transactions -to be discovered to start a new block. - -The purpose is to offer a single entry point for the client and then the system -will take care of spreading the transactions so that any reachable member will -at some point learn about it. - -## Validation Service - -The validation service is there to protect the system against malicious -behaviour. We already mentionned a replay attack which allows an attacker to -listen for transactions and reuse them to double spent, or similar operation. -Bitcoin is protected because of the UTXOs, while Ethereum is using a nonce that -is monotically increasing for each address/identity. - -Another potential issue is the input parameters provided by the transaction -which could allow a malicious client to execute a smart contract in a incorrect -way. The validation service makes sure the result of a transaction execution -only updated what is allowed. - -Finally, because it is the validation that defines what the transaction looks -like, it also provides a manager that will help to create and sign transactions. -For instance, in the case of Ethereum, a client needs to use the correct nonce -for the transaction to be accepted. - -## Ordering Service - -A distributed ledger backed with a blockchain evolves block after block. Each -block contains a list of transactions that will be execute sequentially or in -parrallel depending on the implementation. Each execution will produce zero, one -or several changes in the ledger values. - -The ordering service is responsible for creating the blocks and in another -extent, the chain. The abstraction does not provide any property on how the -consensus is decided, but it defines the ways to read or get the values of the -ledger. - -## Implementations - -- [CoSiPBFT](cosipbft.md) diff --git a/dela/docs/manual_tests.md b/dela/docs/manual_tests.md deleted file mode 100644 index 598b265..0000000 --- a/dela/docs/manual_tests.md +++ /dev/null @@ -1,6 +0,0 @@ -# Manual tests - -Manual tests involve using high level shell scripts in order to setup the nodes, -connect to them, and add a transaction to the blockchain. - -See README.md in `/test`. diff --git a/dela/docs/memcoin.md b/dela/docs/memcoin.md deleted file mode 100644 index a8b86e4..0000000 --- a/dela/docs/memcoin.md +++ /dev/null @@ -1,62 +0,0 @@ -# Memcoin - -Memcoin is the default CLI to handle Dela nodes. You can find it in -"cli/node/memcoin". Be sure to run "go install" in it and add GOPATH/bin to your -PATH environment. - -```sh -# Run three nodes -LLVL=info memcoin --config /tmp/node1 start --listen tcp://127.0.0.1:2001 -LLVL=info memcoin --config /tmp/node2 start --listen tcp://127.0.0.1:2002 -LLVL=info memcoin --config /tmp/node3 start --listen tcp://127.0.0.1:2003 - -# Share the certificate -memcoin --config /tmp/node2 minogrpc join \ - --address //127.0.0.1:2001 $(memcoin --config /tmp/node1 minogrpc token) -memcoin --config /tmp/node3 minogrpc join \ - --address //127.0.0.1:2001 $(memcoin --config /tmp/node1 minogrpc token) - -# Create a new chain with the three nodes -memcoin --config /tmp/node1 ordering setup\ - --member $(memcoin --config /tmp/node1 ordering export)\ - --member $(memcoin --config /tmp/node2 ordering export)\ - --member $(memcoin --config /tmp/node3 ordering export) - -# Create a bls signer to sign transactions. Be sure you have the "crypto" binary -# by running "go install" in cli/crypto. -crypto bls signer new --save private.key -crypto bls signer read --path private.key --format BASE64 - -# Authorize the signer to handle the access contract on each node -memcoin --config /tmp/node1 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) -memcoin --config /tmp/node2 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) -memcoin --config /tmp/node3 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) - -# Update the access contract to allow us to use the value contract. Path to -# private.key is relative to the location where the node has been started. -memcoin --config /tmp/node1 pool add\ - --key private.key\ - --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Access\ - --args access:grant_id --args 0200000000000000000000000000000000000000000000000000000000000000\ - --args access:grant_contract --args go.dedis.ch/dela.Value\ - --args access:grant_command --args all\ - --args access:identity --args $(crypto bls signer read --path private.key --format BASE64_PUBKEY)\ - --args access:command --args GRANT - -# store a value on the value contract -memcoin --config /tmp/node1 pool add\ - --key private.key\ - --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Value\ - --args value:key --args "key1"\ - --args value:value --args "value1"\ - --args value:command --args WRITE - -# list the values stored on the value contract -memcoin --config /tmp/node1 pool add\ - --key private.key\ - --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Value\ - --args value:command --args LIST -``` \ No newline at end of file diff --git a/dela/docs/mino.md b/dela/docs/mino.md deleted file mode 100644 index ac11e81..0000000 --- a/dela/docs/mino.md +++ /dev/null @@ -1,192 +0,0 @@ -# **Mi**nimalistic **N**etwork **O**verlay - -Mino is an abstraction of a network overlay which provides a high-level API to -send messages to a list of participants. A distributed system may involve -hundreds of nodes which have to talk to each other, with the worst case being to -talk to all of the participants. In that case, it is inconceivable to open n^2 -connections, and this is where the overlay improves the situation. - -It provides two approaches: a classic RPC call and a streaming RPC. In the -former, it will contact some nodes and always returns a reply (which can be -empty) so that the sender knows who fails. In the later, the orchestrator (the -initiator of the protocol) opens a stream to one of the participant which will -open to others according to a routing algorithm. The algorithm will then define -the fault-tolerance of the system. - -## Players and Addresses - -Mino uses an abstraction of the roster that will be implied in a protocol. - -```go -type Players interface { - - // Take should a subset of the players according to the filters. - Take(...FilterUpdater) Players - - // AddressIterator returns an iterator that prevents changes of the - // underlying array and save memory by iterating over the same array. - AddressIterator() AddressIterator - - // Len returns the length of the set of players. - Len() int -} -``` - -It provides simple primitives to filter and get the list of addresses. Each -implementation of Mino has its own address representation. Minoch uses Go -channels and therefore uses string identifiers, whereas Minogrpc uses actual -network addresses. - -This interface can later be extended to add more information to the identity of -a participant, like a public key that will be used for collective signing. - -## Namespaces and RPCs - -The Mino interface provides two functions to create an endpoint that can be -called by others: - -```go -type Mino interface { - - ... - - MakeNamespace(namespace string) (Mino, error) - - MakeRPC(name string, h Handler, f serde.Factory) (RPC, error) -} -``` - -When a service needs to create an RPC, it will create its own namespace so that -there is no conflict with others, and then create an RPC with a unique name: - -```go -m := NewMino() - -statusSrvc, err := m.MakeNamespace("status") -if err != nil { ... } - -rpc, err := statusSrvc.MakeRPC("health", healthHandler{}, healthFac{}) -if err != nil { ... } - -ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) -defer cancel() - -resps, err := rpc.Call(ctx, HealthRequest{}, roster) -if err != nil { ... } - -for resp := range resps { - ... -} -``` - -The namespace and rpc combination can be seen as an URI in a web API, like -`go.dedis.ch/status/health` in the example above. You can of course chain the -namespaces as much as you want. - -## API - -### Call (unicast-based protocol) - -Call is one of the API provided by Mino. It takes a context, a message and the -list of participants as input parameters. - -The context can be used to cancel the protocol earlier if necessary. When the -context is done, the connection to other peers will be shutdown and -resources cleaned up. - -A normal execution of the call will send the message to all the participants and -the channel of responses will be populated as soon as a reply arrives. The reply -can either contain an actual message, or an error explaining why a participant -could not return the reply. The channel is closed after all the responses are -populated. - -### Stream (bidirectional stream-based protocol) - -Stream is one of the API provided by Mino. It takes a context and the list of -participants as input parameters: - -```go -sender, receiver, err := rpc.Stream(ctx context.Context, players Players) -``` - -The context defines when the protocol is done, and it should therefore always be -canceled at some point. When it arrives, all the connections are shut down and -the resources are cleaned up. - -Unlike a call, the orchestrator of a protocol will contact **one** of the -participants which will be the root for the routing algorithm. It will then -relays the messages according to the algorithm and create relays to other peers -when necessary. For instance, for a tree-based algorithm, it *could* look like: - -``` - Orchestrator - | - __ A __ - / \ - B C - / | \ / \ - D E F G H -``` - -A message coming from F would then be relayed through B and A to reach the right -side of the tree. This kind of algorithm is efficient in terms of distributed -load but is very sensible to failures so this is of course only an example. - -#### Example - -This illustrates how to use the stream API to implement a simple ping service, -where a message is echoed back to the node who sent it. - -```go -func demo() { - // This mino will be the orchestrator - m, err := NewMinogrpc(addr, tree.NewRouter(AddressFactory{})) - if err {...} - - // We provide the handler that each node will execute - rpc, err := m.MakeRPC("test", handler{}, aMessageFactory) - if err {...} - - // Players is the list of all the participants - sender, receiver, err := rpc.Stream(ctx context.Context, players Players) - if err {...} - - // We send a message to one of the participant - err := <-sender.Send(aMessage, anAddress) - if err {...} - - // The participant will receive the message and execute the handler, which will - // send back the message to us. - from, msg, err := recv.Recv(context.Background()) -} - -type handler struct {} - -// Stream implements mino.Handler. It simply sends back the message that it -// receives. -func (h handler) Stream(out mino.Sender, in mino.Receiver) error { - from, msg, err := in.Recv(context.Background()) - if err != nil { - return err - } - - // Here we are just sending back the message, but one could have a more - // complex handler that for example sends messages to other nodes, waits for - // their replies and does some processing. - err = <-out.Send(msg, from) - if err != nil { - return err - } - - return nil -} -``` - -#### Notes - -Set these env. flags to show GRPC traces: -``` -GRPC_GO_LOG_SEVERITY_LEVEL=info; -GRPC_GO_LOG_VERBOSITY_LEVEL=10; -``` - diff --git a/dela/docs/serde.md b/dela/docs/serde.md deleted file mode 100644 index 3f68264..0000000 --- a/dela/docs/serde.md +++ /dev/null @@ -1,190 +0,0 @@ -# Serde - -Serde is a serialization/deserialization abstraction. It has the purpose to -provide a simple API to serialize or deserialize messages without the concern of -the format, which is configurable. - -A format like JSON is composed of message format engines that will implement the -logic of serialization/deserialization. They can be registered depending on the -choice on the formats available. - -![Diagram](assets/serde.png) - -## Context - -Each format is expected to have a context engine implementation that will be -used to populate a Serde context. This context provides the format name -alongside the _marshal_ and _unmarshal_ API common to most of the encoding -implementations. - -The context can also store factories so that embedded messages can be -deserialized. Similarly to `golang.org/pkg/context`, the factories are only -populated for a given deserialization and thus to a specific context. - -A context API looks like: - -```go -type Context interface { - GetFormat() Format - - GetFactory(key interface{}) Factory - - Marshal(message interface{}) ([]byte, error) - - Unmarshal(data []byte, message interface{}) error -} -``` - -A factory can be provided to a deserialization context through: - -```go -type key struct{} - -type factory struct { - Factory -} - -nextCtx := WithFactory(ctx, key{}, factory{}) - -fac := nextCtx.GetFactory(key{}) // fac == factory{} -``` - -## Format - -A format engine will implement two functions: - -```go -type FormatEngine interface { - Encode(ctx Context, message Message) ([]byte, error) - - Decode(ctx Context, data []byte) (Message, error) -} -``` - -_Encode_ is responsible for serialiazing the message into bytes. It is the -responsability of the implementation to accept one or several types of messages -for a given engine. - -_Decode_ is responsible for deserializing data into a message. Similarly to -_Encode_, it can return one or several types of messages. - -The following snippet is an example of an engine for two types of messages using -the JSON format. The way to differenriate several messages will greatly vary -with the format used and if the format is self-describing or not. - -```go -package json - -type RequestJSON struct { - ... -} - -type ResponseJSON struct { - ... -} - -type MessageJSON struct { - Request *RequestJSON - Response *ResponseJSON -} - -type engine struct{} - -func (engine) Encode(ctx serde.Context, message Message) ([]byte, error) { - var m Message - - switch in := msg.(type) { - case types.Request: - m = MessageJSON{Request: &RequestJSON{...}} - case types.Response: - m = MessageJSON{Response: &ResponseJSON{...}} - default: - return nil, errors.New("invalid message") - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, err - } - - return data, nil -} - -func (engine) Decode(ctx serde.Context, data []byte) (Message, error) { - m := MessageJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, err - } - - if m.Request != nil { - return types.NewRequest(...), nil - } - - if m.Response != nil { - return types.NewResponse(...), nil - } - - return nil, errors.New("empty message") -} -``` - -## Message and Factory - -A message will implement the serialization and a factory will implement the -deserialization. It is left to the implementation to support one or several -formats but serde provides a registry to simplify the registration: - -```go -package types - -var requestFormats = registry.NewSimpleRegistry() - -type Request struct { - ... -} - -func (req Request) Serialize(ctx serde.Context) ([]byte, error) { - format := requestFormats.Get(ctx.GetName()) - // This registry always returns a format which can be an empty one. - - data, err := format.Encode(ctx, req) - if err != nil { - return nil, err - } - - return data, nil -} -``` - -Doing so will allow anyone to implement its own format and register it. A given -format could be overriden if necessary to use a different implementation but the -developer needs to make sure the registration will be done after the standard -one if it is imported as shown below. *Note*: it is not necessary to import for -new format. - -```go -import "go.dedis.ch/dela/serde/json" - -requestFormats.Register(serde.FormatJSON, myEngineImpl{}) -``` - -A factory can also be implemented to prepare the context of the deserialization: - -```go -type Factory struct{} - -func (f Factory) Deserialize(ctx serde.Context, data []byte) (Message, error) { - format := requestFormats.Get(ctx.GetName()) - - // Populate the context with factories of embedded messages if necessary. - // ctx = WithFactory(ctx, ...) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, err - } - - return msg, nil -} -``` diff --git a/dela/docs/sidebar.md b/dela/docs/sidebar.md deleted file mode 100644 index bb8662f..0000000 --- a/dela/docs/sidebar.md +++ /dev/null @@ -1,10 +0,0 @@ -- **General purpose** -- [Dela](dela.md) -- [Serde](serde.md) -- [Mino](mino.md) -- [Ledger](ledger.md) -- **Code** -- [Guideline](guideline.md) -- **Instructions** -- [Memcoin](memcoin.md) -- [Manual tests](manual_tests.md) \ No newline at end of file diff --git a/dela/docs/unicore_logo.png b/dela/docs/unicore_logo.png deleted file mode 100644 index 36d8887..0000000 Binary files a/dela/docs/unicore_logo.png and /dev/null differ diff --git a/dela/go.mod b/dela/go.mod deleted file mode 100644 index 29d8bb0..0000000 --- a/dela/go.mod +++ /dev/null @@ -1,56 +0,0 @@ -module go.dedis.ch/dela - -go 1.19 - -replace go.dedis.ch/kyber/v3 => ../kyber/ - -require ( - github.com/dedis/debugtools v0.0.0-20221206213939-0bc3bacd3042 - github.com/golang/protobuf v1.5.2 - github.com/opentracing-contrib/go-grpc v0.0.0-20200813121455-4a6760c71486 - github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.5.1 - github.com/rs/xid v1.4.0 - github.com/rs/zerolog v1.28.0 - github.com/stretchr/testify v1.8.1 - github.com/uber/jaeger-client-go v2.25.0+incompatible - github.com/urfave/cli/v2 v2.2.0 - go.dedis.ch/kyber/v3 v3.0.14 - go.etcd.io/bbolt v1.3.5 - golang.org/x/net v0.6.0 - golang.org/x/tools v0.6.0 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 - google.golang.org/grpc v1.53.0 - gopkg.in/yaml.v2 v2.4.0 -) - -require ( - github.com/HdrHistogram/hdrhistogram-go v1.0.1 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.10.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/uber/jaeger-lib v2.4.0+incompatible // indirect - go.dedis.ch/fixbuf v1.0.3 // indirect - go.dedis.ch/protobuf v1.0.11 // indirect - go.uber.org/atomic v1.7.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/dela/go.sum b/dela/go.sum deleted file mode 100644 index 7e5353b..0000000 --- a/dela/go.sum +++ /dev/null @@ -1,214 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw= -github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dedis/debugtools v0.0.0-20221206213939-0bc3bacd3042 h1:poR/D0ZoNGzZSbQZNgzDiwXTFJsBuM3dOEToNDh4gd4= -github.com/dedis/debugtools v0.0.0-20221206213939-0bc3bacd3042/go.mod h1:d0B8cSk0nY+sXvY5UOxIKcQoUZo29cOCsEsuYS+AMsQ= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/opentracing-contrib/go-grpc v0.0.0-20200813121455-4a6760c71486 h1:K35HCWaOTJIPW6cDHK4yj3QfRY/NhE0pBbfoc0M2NMQ= -github.com/opentracing-contrib/go-grpc v0.0.0-20200813121455-4a6760c71486/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= -github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaWxXbjwyYwsNaLQ= -github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= -github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= -go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= -go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= -go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg= -go.dedis.ch/kyber/v3 v3.0.14 h1:vnHb/Q5ape/e98oYZdbOrs3CkQ1bUIZFANmOxb/3zyo= -go.dedis.ch/kyber/v3 v3.0.14/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U= -go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= -go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= -go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= -go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dela/internal/depgraph/dep.yml b/dela/internal/depgraph/dep.yml deleted file mode 100644 index d00f7a7..0000000 --- a/dela/internal/depgraph/dep.yml +++ /dev/null @@ -1,24 +0,0 @@ -modname: go.dedis.ch/dela -overwrite: true -outfile: graph.dot -includes: - - go.dedis.ch/dela/core* - - go.dedis.ch/dela/cosi* - - go.dedis.ch/dela/cli* - # - go.dedis.ch/dela/mino* - # - go.dedis.ch/dela/serde* - # - go.dedis.ch/dela/crypto* -excludes: - - go.dedis.ch/dela/core/.*(controller|store|types|json) -interfaces: - - core/access - - core/execution - - core/ordering - - core/store - - core/txn - - core/validation - - core/txn/pool - - mino - - mino/router - - mino/proxy - - cli/node \ No newline at end of file diff --git a/dela/internal/depgraph/mod.go b/dela/internal/depgraph/mod.go deleted file mode 100644 index 1afcc6d..0000000 --- a/dela/internal/depgraph/mod.go +++ /dev/null @@ -1,363 +0,0 @@ -// Package main provides a utility CLI to generate the dependency graph of each -// package inside a module. -// You can get the explanations on how to use it with `go build && ./depgraph -// help`. -package main - -import ( - "fmt" - "go/parser" - "go/token" - "io" - "log" - "os" - "path" - "path/filepath" - "regexp" - "sort" - "strings" - "time" - - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" - "gopkg.in/yaml.v2" -) - -type config struct { - Modname string `yaml:"modname"` - Includes []string `yaml:"includes"` - Excludes []string `yaml:"excludes"` - Interfaces []string `yaml:"interfaces"` - WithTests bool `yaml:"withtests"` - OverWrite bool `yaml:"overwrite"` - OutFile string `yaml:"outfile"` -} - -type bag map[string]struct{} - -func main() { - - app := &cli.App{ - Name: "depgraph", - Usage: "generate a dot graph", - UsageText: "./depgraph [--config | --modname ] source ", - Description: `This utility will recursively parse a folder and extract -for each package that it finds the list of dependencies it uses to generate a -graphviz representation. By default it excludes _test.go files. -Since there might be a lot of dependencies, one can provide a yaml config file -in order to scope the parsing. The config format is the following: - -modname: MODULE_NAME -overwrite: [true|false] -outfile: FILE_PATH.dot -withtests: [true|false] -includes: - - go.dedis.ch/dela/* - - ... -excludes: - - go.dedis.ch/dela/core/.*(types|json) - - ... -interfaces: - - core/validation - - ... - -"includes" and "excludes" are two lists of regular expressions. - -If "includes" is empty then everything is included. Otherwise, the program only -keeps the package AND dependencies that are specified in the includes list. - -Each package AND dependency is checked against the "excludes" list and discarded -if it matches any of the elements. - -"interfaces" is used to mark specific packages that should be displayed -differently. In this case those package will be outlined by a green -background. - -Packages and their dependencies are sorted and the graph built accordingly. - -Examples: - -./depgrah --modname "go.dedis.ch/dela" -o graph.dot -F ./ -./depgrah --config internal/depgraph/dep.yml -o graph.dot -F ./ - -The following commands can be used to generate a visual representation from the -output of depgraph using DOT: - -dot -Tpdf graph.dot -o graph.pdf -dot -Gdpi=300 -Tpng graph.dot -o graph.png -Gsplines=ortho`, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "config", - Aliases: []string{"c"}, - Usage: "the path to a yaml config file", - }, - &cli.StringFlag{ - Name: "modname", - Usage: "the module name, convenient if one doesn't want to " + - "provide a config file. Is not taken into account if a " + - "config with 'modname' is provided. If provided without " + - "a config, must have a trailing '/'", - }, - &cli.StringFlag{ - Name: "out", - Aliases: []string{"o"}, - Usage: "if provided, will save the result to the specified " + - "file. Is not taken into account if a config with " + - "'outfile' is provided.", - }, - &cli.BoolFlag{ - Name: "force", - Aliases: []string{"F"}, - Usage: "overwrites the output file. Is not taken into " + - "account if a config with 'overwrite' is provided.", - }, - &cli.BoolFlag{ - Name: "withTests", - Aliases: []string{"t"}, - Usage: "includes the test files. Is not taken into account " + - "if a config with 'withtests' is provided.", - }, - }, - Action: run, - } - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -// run is the main action of the CLI. -func run(c *cli.Context) error { - searchDir := c.Args().First() - if searchDir == "" { - return xerrors.Errorf("please provide the folder path") - } - - config, err := loadConfig(c) - if err != nil { - return xerrors.Errorf("failed to load config: %v", err) - } - - out, err := getWriter(config) - if err != nil { - return xerrors.Errorf("failed to get writer: %v", err) - } - - interfaces := make(bag) - for _, it := range config.Interfaces { - interfaces[it] = struct{}{} - } - - // links will contain, for every package, a bag of dependencies. - links := make(map[string]bag) - - err = filepath.Walk(searchDir, walkFn(config, links)) - if err != nil { - return xerrors.Errorf("failed to parse folder: %v", err) - } - - displayGraph(out, links, interfaces) - - return nil -} - -func loadConfig(c *cli.Context) (config, error) { - config := config{ - Modname: c.String("modname"), - WithTests: c.Bool("withTests"), - OverWrite: c.Bool("force"), - OutFile: c.String("out"), - } - - configPath := c.String("config") - if configPath != "" { - configBuf, err := os.ReadFile(configPath) - if err != nil { - return config, xerrors.Errorf("failed to read config file: %v", err) - } - - err = yaml.Unmarshal(configBuf, &config) - if err != nil { - return config, xerrors.Errorf("failed to unmarshal config: %v", err) - } - - // we add a "/" to build the full package name. If the module name is - // mod.ch/module, then a package 'pancake' inside it should be - // mod.ch/module/pancake, but the parsing will only extract 'pancake'. - config.Modname = config.Modname + "/" - } - - return config, nil -} - -func getWriter(config config) (io.Writer, error) { - if config.OutFile == "" { - return os.Stdout, nil - } - - _, err := os.Stat(config.OutFile) - if !os.IsNotExist(err) && !config.OverWrite { - return nil, xerrors.Errorf("file '%s' already exist, use '-F' to "+ - "overwrite", config.OutFile) - } - - err = os.MkdirAll(path.Dir(config.OutFile), 0755) - if err != nil { - return nil, xerrors.Errorf("failed to create dir: %v", err) - } - - out, err := os.Create(config.OutFile) - if err != nil { - return nil, xerrors.Errorf("failed to create output file: %v", err) - } - - return out, nil -} - -// walkFn returns the functions that will be called recursively on each file and -// folder -func walkFn(config config, links map[string]bag) filepath.WalkFunc { - return func(path string, f os.FileInfo, err error) error { - fset := token.NewFileSet() - - if err != nil { - return xerrors.Errorf("got an error while walking: %v", err) - } - - // we exclude the dir and non-go files - if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || - strings.HasSuffix(f.Name(), "_test.go") { - - return nil - } - - // we exclude test files if not otherwise asked - if !config.WithTests && strings.HasSuffix(f.Name(), "_test.go") { - return nil - } - - astFile, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly) - if err != nil { - return xerrors.Errorf("failed to parse file: %v", err) - } - - path = filepath.Dir(path) - // This is the full package path. From "mino" we want - // "go.dedis.ch/dela/mino" - packagePath := config.Modname + path - - if !isIncluded(packagePath, config.Includes) || - isExcluded(packagePath, config.Excludes) { - return nil - } - - for _, s := range astFile.Imports { - // because an import path is always surrounded with "" we remove - // them - importPath := s.Path.Value[1 : len(s.Path.Value)-1] - - if !isIncluded(importPath, config.Includes) || - isExcluded(importPath, config.Excludes) { - - continue - } - - // in the case the package imports a package from the same module, - // we want to keep only the "relative" name. From - // "go.dedis.ch/dela/mino/minogrpc" we want only "mino/minogrpc". - importPath = strings.TrimPrefix(importPath, config.Modname) - - if links[packagePath[len(config.Modname):]] == nil { - links[packagePath[len(config.Modname):]] = make(bag) - } - - // add the dependency to the bag - links[packagePath[len(config.Modname):]][importPath] = struct{}{} - } - - return nil - } -} - -func displayGraph(out io.Writer, links map[string]bag, interfaces bag) { - // a bag of nodes, used to keep track of every node added so that we can - // later on outline the interfaces. - nodesList := make(bag) - - fmt.Fprintf(out, "strict digraph {\n") - fmt.Fprintf(out, "labelloc=\"t\";\n") - fmt.Fprintf(out, "label =
(generated %s)>;\n", - time.Now().Format("2 Jan 06 - 15:04:05")) - fmt.Fprintf(out, "graph [fontname = \"helvetica\"];\n") - fmt.Fprintf(out, "graph [fontname = \"helvetica\"];\n") - fmt.Fprintf(out, "node [fontname = \"helvetica\"];\n") - fmt.Fprintf(out, "edge [fontname = \"helvetica\"];\n") - fmt.Fprintf(out, "node [shape=box,style=rounded];\n") - // To have (more or less) deterministric result - fmt.Fprintf(out, "start=0;\n") - fmt.Fprintf(out, "ratio = fill;\n") - fmt.Fprintf(out, "rankdir=\"LR\";\n") - - // We sort packages to improve the rendering - packages := make([]string, 0, len(links)) - for pkg := range links { - packages = append(packages, pkg) - } - - sort.Strings(packages) - - for _, pkg := range packages { - depsBag := links[pkg] - nodesList[pkg] = struct{}{} - - // We sort dependencies to improve the rendering - dependencies := make([]string, 0, len(depsBag)) - for dep := range depsBag { - dependencies = append(dependencies, dep) - } - - sort.Strings(dependencies) - - for _, dep := range dependencies { - nodesList[dep] = struct{}{} - fmt.Fprintf(out, "\"%v\" -> \"%v\" [minlen=1];\n", pkg, dep) - } - } - - // outlines the interface nodes - for k := range nodesList { - _, found := interfaces[k] - if found { - fmt.Fprintf(out, "\"%s\" [style=filled fillcolor=olivedrab1];\n", k) - } - } - - fmt.Fprintf(out, "}\n") -} - -func isIncluded(path string, includes []string) bool { - if len(includes) == 0 { - return true - } - - return matchSlice(path, includes) -} - -func isExcluded(path string, excludes []string) bool { - return matchSlice(path, excludes) -} - -func matchSlice(el string, slice []string) bool { - for _, e := range slice { - reg := regexp.MustCompile(e) - - ok := reg.MatchString(el) - if ok { - return true - } - } - - return false -} diff --git a/dela/internal/mcheck/mod.go b/dela/internal/mcheck/mod.go deleted file mode 100644 index 7ec3746..0000000 --- a/dela/internal/mcheck/mod.go +++ /dev/null @@ -1,126 +0,0 @@ -package main - -// This package provides a custom checks for "go vet". -// It can be used like the following: -// `go build && go vet -vettool=./check -commentLen -ifInit ./...` - -import ( - "go/ast" - "strings" - "unicode/utf8" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/analysis/unitchecker" -) - -// NoLint is a command to disable linting for the next line. -const NoLint = "// @nolint-next-line" - -// MaxLen is the maximum length of a comment -var MaxLen = 80 - -// This check verifies that no comments exceed the "MaxLen" length. It ignores -// files that have as first comment a "// Code genereated..." comment and it -// ignores comments that start with "//go:generate". It also ignores lines that -// starts with a link, ie start with "http(s)://". One can also ignore a next -// comment with "// @nolint-next-line". -var commentLenAnalyzer = &analysis.Analyzer{ - Name: "commentLen", - Doc: "checks the lengths of comments", - Run: runComment, -} - -// This check ensures that no if has an initialization statement -var ifInitAnalyzer = &analysis.Analyzer{ - Name: "ifInit", - Doc: "checks that no if with an initialization statement are used", - Requires: []*analysis.Analyzer{ - inspect.Analyzer, - }, - Run: runIfInitCheck, -} - -func main() { - unitchecker.Main( - commentLenAnalyzer, - ifInitAnalyzer, - ) -} - -// run parses all the comments in ast.File -func runComment(pass *analysis.Pass) (interface{}, error) { -fileLoop: - for _, file := range pass.Files { - isFirst := true - for _, cg := range file.Comments { - for i := 0; i < len(cg.List); i++ { - c := cg.List[i] - - if isFirst && strings.HasPrefix(c.Text, "// Code generated") { - continue fileLoop - } - // in case of /* */ comment there might be multiple lines - lines := strings.Split(c.Text, "\n") - for j := 0; j < len(lines); j++ { - line := lines[j] - - if strings.HasPrefix(line, "//go:generate") { - continue - } - if strings.HasPrefix(line, "// http://") || strings.HasPrefix(line, "// https://") { - continue - } - if utf8.RuneCountInString(line) > MaxLen { - pass.Reportf(c.Pos(), "Comment too long: %s (%d)", - line, utf8.RuneCountInString(line)) - } - if strings.HasPrefix(line, NoLint) { - // Skip next comment for block comment. - j++ - } - } - - isFirst = false - - if strings.HasPrefix(c.Text, NoLint) { - // Skip next comment for one-line comment. - i++ - } - } - } - } - - return nil, nil -} - -// runIfInitCheck parses all the if statement and checks if there is an -// initialization statement used. -func runIfInitCheck(pass *analysis.Pass) (interface{}, error) { -fileLoop: - for _, file := range pass.Files { - // We ignore generated files - if len(file.Comments) != 0 { - cg := file.Comments[0] - if len(cg.List) != 0 { - comment := cg.List[0] - if strings.HasPrefix(comment.Text, "// Code generated") { - continue fileLoop - } - } - } - - ast.Inspect(file, func(node ast.Node) bool { - switch x := node.(type) { - case *ast.IfStmt: - if x.Init != nil { - pass.Reportf(x.Pos(), "Please do not do initialization "+ - "in if statement") - } - } - return true - }) - } - - return nil, nil -} diff --git a/dela/internal/mcheck/mod_test.go b/dela/internal/mcheck/mod_test.go deleted file mode 100644 index 7820b16..0000000 --- a/dela/internal/mcheck/mod_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "testing" - - "golang.org/x/tools/go/analysis/analysistest" -) - -func TestCommentLen(t *testing.T) { - analysistest.Run(t, analysistest.TestData(), commentLenAnalyzer, "comment") -} - -func TestIfCheck(t *testing.T) { - analysistest.Run(t, analysistest.TestData(), ifInitAnalyzer, "ifcheck") -} diff --git a/dela/internal/mcheck/testdata/src/comment/mod1.go b/dela/internal/mcheck/testdata/src/comment/mod1.go deleted file mode 100644 index 05f084e..0000000 --- a/dela/internal/mcheck/testdata/src/comment/mod1.go +++ /dev/null @@ -1,27 +0,0 @@ -package comment - -// This line should be ok, this is why there is no "want" at the end of it, -// which is the way of testing an analyzer. - -// This line is too long and should raise an error because it exceed the 80 chars limit // want "" - -/* -This line is not too long and should not raise an error because it exceed the 80 -chars -*/ - -// @nolint-next-line -// this should not raise an error because we manually set to no lint the next line - -/* -// @nolint-next-line -this should not raise an error because we manually set to no lint the next line inside block comment -*/ - -// https://thisisaverylongurlthatshouldnotraiseanyproblembecauseweacceptlongurlincomments - -// http://thisisaverylongurlthatshouldnotraiseanyproblembecauseweacceptlongurlincomments - -//go:generate this line should be ignore even if it's too long because it started with a go:generate - -// ┌───▼─────┬─────►┌─────▼───┐ even with UFT-8 chars, it counts runes properly diff --git a/dela/internal/mcheck/testdata/src/comment/mod2.go b/dela/internal/mcheck/testdata/src/comment/mod2.go deleted file mode 100644 index 9a999a9..0000000 --- a/dela/internal/mcheck/testdata/src/comment/mod2.go +++ /dev/null @@ -1,5 +0,0 @@ -package comment - -// Code generated so this should no generate errors even with long lines - -// Here is a long line that should not generate an error because we added as first comment a "Code Generated" diff --git a/dela/internal/mcheck/testdata/src/ifcheck/mod1.go b/dela/internal/mcheck/testdata/src/ifcheck/mod1.go deleted file mode 100644 index 40a9b7a..0000000 --- a/dela/internal/mcheck/testdata/src/ifcheck/mod1.go +++ /dev/null @@ -1,26 +0,0 @@ -package ifcheck - -func dummy() { - // Should be good - a := true - if a { - - } - - // Should raise a concern - if b := false; b { // want "Please do not do initialization in if statement" - - } - - // Should work if nested - for i := 0; i < 1; i++ { - // No concern - if i == 2 { - - } - // Should raise a concern - if c := true; c { // want "Please do not do initialization in if statement" - - } - } -} diff --git a/dela/internal/mcheck/testdata/src/ifcheck/mod2.go b/dela/internal/mcheck/testdata/src/ifcheck/mod2.go deleted file mode 100644 index d765f8f..0000000 --- a/dela/internal/mcheck/testdata/src/ifcheck/mod2.go +++ /dev/null @@ -1,10 +0,0 @@ -package ifcheck - -// Code generated so this should no generate errors - -func dummy2() { - // Should be good since this file has the "Code generated" comment - if b := false; b { - - } -} diff --git a/dela/internal/testing/fake/crypto.go b/dela/internal/testing/fake/crypto.go deleted file mode 100644 index 90a3f5a..0000000 --- a/dela/internal/testing/fake/crypto.go +++ /dev/null @@ -1,407 +0,0 @@ -package fake - -import ( - "hash" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/serde" -) - -// PublicKeyFactory is a fake implementation of a public key factory. -// -// - implements crypto.PublicKeyFactory. -type PublicKeyFactory struct { - pubkey PublicKey - err error -} - -// NewPublicKeyFactory returns a fake public key factory that returns the given -// public key. -func NewPublicKeyFactory(pubkey PublicKey) PublicKeyFactory { - return PublicKeyFactory{ - pubkey: pubkey, - } -} - -// NewBadPublicKeyFactory returns a fake public key factory that returns an -// error when appropriate. -func NewBadPublicKeyFactory() PublicKeyFactory { - return PublicKeyFactory{err: fakeErr} -} - -// Deserialize implements serde.Factory. -func (f PublicKeyFactory) Deserialize(serde.Context, []byte) (serde.Message, error) { - return f.pubkey, f.err -} - -// PublicKeyOf implements crypto.PublicKeyFactory. -func (f PublicKeyFactory) PublicKeyOf(serde.Context, []byte) (crypto.PublicKey, error) { - return f.pubkey, f.err -} - -// FromBytes implements crypto.PublicKeyFactory. -func (f PublicKeyFactory) FromBytes([]byte) (crypto.PublicKey, error) { - return f.pubkey, f.err -} - -// SignatureByte is the byte returned when marshaling a fake signature. -const SignatureByte = 0xfe - -// Signature is a fake implementation of the signature. -// -// - implements crypto.Signature -type Signature struct { - crypto.Signature - err error -} - -// NewBadSignature returns a signature that will return error when appropriate. -func NewBadSignature() Signature { - return Signature{err: fakeErr} -} - -// Equal implements crypto.Signature. -func (s Signature) Equal(o crypto.Signature) bool { - _, ok := o.(Signature) - return ok -} - -// Serialize implements serde.Message. -func (s Signature) Serialize(serde.Context) ([]byte, error) { - return []byte("{}"), s.err -} - -// MarshalBinary implements crypto.Signature. -func (s Signature) MarshalBinary() ([]byte, error) { - return []byte{SignatureByte}, s.err -} - -// String implements fmt.Stringer. -func (s Signature) String() string { - return "fakeSignature" -} - -// SignatureFactory is a fake implementation of the signature factory. -// -// - implements crypto.SignatureFactory -type SignatureFactory struct { - Counter *Counter - signature Signature - err error -} - -// NewSignatureFactory returns a fake signature factory. -func NewSignatureFactory(s Signature) SignatureFactory { - return SignatureFactory{signature: s} -} - -// NewBadSignatureFactory returns a signature factory that will return an error -// when appropriate. -func NewBadSignatureFactory() SignatureFactory { - return SignatureFactory{err: fakeErr} -} - -// NewBadSignatureFactoryWithDelay returns a signature factory that will return -// an error after some calls. -func NewBadSignatureFactoryWithDelay(value int) SignatureFactory { - return SignatureFactory{ - err: fakeErr, - Counter: &Counter{Value: value}, - } -} - -// Deserialize implements serde.Factory. -func (f SignatureFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.SignatureOf(ctx, data) -} - -// SignatureOf implements crypto.SignatureFactory. -func (f SignatureFactory) SignatureOf(serde.Context, []byte) (crypto.Signature, error) { - if !f.Counter.Done() { - f.Counter.Decrease() - return f.signature, nil - } - return f.signature, f.err -} - -// PublicKey is a fake implementation of crypto.PublicKey. -// -// - implements crypto.PublicKey -type PublicKey struct { - crypto.PublicKey - err error - verifyErr error -} - -// NewBadPublicKey returns a new fake public key that returns error when -// appropriate. -func NewBadPublicKey() PublicKey { - return PublicKey{ - err: fakeErr, - verifyErr: fakeErr, - } -} - -// NewInvalidPublicKey returns a fake public key that never verifies. -func NewInvalidPublicKey() PublicKey { - return PublicKey{verifyErr: fakeErr} -} - -// Verify implements crypto.PublicKey. -func (pk PublicKey) Verify([]byte, crypto.Signature) error { - return pk.verifyErr -} - -// MarshalBinary implements encoding.BinaryMarshaler. -func (pk PublicKey) MarshalBinary() ([]byte, error) { - return []byte("PK"), pk.err -} - -// MarshalText implements encoding.TextMarshaler. -func (pk PublicKey) MarshalText() ([]byte, error) { - return pk.MarshalBinary() -} - -// Equal implements crypto.PublicKey. -func (pk PublicKey) Equal(other interface{}) bool { - _, ok := other.(PublicKey) - return ok -} - -// Serialize implements serde.Message. -func (pk PublicKey) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), pk.err -} - -// String implements fmt.Stringer. -func (pk PublicKey) String() string { - return "fake.PublicKey" -} - -// Signer is a fake implementation of the crypto.AggregateSigner interface. -// -// - implements crypto.Signer -type Signer struct { - crypto.AggregateSigner - publicKey PublicKey - signatureFactory SignatureFactory - verifierFactory VerifierFactory - err error -} - -// NewSigner returns a new instance of the fake signer. -func NewSigner() crypto.Signer { - return Signer{} -} - -// NewAggregateSigner returns a new signer that implements aggregation. -func NewAggregateSigner() Signer { - return Signer{} -} - -// NewSignerWithSignatureFactory returns a fake signer with the provided -// factory. -func NewSignerWithSignatureFactory(f SignatureFactory) Signer { - return Signer{signatureFactory: f} -} - -// NewSignerWithVerifierFactory returns a new fake signer with the specific -// verifier factory. -func NewSignerWithVerifierFactory(f VerifierFactory) Signer { - return Signer{verifierFactory: f} -} - -// NewSignerWithPublicKey returns a new fake signer with the specific public -// key. -func NewSignerWithPublicKey(k PublicKey) Signer { - return Signer{publicKey: k} -} - -// NewBadSigner returns a fake signer that will return an error when -// appropriate. -func NewBadSigner() Signer { - return Signer{err: fakeErr} -} - -// GetPublicKeyFactory implements crypto.Signer. -func (s Signer) GetPublicKeyFactory() crypto.PublicKeyFactory { - return PublicKeyFactory{} -} - -// GetSignatureFactory implements crypto.Signer. -func (s Signer) GetSignatureFactory() crypto.SignatureFactory { - return s.signatureFactory -} - -// GetVerifierFactory implements crypto.Signer. -func (s Signer) GetVerifierFactory() crypto.VerifierFactory { - return s.verifierFactory -} - -// GetPublicKey implements crypto.Signer. -func (s Signer) GetPublicKey() crypto.PublicKey { - return s.publicKey -} - -// Sign implements crypto.Signer. -func (s Signer) Sign([]byte) (crypto.Signature, error) { - return Signature{}, s.err -} - -// Aggregate implements crypto.AggregateSigner. -func (s Signer) Aggregate(...crypto.Signature) (crypto.Signature, error) { - return Signature{}, s.err -} - -// Verifier is a fake implementation of crypto.Verifier. -// -// - implements crypto.Verifier -type Verifier struct { - crypto.Verifier - err error - count *Counter -} - -// NewBadVerifier returns a verifier that will return an error when appropriate. -func NewBadVerifier() Verifier { - return Verifier{err: fakeErr} -} - -// NewBadVerifierWithDelay returns a verifier that will return an error after a -// given delay. -func NewBadVerifierWithDelay(value int) Verifier { - return Verifier{ - err: fakeErr, - count: NewCounter(value), - } -} - -// Verify implements crypto.Verifier. -func (v Verifier) Verify(msg []byte, s crypto.Signature) error { - if !v.count.Done() { - v.count.Decrease() - return nil - } - - return v.err -} - -// VerifierFactory is a fake implementation of crypto.VerifierFactory. -// -// - implements crypto.VerifierFactory -type VerifierFactory struct { - crypto.VerifierFactory - verifier Verifier - err error - call *Call -} - -// NewVerifierFactory returns a new fake verifier factory. -func NewVerifierFactory(v Verifier) VerifierFactory { - return VerifierFactory{verifier: v} -} - -// NewVerifierFactoryWithCalls returns a new verifier factory that will register -// the calls. -func NewVerifierFactoryWithCalls(c *Call) VerifierFactory { - return VerifierFactory{call: c} -} - -// NewBadVerifierFactory returns a fake verifier factory that returns an error -// when appropriate. -func NewBadVerifierFactory() VerifierFactory { - return VerifierFactory{err: fakeErr} -} - -// FromAuthority implements crypto.VerifierFactory. -func (f VerifierFactory) FromAuthority(ca crypto.CollectiveAuthority) (crypto.Verifier, error) { - f.call.Add(ca) - - return f.verifier, f.err -} - -// FromArray implements crypto.VerifierFactory. -func (f VerifierFactory) FromArray(pubkeys []crypto.PublicKey) (crypto.Verifier, error) { - f.call.Add(pubkeys) - - return f.verifier, f.err -} - -// Hash is a fake implementation of hash.Hash. -// -// - implements hash.Hash -type Hash struct { - hash.Hash - delay int - err error - Call *Call -} - -// NewBadHash returns a fake hash that returns an error when appropriate. -func NewBadHash() *Hash { - return &Hash{err: fakeErr} -} - -// NewBadHashWithDelay returns a fake hash that returns an error after a certain -// amount of calls. -func NewBadHashWithDelay(delay int) *Hash { - return &Hash{err: fakeErr, delay: delay} -} - -// Write implements hash.Hash. -func (h *Hash) Write(in []byte) (int, error) { - if h.Call != nil { - h.Call.Add(in) - } - - if h.delay > 0 { - h.delay-- - return 0, nil - } - return 0, h.err -} - -// Size implements hash.Hash. -func (h *Hash) Size() int { - return 32 -} - -// Sum implements hash.Hash. -func (h *Hash) Sum([]byte) []byte { - return make([]byte, 32) -} - -// MarshalBinary implements encoding.BinaryMarshaler. -func (h *Hash) MarshalBinary() ([]byte, error) { - if h.delay > 0 { - h.delay-- - return []byte{}, nil - } - return []byte{}, h.err -} - -// UnmarshalBinary implements encodi8ng.BinaryUnmarshaler. -func (h *Hash) UnmarshalBinary([]byte) error { - if h.delay > 0 { - h.delay-- - return nil - } - return h.err -} - -// HashFactory is a fake implementation of a hash factory. -// -// - implements crypto.HashFactory -type HashFactory struct { - hash *Hash -} - -// NewHashFactory returns a fake hash factory. -func NewHashFactory(h *Hash) HashFactory { - return HashFactory{hash: h} -} - -// New implements crypto.HashFactory. -func (f HashFactory) New() hash.Hash { - return f.hash -} diff --git a/dela/internal/testing/fake/mino.go b/dela/internal/testing/fake/mino.go deleted file mode 100644 index f73ee92..0000000 --- a/dela/internal/testing/fake/mino.go +++ /dev/null @@ -1,615 +0,0 @@ -package fake - -import ( - "bytes" - "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "encoding/binary" - "fmt" - "io" - "math/big" - "net" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// Address is a fake implementation of an address. -// -// - implements mino.Address -type Address struct { - mino.Address - index int - err error -} - -// NewAddress returns a fake address with the given index. -func NewAddress(index int) Address { - return Address{index: index} -} - -// NewBadAddress returns a fake address that returns an error when appropriate. -func NewBadAddress() Address { - return Address{err: fakeErr} -} - -// Equal implements mino.Address. -func (a Address) Equal(o mino.Address) bool { - other, ok := o.(Address) - return ok && other.index == a.index -} - -// MarshalText implements encoding.TextMarshaler. -func (a Address) MarshalText() ([]byte, error) { - buffer := make([]byte, 4) - binary.LittleEndian.PutUint32(buffer, uint32(a.index)) - return buffer, a.err -} - -// String implements fmt.Stringer. -func (a Address) String() string { - return fmt.Sprintf("fake.Address[%d]", a.index) -} - -// AddressFactory is a fake implementation of an address factory. -// -// - implements mino.AddressFactory -type AddressFactory struct { - mino.AddressFactory -} - -// FromText implements mino.AddressFactory. -func (f AddressFactory) FromText(text []byte) mino.Address { - if len(text) >= 4 { - index := binary.LittleEndian.Uint32(text) - return Address{index: int(index)} - } - return Address{} -} - -// AddressIterator is a fake implementation an address iterator. -// -// - implements mino.AddressIterator -type AddressIterator struct { - mino.AddressIterator - addrs []mino.Address - index int -} - -// NewAddressIterator returns a new address iterator -func NewAddressIterator(addrs []mino.Address) *AddressIterator { - return &AddressIterator{ - addrs: addrs, - } -} - -// Seek implements mino.AddressIterator. -func (i *AddressIterator) Seek(index int) { - i.index = index -} - -// HasNext implements mino.AddressIterator. -func (i *AddressIterator) HasNext() bool { - return i.index < len(i.addrs) -} - -// GetNext implements mino.AddressIterator. -func (i *AddressIterator) GetNext() mino.Address { - res := i.addrs[i.index] - i.index++ - return res -} - -// PublicKeyIterator is a fake implementation of a public key iterator. -// -// - implements crypto.PublicKeyIterator -type PublicKeyIterator struct { - signers []crypto.Signer - index int -} - -// NewPublicKeyIterator returns a new address iterator -func NewPublicKeyIterator(signers []crypto.Signer) *PublicKeyIterator { - return &PublicKeyIterator{ - signers: signers, - } -} - -// Seek implements crypto.PublicKeyIterator. -func (i *PublicKeyIterator) Seek(index int) { - i.index = index -} - -// HasNext implements crypto.PublicKeyIterator. -func (i *PublicKeyIterator) HasNext() bool { - return i.index < len(i.signers) -} - -// GetNext implements crypto.PublicKeyIterator. -func (i *PublicKeyIterator) GetNext() crypto.PublicKey { - if i.HasNext() { - res := i.signers[i.index] - i.index++ - return res.GetPublicKey() - } - return nil -} - -// CollectiveAuthority is a fake implementation of a collective authority. -// -// - implements crypto.CollectiveAuthority -type CollectiveAuthority struct { - crypto.CollectiveAuthority - addrs []mino.Address - signers []crypto.Signer - - Call *Call - PubkeyNotFound bool -} - -// GenSigner is a function to generate a signer. -type GenSigner func() crypto.Signer - -// NewAuthority returns a new collective authority of n members with new signers -// generated by g. -func NewAuthority(n int, g GenSigner) CollectiveAuthority { - return NewAuthorityWithBase(0, n, g) -} - -// NewAuthorityWithBase returns a new fake collective authority of size n with -// a given starting base index. -func NewAuthorityWithBase(base int, n int, g GenSigner) CollectiveAuthority { - signers := make([]crypto.Signer, n) - for i := range signers { - signers[i] = g() - } - - addrs := make([]mino.Address, n) - for i := range addrs { - addrs[i] = Address{index: i + base} - } - - return CollectiveAuthority{ - signers: signers, - addrs: addrs, - } -} - -// NewAuthorityFromMino returns a new fake collective authority using -// the addresses of the Mino instances. -func NewAuthorityFromMino(g GenSigner, instances ...mino.Mino) CollectiveAuthority { - signers := make([]crypto.Signer, len(instances)) - for i := range signers { - signers[i] = g() - } - - addrs := make([]mino.Address, len(instances)) - for i, instance := range instances { - addrs[i] = instance.GetAddress() - } - - return CollectiveAuthority{ - signers: signers, - addrs: addrs, - } -} - -// GetAddress returns the address at the provided index. -func (ca CollectiveAuthority) GetAddress(index int) mino.Address { - return ca.addrs[index] -} - -// GetSigner returns the signer at the provided index. -func (ca CollectiveAuthority) GetSigner(index int) crypto.Signer { - return ca.signers[index] -} - -// GetPublicKey implements crypto.CollectiveAuthority. -func (ca CollectiveAuthority) GetPublicKey(addr mino.Address) (crypto.PublicKey, int) { - if ca.PubkeyNotFound { - return nil, -1 - } - - for i, address := range ca.addrs { - if address.Equal(addr) { - return ca.signers[i].GetPublicKey(), i - } - } - return nil, -1 -} - -// Take implements mino.Players. -func (ca CollectiveAuthority) Take(updaters ...mino.FilterUpdater) mino.Players { - filter := mino.ApplyFilters(updaters) - newCA := CollectiveAuthority{ - Call: ca.Call, - addrs: make([]mino.Address, len(filter.Indices)), - signers: make([]crypto.Signer, len(filter.Indices)), - } - for i, k := range filter.Indices { - newCA.addrs[i] = ca.addrs[k] - newCA.signers[i] = ca.signers[k] - } - return newCA -} - -// Len implements mino.Players. -func (ca CollectiveAuthority) Len() int { - return len(ca.signers) -} - -// AddressIterator implements mino.Players. -func (ca CollectiveAuthority) AddressIterator() mino.AddressIterator { - return &AddressIterator{addrs: ca.addrs} -} - -// PublicKeyIterator implements crypto.CollectiveAuthority. -func (ca CollectiveAuthority) PublicKeyIterator() crypto.PublicKeyIterator { - return &PublicKeyIterator{signers: ca.signers} -} - -// ReceiverMessage is the combination of an address and a message that is -// returned by the receiver. -type ReceiverMessage struct { - Address mino.Address - Message serde.Message -} - -// NewRecvMsg creates a new receiver message. -func NewRecvMsg(addr mino.Address, msg serde.Message) ReceiverMessage { - return ReceiverMessage{ - Address: addr, - Message: msg, - } -} - -// Receiver is a fake RPC stream receiver. It will return the consecutive -// messages stored in the Msg slice. -// -// - implements mino.Receiver -type Receiver struct { - mino.Receiver - err error - Msgs []ReceiverMessage - index int - blocking bool -} - -// NewReceiver returns a new receiver -func NewReceiver(msgs ...ReceiverMessage) *Receiver { - return &Receiver{ - Msgs: msgs, - err: io.EOF, - } -} - -// NewBlockingReceiver returns a new fake receiver that is blocking until the -// context is done. -func NewBlockingReceiver() *Receiver { - return &Receiver{ - blocking: true, - } -} - -// NewBadReceiver returns a new receiver that returns an error. -func NewBadReceiver(msg ...ReceiverMessage) *Receiver { - return &Receiver{Msgs: msg, err: fakeErr} -} - -// Recv implements mino.Receiver. -func (r *Receiver) Recv(ctx context.Context) (mino.Address, serde.Message, error) { - if r.blocking { - <-ctx.Done() - return nil, nil, ctx.Err() - } - - if len(r.Msgs) == 0 { - return nil, nil, r.err - } - - // In the case there are no more messages to read we return nil. - if r.index >= len(r.Msgs) { - return nil, nil, r.err - } - - defer func() { - r.index++ - }() - - m := r.Msgs[r.index] - - return m.Address, m.Message, nil -} - -// Sender is a fake RPC stream sender. -// -// - implements mino.Sender -type Sender struct { - mino.Sender - err error -} - -// NewBadSender returns a sender that always returns an error. -func NewBadSender() Sender { - return Sender{err: fakeErr} -} - -// Send implements mino.Sender. -func (s Sender) Send(serde.Message, ...mino.Address) <-chan error { - errs := make(chan error, 1) - if s.err != nil { - errs <- s.err - } - - close(errs) - return errs -} - -// RPC is a fake implementation of an RPC. -// -// - implements mino.RPC -type RPC struct { - mino.RPC - Calls *Call - msgs chan mino.Response - receiver *Receiver - sender Sender - err error -} - -// NewRPC returns a fake rpc. -func NewRPC() *RPC { - rpc := &RPC{} - rpc.Reset() - return rpc -} - -// NewStreamRPC returns a fake rpc with specific stream options. -func NewStreamRPC(r *Receiver, s Sender) *RPC { - rpc := &RPC{ - receiver: r, - sender: s, - } - rpc.Reset() - return rpc -} - -// NewBadRPC returns a fake rpc that returns an error when appropriate. -func NewBadRPC() *RPC { - rpc := &RPC{ - err: fakeErr, - } - rpc.Reset() - return rpc -} - -// SendResponse fills the rpc with a message. -func (rpc *RPC) SendResponse(from mino.Address, msg serde.Message) { - rpc.msgs <- mino.NewResponse(from, msg) -} - -// SendResponseWithError fills the rpc with an error. -func (rpc *RPC) SendResponseWithError(from mino.Address, err error) { - rpc.msgs <- mino.NewResponseWithError(from, err) -} - -// Done closes the response channel. -func (rpc *RPC) Done() { - close(rpc.msgs) -} - -// Call implements mino.RPC. -func (rpc *RPC) Call(ctx context.Context, - m serde.Message, p mino.Players) (<-chan mino.Response, error) { - - rpc.Calls.Add(ctx, m, p) - - return rpc.msgs, rpc.err -} - -// Stream implements mino.RPC. -func (rpc *RPC) Stream(ctx context.Context, p mino.Players) (mino.Sender, mino.Receiver, error) { - rpc.Calls.Add(ctx, p) - - return rpc.sender, rpc.receiver, rpc.err -} - -// Reset resets the channels. -func (rpc *RPC) Reset() { - rpc.Calls = &Call{} - rpc.msgs = make(chan mino.Response, 100) -} - -// Mino is a fake implementation of mino. -// -// - implements mino.Mino -type Mino struct { - mino.Mino - err error -} - -// NewBadMino returns a Mino instance that returns an error when appropriate. -func NewBadMino() Mino { - return Mino{err: fakeErr} -} - -// GetAddress implements mino.Mino. -func (m Mino) GetAddress() mino.Address { - if m.err != nil { - return NewBadAddress() - } - - return Address{} -} - -// GetAddressFactory implements mino.Mino. -func (m Mino) GetAddressFactory() mino.AddressFactory { - return AddressFactory{} -} - -// WithSegment implements mino.Mino. -func (m Mino) WithSegment(segment string) mino.Mino { - return m -} - -// CreateRPC implements mino.Mino. -func (m Mino) CreateRPC(string, mino.Handler, serde.Factory) (mino.RPC, error) { - return NewRPC(), nil -} - -// MakeCertificate generates a valid certificate for the localhost address and -// for an hour. It outputs only its byte representation. -func MakeCertificate(t *testing.T, ips ...net.IP) []byte { - priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - require.NoError(t, err) - - tmpl := &x509.Certificate{ - SerialNumber: big.NewInt(1), - IPAddresses: ips, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: true, - MaxPathLen: 1, - } - - buf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &priv.PublicKey, priv) - require.NoError(t, err) - - return buf -} - -// MakeFullCertificate generates a valid certificate for the localhost address -// and for an hour. it outputs the TLS certificate and its byte representation. -func MakeFullCertificate(t *testing.T) (*tls.Certificate, []byte) { - priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - require.NoError(t, err) - - tmpl := &x509.Certificate{ - SerialNumber: big.NewInt(1), - IPAddresses: []net.IP{}, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: true, - MaxPathLen: 1, - } - - buf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &priv.PublicKey, priv) - require.NoError(t, err) - - cert, err := x509.ParseCertificate(buf) - require.NoError(t, err) - - return &tls.Certificate{ - Certificate: [][]byte{buf}, - PrivateKey: priv, - Leaf: cert, - }, buf -} - -// MakeCertificateChain creates a valid certificate chain with an intermediary -// certificate. -func MakeCertificateChain(t *testing.T) []byte { - root, pk := makeRootCertificate(t) - intermediary, pk2 := makeIntermediaryCertificate(t, root, pk) - server, _ := makeServerCertificate(t, intermediary, pk2) - - chain := bytes.Buffer{} - chain.Write(server.Raw) - chain.Write(intermediary.Raw) - chain.Write(root.Raw) - - return chain.Bytes() -} - -func genCert(t *testing.T, template, parent *x509.Certificate, - publicKey *ecdsa.PublicKey, privateKey *ecdsa.PrivateKey) *x509.Certificate { - - certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, publicKey, privateKey) - require.NoError(t, err) - - cert, err := x509.ParseCertificate(certBytes) - require.NoError(t, err) - - return cert -} - -func makeRootCertificate(t *testing.T) (*x509.Certificate, *ecdsa.PrivateKey) { - var template = x509.Certificate{ - SerialNumber: big.NewInt(1), - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: true, - MaxPathLen: 2, - IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, - } - - priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - require.NoError(t, err) - - rootCert := genCert(t, &template, &template, &priv.PublicKey, priv) - - return rootCert, priv -} - -func makeIntermediaryCertificate(t *testing.T, rootCert *x509.Certificate, - rootKey *ecdsa.PrivateKey) (*x509.Certificate, *ecdsa.PrivateKey) { - - priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - require.NoError(t, err) - - var template = x509.Certificate{ - SerialNumber: big.NewInt(1), - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: true, - MaxPathLenZero: false, - MaxPathLen: 1, - IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, - } - - intermediary := genCert(t, &template, rootCert, &priv.PublicKey, rootKey) - - return intermediary, priv -} - -func makeServerCertificate(t *testing.T, intermediaryCert *x509.Certificate, - intermediaryKey *ecdsa.PrivateKey) (*x509.Certificate, *ecdsa.PrivateKey) { - - priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - require.NoError(t, err) - - var template = x509.Certificate{ - SerialNumber: big.NewInt(1), - NotBefore: time.Now().Add(-10 * time.Second), - NotAfter: time.Now().AddDate(10, 0, 0), - KeyUsage: x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - IsCA: false, - MaxPathLenZero: true, - IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, - } - - ServerCert := genCert(t, &template, intermediaryCert, &priv.PublicKey, intermediaryKey) - - return ServerCert, priv -} diff --git a/dela/internal/testing/fake/mod.go b/dela/internal/testing/fake/mod.go deleted file mode 100644 index e5e7cb5..0000000 --- a/dela/internal/testing/fake/mod.go +++ /dev/null @@ -1,183 +0,0 @@ -// Package fake provides fake implementations for interfaces commonly used in -// the repository. -// The implementations offer configuration to return errors when it is needed by -// the unit test and it is also possible to record the call of functions of an -// object in some cases. -package fake - -import ( - "bytes" - "crypto/rand" - "fmt" - "io" - "strings" - "sync" - "testing" - "time" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/require" - "golang.org/x/xerrors" -) - -func init() { - // A random value is injected every time, so that the error is never the - // same and prevent hardcoded values in the tests. - random := make([]byte, 4) - rand.Read(random) - - fakeErr = xerrors.Errorf("fake error (%x)", random) -} - -// fakeErr is initialized with a random value so that the test suite cannot rely -// on a fixed value. -var fakeErr error - -// GetError returns the fake error. -func GetError() error { - return fakeErr -} - -// Err returns the expected format of an error returned by a fake component. -func Err(msg string) string { - return fmt.Sprintf("%s: %v", msg, fakeErr) -} - -// Call is a tool to keep track of a function calls. -type Call struct { - sync.Mutex - calls [][]interface{} -} - -// NewCall returns a new empty call monitor. -func NewCall() *Call { - return &Call{} -} - -// Get returns the nth call ith parameter. -func (c *Call) Get(n, i int) interface{} { - if c == nil { - return nil - } - - c.Lock() - defer c.Unlock() - - return c.calls[n][i] -} - -// Len returns the number of calls. -func (c *Call) Len() int { - if c == nil { - return 0 - } - - c.Lock() - defer c.Unlock() - - return len(c.calls) -} - -// Add adds a call to the list. -func (c *Call) Add(args ...interface{}) { - if c == nil { - return - } - - c.Lock() - defer c.Unlock() - - c.calls = append(c.calls, args) -} - -// Clear clears the array of calls. -func (c *Call) Clear() { - if c != nil { - c.Lock() - c.calls = nil - c.Unlock() - } -} - -// Counter is a helper to delay errors or actions. It can be nil without panics. -type Counter struct { - Value int -} - -// NewCounter returns a new counter set to the given value. -func NewCounter(value int) *Counter { - return &Counter{ - Value: value, - } -} - -// Done returns true when the counter reached zero. -func (c *Counter) Done() bool { - return c == nil || c.Value <= 0 -} - -// Decrease decrements the counter. -func (c *Counter) Decrease() { - if c == nil { - return - } - c.Value-- -} - -// WaitLog is a helper to wait for a log to be printed. It executes the callback -// when it detects it. -func WaitLog(msg string, t time.Duration) (zerolog.Logger, func(t *testing.T)) { - reader, writer := io.Pipe() - done := make(chan struct{}) - found := false - - buffer := new(bytes.Buffer) - tee := io.TeeReader(reader, buffer) - - go func() { - select { - case <-done: - case <-time.After(t): - writer.Close() - } - }() - - go func() { - defer close(done) - - data := make([]byte, 1024) - - for { - n, err := tee.Read(data) - if err != nil { - return - } - - if strings.Contains(string(data[:n]), fmt.Sprintf(`"%s"`, msg)) { - found = true - return - } - } - }() - - wait := func(t *testing.T) { - <-done - if !found { - t.Fatalf("log not found in %s", buffer.String()) - } - } - - return zerolog.New(writer), wait -} - -// CheckLog returns a logger and a check function. When called, the function -// will verify if the logger has seen the message printed. -func CheckLog(msg string) (zerolog.Logger, func(t *testing.T)) { - buffer := new(bytes.Buffer) - - check := func(t *testing.T) { - require.Contains(t, buffer.String(), fmt.Sprintf(`"%s"`, msg)) - } - - return zerolog.New(buffer), check -} diff --git a/dela/internal/testing/fake/serde.go b/dela/internal/testing/fake/serde.go deleted file mode 100644 index b44f606..0000000 --- a/dela/internal/testing/fake/serde.go +++ /dev/null @@ -1,201 +0,0 @@ -package fake - -import ( - "crypto/rand" - "encoding/json" - "io" - - "go.dedis.ch/dela/serde" -) - -func init() { - // A random value is injected to prevent hardcoded value in the tests. - fakeFmtValue = make([]byte, 8) - rand.Read(fakeFmtValue) -} - -const ( - // GoodFormat should register working format engines. - GoodFormat = serde.Format("FakeGood") - - // BadFormat should register non-working format engines. - BadFormat = serde.Format("FakeBad") - - // MsgFormat should register an engine for fake.Message. - MsgFormat = serde.Format("FakeMsg") -) - -var fakeFmtValue []byte - -// GetFakeFormatValue returns the value of the fake format serialization. -func GetFakeFormatValue() []byte { - return append([]byte{}, fakeFmtValue...) -} - -// Message is a fake implementation if a serde message. -// -// - implements serde.Message -type Message struct { - Digest []byte -} - -// Fingerprint implements serde.Fingerprinter. -func (m Message) Fingerprint(w io.Writer) error { - w.Write(m.Digest) - return nil -} - -// Serialize implements serde.Message. -func (m Message) Serialize(ctx serde.Context) ([]byte, error) { - return ctx.Marshal(struct{}{}) -} - -// MessageFactory is a fake implementation of a serde factory. -// -// - implements serde.Factory -type MessageFactory struct { - err error -} - -// NewBadMessageFactory returns a new message factory that returns an error. -func NewBadMessageFactory() MessageFactory { - return MessageFactory{err: fakeErr} -} - -// Deserialize implements serde.Factory. -func (f MessageFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return Message{}, f.err -} - -// Format is a fake format engine implementation. -// -// - implements serde.FormatEngine -type Format struct { - err error - Msg serde.Message - Call *Call -} - -// NewBadFormat returns a new format engine that always return an error. -func NewBadFormat() Format { - return Format{err: fakeErr} -} - -// Encode implements serde.FormatEngine. -func (f Format) Encode(ctx serde.Context, m serde.Message) ([]byte, error) { - f.Call.Add(ctx, m) - - return GetFakeFormatValue(), f.err -} - -// Decode implements serde.FormatEngine. -func (f Format) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - f.Call.Add(ctx, data) - return f.Msg, f.err -} - -// MessageFormat is a format engine to encode and decode fake messages. -// -// - implements serde.FormatEngine -type MessageFormat struct{} - -// NewMsgFormat creates a new format. -func NewMsgFormat() MessageFormat { - return MessageFormat{} -} - -// Encode implements serde.FormatEngine. -func (f MessageFormat) Encode(ctx serde.Context, m serde.Message) ([]byte, error) { - return Message{}.Serialize(ctx) -} - -// Decode implements serde.FormatEngine. -func (f MessageFormat) Decode(serde.Context, []byte) (serde.Message, error) { - return Message{}, nil -} - -// ContextEngine is a fake implementation of a serde context engine that is -// using JSON as the underlying marshaler. -// -// - implements serde.ContextEngine -type ContextEngine struct { - Count *Counter - format serde.Format - err error -} - -// NewContext returns a new serde context. -func NewContext() serde.Context { - return serde.NewContext(ContextEngine{ - format: GoodFormat, - }) -} - -// NewContextWithFormat returns a new serde context that is using the provided -// format. -func NewContextWithFormat(f serde.Format) serde.Context { - return serde.NewContext(ContextEngine{ - format: f, - }) -} - -// NewBadContext returns a new serde context that produces errors. -func NewBadContext() serde.Context { - return serde.NewContext(ContextEngine{ - format: BadFormat, - err: fakeErr, - }) -} - -// NewBadContextWithDelay returns a new serde context that produces errors after -// some calls. -func NewBadContextWithDelay(delay int) serde.Context { - return serde.NewContext(ContextEngine{ - Count: &Counter{Value: delay}, - format: BadFormat, - err: fakeErr, - }) -} - -// NewMsgContext returns a new serde context that always produces a fake -// message. -func NewMsgContext() serde.Context { - return serde.NewContext(ContextEngine{ - format: MsgFormat, - }) -} - -// GetFormat implements serde.ContextEngine. -func (ctx ContextEngine) GetFormat() serde.Format { - return ctx.format -} - -// Marshal implements serde.ContextEngine. -func (ctx ContextEngine) Marshal(m interface{}) ([]byte, error) { - data, err := json.Marshal(m) - if err != nil { - return nil, err - } - - if !ctx.Count.Done() { - ctx.Count.Decrease() - return data, nil - } - - return data, ctx.err -} - -// Unmarshal implements serde.ContextEngine. -func (ctx ContextEngine) Unmarshal(data []byte, m interface{}) error { - err := json.Unmarshal(data, m) - if err != nil { - return err - } - - if !ctx.Count.Done() { - ctx.Count.Decrease() - return nil - } - - return ctx.err -} diff --git a/dela/internal/testing/fake/store.go b/dela/internal/testing/fake/store.go deleted file mode 100644 index 4670df9..0000000 --- a/dela/internal/testing/fake/store.go +++ /dev/null @@ -1,211 +0,0 @@ -package fake - -import ( - "go.dedis.ch/dela/core/store" - "go.dedis.ch/dela/core/store/kv" -) - -// InMemorySnapshot is a fake implementation of a store snapshot. -// -// - implements store.Snapshot -type InMemorySnapshot struct { - store.Snapshot - - values map[string][]byte - ErrRead error - ErrWrite error - ErrDelete error -} - -// NewSnapshot creates a new empty snapshot. -func NewSnapshot() *InMemorySnapshot { - return &InMemorySnapshot{ - values: make(map[string][]byte), - } -} - -// NewBadSnapshot creates a new empty snapshot that will always return an error. -func NewBadSnapshot() *InMemorySnapshot { - return &InMemorySnapshot{ - values: make(map[string][]byte), - ErrRead: fakeErr, - ErrWrite: fakeErr, - ErrDelete: fakeErr, - } -} - -// Get implements store.Snapshot. -func (snap *InMemorySnapshot) Get(key []byte) ([]byte, error) { - return snap.values[string(key)], snap.ErrRead -} - -// Set implements store.Snapshot. -func (snap *InMemorySnapshot) Set(key, value []byte) error { - snap.values[string(key)] = value - - return snap.ErrWrite -} - -// Delete implements store.Snapshot. -func (snap *InMemorySnapshot) Delete(key []byte) error { - delete(snap.values, string(key)) - - return snap.ErrDelete -} - -// InMemoryDB is a fake implementation of a key/value storage. -// -// - implements kv.DB -type InMemoryDB struct { - buckets map[string]*Bucket - err error - errView error -} - -// NewInMemoryDB returns a new empty database. -func NewInMemoryDB() *InMemoryDB { - return &InMemoryDB{ - buckets: make(map[string]*Bucket), - } -} - -// NewBadDB returns a new database that will return an error inside the -// transactions, and when closing the database. -func NewBadDB() *InMemoryDB { - db := NewInMemoryDB() - db.err = fakeErr - - return db -} - -// NewBadViewDB returns a new database that fails to open view transactions. -func NewBadViewDB() *InMemoryDB { - db := NewInMemoryDB() - db.errView = fakeErr - - return db -} - -// SetBucket allows to define a bucket in the database. -func (db *InMemoryDB) SetBucket(name []byte, b *Bucket) { - db.buckets[string(name)] = b -} - -// View implements kv.DB. -func (db *InMemoryDB) View(fn func(tx kv.ReadableTx) error) error { - if db.errView != nil { - return db.errView - } - - return fn(dbTx{buckets: db.buckets, err: db.err}) -} - -// Update implements kv.DB. -func (db *InMemoryDB) Update(fn func(tx kv.WritableTx) error) error { - return fn(dbTx{buckets: db.buckets, err: db.err}) -} - -// Close implements kv.DB. -func (db *InMemoryDB) Close() error { - return db.err -} - -// dbTx is a fake implementation of a database transaction. -// -// - implements kv.WritableTx -type dbTx struct { - buckets map[string]*Bucket - err error -} - -// GetBucket implements kv.ReadableTx. -func (tx dbTx) GetBucket(name []byte) kv.Bucket { - bucket, found := tx.buckets[string(name)] - if !found { - return nil - } - - return bucket -} - -// GetBucketOrCreate implements kv.WritableTx. -func (tx dbTx) GetBucketOrCreate(name []byte) (kv.Bucket, error) { - return tx.GetBucket(name), tx.err -} - -// GetBucketOrCreate implements store.Transaction. -func (dbTx) OnCommit(fn func()) {} - -// Bucket is a fake key/value storage bucket. -// -// - implements kv.Bucket -type Bucket struct { - kv.Bucket - - values map[string][]byte - errSet error - errDelete error - errForeach error -} - -// NewBucket returns a new empty bucket. -func NewBucket() *Bucket { - return &Bucket{ - values: make(map[string][]byte), - } -} - -// NewBadWriteBucket returns a new empty bucket that fails to write. -func NewBadWriteBucket() *Bucket { - b := NewBucket() - b.errSet = fakeErr - - return b -} - -// NewBadDeleteBucket returns a new empty bucket that fails to delete. -func NewBadDeleteBucket() *Bucket { - b := NewBucket() - b.errDelete = fakeErr - - return b -} - -// NewBadForeachBucket returns a new empty bucket that fails to iterate -func NewBadForeachBucket() *Bucket { - b := NewBucket() - b.errForeach = fakeErr - - return b -} - -// Get implements kv.Bucket. -func (b *Bucket) Get(key []byte) []byte { - return b.values[string(key)] -} - -// Set implements kv.Bucket. -func (b *Bucket) Set(key, value []byte) error { - b.values[string(key)] = value - - return b.errSet -} - -// Delete implements kv.Bucket. -func (b *Bucket) Delete(key []byte) error { - delete(b.values, string(key)) - - return b.errDelete -} - -// ForEach implements kv.Bucket. -func (b *Bucket) ForEach(fn func(key, value []byte) error) error { - for key, value := range b.values { - err := fn([]byte(key), value) - if err != nil { - return err - } - } - - return b.errForeach -} diff --git a/dela/internal/testing/fake/tracing.go b/dela/internal/testing/fake/tracing.go deleted file mode 100644 index 96f7b3c..0000000 --- a/dela/internal/testing/fake/tracing.go +++ /dev/null @@ -1,15 +0,0 @@ -package fake - -import opentracing "github.com/opentracing/opentracing-go" - -// GetTracerForAddrWithError is used to mock `tracing.GetTracerForAddr` with an -// error. -func GetTracerForAddrWithError(addr string) (opentracing.Tracer, error) { - return nil, fakeErr -} - -// GetTracerForAddrEmpty is used to mock `tracing.GetTracerForAddr` with an -// empty tracer. -func GetTracerForAddrEmpty(_ string) (opentracing.Tracer, error) { - return opentracing.NoopTracer{}, nil -} diff --git a/dela/internal/testing/mod.go b/dela/internal/testing/mod.go deleted file mode 100644 index 4ca9d70..0000000 --- a/dela/internal/testing/mod.go +++ /dev/null @@ -1,36 +0,0 @@ -package testing - -import ( - "reflect" - "strings" - "testing" - - "github.com/golang/protobuf/descriptor" - "github.com/golang/protobuf/proto" - "github.com/stretchr/testify/require" -) - -// CoverProtoMessage triggers a test on the message definition to force the -// coverage to be taken in account. -func CoverProtoMessage(t *testing.T, message proto.Message) error { - buffer, err := proto.Marshal(message) - require.NoError(t, err) - err = proto.Unmarshal(buffer, message) - require.NoError(t, err) - proto.Merge(message, message) - proto.DiscardUnknown(message) - require.NotNil(t, message.String()) - message.(descriptor.Message).Descriptor() - - // Run the getters - tt := reflect.TypeOf(message) - for i := 0; i < tt.NumMethod(); i++ { - m := tt.Method(i).Name - if strings.HasPrefix(m, "Get") { - reflect.ValueOf(message).MethodByName(m).Call(nil) - reflect.Zero(tt).MethodByName(m).Call(nil) - } - } - - return nil -} diff --git a/dela/internal/tracing/mod.go b/dela/internal/tracing/mod.go deleted file mode 100644 index d9f159e..0000000 --- a/dela/internal/tracing/mod.go +++ /dev/null @@ -1,82 +0,0 @@ -package tracing - -import ( - "io" - "sync" - - opentracing "github.com/opentracing/opentracing-go" - _ "github.com/uber/jaeger-client-go" - jaegercfg "github.com/uber/jaeger-client-go/config" - "golang.org/x/xerrors" -) - -type key int - -// ProtocolKey is the key used to denote a protocol in a `context.Context`. -const ProtocolKey key = iota - -var ( - // ProtocolTag specified the span tag used for denoting a protocol. - ProtocolTag = "protocol" - // UndefinedProtocol is the default ProtocolTag value used if no - // ProtocolKey is present in the context. - UndefinedProtocol = "__UNDEFINED_PROTOCOL__" -) - -type tracerCatalog struct { - sync.Mutex - - tracerByAddr map[string]closableTracer -} - -type closableTracer struct { - tracer opentracing.Tracer - closer io.Closer -} - -var catalog = tracerCatalog{ - tracerByAddr: make(map[string]closableTracer), -} - -// GetTracerForAddr returns an `opentracing.Tracer` instance for the given -// address. Since the tracers are cached, it returns an existing one if it -// has been initialized before. -func GetTracerForAddr(addr string) (opentracing.Tracer, error) { - catalog.Lock() - defer catalog.Unlock() - - tc, ok := catalog.tracerByAddr[addr] - if ok { - return tc.tracer, nil - } - - cfg, err := jaegercfg.FromEnv() - if err != nil { - return nil, xerrors.Errorf("failed to parse jaeger configuration from environment: %v", err) - } - - cfg.ServiceName = addr - tracer, closer, err := cfg.NewTracer() - if err != nil { - return nil, xerrors.Errorf("failed to create new tracer: %v", err) - } - - catalog.tracerByAddr[addr] = closableTracer{ - tracer: tracer, - closer: closer, - } - - return tracer, nil -} - -// CloseAll closes all the tracer instances. -func CloseAll() error { - for addr, tc := range catalog.tracerByAddr { - err := tc.closer.Close() - if err != nil { - return xerrors.Errorf("failed to close tracer for %s: %v", addr, err) - } - } - - return nil -} diff --git a/dela/internal/traffic/mod.go b/dela/internal/traffic/mod.go deleted file mode 100644 index a437d8b..0000000 --- a/dela/internal/traffic/mod.go +++ /dev/null @@ -1,447 +0,0 @@ -package traffic - -import ( - "bytes" - "context" - "fmt" - "io" - "math" - "os" - "regexp" - "sort" - "sync" - "time" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/core" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router" - "golang.org/x/xerrors" - "google.golang.org/grpc/metadata" -) - -// EnvVariable is the name of the environment variable to enable the traffic. -const EnvVariable = "MINO_TRAFFIC" - -// -// Traffic is a utility to save network information. It can be useful for -// debugging and to understand how the network works. Each server has an -// instance of traffic but does not store logs by default. To do that you can -// set MINO_TRAFFIC=log as varenv. You can also set MINO_TRAFFIC=print to print -// the packets. -// -// There is the possibility to save a graphviz representation of network -// activity. The following snippet shows practical use of it: -// -// defer func() { -// minogrpc.SaveItems("graph.dot", true, true) -// minogrpc.SaveEvents("events.dot") -// }() -// -// Then you can generate a PDF with `dot -Tpdf graph.dot -o graph.pdf`, or -// MINO_TRAFFIC=log go test -run TestPedersen_Scenario && \ -// dot -Tpdf graph.dot -o graph.pdf -// - -// watcherSize is the size of the watcher channels -const watcherSize = 100 - -var ( - eachLine = regexp.MustCompile(`(?m)^(.+)$`) - globalCounter = atomicCounter{} - sendCounter = &atomicCounter{} - recvCounter = &atomicCounter{} - eventCounter = &atomicCounter{} - traffics = trafficSlice{} - // LogItems allows one to granularly say when items should be logged or not. - // This is useful for example in an integration test where a specific part - // raises a problem but the full graph would be too noisy. For that, one - // can set LogItems = false and change it to true when needed. - LogItems = true - // LogEvent works the same as LogItems but for events. Note that in both - // cases the varenv should be set. - LogEvent = true - - headerURIKey = "apiuri" -) - -// GlobalWatcher can be used to watch for sent and received messages. -var GlobalWatcher = Watcher{ - outWatcher: core.NewWatcher(), - inWatcher: core.NewWatcher(), -} - -// Watcher defines an element to watch for sent and received messages. -type Watcher struct { - outWatcher core.Observable - inWatcher core.Observable -} - -// WatchOuts returns a channel populated with sent messages. -func (w *Watcher) WatchOuts(ctx context.Context) <-chan Event { - return watch(ctx, w.outWatcher) -} - -// WatchIns returns a channel populated with received messages. -func (w *Watcher) WatchIns(ctx context.Context) <-chan Event { - return watch(ctx, w.inWatcher) -} - -// watch is a generic function to watch for events -func watch(ctx context.Context, watcher core.Observable) <-chan Event { - obs := observer{ch: make(chan Event, watcherSize)} - - watcher.Add(obs) - - go func() { - <-ctx.Done() - watcher.Remove(obs) - close(obs.ch) - }() - - return obs.ch -} - -// SaveItems saves all the items as a graph -func SaveItems(path string, withSend, withRcv bool) error { - f, err := os.Create(path) - if err != nil { - return xerrors.Errorf("file: %v", err) - } - - GenerateItemsGraphviz(f, withSend, withRcv, traffics...) - return nil -} - -// SaveEvents saves all the events as a graph -func SaveEvents(path string) error { - f, err := os.Create(path) - if err != nil { - return xerrors.Errorf("file: %v", err) - } - - GenerateEventGraphviz(f, traffics...) - return nil -} - -// Traffic is used to keep track of packets Traffic in a server -type Traffic struct { - sync.Mutex - out io.Writer - src mino.Address - items []item - events []event -} - -// NewTraffic creates a new empty traffic recorder. -func NewTraffic(src mino.Address, out io.Writer) *Traffic { - traffic := &Traffic{ - src: src, - items: make([]item, 0), - out: out, - } - - traffics = append(traffics, traffic) - - return traffic -} - -// Save saves the items graph to the given address. -func (t *Traffic) Save(path string, withSend, withRcv bool) error { - f, err := os.Create(path) - if err != nil { - return xerrors.Errorf("file: %v", err) - } - - GenerateItemsGraphviz(f, withSend, withRcv, t) - return nil -} - -// LogSend records a packet sent by the node. It records the node address as the -// sender and the gateway as the receiver, while also recording the packet -// itself. -func (t *Traffic) LogSend(ctx context.Context, gateway mino.Address, pkt router.Packet) { - GlobalWatcher.outWatcher.Notify(Event{Address: gateway, Pkt: pkt}) - - t.addItem(ctx, "send", gateway, pkt) -} - -// LogRecv records a packet received by the node. The sender is the gateway and -// the receiver the node. -func (t *Traffic) LogRecv(ctx context.Context, gateway mino.Address, pkt router.Packet) { - GlobalWatcher.inWatcher.Notify(Event{Address: gateway, Pkt: pkt}) - - t.addItem(ctx, "received", gateway, pkt) -} - -// LogRelay records a new relay. -func (t *Traffic) LogRelay(to mino.Address) { - t.addEvent("relay", to) -} - -// LogRelayClosed records the end of a relay. -func (t *Traffic) LogRelayClosed(to mino.Address) { - t.addEvent("close", to) -} - -// Display prints the current traffic to the writer. -func (t *Traffic) Display(out io.Writer) { - t.Lock() - defer t.Unlock() - - fmt.Fprint(out, "- traffic:\n") - var buf bytes.Buffer - for _, item := range t.items { - item.Display(&buf) - } - fmt.Fprint(out, eachLine.ReplaceAllString(buf.String(), "-$1")) -} - -func (t *Traffic) addItem(ctx context.Context, typeStr string, gw mino.Address, msg router.Packet) { - if t == nil || !LogItems { - return - } - - t.Lock() - defer t.Unlock() - - newItem := item{ - src: t.src, - gateway: gw, - typeStr: typeStr, - msg: msg, - context: t.getContext(ctx), - globalCounter: globalCounter.IncrementAndGet(), - } - - switch typeStr { - case "received": - newItem.typeCounter = recvCounter.IncrementAndGet() - case "send": - newItem.typeCounter = sendCounter.IncrementAndGet() - } - - fmt.Fprintf(t.out, "\n> %v\n", t.src) - newItem.Display(t.out) - - t.items = append(t.items, newItem) -} - -func (t *Traffic) addEvent(typeStr string, to mino.Address) { - if t == nil || !LogEvent { - return - } - - t.Lock() - defer t.Unlock() - - event := event{ - typeStr: typeStr, - from: t.src, - to: to, - globalCounter: eventCounter.IncrementAndGet(), - } - - fmt.Fprintf(t.out, "\n> %v\n", t.src) - event.Display(t.out) - - t.events = append(t.events, event) -} - -func (t *Traffic) getContext(ctx context.Context) string { - headers, ok := metadata.FromIncomingContext(ctx) - if !ok { - headers, ok = metadata.FromOutgoingContext(ctx) - if !ok { - return "" - } - } - - values := headers.Get(headerURIKey) - if len(values) == 0 { - return "" - } - - return values[0] -} - -func (t *Traffic) getFirstCounter() int { - if len(t.items) > 0 { - return t.items[0].globalCounter - } - - if len(t.events) > 0 { - return t.events[0].globalCounter - } - - return math.MaxInt64 -} - -type item struct { - typeStr string - src mino.Address - gateway mino.Address - msg router.Packet - context string - globalCounter int - typeCounter int -} - -func (p item) Display(out io.Writer) { - fmt.Fprint(out, "- item:\n") - fmt.Fprintf(out, "-- typeStr: %s\n", p.typeStr) - fmt.Fprintf(out, "-- node: %v\n", p.src) - fmt.Fprintf(out, "-- gateway: %v\n", p.gateway) - fmt.Fprintf(out, "-- msg: (type %T) %s\n", p.msg, p.msg) - if p.msg != nil { - fmt.Fprintf(out, "--- To: %v\n", p.msg.GetDestination()) - } - fmt.Fprintf(out, "-- context: %s\n", p.context) -} - -// GenerateItemsGraphviz creates a graphviz representation of the items. One can -// generate a graphical representation with `dot -Tpdf graph.dot -o graph.pdf` -func GenerateItemsGraphviz(out io.Writer, withSend, withRcv bool, traffics ...*Traffic) { - - sort.Sort(trafficSlice(traffics)) - - fmt.Fprintf(out, "digraph network_activity {\n") - fmt.Fprintf(out, "labelloc=\"t\";") - fmt.Fprintf(out, "label =
(generated %s)>;", len(traffics), time.Now().Format("2 Jan 06 - 15:04:05")) - fmt.Fprintf(out, "graph [fontname = \"helvetica\"];") - fmt.Fprintf(out, "graph [fontname = \"helvetica\"];") - fmt.Fprintf(out, "node [fontname = \"helvetica\"];") - fmt.Fprintf(out, "edge [fontname = \"helvetica\"];") - - for _, traffic := range traffics { - for _, item := range traffic.items { - - if !withSend && item.typeStr == "send" { - continue - } - if !withRcv && item.typeStr == "received" { - continue - } - - color := "#4AB2FF" - - if item.typeStr == "received" { - color = "#A8A8A8" - } - - var toStr, from, msgStr string - - if item.msg != nil { - for _, to := range item.msg.GetDestination() { - toStr += fmt.Sprintf("to \"%v\"
", to) - } - - from = item.msg.GetSource().String() - - msgStr = fmt.Sprintf("from \"%v\"
%s
", - from, toStr) - } - - fmt.Fprintf(out, "\"%v\" -> \"%v\" "+ - "[ label = < %d (%d)
%s> color=\"%s\" ];\n", - item.src, item.gateway, item.typeCounter, item.globalCounter, msgStr, color) - } - } - fmt.Fprintf(out, "}\n") -} - -// GenerateEventGraphviz creates a graphviz representation of the events -func GenerateEventGraphviz(out io.Writer, traffics ...*Traffic) { - - sort.Sort(trafficSlice(traffics)) - - fmt.Fprintf(out, "digraph network_activity {\n") - fmt.Fprintf(out, "labelloc=\"t\";") - fmt.Fprintf(out, "label =
(generated %s)>;", len(traffics), time.Now().Format("2 Jan 06 - 15:04:05")) - fmt.Fprintf(out, "graph [fontname = \"helvetica\"];") - fmt.Fprintf(out, "graph [fontname = \"helvetica\"];") - fmt.Fprintf(out, "node [fontname = \"helvetica\"];") - fmt.Fprintf(out, "edge [fontname = \"helvetica\"];") - - for _, t := range traffics { - for _, event := range t.events { - - msgStr := event.typeStr - - color := "#4AB2FF" - - if event.typeStr == "close" { - color = "#A8A8A8" - } - - fmt.Fprintf(out, "\"%v\" -> \"%v\" "+ - "[ label = < %d
%s> color=\"%s\" ];\n", - event.from, event.to, event.globalCounter, msgStr, color) - } - } - - fmt.Fprintf(out, "}\n") -} - -// This counter only makes sense when all the nodes are running locally. It is -// useful to analyse the traffic in a developping/test environment, when packets -// order makes sense. -type atomicCounter struct { - sync.Mutex - c int -} - -func (c *atomicCounter) IncrementAndGet() int { - c.Lock() - defer c.Unlock() - c.c++ - return c.c -} - -type event struct { - typeStr string - from mino.Address - to mino.Address - globalCounter int -} - -func (e event) Display(out io.Writer) { - fmt.Fprintf(out, "%s\t%s:\n", e.typeStr, e.to) -} - -type trafficSlice []*Traffic - -func (tt trafficSlice) Len() int { - return len(tt) -} - -func (tt trafficSlice) Less(i, j int) bool { - return tt[i].getFirstCounter() < tt[j].getFirstCounter() -} - -func (tt trafficSlice) Swap(i, j int) { - tt[i], tt[j] = tt[j], tt[i] -} - -// observer defines an observer that fills a channel to notify. -// -// - implements core.Observer -type observer struct { - ch chan Event -} - -// NotifyCallback implements core.Observer. It drops the message if the channel -// is full. -func (o observer) NotifyCallback(event interface{}) { - select { - case o.ch <- event.(Event): - default: - dela.Logger.Warn().Msg("event channel full, dropping") - } -} - -// Event defines the elements of a receive or sent event -type Event struct { - Address mino.Address - Pkt router.Packet -} diff --git a/dela/internal/traffic/mod_test.go b/dela/internal/traffic/mod_test.go deleted file mode 100644 index 7e5931a..0000000 --- a/dela/internal/traffic/mod_test.go +++ /dev/null @@ -1,237 +0,0 @@ -package traffic - -import ( - "bytes" - "context" - "io" - "os" - "path/filepath" - "runtime" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router" - "google.golang.org/grpc/metadata" -) - -func TestTraffic_Integration(t *testing.T) { - src := fake.NewAddress(0) - a2 := fake.NewAddress(1) - gw := fake.NewAddress(2) - - traffic := NewTraffic(src, io.Discard) - - header := metadata.New(map[string]string{headerURIKey: "test"}) - ctx := metadata.NewOutgoingContext(context.Background(), header) - - traffic.LogRecv(ctx, gw, newFakePacket(src, a2)) - traffic.LogSend(context.Background(), gw, newFakePacket(fake.NewAddress(0))) - - buffer := new(bytes.Buffer) - traffic.Display(buffer) - - expected := `- traffic: --- item: ---- typeStr: received ---- node: fake.Address[0] ---- gateway: fake.Address[2] ---- msg: (type traffic.fakePacket) fakePacket ----- To: [fake.Address[1]] ---- context: test --- item: ---- typeStr: send ---- node: fake.Address[0] ---- gateway: fake.Address[2] ---- msg: (type traffic.fakePacket) fakePacket ----- To: [] ---- context: -` - require.Equal(t, expected, buffer.String()) - - file := filepath.Join(os.TempDir(), "minogrpc-test-traffic") - - err := traffic.Save(file, true, true) - require.NoError(t, err) - - defer os.Remove(file) - - content, err := os.ReadFile(file) - require.NoError(t, err) - require.True(t, len(content) > 0) -} - -func TestSaveItems(t *testing.T) { - path, err := os.MkdirTemp("", "go-test-save-items") - require.NoError(t, err) - - defer os.RemoveAll(path) - - if runtime.GOOS == "windows" { - return - } - - err = os.Chmod(path, 0000) - require.NoError(t, err) - - err = SaveItems(path+"/items.dot", true, true) - require.Contains(t, err.Error(), "permission denied") -} - -func TestSaveEvents(t *testing.T) { - path, err := os.MkdirTemp("", "go-test-save-events") - require.NoError(t, err) - - defer os.RemoveAll(path) - - if runtime.GOOS == "windows" { - return - } - - err = os.Chmod(path, 0000) - require.NoError(t, err) - - err = SaveEvents(path + "/events.dot") - require.Contains(t, err.Error(), "permission denied") -} - -func TestTraffic_Save(t *testing.T) { - traffic := NewTraffic(fake.NewAddress(0), io.Discard) - - path, err := os.MkdirTemp("", "go-test-traffic-save") - require.NoError(t, err) - - defer os.RemoveAll(path) - - if runtime.GOOS == "windows" { - return - } - - err = os.Chmod(path, 0000) - require.NoError(t, err) - - err = traffic.Save(path+"/traffic.dot", false, false) - require.Contains(t, err.Error(), "permission denied") -} - -func TestTraffic_LogRecv(t *testing.T) { - var traffic *Traffic - - // Should not panic - traffic.LogRecv(context.Background(), nil, nil) - - traffic = NewTraffic(fake.NewAddress(0), io.Discard) - require.Len(t, traffic.items, 0) - - traffic.LogRecv(context.Background(), nil, nil) - require.Len(t, traffic.items, 1) - - ctx := metadata.NewOutgoingContext(context.Background(), metadata.New(nil)) - traffic.LogRecv(ctx, nil, nil) - require.Len(t, traffic.items, 2) -} - -func TestTraffic_LogRelay(t *testing.T) { - var traffic *Traffic - traffic.LogRelay(fake.NewAddress(1)) - - traffic = NewTraffic(fake.NewAddress(0), io.Discard) - require.Len(t, traffic.events, 0) - - traffic.LogRelay(fake.NewAddress(5)) - require.Len(t, traffic.events, 1) -} - -func TestGenerateItemsGraphViz(t *testing.T) { - traffic := NewTraffic(fake.NewAddress(0), io.Discard) - traffic.LogRecv(context.Background(), fake.NewAddress(1), newFakePacket(fake.NewAddress(2))) - traffic.LogSend(context.Background(), fake.NewAddress(1), newFakePacket(fake.NewAddress(2))) - - traffic2 := NewTraffic(fake.NewAddress(1), io.Discard) - - traffic3 := NewTraffic(fake.NewAddress(3), io.Discard) - traffic3.LogRelay(fake.NewAddress(0)) - - buffer := new(bytes.Buffer) - GenerateItemsGraphviz(buffer, false, false, traffic2, traffic, traffic3) - require.NotContains(t, buffer.String(), "received") - require.NotContains(t, buffer.String(), "send") -} - -func TestGenerateEventGraphViz(t *testing.T) { - traffic := NewTraffic(fake.NewAddress(0), io.Discard) - traffic.LogRelay(fake.NewAddress(1)) - traffic.LogRelay(fake.NewAddress(2)) - traffic.LogRelayClosed(fake.NewAddress(2)) - - buffer := new(bytes.Buffer) - GenerateEventGraphviz(buffer, traffic) - require.Contains(t, buffer.String(), `"fake.Address[0]" -> "fake.Address[1]"`) -} - -func TestWatcherIns(t *testing.T) { - watcher := GlobalWatcher - events := watcher.WatchIns(context.Background()) - - traffic := NewTraffic(fake.NewAddress(0), io.Discard) - - addr := fake.NewAddress(0) - pkt := newFakePacket(fake.NewAddress(1), fake.NewAddress(2)) - traffic.LogRecv(context.Background(), addr, pkt) - - select { - case event := <-events: - require.Equal(t, addr, event.Address) - require.Equal(t, pkt, event.Pkt) - default: - t.Error("events not saved") - } -} - -func TestWatcherOuts(t *testing.T) { - watcher := GlobalWatcher - events := watcher.WatchOuts(context.Background()) - - traffic := NewTraffic(fake.NewAddress(0), io.Discard) - - addr := fake.NewAddress(0) - pkt := newFakePacket(fake.NewAddress(1), fake.NewAddress(2)) - traffic.LogSend(context.Background(), addr, pkt) - - select { - case event := <-events: - require.Equal(t, addr, event.Address) - require.Equal(t, pkt, event.Pkt) - default: - t.Error("events not saved") - } -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakePacket struct { - router.Packet - source mino.Address - dest []mino.Address -} - -func newFakePacket(source mino.Address, dest ...mino.Address) router.Packet { - return fakePacket{ - source: source, - dest: dest, - } -} - -func (p fakePacket) GetSource() mino.Address { - return p.source -} - -func (p fakePacket) GetDestination() []mino.Address { - return p.dest -} - -func (p fakePacket) String() string { - return "fakePacket" -} diff --git a/dela/mino/addr.go b/dela/mino/addr.go deleted file mode 100644 index 47d7ab3..0000000 --- a/dela/mino/addr.go +++ /dev/null @@ -1,84 +0,0 @@ -// -// Documentation Last Review: 07.10.2020 -// - -package mino - -// NewAddressIterator returns a new address iterator. -func NewAddressIterator(addrs []Address) AddressIterator { - return &addressIterator{ - addrs: addrs, - } -} - -// addressIterator is an implementation of the iterator for addresses. -// -// - implements mino.AddressIterator -type addressIterator struct { - index int - addrs []Address -} - -// Seek implements mino.AddressIterator. It moves the iterator to a specific -// position. The next address can be either defined or nil, it is the -// responsibility of the caller to verify. -func (it *addressIterator) Seek(index int) { - it.index = index -} - -// HasNext implements mino.AddressIterator. It returns true if there is an -// address available. -func (it *addressIterator) HasNext() bool { - return it.index < len(it.addrs) -} - -// GetNext implements mino.AddressIterator. It returns the address at the -// current index and moves the iterator to the next address. -func (it *addressIterator) GetNext() Address { - res := it.addrs[it.index] - it.index++ - return res -} - -// roster is an implementation of the players interface. It provides helper when -// known addresses need to be grouped into a roster for Mino calls. -// -// - implements mino.Players -type roster struct { - addrs []Address -} - -// NewAddresses is a helper to instantiate a Players interface with only a few -// addresses. -func NewAddresses(addrs ...Address) Players { - return roster{addrs: addrs} -} - -// Take implements mino.Players. It returns a subset of the roster according to -// the filter. -func (r roster) Take(updaters ...FilterUpdater) Players { - filter := &Filter{} - for _, fn := range updaters { - fn(filter) - } - - addrs := make([]Address, len(filter.Indices)) - for i, k := range filter.Indices { - addrs[i] = r.addrs[k] - } - - newRoster := roster{addrs: addrs} - - return newRoster -} - -// AddressIterator implements mino.Players. It returns an iterator for the -// authority. -func (r roster) AddressIterator() AddressIterator { - return NewAddressIterator(r.addrs) -} - -// Len implements mino.Players. It returns the length of the authority. -func (r roster) Len() int { - return len(r.addrs) -} diff --git a/dela/mino/addr_test.go b/dela/mino/addr_test.go deleted file mode 100644 index e6259f8..0000000 --- a/dela/mino/addr_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package mino - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestAddressIterator_Seek(t *testing.T) { - addrs := []Address{fakeAddr{}, fakeAddr{}} - - iter := addressIterator{addrs: addrs} - iter.Seek(1) - require.NotNil(t, iter.GetNext()) - require.False(t, iter.HasNext()) -} - -func TestAddressIterator_HasNext(t *testing.T) { - addrs := []Address{fakeAddr{}, fakeAddr{}} - - iter := addressIterator{addrs: addrs} - require.True(t, iter.HasNext()) - - iter.GetNext() - iter.GetNext() - require.False(t, iter.HasNext()) -} - -func TestAddressIterator_GetNext(t *testing.T) { - addrs := []Address{fakeAddr{}, fakeAddr{}} - - iter := addressIterator{addrs: addrs} - require.NotNil(t, iter.GetNext()) - require.NotNil(t, iter.GetNext()) - require.False(t, iter.HasNext()) -} - -func TestRoster_Take(t *testing.T) { - addrs := NewAddresses(fakeAddr{}, fakeAddr{}, fakeAddr{}) - - addrs2 := addrs.Take(IndexFilter(0), IndexFilter(2)) - require.Equal(t, 2, len(addrs2.(roster).addrs)) -} - -func TestRoster_AddressIterator(t *testing.T) { - addrs := NewAddresses(fakeAddr{}) - - iter := addrs.AddressIterator() - require.NotNil(t, iter.GetNext()) - require.False(t, iter.HasNext()) -} - -func TestRoster_Len(t *testing.T) { - addrs := NewAddresses(fakeAddr{}, fakeAddr{}) - require.Equal(t, 2, addrs.Len()) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeAddr struct { - Address -} diff --git a/dela/mino/gossip/flat.go b/dela/mino/gossip/flat.go deleted file mode 100644 index 75bab41..0000000 --- a/dela/mino/gossip/flat.go +++ /dev/null @@ -1,143 +0,0 @@ -// -// Documentation Last Review: 06.10.2020 -// - -package gossip - -import ( - "context" - "sync" - "time" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -const ( - rumorTimeout = 10 * time.Second -) - -// Flat is an implementation of a message passing protocol that is using a flat -// communication approach by sending a rumor to all the known participants. -// -// - implements gossip.Gossiper -type Flat struct { - sync.RWMutex - mino mino.Mino - rumorFactory serde.Factory - ch chan Rumor -} - -// NewFlat creates a new instance of a flat gossip protocol. -func NewFlat(m mino.Mino, f serde.Factory) *Flat { - return &Flat{ - mino: m, - rumorFactory: f, - ch: make(chan Rumor, 100), - } -} - -// Listen implements gossip.Gossiper. It creates the RPC and starts to listen -// for incoming rumors while spreading its own ones. -func (flat *Flat) Listen() (Actor, error) { - h := handler{Flat: flat} - - actor := &flatActor{ - logger: dela.Logger.With().Str("addr", flat.mino.GetAddress().String()).Logger(), - rpc: mino.MustCreateRPC(flat.mino, "flatgossip", h, flat.rumorFactory), - } - - return actor, nil -} - -// Rumors implements gossip.Gossiper. It returns the channel that is populated -// with new rumors. -func (flat *Flat) Rumors() <-chan Rumor { - return flat.ch -} - -// flatActor is the actor returned by the gossiper that provide the primitives -// to send a rumor. -// -// - implements gossip.Actor -type flatActor struct { - sync.Mutex - - logger zerolog.Logger - rpc mino.RPC - players mino.Players -} - -// SetPlayers implements gossip.Actor. It changes the set of participants where -// the rumors will be sent. -func (a *flatActor) SetPlayers(players mino.Players) { - a.Lock() - a.players = players - a.Unlock() -} - -// Add implements gossip.Actor. It adds the rumor to the pool of rumors. It will -// be spread to the players. -func (a *flatActor) Add(rumor Rumor) error { - a.Lock() - players := a.players - a.Unlock() - - if players == nil { - // Drop rumors if the network is empty. - return nil - } - - ctx, cancel := context.WithTimeout(context.Background(), rumorTimeout) - defer cancel() - - resps, err := a.rpc.Call(ctx, rumor, players) - if err != nil { - return xerrors.Errorf("couldn't call peers: %v", err) - } - - for { - resp, more := <-resps - if !more { - return nil - } - - _, err := resp.GetMessageOrError() - if err != nil { - a.logger.Warn().Err(err).Msg("rumor not sent") - } - } -} - -// Close implements gossip.Actor. It stops the gossip actor. -func (a *flatActor) Close() error { - a.Lock() - a.players = nil - a.Unlock() - - return nil -} - -// Handler processes the messages coming from the gossip network. -// -// - implements mino.Handler -type handler struct { - *Flat - mino.UnsupportedHandler -} - -// Process implements mino.Handler. It notifies the new rumor if appropriate and -// does not return anything. -func (h handler) Process(req mino.Request) (serde.Message, error) { - rumor, ok := req.Message.(Rumor) - if !ok { - return nil, xerrors.Errorf("unexpected rumor of type '%T'", req.Message) - } - - h.ch <- rumor - - return nil, nil -} diff --git a/dela/mino/gossip/flat_test.go b/dela/mino/gossip/flat_test.go deleted file mode 100644 index 7762452..0000000 --- a/dela/mino/gossip/flat_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package gossip - -import ( - "bytes" - "testing" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func TestFlat_Listen(t *testing.T) { - gossiper := NewFlat(fake.Mino{}, nil) - - actor, err := gossiper.Listen() - require.NoError(t, err) - require.NotNil(t, actor) -} - -func TestFlat_Rumors(t *testing.T) { - gossiper := NewFlat(nil, nil) - require.NotNil(t, gossiper.Rumors()) -} - -func TestActor_SetPlayers(t *testing.T) { - actor := &flatActor{} - - actor.SetPlayers(fake.NewAuthority(3, fake.NewSigner)) - require.Equal(t, 3, actor.players.Len()) - - actor.SetPlayers(nil) - require.Nil(t, actor.players) -} - -func TestActor_Add(t *testing.T) { - rpc := fake.NewRPC() - actor := &flatActor{ - rpc: rpc, - players: fake.NewAuthority(3, fake.NewSigner), - } - - rpc.Done() - err := actor.Add(fakeRumor{}) - require.NoError(t, err) - - actor.rpc = fake.NewBadRPC() - err = actor.Add(fakeRumor{}) - require.EqualError(t, err, fake.Err("couldn't call peers")) - - buffer := new(bytes.Buffer) - rpc = fake.NewRPC() - actor.rpc = rpc - actor.logger = zerolog.New(buffer).Level(zerolog.WarnLevel) - rpc.SendResponseWithError(nil, fake.GetError()) - rpc.Done() - - err = actor.Add(fakeRumor{}) - require.NoError(t, err) - require.Contains(t, buffer.String(), `"message":"rumor not sent"`) - - actor.players = nil - err = actor.Add(fakeRumor{}) - require.NoError(t, err) -} - -func TestActor_Close(t *testing.T) { - actor := &flatActor{ - players: fake.NewAuthority(3, fake.NewSigner), - } - - require.NoError(t, actor.Close()) - require.Nil(t, actor.players) -} - -func TestHandler_Process(t *testing.T) { - h := handler{ - Flat: &Flat{ - rumorFactory: fakeRumorFactory{}, - ch: make(chan Rumor, 1), - }, - } - - resp, err := h.Process(mino.Request{Message: fakeRumor{}}) - require.NoError(t, err) - require.Nil(t, resp) - - h.rumorFactory = fake.MessageFactory{} - _, err = h.Process(mino.Request{Message: fake.Message{}}) - require.EqualError(t, err, "unexpected rumor of type 'fake.Message'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeRumorFactory struct{} - -func (r fakeRumorFactory) Deserialize(serde.Context, []byte) (serde.Message, error) { - return fakeRumor{}, nil -} - -type fakeRumor struct{} - -func (r fakeRumor) GetID() []byte { - return []byte{0xa} -} - -func (r fakeRumor) Serialize(serde.Context) ([]byte, error) { - return []byte("{}"), nil -} diff --git a/dela/mino/gossip/mod.go b/dela/mino/gossip/mod.go deleted file mode 100644 index 42af2ad..0000000 --- a/dela/mino/gossip/mod.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package gossip defines an abstraction to gossip messages to a defined set of -// participants. -// -// Documentation Last Review: 06.10.2020 -// -package gossip - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// Rumor is the message that must be gossiped through the network. It is using -// the identifier as a unique way to differentiate all the rumors. -type Rumor interface { - serde.Message - - // GetID returns the unique identifier of the rumor. - GetID() []byte -} - -// Actor is an actor that can send rumor to a gossip network. -type Actor interface { - // SetPlayers changes the list of participants that the actor should send - // rumors to. It is up to the implementation to send to only a subset. - SetPlayers(mino.Players) - - // Add adds the rumor in the set of rumors that must be spread to the - // participants. - Add(rumor Rumor) error - - // Close cleans any resource used by the actor. - Close() error -} - -// Gossiper is an abstraction of a message passing protocol that uses internally -// a gossip protocol. -type Gossiper interface { - // Rumors returns a channel populated with the new rumors. - Rumors() <-chan Rumor - - // Listen starts to listen for rumors and returns a gossip actor. - Listen() (Actor, error) -} diff --git a/dela/mino/mino.go b/dela/mino/mino.go deleted file mode 100644 index a9c3d19..0000000 --- a/dela/mino/mino.go +++ /dev/null @@ -1,179 +0,0 @@ -// Package mino defines a minimalist network overlay to communicate between a -// set of participants. -// -// The overlay provides two primitives to send messages: Call that will directly -// contact the addresses and Stream that will build a network and distribute the -// load to forward the messages. -// -// Documentation Last Review: 07.10.2020 -package mino - -import ( - "context" - "encoding" - "errors" - - "go.dedis.ch/dela/serde" -) - -// Mino is an abstraction of a overlay network. It provides primitives to send -// messages to a set of participants. -// -// It uses segments to separate the different RPCs of multiple services in a -// URI-like manner (i.e. /my/awesome/rpc has _my_ and _awesome_ segments). -type Mino interface { - GetAddressFactory() AddressFactory - - // Address returns the address that other participants should use to contact - // this instance. - GetAddress() Address - - // WithSegment returns a new mino instance that will have its URI path - // extended with the provided segment. - WithSegment(segment string) Mino - - // CreateRPC creates an RPC that can send to and receive from a unique - // URI which is computed with URI = (segment || segment || ... || name). If - // the combination already exists, it will return an error. - CreateRPC(name string, h Handler, f serde.Factory) (RPC, error) -} - -// Address is a representation of a node's address. -type Address interface { - encoding.TextMarshaler - - // Equal returns true when both addresses are similar. - Equal(other Address) bool - - // String returns a string representation of the address. - String() string -} - -// AddressFactory is the factory to deserialize addresses. -type AddressFactory interface { - serde.Factory - - // FromText returns the address of the text. - FromText(text []byte) Address -} - -// AddressIterator is an iterator over the list of addresses of a membership. -type AddressIterator interface { - // Seek moves the iterator to a specific index. - Seek(int) - - // HasNext returns true if a address is available, false if the iterator is - // exhausted. - HasNext() bool - - // GetNext returns the next address in case HasNext returns true, otherwise - // no assumption can be done. - GetNext() Address -} - -// Players is an interface to represent a set of nodes participating in a -// message passing protocol. -type Players interface { - // Take should a subset of the players according to the filters. - Take(...FilterUpdater) Players - - // AddressIterator returns an iterator that prevents changes of the - // underlying array and save memory by iterating over the same array. - AddressIterator() AddressIterator - - // Len returns the length of the set of players. - Len() int -} - -// RPC is an abstraction of a distributed Remote Procedure Call. -type RPC interface { - // Call is a basic request to one or multiple distant peers. It directly - // contacts all the players and thus expects a reasonable number of peers. - // - // The response channel must be closed once the request ends in a result, - // either a reply or an error. - Call(ctx context.Context, req serde.Message, players Players) (<-chan Response, error) - - // Stream is a persistent request that will be closed only when the - // orchestrator is done or an error has occured, or the context is done. - Stream(ctx context.Context, players Players) (Sender, Receiver, error) -} - -// Request is a wrapper around the context of a message received from a player -// and that needs to be processed by the node. It provides some useful -// information about the network layer. -type Request struct { - // Address is the address of the sender of the request. - Address Address - - // Message is the message of the request. - Message serde.Message -} - -// Response represents the response of a distributed RPC. It provides the -// address of the original sender and the reply, or an error if something went -// wrong upfront. -type Response interface { - // GetFrom returns the address of the source of the reply. - GetFrom() Address - - // GetMessageOrError returns the message, or an error if something wrong - // happened. - GetMessageOrError() (serde.Message, error) -} - -// Sender is an abstraction to send messages to a stream in the context of a -// distributed RPC. -type Sender interface { - // Send sends the message to all the addresses. It returns a channel that - // will be populated with errors coming from the network layer if the - // message cannot be sent. The channel must be closed after the message has - // been sent or failed to be sent. - Send(msg serde.Message, addrs ...Address) <-chan error -} - -// Receiver is an abstraction to receive messages from a stream in the context -// of a distributed RPC. -type Receiver interface { - // Recv waits for a message to send received from the stream. It returns the - // address of the original sender and the message, or a message if the - // stream is closed or the context is done. - Recv(context.Context) (Address, serde.Message, error) -} - -// Handler is the interface to implement to create a public endpoint. -type Handler interface { - // Process handles a single request by producing the response according to - // the request message. - Process(req Request) (resp serde.Message, err error) - - // Stream is a handler for a stream request. It will open a stream with the - // participants. - Stream(out Sender, in Receiver) error -} - -// UnsupportedHandler implements the Handler interface with default behaviour so -// that an implementation can focus on its needs. -type UnsupportedHandler struct{} - -// Process is the default implementation for a handler. It will return an error. -func (h UnsupportedHandler) Process(req Request) (serde.Message, error) { - return nil, errors.New("rpc is not supported") -} - -// Stream is the default implementation for a handler. It will return an error. -func (h UnsupportedHandler) Stream(in Sender, out Receiver) error { - return errors.New("stream is not supported") -} - -// MustCreateRPC creates the RPC with the given name, handler and factory to the -// Mino instance and expects the operation to be successful, otherwise it will -// panic. -func MustCreateRPC(m Mino, name string, h Handler, f serde.Factory) RPC { - rpc, err := m.CreateRPC(name, h, f) - if err != nil { - panic(err) - } - - return rpc -} diff --git a/dela/mino/mino_test.go b/dela/mino/mino_test.go deleted file mode 100644 index ea6c075..0000000 --- a/dela/mino/mino_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package mino - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func TestUnsupportedHandler_Process(t *testing.T) { - h := UnsupportedHandler{} - resp, err := h.Process(Request{}) - require.Error(t, err) - require.Nil(t, resp) -} - -func TestUnsupportedHandler_Stream(t *testing.T) { - h := UnsupportedHandler{} - require.Error(t, h.Stream(nil, nil)) -} - -func TestMustCreateRPC(t *testing.T) { - rpc := MustCreateRPC(fakeMino{}, "name", nil, nil) - require.NotNil(t, rpc) -} - -func TestMustCreateRPC_Panic(t *testing.T) { - defer func() { - err := recover().(error) - require.EqualError(t, err, "rpc_error") - }() - - MustCreateRPC(fakeMino{err: xerrors.New("rpc_error")}, "name", nil, nil) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeRPC struct { - RPC -} - -type fakeMino struct { - Mino - - err error -} - -func (m fakeMino) CreateRPC(name string, h Handler, f serde.Factory) (RPC, error) { - return fakeRPC{}, m.err -} diff --git a/dela/mino/minoch/address.go b/dela/mino/minoch/address.go deleted file mode 100644 index f7f8ac5..0000000 --- a/dela/mino/minoch/address.go +++ /dev/null @@ -1,50 +0,0 @@ -// This file implements the address abstraction for an in-memory implementation. -// Each address uses a unique string to identify the instance it belongs to. -// -// Documentation Last Review: 06.10.2020 -// - -package minoch - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// Address is the representation of an identifier for minoch. -// -// - implements mino.Address -type address struct { - orchestrator bool - id string -} - -// Equal implements mino.Address. It returns true if both addresses are equal. -func (a address) Equal(other mino.Address) bool { - addr, ok := other.(address) - return ok && addr.id == a.id -} - -// MarshalText implements encoding.TextMarshaler. It returns the string -// representation of an address. -func (a address) MarshalText() ([]byte, error) { - return []byte(a.id), nil -} - -// String implements fmt.Stringer. It returns the address as a string. -func (a address) String() string { - return a.id -} - -// AddressFactory is a factory to deserialize Minoch addresses. -// -// - implements mino.AddressFactory -type AddressFactory struct { - serde.Factory -} - -// FromText implements mino.AddressFactory. It returns an instance of an address -// from a byte slice. -func (f AddressFactory) FromText(text []byte) mino.Address { - return address{id: string(text)} -} diff --git a/dela/mino/minoch/address_test.go b/dela/mino/minoch/address_test.go deleted file mode 100644 index ba83a85..0000000 --- a/dela/mino/minoch/address_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package minoch - -import ( - "bytes" - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/mino" -) - -func TestAddress_Equal(t *testing.T) { - addr := address{id: "A"} - require.True(t, addr.Equal(addr)) - require.False(t, addr.Equal(address{})) - require.False(t, addr.Equal(fakeAddress{})) -} - -func TestAddress_MarshalText(t *testing.T) { - f := func(id string) bool { - addr := address{id: id} - buffer, err := addr.MarshalText() - require.NoError(t, err) - - return bytes.Equal([]byte(id), buffer) - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestAddress_String(t *testing.T) { - f := func(id string) bool { - addr := address{id: id} - - return id == addr.String() - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestAddressFactory_FromText(t *testing.T) { - f := func(id string) bool { - factory := AddressFactory{} - addr := factory.FromText([]byte(id)) - - return addr.(address).id == id - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -//------------------------------------------------------------------------------ -// Utility functions - -type fakeAddress struct { - mino.Address -} diff --git a/dela/mino/minoch/example_test.go b/dela/mino/minoch/example_test.go deleted file mode 100644 index c6438f3..0000000 --- a/dela/mino/minoch/example_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package minoch - -import ( - "context" - "fmt" - - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func ExampleRPC_Call() { - manager := NewManager() - - minoA := MustCreate(manager, "A").WithSegment("example") - minoB := MustCreate(manager, "B").WithSegment("example") - - rpcA := mino.MustCreateRPC(minoA, "hello", exampleHandler{}, exampleFactory{}) - mino.MustCreateRPC(minoB, "hello", exampleHandler{}, exampleFactory{}) - - roster := mino.NewAddresses(minoA.GetAddress(), minoB.GetAddress()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - msg := exampleMessage{value: "Hello World!"} - - resps, err := rpcA.Call(ctx, msg, roster) - if err != nil { - panic("call failed: " + err.Error()) - } - - for resp := range resps { - reply, err := resp.GetMessageOrError() - if err != nil { - panic("error in response: " + err.Error()) - } - - fmt.Println(reply.(exampleMessage).value) - } - - // Output: Hello World! - // Hello World! -} - -// exampleHandler is an RPC handler example. -// -// - implements mino.Handler -type exampleHandler struct { - mino.UnsupportedHandler -} - -// Process implements mino.Handler. It returns the message received. -func (exampleHandler) Process(req mino.Request) (serde.Message, error) { - return req.Message, nil -} - -// exampleMessage is an example of a message. -// -// - implements serde.Message -type exampleMessage struct { - value string -} - -// Serialize implements serde.Message. It returns the value contained in the -// message. -func (m exampleMessage) Serialize(serde.Context) ([]byte, error) { - return []byte(m.value), nil -} - -// exampleFactory is an example of a factory. -// -// - implements serde.Factory -type exampleFactory struct{} - -// Deserialize implements serde.Factory. It returns the message using data as -// the inner value. -func (exampleFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return exampleMessage{value: string(data)}, nil -} diff --git a/dela/mino/minoch/manager.go b/dela/mino/minoch/manager.go deleted file mode 100644 index f36ccb6..0000000 --- a/dela/mino/minoch/manager.go +++ /dev/null @@ -1,67 +0,0 @@ -// This file implements a manager that will connect the different instances of -// Minoch so that they can communicate between each others. -// -// Documentation Last Review: 06.10.2020 -// - -package minoch - -import ( - "sync" - - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -// Manager manages the communication between the local instances of Mino. -type Manager struct { - sync.Mutex - instances map[string]*Minoch -} - -// NewManager creates a new empty manager. -func NewManager() *Manager { - return &Manager{ - instances: make(map[string]*Minoch), - } -} - -func (m *Manager) get(a mino.Address) (*Minoch, error) { - m.Lock() - defer m.Unlock() - - addr, ok := a.(address) - if !ok { - return nil, xerrors.Errorf("invalid address type '%T'", a) - } - - peer, ok := m.instances[addr.id] - if !ok { - return nil, xerrors.Errorf("address <%s> not found", addr.id) - } - - return peer, nil -} - -func (m *Manager) insert(inst mino.Mino) error { - instance, ok := inst.(*Minoch) - if !ok { - return xerrors.Errorf("invalid instance type '%T'", inst) - } - - if instance.identifier == "" { - return xerrors.New("cannot have an empty identifier") - } - - m.Lock() - defer m.Unlock() - - _, found := m.instances[instance.identifier] - if found { - return xerrors.Errorf("identifier <%s> already exists", instance.identifier) - } - - m.instances[instance.identifier] = instance - - return nil -} diff --git a/dela/mino/minoch/manager_test.go b/dela/mino/minoch/manager_test.go deleted file mode 100644 index 82bacf0..0000000 --- a/dela/mino/minoch/manager_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package minoch - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func TestManager_Get(t *testing.T) { - manager := &Manager{ - instances: map[string]*Minoch{"A": {}}, - } - - m, err := manager.get(address{id: "A"}) - require.NoError(t, err) - require.NotNil(t, m) - - _, err = manager.get(address{id: "B"}) - require.EqualError(t, err, "address not found") - - _, err = manager.get(fake.NewBadAddress()) - require.EqualError(t, err, "invalid address type 'fake.Address'") -} - -func TestManager_Insert(t *testing.T) { - manager := NewManager() - - err := manager.insert(&Minoch{identifier: "A"}) - require.NoError(t, err) - - err = manager.insert(&Minoch{identifier: "A"}) - require.EqualError(t, err, "identifier already exists") - - err = manager.insert(&Minoch{}) - require.EqualError(t, err, "cannot have an empty identifier") - - err = manager.insert(fakeInstance{}) - require.EqualError(t, err, "invalid instance type 'minoch.fakeInstance'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeInstance struct { - mino.Mino -} diff --git a/dela/mino/minoch/mod.go b/dela/mino/minoch/mod.go deleted file mode 100644 index 65d152f..0000000 --- a/dela/mino/minoch/mod.go +++ /dev/null @@ -1,135 +0,0 @@ -// Package minoch is an implementation of Mino that is using channels and a -// local manager to exchange messages. -// -// Because it is using only Go channels to communicate, this implementation can -// only be used by multiple instances in the same process. Its usage is purely -// to simplify the writing of tests, therefore it also provides some additionnal -// functionalities like filters. -// -// A filter is called for any message incoming and it will determine if the -// instance should drop the message. -// -// Documentation Last Review: 06.10.2020 -// -package minoch - -import ( - "fmt" - "sync" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -// Filter is a function called for any request to an RPC which will drop it if -// it returns false. -type Filter func(mino.Request) bool - -// Minoch is an implementation of the Mino interface using channels. Each -// instance must have a unique string assigned to it. -// -// - implements mino.Mino -type Minoch struct { - sync.Mutex - - manager *Manager - identifier string - path string - rpcs map[string]*RPC - context serde.Context - filters []Filter -} - -// NewMinoch creates a new instance of a local Mino instance. -func NewMinoch(manager *Manager, identifier string) (*Minoch, error) { - inst := &Minoch{ - manager: manager, - identifier: identifier, - path: "", - rpcs: make(map[string]*RPC), - context: json.NewContext(), - } - - err := manager.insert(inst) - if err != nil { - return nil, xerrors.Errorf("manager refused: %v", err.Error()) - } - - dela.Logger.Trace().Msgf("New instance with identifier %s", identifier) - - return inst, nil -} - -// MustCreate creates a new minoch instance and panic if the identifier is -// refused by the manager. -func MustCreate(manager *Manager, identifier string) *Minoch { - m, err := NewMinoch(manager, identifier) - if err != nil { - panic(err) - } - - return m -} - -// GetAddressFactory implements mino.Mino. It returns the address factory. -func (m *Minoch) GetAddressFactory() mino.AddressFactory { - return AddressFactory{} -} - -// GetAddress implements mino.Mino. It returns the address that other -// participants should use to contact this instance. -func (m *Minoch) GetAddress() mino.Address { - return address{id: m.identifier} -} - -// AddFilter adds the filter to all of the RPCs. This must be called before -// receiving requests. -func (m *Minoch) AddFilter(filter Filter) { - m.filters = append(m.filters, filter) - - for _, rpc := range m.rpcs { - rpc.filters = m.filters - } -} - -// WithSegment returns a new mino instance that will have its URI path extended -// with the provided segment. -func (m *Minoch) WithSegment(path string) mino.Mino { - newMinoch := &Minoch{ - manager: m.manager, - identifier: m.identifier, - path: fmt.Sprintf("%s/%s", m.path, path), - rpcs: m.rpcs, - } - - return newMinoch -} - -// CreateRPC creates an RPC that can send to and receive from the unique path. -func (m *Minoch) CreateRPC(name string, h mino.Handler, f serde.Factory) (mino.RPC, error) { - rpc := &RPC{ - manager: m.manager, - addr: m.GetAddress(), - path: fmt.Sprintf("%s/%s", m.path, name), - h: h, - context: m.context, - factory: f, - filters: m.filters, - } - - m.Lock() - - _, found := m.rpcs[rpc.path] - if found { - return nil, xerrors.Errorf("rpc '%s' already exists", rpc.path) - } - - m.rpcs[rpc.path] = rpc - - m.Unlock() - - return rpc, nil -} diff --git a/dela/mino/minoch/mod_test.go b/dela/mino/minoch/mod_test.go deleted file mode 100644 index e50dc22..0000000 --- a/dela/mino/minoch/mod_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package minoch - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func TestMinoch_New(t *testing.T) { - manager := NewManager() - - m1 := MustCreate(manager, "A") - require.NotNil(t, m1) - - m2 := MustCreate(manager, "B") - require.NotNil(t, m2) -} - -func TestMinoch_Panic_MustCreate(t *testing.T) { - defer func() { - r := recover().(error) - require.EqualError(t, r, "manager refused: identifier already exists") - }() - - manager := NewManager() - - MustCreate(manager, "A") - MustCreate(manager, "A") -} - -func TestMinoch_GetAddressFactory(t *testing.T) { - m := &Minoch{} - require.IsType(t, AddressFactory{}, m.GetAddressFactory()) -} - -func TestMinoch_GetAddress(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - addr := m.GetAddress() - require.Equal(t, "A", addr.String()) -} - -func TestMinoch_AddFilter(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - rpc := mino.MustCreateRPC(m, "test", nil, nil) - - m.AddFilter(func(m mino.Request) bool { return true }) - require.Len(t, m.filters, 1) - require.Len(t, rpc.(*RPC).filters, 1) -} - -func TestMinoch_WithSegment(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - m2 := m.WithSegment("abc") - require.Equal(t, m.identifier, m2.(*Minoch).identifier) - require.Equal(t, "/abc", m2.(*Minoch).path) -} - -func TestMinoch_CreateRPC(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - rpc, err := m.CreateRPC("rpc_name", fakeHandler{}, fake.MessageFactory{}) - require.NoError(t, err) - require.NotNil(t, rpc) - - _, err = m.CreateRPC("rpc_name", fakeHandler{}, fake.MessageFactory{}) - require.EqualError(t, err, "rpc '/rpc_name' already exists") -} - -type badHandler struct { - mino.UnsupportedHandler -} - -type fakeHandler struct { - mino.UnsupportedHandler -} - -func (h fakeHandler) Process(req mino.Request) (resp serde.Message, err error) { - return fake.Message{}, nil -} diff --git a/dela/mino/minoch/rpc.go b/dela/mino/minoch/rpc.go deleted file mode 100644 index f2b4c67..0000000 --- a/dela/mino/minoch/rpc.go +++ /dev/null @@ -1,300 +0,0 @@ -// This file contains the orchestrator side of the RPC implementation. -// -// Documentation Last Review: 06.10.2020 -// - -package minoch - -import ( - "context" - "io" - "math" - "sync" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// bufSize defines the buffer size of channels used to store messages -var bufSize = 10000 - -// Envelope is the wrapper to send messages through streams. -type Envelope struct { - to []mino.Address - from address - message []byte -} - -// RPC implements a remote procedure call that is calling its peers using the -// channels registered by the manager. -// -// - implements mino.RPC -type RPC struct { - manager *Manager - addr mino.Address - path string - h mino.Handler - context serde.Context - factory serde.Factory - filters []Filter -} - -// Call implements mino.RPC. It sends the message to all participants and -// gathers their replies. The context is ignored in the scope of channel -// communication as there is no blocking I/O. The response channel will receive -// n responses for n players and be closed eventually. -func (c RPC) Call(ctx context.Context, - req serde.Message, players mino.Players) (<-chan mino.Response, error) { - - data, err := req.Serialize(c.context) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize: %v", err) - } - - out := make(chan mino.Response, players.Len()) - - wg := sync.WaitGroup{} - wg.Add(players.Len()) - - iter := players.AddressIterator() - - for iter.HasNext() { - peer, err := c.manager.get(iter.GetNext()) - if err != nil { - // Abort everything if a peer is missing. - return nil, xerrors.Errorf("couldn't find peer: %v", err) - } - - go func(m *Minoch) { - defer wg.Done() - - from := peer.GetAddress() - - msg, err := c.factory.Deserialize(c.context, data) - if err != nil { - resp := mino.NewResponseWithError( - from, - xerrors.Errorf("couldn't deserialize: %v", err), - ) - - out <- resp - return - } - - req := mino.Request{ - Address: c.addr, - Message: msg, - } - - m.Lock() - rpc, ok := m.rpcs[c.path] - m.Unlock() - - if !ok { - resp := mino.NewResponseWithError( - from, - xerrors.Errorf("unknown rpc %s", c.path), - ) - - out <- resp - return - } - - if !rpc.runFilters(req) { - // Message is dropped by one of the filter. - return - } - - resp, err := rpc.h.Process(req) - if err != nil { - resp := mino.NewResponseWithError( - from, - xerrors.Errorf("couldn't process request: %v", err), - ) - - out <- resp - return - } - - out <- mino.NewResponse(from, resp) - }(peer) - } - - // Only wait if all the requests have been correctly started. - go func() { - wg.Wait() - close(out) - }() - - return out, nil -} - -func (c RPC) runFilters(req mino.Request) bool { - for _, filter := range c.filters { - if !filter(req) { - return false - } - } - - return true -} - -// Stream implements mino.RPC. It simulates the stream by using the orchestrator -// as the router for all the messages. They are redirected to the channel -// associated with the address. -func (c RPC) Stream(ctx context.Context, memship mino.Players) (mino.Sender, mino.Receiver, error) { - in := make(chan Envelope, bufSize) - out := make(chan Envelope, bufSize) - errs := make(chan error, 1000) - - outs := make(map[string]receiver) - - iter := memship.AddressIterator() - for iter.HasNext() { - addr := iter.GetNext() - - peer, err := c.manager.get(addr) - if err != nil { - return nil, nil, xerrors.Errorf("couldn't find peer: %v", err) - } - - ch := make(chan Envelope, bufSize*100) - outs[addr.String()] = receiver{ - out: ch, - context: c.context, - factory: c.factory, - } - - go func(r receiver) { - s := sender{ - addr: peer.GetAddress(), - in: in, - context: c.context, - } - - err := peer.rpcs[c.path].h.Stream(s, r) - if err != nil { - errs <- xerrors.Errorf("couldn't process: %v", err) - } - }(outs[addr.String()]) - } - - orchAddr := c.addr.(address) - orchAddr.orchestrator = true - - orchSender := sender{ - addr: orchAddr, - in: in, - context: c.context, - } - - orchRecv := receiver{ - out: out, - errs: errs, - context: c.context, - factory: c.factory, - } - - go func() { - for { - select { - case <-ctx.Done(): - // closes the orchestrator.. - close(out) - // closes the participants.. - for _, r := range outs { - close(r.out) - } - return - case env := <-in: - for _, to := range env.to { - output := orchRecv.out - if !to.(address).orchestrator { - output = outs[to.String()].out - } - - select { - case output <- env: - default: - dela.Logger.Warn().Str("to", to.String()). - Str("from", env.from.String()).Msg("full") - output <- env - } - } - } - } - }() - - return orchSender, orchRecv, nil -} - -// Sender implements the sender associated with a stream. -// -// - implements mino.Sender -type sender struct { - addr mino.Address - in chan Envelope - context serde.Context -} - -// Send implements mino.Sender. It sends the message to all the addresses and -// returns a channel that will be populated with any error happening. The -// channel is eventually closed after all participants have gotten their -// message. -func (s sender) Send(msg serde.Message, addrs ...mino.Address) <-chan error { - errs := make(chan error, int(math.Max(1, float64(len(addrs))))) - - data, err := msg.Serialize(s.context) - if err != nil { - errs <- xerrors.Errorf("couldn't marshal message: %v", err) - close(errs) - - return errs - } - - go func() { - s.in <- Envelope{ - from: s.addr.(address), - to: addrs, - message: data, - } - close(errs) - }() - - return errs -} - -// Receiver implements the receiver side of a stream. -// -// - implements mino.Receiver -type receiver struct { - out chan Envelope - errs chan error - context serde.Context - factory serde.Factory -} - -// Recv implements mino.Receiver. It listens for messages until the context is -// done, or a message is received. On a graceful close, the receiver will return -// an EOF error, or the error from the context if it finishes before. -func (r receiver) Recv(ctx context.Context) (mino.Address, serde.Message, error) { - select { - case env, ok := <-r.out: - if !ok { - return nil, nil, io.EOF - } - - msg, err := r.factory.Deserialize(r.context, env.message) - if err != nil { - return nil, nil, xerrors.Errorf("couldn't deserialize: %v", err) - } - - return env.from, msg, nil - case err := <-r.errs: - return nil, nil, err - case <-ctx.Done(): - return nil, nil, ctx.Err() - } -} diff --git a/dela/mino/minoch/rpc_test.go b/dela/mino/minoch/rpc_test.go deleted file mode 100644 index 8c62c45..0000000 --- a/dela/mino/minoch/rpc_test.go +++ /dev/null @@ -1,288 +0,0 @@ -package minoch - -import ( - "bytes" - "context" - "strings" - "testing" - "time" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/require" - "go.dedis.ch/dela" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -func TestRPC_Call(t *testing.T) { - manager := NewManager() - - mA := MustCreate(manager, "A") - rpcA := mino.MustCreateRPC(mA, "test", fakeHandler{}, fake.MessageFactory{}) - - mB := MustCreate(manager, "B") - mino.MustCreateRPC(mB, "test", fakeHandler{}, fake.MessageFactory{}) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(mA.GetAddress(), mB.GetAddress()) - resps, err := rpcA.Call(ctx, fake.Message{}, addrs) - require.NoError(t, err) - - err = testWait(t, resps, nil) - require.NoError(t, err) - - err = testWait(t, resps, nil) - require.NoError(t, err) -} - -func TestRPC_Filter_Call(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - rpc := mino.MustCreateRPC(m, "test", fakeHandler{}, fake.MessageFactory{}) - - m.AddFilter(func(m mino.Request) bool { return false }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resps, err := rpc.Call(ctx, fake.Message{}, mino.NewAddresses(m.GetAddress())) - require.NoError(t, err) - - _, more := <-resps - require.False(t, more) -} - -func TestRPC_BadContext_Call(t *testing.T) { - rpc := &RPC{ - context: fake.NewBadContext(), - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := rpc.Call(ctx, fake.Message{}, nil) - require.EqualError(t, err, fake.Err("couldn't serialize")) -} - -func TestRPC_BadFactory_Call(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - rpc := mino.MustCreateRPC(m, "test", fakeHandler{}, fake.NewBadMessageFactory()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resps, err := rpc.Call(ctx, fake.Message{}, mino.NewAddresses(m.GetAddress())) - require.NoError(t, err) - - err = testWait(t, resps, nil) - require.EqualError(t, err, fake.Err("couldn't deserialize")) -} - -func TestRPC_UnkownPeer_Call(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - rpc := mino.MustCreateRPC(m, "test", fakeHandler{}, fake.MessageFactory{}) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - to := address{ - id: "B", - } - - _, err := rpc.Call(ctx, fake.Message{}, mino.NewAddresses(to)) - require.EqualError(t, err, "couldn't find peer: address not found") -} - -func TestRPC_MissingHandler_Call(t *testing.T) { - manager := NewManager() - - mA := MustCreate(manager, "A") - rpcA := mino.MustCreateRPC(mA, "test", fakeHandler{}, fake.MessageFactory{}) - - mB := MustCreate(manager, "B") - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resps, err := rpcA.Call(ctx, fake.Message{}, mino.NewAddresses(mB.GetAddress())) - require.NoError(t, err) - - err = testWait(t, resps, nil) - require.EqualError(t, err, "unknown rpc /test") -} - -func TestRPC_BadHandler_Call(t *testing.T) { - manager := NewManager() - - mA := MustCreate(manager, "A") - rpcA := mino.MustCreateRPC(mA, "test", fakeHandler{}, fake.MessageFactory{}) - - mB := MustCreate(manager, "B") - mino.MustCreateRPC(mB, "test", badHandler{}, fake.MessageFactory{}) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resps, err := rpcA.Call(ctx, fake.Message{}, mino.NewAddresses(mB.GetAddress())) - require.NoError(t, err) - - err = testWait(t, resps, nil) - require.EqualError(t, err, "couldn't process request: rpc is not supported") -} - -func TestRPC_Stream(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - rpc := mino.MustCreateRPC(m, "test", fakeStreamHandler{}, fake.MessageFactory{}) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sender, receiver, err := rpc.Stream(ctx, mino.NewAddresses(m.GetAddress())) - require.NoError(t, err) - - sender.Send(fake.Message{}, m.GetAddress()) - _, _, err = receiver.Recv(context.Background()) - require.NoError(t, err) - - ctx, cancel2 := context.WithCancel(context.Background()) - cancel2() // fake a timeout - _, _, err = receiver.Recv(ctx) - require.Equal(t, err, context.Canceled) -} - -func TestRPC_Failures_Stream(t *testing.T) { - manager := NewManager() - - m := MustCreate(manager, "A") - - m.context = fake.NewBadContext() - - rpc := mino.MustCreateRPC(m, "test", fakeBadStreamHandler{}, fake.MessageFactory{}) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - out, in, err := rpc.Stream(ctx, mino.NewAddresses(m.GetAddress())) - require.NoError(t, err) - _, _, err = in.Recv(ctx) - require.EqualError(t, err, fake.Err("couldn't process")) - - errs := out.Send(fake.Message{}) - err = testWait(t, nil, errs) - require.EqualError(t, err, fake.Err("couldn't marshal message")) - - m.context = serde.NewContext(fake.ContextEngine{}) - _, _, err = rpc.Stream(ctx, mino.NewAddresses(fake.NewAddress(0))) - require.EqualError(t, err, - "couldn't find peer: invalid address type 'fake.Address'") -} - -func TestRPC_Full_Stream(t *testing.T) { - bufSize = 0 - - oldLogger := dela.Logger - defer func() { - dela.Logger = oldLogger - }() - - buf := new(bytes.Buffer) - dela.Logger = zerolog.New(buf) - - manager := NewManager() - - m := MustCreate(manager, "A") - rpc := mino.MustCreateRPC(m, "test", fakeStreamHandler{}, fake.MessageFactory{}) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sender, _, err := rpc.Stream(ctx, mino.NewAddresses(m.GetAddress())) - require.NoError(t, err) - - go sender.Send(fake.Message{}, m.GetAddress()) - time.Sleep(time.Millisecond * 10) - - expected := `{"level":"warn","to":"A","from":"A","message":"full"}` - require.True(t, strings.Contains(buf.String(), expected), buf.String()) -} - -func TestReceiver_Recv(t *testing.T) { - recv := receiver{ - out: make(chan Envelope, 1), - errs: make(chan error), - context: serde.NewContext(fake.ContextEngine{}), - factory: fake.MessageFactory{}, - } - - recv.out <- Envelope{ - from: address{id: "A"}, - message: []byte(`{}`), - } - - from, msg, err := recv.Recv(context.Background()) - require.NoError(t, err) - require.Equal(t, address{id: "A"}, from) - require.NotNil(t, msg) - - recv.factory = fake.NewBadMessageFactory() - recv.out <- Envelope{} - _, _, err = recv.Recv(context.Background()) - require.EqualError(t, err, fake.Err("couldn't deserialize")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func testWait(t *testing.T, resps <-chan mino.Response, errs <-chan error) error { - select { - case <-time.After(50 * time.Millisecond): - t.Fatal("an error is expected") - return nil - case resp, more := <-resps: - if !more { - return nil - } - - _, err := resp.GetMessageOrError() - - return err - case err := <-errs: - return err - } -} - -type fakeStreamHandler struct { - mino.UnsupportedHandler -} - -func (h fakeStreamHandler) Stream(out mino.Sender, in mino.Receiver) error { - for { - addr, msg, err := in.Recv(context.Background()) - if err != nil { - return err - } - - errs := out.Send(msg, addr) - for err := range errs { - return err - } - } -} - -type fakeBadStreamHandler struct { - mino.UnsupportedHandler -} - -func (h fakeBadStreamHandler) Stream(out mino.Sender, in mino.Receiver) error { - return fake.GetError() -} diff --git a/dela/mino/minogrpc/certs/disk.go b/dela/mino/minogrpc/certs/disk.go deleted file mode 100644 index 82d4d1a..0000000 --- a/dela/mino/minogrpc/certs/disk.go +++ /dev/null @@ -1,185 +0,0 @@ -// This file contains an implementation of a certificate storage on the disk -// which enables persistence. -// -// Documentation Last Review: 07.10.2020 -// - -package certs - -import ( - "errors" - - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -var certBucket = []byte("certificates") - -var errInterrupt = errors.New("interrupted") - -// DiskStore is a persistent implementation of a certificate storage. It uses -// internally an in-memory store to cache the certificates. -// -// - implements certs.Storage -type DiskStore struct { - *InMemoryStore - - db kv.DB - bucket []byte - addrFac mino.AddressFactory -} - -// NewDiskStore returns a new empty disk store. If certificates are stored in -// the database, they will be loaded on demand. -func NewDiskStore(db kv.DB, fac mino.AddressFactory) *DiskStore { - return &DiskStore{ - InMemoryStore: NewInMemoryStore(), - db: db, - bucket: certBucket, - addrFac: fac, - } -} - -// Store implements certs.Storage. It stores the certificate in the disk and in -// the cache. -func (s *DiskStore) Store(addr mino.Address, chain CertChain) error { - key, err := addr.MarshalText() - if err != nil { - return xerrors.Errorf("certificate key failed: %v", err) - } - - // Save the certificate in the disk so that it can later be retrieved. - err = s.db.Update(func(tx kv.WritableTx) error { - bucket, err := tx.GetBucketOrCreate(s.bucket) - if err != nil { - return xerrors.Errorf("while getting bucket: %v", err) - } - - err = bucket.Set(key, chain) - if err != nil { - return xerrors.Errorf("while writing: %v", err) - } - - return nil - }) - if err != nil { - return xerrors.Errorf("while updating db: %v", err) - } - - s.InMemoryStore.Store(addr, chain) - - return nil -} - -// Load implements certs.Storage. It first tries to read the certificate from -// the cache, then from the disk. It returns nil if not found in both. -func (s *DiskStore) Load(addr mino.Address) (CertChain, error) { - cached, _ := s.InMemoryStore.Load(addr) - if cached != nil { - return cached, nil - } - - key, err := addr.MarshalText() - if err != nil { - return nil, xerrors.Errorf("certificate key failed: %v", err) - } - - var chain CertChain - - err = s.db.View(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(s.bucket) - if bucket == nil { - return nil - } - - value := bucket.Get(key) - - if len(value) == 0 { - return nil - } - - data := make([]byte, len(value)) - copy(data, value) - - chain = data - - return nil - }) - if err != nil { - return nil, xerrors.Errorf("while reading db: %v", err) - } - - // Keep the certificate in cache for faster access. - s.InMemoryStore.Store(addr, chain) - - return chain, nil -} - -// Delete implements certs.Storage. It deletes the certificate from the disk and -// the cache. -func (s *DiskStore) Delete(addr mino.Address) error { - s.InMemoryStore.Delete(addr) - - key, err := addr.MarshalText() - if err != nil { - return xerrors.Errorf("certificate key failed: %v", err) - } - - err = s.db.Update(func(tx kv.WritableTx) error { - bucket := tx.GetBucket(s.bucket) - if bucket == nil { - return nil - } - - err := bucket.Delete(key) - if err != nil { - return xerrors.Errorf("while deleting: %v", err) - } - - return nil - }) - if err != nil { - return xerrors.Errorf("while updating db: %v", err) - } - - return nil -} - -// Range implements certs.Storage. It iterates over each certificate present in -// the disk. -func (s *DiskStore) Range(fn func(mino.Address, CertChain) bool) error { - err := s.db.View(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket(s.bucket) - if bucket == nil { - return nil - } - - return bucket.ForEach(func(key, value []byte) error { - // The raw certificate is retained in the x509 leaf, therefore it - // needs an independent array. The database could reuse the one - // provided. - data := make([]byte, len(value)) - copy(data, value) - - addr := s.addrFac.FromText(key) - - next := fn(addr, data) - if !next { - return errInterrupt - } - - return nil - }) - }) - if errors.Is(err, errInterrupt) { - // The iteration is interrupted by the caller, so that is not a real - // error. - return nil - } - if err != nil { - return xerrors.Errorf("while reading db: %v", err) - } - - return nil -} diff --git a/dela/mino/minogrpc/certs/disk_test.go b/dela/mino/minogrpc/certs/disk_test.go deleted file mode 100644 index 90bf9c6..0000000 --- a/dela/mino/minogrpc/certs/disk_test.go +++ /dev/null @@ -1,227 +0,0 @@ -package certs - -import ( - "net" - "os" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func TestDiskStore_Store(t *testing.T) { - db, clean := makeDb(t) - defer clean() - - cert0 := fake.MakeCertificate(t, net.IPv4(127, 0, 0, 1)) - cert12 := fake.MakeCertificate(t, net.IPv4(127, 0, 0, 12)) - - store := NewDiskStore(db, fake.AddressFactory{}) - - err := store.Store(fake.NewAddress(0), cert0) - require.NoError(t, err) - - err = store.Store(fake.NewAddress(12), cert12) - require.NoError(t, err) - - cert, err := store.InMemoryStore.Load(fake.NewAddress(0)) - require.NoError(t, err) - require.Equal(t, CertChain(cert0), cert) - cert, err = store.InMemoryStore.Load(fake.NewAddress(12)) - require.NoError(t, err) - require.Equal(t, CertChain(cert12), cert) - - other := NewDiskStore(db, fake.AddressFactory{}) - - cert, err = other.Load(fake.NewAddress(0)) - require.NoError(t, err) - require.Equal(t, CertChain(cert0), cert) - cert, err = other.Load(fake.NewAddress(12)) - require.NoError(t, err) - require.Equal(t, CertChain(cert12), cert) -} - -func TestDiskStore_BadKey_Store(t *testing.T) { - store := &DiskStore{} - - err := store.Store(fake.NewBadAddress(), nil) - require.EqualError(t, err, fake.Err("certificate key failed")) -} - -func TestDiskStore_FailCreateBucket_Store(t *testing.T) { - store := &DiskStore{ - db: fake.NewBadDB(), - } - - err := store.Store(fake.NewAddress(0), fake.MakeCertificate(t)) - require.EqualError(t, err, fake.Err("while updating db: while getting bucket")) -} - -func TestDiskStore_FailWriteDB_Store(t *testing.T) { - memdb := fake.NewInMemoryDB() - memdb.SetBucket(certBucket, fake.NewBadWriteBucket()) - - store := &DiskStore{ - bucket: certBucket, - db: memdb, - } - - err := store.Store(fake.NewAddress(0), fake.MakeCertificate(t)) - require.EqualError(t, err, fake.Err("while updating db: while writing")) -} - -func TestDiskStore_Load(t *testing.T) { - db, clean := makeDb(t) - defer clean() - - store := NewDiskStore(db, fake.AddressFactory{}) - - err := store.Store(fake.NewAddress(0), fake.MakeCertificate(t)) - require.NoError(t, err) - - cert, err := store.Load(fake.NewAddress(0)) - require.NoError(t, err) - require.NotNil(t, cert) - - cert, err = store.Load(fake.NewAddress(1)) - require.NoError(t, err) - require.Nil(t, cert) - - store = NewDiskStore(fake.NewInMemoryDB(), fake.AddressFactory{}) - cert, err = store.Load(fake.NewAddress(0)) - require.NoError(t, err) - require.Nil(t, cert) -} - -func TestDiskStore_BadKey_Load(t *testing.T) { - store := &DiskStore{ - InMemoryStore: NewInMemoryStore(), - } - - _, err := store.Load(fake.NewBadAddress()) - require.EqualError(t, err, fake.Err("certificate key failed")) -} - -func TestDiskStore_FailReadDB_Load(t *testing.T) { - store := &DiskStore{ - InMemoryStore: NewInMemoryStore(), - db: fake.NewBadViewDB(), - } - - _, err := store.Load(fake.NewAddress(0)) - require.EqualError(t, err, fake.Err("while reading db")) -} - -func TestDiskStore_Delete(t *testing.T) { - db, clean := makeDb(t) - defer clean() - - key := fake.NewAddress(0) - - store := NewDiskStore(db, fake.AddressFactory{}) - - err := store.Delete(key) - require.NoError(t, err) - - err = store.Store(key, fake.MakeCertificate(t)) - require.NoError(t, err) - - err = store.Delete(key) - require.NoError(t, err) - - cert, err := store.Load(key) - require.NoError(t, err) - require.Nil(t, cert) - - store = NewDiskStore(db, fake.AddressFactory{}) - - cert, err = store.Load(key) - require.NoError(t, err) - require.Nil(t, cert) -} - -func TestDiskStore_BadKey_Delete(t *testing.T) { - store := &DiskStore{ - InMemoryStore: NewInMemoryStore(), - } - - err := store.Delete(fake.NewBadAddress()) - require.EqualError(t, err, fake.Err("certificate key failed")) -} - -func TestDiskStore_FailWriteDB_Delete(t *testing.T) { - memdb := fake.NewInMemoryDB() - memdb.SetBucket(certBucket, fake.NewBadDeleteBucket()) - - store := &DiskStore{ - InMemoryStore: NewInMemoryStore(), - bucket: certBucket, - db: memdb, - } - - err := store.Delete(fake.NewAddress(0)) - require.EqualError(t, err, fake.Err("while updating db: while deleting")) -} - -func TestDiskStore_Range(t *testing.T) { - db, clean := makeDb(t) - defer clean() - - store := NewDiskStore(db, fake.AddressFactory{}) - - count := 0 - err := store.Range(func(addr mino.Address, chain CertChain) bool { - count++ - return true - }) - require.NoError(t, err) - require.Equal(t, 0, count) - - err = store.Store(fake.NewAddress(0), fake.MakeCertificate(t)) - require.NoError(t, err) - - err = store.Store(fake.NewAddress(1), fake.MakeCertificate(t)) - require.NoError(t, err) - - err = store.Store(fake.NewAddress(2), fake.MakeCertificate(t)) - require.NoError(t, err) - - err = store.Range(func(addr mino.Address, chain CertChain) bool { - count++ - return true - }) - require.NoError(t, err) - require.Equal(t, 3, count) - - err = store.Range(func(addr mino.Address, chain CertChain) bool { - count++ - return false - }) - require.NoError(t, err) - require.Equal(t, 4, count) -} - -func TestDiskStore_Range_DB_Fail(t *testing.T) { - db := fake.NewInMemoryDB() - - db.SetBucket(certBucket, fake.NewBadForeachBucket()) - store := NewDiskStore(db, fake.AddressFactory{}) - - err := store.Range(nil) - require.EqualError(t, err, fake.Err("while reading db")) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeDb(t *testing.T) (kv.DB, func()) { - file, err := os.CreateTemp(os.TempDir(), "minogrpc-certs") - require.NoError(t, err) - - db, err := kv.New(file.Name()) - require.NoError(t, err) - - return db, func() { os.Remove(file.Name()) } -} diff --git a/dela/mino/minogrpc/certs/example_test.go b/dela/mino/minogrpc/certs/example_test.go deleted file mode 100644 index d7cc7ea..0000000 --- a/dela/mino/minogrpc/certs/example_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package certs_test - -import ( - "crypto/x509" - "fmt" - - "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" -) - -func ExampleStorage_Fetch() { - router := tree.NewRouter(minogrpc.NewAddressFactory()) - - m, err := minogrpc.NewMinogrpc(minogrpc.ParseAddress("127.0.0.1", 0), nil, router) - if err != nil { - panic("couldn't start mino: " + err.Error()) - } - - store := certs.NewInMemoryStore() - - digest, err := store.Hash(m.GetCertificateChain()) - if err != nil { - panic("certificate digest failed: " + err.Error()) - } - - err = store.Fetch(m.GetAddress().(session.Address), digest) - if err != nil { - panic("fetch failed: " + err.Error()) - } - - certBuf, err := store.Load(m.GetAddress()) - if err != nil { - panic("while loading certificate: " + err.Error()) - } - - cert, err := x509.ParseCertificate(certBuf) - if err != nil { - panic("while parsing certificate") - } - - fmt.Println("Certificate host", cert.IPAddresses) - - // Output: Certificate host [127.0.0.1] -} diff --git a/dela/mino/minogrpc/certs/mem.go b/dela/mino/minogrpc/certs/mem.go deleted file mode 100644 index 942e6b4..0000000 --- a/dela/mino/minogrpc/certs/mem.go +++ /dev/null @@ -1,129 +0,0 @@ -// This file contains the implementation of an in-memory certificate storage. -// -// Documentation Last Review: 07.10.2020 -// - -package certs - -import ( - "bytes" - "crypto/tls" - "sync" - - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -// InMemoryStore is a certificate store that keeps the certificates in -// memory only, which means it does not persist. -// -// - implements certs.Storage -type InMemoryStore struct { - certs *sync.Map - hashFactory crypto.HashFactory -} - -// NewInMemoryStore creates a new empty certificate store. -func NewInMemoryStore() *InMemoryStore { - return &InMemoryStore{ - certs: &sync.Map{}, - hashFactory: crypto.NewSha256Factory(), - } -} - -// Store implements certs.Storage. It stores the certificate with the address as -// the key. -func (s *InMemoryStore) Store(addr mino.Address, chain CertChain) error { - s.certs.Store(addr, chain) - - return nil -} - -// Load implements certs.Storage. It looks for the certificate associated to the -// address. If it does not exist, it will return nil. -func (s *InMemoryStore) Load(addr mino.Address) (CertChain, error) { - val, found := s.certs.Load(addr) - if !found { - return nil, nil - } - - return val.(CertChain), nil -} - -// Delete implements certs.Storage. It deletes the certificate associated to the -// address if any, otherwise it does nothing. -func (s *InMemoryStore) Delete(addr mino.Address) error { - s.certs.Delete(addr) - - return nil -} - -// Range implements certs.Storage. It iterates over all the certificates stored -// as long as the callback return true. -func (s *InMemoryStore) Range(fn func(addr mino.Address, chain CertChain) bool) error { - s.certs.Range(func(key, value interface{}) bool { - return fn(key.(mino.Address), value.(CertChain)) - }) - - return nil -} - -// Fetch implements certs.Storage. It tries to open a TLS connection to the -// address only to get the certificate from the distant peer. The connection is -// dropped right after the certificate is read and stored. -func (s *InMemoryStore) Fetch(addr Dialable, hash []byte) error { - cfg := &tls.Config{ - // The server certificate is unknown yet, but we don't want to - // communicate, only fetch the certificate. The integrity is verified - // through the hash to prevent man-in-the-middle attacks. - InsecureSkipVerify: true, - MinVersion: tls.VersionTLS12, - } - - // This connection will be used to fetch the certificate of the server and - // to verify that it matches the expected hash. - conn, err := tls.Dial("tcp", addr.GetDialAddress(), cfg) - if err != nil { - return xerrors.Errorf("failed to dial: %v", err) - } - - conn.Close() - - // we can assume that `peers` is not empty (see doc of `PeerCertificates`) - peers := conn.ConnectionState().PeerCertificates - - chain := bytes.Buffer{} - - for _, peer := range peers { - chain.Write(peer.Raw) - } - - digest, err := s.Hash(chain.Bytes()) - if err != nil { - return xerrors.Errorf("couldn't hash certificate: %v", err) - } - - if !bytes.Equal(digest, hash) { - return xerrors.Errorf("mismatch certificate digest") - } - - // We need only the root certificate from a distant peer, as it is - // sufficient to verify it. - s.certs.Store(addr, CertChain(peers[len(peers)-1].Raw)) - - return nil -} - -// Hash implements certs.Storage. It returns the unique digest for the -// certificate. -func (s *InMemoryStore) Hash(chain CertChain) ([]byte, error) { - h := s.hashFactory.New() - - _, err := h.Write(chain) - if err != nil { - return nil, xerrors.Errorf("couldn't write cert: %v", err) - } - - return h.Sum(nil), nil -} diff --git a/dela/mino/minogrpc/certs/mem_test.go b/dela/mino/minogrpc/certs/mem_test.go deleted file mode 100644 index 0270d7f..0000000 --- a/dela/mino/minogrpc/certs/mem_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package certs - -import ( - "crypto/tls" - "net" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func TestInMemoryStore_Store(t *testing.T) { - store := NewInMemoryStore() - - store.Store(fake.NewAddress(0), CertChain{}) - store.Store(fake.NewAddress(1), CertChain{}) - store.Store(fake.NewAddress(0), CertChain{}) - - num := 0 - store.certs.Range(func(key, value interface{}) bool { - num++ - require.IsType(t, fake.Address{}, key) - require.IsType(t, CertChain{}, value) - return true - }) - require.Equal(t, 2, num) -} - -func TestInMemoryStore_Load(t *testing.T) { - store := NewInMemoryStore() - - store.certs.Store(fake.NewAddress(0), CertChain{}) - store.certs.Store(fake.NewAddress(1), CertChain{}) - - cert, err := store.Load(fake.NewAddress(0)) - require.NoError(t, err) - require.NotNil(t, cert) - - cert, err = store.Load(fake.NewAddress(1)) - require.NoError(t, err) - require.NotNil(t, cert) - - cert, err = store.Load(fake.NewAddress(2)) - require.NoError(t, err) - require.Nil(t, cert) -} - -func TestInMemoryStore_Delete(t *testing.T) { - store := NewInMemoryStore() - - store.certs.Store(fake.NewAddress(0), CertChain{}) - store.certs.Store(fake.NewAddress(1), CertChain{}) - - store.Delete(fake.NewAddress(0)) - - _, found := store.certs.Load(fake.NewAddress(0)) - require.False(t, found) - - _, found = store.certs.Load(fake.NewAddress(1)) - require.True(t, found) -} - -func TestInMemoryStore_Range(t *testing.T) { - store := NewInMemoryStore() - - store.certs.Store(fake.NewAddress(0), CertChain{}) - store.certs.Store(fake.NewAddress(1), CertChain{}) - - num := 0 - store.Range(func(addr mino.Address, chain CertChain) bool { - require.Regexp(t, "fake.Address\\[[0-1]\\]", addr.String()) - num++ - return true - }) - - require.Equal(t, 2, num) -} - -func TestInMemoryStore_Fetch(t *testing.T) { - store := NewInMemoryStore() - - cert, certBuf := fake.MakeFullCertificate(t) - - cfg := &tls.Config{ - Certificates: []tls.Certificate{*cert}, - } - - l := listenTLS(t, cfg) - defer l.Close() - - digest, err := store.Hash(certBuf) - require.NoError(t, err) - - err = store.Fetch(fakeDialable{host: l.Addr().String()}, digest) - require.NoError(t, err) - l.Close() - - err = store.Fetch(fakeDialable{}, digest) - require.EqualError(t, err, "failed to dial: dial tcp: missing address") - - l = listenTLS(t, cfg) - err = store.Fetch(fakeDialable{host: l.Addr().String()}, []byte{}) - require.EqualError(t, err, "mismatch certificate digest") - l.Close() - - l = listenTLS(t, cfg) - store.hashFactory = fake.NewHashFactory(fake.NewBadHash()) - err = store.Fetch(fakeDialable{host: l.Addr().String()}, []byte{}) - require.EqualError(t, err, - fake.Err("couldn't hash certificate: couldn't write cert")) -} - -func TestInMemoryStore_Hash(t *testing.T) { - store := NewInMemoryStore() - - digest, err := store.Hash([]byte{}) - require.NoError(t, err) - require.Len(t, digest, 32) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeDialable struct { - mino.Address - host string -} - -func (a fakeDialable) GetDialAddress() string { - return a.host -} - -func listenTLS(t *testing.T, cfg *tls.Config) net.Listener { - l, err := tls.Listen("tcp", "127.0.0.1:0", cfg) - require.NoError(t, err) - - go func() { - conn, err := l.Accept() - require.NoError(t, err) - require.NotNil(t, conn) - - conn.(*tls.Conn).Handshake() - conn.Close() - }() - - return l -} diff --git a/dela/mino/minogrpc/certs/mod.go b/dela/mino/minogrpc/certs/mod.go deleted file mode 100644 index f72c5b9..0000000 --- a/dela/mino/minogrpc/certs/mod.go +++ /dev/null @@ -1,49 +0,0 @@ -// Package certs defines a certificate store that will provide primitives to -// store and get certificates for a given address. -// -// It also provide a primitive to fetch a certificate from a known address using -// the hash as integrity validation. -// -// Documentation Last Review: 07.10.2020 -// -package certs - -import ( - "go.dedis.ch/dela/mino" -) - -// CertChain represents a list of x509 certificates formatted as ASN.1 DER data. -// The certificates must be concatenated with no intermediate padding. Can be -// parsed with `x509.LoadCertificates`. -type CertChain []byte - -// Dialable is an extension of the mino.Address interface to get a network -// address that can be used to dial the distant server. -type Dialable interface { - mino.Address - - GetDialAddress() string -} - -// Storage is an interface to manage the certificates of a server. -type Storage interface { - // Store stores the certificate with the address as the key. - Store(mino.Address, CertChain) error - - // Load returns the certificate associated with the address if any. - Load(mino.Address) (CertChain, error) - - // Delete removes all the certificates associated with the address. - Delete(mino.Address) error - - // Range iterates over the certificates held by the store. If the callback - // returns false, range stops the iteration. - Range(func(addr mino.Address, cert CertChain) bool) error - - // Fetch calls the address to fetch its certificate and verifies the - // integrity with the given digest. - Fetch(Dialable, []byte) error - - // Hash generates the digest of a certificate. - Hash(CertChain) ([]byte, error) -} diff --git a/dela/mino/minogrpc/controller/actions.go b/dela/mino/minogrpc/controller/actions.go deleted file mode 100644 index 8c3fb0f..0000000 --- a/dela/mino/minogrpc/controller/actions.go +++ /dev/null @@ -1,158 +0,0 @@ -// This file contains the implementation of the controller actions. -// -// Documentation Last Review: 07.10.2020 -// - -package controller - -import ( - "crypto/x509" - "encoding/base64" - "encoding/hex" - "fmt" - "net/url" - "strings" - - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc" - "go.dedis.ch/dela/mino/minogrpc/certs" - "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, chain certs.CertChain) bool { - buff, _ := addr.MarshalText() - addrB64 := base64.StdEncoding.EncodeToString(buff) - - certs, err := x509.ParseCertificates(chain) - if err != nil { - return false - } - - certStr := make([]string, len(certs)) - for i, c := range certs { - certStr[i] = hex.EncodeToString(c.Raw[:8]) + "..." - } - - fmt.Fprintf(req.Out, "Address: %v (%s) Certificate: %s\n", addr, addrB64, strings.Join(certStr, "<-")) - return true - }) - - return nil -} - -// RemoveAction is an action to remove certificates associated to an address -// from the server. -// -// - implements node.ActionTemplate -type removeAction struct{} - -// Execute implements node.ActionTemplate. It removes a certificate based on an -// address. -func (a removeAction) 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) - } - - addrBuf, err := base64.StdEncoding.DecodeString(req.Flags.String("address")) - if err != nil { - return xerrors.Errorf("failed to decode base64 address: %v", err) - } - - addr := m.GetAddressFactory().FromText(addrBuf) - - err = m.GetCertificateStore().Delete(addr) - if err != nil { - return xerrors.Errorf("failed to delete: %v", err) - } - - fmt.Fprintf(req.Out, "certificate(s) with address %q removed", addrBuf) - - 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) - - chain := m.GetCertificateChain() - - digest, err := m.GetCertificateStore().Hash(chain) - 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") - certHash := req.Flags.String("cert-hash") - - addrURL, err := url.Parse(req.Flags.String("address")) - if err != nil { - return xerrors.Errorf("failed to parse addr: %v", err) - } - - 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(addrURL, token, cert) - if err != nil { - return xerrors.Errorf("couldn't join: %v", err) - } - - return nil -} diff --git a/dela/mino/minogrpc/controller/actions_test.go b/dela/mino/minogrpc/controller/actions_test.go deleted file mode 100644 index 4c7d497..0000000 --- a/dela/mino/minogrpc/controller/actions_test.go +++ /dev/null @@ -1,326 +0,0 @@ -package controller - -import ( - "bytes" - "encoding/base64" - "encoding/hex" - "fmt" - "net/url" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc" - "go.dedis.ch/dela/mino/minogrpc/certs" -) - -func TestCertAction_Execute(t *testing.T) { - action := certAction{} - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Injector: node.NewInjector(), - } - - cert, chain := fake.MakeFullCertificate(t) - - store := certs.NewInMemoryStore() - store.Store(fake.NewAddress(0), chain) - - req.Injector.Inject(fakeJoinable{certs: store}) - - err := action.Execute(req) - require.NoError(t, err) - - expected := fmt.Sprintf("Address: fake.Address[0] (AAAAAA==) Certificate: %s...\n", hex.EncodeToString(cert.Certificate[0][:8])) - require.Equal(t, expected, out.String()) - - req.Injector.Inject(fakeJoinable{certs: badCertStore{}}) - - err = action.Execute(req) - require.NoError(t, err, "") - - req.Injector = node.NewInjector() - err = action.Execute(req) - require.EqualError(t, err, - "couldn't resolve: couldn't find dependency for 'minogrpc.Joinable'") -} - -func TestRemoveCert_Execute(t *testing.T) { - action := removeAction{} - - addr := fake.NewAddress(0) - addrBuff, err := addr.MarshalText() - require.NoError(t, err) - - addrB64 := base64.StdEncoding.EncodeToString(addrBuff) - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Injector: node.NewInjector(), - Flags: node.FlagSet{ - "address": addrB64, - }, - } - - cert := fake.MakeCertificate(t) - - store := certs.NewInMemoryStore() - store.Store(addr, cert) - - req.Injector.Inject(fakeJoinable{certs: store}) - - err = action.Execute(req) - require.NoError(t, err) - - store.Range(func(addr mino.Address, cert certs.CertChain) bool { - t.Error("store should be empty") - return false - }) - - expected := fmt.Sprintf("certificate(s) with address %q removed", addrBuff) - require.Equal(t, expected, out.String()) -} - -func TestRemoveCert_Execute_NoJoinable(t *testing.T) { - action := removeAction{} - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Injector: node.NewInjector(), - } - - err := action.Execute(req) - require.EqualError(t, err, "couldn't resolve: couldn't find dependency for 'minogrpc.Joinable'") -} - -func TestRemoveCert_Execute_BadAddress(t *testing.T) { - action := removeAction{} - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Injector: node.NewInjector(), - Flags: node.FlagSet{ - "address": "xx", - }, - } - - store := certs.NewInMemoryStore() - - req.Injector.Inject(fakeJoinable{certs: store}) - - err := action.Execute(req) - require.EqualError(t, err, "failed to decode base64 address: illegal base64 data at input byte 0") -} - -func TestRemoveCert_Execute_BadDelete(t *testing.T) { - action := removeAction{} - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Injector: node.NewInjector(), - Flags: node.FlagSet{ - "address": "xx==", - }, - } - - store := badCertStore{err: fake.GetError()} - - req.Injector.Inject(fakeJoinable{certs: store}) - - err := action.Execute(req) - require.EqualError(t, err, fake.Err("failed to delete")) -} - -func TestTokenAction_Execute(t *testing.T) { - action := tokenAction{} - - flags := make(node.FlagSet) - flags["expiration"] = time.Millisecond - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Flags: flags, - Injector: node.NewInjector(), - } - - cert := fake.MakeCertificate(t) - - store := certs.NewInMemoryStore() - store.Store(fake.NewAddress(0), cert) - - hash, err := store.Hash(cert) - require.NoError(t, err) - - req.Injector.Inject(fakeJoinable{certs: store}) - - err = action.Execute(req) - require.NoError(t, err) - - expected := fmt.Sprintf("--token abc --cert-hash %s\n", - base64.StdEncoding.EncodeToString(hash)) - require.Equal(t, expected, out.String()) - - req.Injector = node.NewInjector() - err = action.Execute(req) - require.EqualError(t, err, - "couldn't resolve: couldn't find dependency for 'minogrpc.Joinable'") -} - -func TestTokenAction_FailedHash(t *testing.T) { - action := tokenAction{} - - flags := make(node.FlagSet) - flags["expiration"] = time.Millisecond - - out := new(bytes.Buffer) - req := node.Context{ - Out: out, - Flags: flags, - Injector: node.NewInjector(), - } - - cert := fake.MakeCertificate(t) - - store := certs.NewInMemoryStore() - store.Store(fake.NewAddress(0), cert) - - req.Injector.Inject(fakeJoinable{certs: badCertStore{err: fake.GetError()}}) - - err := action.Execute(req) - require.EqualError(t, err, fake.Err("couldn't hash certificate")) -} - -func TestJoinAction_Execute(t *testing.T) { - action := joinAction{} - - flags := make(node.FlagSet) - flags["cert-hash"] = "YQ==" - - req := node.Context{ - Flags: flags, - Injector: node.NewInjector(), - } - - req.Injector.Inject(fakeJoinable{}) - - err := action.Execute(req) - require.NoError(t, err) - - flags["cert-hash"] = "a" - err = action.Execute(req) - require.EqualError(t, err, - "couldn't decode digest: illegal base64 data at input byte 0") - - flags["cert-hash"] = "YQ==" - req.Injector.Inject(fakeJoinable{err: fake.GetError()}) - err = action.Execute(req) - require.EqualError(t, err, fake.Err("couldn't join")) - - req.Injector = node.NewInjector() - err = action.Execute(req) - require.EqualError(t, err, - "couldn't resolve: couldn't find dependency for 'minogrpc.Joinable'") -} - -func TestJoinAction_FailedParseAddr(t *testing.T) { - action := joinAction{} - - flags := make(node.FlagSet) - flags["address"] = ":xxx" - - err := action.Execute(node.Context{Flags: flags}) - require.EqualError(t, err, "failed to parse addr: parse \":xxx\": missing protocol scheme") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeJoinable struct { - minogrpc.Joinable - certs certs.Storage - err error -} - -func (j fakeJoinable) GetCertificateChain() certs.CertChain { - cert, _ := j.certs.Load(fake.NewAddress(0)) - - return cert -} - -func (j fakeJoinable) GetCertificateStore() certs.Storage { - return j.certs -} - -func (j fakeJoinable) GenerateToken(time.Duration) string { - return "abc" -} - -func (j fakeJoinable) Join(*url.URL, string, []byte) error { - return j.err -} - -func (fakeJoinable) GetAddressFactory() mino.AddressFactory { - return fake.AddressFactory{} -} - -type fakeContext struct { - cli.Flags - duration time.Duration - str map[string]string - path map[string]string - num int - boolean bool -} - -func (ctx fakeContext) Duration(string) time.Duration { - return ctx.duration -} - -func (ctx fakeContext) String(key string) string { - return ctx.str[key] -} - -func (ctx fakeContext) Path(key string) string { - return ctx.path[key] -} - -func (ctx fakeContext) Int(string) int { - return ctx.num -} - -func (ctx fakeContext) Bool(string) bool { - return ctx.boolean -} - -type badCertStore struct { - certs.Storage - err error -} - -func (badCertStore) Load(mino.Address) (certs.CertChain, error) { - return nil, nil -} - -func (c badCertStore) Hash(certs.CertChain) ([]byte, error) { - return nil, c.err -} - -func (c badCertStore) Delete(mino.Address) error { - return c.err -} - -func (c badCertStore) Range(f func(addr mino.Address, chain certs.CertChain) bool) error { - f(fake.NewAddress(0), certs.CertChain("bad cert")) - return nil -} diff --git a/dela/mino/minogrpc/controller/mod.go b/dela/mino/minogrpc/controller/mod.go deleted file mode 100644 index 111fdb7..0000000 --- a/dela/mino/minogrpc/controller/mod.go +++ /dev/null @@ -1,325 +0,0 @@ -// 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 controller - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "fmt" - "io" - "net" - "net/url" - "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" - "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.StringFlag{ - Name: "listen", - Usage: "set the address to listen on", - Value: "tcp://0.0.0.0:2000", - }, - cli.StringFlag{ - Name: "public", - Usage: "sets the public node address. By default it uses the same as --listen", - Value: "", - Required: false, - }, - cli.StringFlag{ - Name: "routing", - Usage: "sets the kind of routing: 'flat' or 'tree'", - Value: "flat", - Required: false, - }, - cli.StringFlag{ - Name: "certKey", - Usage: "provides the certificate private key path", - Required: false, - }, - cli.StringFlag{ - Name: "certChain", - Usage: "provides the chain certificate file path", - Required: false, - }, - cli.BoolFlag{ - Name: "noTLS", - Usage: "disables TLS on gRPC connections", - Required: false, - 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{})) - - rm := sub.SetSubCommand("rm") - rm.SetDescription("remove a certificate") - rm.SetFlags(cli.StringFlag{ - Name: "address", - Usage: "address associated to the certificate(s), in base64", - Required: true, - }) - rm.SetAction(builder.MakeAction(removeAction{})) - - 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 { - listenURL, err := url.Parse(ctx.String("listen")) - if err != nil { - return xerrors.Errorf("failed to parse listen URL: %v", err) - } - - listen, err := net.ResolveTCPAddr(listenURL.Scheme, listenURL.Host) - if err != nil { - return xerrors.Errorf("failed to resolve tcp address: %v", err) - } - - var rter router.Router - - switch ctx.String("routing") { - case "flat": - rter = tree.NewRouter(minogrpc.NewAddressFactory(), tree.WithHeight(1)) - case "tree": - rter = tree.NewRouter(minogrpc.NewAddressFactory()) - default: - return xerrors.Errorf("unknown routing: %s", ctx.String("routing")) - } - - var opts []minogrpc.Option - - if !ctx.Bool("noTLS") { - opts, err = m.getOptionCert(ctx, inj) - if err != nil { - return xerrors.Errorf("failed to get cert option: %v", err) - } - } else { - opts = []minogrpc.Option{minogrpc.DisableTLS()} - } - - var public *url.URL - - if ctx.String("public") != "" { - public, err = url.Parse(ctx.String("public")) - if err != nil { - return xerrors.Errorf("failed to parse public: %v", err) - } - } - - o, err := minogrpc.NewMinogrpc(listen, public, 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) getOptionCert(ctx cli.Flags, inj node.Injector) ([]minogrpc.Option, error) { - var db kv.DB - err := inj.Resolve(&db) - if err != nil { - return nil, xerrors.Errorf("injector: %v", err) - } - - certs := certs.NewDiskStore(db, session.AddressFactory{}) - - key, err := m.getKey(ctx) - if err != nil { - return nil, xerrors.Errorf("cert private key: %v", err) - } - - certKey := ctx.Path("certKey") - if certKey == "" { - certKey = filepath.Join(ctx.Path("config"), certKeyName) - } - - type extendedKey interface { - Public() crypto.PublicKey - } - - opts := []minogrpc.Option{ - minogrpc.WithCertificateKey(key, key.(extendedKey).Public()), - minogrpc.WithStorage(certs), - } - - certChain := ctx.Path("certChain") - - if certChain != "" { - fmt.Println("certChain:", certChain, "certKey:", certKey) - cert, err := tls.LoadX509KeyPair(certChain, certKey) - if err != nil { - return nil, xerrors.Errorf("failed to load certificate: %v", err) - } - - opts = append(opts, minogrpc.WithCert(&cert)) - } - - return opts, nil -} - -func (m miniController) getKey(flags cli.Flags) (crypto.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) - } - - var key crypto.PrivateKey - - block, _ := pem.Decode(keydata) - if block != nil { - keydata = block.Bytes - } - - key, err = x509.ParseECPrivateKey(keydata) - if err == nil { - return key, nil - } - - key, err = x509.ParsePKCS8PrivateKey(keydata) - if err == nil { - return key, nil - } - - return nil, xerrors.Errorf("key parsing failed: %v", err) -} - -// 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 -} diff --git a/dela/mino/minogrpc/controller/mod_test.go b/dela/mino/minogrpc/controller/mod_test.go deleted file mode 100644 index 3b0bad7..0000000 --- a/dela/mino/minogrpc/controller/mod_test.go +++ /dev/null @@ -1,423 +0,0 @@ -package controller - -import ( - "crypto/elliptic" - "net" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino/minogrpc" -) - -func TestMiniController_Build(t *testing.T) { - ctrl := NewController() - - call := &fake.Call{} - ctrl.SetCommands(fakeBuilder{call: call}) - - require.Equal(t, 22, call.Len()) -} - -func TestMiniController_OnStart(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "minogrpc") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - ctrl := NewController().(miniController) - - injector := node.NewInjector() - injector.Inject(db) - - str := map[string]string{"routing": "flat"} - paths := map[string]string{"config": dir} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.NoError(t, err) - - str = map[string]string{"routing": "tree"} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.NoError(t, err) - - var m *minogrpc.Minogrpc - err = injector.Resolve(&m) - require.NoError(t, err) - require.NoError(t, m.GracefulStop()) -} - -func TestMiniController_OnStart_NoTLS(t *testing.T) { - ctrl := NewController() - - str := map[string]string{"routing": "flat"} - - err := ctrl.OnStart(fakeContext{str: str, boolean: true}, node.NewInjector()) - require.NoError(t, err) -} - -func TestMiniController_InvalidAddr_OnStart(t *testing.T) { - ctrl := NewController() - - str := map[string]string{"listen": ":xxx"} - - err := ctrl.OnStart(fakeContext{str: str}, node.NewInjector()) - require.EqualError(t, err, "failed to parse listen URL: parse \":xxx\": missing protocol scheme") -} - -func TestMiniController_OverlayFailed_OnStart(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "minogrpc") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - ctrl := NewController().(miniController) - - injector := node.NewInjector() - injector.Inject(db) - - listener, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - - defer listener.Close() - - // The address is correct but it will yield an error because it is already - // used. - - str := map[string]string{"listen": "tcp://" + listener.Addr().String(), "routing": "flat"} - paths := map[string]string{"config": dir} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.True(t, strings.HasPrefix(err.Error(), "couldn't make overlay: failed to bind"), err.Error()) -} - -func TestMiniController_MissingDB_OnStart(t *testing.T) { - ctrl := NewController() - - str := map[string]string{"routing": "flat"} - - err := ctrl.OnStart(fakeContext{str: str}, node.NewInjector()) - require.EqualError(t, err, "failed to get cert option: injector: couldn't find dependency for 'kv.DB'") -} - -func TestMiniController_UnknownRouting_OnStart(t *testing.T) { - ctrl := NewController() - - str := map[string]string{"routing": "fake"} - - err := ctrl.OnStart(fakeContext{str: str}, node.NewInjector()) - require.EqualError(t, err, "unknown routing: fake") -} - -func TestMiniController_FailGenerateKey_OnStart(t *testing.T) { - ctrl := NewController().(miniController) - ctrl.random = badReader{} - - inj := node.NewInjector() - inj.Inject(fake.NewInMemoryDB()) - - str := map[string]string{"routing": "flat"} - - err := ctrl.OnStart(fakeContext{str: str}, inj) - require.EqualError(t, err, - fake.Err("failed to get cert option: cert private key: while loading: generator failed: ecdsa")) -} - -func TestMiniController_FailMarshalKey_OnStart(t *testing.T) { - ctrl := NewController().(miniController) - ctrl.curve = badCurve{Curve: elliptic.P224()} - - inj := node.NewInjector() - inj.Inject(fake.NewInMemoryDB()) - - str := map[string]string{"routing": "flat"} - - err := ctrl.OnStart(fakeContext{str: str}, inj) - require.EqualError(t, err, "failed to get cert option: cert private key: "+ - "while loading: generator failed: while marshaling: x509: unknown elliptic curve") -} - -func TestMiniController_FailParseKey_OnStart(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - ctrl := NewController().(miniController) - - inj := node.NewInjector() - inj.Inject(fake.NewInMemoryDB()) - - file, err := os.Create(filepath.Join(dir, certKeyName)) - require.NoError(t, err) - - defer file.Close() - - str := map[string]string{"routing": "flat"} - paths := map[string]string{"config": dir} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, inj) - require.Error(t, err) - require.Contains(t, err.Error(), "cert private key: key parsing failed: ", err.Error()) -} - -func TestMiniController_LoadCertChain_OnStart(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "minogrpc") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - ctrl := NewController().(miniController) - - injector := node.NewInjector() - injector.Inject(db) - - str := map[string]string{"routing": "flat"} - certPath := filepath.Join(dir, "cert.pem") - paths := map[string]string{"config": dir, "certChain": certPath} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.True(t, strings.HasPrefix(err.Error(), "failed to get cert option: "+ - "failed to load certificate:"), err) - - // openssl ecparam -genkey -name secp384r1 -out server.key - key := []byte(` ------BEGIN EC PRIVATE KEY----- -MIGkAgEBBDBeJdT0obeUhS8lkDPcHZWyQPtbT2MzlLWEvwQn0B8l8TY+tKJyHb0N -DWV5URssaGCgBwYFK4EEACKhZANiAASOZ6u/2lP9Q70XZVGAXDsMfBLzqUBT7YbP -1AkJWqQdEkN7ORbrTA/atGLT+mnz3kFSMTZZ9CQppnDnoMcWFSms9znF880z0Dr/ -y4MT5nPHp28W9xpgvZuU/0v5xuNerxs= ------END EC PRIVATE KEY----- -`) - // openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 - cert := []byte(` ------BEGIN CERTIFICATE----- -MIICHTCCAaKgAwIBAgIUP2nfWp+QLXcX+wRAqqm433ep/p4wCgYIKoZIzj0EAwIw -RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu -dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjA3MjUxNTE2NDNaFw0zMjA3MjIx -NTE2NDNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD -VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAASOZ6u/2lP9Q70XZVGAXDsMfBLzqUBT7YbP1AkJWqQdEkN7ORbrTA/atGLT -+mnz3kFSMTZZ9CQppnDnoMcWFSms9znF880z0Dr/y4MT5nPHp28W9xpgvZuU/0v5 -xuNerxujUzBRMB0GA1UdDgQWBBT2bYWZ7Uj/6KdAOF0ox/CNvebRwDAfBgNVHSME -GDAWgBT2bYWZ7Uj/6KdAOF0ox/CNvebRwDAPBgNVHRMBAf8EBTADAQH/MAoGCCqG -SM49BAMCA2kAMGYCMQCX6h4/ISxMEmYsodBBZpLsWSdH61V5wbde6jIy7H3YU/iA -7z7ljg9k+ANkSJDLpukCMQC4yxaPKkIjIjpVH8oXqUwwDVEhW7m34q+cWGjTeTKW -SHG2NjtOc3koWHIAr2waQuA= ------END CERTIFICATE----- -`) - - keyPath := filepath.Join(dir, "key.pem") - - err = os.WriteFile(certPath, cert, 0755) - require.NoError(t, err) - err = os.WriteFile(keyPath, key, 0755) - require.NoError(t, err) - - paths["certKey"] = keyPath - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.NoError(t, err) -} - -func TestMiniController_FailedTCPResolve_OnStart(t *testing.T) { - ctrl := NewController() - - str := map[string]string{"listen": "yyy:xxx"} - - err := ctrl.OnStart(fakeContext{str: str}, node.NewInjector()) - require.EqualError(t, err, "failed to resolve tcp address: unknown network yyy") -} - -func TestMiniController_FailedPublicParse_OnStart(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "minogrpc") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - ctrl := NewController().(miniController) - - injector := node.NewInjector() - injector.Inject(db) - - // The address is correct but it will yield an error because it is already - // used. - - str := map[string]string{"listen": "tcp://1.2.3.4:0", "public": ":xxx", "routing": "flat"} - paths := map[string]string{"config": dir} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.EqualError(t, err, `failed to parse public: parse ":xxx": missing protocol scheme`) -} - -func TestMiniController_OnStop(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "minogrpc") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - db, err := kv.New(filepath.Join(dir, "test.db")) - require.NoError(t, err) - - ctrl := NewController() - - injector := node.NewInjector() - injector.Inject(db) - - str := map[string]string{"routing": "flat"} - paths := map[string]string{"config": dir} - - err = ctrl.OnStart(fakeContext{path: paths, str: str}, injector) - require.NoError(t, err) - - err = ctrl.OnStop(injector) - require.NoError(t, err) -} - -func TestMiniController_MissingMino_OnStop(t *testing.T) { - ctrl := NewController() - - err := ctrl.OnStop(node.NewInjector()) - require.EqualError(t, err, "injector: couldn't find dependency for 'controller.StoppableMino'") -} - -func TestMiniController_FailStopMino_OnStop(t *testing.T) { - ctrl := NewController() - - inj := node.NewInjector() - inj.Inject(badMino{}) - - err := ctrl.OnStop(inj) - require.EqualError(t, err, fake.Err("while stopping mino")) -} - -func TestGetKey_Pem(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "minogrpc") - require.NoError(t, err) - - defer os.RemoveAll(dir) - - ctrl := NewController() - - keyPath := filepath.Join(dir, certKeyName) - - // // openssl ecparam -genkey -name secp384r1 -out key.key - ECKey := []byte(` ------BEGIN EC PRIVATE KEY----- -MIGkAgEBBDBeJdT0obeUhS8lkDPcHZWyQPtbT2MzlLWEvwQn0B8l8TY+tKJyHb0N -DWV5URssaGCgBwYFK4EEACKhZANiAASOZ6u/2lP9Q70XZVGAXDsMfBLzqUBT7YbP -1AkJWqQdEkN7ORbrTA/atGLT+mnz3kFSMTZZ9CQppnDnoMcWFSms9znF880z0Dr/ -y4MT5nPHp28W9xpgvZuU/0v5xuNerxs= ------END EC PRIVATE KEY----- -`) - - err = os.WriteFile(keyPath, ECKey, 0755) - require.NoError(t, err) - - _, err = ctrl.(miniController).getKey(fakeContext{path: map[string]string{"config": dir}}) - require.NoError(t, err) - - // openssl genrsa -out key.pem 1024 - PKCS8Key := []byte(` ------BEGIN PRIVATE KEY----- -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOigddRqfCewqeht -7PcVbgNyzBjZ8HUHA19FkLUqcuqgYJfSexyGi9N0yeYBOVg+PFRx23nP3t/na88O -9AHf47ztcmYTbU0UZpbu/So2yID/f1v6AegIKK/HZo2OuHJPH2E5r8uXdJLWqRUc -ToYfO/YgTIJ1hEfRL6e7f2NO5UwjAgMBAAECgYA6Za2uuVyZihvdIVtPW63WZ8cc -pflbJ3uNOyVslU9r3v7gnhIRwyTu3G6issP2hwkWGc8C8U/93VaPEC3pGo9Mr7M1 -qi8fhwsaVNgbg5J8qaQ6lD7Sdy1ZMz7nkELWRUZ6wlZ+5++5v0pKYoUncm5wVA59 -7uZV90hpZKgdAkYXwQJBAPjHR+I71+PlPCoqN+21scxzfSlmeHocLXp9f4QL9ZBV -tuhSPzCiPuJkvyIj6nN36bxjtv41/8CGi3ObYe7xFlkCQQDvYSdR64ijLP7mpUFm -S8HA0avE8jiAi5nBnKP+r59JoyMdv1980G87OMuKA507w4HtC85OuFPtlN8UpG5C -tt7bAkAR84dLWtgcOLlbrYo1m+vFffvlFeDRpuDdOtsNszM4BAdbwjuPDdYNzglA -tGjBhkCWeHeG5mya/tpnMCoj7L+ZAkEAqFkBGCG3JFreoUKTLegVSQ+r54QZrH2B -EqKgytqkAVuTtLYD53mG4HVe358PExq54wWsf7wueiV6hb/mM1D8hQJAIeJoyDha -cuf0orehi7zxdSmj9T217EXJkF4ENr0VcT7uTOOB7gp2KAQVo/xIfvH8F4h3Btae -UpY5smzho+wOGQ== ------END PRIVATE KEY----- -`) - - err = os.WriteFile(keyPath, PKCS8Key, 0755) - require.NoError(t, err) - - _, err = ctrl.(miniController).getKey(fakeContext{path: map[string]string{"config": dir}}) - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeCommandBuilder struct { - call *fake.Call -} - -func (b fakeCommandBuilder) SetSubCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return b -} - -func (b fakeCommandBuilder) SetDescription(value string) { - b.call.Add(value) -} - -func (b fakeCommandBuilder) SetFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeCommandBuilder) SetAction(a cli.Action) { - b.call.Add(a) -} - -type fakeBuilder struct { - call *fake.Call -} - -func (b fakeBuilder) SetCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return fakeCommandBuilder(b) -} - -func (b fakeBuilder) SetStartFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeBuilder) MakeAction(tmpl node.ActionTemplate) cli.Action { - b.call.Add(tmpl) - return nil -} - -type badMino struct { - StoppableMino -} - -func (badMino) GracefulStop() error { - return fake.GetError() -} - -type badReader struct{} - -func (badReader) Read([]byte) (int, error) { - return 0, fake.GetError() -} - -type badCurve struct { - elliptic.Curve -} diff --git a/dela/mino/minogrpc/example_test.go b/dela/mino/minogrpc/example_test.go deleted file mode 100644 index 6bdd9fe..0000000 --- a/dela/mino/minogrpc/example_test.go +++ /dev/null @@ -1,242 +0,0 @@ -package minogrpc - -import ( - "context" - "fmt" - "time" - - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router/tree" - "go.dedis.ch/dela/serde" -) - -func ExampleRPC_Call() { - mA, err := NewMinogrpc(ParseAddress("127.0.0.1", 0), nil, tree.NewRouter(NewAddressFactory())) - if err != nil { - panic("overlay A failed: " + err.Error()) - } - - rpcA := mino.MustCreateRPC(mA, "test", exampleHandler{}, exampleFactory{}) - - mB, err := NewMinogrpc(ParseAddress("127.0.0.1", 0), nil, tree.NewRouter(NewAddressFactory())) - if err != nil { - panic("overlay B failed: " + err.Error()) - } - - mino.MustCreateRPC(mB, "test", exampleHandler{}, exampleFactory{}) - - mA.GetCertificateStore().Store(mB.GetAddress(), mB.GetCertificateChain()) - mB.GetCertificateStore().Store(mA.GetAddress(), mA.GetCertificateChain()) - - addrs := mino.NewAddresses(mA.GetAddress(), mB.GetAddress()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resps, err := rpcA.Call(ctx, exampleMessage{value: "Hello World!"}, addrs) - if err != nil { - panic("call failed: " + err.Error()) - } - - for resp := range resps { - reply, err := resp.GetMessageOrError() - if err != nil { - panic("error in reply: " + err.Error()) - } - - if resp.GetFrom().Equal(mA.GetAddress()) { - fmt.Println("A", reply.(exampleMessage).value) - } - if resp.GetFrom().Equal(mB.GetAddress()) { - fmt.Println("B", reply.(exampleMessage).value) - } - } - - // Unordered output: A Hello World! - // B Hello World! -} - -func ExampleRPC_Stream() { - mA, err := NewMinogrpc(ParseAddress("127.0.0.1", 0), nil, tree.NewRouter(NewAddressFactory())) - if err != nil { - panic("overlay A failed: " + err.Error()) - } - - rpcA := mino.MustCreateRPC(mA, "test", exampleHandler{}, exampleFactory{}) - - mB, err := NewMinogrpc(ParseAddress("127.0.0.1", 0), nil, tree.NewRouter(NewAddressFactory())) - if err != nil { - panic("overlay B failed: " + err.Error()) - } - - mino.MustCreateRPC(mB, "test", exampleHandler{}, exampleFactory{}) - - mA.GetCertificateStore().Store(mB.GetAddress(), mB.GetCertificateChain()) - mB.GetCertificateStore().Store(mA.GetAddress(), mA.GetCertificateChain()) - - addrs := mino.NewAddresses(mA.GetAddress(), mB.GetAddress()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sender, recv, err := rpcA.Stream(ctx, addrs) - if err != nil { - panic("stream failed: " + err.Error()) - } - - err = <-sender.Send(exampleMessage{value: "Hello World!"}, mB.GetAddress()) - if err != nil { - panic("failed to send: " + err.Error()) - } - - from, msg, err := recv.Recv(ctx) - if err != nil { - panic("failed to receive: " + err.Error()) - } - - if from.Equal(mB.GetAddress()) { - fmt.Println("B", msg.(exampleMessage).value) - } - - // Output: B Hello World! -} - -func ExampleRPC_OpentracingDemo() { - N := 20 - minos := make([]*Minogrpc, N) - rpcs := make([]mino.RPC, N) - - for i := 0; i < N; i++ { - m, err := NewMinogrpc(ParseAddress("127.0.0.1", 0), nil, tree.NewRouter(NewAddressFactory())) - if err != nil { - panic(fmt.Sprintf("overlay %d failed: %s", i, err.Error())) - } - minos[i] = m - defer minos[i].GracefulStop() - - rpcs[i] = mino.MustCreateRPC(minos[i], "test", exampleHandler{}, exampleFactory{}) - } - - for i := 0; i < N; i++ { - for j := 0; j < N; j++ { - if i == j { - continue - } - mA, mB := minos[i], minos[j] - mA.GetCertificateStore().Store(mB.GetAddress(), mB.GetCertificateChain()) - } - } - - addrs := make([]mino.Address, N) - for i := 0; i < N; i++ { - addrs[i] = minos[i].GetAddress() - } - minoAddrs := mino.NewAddresses(addrs...) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - ctx = context.WithValue(ctx, tracing.ProtocolKey, "example-protocol") - - sender, recv, err := rpcs[0].Stream(ctx, minoAddrs) - if err != nil { - panic("stream failed: " + err.Error()) - } - - err = <-sender.Send(exampleMessage{value: "Hello World!"}, addrs[1:]...) - if err != nil { - panic("failed to send: " + err.Error()) - } - - for i := 0; i < N-1; i++ { - from, msg, err := recv.Recv(ctx) - if err != nil { - panic("failed to receive: " + err.Error()) - } - - idx := -1 - for i := 1; i < N; i++ { - if addrs[i].Equal(from) { - idx = i - break - } - } - - fmt.Printf("%d %s\n", idx, msg.(exampleMessage).value) - } - - // Unordered output: 1 Hello World! - // 2 Hello World! - // 3 Hello World! - // 4 Hello World! - // 5 Hello World! - // 6 Hello World! - // 7 Hello World! - // 8 Hello World! - // 9 Hello World! - // 10 Hello World! - // 11 Hello World! - // 12 Hello World! - // 13 Hello World! - // 14 Hello World! - // 15 Hello World! - // 16 Hello World! - // 17 Hello World! - // 18 Hello World! - // 19 Hello World! - -} - -// exampleHandler is an RPC handler example. -// -// - implements mino.Handler -type exampleHandler struct { - mino.UnsupportedHandler -} - -// Process implements mino.Handler. It returns the message received. -func (exampleHandler) Process(req mino.Request) (serde.Message, error) { - return req.Message, nil -} - -// Stream implements mino.Handler. It returns the message to the sender. -func (exampleHandler) Stream(sender mino.Sender, recv mino.Receiver) error { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - from, msg, err := recv.Recv(ctx) - if err != nil { - return err - } - - err = <-sender.Send(msg, from) - if err != nil { - return err - } - - return nil -} - -// exampleMessage is an example of a message. -// -// - implements serde.Message -type exampleMessage struct { - value string -} - -// Serialize implements serde.Message. It returns the value contained in the -// message. -func (m exampleMessage) Serialize(serde.Context) ([]byte, error) { - return []byte(m.value), nil -} - -// exampleFactory is an example of a factory. -// -// - implements serde.Factory -type exampleFactory struct{} - -// Deserialize implements serde.Factory. It returns the message using data as -// the inner value. -func (exampleFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return exampleMessage{value: string(data)}, nil -} diff --git a/dela/mino/minogrpc/mod.go b/dela/mino/minogrpc/mod.go deleted file mode 100644 index be74924..0000000 --- a/dela/mino/minogrpc/mod.go +++ /dev/null @@ -1,437 +0,0 @@ -// Package minogrpc implements a network overlay using gRPC. -// -// Documentation Last Review: 07.10.2020 -// - -package minogrpc - -import ( - "context" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "net" - "net/url" - "regexp" - "strings" - "sync" - "time" - - otgrpc "github.com/opentracing-contrib/go-grpc" - opentracing "github.com/opentracing/opentracing-go" - "go.dedis.ch/dela" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/internal/traffic" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/certs" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/minogrpc/session" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/metadata" -) - -var ( - segmentMatch = regexp.MustCompile("^[a-zA-Z0-9]+$") - addressFac = session.AddressFactory{} -) - -// NewAddressFactory returns a new address factory. -func NewAddressFactory() mino.AddressFactory { - return addressFac -} - -// ParseAddress is a helper to create a TCP network address. -func ParseAddress(ip string, port uint16) net.Addr { - return &net.TCPAddr{ - IP: net.ParseIP(ip), - Port: int(port), - } -} - -// listener is the default listener used to create the socket. Having it as a -// variable is convenient for the tests. -var listener = net.Listen - -// Joinable is an extension of the mino.Mino interface to allow distant servers -// to join a network of participants. -type Joinable interface { - mino.Mino - - // GetCertificateChain returns the certificate chain of the instance. - GetCertificateChain() certs.CertChain - - // GetCertificateStore returns the certificate storage which contains every - // known peer certificate. - GetCertificateStore() certs.Storage - - // GenerateToken returns a token that can be provided by a distant peer to - // mutually share certificates with this instance. - GenerateToken(expiration time.Duration) string - - // Join tries to mutually share certificates of the distant address in - // parameter using the token as a credential. The certificate of the distant - // address digest is compared against the one in parameter. - // - // The token and the certificate digest are provided by the distant peer - // over a secure channel. - // - // Only the "host" and "path" parts are used in the URL, which must be of - // form //:/ - Join(addr *url.URL, token string, certHash []byte) error -} - -// Endpoint defines the requirement of an endpoint. Since the endpoint can be -// called multiple times concurrently we need a mutex and we need to use the -// same sender/receiver. -type Endpoint struct { - // We need this mutex to prevent two processes from concurrently checking - // that the stream session must be created. Using a sync.Map would require - // to use the "LoadOrStore" function, which would make us create the session - // each time, but only saving it the first time. - sync.RWMutex - - Handler mino.Handler - Factory serde.Factory - streams map[string]session.Session -} - -// Minogrpc is an implementation of a minimalist network overlay using gRPC -// internally to communicate with distant peers. -// -// - implements mino.Mino -// - implements fmt.Stringer -type Minogrpc struct { - *overlay - - server *grpc.Server - segments []string - endpoints map[string]*Endpoint - started chan struct{} - closing chan error -} - -type minoTemplate struct { - myAddr session.Address - router router.Router - fac mino.AddressFactory - certs certs.Storage - secret interface{} - public interface{} - curve elliptic.Curve - random io.Reader - cert *tls.Certificate - useTLS bool -} - -// Option is the type to set some fields when instantiating an overlay. -type Option func(*minoTemplate) - -// WithStorage is an option to set a different certificate storage. -func WithStorage(certs certs.Storage) Option { - return func(tmpl *minoTemplate) { - tmpl.certs = certs - } -} - -// WithCertificateKey is an option to set the key of the server certificate. -func WithCertificateKey(secret, public interface{}) Option { - return func(tmpl *minoTemplate) { - tmpl.secret = secret - tmpl.public = public - } -} - -// WithRandom is an option to set the randomness if the certificate private key -// needs to be generated. -func WithRandom(r io.Reader) Option { - return func(tmpl *minoTemplate) { - tmpl.random = r - } -} - -// WithCert is an option to set the node's certificate in case it is not already -// present in the certificate store. -func WithCert(cert *tls.Certificate) Option { - return func(tmpl *minoTemplate) { - tmpl.cert = cert - } -} - -// DisableTLS disables TLS encryption on gRPC connections. It takes precedence -// over WithCert. -func DisableTLS() Option { - return func(tmpl *minoTemplate) { - tmpl.useTLS = false - } -} - -// NewMinogrpc creates and starts a new instance. it will try to listen for the -// address and returns an error if it fails. "listen" is the local address, -// while "public" is the public node address. If public is empty it uses the -// local address. Public does not support any scheme, it should be of form -// //:/. -func NewMinogrpc(listen net.Addr, public *url.URL, router router.Router, opts ...Option) (*Minogrpc, error) { - socket, err := listener(listen.Network(), listen.String()) - if err != nil { - return nil, xerrors.Errorf("failed to bind: %v", err) - } - - if public == nil { - public, err = url.Parse("//" + socket.Addr().String()) - if err != nil { - return nil, xerrors.Errorf("failed to parse public URL: %v", err) - } - } - - dela.Logger.Info().Msgf("public URL is: %s", public.String()) - - tmpl := minoTemplate{ - myAddr: session.NewAddress(public.Host + public.Path), - router: router, - fac: addressFac, - certs: certs.NewInMemoryStore(), - curve: elliptic.P521(), - random: rand.Reader, - useTLS: true, - } - - for _, opt := range opts { - opt(&tmpl) - } - - o, err := newOverlay(&tmpl) - if err != nil { - socket.Close() - return nil, xerrors.Errorf("overlay: %v", err) - } - - dialAddr := o.myAddr.GetDialAddress() - tracer, err := getTracerForAddr(dialAddr) - if err != nil { - return nil, xerrors.Errorf("failed to get tracer for addr %s: %v", dialAddr, err) - } - - srvOpts := []grpc.ServerOption{ - grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer, otgrpc.SpanDecorator(decorateServerTrace))), - grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(tracer, otgrpc.SpanDecorator(decorateServerTrace))), - } - - if !tmpl.useTLS { - dela.Logger.Warn().Msg("⚠️ running in insecure mode, you should not " + - "publicly expose the node's socket") - } - - if tmpl.useTLS { - chainBuf := o.GetCertificateChain() - certs, err := x509.ParseCertificates(chainBuf) - if err != nil { - socket.Close() - return nil, xerrors.Errorf("failed to parse chain: %v", err) - } - - certsBuf := make([][]byte, len(certs)) - for i, c := range certs { - certsBuf[i] = c.Raw - } - - creds := credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{{ - Certificate: certsBuf, - Leaf: certs[0], - PrivateKey: o.secret, - }}, - MinVersion: tls.VersionTLS12, - }) - - srvOpts = append(srvOpts, grpc.Creds(creds)) - } - - server := grpc.NewServer(srvOpts...) - - m := &Minogrpc{ - overlay: o, - server: server, - segments: nil, - endpoints: make(map[string]*Endpoint), - started: make(chan struct{}), - closing: make(chan error, 1), - } - - // Counter needs to be >=1 for asynchronous call to Add. - m.closer.Add(1) - - ptypes.RegisterOverlayServer(server, &overlayServer{ - overlay: o, - endpoints: m.endpoints, - }) - - dela.Logger.Info().Msgf("listening on: %s", socket.Addr().String()) - - m.listen(socket) - - return m, nil -} - -// GetAddressFactory implements mino.Mino. It returns the address -// factory. -func (m *Minogrpc) GetAddressFactory() mino.AddressFactory { - return NewAddressFactory() -} - -// GetAddress implements mino.Mino. It returns the address of the server. -func (m *Minogrpc) GetAddress() mino.Address { - return m.overlay.myAddr -} - -// GenerateToken implements minogrpc.Joinable. It generates and returns a new -// token that will be valid for the given amount of time. -func (m *Minogrpc) GenerateToken(expiration time.Duration) string { - return m.tokens.Generate(expiration) -} - -// GracefulStop first stops the grpc server then waits for the remaining -// handlers to close. -func (m *Minogrpc) GracefulStop() error { - m.server.GracefulStop() - - return m.postCheckClose() -} - -// Stop stops the server immediately. -func (m *Minogrpc) Stop() error { - m.server.Stop() - - return m.postCheckClose() -} - -func (m *Minogrpc) postCheckClose() error { - m.closer.Wait() - - err := <-m.closing - if err != nil { - return xerrors.Errorf("server stopped unexpectedly: %v", err) - } - - numConns := m.overlay.connMgr.Len() - if numConns > 0 { - return xerrors.Errorf("connection manager not empty: %d", numConns) - } - - err = tracing.CloseAll() - if err != nil { - return xerrors.Errorf("failed to close tracers: %v", err) - } - - return nil -} - -// WithSegment returns a new mino instance that will have its URI path extended -// with the provided segment. The segment can not be empty and should match -// [a-zA-Z0-9]+ -func (m *Minogrpc) WithSegment(segment string) mino.Mino { - - if segment == "" { - return m - } - - newM := &Minogrpc{ - server: m.server, - overlay: m.overlay, - segments: append(m.segments, segment), - endpoints: m.endpoints, - } - - return newM -} - -// CreateRPC implements Mino. It returns a newly created rpc with the provided -// name and reserved for the current namespace. When contacting distant peers, -// it will only talk to mirrored RPCs with the same name and namespace. -func (m *Minogrpc) CreateRPC(name string, h mino.Handler, f serde.Factory) (mino.RPC, error) { - uri := append(append([]string{}, m.segments...), name) - - rpc := &RPC{ - uri: strings.Join(uri, "/"), - overlay: m.overlay, - factory: f, - } - - for _, segment := range uri { - ok := segmentMatch.MatchString(segment) - if !ok { - return nil, xerrors.Errorf("invalid segment in uri '%s': '%s'", rpc.uri, segment) - } - } - - _, found := m.endpoints[rpc.uri] - if found { - return nil, xerrors.Errorf("rpc '%s' already exists", rpc.uri) - } - - m.endpoints[rpc.uri] = &Endpoint{ - Handler: h, - Factory: f, - streams: make(map[string]session.Session), - } - - return rpc, nil -} - -// String implements fmt.Stringer. It prints a short description of the -// instance. -func (m *Minogrpc) String() string { - return fmt.Sprintf("mino[%v]", m.overlay.myAddr) -} - -// GetTrafficWatcher returns the traffic watcher. -func (m *Minogrpc) GetTrafficWatcher() traffic.Watcher { - return traffic.GlobalWatcher -} - -// Listen starts the server. It waits for the go routine to start before -// returning. -func (m *Minogrpc) listen(socket net.Listener) { - go func() { - defer m.closer.Done() - - close(m.started) - - err := m.server.Serve(socket) - if err != nil { - m.closing <- xerrors.Errorf("failed to serve: %v", err) - } - - close(m.closing) - }() - - // Force the go routine to be executed before returning which means the - // server has well started after that point. - <-m.started -} - -// decorateServerTrace adds the protocol tag and the streamID tag to a server -// side trace. -func decorateServerTrace(ctx context.Context, span opentracing.Span, method string, - req, resp interface{}, grpcError error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return - } - - protocol := getOrEmpty(md, tracing.ProtocolTag) - if protocol != "" { - span.SetTag(tracing.ProtocolTag, protocol) - } - - streamID := getOrEmpty(md, headerStreamIDKey) - if streamID != "" { - span.SetTag(headerStreamIDKey, streamID) - } -} diff --git a/dela/mino/minogrpc/mod_test.go b/dela/mino/minogrpc/mod_test.go deleted file mode 100644 index 7440555..0000000 --- a/dela/mino/minogrpc/mod_test.go +++ /dev/null @@ -1,309 +0,0 @@ -package minogrpc - -import ( - "context" - "net" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/certs" - "go.dedis.ch/dela/mino/minogrpc/session" - "go.dedis.ch/dela/mino/minogrpc/tokens" - "go.dedis.ch/dela/mino/router/tree" - "google.golang.org/grpc" -) - -func TestMinogrpc_New(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - - router := tree.NewRouter(addressFac) - - m, err := NewMinogrpc(addr, nil, router) - require.NoError(t, err) - - require.Equal(t, "127.0.0.1:3333", m.GetAddress().String()) - require.Empty(t, m.segments) - - cert := m.GetCertificateChain() - require.NotNil(t, cert) - - <-m.started - require.NoError(t, m.GracefulStop()) -} - -func TestMinogrpc_noTLS(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - - router := tree.NewRouter(addressFac) - - m, err := NewMinogrpc(addr, nil, router, DisableTLS()) - require.NoError(t, err) - - require.Equal(t, "127.0.0.1:3333", m.GetAddress().String()) - require.Empty(t, m.segments) - - cert, err := m.certs.Load(m.GetAddress()) - require.NoError(t, err) - require.Nil(t, cert) - - <-m.started - require.NoError(t, m.GracefulStop()) -} - -func TestMinogrpc_New_FailedParsePublic(t *testing.T) { - l := listener - defer func() { - listener = l - }() - - listener = func(network, address string) (net.Listener, error) { - return fakeListener{addr: ":xxx"}, nil - } - - router := tree.NewRouter(addressFac) - - addr := net.IPAddr{} - - _, err := NewMinogrpc(&addr, nil, router) - require.EqualError(t, err, "failed to parse public URL: parse \"//:xxx\": invalid port \":xxx\" after host") -} - -func TestMinogrpc_FailGenerateKey_New(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router, WithRandom(badReader{})) - require.EqualError(t, err, fake.Err("overlay: cert private key")) -} - -func TestMinogrpc_FailCreateCertNew(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router, WithCertificateKey(struct{}{}, struct{}{})) - require.Error(t, err) - require.Contains(t, err.Error(), "overlay: certificate failed: while creating: x509: ") -} - -func TestMinogrpc_FailStoreCerTNew(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router, WithStorage(fakeCerts{errStore: fake.GetError()})) - require.EqualError(t, err, fake.Err("overlay: certificate failed: while storing")) -} - -func TestMinogrpc_FailLoadCerTNew(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router, WithStorage(fakeCerts{errLoad: fake.GetError()})) - require.EqualError(t, err, fake.Err("overlay: while loading cert")) -} - -func TestMinogrpc_FailedBadCerTNew(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router, WithStorage(fakeCerts{})) - require.EqualError(t, err, "failed to parse chain: x509: malformed certificate") -} - -func TestMinogrpc_WithCerTNew(t *testing.T) { - addr := ParseAddress("127.0.0.1", 3333) - router := tree.NewRouter(addressFac) - - cert, _ := fake.MakeFullCertificate(t) - - m, err := NewMinogrpc(addr, nil, router, WithCert(cert)) - require.NoError(t, err) - - defer m.GracefulStop() - - chain := m.GetCertificateChain() - require.Equal(t, certs.CertChain(cert.Certificate[0]), chain) -} - -func TestMinogrpc_BadAddress_New(t *testing.T) { - addr := ParseAddress("123.4.5.6", 1) - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router) - require.Error(t, err) - // Funny enough, macos would output: - // couldn't start the server: failed to listen: listen tcp 123.4.5.6:1: - // bind: can't assign requested address - // While linux outpus: - // couldn't start the server: failed to listen: listen tcp 123.4.5.6:1: - // bind: cannot assign requested address - require.Regexp(t, "^failed to bind: listen tcp 123.4.5.6:1:", err) -} - -func TestMinogrpc_BadTracer_New(t *testing.T) { - getTracerForAddr = fake.GetTracerForAddrWithError - - addr := ParseAddress("127.0.0.1", 3333) - - router := tree.NewRouter(addressFac) - - _, err := NewMinogrpc(addr, nil, router) - require.EqualError(t, err, fake.Err("failed to get tracer for addr 127.0.0.1:3333")) - - getTracerForAddr = tracing.GetTracerForAddr -} - -func TestMinogrpc_GetTrafficWatcher(t *testing.T) { - m := Minogrpc{} - m.GetTrafficWatcher() -} - -func TestMinogrpc_GetAddressFactory(t *testing.T) { - m := &Minogrpc{} - require.IsType(t, addressFac, m.GetAddressFactory()) -} - -func TestMinogrpc_GetAddress(t *testing.T) { - addr := session.NewAddress("") - minoGrpc := &Minogrpc{ - overlay: &overlay{myAddr: addr}, - } - - require.Equal(t, addr, minoGrpc.GetAddress()) -} - -func TestMinogrpc_Token(t *testing.T) { - minoGrpc := &Minogrpc{ - overlay: &overlay{tokens: tokens.NewInMemoryHolder()}, - } - - token := minoGrpc.GenerateToken(time.Minute) - require.True(t, minoGrpc.tokens.Verify(token)) -} - -func TestMinogrpc_GracefulClose(t *testing.T) { - m := &Minogrpc{ - overlay: &overlay{ - closer: new(sync.WaitGroup), - connMgr: fakeConnMgr{}, - }, - server: grpc.NewServer(), - closing: make(chan error), - } - - close(m.closing) - err := m.GracefulStop() - require.NoError(t, err) - - m.closing = make(chan error, 1) - m.closing <- fake.GetError() - err = m.GracefulStop() - require.EqualError(t, err, fake.Err("server stopped unexpectedly")) - - m.closing = make(chan error) - close(m.closing) - m.connMgr = fakeConnMgr{len: 1} - err = m.GracefulStop() - require.EqualError(t, err, "connection manager not empty: 1") -} - -func TestMinogrpc_WithSegment(t *testing.T) { - m := &Minogrpc{} - ns := "Test" - - newMino := m.WithSegment(ns) - - newMinoGrpc, ok := newMino.(*Minogrpc) - require.True(t, ok) - require.Equal(t, ns, newMinoGrpc.segments[0]) - - newMino = m.WithSegment("") - require.Equal(t, m, newMino) -} - -func TestMinogrpc_CreateRPC(t *testing.T) { - m := Minogrpc{ - overlay: &overlay{}, - endpoints: make(map[string]*Endpoint), - } - - mNs := m.WithSegment("segment") - - rpc, err := mNs.CreateRPC("name", emptyHandler{}, fake.MessageFactory{}) - require.NoError(t, err) - - expectedRPC := &RPC{ - factory: fake.MessageFactory{}, - overlay: &overlay{}, - uri: "segment/name", - } - - endpoint, ok := m.endpoints[expectedRPC.uri] - require.True(t, ok) - require.Equal(t, emptyHandler{}, endpoint.Handler) - require.Equal(t, expectedRPC, rpc) - - _, err = mNs.CreateRPC("name", emptyHandler{}, fake.MessageFactory{}) - require.EqualError(t, err, "rpc 'segment/name' already exists") -} - -func TestMinogrpc_InvalidSegment_CreateRPC(t *testing.T) { - m := &Minogrpc{ - segments: []string{"example"}, - } - - handler := mino.UnsupportedHandler{} - - _, err := m.CreateRPC("/test", handler, fake.MessageFactory{}) - require.EqualError(t, err, "invalid segment in uri 'example//test': '/test'") - - _, err = m.CreateRPC(" test", handler, fake.MessageFactory{}) - require.EqualError(t, err, "invalid segment in uri 'example/ test': ' test'") - - _, err = m.CreateRPC("test$", handler, fake.MessageFactory{}) - require.EqualError(t, err, "invalid segment in uri 'example/test$': 'test$'") -} - -func TestMinogrpc_String(t *testing.T) { - minoGrpc := &Minogrpc{ - overlay: &overlay{myAddr: session.Address{}}, - } - - require.Equal(t, "mino[]", minoGrpc.String()) -} - -func TestMinogrpc_DecorateTrace_NoFound(t *testing.T) { - ctx := context.Background() - decorateServerTrace(ctx, nil, "", nil, nil, nil) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badReader struct{} - -func (badReader) Read([]byte) (int, error) { - return 0, fake.GetError() -} - -type fakeListener struct { - net.Listener - addr string -} - -func (l fakeListener) Addr() net.Addr { - return fakeAddr{addr: l.addr} -} - -type fakeAddr struct { - net.Addr - addr string -} - -func (a fakeAddr) String() string { - return a.addr -} diff --git a/dela/mino/minogrpc/ptypes/mod.go b/dela/mino/minogrpc/ptypes/mod.go deleted file mode 100644 index f515b6f..0000000 --- a/dela/mino/minogrpc/ptypes/mod.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package ptypes contains the protobuf definitions for the implementation of -// minogrpc. -package ptypes - -//go:generate protoc -I ./ --go_out=plugins=grpc:./ ./overlay.proto diff --git a/dela/mino/minogrpc/ptypes/overlay.pb.go b/dela/mino/minogrpc/ptypes/overlay.pb.go deleted file mode 100644 index d83a940..0000000 --- a/dela/mino/minogrpc/ptypes/overlay.pb.go +++ /dev/null @@ -1,645 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: overlay.proto - -package ptypes - -import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// CertificateChain is a wrapper around a chain of x509 raw certificates and its -// address. -type CertificateChain struct { - Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - // value represents certificates as ASN.1 DER data. The certificates must be - // concatenated with no intermediate padding. This value can be parsed with - // `x509.LoadCertificates`. - Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CertificateChain) Reset() { *m = CertificateChain{} } -func (m *CertificateChain) String() string { return proto.CompactTextString(m) } -func (*CertificateChain) ProtoMessage() {} -func (*CertificateChain) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{0} -} - -func (m *CertificateChain) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CertificateChain.Unmarshal(m, b) -} -func (m *CertificateChain) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CertificateChain.Marshal(b, m, deterministic) -} -func (m *CertificateChain) XXX_Merge(src proto.Message) { - xxx_messageInfo_CertificateChain.Merge(m, src) -} -func (m *CertificateChain) XXX_Size() int { - return xxx_messageInfo_CertificateChain.Size(m) -} -func (m *CertificateChain) XXX_DiscardUnknown() { - xxx_messageInfo_CertificateChain.DiscardUnknown(m) -} - -var xxx_messageInfo_CertificateChain proto.InternalMessageInfo - -func (m *CertificateChain) GetAddress() []byte { - if m != nil { - return m.Address - } - return nil -} - -func (m *CertificateChain) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -type CertificateAck struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CertificateAck) Reset() { *m = CertificateAck{} } -func (m *CertificateAck) String() string { return proto.CompactTextString(m) } -func (*CertificateAck) ProtoMessage() {} -func (*CertificateAck) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{1} -} - -func (m *CertificateAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CertificateAck.Unmarshal(m, b) -} -func (m *CertificateAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CertificateAck.Marshal(b, m, deterministic) -} -func (m *CertificateAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_CertificateAck.Merge(m, src) -} -func (m *CertificateAck) XXX_Size() int { - return xxx_messageInfo_CertificateAck.Size(m) -} -func (m *CertificateAck) XXX_DiscardUnknown() { - xxx_messageInfo_CertificateAck.DiscardUnknown(m) -} - -var xxx_messageInfo_CertificateAck proto.InternalMessageInfo - -// JoinRequest sends a request to join a network to a distant node. It must -// contain a valid token and its own certificate. -type JoinRequest struct { - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - Chain *CertificateChain `protobuf:"bytes,2,opt,name=chain,proto3" json:"chain,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *JoinRequest) Reset() { *m = JoinRequest{} } -func (m *JoinRequest) String() string { return proto.CompactTextString(m) } -func (*JoinRequest) ProtoMessage() {} -func (*JoinRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{2} -} - -func (m *JoinRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_JoinRequest.Unmarshal(m, b) -} -func (m *JoinRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_JoinRequest.Marshal(b, m, deterministic) -} -func (m *JoinRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_JoinRequest.Merge(m, src) -} -func (m *JoinRequest) XXX_Size() int { - return xxx_messageInfo_JoinRequest.Size(m) -} -func (m *JoinRequest) XXX_DiscardUnknown() { - xxx_messageInfo_JoinRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_JoinRequest proto.InternalMessageInfo - -func (m *JoinRequest) GetToken() string { - if m != nil { - return m.Token - } - return "" -} - -func (m *JoinRequest) GetChain() *CertificateChain { - if m != nil { - return m.Chain - } - return nil -} - -// JoinResponse is a response of a join request that contains the list of -// certificates known by the distant node. -type JoinResponse struct { - Peers []*CertificateChain `protobuf:"bytes,1,rep,name=peers,proto3" json:"peers,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *JoinResponse) Reset() { *m = JoinResponse{} } -func (m *JoinResponse) String() string { return proto.CompactTextString(m) } -func (*JoinResponse) ProtoMessage() {} -func (*JoinResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{3} -} - -func (m *JoinResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_JoinResponse.Unmarshal(m, b) -} -func (m *JoinResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_JoinResponse.Marshal(b, m, deterministic) -} -func (m *JoinResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_JoinResponse.Merge(m, src) -} -func (m *JoinResponse) XXX_Size() int { - return xxx_messageInfo_JoinResponse.Size(m) -} -func (m *JoinResponse) XXX_DiscardUnknown() { - xxx_messageInfo_JoinResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_JoinResponse proto.InternalMessageInfo - -func (m *JoinResponse) GetPeers() []*CertificateChain { - if m != nil { - return m.Peers - } - return nil -} - -// Message is a network message that contains the address of the sender and the -// payload. -type Message struct { - From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` - Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} -func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{4} -} - -func (m *Message) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Message.Unmarshal(m, b) -} -func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Message.Marshal(b, m, deterministic) -} -func (m *Message) XXX_Merge(src proto.Message) { - xxx_messageInfo_Message.Merge(m, src) -} -func (m *Message) XXX_Size() int { - return xxx_messageInfo_Message.Size(m) -} -func (m *Message) XXX_DiscardUnknown() { - xxx_messageInfo_Message.DiscardUnknown(m) -} - -var xxx_messageInfo_Message proto.InternalMessageInfo - -func (m *Message) GetFrom() []byte { - if m != nil { - return m.From - } - return nil -} - -func (m *Message) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -// Packet is a wrapper around a packet. -type Packet struct { - Serialized []byte `protobuf:"bytes,1,opt,name=serialized,proto3" json:"serialized,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Packet) Reset() { *m = Packet{} } -func (m *Packet) String() string { return proto.CompactTextString(m) } -func (*Packet) ProtoMessage() {} -func (*Packet) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{5} -} - -func (m *Packet) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Packet.Unmarshal(m, b) -} -func (m *Packet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Packet.Marshal(b, m, deterministic) -} -func (m *Packet) XXX_Merge(src proto.Message) { - xxx_messageInfo_Packet.Merge(m, src) -} -func (m *Packet) XXX_Size() int { - return xxx_messageInfo_Packet.Size(m) -} -func (m *Packet) XXX_DiscardUnknown() { - xxx_messageInfo_Packet.DiscardUnknown(m) -} - -var xxx_messageInfo_Packet proto.InternalMessageInfo - -func (m *Packet) GetSerialized() []byte { - if m != nil { - return m.Serialized - } - return nil -} - -// Ack is the return of a unicast request to forward a message. -type Ack struct { - Errors []string `protobuf:"bytes,1,rep,name=errors,proto3" json:"errors,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Ack) Reset() { *m = Ack{} } -func (m *Ack) String() string { return proto.CompactTextString(m) } -func (*Ack) ProtoMessage() {} -func (*Ack) Descriptor() ([]byte, []int) { - return fileDescriptor_61fc82527fbe24ad, []int{6} -} - -func (m *Ack) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Ack.Unmarshal(m, b) -} -func (m *Ack) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Ack.Marshal(b, m, deterministic) -} -func (m *Ack) XXX_Merge(src proto.Message) { - xxx_messageInfo_Ack.Merge(m, src) -} -func (m *Ack) XXX_Size() int { - return xxx_messageInfo_Ack.Size(m) -} -func (m *Ack) XXX_DiscardUnknown() { - xxx_messageInfo_Ack.DiscardUnknown(m) -} - -var xxx_messageInfo_Ack proto.InternalMessageInfo - -func (m *Ack) GetErrors() []string { - if m != nil { - return m.Errors - } - return nil -} - -func init() { - proto.RegisterType((*CertificateChain)(nil), "ptypes.CertificateChain") - proto.RegisterType((*CertificateAck)(nil), "ptypes.CertificateAck") - proto.RegisterType((*JoinRequest)(nil), "ptypes.JoinRequest") - proto.RegisterType((*JoinResponse)(nil), "ptypes.JoinResponse") - proto.RegisterType((*Message)(nil), "ptypes.Message") - proto.RegisterType((*Packet)(nil), "ptypes.Packet") - proto.RegisterType((*Ack)(nil), "ptypes.Ack") -} - -func init() { - proto.RegisterFile("overlay.proto", fileDescriptor_61fc82527fbe24ad) -} - -var fileDescriptor_61fc82527fbe24ad = []byte{ - // 361 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x4d, 0x6b, 0xdb, 0x40, - 0x14, 0x94, 0xfc, 0x21, 0xe1, 0x67, 0xd7, 0x35, 0xaf, 0xc6, 0x08, 0x41, 0x4b, 0xd9, 0x93, 0xe8, - 0x41, 0x14, 0xfb, 0xd0, 0x43, 0x21, 0xe0, 0x18, 0x72, 0x08, 0x84, 0x04, 0xf9, 0x17, 0x6c, 0xa4, - 0xe7, 0x58, 0x48, 0xd6, 0x2a, 0xbb, 0x6b, 0x07, 0xe7, 0x96, 0x7f, 0x1e, 0xa4, 0x95, 0xc0, 0x71, - 0x3e, 0x6e, 0x9a, 0xd9, 0x37, 0xa3, 0x99, 0xb7, 0x0b, 0xdf, 0xc4, 0x81, 0x64, 0xce, 0x8f, 0x61, - 0x29, 0x85, 0x16, 0xe8, 0x94, 0xfa, 0x58, 0x92, 0x62, 0x97, 0x30, 0x59, 0x91, 0xd4, 0xe9, 0x26, - 0x8d, 0xb9, 0xa6, 0xd5, 0x96, 0xa7, 0x05, 0x7a, 0xe0, 0xf2, 0x24, 0x91, 0xa4, 0x94, 0x67, 0xff, - 0xb6, 0x83, 0x51, 0xd4, 0x42, 0x9c, 0x42, 0xff, 0xc0, 0xf3, 0x3d, 0x79, 0x9d, 0x9a, 0x37, 0x80, - 0x4d, 0x60, 0x7c, 0xe2, 0xb1, 0x8c, 0x33, 0xb6, 0x86, 0xe1, 0xb5, 0x48, 0x8b, 0x88, 0x1e, 0xf7, - 0xa4, 0x74, 0x25, 0xd3, 0x22, 0xa3, 0xa2, 0xb6, 0x1b, 0x44, 0x06, 0x60, 0x08, 0xfd, 0xb8, 0xfa, - 0x5f, 0x6d, 0x36, 0x9c, 0x7b, 0xa1, 0x89, 0x14, 0x9e, 0xe7, 0x89, 0xcc, 0x18, 0xbb, 0x80, 0x91, - 0x31, 0x55, 0xa5, 0x28, 0x14, 0x55, 0xfa, 0x92, 0x48, 0x56, 0x21, 0xbb, 0x5f, 0xeb, 0xeb, 0x31, - 0xf6, 0x0f, 0xdc, 0x1b, 0x52, 0x8a, 0x3f, 0x10, 0x22, 0xf4, 0x36, 0x52, 0xec, 0x9a, 0x7a, 0xf5, - 0x77, 0xd5, 0xba, 0xe4, 0xc7, 0x5c, 0xf0, 0xa4, 0x69, 0xd7, 0x42, 0x16, 0x80, 0x73, 0xc7, 0xe3, - 0x8c, 0x34, 0xfe, 0x02, 0x50, 0x24, 0x53, 0x9e, 0xa7, 0xcf, 0x94, 0x34, 0xea, 0x13, 0x86, 0xfd, - 0x84, 0xee, 0x32, 0xce, 0x70, 0x06, 0x0e, 0x49, 0x29, 0x9a, 0x68, 0x83, 0xa8, 0x41, 0xf3, 0x97, - 0x0e, 0xb8, 0xb7, 0xe6, 0x1a, 0x70, 0x01, 0xbd, 0xaa, 0x0d, 0xfe, 0x68, 0x63, 0x9f, 0x2c, 0xcc, - 0x9f, 0xbe, 0x25, 0x4d, 0x61, 0x66, 0xe1, 0x7f, 0xe8, 0xaf, 0xb7, 0x5c, 0x12, 0x7e, 0x5a, 0xd6, - 0x9f, 0x7d, 0x70, 0x52, 0x5d, 0x89, 0x85, 0x7f, 0xa0, 0xb7, 0xe2, 0x79, 0x8e, 0xdf, 0xdb, 0x89, - 0x66, 0x1b, 0xfe, 0x39, 0xc1, 0x2c, 0x0c, 0xc1, 0x59, 0x6b, 0x49, 0x7c, 0x87, 0xe3, 0xf6, 0xd0, - 0xac, 0xc0, 0x3f, 0xc3, 0xcc, 0x0a, 0xec, 0xbf, 0x36, 0x06, 0xe0, 0x5e, 0x09, 0xf9, 0xc4, 0x65, - 0xf2, 0x4e, 0x30, 0x6c, 0x71, 0x9d, 0xe2, 0xde, 0xa9, 0xdf, 0xdf, 0xe2, 0x35, 0x00, 0x00, 0xff, - 0xff, 0xd2, 0x04, 0xdd, 0x3c, 0x90, 0x02, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConnInterface - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// OverlayClient is the client API for Overlay service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type OverlayClient interface { - // Join handles join request from an unknown node. It accepts to share the - // certificates if the token is valid. - Join(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (*JoinResponse, error) - // Share handles a certificate share from another participant of the - // network. - Share(ctx context.Context, in *CertificateChain, opts ...grpc.CallOption) (*CertificateAck, error) - // Call is a unicast rpc to send a message to a participant and expect a - // reply from it. - Call(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) - // Stream is a stream rpc that will build a network of nodes which will - // relay the messages between each others. - Stream(ctx context.Context, opts ...grpc.CallOption) (Overlay_StreamClient, error) - // Forward is used in association with Stream to send a message through - // relays and get a feedback that the message has been received. - Forward(ctx context.Context, in *Packet, opts ...grpc.CallOption) (*Ack, error) -} - -type overlayClient struct { - cc grpc.ClientConnInterface -} - -func NewOverlayClient(cc grpc.ClientConnInterface) OverlayClient { - return &overlayClient{cc} -} - -func (c *overlayClient) Join(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (*JoinResponse, error) { - out := new(JoinResponse) - err := c.cc.Invoke(ctx, "/ptypes.Overlay/Join", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *overlayClient) Share(ctx context.Context, in *CertificateChain, opts ...grpc.CallOption) (*CertificateAck, error) { - out := new(CertificateAck) - err := c.cc.Invoke(ctx, "/ptypes.Overlay/Share", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *overlayClient) Call(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) { - out := new(Message) - err := c.cc.Invoke(ctx, "/ptypes.Overlay/Call", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *overlayClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Overlay_StreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Overlay_serviceDesc.Streams[0], "/ptypes.Overlay/Stream", opts...) - if err != nil { - return nil, err - } - x := &overlayStreamClient{stream} - return x, nil -} - -type Overlay_StreamClient interface { - Send(*Packet) error - Recv() (*Packet, error) - grpc.ClientStream -} - -type overlayStreamClient struct { - grpc.ClientStream -} - -func (x *overlayStreamClient) Send(m *Packet) error { - return x.ClientStream.SendMsg(m) -} - -func (x *overlayStreamClient) Recv() (*Packet, error) { - m := new(Packet) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *overlayClient) Forward(ctx context.Context, in *Packet, opts ...grpc.CallOption) (*Ack, error) { - out := new(Ack) - err := c.cc.Invoke(ctx, "/ptypes.Overlay/Forward", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// OverlayServer is the server API for Overlay service. -type OverlayServer interface { - // Join handles join request from an unknown node. It accepts to share the - // certificates if the token is valid. - Join(context.Context, *JoinRequest) (*JoinResponse, error) - // Share handles a certificate share from another participant of the - // network. - Share(context.Context, *CertificateChain) (*CertificateAck, error) - // Call is a unicast rpc to send a message to a participant and expect a - // reply from it. - Call(context.Context, *Message) (*Message, error) - // Stream is a stream rpc that will build a network of nodes which will - // relay the messages between each others. - Stream(Overlay_StreamServer) error - // Forward is used in association with Stream to send a message through - // relays and get a feedback that the message has been received. - Forward(context.Context, *Packet) (*Ack, error) -} - -// UnimplementedOverlayServer can be embedded to have forward compatible implementations. -type UnimplementedOverlayServer struct { -} - -func (*UnimplementedOverlayServer) Join(ctx context.Context, req *JoinRequest) (*JoinResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Join not implemented") -} -func (*UnimplementedOverlayServer) Share(ctx context.Context, req *CertificateChain) (*CertificateAck, error) { - return nil, status.Errorf(codes.Unimplemented, "method Share not implemented") -} -func (*UnimplementedOverlayServer) Call(ctx context.Context, req *Message) (*Message, error) { - return nil, status.Errorf(codes.Unimplemented, "method Call not implemented") -} -func (*UnimplementedOverlayServer) Stream(srv Overlay_StreamServer) error { - return status.Errorf(codes.Unimplemented, "method Stream not implemented") -} -func (*UnimplementedOverlayServer) Forward(ctx context.Context, req *Packet) (*Ack, error) { - return nil, status.Errorf(codes.Unimplemented, "method Forward not implemented") -} - -func RegisterOverlayServer(s *grpc.Server, srv OverlayServer) { - s.RegisterService(&_Overlay_serviceDesc, srv) -} - -func _Overlay_Join_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(JoinRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OverlayServer).Join(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ptypes.Overlay/Join", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OverlayServer).Join(ctx, req.(*JoinRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Overlay_Share_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CertificateChain) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OverlayServer).Share(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ptypes.Overlay/Share", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OverlayServer).Share(ctx, req.(*CertificateChain)) - } - return interceptor(ctx, in, info, handler) -} - -func _Overlay_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Message) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OverlayServer).Call(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ptypes.Overlay/Call", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OverlayServer).Call(ctx, req.(*Message)) - } - return interceptor(ctx, in, info, handler) -} - -func _Overlay_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OverlayServer).Stream(&overlayStreamServer{stream}) -} - -type Overlay_StreamServer interface { - Send(*Packet) error - Recv() (*Packet, error) - grpc.ServerStream -} - -type overlayStreamServer struct { - grpc.ServerStream -} - -func (x *overlayStreamServer) Send(m *Packet) error { - return x.ServerStream.SendMsg(m) -} - -func (x *overlayStreamServer) Recv() (*Packet, error) { - m := new(Packet) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func _Overlay_Forward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Packet) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OverlayServer).Forward(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ptypes.Overlay/Forward", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OverlayServer).Forward(ctx, req.(*Packet)) - } - return interceptor(ctx, in, info, handler) -} - -var _Overlay_serviceDesc = grpc.ServiceDesc{ - ServiceName: "ptypes.Overlay", - HandlerType: (*OverlayServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Join", - Handler: _Overlay_Join_Handler, - }, - { - MethodName: "Share", - Handler: _Overlay_Share_Handler, - }, - { - MethodName: "Call", - Handler: _Overlay_Call_Handler, - }, - { - MethodName: "Forward", - Handler: _Overlay_Forward_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Stream", - Handler: _Overlay_Stream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "overlay.proto", -} diff --git a/dela/mino/minogrpc/ptypes/overlay.proto b/dela/mino/minogrpc/ptypes/overlay.proto deleted file mode 100644 index aff28e4..0000000 --- a/dela/mino/minogrpc/ptypes/overlay.proto +++ /dev/null @@ -1,67 +0,0 @@ -syntax = "proto3"; - -package ptypes; - -// CertificateChain is a wrapper around a chain of x509 raw certificates and its -// address. -message CertificateChain { - bytes address = 1; - // value represents certificates as ASN.1 DER data. The certificates must be - // concatenated with no intermediate padding. This value can be parsed with - // `x509.LoadCertificates`. - bytes value = 2; -} - -message CertificateAck {} - -// JoinRequest sends a request to join a network to a distant node. It must -// contain a valid token and its own certificate. -message JoinRequest { - string token = 1; - CertificateChain chain = 2; -} - -// JoinResponse is a response of a join request that contains the list of -// certificates known by the distant node. -message JoinResponse { - repeated CertificateChain peers = 1; -} - -// Message is a network message that contains the address of the sender and the -// payload. -message Message { - bytes from = 1; - bytes payload = 2; -} - -// Packet is a wrapper around a packet. -message Packet { - bytes serialized = 1; -} - -// Ack is the return of a unicast request to forward a message. -message Ack { - repeated string errors = 1; -} - -service Overlay { - // Join handles join request from an unknown node. It accepts to share the - // certificates if the token is valid. - rpc Join(JoinRequest) returns (JoinResponse) {} - - // Share handles a certificate share from another participant of the - // network. - rpc Share(CertificateChain) returns (CertificateAck) {} - - // Call is a unicast rpc to send a message to a participant and expect a - // reply from it. - rpc Call(Message) returns (Message) {} - - // Stream is a stream rpc that will build a network of nodes which will - // relay the messages between each others. - rpc Stream(stream Packet) returns (stream Packet) {} - - // Forward is used in association with Stream to send a message through - // relays and get a feedback that the message has been received. - rpc Forward(Packet) returns (Ack) {} -} diff --git a/dela/mino/minogrpc/rpc.go b/dela/mino/minogrpc/rpc.go deleted file mode 100644 index 0e16b7b..0000000 --- a/dela/mino/minogrpc/rpc.go +++ /dev/null @@ -1,261 +0,0 @@ -// This file contains the implementation of the RPC in the context of gRPC. -// -// Documentation Last Review: 07.10.2020 -// - -package minogrpc - -import ( - context "context" - "github.com/rs/xid" - "go.dedis.ch/dela" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/minogrpc/session" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "sync" -) - -// RPC represents an RPC that has been registered by a client, which allows -// clients to call an RPC that will execute the provided handler. -// -// - implements mino.RPC -type RPC struct { - overlay *overlay - uri string - factory serde.Factory -} - -// Call implements mino.RPC. It calls the RPC on each provided address. -func (rpc *RPC) Call(ctx context.Context, - req serde.Message, players mino.Players) (<-chan mino.Response, error) { - - data, err := req.Serialize(rpc.overlay.context) - if err != nil { - return nil, xerrors.Errorf("while serializing: %v", err) - } - - sendMsg := &ptypes.Message{ - From: []byte(rpc.overlay.myAddrStr), - Payload: data, - } - - out := make(chan mino.Response, players.Len()) - - wg := sync.WaitGroup{} - wg.Add(players.Len()) - - iter := players.AddressIterator() - for iter.HasNext() { - addr := iter.GetNext() - - go func() { - defer wg.Done() - - clientConn, err := rpc.overlay.connMgr.Acquire(addr) - if err != nil { - resp := mino.NewResponseWithError( - addr, - xerrors.Errorf("failed to get client conn: %v", err), - ) - - out <- resp - return - } - - defer rpc.overlay.connMgr.Release(addr) - - cl := ptypes.NewOverlayClient(clientConn) - - header := metadata.New(map[string]string{headerURIKey: rpc.uri}) - newCtx := metadata.NewOutgoingContext(ctx, header) - - callResp, err := cl.Call(newCtx, sendMsg) - if err != nil { - resp := mino.NewResponseWithError( - addr, - xerrors.Errorf("failed to call client: %v", err), - ) - - out <- resp - return - } - - if callResp.GetPayload() == nil { - return - } - - resp, err := rpc.factory.Deserialize(rpc.overlay.context, callResp.GetPayload()) - if err != nil { - resp := mino.NewResponseWithError( - addr, - xerrors.Errorf("couldn't unmarshal payload: %v", err), - ) - - out <- resp - return - } - - out <- mino.NewResponse(addr, resp) - }() - } - - go func() { - wg.Wait() - close(out) - }() - - return out, nil -} - -// Stream implements mino.RPC. It will open a stream to one of the addresses -// with a bidirectional channel that will send and receive packets. The chosen -// address will open one or several streams to the rest of the players. The -// choice of the gateway is first the local node if it belongs to the list, -// otherwise the first node of the list. -// -// The way routes are created depends on the router implementation chosen for -// the endpoint. It can for instance use a tree structure, which means the -// network for 8 nodes could look like this: -// -// Orchestrator -// | -// __ A __ -// / \ -// B C -// / | \ / \ -// D E F G H -// -// If C has to send a message to B, it will send it through node A. Similarly, -// if D has to send a message to G, it will move up the tree through B, A and -// finally C. -func (rpc RPC) Stream(ctx context.Context, players mino.Players) (mino.Sender, mino.Receiver, error) { - if players == nil || players.Len() == 0 { - return nil, nil, xerrors.New("empty list of addresses") - } - - streamID := xid.New().String() - - md := metadata.Pairs( - headerURIKey, rpc.uri, - headerStreamIDKey, streamID, - headerGatewayKey, rpc.overlay.myAddrStr, - ) - - protocol := ctx.Value(tracing.ProtocolKey) - - if protocol != nil { - md.Append(tracing.ProtocolTag, protocol.(string)) - } else { - md.Append(tracing.ProtocolTag, tracing.UndefinedProtocol) - } - - table, err := rpc.overlay.router.New(mino.NewAddresses(), rpc.overlay.myAddr) - if err != nil { - return nil, nil, xerrors.Errorf("routing table failed: %v", err) - } - - gw, others := rpc.findGateway(players) - - for _, addr := range others { - addr, err := addr.MarshalText() - if err != nil { - return nil, nil, xerrors.Errorf("while marshaling address: %v", err) - } - - md.Append(headerAddressKey, string(addr)) - } - - conn, err := rpc.overlay.connMgr.Acquire(gw) - if err != nil { - return nil, nil, xerrors.Errorf("gateway connection failed: %v", err) - } - - client := ptypes.NewOverlayClient(conn) - - ctx = metadata.NewOutgoingContext(ctx, md) - - stream, err := client.Stream(ctx) - if err != nil { - rpc.overlay.connMgr.Release(gw) - - return nil, nil, xerrors.Errorf("failed to open stream: %v", err) - } - - // Wait for the event from the server to tell that the stream is - // initialized. - _, err = stream.Header() - if err != nil { - rpc.overlay.connMgr.Release(gw) - - return nil, nil, xerrors.Errorf("failed to receive header: %v", err) - } - - relay := session.NewRelay(stream, gw, rpc.overlay.context, conn, md) - - sess := session.NewSession( - md, - session.NewOrchestratorAddress(rpc.overlay.myAddr), - rpc.factory, - rpc.overlay.router.GetPacketFactory(), - rpc.overlay.context, - rpc.overlay.connMgr, - ) - - // There is no listen for the orchestrator as we need to forward the - // messages received from the stream. - sess.SetPassive(relay, table) - - rpc.overlay.closer.Add(1) - - go func() { - defer func() { - relay.Close() - rpc.overlay.connMgr.Release(gw) - rpc.overlay.closer.Done() - }() - - for { - p, err := stream.Recv() - if err != nil { - if status.Code(err) == codes.Unknown { - dela.Logger.Err(err).Msg("stream to root failed") - } - - return - } - - sess.RecvPacket(gw, p) - } - }() - - return sess, sess, nil -} - -func (rpc RPC) findGateway(players mino.Players) (mino.Address, []mino.Address) { - iter := players.AddressIterator() - addrs := make([]mino.Address, 0, players.Len()) - - var gw mino.Address - - for iter.HasNext() { - addr := iter.GetNext() - - if addr.Equal(rpc.overlay.myAddr) { - gw = addr - } else { - addrs = append(addrs, addr) - } - } - - if gw == nil { - return addrs[0], addrs[1:] - } - - return gw, addrs -} diff --git a/dela/mino/minogrpc/rpc_test.go b/dela/mino/minogrpc/rpc_test.go deleted file mode 100644 index 5616fe4..0000000 --- a/dela/mino/minogrpc/rpc_test.go +++ /dev/null @@ -1,446 +0,0 @@ -package minogrpc - -import ( - context "context" - "io" - "sync" - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/minogrpc/session" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/mino/router/tree" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" -) - -func TestRPC_Call(t *testing.T) { - rpc := &RPC{ - factory: fake.MessageFactory{}, - overlay: &overlay{ - connMgr: fakeConnMgr{}, - context: json.NewContext(), - }, - } - - ctx := context.Background() - addrs := []mino.Address{session.NewAddress("A"), session.NewAddress("B")} - - msgs, err := rpc.Call(ctx, fake.Message{}, mino.NewAddresses(addrs...)) - require.NoError(t, err) - - for i := 0; i < 2; i++ { - msg, more := <-msgs - require.True(t, more) - require.NotNil(t, msg) - - reply, err := msg.GetMessageOrError() - require.NoError(t, err) - require.Equal(t, fake.Message{}, reply) - - isA := msg.GetFrom().Equal(session.NewAddress("A")) - isB := msg.GetFrom().Equal(session.NewAddress("B")) - require.True(t, isA || isB) - } - - _, more := <-msgs - require.False(t, more) -} - -func TestRPC_EmptyAnswer_Call(t *testing.T) { - rpc := &RPC{ - overlay: &overlay{ - connMgr: fakeConnMgr{empty: true}, - context: json.NewContext(), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - msgs, err := rpc.Call(ctx, fake.Message{}, addrs) - require.NoError(t, err) - - _, more := <-msgs - require.False(t, more) -} - -func TestRPC_FailSerialize_Call(t *testing.T) { - rpc := &RPC{ - overlay: &overlay{ - context: fake.NewBadContext(), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := rpc.Call(ctx, fake.Message{}, mino.NewAddresses()) - require.EqualError(t, err, fake.Err("while serializing")) -} - -func TestRPC_FailConn_Call(t *testing.T) { - rpc := &RPC{ - factory: fake.MessageFactory{}, - overlay: &overlay{ - connMgr: fakeConnMgr{err: fake.GetError()}, - context: json.NewContext(), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - msgs, err := rpc.Call(ctx, fake.Message{}, addrs) - require.NoError(t, err) - - msg := <-msgs - _, err = msg.GetMessageOrError() - require.EqualError(t, err, fake.Err("failed to get client conn")) -} - -func TestRPC_BadConn_Call(t *testing.T) { - rpc := &RPC{ - factory: fake.MessageFactory{}, - overlay: &overlay{ - connMgr: fakeConnMgr{errConn: fake.GetError()}, - context: json.NewContext(), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - msgs, err := rpc.Call(ctx, fake.Message{}, addrs) - require.NoError(t, err) - - msg := <-msgs - _, err = msg.GetMessageOrError() - require.EqualError(t, err, fake.Err("failed to call client")) -} - -func TestRPC_FailDeserialize_Call(t *testing.T) { - rpc := &RPC{ - factory: fake.NewBadMessageFactory(), - overlay: &overlay{ - connMgr: fakeConnMgr{}, - context: json.NewContext(), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - msgs, err := rpc.Call(ctx, fake.Message{}, addrs) - require.NoError(t, err) - - msg := <-msgs - _, err = msg.GetMessageOrError() - require.EqualError(t, err, fake.Err("couldn't unmarshal payload")) -} - -func TestRPC_Stream(t *testing.T) { - addrs := []mino.Address{session.NewAddress("A"), session.NewAddress("B")} - calls := &fake.Call{} - - rpc := &RPC{ - overlay: &overlay{ - closer: new(sync.WaitGroup), - myAddr: session.NewAddress("C"), - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - connMgr: fakeConnMgr{calls: calls}, - context: json.NewContext(), - }, - factory: fake.MessageFactory{}, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Send to multiple addresses. - out, in, err := rpc.Stream(ctx, mino.NewAddresses(addrs...)) - require.NoError(t, err) - - out.Send(fake.Message{}, session.NewOrchestratorAddress(session.NewAddress("C")), addrs[1]) - in.Recv(ctx) - - cancel() - rpc.overlay.closer.Wait() - require.Equal(t, 2, calls.Len()) - require.Equal(t, addrs[0], calls.Get(1, 1)) - require.Equal(t, "release", calls.Get(1, 0)) - - // Only one is contacted, and the context is canceled. - out, in, err = rpc.Stream(ctx, mino.NewAddresses(addrs[0])) - require.NoError(t, err) - - out.Send(fake.Message{}, addrs[0]) - _, _, err = in.Recv(ctx) - require.Equal(t, context.Canceled, err) - - rpc.overlay.closer.Wait() -} - -func TestRPC_EmptyPlayers_Stream(t *testing.T) { - rpc := &RPC{} - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, _, err := rpc.Stream(ctx, mino.NewAddresses()) - require.EqualError(t, err, "empty list of addresses") -} - -func TestRPC_FailRtingTable_Stream(t *testing.T) { - rpc := &RPC{ - overlay: &overlay{ - router: badRouter{}, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, _, err := rpc.Stream(ctx, mino.NewAddresses(session.NewAddress(""))) - require.EqualError(t, err, fake.Err("routing table failed")) -} - -func TestRPC_BadAddress_Stream(t *testing.T) { - rpc := &RPC{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress(""), fake.NewBadAddress()) - - _, _, err := rpc.Stream(ctx, addrs) - require.EqualError(t, err, fake.Err("while marshaling address")) -} - -func TestRPC_BadGateway_Stream(t *testing.T) { - rpc := &RPC{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - connMgr: fakeConnMgr{err: fake.GetError()}, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - _, _, err := rpc.Stream(ctx, addrs) - require.EqualError(t, err, fake.Err("gateway connection failed")) -} - -func TestRPC_BadConn_Stream(t *testing.T) { - calls := fake.NewCall() - - rpc := &RPC{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - connMgr: fakeConnMgr{errConn: fake.GetError(), calls: calls}, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - _, _, err := rpc.Stream(ctx, addrs) - require.EqualError(t, err, fake.Err("failed to open stream")) - require.Equal(t, 2, calls.Len()) - require.Equal(t, "release", calls.Get(1, 0)) -} - -func TestRPC_BadStream_Stream(t *testing.T) { - calls := fake.NewCall() - - rpc := &RPC{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - connMgr: fakeConnMgr{errStream: fake.GetError(), calls: calls}, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - addrs := mino.NewAddresses(session.NewAddress("")) - - _, _, err := rpc.Stream(ctx, addrs) - require.EqualError(t, err, fake.Err("failed to receive header")) - require.Equal(t, 2, calls.Len()) - require.Equal(t, "release", calls.Get(1, 0)) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeClientStream struct { - grpc.ClientStream - init *ptypes.Packet - ch chan *ptypes.Packet - err error -} - -func (str *fakeClientStream) Context() context.Context { - return context.Background() -} - -func (str *fakeClientStream) Header() (metadata.MD, error) { - return make(metadata.MD), str.err -} - -func (str *fakeClientStream) SendMsg(m interface{}) error { - if str.err != nil { - return str.err - } - - if str.init == nil { - str.init = m.(*ptypes.Packet) - return nil - } - - str.ch <- m.(*ptypes.Packet) - return nil -} - -func (str *fakeClientStream) RecvMsg(m interface{}) error { - msg, more := <-str.ch - if !more { - return io.EOF - } - - *(m.(*ptypes.Packet)) = *msg - return nil -} - -func (str *fakeClientStream) CloseSend() error { - return nil -} - -type fakeConnection struct { - grpc.ClientConnInterface - resp interface{} - empty bool - err error - errStream error -} - -func (conn fakeConnection) Invoke(ctx context.Context, m string, arg interface{}, - resp interface{}, opts ...grpc.CallOption) error { - - if conn.empty { - return conn.err - } - - switch msg := resp.(type) { - case *ptypes.Message: - *msg = ptypes.Message{ - Payload: []byte(`{}`), - } - case *ptypes.JoinResponse: - *msg = conn.resp.(ptypes.JoinResponse) - default: - } - - return conn.err -} - -func (conn fakeConnection) NewStream(ctx context.Context, desc *grpc.StreamDesc, - m string, opts ...grpc.CallOption) (grpc.ClientStream, error) { - - ch := make(chan *ptypes.Packet, 1) - - go func() { - <-ctx.Done() - close(ch) - }() - - return &fakeClientStream{ch: ch, err: conn.errStream}, conn.err -} - -type fakeConnMgr struct { - session.ConnectionManager - resp interface{} - empty bool - len int - calls *fake.Call - err error - errConn error - errStream error -} - -func (f fakeConnMgr) Len() int { - return f.len -} - -func (f fakeConnMgr) Acquire(addr mino.Address) (grpc.ClientConnInterface, error) { - f.calls.Add("acquire", addr) - - conn := fakeConnection{ - empty: f.empty, - resp: f.resp, - err: f.errConn, - errStream: f.errStream, - } - - return conn, f.err -} - -func (f fakeConnMgr) Release(addr mino.Address) { - f.calls.Add("release", addr) -} - -type badRouter struct { - router.Router - - errFac bool -} - -func (r badRouter) GetHandshakeFactory() router.HandshakeFactory { - if r.errFac { - return fakeHandshakeFactory{err: fake.GetError()} - } - - return fakeHandshakeFactory{} -} - -func (badRouter) New(mino.Players, mino.Address) (router.RoutingTable, error) { - return nil, fake.GetError() -} - -func (badRouter) GenerateTableFrom(router.Handshake) (router.RoutingTable, error) { - return nil, fake.GetError() -} - -type fakeHandshakeFactory struct { - router.HandshakeFactory - - err error -} - -func (fac fakeHandshakeFactory) HandshakeOf(serde.Context, []byte) (router.Handshake, error) { - return nil, fac.err -} diff --git a/dela/mino/minogrpc/server.go b/dela/mino/minogrpc/server.go deleted file mode 100644 index c1b1bea..0000000 --- a/dela/mino/minogrpc/server.go +++ /dev/null @@ -1,859 +0,0 @@ -// This file contains the implementation of the inner overlay of the minogrpc -// instance which processes the requests from the gRPC server. -// -// Documentation Last Review: 07.10.2020 -// - -package minogrpc - -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "sync" - - "math/big" - "net" - "net/url" - "time" - - otgrpc "github.com/opentracing-contrib/go-grpc" - "github.com/opentracing/opentracing-go" - - "go.dedis.ch/dela" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/certs" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/minogrpc/session" - "go.dedis.ch/dela/mino/minogrpc/tokens" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" - "google.golang.org/grpc" - "google.golang.org/grpc/backoff" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" -) - -const ( - // headerURIKey is the key used in rpc header to pass the handler URI - headerURIKey = "apiuri" - headerStreamIDKey = "streamid" - headerGatewayKey = "gateway" - // headerAddressKey is the key of the header that contains the list of - // addresses that will allow a node to create a routing table. - headerAddressKey = "addr" - - certificateDuration = time.Hour * 24 * 180 - - // defaultMinConnectTimeout is the minimum amount of time we are willing to - // wait for a grpc connection to complete - defaultMinConnectTimeout = 60 * time.Second -) - -var getTracerForAddr = tracing.GetTracerForAddr - -type overlayServer struct { - *overlay - - endpoints map[string]*Endpoint -} - -// Join implements ptypes.OverlayServer. It processes the request by checking -// the validity of the token and if it is accepted, by sending the certificate -// to the known peers. It finally returns the certificates to the caller. -func (o overlayServer) Join(ctx context.Context, req *ptypes.JoinRequest) (*ptypes.JoinResponse, error) { - // 1. Check validity of the token. - if !o.tokens.Verify(req.Token) { - return nil, xerrors.Errorf("token '%s' is invalid", req.Token) - } - - dela.Logger.Debug(). - Str("from", string(req.GetChain().GetAddress())). - Msg("valid token received") - - // 2. Share certificates to current participants. - list := make(map[mino.Address][]byte) - o.certs.Range(func(addr mino.Address, chain certs.CertChain) bool { - list[addr] = chain - return true - }) - - peers := make([]*ptypes.CertificateChain, 0, len(list)) - res := make(chan error, 1) - - for to, cert := range list { - text, err := to.MarshalText() - if err != nil { - return nil, xerrors.Errorf("couldn't marshal address: %v", err) - } - - msg := &ptypes.CertificateChain{Address: text, Value: cert} - - // Prepare the list of known certificates to send back to the new node. - peers = append(peers, msg) - - // Share the new node certificate with existing peers. - go func(to mino.Address) { - conn, err := o.connMgr.Acquire(to) - if err != nil { - res <- xerrors.Errorf("couldn't open connection: %v", err) - return - } - - defer o.connMgr.Release(to) - - client := ptypes.NewOverlayClient(conn) - - _, err = client.Share(ctx, req.GetChain()) - if err != nil { - res <- xerrors.Errorf("couldn't call share: %v", err) - return - } - - res <- nil - }(to) - } - - ack := 0 - for ack < len(peers) { - err := <-res - if err != nil { - return nil, xerrors.Errorf("failed to share certificate: %v", err) - } - - ack++ - } - - // 3. Return the set of known certificates. - return &ptypes.JoinResponse{Peers: peers}, nil -} - -// Share implements ptypes.OverlayServer. It accepts a certificate from a -// participant only if it is valid from the address it claims to be. -func (o overlayServer) Share(ctx context.Context, msg *ptypes.CertificateChain) (*ptypes.CertificateAck, error) { - from := o.addrFactory.FromText(msg.GetAddress()).(session.Address) - - hostname, err := from.GetHostname() - if err != nil { - return nil, xerrors.Errorf("malformed address: %v", err) - } - - certs, err := x509.ParseCertificates(msg.GetValue()) - if err != nil { - return nil, xerrors.Errorf("couldn't parse certificate: %v", err) - } - - if len(certs) == 0 { - return nil, xerrors.New("no certificate found") - } - - root := x509.NewCertPool() - intermediate := x509.NewCertPool() - - // if there is only one cert, then it can be self-signed - root.AddCert(certs[len(certs)-1]) - - for i := 1; i < len(certs)-1; i++ { - intermediate.AddCert(certs[i]) - } - - opts := x509.VerifyOptions{ - DNSName: hostname, - Roots: root, - Intermediates: intermediate, - CurrentTime: time.Now(), - } - - _, err = certs[0].Verify(opts) - if err != nil { - return nil, xerrors.Errorf("chain cert invalid: %v", err) - } - - o.certs.Store(from, msg.GetValue()) - - return &ptypes.CertificateAck{}, nil -} - -// Call implements minogrpc.OverlayServer. It processes the request with the -// targeted handler if it exists, otherwise it returns an error. -func (o overlayServer) Call(ctx context.Context, msg *ptypes.Message) (*ptypes.Message, error) { - // We fetch the uri that identifies the handler in the handlers map with the - // grpc metadata api. Using context.Value won't work. - uri := uriFromContext(ctx) - - endpoint, found := o.endpoints[uri] - if !found { - return nil, xerrors.Errorf("handler '%s' is not registered", uri) - } - - message, err := endpoint.Factory.Deserialize(o.context, msg.GetPayload()) - if err != nil { - return nil, xerrors.Errorf("couldn't deserialize message: %v", err) - } - - from := o.addrFactory.FromText(msg.GetFrom()) - - req := mino.Request{ - Address: from, - Message: message, - } - - result, err := endpoint.Handler.Process(req) - if err != nil { - return nil, xerrors.Errorf("handler failed to process: %v", err) - } - - if result == nil { - return &ptypes.Message{}, nil - } - - res, err := result.Serialize(o.context) - if err != nil { - return nil, xerrors.Errorf("couldn't serialize result: %v", err) - } - - return &ptypes.Message{Payload: res}, nil -} - -// Stream implements ptypes.OverlayServer. It can be called in two different -// situations: 1. the node is the very first contacted by the orchestrator and -// will then lead the protocol, or 2. the node is part of the protocol. -// -// A stream is always built with the orchestrator as the original caller that -// contacts one of the participant. The chosen one will relay the messages to -// and from the orchestrator. -// -// Other participants of the protocol will wake up when receiving a message from -// a parent which will open a stream that will determine when to close. If they -// have to relay a message, they will use a unicast call recursively until the -// message is delivered, or has failed. This allows to return an acknowledgement -// that contains the missing delivered addresses. -func (o *overlayServer) Stream(stream ptypes.Overlay_StreamServer) error { - o.closer.Add(1) - defer o.closer.Done() - - headers, ok := metadata.FromIncomingContext(stream.Context()) - if !ok { - return xerrors.New("missing headers") - } - - table, isRoot, err := o.tableFromHeaders(headers) - if err != nil { - return xerrors.Errorf("routing table: %v", err) - } - - uri, streamID, gateway, protocol := readHeaders(headers) - if streamID == "" { - return xerrors.New("unexpected empty stream ID") - } - - endpoint, found := o.endpoints[uri] - if !found { - return xerrors.Errorf("handler '%s' is not registered", uri) - } - - md := metadata.Pairs( - headerURIKey, uri, - headerStreamIDKey, streamID, - headerGatewayKey, o.myAddrStr, - tracing.ProtocolTag, protocol, - ) - - // This lock will make sure that a session is ready before any message - // forwarded will be received. - endpoint.Lock() - - sess, initiated := endpoint.streams[streamID] - if !initiated { - sess = session.NewSession( - md, - o.myAddr, - endpoint.Factory, - o.router.GetPacketFactory(), - o.context, - o.connMgr, - ) - - endpoint.streams[streamID] = sess - } - - gatewayAddr := o.addrFactory.FromText([]byte(gateway)) - - var relay session.Relay - var conn grpc.ClientConnInterface - if isRoot { - // Only the original address is sent to make use of the cached marshaled - // value, so it needs to be upgraded to an orchestrator address. - gatewayAddr = session.NewOrchestratorAddress(gatewayAddr) - - // The relay back to the orchestrator is using the stream as this is the - // only way to get back to it. Fortunately, if the stream succeeds, it - // means the packet arrived. - relay = session.NewStreamRelay(gatewayAddr, stream, o.context) - } else { - conn, err = o.connMgr.Acquire(gatewayAddr) - if err != nil { - endpoint.Unlock() - return xerrors.Errorf("gateway connection failed: %v", err) - } - - defer o.connMgr.Release(gatewayAddr) - - relay = session.NewRelay(stream, gatewayAddr, o.context, conn, md) - } - - o.closer.Add(1) - - ready := make(chan struct{}) - - go func() { - sess.Listen(relay, table, ready) - - o.cleanStream(endpoint, streamID) - o.closer.Done() - }() - - // There, it is important to make sure the parent relay is registered in the - // session before releasing the endpoint. - <-ready - - endpoint.Unlock() - - // This event sends a confirmation to the parent that the stream is - // registered and it can send messages to it. - err = stream.SendHeader(make(metadata.MD)) - if err != nil { - return xerrors.Errorf("failed to send header: %v", err) - } - - err = endpoint.Handler.Stream(sess, sess) - if err != nil { - return xerrors.Errorf("handler failed to process: %v", err) - } - - <-stream.Context().Done() - - return nil -} - -func (o *overlayServer) cleanStream(endpoint *Endpoint, id string) { - endpoint.Lock() - defer endpoint.Unlock() - - // It's important to check the session currently stored as it may be a new - // one with an active parent, or it might be already cleaned. - sess, found := endpoint.streams[id] - if !found { - return - } - - if sess.GetNumParents() > 0 { - return - } - - delete(endpoint.streams, id) - - sess.Close() - - dela.Logger.Trace(). - Str("id", id). - Msg("stream has been cleaned") -} - -func (o *overlayServer) tableFromHeaders(h metadata.MD) (router.RoutingTable, bool, error) { - values := h.Get(session.HandshakeKey) - if len(values) != 0 { - hs, err := o.overlay.router.GetHandshakeFactory().HandshakeOf(o.context, []byte(values[0])) - if err != nil { - return nil, false, xerrors.Errorf("malformed handshake: %v", err) - } - - table, err := o.router.GenerateTableFrom(hs) - if err != nil { - return nil, false, xerrors.Errorf("invalid handshake: %v", err) - } - - return table, false, nil - } - - values = h.Get(headerAddressKey) - - addrs := make([]mino.Address, len(values)) - for i, addr := range values { - addrs[i] = o.addrFactory.FromText([]byte(addr)) - } - - table, err := o.router.New(mino.NewAddresses(addrs...), o.myAddr) - if err != nil { - return nil, true, xerrors.Errorf("failed to create: %v", err) - } - - return table, true, nil -} - -// Forward implements ptypes.OverlayServer. It handles a request to forward a -// packet by sending it to the appropriate session. -func (o *overlayServer) Forward(ctx context.Context, p *ptypes.Packet) (*ptypes.Ack, error) { - headers, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, xerrors.New("no header in the context") - } - - uri, streamID, gateway, _ := readHeaders(headers) - - endpoint, found := o.endpoints[uri] - if !found { - return nil, xerrors.Errorf("handler '%s' is not registered", uri) - } - - endpoint.RLock() - sess, ok := endpoint.streams[streamID] - endpoint.RUnlock() - - if !ok { - return nil, xerrors.Errorf("no stream '%s' found", streamID) - } - - from := o.addrFactory.FromText([]byte(gateway)) - - return sess.RecvPacket(from, p) -} - -type overlay struct { - closer *sync.WaitGroup - context serde.Context - myAddr session.Address - certs certs.Storage - tokens tokens.Holder - router router.Router - connMgr session.ConnectionManager - addrFactory mino.AddressFactory - - // secret and public are the key pair that has generated the server - // certificate. - secret interface{} - public interface{} - - // Keep a text marshalled value for the overlay address so that it's not - // calculated for each request. - myAddrStr string -} - -func newOverlay(tmpl *minoTemplate) (*overlay, error) { - // session.Address never returns an error - myAddrBuf, _ := tmpl.myAddr.MarshalText() - - if tmpl.cert != nil && tmpl.useTLS { - tmpl.secret = tmpl.cert.PrivateKey - // it is okay to crash at this point, as the certificate's key is - // invalid - tmpl.public = tmpl.cert.PrivateKey.(interface{ Public() crypto.PublicKey }).Public() - } - - if tmpl.secret == nil && tmpl.useTLS { - priv, err := ecdsa.GenerateKey(tmpl.curve, tmpl.random) - if err != nil { - return nil, xerrors.Errorf("cert private key: %v", err) - } - - tmpl.secret = priv - tmpl.public = priv.Public() - } - - o := &overlay{ - closer: new(sync.WaitGroup), - context: json.NewContext(), - myAddr: tmpl.myAddr, - myAddrStr: string(myAddrBuf), - tokens: tokens.NewInMemoryHolder(), - certs: tmpl.certs, - router: tmpl.router, - connMgr: newConnManager(tmpl.myAddr, tmpl.certs, tmpl.useTLS), - addrFactory: tmpl.fac, - secret: tmpl.secret, - public: tmpl.public, - } - - if tmpl.cert != nil && tmpl.useTLS { - chain := bytes.Buffer{} - for _, c := range tmpl.cert.Certificate { - chain.Write(c) - } - - err := o.certs.Store(o.myAddr, chain.Bytes()) - if err != nil { - return nil, xerrors.Errorf("failed to store cert: %v", err) - } - } - - if tmpl.useTLS { - cert, err := o.certs.Load(o.myAddr) - if err != nil { - return nil, xerrors.Errorf("while loading cert: %v", err) - } - - if cert == nil { - err = o.makeCertificate() - if err != nil { - return nil, xerrors.Errorf("certificate failed: %v", err) - } - } - } - - return o, nil -} - -// GetCertificate returns the certificate of the overlay with its private key -// set. This function will panic if the overlay has the "noTLS" flag sets. -func (o *overlay) GetCertificateChain() certs.CertChain { - me, err := o.certs.Load(o.myAddr) - if err != nil { - // An error when getting the certificate of the server is caused by the - // underlying storage, and that should never happen in healthy - // environment. - panic(xerrors.Errorf("certificate of the overlay is inaccessible: %v", err)) - } - if me == nil { - // This should never happen and it will panic if it does as this will - // provoke several issues later on. - panic("certificate of the overlay must be populated") - } - - return me -} - -// GetCertificateStore returns the certificate store. -func (o *overlay) GetCertificateStore() certs.Storage { - return o.certs -} - -// Join sends a join request to a distant node with token generated beforehands -// by the later. -func (o *overlay) Join(addr *url.URL, token string, certHash []byte) error { - - target := session.NewAddress(addr.Host + addr.Path) - - chain := o.GetCertificateChain() - - // Fetch the certificate of the node we want to join. The hash is used to - // ensure that we get the right certificate. - err := o.certs.Fetch(target, certHash) - if err != nil { - return xerrors.Errorf("couldn't fetch distant certificate: %v", err) - } - - conn, err := o.connMgr.Acquire(target) - if err != nil { - return xerrors.Errorf("couldn't open connection: %v", err) - } - - defer o.connMgr.Release(target) - - client := ptypes.NewOverlayClient(conn) - - req := &ptypes.JoinRequest{ - Token: token, - Chain: &ptypes.CertificateChain{ - Address: []byte(o.myAddrStr), - Value: chain, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resp, err := client.Join(ctx, req) - if err != nil { - return xerrors.Errorf("couldn't call join: %v", err) - } - - // Update the certificate store with the response from the node we just - // joined. That will allow the node to communicate with the network. - for _, raw := range resp.Peers { - from := o.addrFactory.FromText(raw.GetAddress()) - o.certs.Store(from, raw.GetValue()) - } - - return nil -} - -func (o *overlay) makeCertificate() error { - var ips []net.IP - var dnsNames []string - - hostname, err := o.myAddr.GetHostname() - if err != nil { - return xerrors.Errorf("failed to get hostname: %v", err) - } - - ip := net.ParseIP(hostname) - if ip != nil { - ips = []net.IP{ip} - } else { - dnsNames = []string{hostname} - } - - dela.Logger.Info().Str("hostname", hostname). - Strs("dnsNames", dnsNames). - Msgf("creating certificate: ips: %v", ips) - - tmpl := &x509.Certificate{ - SerialNumber: big.NewInt(1), - IPAddresses: ips, - DNSNames: dnsNames, - NotBefore: time.Now(), - NotAfter: time.Now().Add(certificateDuration), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - MaxPathLen: 1, - IsCA: true, - } - - buf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, o.public, o.secret) - if err != nil { - return xerrors.Errorf("while creating: %+v", err) - } - - err = o.certs.Store(o.myAddr, buf) - if err != nil { - return xerrors.Errorf("while storing: %v", err) - } - - return nil -} - -// ConnManager is a manager to dial and close connections depending on the -// usage. -// -// - implements session.ConnectionManager -type connManager struct { - sync.Mutex - certs certs.Storage - myAddr mino.Address - counters map[mino.Address]int - conns map[mino.Address]*grpc.ClientConn - useTLS bool -} - -func newConnManager(myAddr mino.Address, certs certs.Storage, useTLS bool) *connManager { - return &connManager{ - certs: certs, - myAddr: myAddr, - counters: make(map[mino.Address]int), - conns: make(map[mino.Address]*grpc.ClientConn), - useTLS: useTLS, - } -} - -// Len implements session.ConnectionManager. It returns the number of active -// connections in the manager. -func (mgr *connManager) Len() int { - mgr.Lock() - defer mgr.Unlock() - - return len(mgr.conns) -} - -// Acquire implements session.ConnectionManager. It either dials to open the -// connection or returns an existing one for the address. -func (mgr *connManager) Acquire(to mino.Address) (grpc.ClientConnInterface, error) { - mgr.Lock() - defer mgr.Unlock() - - conn, ok := mgr.conns[to] - if ok { - mgr.counters[to]++ - return conn, nil - } - - netAddr, ok := to.(session.Address) - if !ok { - return nil, xerrors.Errorf("invalid address type '%T'", to) - } - - // Connecting using TLS and the distant server certificate as the root. - addr := netAddr.GetDialAddress() - tracer, err := getTracerForAddr(addr) - if err != nil { - return nil, xerrors.Errorf("failed to get tracer for addr %s: %v", addr, err) - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(2)*time.Second) - defer cancel() - - opts := []grpc.DialOption{ - grpc.WithConnectParams(grpc.ConnectParams{ - Backoff: backoff.DefaultConfig, - MinConnectTimeout: defaultMinConnectTimeout, - }), - grpc.WithUnaryInterceptor( - otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.SpanDecorator(decorateClientTrace)), - ), - grpc.WithStreamInterceptor( - otgrpc.OpenTracingStreamClientInterceptor(tracer, otgrpc.SpanDecorator(decorateClientTrace)), - ), - grpc.WithTransportCredentials(insecure.NewCredentials()), - } - - if mgr.useTLS { - ta, err := mgr.getTransportCredential(to) - if err != nil { - return nil, xerrors.Errorf("failed to retrieve transport credential: %v", err) - } - - opts = append(opts, grpc.WithTransportCredentials(ta)) - } - - conn, err = grpc.DialContext( - ctx, - addr, - opts..., - ) - if err != nil { - return nil, xerrors.Errorf("failed to dial: %v", err) - } - - mgr.conns[to] = conn - mgr.counters[to] = 1 - - return conn, nil -} - -func (mgr *connManager) getTransportCredential(addr mino.Address) (credentials.TransportCredentials, error) { - clientChain, err := mgr.certs.Load(addr) - if err != nil { - return nil, xerrors.Errorf("while loading distant cert: %v", err) - } - if clientChain == nil { - return nil, xerrors.Errorf("certificate for '%v' not found", addr) - } - - meChain, err := mgr.certs.Load(mgr.myAddr) - if err != nil { - return nil, xerrors.Errorf("while loading own cert: %v", err) - } - if meChain == nil { - return nil, xerrors.Errorf("couldn't find server '%v' cert", mgr.myAddr) - } - - pool := x509.NewCertPool() - - certs, err := x509.ParseCertificates(clientChain) - if err != nil { - return nil, xerrors.Errorf("failed to parse distant cert: %v", err) - } - - // Add the root certificate as the CA - pool.AddCert(certs[len(certs)-1]) - - meCerts, err := x509.ParseCertificates(meChain) - if err != nil { - return nil, xerrors.Errorf("failed to parse our own cert: %v", err) - } - - if len(meCerts) == 0 { - return nil, xerrors.New("no certificate found") - } - - ta := credentials.NewTLS(&tls.Config{ - Certificates: []tls.Certificate{{ - Certificate: [][]byte{meCerts[0].Raw}, - Leaf: meCerts[0], - }}, - RootCAs: pool, - MinVersion: tls.VersionTLS12, - }) - - return ta, nil -} - -// Release implements session.ConnectionManager. It closes the connection to the -// address if appropriate. -func (mgr *connManager) Release(to mino.Address) { - mgr.Lock() - defer mgr.Unlock() - - count, ok := mgr.counters[to] - if ok { - if count <= 1 { - delete(mgr.counters, to) - - conn := mgr.conns[to] - delete(mgr.conns, to) - - err := conn.Close() - dela.Logger.Trace(). - Err(err). - Stringer("to", to). - Stringer("from", mgr.myAddr). - Int("length", len(mgr.conns)). - Msg("connection closed") - - return - } - - mgr.counters[to]-- - } -} - -func readHeaders(md metadata.MD) (uri string, streamID string, gw string, protocol string) { - uri = getOrEmpty(md, headerURIKey) - streamID = getOrEmpty(md, headerStreamIDKey) - gw = getOrEmpty(md, headerGatewayKey) - protocol = getOrEmpty(md, tracing.ProtocolTag) - - return -} - -func getOrEmpty(md metadata.MD, key string) string { - values := md.Get(key) - if len(values) == 0 { - return "" - } - - return values[0] -} - -func uriFromContext(ctx context.Context) string { - headers, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "" - } - - apiURI := headers.Get(headerURIKey) - if len(apiURI) == 0 { - return "" - } - - return apiURI[0] -} - -// decorateClientTrace adds the protocol tag and the streamID tag to a client -// side trace. -func decorateClientTrace(ctx context.Context, span opentracing.Span, method string, - req, resp interface{}, grpcError error) { - md, ok := metadata.FromOutgoingContext(ctx) - if !ok { - return - } - - protocol := getOrEmpty(md, tracing.ProtocolTag) - if protocol != "" { - span.SetTag(tracing.ProtocolTag, protocol) - } - - streamID := getOrEmpty(md, headerStreamIDKey) - if streamID != "" { - span.SetTag(headerStreamIDKey, streamID) - } -} diff --git a/dela/mino/minogrpc/server_test.go b/dela/mino/minogrpc/server_test.go deleted file mode 100644 index 9cac80c..0000000 --- a/dela/mino/minogrpc/server_test.go +++ /dev/null @@ -1,1191 +0,0 @@ -package minogrpc - -import ( - "context" - "crypto/elliptic" - "crypto/rand" - "fmt" - "net" - "net/url" - "strings" - "sync" - "testing" - "time" - - opentracing "github.com/opentracing/opentracing-go" - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/internal/tracing" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/certs" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/minogrpc/session" - "go.dedis.ch/dela/mino/minogrpc/tokens" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/mino/router/tree" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "google.golang.org/grpc/metadata" -) - -func TestIntegration_Scenario_Stream(t *testing.T) { - - // Use with MINO_TRAFFIC=log - // defer func() { - // SaveItems("graph.dot", true, true) - // SaveEvents("events.dot") - // }() - - mm, rpcs := makeInstances(t, 6, nil) - - authority := fake.NewAuthorityFromMino(fake.NewSigner, mm...) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sender, recv, err := rpcs[0].Stream(ctx, authority) - require.NoError(t, err) - - iter := authority.AddressIterator() - for iter.HasNext() { - to := iter.GetNext() - err := <-sender.Send(fake.Message{}, to) - require.NoError(t, err) - - from, msg, err := recv.Recv(context.Background()) - require.NoError(t, err) - require.True(t, to.Equal(from)) - require.IsType(t, fake.Message{}, msg) - } - - // Start the shutdown procedure. - cancel() - - for _, m := range mm { - // This makes sure that the relay handlers have been closed by the - // context. - require.NoError(t, m.(*Minogrpc).GracefulStop()) - } -} - -func TestIntegration_Scenario_Call(t *testing.T) { - call := &fake.Call{} - mm, rpcs := makeInstances(t, 10, call) - - authority := fake.NewAuthorityFromMino(fake.NewSigner, mm...) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resps, err := rpcs[0].Call(ctx, fake.Message{}, authority) - require.NoError(t, err) - - for { - select { - case resp, more := <-resps: - if !more { - for _, m := range mm { - require.NoError(t, m.(*Minogrpc).GracefulStop()) - } - - // Verify the parameter of the Process handler. - require.Equal(t, 10, call.Len()) - for i := 0; i < 10; i++ { - req := call.Get(i, 0).(mino.Request) - require.Equal(t, mm[0].GetAddress(), req.Address) - } - - return - } - - msg, err := resp.GetMessageOrError() - require.NoError(t, err) - require.Equal(t, fake.Message{}, msg) - - case <-time.After(5 * time.Second): - t.Fatal("timeout waiting for closure") - } - } -} - -func TestMinogrpc_Scenario_Failures(t *testing.T) { - srvs, rpcs := makeInstances(t, 14, nil) - defer func() { - require.NoError(t, srvs[1].(*Minogrpc).GracefulStop()) - require.NoError(t, srvs[3].(*Minogrpc).GracefulStop()) - for _, srv := range srvs[5:] { - require.NoError(t, srv.(*Minogrpc).GracefulStop()) - } - }() - - // Shutdown one of the instance - require.NoError(t, srvs[0].(*Minogrpc).GracefulStop()) - - authority := fake.NewAuthorityFromMino(fake.NewSigner, srvs...) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - sender, recvr, err := rpcs[1].Stream(ctx, authority) - require.NoError(t, err) - - // Send a message to the shutted down instance to setup the relay, so that - // we can try it will remove it and use another address later. - err = <-sender.Send(fake.Message{}, srvs[0].GetAddress()) - checkError(t, err, srvs[0]) - - // Test if the router learnt about the dead node and fixed the relay, while - // opening relay to known nodes for the following test. - iter := authority.Take(mino.ListFilter([]int{1, 4, 8, 11, 12, 13})).AddressIterator() - for iter.HasNext() { - to := iter.GetNext() - errs := sender.Send(fake.Message{}, srvs[0].GetAddress(), to) - checkError(t, <-errs, srvs[0]) - require.NoError(t, <-errs) - - from, _, err := recvr.Recv(context.Background()) - require.NoError(t, err) - require.True(t, to.Equal(from)) - } - - // This node is a relay for sure by using the tree router, so we close it to - // make sure the protocol can progress. - srvs[4].(*Minogrpc).Stop() - // Close also a leaf to see if we get the feedback that it has failed. - srvs[2].(*Minogrpc).Stop() - - closed := []mino.Address{ - srvs[0].GetAddress(), - srvs[4].GetAddress(), - srvs[2].GetAddress(), - } - - // Test if the network can progress with the loss of a relay. - iter = authority.Take(mino.ListFilter([]int{3, 5, 6, 7, 9})).AddressIterator() - for iter.HasNext() { - to := iter.GetNext() - errs := sender.Send(fake.Message{}, append([]mino.Address{to}, closed...)...) - checkError(t, <-errs, srvs[0], srvs[2], srvs[4]) - checkError(t, <-errs, srvs[0], srvs[2], srvs[4]) - checkError(t, <-errs, srvs[0], srvs[2], srvs[4]) - require.NoError(t, <-errs) - - from, _, err := recvr.Recv(context.Background()) - require.NoError(t, err) - require.True(t, to.Equal(from)) - } - - cancel() -} - -func TestOverlayServer_Join(t *testing.T) { - o, err := newOverlay(&minoTemplate{ - myAddr: session.NewAddress("127.0.0.1:0"), - certs: certs.NewInMemoryStore(), - router: tree.NewRouter(addressFac), - curve: elliptic.P521(), - random: rand.Reader, - useTLS: true, - }) - require.NoError(t, err) - - o.connMgr = fakeConnMgr{} - - overlay := &overlayServer{overlay: o} - - cert := overlay.GetCertificateChain() - token := overlay.tokens.Generate(time.Hour) - - ctx := context.Background() - req := &ptypes.JoinRequest{ - Token: token, - Chain: &ptypes.CertificateChain{ - Address: []byte{}, - Value: cert, - }, - } - - resp, err := overlay.Join(ctx, req) - require.NoError(t, err) - require.NotNil(t, resp) -} - -func TestOverlayJoin_InvalidToken_Join(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - tokens: tokens.NewInMemoryHolder(), - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - req := &ptypes.JoinRequest{Token: "abc"} - - _, err := overlay.Join(ctx, req) - require.EqualError(t, err, "token 'abc' is invalid") -} - -func TestOverlayJoin_BadAddress_Join(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - tokens: tokens.NewInMemoryHolder(), - certs: certs.NewInMemoryStore(), - }, - } - - overlay.certs.Store(fake.NewBadAddress(), fake.MakeCertificate(t)) - - token := overlay.tokens.Generate(time.Hour) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - req := &ptypes.JoinRequest{Token: token} - - _, err := overlay.Join(ctx, req) - require.EqualError(t, err, fake.Err("couldn't marshal address")) -} - -func TestOverlayJoin_BadNetwork_Join(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - tokens: tokens.NewInMemoryHolder(), - certs: certs.NewInMemoryStore(), - connMgr: fakeConnMgr{err: fake.GetError()}, - }, - } - - overlay.certs.Store(session.NewAddress(""), fake.MakeCertificate(t)) - - token := overlay.tokens.Generate(time.Hour) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - req := &ptypes.JoinRequest{Token: token} - - _, err := overlay.Join(ctx, req) - require.EqualError(t, err, - fake.Err("failed to share certificate: couldn't open connection")) -} - -func TestOverlayJoin_BadConn_Join(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - tokens: tokens.NewInMemoryHolder(), - certs: certs.NewInMemoryStore(), - connMgr: fakeConnMgr{errConn: fake.GetError()}, - }, - } - - overlay.certs.Store(session.NewAddress(""), fake.MakeCertificate(t)) - - token := overlay.tokens.Generate(time.Hour) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - req := &ptypes.JoinRequest{Token: token} - - _, err := overlay.Join(ctx, req) - require.EqualError(t, err, fake.Err("failed to share certificate: couldn't call share")) -} - -func TestOverlayServer_Share(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - certs: certs.NewInMemoryStore(), - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - from := session.NewAddress("127.0.0.1:8080") - fromBuf, err := from.MarshalText() - require.NoError(t, err) - - req := &ptypes.CertificateChain{ - Address: fromBuf, - Value: fake.MakeCertificate(t, net.IPv4(127, 0, 0, 1)), - } - - resp, err := overlay.Share(ctx, req) - require.NoError(t, err) - require.NotNil(t, resp) - - shared, err := overlay.certs.Load(from) - require.NoError(t, err) - require.NotNil(t, shared) - - _, err = overlay.Share(ctx, &ptypes.CertificateChain{}) - require.EqualError(t, err, - "no certificate found") -} - -func TestOverlayServer_Share_Bad_Cert(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - addrFactory: addressFac, - }, - } - - from := session.NewAddress("127.0.0.1:8080") - fromBuf, err := from.MarshalText() - require.NoError(t, err) - - msg := &ptypes.CertificateChain{ - Address: fromBuf, - Value: []byte("wrong cert"), - } - _, err = overlay.Share(context.Background(), msg) - require.EqualError(t, err, "couldn't parse certificate: x509: malformed certificate") -} - -func TestOverlayServer_Share_Chain_OK(t *testing.T) { - certs := certs.NewInMemoryStore() - addr := session.NewAddress("127.0.0.1:0") - - chain := fake.MakeCertificateChain(t) - - overlay := overlayServer{ - overlay: &overlay{ - addrFactory: addressFac, - certs: certs, - myAddr: addr, - }, - } - - from := session.NewAddress("127.0.0.1:8080") - fromBuf, err := from.MarshalText() - require.NoError(t, err) - - msg := &ptypes.CertificateChain{ - Address: fromBuf, - Value: chain, - } - - _, err = overlay.Share(context.Background(), msg) - require.NoError(t, err) -} - -func TestOverlayServer_Share_Malformed_Address(t *testing.T) { - chain := fake.MakeCertificateChain(t) - - overlay := overlayServer{ - overlay: &overlay{ - addrFactory: addressFac, - certs: certs.NewInMemoryStore(), - }, - } - - from := session.NewAddress(" ") - fromBuf, err := from.MarshalText() - require.NoError(t, err) - - msg := &ptypes.CertificateChain{ - Address: fromBuf, - Value: chain, - } - - _, err = overlay.Share(context.Background(), msg) - require.NotNil(t, err) - require.True(t, strings.HasPrefix(err.Error(), "malformed address:"), err) -} - -func TestOverlayServer_Share_Chain_Invalid(t *testing.T) { - certs := certs.NewInMemoryStore() - addr := session.NewAddress("127.0.0.1:0") - - chain, _ := fake.MakeFullCertificate(t) - - overlay := overlayServer{ - overlay: &overlay{ - addrFactory: addressFac, - certs: certs, - myAddr: addr, - }, - } - - from := session.NewAddress("127.0.0.1:8080") - fromBuf, err := from.MarshalText() - require.NoError(t, err) - - msg := &ptypes.CertificateChain{ - Address: fromBuf, - Value: chain.Certificate[0], - } - - _, err = overlay.Share(context.Background(), msg) - require.EqualError(t, err, "chain cert invalid: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs") -} - -func TestOverlayServer_Call(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - context: json.NewContext(), - addrFactory: addressFac, - }, - endpoints: map[string]*Endpoint{ - "test": {Handler: testHandler{}, Factory: fake.MessageFactory{}}, - "empty": {Handler: emptyHandler{}, Factory: fake.MessageFactory{}}, - }, - } - - ctx := makeCtx(headerURIKey, "test") - - resp, err := overlay.Call(ctx, &ptypes.Message{Payload: []byte(`{}`)}) - require.NoError(t, err) - require.NotNil(t, resp) - require.Equal(t, []byte(`{}`), resp.GetPayload()) - - ctx = makeCtx(headerURIKey, "empty") - - resp, err = overlay.Call(ctx, &ptypes.Message{Payload: []byte(`{}`)}) - require.NoError(t, err) - require.NotNil(t, resp) - require.Nil(t, resp.GetPayload()) -} - -func TestOverlayServer_UnknownHandler_Call(t *testing.T) { - overlay := overlayServer{ - endpoints: make(map[string]*Endpoint), - } - - ctx := makeCtx(headerURIKey, "unknown") - - _, err := overlay.Call(ctx, nil) - require.EqualError(t, err, "handler 'unknown' is not registered") - - _, err = overlay.Call(context.Background(), nil) - require.EqualError(t, err, "handler '' is not registered") - - _, err = overlay.Call(makeCtx(), nil) - require.EqualError(t, err, "handler '' is not registered") -} - -func TestOverlayServer_BadHandlerFactory_Call(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{}, - endpoints: map[string]*Endpoint{ - "test": {Handler: testHandler{}, Factory: fake.NewBadMessageFactory()}, - }, - } - - ctx := makeCtx(headerURIKey, "test") - - _, err := overlay.Call(ctx, &ptypes.Message{Payload: []byte(``)}) - require.EqualError(t, err, fake.Err("couldn't deserialize message")) -} - -func TestOverlayServer_BadHandler_Call(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - addrFactory: addressFac, - }, - endpoints: map[string]*Endpoint{ - "test": {Handler: mino.UnsupportedHandler{}, Factory: fake.MessageFactory{}}, - }, - } - - ctx := makeCtx(headerURIKey, "test") - - _, err := overlay.Call(ctx, &ptypes.Message{Payload: []byte(``)}) - require.EqualError(t, err, "handler failed to process: rpc is not supported") -} - -func TestOverlayServer_BadResponseFactory_Call(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - addrFactory: addressFac, - context: fake.NewBadContext(), - }, - endpoints: map[string]*Endpoint{ - "test": {Handler: testHandler{}, Factory: fake.MessageFactory{}}, - }, - } - - ctx := makeCtx(headerURIKey, "test") - - _, err := overlay.Call(ctx, &ptypes.Message{Payload: []byte(``)}) - require.EqualError(t, err, fake.Err("couldn't serialize result")) -} - -func TestOverlayServer_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - context: json.NewContext(), - addrFactory: addressFac, - myAddr: session.NewAddress("127.0.0.1:0"), - closer: &sync.WaitGroup{}, - connMgr: fakeConnMgr{}, - }, - endpoints: map[string]*Endpoint{ - "test": { - Handler: testHandler{skip: true}, - streams: make(map[string]session.Session), - }, - }, - } - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - inCtx := metadata.NewIncomingContext(ctx, metadata.Pairs( - headerURIKey, "test", - headerStreamIDKey, "streamTest", - session.HandshakeKey, "{}")) - - wg := sync.WaitGroup{} - wg.Add(5) - for i := 0; i < 5; i++ { - go func() { - defer wg.Done() - - err := overlay.Stream(&fakeSrvStream{ctx: inCtx}) - require.NoError(t, err) - }() - } - wg.Wait() - overlay.closer.Wait() - require.Empty(t, overlay.endpoints["test"].streams) - - overlay.endpoints["test"].streams["streamTest"] = fakeSession{numParents: 1} - err := overlay.Stream(&fakeSrvStream{ctx: inCtx}) - overlay.closer.Wait() - require.NoError(t, err) - require.Len(t, overlay.endpoints["test"].streams, 1) -} - -func TestOverlay_MissingHeaders_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - closer: &sync.WaitGroup{}, - }, - } - - stream := &fakeSrvStream{ctx: context.Background()} - - err := overlay.Stream(stream) - require.EqualError(t, err, "missing headers") -} - -func TestOverlay_MalformedRtingTable_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - closer: &sync.WaitGroup{}, - router: badRouter{}, - addrFactory: addressFac, - }, - } - - ctx := makeCtx(headerStreamIDKey, "abc", headerAddressKey, "{}") - - stream := &fakeSrvStream{ctx: ctx} - - err := overlay.Stream(stream) - require.EqualError(t, err, fake.Err("routing table: failed to create")) - - stream.ctx = makeCtx( - headerStreamIDKey, "abc", - headerAddressKey, "{}", - session.HandshakeKey, "{}") - - err = overlay.Stream(stream) - require.EqualError(t, err, fake.Err("routing table: invalid handshake")) - - overlay.router = badRouter{errFac: true} - err = overlay.Stream(stream) - require.EqualError(t, err, fake.Err("routing table: malformed handshake")) -} - -func TestOverlay_UnknownHandler_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - closer: &sync.WaitGroup{}, - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - context: json.NewContext(), - }, - } - - ctx := makeCtx(headerStreamIDKey, "abc", session.HandshakeKey, "{}") - - stream := &fakeSrvStream{ctx: ctx} - - err := overlay.Stream(stream) - require.EqualError(t, err, "handler '' is not registered") - - stream.ctx = makeCtx( - headerURIKey, "unknown", - session.HandshakeKey, "{}", - headerStreamIDKey, "abc") - - err = overlay.Stream(stream) - require.EqualError(t, err, "handler 'unknown' is not registered") -} - -func TestOverlay_BadStreamID_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - closer: &sync.WaitGroup{}, - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - context: json.NewContext(), - }, - } - - ctx := makeCtx(headerStreamIDKey, "", session.HandshakeKey, "{}") - - stream := &fakeSrvStream{ctx: ctx} - - err := overlay.Stream(stream) - require.EqualError(t, err, "unexpected empty stream ID") -} - -func TestOverlay_BadHandler_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - myAddr: session.NewAddress(""), - closer: &sync.WaitGroup{}, - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - context: json.NewContext(), - connMgr: fakeConnMgr{}, - }, - endpoints: map[string]*Endpoint{ - "test": { - Handler: testHandler{skip: true, err: fake.GetError()}, - streams: make(map[string]session.Session), - }, - }, - } - - ctx := makeCtx(headerURIKey, "test", headerStreamIDKey, "abc", session.HandshakeKey, "{}") - - stream := &fakeSrvStream{ctx: ctx} - - err := overlay.Stream(stream) - require.EqualError(t, err, fake.Err("handler failed to process")) -} - -func TestOverlay_BadConn_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - myAddr: session.NewAddress(""), - closer: &sync.WaitGroup{}, - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - context: json.NewContext(), - connMgr: fakeConnMgr{}, - }, - endpoints: map[string]*Endpoint{ - "test": { - Handler: testHandler{}, - streams: make(map[string]session.Session), - }, - }, - } - - ctx := makeCtx(headerURIKey, "test", headerStreamIDKey, "abc", session.HandshakeKey, "{}") - - stream := &fakeSrvStream{ctx: ctx, err: fake.GetError()} - - err := overlay.Stream(stream) - require.EqualError(t, err, fake.Err("failed to send header")) -} - -func TestOverlay_BadParentGateway_Stream(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - myAddr: session.NewAddress(""), - closer: &sync.WaitGroup{}, - router: tree.NewRouter(addressFac), - addrFactory: addressFac, - context: json.NewContext(), - connMgr: fakeConnMgr{err: fake.GetError()}, - }, - endpoints: map[string]*Endpoint{ - "test": { - Handler: testHandler{}, - streams: make(map[string]session.Session), - }, - }, - } - - ctx := makeCtx(headerURIKey, "test", headerStreamIDKey, "abc", session.HandshakeKey, "{}") - - stream := &fakeSrvStream{ctx: ctx, err: fake.GetError()} - - err := overlay.Stream(stream) - require.EqualError(t, err, fake.Err("gateway connection failed")) -} - -func TestOverlay_Forward(t *testing.T) { - overlay := overlayServer{ - overlay: &overlay{ - router: tree.NewRouter(addressFac), - context: json.NewContext(), - addrFactory: addressFac, - myAddr: session.NewAddress("127.0.0.1:0"), - closer: &sync.WaitGroup{}, - connMgr: fakeConnMgr{}, - }, - endpoints: make(map[string]*Endpoint), - } - - overlay.endpoints["test"] = &Endpoint{ - Handler: testHandler{skip: true}, - streams: map[string]session.Session{ - "stream-1": fakeSession{}, - }, - } - - ctx := makeCtx(headerURIKey, "test", headerStreamIDKey, "stream-1") - - ack, err := overlay.Forward(ctx, &ptypes.Packet{}) - require.NoError(t, err) - require.NotNil(t, ack) - - _, err = overlay.Forward(context.Background(), &ptypes.Packet{}) - require.EqualError(t, err, "no header in the context") - - _, err = overlay.Forward(makeCtx(headerURIKey, "unknown"), &ptypes.Packet{}) - require.EqualError(t, err, "handler 'unknown' is not registered") - - _, err = overlay.Forward(makeCtx(headerURIKey, "test", headerStreamIDKey, "nope"), &ptypes.Packet{}) - require.EqualError(t, err, "no stream 'nope' found") -} - -func TestOverlay_New(t *testing.T) { - o, err := newOverlay(&minoTemplate{ - myAddr: session.NewAddress("127.0.0.1:0"), - certs: certs.NewInMemoryStore(), - curve: elliptic.P521(), - random: rand.Reader, - useTLS: true, - }) - require.NoError(t, err) - - cert, err := o.certs.Load(session.NewAddress("127.0.0.1:0")) - require.NoError(t, err) - require.NotNil(t, cert) -} - -func TestOverlay_New_Hostname(t *testing.T) { - o, err := newOverlay(&minoTemplate{ - myAddr: session.NewAddress("localhost:0"), - certs: certs.NewInMemoryStore(), - curve: elliptic.P521(), - random: rand.Reader, - useTLS: true, - }) - require.NoError(t, err) - - cert, err := o.certs.Load(session.NewAddress("localhost:0")) - require.NoError(t, err) - require.NotNil(t, cert) -} - -func TestOverlay_New_Wrong_Cert_Store(t *testing.T) { - cert, _ := fake.MakeFullCertificate(t) - - _, err := newOverlay(&minoTemplate{ - cert: cert, - certs: fakeCerts{errStore: fake.GetError()}, - curve: elliptic.P521(), - random: rand.Reader, - useTLS: true, - }) - require.EqualError(t, err, fake.Err("failed to store cert")) -} - -func TestOverlay_Panic_GetCertificate(t *testing.T) { - defer func() { - r := recover() - require.Equal(t, "certificate of the overlay must be populated", r) - }() - - o := &overlay{ - certs: certs.NewInMemoryStore(), - } - - o.GetCertificateChain() -} - -func TestOverlay_Panic2_GetCertificate(t *testing.T) { - defer func() { - r := recover() - require.EqualError(t, r.(error), fake.Err("certificate of the overlay is inaccessible")) - }() - - o := &overlay{ - certs: fakeCerts{errLoad: fake.GetError()}, - } - - o.GetCertificateChain() -} - -func TestOverlay_Join(t *testing.T) { - overlay, err := newOverlay(&minoTemplate{ - myAddr: session.NewAddress("127.0.0.1:0"), - certs: certs.NewInMemoryStore(), - router: tree.NewRouter(addressFac), - fac: addressFac, - curve: elliptic.P521(), - random: rand.Reader, - useTLS: true, - }) - require.NoError(t, err) - - overlay.connMgr = fakeConnMgr{ - resp: ptypes.JoinResponse{ - Peers: []*ptypes.CertificateChain{{Value: overlay.GetCertificateChain()}}, - }, - } - - overlay.certs = fakeCerts{} - err = overlay.Join(&url.URL{}, "", nil) - require.NoError(t, err) - - overlay.myAddr = session.NewAddress("127.0.0.1:0") - overlay.certs = fakeCerts{err: fake.GetError()} - err = overlay.Join(&url.URL{}, "", nil) - require.EqualError(t, err, fake.Err("couldn't fetch distant certificate")) - - overlay.certs = fakeCerts{} - overlay.connMgr = fakeConnMgr{err: fake.GetError()} - err = overlay.Join(&url.URL{}, "", nil) - require.EqualError(t, err, fake.Err("couldn't open connection")) - - overlay.connMgr = fakeConnMgr{resp: ptypes.JoinResponse{}, errConn: fake.GetError()} - err = overlay.Join(&url.URL{}, "", nil) - require.EqualError(t, err, fake.Err("couldn't call join")) -} - -func TestMakeCertificate_WrongHostname(t *testing.T) { - o := overlay{} - o.myAddr = session.NewAddress(":xxx") - - err := o.makeCertificate() - require.EqualError(t, err, "failed to get hostname: malformed address: parse \"//:xxx\": invalid port \":xxx\" after host") -} - -func TestConnManager_Acquire(t *testing.T) { - addr := ParseAddress("127.0.0.1", 0) - - dst, err := NewMinogrpc(addr, nil, nil) - require.NoError(t, err) - - defer dst.GracefulStop() - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - - certsStore := mgr.certs - certsStore.Store(mgr.myAddr, certs.CertChain(fake.MakeCertificate(t))) - certsStore.Store(dst.GetAddress(), dst.GetCertificateChain()) - - conn, err := mgr.Acquire(dst.GetAddress()) - require.NoError(t, err) - require.NotNil(t, conn) - - _, err = mgr.Acquire(dst.GetAddress()) - require.NoError(t, err) - require.Len(t, mgr.conns, 1) - require.Equal(t, 2, mgr.counters[dst.GetAddress()]) - - mgr.Release(dst.GetAddress()) - mgr.Release(dst.GetAddress()) - require.Len(t, mgr.conns, 0) - require.Equal(t, 0, mgr.counters[dst.GetAddress()]) -} - -func TestConnManager_FailLoadDistantCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - mgr.certs = fakeCerts{errLoad: fake.GetError()} - - _, err := mgr.Acquire(session.Address{}) - require.EqualError(t, err, fake.Err("failed to retrieve transport credential: while loading distant cert")) -} - -func TestConnManager_MissingCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - - to := session.NewAddress("fake") - _, err := mgr.Acquire(to) - require.EqualError(t, err, "failed to retrieve transport credential: certificate for 'fake' not found") -} - -func TestConnManager_FailLoadOwnCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - mgr.certs = fakeCerts{ - errLoad: fake.GetError(), - counter: fake.NewCounter(1), - } - - _, err := mgr.Acquire(session.Address{}) - require.EqualError(t, err, fake.Err("failed to retrieve transport credential: while loading own cert")) -} - -func TestConnManager_MissingOwnCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - - to := session.NewAddress("fake") - mgr.certs.Store(to, fake.MakeCertificate(t)) - - _, err := mgr.Acquire(to) - require.EqualError(t, err, "failed to retrieve transport credential: couldn't find server 'fake.Address[0]' cert") -} - -func TestConnManager_BadDistantCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - to := session.NewAddress("fake") - - mgr.certs.Store(fake.NewAddress(0), fake.MakeCertificate(t)) - mgr.certs.Store(to, certs.CertChain("bad chain")) - - _, err := mgr.Acquire(to) - require.EqualError(t, err, "failed to retrieve transport credential: failed to parse distant cert: x509: malformed certificate") -} - -func TestConnManager_BadOwnCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - to := session.NewAddress("fake") - - mgr.certs.Store(fake.NewAddress(0), certs.CertChain("bad chain")) - mgr.certs.Store(to, fake.MakeCertificate(t)) - - _, err := mgr.Acquire(to) - require.EqualError(t, err, "failed to retrieve transport credential: failed to parse our own cert: x509: malformed certificate") -} - -func TestConnManager_EmptyOwnCert_Acquire(t *testing.T) { - defer revertGetTracer(getTracerForAddr) - getTracerForAddr = fake.GetTracerForAddrEmpty - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), true) - to := session.NewAddress("fake") - - mgr.certs.Store(fake.NewAddress(0), certs.CertChain{}) - mgr.certs.Store(to, fake.MakeCertificate(t)) - - _, err := mgr.Acquire(to) - require.EqualError(t, err, "failed to retrieve transport credential: no certificate found") -} - -func TestConnManager_BadAddress_Acquire(t *testing.T) { - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), false) - - mgr.certs.Store(fake.NewAddress(0), fake.MakeCertificate(t)) - - _, err := mgr.Acquire(mgr.myAddr) - require.EqualError(t, err, "invalid address type 'fake.Address'") -} - -func TestConnManager_BadTracer_Acquire(t *testing.T) { - addr := ParseAddress("127.0.0.1", 0) - - dst, err := NewMinogrpc(addr, nil, nil) - require.NoError(t, err) - - defer dst.GracefulStop() - - mgr := newConnManager(fake.NewAddress(0), certs.NewInMemoryStore(), false) - - getTracerForAddr = fake.GetTracerForAddrWithError - - certsStore := mgr.certs - certsStore.Store(mgr.myAddr, certs.CertChain(fake.MakeCertificate(t))) - certsStore.Store(dst.GetAddress(), dst.GetCertificateChain()) - - dstAddr := dst.GetAddress() - _, err = mgr.Acquire(dstAddr) - require.EqualError(t, err, fmt.Sprintf("failed to get tracer for addr %s: %s", - dst.GetAddress(), fake.GetError().Error())) - - getTracerForAddr = tracing.GetTracerForAddr -} - -func TestDecorateClientTrace_NoFound(t *testing.T) { - ctx := context.Background() - decorateClientTrace(ctx, nil, "", nil, nil, nil) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeInstances(t *testing.T, n int, call *fake.Call) ([]mino.Mino, []mino.RPC) { - mm := make([]mino.Mino, n) - rpcs := make([]mino.RPC, n) - for i := range mm { - addr := ParseAddress("127.0.0.1", 0) - - m, err := NewMinogrpc(addr, nil, tree.NewRouter(addressFac)) - require.NoError(t, err) - - mm[i] = m - rpcs[i] = mino.MustCreateRPC(m, "test", testHandler{call: call}, fake.MessageFactory{}) - - for _, k := range mm[:i] { - km := k.(*Minogrpc) - - m.GetCertificateStore().Store(k.GetAddress(), km.GetCertificateChain()) - km.GetCertificateStore().Store(m.GetAddress(), m.GetCertificateChain()) - } - } - - return mm, rpcs -} - -func checkError(t *testing.T, err error, mm ...mino.Mino) { - require.Error(t, err) - - for _, m := range mm { - if err.Error() == fmt.Sprintf("no route to %s: address is unreachable", m.GetAddress()) { - return - } - } - - t.Fatal("unexpected error", err) -} - -func makeCtx(kv ...string) context.Context { - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - return metadata.NewIncomingContext(ctx, metadata.Pairs(kv...)) -} - -type testHandler struct { - mino.UnsupportedHandler - call *fake.Call - skip bool - err error -} - -// Stream implements mino.Handler. It implements a simple receiver that will -// return the message received and close. -func (h testHandler) Stream(out mino.Sender, in mino.Receiver) error { - if h.skip { - return h.err - } - - from, msg, err := in.Recv(context.Background()) - if err != nil { - return err - } - - err = <-out.Send(msg, from) - if err != nil { - return err - } - - return nil -} - -func (h testHandler) Process(req mino.Request) (serde.Message, error) { - h.call.Add(req) - - return req.Message, nil -} - -type emptyHandler struct { - mino.UnsupportedHandler -} - -func (h emptyHandler) Process(req mino.Request) (serde.Message, error) { - return nil, nil -} - -type fakeSrvStream struct { - ptypes.Overlay_StreamServer - ctx context.Context - err error -} - -func (s fakeSrvStream) SendHeader(metadata.MD) error { - return s.err -} - -func (s fakeSrvStream) Context() context.Context { - return s.ctx -} - -func (s fakeSrvStream) Recv() (*ptypes.Packet, error) { - return nil, s.err -} - -type fakeCerts struct { - certs.Storage - err error - errLoad error - errStore error - counter *fake.Counter -} - -func (s fakeCerts) Store(mino.Address, certs.CertChain) error { - return s.errStore -} - -func (s fakeCerts) Load(mino.Address) (certs.CertChain, error) { - if s.errStore != nil { - return nil, s.errLoad - } - - if s.errLoad != nil && s.counter.Done() { - return nil, s.errLoad - } - - s.counter.Decrease() - - return []byte{0x89}, nil -} - -func (s fakeCerts) Fetch(certs.Dialable, []byte) error { - return s.err -} - -type fakeSession struct { - session.Session - - numParents int -} - -func (sess fakeSession) GetNumParents() int { - return sess.numParents -} - -func (fakeSession) Listen(p session.Relay, t router.RoutingTable, c chan struct{}) { - close(c) -} - -func (fakeSession) RecvPacket(mino.Address, *ptypes.Packet) (*ptypes.Ack, error) { - return &ptypes.Ack{}, nil -} - -func revertGetTracer(getTracer func(addr string) (opentracing.Tracer, error)) { - getTracerForAddr = getTracer -} diff --git a/dela/mino/minogrpc/session/addr.go b/dela/mino/minogrpc/session/addr.go deleted file mode 100644 index 2f11627..0000000 --- a/dela/mino/minogrpc/session/addr.go +++ /dev/null @@ -1,165 +0,0 @@ -// This file implements the address for minogrpc. -// -// Documentation Last Review: 07.10.2020 -// - -package session - -import ( - "fmt" - "net/url" - - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -const ( - orchestratorCode = "O" - followerCode = "F" -) - -// Address is a representation of the network Address of a participant. The -// overlay implementation requires a difference between an orchestrator and its -// source address, where the former initiates a protocol and the later -// participates. -// -// See session.wrapAddress for the abstraction provided to a caller external to -// the overlay module. -// -// - implements mino.Address -type Address struct { - orchestrator bool - host string -} - -// NewOrchestratorAddress creates a new address which will be considered as the -// initiator of a protocol. -func NewOrchestratorAddress(addr mino.Address) Address { - return Address{ - orchestrator: true, - host: addr.String(), - } -} - -// NewAddress creates a new address. -func NewAddress(host string) Address { - return Address{host: host} -} - -// GetDialAddress returns a string formatted to be understood by grpc.Dial() -// functions. -func (a Address) GetDialAddress() string { - return a.host -} - -// GetHostname parses the address to extract the hostname. -func (a Address) GetHostname() (string, error) { - url, err := url.Parse(fmt.Sprintf("//%s", a.host)) - if err != nil { - return "", xerrors.Errorf("malformed address: %v", err) - } - - return url.Hostname(), nil -} - -// Equal implements mino.Address. It returns true if both addresses are exactly -// similar, in the sense that an orchestrator won't match a follower address -// with the same host. -func (a Address) Equal(other mino.Address) bool { - switch addr := other.(type) { - case Address: - return addr == a - case wrapAddress: - // Switch to the wrapped address equality. - return addr.Equal(a) - } - - return false -} - -// MarshalText implements mino.Address. It returns the text format of the -// address that can later be deserialized. -func (a Address) MarshalText() ([]byte, error) { - data := []byte(followerCode) - if a.orchestrator { - data = []byte(orchestratorCode) - } - - data = append(data, []byte(a.host)...) - - return data, nil -} - -// String implements fmt.Stringer. It returns a string representation of the -// address. -func (a Address) String() string { - if a.orchestrator { - return fmt.Sprintf("Orchestrator:%s", a.host) - } - - return a.host -} - -// WrapAddress is a super type of the address so that the orchestrator becomes -// equal to its original address. It allows a caller of a protocol to compare -// the actual source address of a request while preserving the orchestrator -// address for the overlay when sending a message back. -// -// - implements mino.Address -type wrapAddress struct { - mino.Address -} - -func newWrapAddress(addr mino.Address) wrapAddress { - return wrapAddress{ - Address: addr, - } -} - -// Unwrap returns the wrapped address. -func (a wrapAddress) Unwrap() mino.Address { - return a.Address -} - -// Equal implements mino.Address. When it wraps a network address, it will -// consider addresses with the same host as similar, otherwise it returns the -// result of the underlying address comparison. That way, an orchestrator -// address will match the address with the same origin. -func (a wrapAddress) Equal(other mino.Address) bool { - me, ok := a.Address.(Address) - if !ok { - return a.Address.Equal(other) - } - - switch addr := other.(type) { - case Address: - return addr.host == me.host - case wrapAddress: - return addr == a - } - - return false -} - -// AddressFactory is a factory for addresses. -// -// - implements mino.AddressFactory -type AddressFactory struct { - serde.Factory -} - -// FromText implements mino.AddressFactory. It returns an instance of an -// address from a byte slice. -func (f AddressFactory) FromText(text []byte) mino.Address { - str := string(text) - - if len(str) == 0 { - return Address{} - } - - return Address{ - host: str[1:], - orchestrator: str[0] == orchestratorCode[0], - } -} diff --git a/dela/mino/minogrpc/session/addr_test.go b/dela/mino/minogrpc/session/addr_test.go deleted file mode 100644 index 3c3c973..0000000 --- a/dela/mino/minogrpc/session/addr_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package session - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestAddress_GetDialAddress(t *testing.T) { - addr := NewAddress("127.0.0.2") - require.Equal(t, "127.0.0.2", addr.GetDialAddress()) -} - -func TestAddress_GetHostname(t *testing.T) { - addr := NewAddress("127.0.0.1:2000") - - hostname, err := addr.GetHostname() - require.NoError(t, err) - require.Equal(t, "127.0.0.1", hostname) - - addr = NewAddress("example.com:8080") - - hostname, err = addr.GetHostname() - require.NoError(t, err) - require.Equal(t, "example.com", hostname) - - addr = NewAddress("\x00") - - _, err = addr.GetHostname() - require.EqualError(t, err, "malformed address: parse \"//\\x00\": net/url: invalid control character in URL") -} - -func TestAddress_Equal(t *testing.T) { - addr := NewAddress("127.0.0.1:2000") - require.True(t, addr.Equal(addr)) - require.False(t, addr.Equal(Address{})) - require.False(t, addr.Equal(fake.NewAddress(0))) - - orch := NewOrchestratorAddress(addr) - require.True(t, orch.Equal(orch)) - require.False(t, addr.Equal(orch)) - - wrapped := newWrapAddress(orch) - require.True(t, wrapped.Equal(wrapped)) - require.True(t, wrapped.Equal(addr)) - require.True(t, addr.Equal(wrapped)) - require.True(t, wrapped.Equal(orch)) - require.False(t, wrapped.Equal(fake.NewAddress(0))) -} - -func TestAddress_MarshalText(t *testing.T) { - addr := NewAddress("127.0.0.1:2000") - - buffer, err := addr.MarshalText() - require.NoError(t, err) - require.Equal(t, "F127.0.0.1:2000", string(buffer)) - - orch := NewOrchestratorAddress(addr) - - buffer, err = orch.MarshalText() - require.NoError(t, err) - require.Equal(t, "O127.0.0.1:2000", string(buffer)) -} - -func TestAddress_String(t *testing.T) { - addr := NewAddress("127.0.0.1:2000") - require.Equal(t, addr.host, addr.String()) - - orch := NewOrchestratorAddress(addr) - require.Equal(t, "Orchestrator:"+addr.host, orch.String()) -} - -func TestWrapAddress_Unwrap(t *testing.T) { - addr := newWrapAddress(NewAddress("A")) - - require.Equal(t, NewAddress("A"), addr.Unwrap()) -} - -func TestAddressFactory_FromText(t *testing.T) { - factory := AddressFactory{} - - addr := factory.FromText([]byte(orchestratorCode + "127.0.0.1:2000")) - require.Equal(t, "127.0.0.1:2000", addr.(Address).host) - require.True(t, addr.(Address).orchestrator) - - addr = factory.FromText(nil) - require.Equal(t, "", addr.(Address).host) - require.False(t, addr.(Address).orchestrator) - - addr = factory.FromText([]byte(followerCode + "127.0.0.1:2001")) - require.Equal(t, "127.0.0.1:2001", addr.(Address).host) - require.False(t, addr.(Address).orchestrator) - - addr = factory.FromText([]byte{1}) - require.Equal(t, "", addr.(Address).host) -} diff --git a/dela/mino/minogrpc/session/mod.go b/dela/mino/minogrpc/session/mod.go deleted file mode 100644 index e5ff4d9..0000000 --- a/dela/mino/minogrpc/session/mod.go +++ /dev/null @@ -1,663 +0,0 @@ -// Package session defines an abstraction of a session during a distributed RPC. -// -// During a stream-based distributed RPC in minogrpc, the stream is kept alive -// during the whole protocol to act as a health check so that resources can be -// cleaned eventually, or if something goes wrong. The session manages this -// state while also managing the relays to other participants that the node must -// forward the messages to. Basically, a session has one or several relays open -// to the parent nodes and zero, one or multiple relays to other participants -// depending on the routing of the messages. -// -// The package implements a unicast and a stream relay. Stream relay is only -// used when the orchestrator of a protocol is connecting to the first -// participant. Unicast is then used so that the sender of a message can receive -// feedbacks on the status of the message. -// -// Documentation Last Review: 07.10.20202 -package session - -import ( - "context" - "io" - "os" - "sync" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/internal/traffic" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" -) - -// HandshakeKey is the key to the handshake store in the headers. -const HandshakeKey = "handshake" - -// ConnectionManager is an interface required by the session to open and release -// connections to the relays. -type ConnectionManager interface { - Len() int - Acquire(mino.Address) (grpc.ClientConnInterface, error) - Release(mino.Address) -} - -// Session is an interface for a stream session that allows to send messages to -// the parent and relays, while receiving the ones for the local address. -type Session interface { - mino.Sender - mino.Receiver - - // GetNumParents returns the number of active parents for the session. - GetNumParents() int - - // Listen takes a stream that will determine when to close the session. - Listen(parent Relay, table router.RoutingTable, ready chan struct{}) - - // SetPassive sets a new passive parent. A passive parent is part of the - // parent relays, but the stream does not listen to, and thus it is not - // removed from the map if it closed. - SetPassive(parent Relay, table router.RoutingTable) - - // Close shutdowns the session so that future calls to receive will return - // an error. - Close() - - // RecvPacket takes a packet and the address of the distant peer that have - // sent it, then pass it to the correct relay according to the routing - // table. - RecvPacket(from mino.Address, p *ptypes.Packet) (*ptypes.Ack, error) -} - -// Relay is the interface of the relays spawn by the session when trying to -// contact a child node. -type Relay interface { - // GetDistantAddress returns the address of the peer at the other end of the - // relay. - GetDistantAddress() mino.Address - - // Stream returns the stream that is holding the relay. - Stream() PacketStream - - // Send sends a packet through the relay. - Send(ctx context.Context, p router.Packet) (*ptypes.Ack, error) - - // Close closes the relay and clean the resources. - Close() error -} - -type parent struct { - relay Relay - table router.RoutingTable -} - -// session is a participant to a stream protocol which has a parent gateway that -// determines when to close, and it can open further relays to distant peers if -// the routing table requires it. -// -// - implements session.Session -type session struct { - sync.Mutex - sync.WaitGroup - - log zerolog.Logger - md metadata.MD - me mino.Address - errs chan error - pktFac router.PacketFactory - msgFac serde.Factory - context serde.Context - queue Queue - relays map[mino.Address]Relay - connMgr ConnectionManager - traffic *traffic.Traffic - - parents map[mino.Address]parent - // A read-write lock is used there as there are much more read requests than - // write ones, and the read should be parallelized. - parentsLock sync.RWMutex -} - -// NewSession creates a new session for the provided parent relay. -func NewSession( - md metadata.MD, - me mino.Address, - msgFac serde.Factory, - pktFac router.PacketFactory, - ctx serde.Context, - connMgr ConnectionManager, -) Session { - sess := &session{ - log: dela.Logger.With().Str("addr", me.String()).Logger(), - md: md, - me: me, - errs: make(chan error, 1), - msgFac: msgFac, - pktFac: pktFac, - context: ctx, - queue: newNonBlockingQueue(), - relays: make(map[mino.Address]Relay), - connMgr: connMgr, - parents: make(map[mino.Address]parent), - } - - switch os.Getenv(traffic.EnvVariable) { - case "log": - sess.traffic = traffic.NewTraffic(me, io.Discard) - case "print": - sess.traffic = traffic.NewTraffic(me, os.Stdout) - } - - return sess -} - -// GetNumParents implements session.Session. It returns the number of active -// parents in the session. -func (s *session) GetNumParents() int { - s.parentsLock.RLock() - defer s.parentsLock.RUnlock() - - return len(s.parents) -} - -// Listen implements session.Session. It listens for the stream and returns only -// when the stream has been closed. -func (s *session) Listen(relay Relay, table router.RoutingTable, ready chan struct{}) { - defer func() { - s.parentsLock.Lock() - - delete(s.parents, relay.GetDistantAddress()) - - s.parentsLock.Unlock() - }() - - s.parentsLock.Lock() - s.parents[relay.GetDistantAddress()] = parent{relay: relay, table: table} - s.parentsLock.Unlock() - - close(ready) - - for { - _, err := relay.Stream().Recv() - code := status.Code(err) - if err == io.EOF || code != codes.Unknown { - s.log.Trace().Stringer("code", code).Msg("session closing") - - return - } - if err != nil { - s.errs <- xerrors.Errorf("stream closed unexpectedly: %v", err) - - return - } - } -} - -// SetPassive implements session.Session. It adds the parent relay to the map -// but in the contrary of Listen, it won't listen for the stream. -func (s *session) SetPassive(p Relay, table router.RoutingTable) { - s.parentsLock.Lock() - s.parents[p.GetDistantAddress()] = parent{ - relay: p, - table: table, - } - s.parentsLock.Unlock() -} - -// Close implements session.Session. It shutdowns the session and waits for the -// relays to close. -func (s *session) Close() { - close(s.errs) - - s.Wait() - - s.log.Trace().Msg("session has been closed") -} - -func (s *session) CopyParents() map[mino.Address]parent { - ps := map[mino.Address]parent{} - for k, v := range s.parents { - ps[k] = v - } - return ps -} - -// RecvPacket implements session.Session. It process the packet and send it to -// the relays, or itself. -func (s *session) RecvPacket(from mino.Address, p *ptypes.Packet) (*ptypes.Ack, error) { - pkt, err := s.pktFac.PacketOf(s.context, p.GetSerialized()) - if err != nil { - return nil, xerrors.Errorf("packet malformed: %v", err) - } - - s.parentsLock.RLock() - parents := s.CopyParents() - s.parentsLock.RUnlock() - - // Try to send the packet to each parent until one works. - for _, parent := range parents { - s.traffic.LogRecv(parent.relay.Stream().Context(), from, pkt) - - errs := make(chan error, len(pkt.GetDestination())) - sent := s.sendPacket(parent, pkt, errs) - close(errs) - - if sent { - ack := &ptypes.Ack{} - - for err := range errs { - ack.Errors = append(ack.Errors, err.Error()) - } - - return ack, nil - } - } - - return nil, xerrors.Errorf("packet is dropped (tried %d parent-s)", len(parents)) -} - -// Send implements mino.Sender. It sends the message to the provided addresses -// through the relays or the parent. -func (s *session) Send(msg serde.Message, addrs ...mino.Address) <-chan error { - errs := make(chan error, len(addrs)+1) - - for i, addr := range addrs { - switch to := addr.(type) { - case wrapAddress: - addrs[i] = to.Unwrap() - } - } - - go func() { - defer close(errs) - - data, err := msg.Serialize(s.context) - if err != nil { - errs <- xerrors.Errorf("failed to serialize msg: %v", err) - return - } - - s.parentsLock.RLock() - parents := s.CopyParents() - s.parentsLock.RUnlock() - - for _, parent := range parents { - packet := parent.table.Make(s.me, addrs, data) - - sent := s.sendPacket(parent, packet, errs) - if sent { - return - } - } - - errs <- xerrors.New("packet ignored") - }() - - return errs -} - -// Recv implements mino.Receiver. It waits for a message to arrive and returns -// it, or returns an error if something wrong happens. The context can cancel -// the blocking call. -func (s *session) Recv(ctx context.Context) (mino.Address, serde.Message, error) { - select { - case <-ctx.Done(): - return nil, nil, ctx.Err() - - case err := <-s.errs: - if err != nil { - return nil, nil, xerrors.Errorf("stream closed unexpectedly: %v", err) - } - - return nil, nil, io.EOF - - case packet := <-s.queue.Channel(): - msg, err := s.msgFac.Deserialize(s.context, packet.GetMessage()) - if err != nil { - return nil, nil, xerrors.Errorf("message: %v", err) - } - - // The source address is wrapped so that an orchestrator will look like - // its actual source address to the caller. - from := newWrapAddress(packet.GetSource()) - - return from, msg, nil - } -} - -func (s *session) sendPacket(p parent, pkt router.Packet, errs chan error) bool { - me := pkt.Slice(s.me) - if me != nil { - err := s.queue.Push(me) - if err != nil { - errs <- xerrors.Errorf("%v dropped the packet: %v", s.me, err) - } - } - - routes, voids := p.table.Forward(pkt) - for addr, void := range voids { - errs <- xerrors.Errorf("no route to %v: %v", addr, void.Error) - } - - if len(routes) == 0 && len(voids) == 0 { - return me != nil - } - - wg := sync.WaitGroup{} - wg.Add(len(routes)) - - for addr, packet := range routes { - go s.sendTo(p, addr, packet, errs, &wg) - } - - wg.Wait() - - return true -} - -func (s *session) sendTo(p parent, to mino.Address, pkt router.Packet, errs chan error, wg *sync.WaitGroup) { - defer wg.Done() - - var relay Relay - var err error - - if to == nil { - relay = p.relay - } else { - relay, err = s.setupRelay(p, to) - if err != nil { - s.log.Warn().Err(err).Stringer("to", to).Msg("failed to setup relay") - - // Try to open a different relay. - s.onFailure(p, to, pkt, errs) - - return - } - } - - ctx := p.relay.Stream().Context() - - ack, err := relay.Send(ctx, pkt) - if to == nil && err != nil { - // The parent relay is unavailable which means the session will - // eventually close. - s.log.Warn().Err(err).Msg("parent is closing") - - code := status.Code(xerrors.Unwrap(err)) - - errs <- xerrors.Errorf("session %v is closing: %v", s.me, code) - - return - } - if err != nil { - s.log.Warn().Err(err).Msg("relay failed to send") - - // Try to send the packet through a different route. - s.onFailure(p, relay.GetDistantAddress(), pkt, errs) - - return - } - - for _, err := range ack.Errors { - // Note: it would be possible to use this ack feedback to further - // improve the correction of the routes by retrying here too. - errs <- xerrors.New(err) - } -} - -func (s *session) setupRelay(p parent, addr mino.Address) (Relay, error) { - s.Lock() - defer s.Unlock() - - relay, initiated := s.relays[addr] - - if initiated { - return relay, nil - } - - hs, err := p.table.PrepareHandshakeFor(addr).Serialize(s.context) - if err != nil { - return nil, xerrors.Errorf("failed to serialize handshake: %v", err) - } - - // 1. Acquire a connection to the distant peer. - conn, err := s.connMgr.Acquire(addr) - if err != nil { - return nil, xerrors.Errorf("failed to dial: %v", err) - } - - md := s.md.Copy() - md.Set(HandshakeKey, string(hs)) - - ctx := metadata.NewOutgoingContext(p.relay.Stream().Context(), md) - - cl := ptypes.NewOverlayClient(conn) - - stream, err := cl.Stream(ctx, grpc.WaitForReady(false)) - if err != nil { - s.connMgr.Release(addr) - return nil, xerrors.Errorf("client: %v", err) - } - - // 2. Wait for the header event to confirm the stream is registered in the - // session at the other end. - _, err = stream.Header() - if err != nil { - s.connMgr.Release(addr) - return nil, xerrors.Errorf("failed to receive header: %v", err) - } - - // 3. Create and run the relay to respond to incoming packets. - newRelay := NewRelay(stream, addr, s.context, conn, s.md) - - s.relays[addr] = newRelay - s.Add(1) - - go func() { - defer func() { - s.Lock() - delete(s.relays, addr) - s.Unlock() - - newRelay.Close() - - // Let the manager know it can close the connection if necessary. - s.connMgr.Release(addr) - - s.Done() - - s.traffic.LogRelayClosed(addr) - s.log.Trace(). - Err(err). - Stringer("gateway", addr). - Msg("relay has closed") - }() - - for { - _, err := stream.Recv() - code := status.Code(err) - if err == io.EOF || code != codes.Unknown { - s.log.Trace(). - Stringer("code", code). - Stringer("to", addr). - Msg("relay is closing") - - return - } - if err != nil { - s.log. - Err(err). - Stringer("to", addr). - Msg("relay closed unexpectedly") - - // Relay has lost the connection, therefore we announce the - // address as unreachable. - p.table.OnFailure(addr) - - return - } - } - }() - - s.traffic.LogRelay(addr) - - s.log.Trace().Stringer("to", addr).Msg("relay opened") - - return newRelay, nil -} - -func (s *session) onFailure(p parent, gateway mino.Address, pkt router.Packet, errs chan error) { - err := p.table.OnFailure(gateway) - if err != nil { - errs <- xerrors.Errorf("no route to %v: %v", gateway, err) - return - } - - // Retry to send the packet after the announcement of a link failure. This - // recursive call will eventually end by either a success, or a total - // failure to send the packet. - s.sendPacket(p, pkt, errs) -} - -// PacketStream is a gRPC stream to send and receive protobuf packets. -type PacketStream interface { - Context() context.Context - Send(*ptypes.Packet) error - Recv() (*ptypes.Packet, error) -} - -// UnicastRelay is a relay to a distant peer that is using unicast to send -// packets so that it can learn about failures. -// -// - implements session.Relay -type unicastRelay struct { - sync.Mutex - md metadata.MD - gw mino.Address - stream PacketStream - conn grpc.ClientConnInterface - context serde.Context -} - -// NewRelay returns a new relay that will send messages to the gateway through -// unicast requests. -func NewRelay(stream PacketStream, gw mino.Address, - ctx serde.Context, conn grpc.ClientConnInterface, md metadata.MD) Relay { - - r := &unicastRelay{ - md: md, - gw: gw, - stream: stream, - context: ctx, - conn: conn, - } - - return r -} - -// GetDistantAddress implements session.Relay. It returns the address of the -// distant peer. -func (r *unicastRelay) GetDistantAddress() mino.Address { - return r.gw -} - -// Stream implements session.Relay. It returns the stream associated to the -// relay. -func (r *unicastRelay) Stream() PacketStream { - return r.stream -} - -// Send implements session.Relay. It sends the message to the distant peer. -func (r *unicastRelay) Send(ctx context.Context, p router.Packet) (*ptypes.Ack, error) { - data, err := p.Serialize(r.context) - if err != nil { - return nil, xerrors.Errorf("failed to serialize: %v", err) - } - - client := ptypes.NewOverlayClient(r.conn) - - ctx = metadata.NewOutgoingContext(ctx, r.md) - - ack, err := client.Forward(ctx, &ptypes.Packet{Serialized: data}) - if err != nil { - return nil, xerrors.Errorf("client: %w", err) - } - - return ack, nil -} - -// Close implements session.Relay. It closes the stream. -func (r *unicastRelay) Close() error { - stream, ok := r.stream.(ptypes.Overlay_StreamClient) - if ok { - err := stream.CloseSend() - if err != nil { - return xerrors.Errorf("failed to close stream: %v", err) - } - } - - return nil -} - -// StreamRelay is a relay to a distant peer that will send the packets through a -// stream, which means that it assumes the packet arrived if send is successful. -// -// - implements session.Relay -type streamRelay struct { - sync.Mutex - gw mino.Address - stream PacketStream - context serde.Context -} - -// NewStreamRelay creates a new relay that will send the packets through the -// stream. -func NewStreamRelay(gw mino.Address, stream PacketStream, ctx serde.Context) Relay { - return &streamRelay{ - gw: gw, - stream: stream, - context: ctx, - } -} - -// GetDistantAddress implements session.Relay. It returns the address of the -// distant peer. -func (r *streamRelay) GetDistantAddress() mino.Address { - return r.gw -} - -// Stream implements session.Relay. It returns the stream associated with the -// relay. -func (r *streamRelay) Stream() PacketStream { - return r.stream -} - -// Send implements session.Relay. It sends the packet through the stream. -func (r *streamRelay) Send(ctx context.Context, p router.Packet) (*ptypes.Ack, error) { - data, err := p.Serialize(r.context) - if err != nil { - return nil, xerrors.Errorf("failed to serialize: %v", err) - } - - r.Lock() - defer r.Unlock() - - err = r.stream.Send(&ptypes.Packet{Serialized: data}) - - if err != nil { - return nil, xerrors.Errorf("stream: %v", err) - } - - return &ptypes.Ack{}, nil -} - -// Close implements session.Relay. It does not do anything as it is not -// responsible for closing the stream. -func (r *streamRelay) Close() error { - return nil -} diff --git a/dela/mino/minogrpc/session/mod_test.go b/dela/mino/minogrpc/session/mod_test.go deleted file mode 100644 index 164537b..0000000 --- a/dela/mino/minogrpc/session/mod_test.go +++ /dev/null @@ -1,636 +0,0 @@ -package session - -import ( - "context" - "io" - "os" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/internal/traffic" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/ptypes" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" -) - -func TestSessionNew(t *testing.T) { - curr := os.Getenv(traffic.EnvVariable) - defer os.Setenv(traffic.EnvVariable, curr) - - os.Setenv(traffic.EnvVariable, "log") - sess := NewSession(nil, fake.NewAddress(999), nil, nil, fake.NewContext(), nil) - require.NotNil(t, sess.(*session).traffic) - - os.Setenv(traffic.EnvVariable, "print") - sess = NewSession(nil, fake.NewAddress(999), nil, nil, fake.NewContext(), nil) - require.NotNil(t, sess.(*session).traffic) - - os.Unsetenv(traffic.EnvVariable) - sess = NewSession(nil, fake.NewAddress(999), nil, nil, fake.NewContext(), nil) - require.Nil(t, sess.(*session).traffic) -} - -func TestSession_getNumParents(t *testing.T) { - sess := &session{} - - num := sess.GetNumParents() - require.Equal(t, 0, num) - - sess.parents = map[mino.Address]parent{ - fake.NewAddress(0): {}, - fake.NewAddress(1): {}, - } - - num = sess.GetNumParents() - require.Equal(t, 2, num) -} - -func TestSession_Listen(t *testing.T) { - p := &streamRelay{ - stream: &fakeStream{}, - gw: fake.NewAddress(123), - } - - sess := &session{ - errs: make(chan error), - parents: map[mino.Address]parent{ - p.gw: {relay: p}, - }, - } - - ready := make(chan struct{}) - - sess.Listen(p, fakeTable{}, ready) - select { - case <-ready: - case <-time.After(time.Second): - t.Fatal("expect channel to be closed") - } - - require.Len(t, sess.parents, 0) - - sess.errs = make(chan error, 1) - p.stream = &fakeStream{err: fake.GetError()} - sess.Listen(p, fakeTable{}, make(chan struct{})) - select { - case err := <-sess.errs: - require.EqualError(t, err, fake.Err("stream closed unexpectedly")) - default: - t.Fatal("expect an error") - } -} - -func TestSession_Close(t *testing.T) { - sess := &session{errs: make(chan error)} - - sess.Close() - select { - case <-sess.errs: - default: - t.Fatal("expect channel to be closed") - } -} - -func TestSession_Passive(t *testing.T) { - sess := &session{ - parents: make(map[mino.Address]parent), - } - - sess.SetPassive(&streamRelay{}, fakeTable{}) - require.Len(t, sess.parents, 1) -} - -func TestSession_RecvPacket(t *testing.T) { - sess := &session{ - pktFac: fakePktFac{}, - queue: newNonBlockingQueue(), - parents: map[mino.Address]parent{ - fake.NewAddress(123): { - relay: &streamRelay{stream: &fakeStream{}}, - table: fakeTable{err: xerrors.New("bad route")}, - }, - }, - } - - ack, err := sess.RecvPacket(fake.NewAddress(0), &ptypes.Packet{}) - require.NoError(t, err) - require.NotEmpty(t, ack.Errors) - require.Equal(t, "no route to fake.Address[400]: bad route", ack.GetErrors()[0]) - - sess.pktFac = fakePktFac{err: fake.GetError()} - _, err = sess.RecvPacket(fake.NewAddress(0), &ptypes.Packet{}) - require.EqualError(t, err, fake.Err("packet malformed")) - - sess.pktFac = fakePktFac{} - sess.parents = nil - _, err = sess.RecvPacket(fake.NewAddress(0), &ptypes.Packet{}) - require.EqualError(t, err, "packet is dropped (tried 0 parent-s)") -} - -func TestSession_Send(t *testing.T) { - stream := &fakeStream{calls: &fake.Call{}} - key := fake.NewAddress(123) - - sess := &session{ - me: fake.NewAddress(600), - context: fake.NewContext(), - queue: newNonBlockingQueue(), - relays: make(map[mino.Address]Relay), - parents: map[mino.Address]parent{ - key: { - relay: &streamRelay{stream: stream}, - table: fakeTable{}, - }, - }, - } - - errs := sess.Send(fake.Message{}, newWrapAddress(fake.NewAddress(0))) - require.NoError(t, <-errs) - require.Equal(t, 1, stream.calls.Len()) - - sess.parents[key] = parent{ - relay: &streamRelay{stream: stream}, - table: fakeTable{route: fake.NewAddress(0)}, - } - sess.relays[fake.NewAddress(0)] = NewStreamRelay(nil, &fakeStream{}, sess.context) - errs = sess.Send(fake.Message{}, fake.NewAddress(1)) - require.NoError(t, <-errs) - - errs = sess.Send(fake.NewBadPublicKey()) - require.EqualError(t, <-errs, fake.Err("failed to serialize msg")) - require.NoError(t, <-errs) - - sess.queue = badQueue{} - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, fake.Err("fake.Address[600] dropped the packet")) - - sess.queue = newNonBlockingQueue() - sess.parents[key] = parent{ - relay: &streamRelay{stream: stream}, - table: fakeTable{err: fake.GetError()}, - } - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, fake.Err("no route to fake.Address[400]")) - require.NoError(t, <-errs) - - // Test when an error occurred when setting up a relay, which moves to the - // failure handler that will then fail on the routing table. - sess.parents[key] = parent{ - relay: &streamRelay{stream: stream}, - table: fakeTable{errFail: fake.GetError(), route: fake.NewAddress(5)}, - } - sess.connMgr = fakeConnMgr{err: xerrors.New("blabla")} - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, fake.Err("no route to fake.Address[5]")) - require.NoError(t, <-errs) - - // Test when an error occurred when forwarding a message to a relay, which - // moves to the failure handler that will fail on the routing table. - sess.relays[fake.NewAddress(6)] = NewStreamRelay( - fake.NewAddress(800), - &fakeStream{err: fake.GetError()}, - sess.context) - - sess.parents[key] = parent{ - relay: NewStreamRelay(nil, &fakeStream{err: fake.GetError()}, sess.context), - table: fakeTable{errFail: xerrors.New("unavailable"), route: fake.NewAddress(6)}, - } - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, "no route to fake.Address[800]: unavailable") - require.NoError(t, <-errs) - - // Test when the parent stream has closed. - sess.me = fake.NewAddress(123) - sess.parents[key] = parent{ - relay: NewStreamRelay(nil, &fakeStream{err: fake.GetError()}, sess.context), - table: fakeTable{}, - } - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, "session fake.Address[123] is closing: OK") - - // Test when a packet is sent but some addresses are not reachable. - sess.parents[key] = parent{ - relay: &unicastRelay{ - stream: &fakeStream{}, - conn: fakeConnection{ack: &ptypes.Ack{Errors: []string{"bad route"}}}, - }, - table: fakeTable{}, - } - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, "bad route") - - sess.parents = map[mino.Address]parent{ - key: {table: fakeTable{empty: true}}, - } - errs = sess.Send(fake.Message{}) - require.EqualError(t, <-errs, "packet ignored") -} - -func TestSession_SetupRelay(t *testing.T) { - sess := &session{ - connMgr: fakeConnMgr{}, - context: fake.NewContext(), - relays: make(map[mino.Address]Relay), - queue: newNonBlockingQueue(), - } - - p := parent{ - relay: &streamRelay{stream: &fakeStream{}}, - table: fakeTable{}, - } - - relay, err := sess.setupRelay(p, fake.NewAddress(0)) - require.NoError(t, err) - require.NotNil(t, relay) - sess.Wait() - - sess.connMgr = fakeConnMgr{err: fake.GetError()} - _, err = sess.setupRelay(p, fake.NewAddress(1)) - require.EqualError(t, err, fake.Err("failed to dial")) - - sess.connMgr = fakeConnMgr{errConn: fake.GetError()} - _, err = sess.setupRelay(p, fake.NewAddress(1)) - require.EqualError(t, err, fake.Err("client")) - - sess.connMgr = fakeConnMgr{errHeader: fake.GetError()} - _, err = sess.setupRelay(p, fake.NewAddress(1)) - require.EqualError(t, err, fake.Err("failed to receive header")) - - sess.connMgr = fakeConnMgr{} - p.table = fakeTable{err: fake.GetError()} - _, err = sess.setupRelay(p, fake.NewAddress(1)) - require.EqualError(t, err, fake.Err("failed to serialize handshake")) - - p.table = fakeTable{} - sess.connMgr = fakeConnMgr{errRecv: status.Error(codes.Canceled, "")} - _, err = sess.setupRelay(p, fake.NewAddress(1)) - require.NoError(t, err) - sess.Wait() - - sess.connMgr = fakeConnMgr{errRecv: fake.GetError()} - _, err = sess.setupRelay(p, fake.NewAddress(2)) - require.NoError(t, err) - sess.Wait() -} - -func TestSession_Recv(t *testing.T) { - sess := &session{ - queue: newNonBlockingQueue(), - msgFac: fake.MessageFactory{}, - errs: make(chan error, 1), - } - - sess.queue.Push(fakePkt{}) - sess.queue.Push(fakePkt{}) - - ctx, cancel := context.WithCancel(context.Background()) - - from, msg, err := sess.Recv(ctx) - require.NoError(t, err) - require.True(t, from.Equal(fake.NewAddress(700))) - require.Equal(t, fake.Message{}, msg) - - sess.msgFac = fake.NewBadMessageFactory() - _, _, err = sess.Recv(ctx) - require.EqualError(t, err, fake.Err("message")) - - sess.errs <- fake.GetError() - _, _, err = sess.Recv(ctx) - require.EqualError(t, err, fake.Err("stream closed unexpectedly")) - - sess.errs <- nil - _, _, err = sess.Recv(ctx) - require.Equal(t, io.EOF, err) - - cancel() - _, _, err = sess.Recv(ctx) - require.Equal(t, context.Canceled, err) -} - -func TestSession_OnFailure(t *testing.T) { - sess := &session{ - queue: newNonBlockingQueue(), - } - - p := parent{ - relay: &streamRelay{stream: &fakeStream{}}, - table: fakeTable{}, - } - - errs := make(chan error, 1) - - sess.onFailure(p, fake.NewAddress(0), fakePkt{}, errs) - - p.table = fakeTable{errFail: fake.GetError()} - sess.onFailure(p, fake.NewAddress(0), fakePkt{}, errs) - require.EqualError(t, <-errs, fake.Err("no route to fake.Address[0]")) - - p.table = fakeTable{err: fake.GetError()} - sess.onFailure(p, fake.NewAddress(0), fakePkt{dest: fake.NewAddress(1)}, errs) - require.EqualError(t, <-errs, fake.Err("no route to fake.Address[400]")) -} - -func TestRelay_Send(t *testing.T) { - r := &unicastRelay{ - md: make(metadata.MD), - conn: fakeConnection{}, - } - - ack, err := r.Send(context.Background(), fakePkt{}) - require.NoError(t, err) - require.NotNil(t, ack) - - _, err = r.Send(context.Background(), fakePkt{err: fake.GetError()}) - require.EqualError(t, err, fake.Err("failed to serialize")) - - r.conn = fakeConnection{err: fake.GetError()} - _, err = r.Send(context.Background(), fakePkt{}) - require.EqualError(t, err, fake.Err("client")) -} - -func TestRelay_Close(t *testing.T) { - r := &unicastRelay{ - stream: &fakeStream{}, - } - - err := r.Close() - require.NoError(t, err) - - r.stream = &fakeStream{err: fake.GetError()} - err = r.Close() - require.EqualError(t, err, fake.Err("failed to close stream")) -} - -func TestStreamRelay_Send(t *testing.T) { - r := &streamRelay{ - stream: &fakeStream{}, - } - - ack, err := r.Send(context.Background(), fakePkt{}) - require.NoError(t, err) - require.Empty(t, ack.Errors) - - _, err = r.Send(context.Background(), fakePkt{err: fake.GetError()}) - require.EqualError(t, err, fake.Err("failed to serialize")) -} - -func TestStreamRelay_Close(t *testing.T) { - r := &streamRelay{} - - require.NoError(t, r.Close()) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeStream struct { - ptypes.Overlay_StreamClient - - num int - calls *fake.Call - err error -} - -func (s *fakeStream) Context() context.Context { - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - return ctx -} - -func (s *fakeStream) Recv() (*ptypes.Packet, error) { - s.calls.Add("recv") - if s.num > 0 { - s.num-- - - return &ptypes.Packet{}, nil - } - - if s.err != nil { - return nil, s.err - } - - return nil, io.EOF -} - -func (s *fakeStream) Send(p *ptypes.Packet) error { - s.calls.Add("send", p) - return s.err -} - -func (s *fakeStream) CloseSend() error { - return s.err -} - -type fakePkt struct { - router.Packet - dest mino.Address - empty bool - err error -} - -func (p fakePkt) GetSource() mino.Address { - return fake.NewAddress(700) -} - -func (p fakePkt) GetDestination() []mino.Address { - if p.dest == nil { - return nil - } - - return []mino.Address{p.dest} -} - -func (p fakePkt) GetMessage() []byte { - return []byte(`{}`) -} - -func (p fakePkt) Slice(mino.Address) router.Packet { - if p.empty { - return nil - } - - return p -} - -func (p fakePkt) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), p.err -} - -type fakePktFac struct { - router.PacketFactory - - err error -} - -func (fac fakePktFac) PacketOf(serde.Context, []byte) (router.Packet, error) { - return fakePkt{dest: fake.NewAddress(0)}, fac.err -} - -type fakeTable struct { - router.RoutingTable - - route mino.Address - empty bool - err error - errFail error -} - -func (t fakeTable) Make(mino.Address, []mino.Address, []byte) router.Packet { - return fakePkt{dest: fake.NewAddress(0), empty: t.empty} -} - -func (t fakeTable) PrepareHandshakeFor(mino.Address) router.Handshake { - return fakeHandshake{err: t.err} -} - -func (t fakeTable) Forward(router.Packet) (router.Routes, router.Voids) { - voids := make(router.Voids) - if t.err != nil { - voids[fake.NewAddress(400)] = router.Void{Error: t.err} - } - - routes := router.Routes{} - if !t.empty { - routes[t.route] = fakePkt{} - } - - return routes, voids -} - -func (t fakeTable) OnFailure(mino.Address) error { - return t.errFail -} - -type fakeHandshake struct { - router.Handshake - - err error -} - -func (h fakeHandshake) Serialize(serde.Context) ([]byte, error) { - return []byte(`{}`), h.err -} - -type fakeConnMgr struct { - ConnectionManager - - msg *ptypes.Packet - err error - errConn error - errStream error - errRecv error - errHeader error -} - -func (mgr fakeConnMgr) Acquire(mino.Address) (grpc.ClientConnInterface, error) { - conn := fakeConnection{ - msg: mgr.msg, - err: mgr.errConn, - errStream: mgr.errStream, - errRecv: mgr.errRecv, - errHeader: mgr.errHeader, - } - - return conn, mgr.err -} - -func (mgr fakeConnMgr) Release(mino.Address) {} - -type fakeConnection struct { - grpc.ClientConnInterface - msg *ptypes.Packet - ack *ptypes.Ack - err error - errStream error - errRecv error - errHeader error -} - -func (conn fakeConnection) Invoke(ctx context.Context, - method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error { - - if conn.ack != nil { - *(reply.(*ptypes.Ack)) = *conn.ack - } - - return conn.err -} - -func (conn fakeConnection) NewStream(ctx context.Context, desc *grpc.StreamDesc, - m string, opts ...grpc.CallOption) (grpc.ClientStream, error) { - - ch := make(chan *ptypes.Packet, 1) - - if conn.msg != nil { - ch <- conn.msg - } - - go func() { - <-ctx.Done() - close(ch) - }() - - stream := &fakeClientStream{ - ch: ch, - err: conn.errStream, - errRecv: conn.errRecv, - errHeader: conn.errHeader, - } - - return stream, conn.err -} - -type fakeClientStream struct { - grpc.ClientStream - ch chan *ptypes.Packet - err error - errRecv error - errHeader error -} - -func (str *fakeClientStream) Context() context.Context { - return context.Background() -} - -func (str *fakeClientStream) Header() (metadata.MD, error) { - return make(metadata.MD), str.errHeader -} - -func (str *fakeClientStream) SendMsg(m interface{}) error { - return str.err -} - -func (str *fakeClientStream) RecvMsg(m interface{}) error { - if str.errRecv != nil { - return str.errRecv - } - - msg, more := <-str.ch - if !more { - return io.EOF - } - - *(m.(*ptypes.Packet)) = *msg - return nil -} - -func (str *fakeClientStream) CloseSend() error { - return nil -} - -type badQueue struct { - Queue -} - -func (badQueue) Push(router.Packet) error { - return fake.GetError() -} diff --git a/dela/mino/minogrpc/session/queue.go b/dela/mino/minogrpc/session/queue.go deleted file mode 100644 index c04b049..0000000 --- a/dela/mino/minogrpc/session/queue.go +++ /dev/null @@ -1,125 +0,0 @@ -// This file contains an implementation of a non-blocking queue for messages. -// -// Documentation Last Review: 07.10.2020 -// - -package session - -import ( - "go.dedis.ch/dela/mino/router" - "golang.org/x/xerrors" - "math" - "sync" -) - -// maximum capacity of the buffer is: (2^limitExponent) * initialCapacity -const initialCapacity = 10000 -const limitExponent = 14 - -// Queue is an interface to queue messages. -type Queue interface { - Channel() <-chan router.Packet - Push(router.Packet) error -} - -// NonBlockingQueue is an implementation of a queue that makes sure pushing a -// message will never hang. The queue will fill a buffer if the channel is not -// drained and will drop messages when the limit is reached. -// -// - implements session.Queue -type NonBlockingQueue struct { - sync.Mutex - working sync.WaitGroup - buffer []router.Packet - cap float64 - limit float64 - running bool - ch chan router.Packet -} - -func newNonBlockingQueue() *NonBlockingQueue { - return &NonBlockingQueue{ - ch: make(chan router.Packet, 1), - cap: initialCapacity, - limit: limitExponent, - } -} - -// Channel implements session.Queue. It returns a channel that will be populated -// with incoming messages. The queue uses a buffer when the channel is busy -// therefore this channel should listened to as much as possible to drain the -// messages. At some point when the size of the buffer reaches a limit, messages -// will be dropped. -func (q *NonBlockingQueue) Channel() <-chan router.Packet { - return q.ch -} - -// Push implements session.Queue. It appends the message to the queue without -// blocking. The message is dropped if the queue is at maximum capacity by -// returning an error. -func (q *NonBlockingQueue) Push(msg router.Packet) error { - select { - case q.ch <- msg: - // Message went through ! - default: - q.Lock() - - if len(q.buffer) == cap(q.buffer) { - if !q.replaceBuffer() { - q.Unlock() - return xerrors.New("queue is at maximum capacity") - } - } - - q.buffer = append(q.buffer, msg) - - if !q.running { - q.running = true - go q.pushAndWait() - } - q.Unlock() - } - - return nil -} - -func (q *NonBlockingQueue) pushAndWait() { - q.working.Add(1) - defer q.working.Done() - - for { - q.Lock() - if len(q.buffer) == 0 { - q.running = false - q.Unlock() - return - } - - msg := q.buffer[0] - q.buffer = q.buffer[1:] - - q.Unlock() - - // Wait for the channel to be available to writings. - q.ch <- msg - } -} - -func (q *NonBlockingQueue) replaceBuffer() bool { - exp := float64(0) - if cap(q.buffer) > 0 { - exp = math.Floor(math.Log2(float64(cap(q.buffer))/q.cap)) + 1 - } - - if exp > q.limit { - return false - } - - newSize := int(math.Pow(2, exp) * q.cap) - - buffer := make([]router.Packet, len(q.buffer), newSize) - copy(buffer, q.buffer) - q.buffer = buffer - - return true -} diff --git a/dela/mino/minogrpc/session/queue_test.go b/dela/mino/minogrpc/session/queue_test.go deleted file mode 100644 index cc0e4e5..0000000 --- a/dela/mino/minogrpc/session/queue_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package session - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestNonBlockingQueue_Push(t *testing.T) { - queue := newNonBlockingQueue() - require.Len(t, queue.buffer, 0) - require.Equal(t, 0, cap(queue.buffer)) - - queue.cap = 2 - queue.limit = 3 // 2^3*2 = 16 is the maximum capacity - queue.running = true - - // First packet goes to the channel. - sendPkt(t, queue, 1) - require.Equal(t, 0, cap(queue.buffer)) - - sendPkt(t, queue, 1) - require.Equal(t, 2, cap(queue.buffer)) - - sendPkt(t, queue, 2) - require.Equal(t, 4, cap(queue.buffer)) - - sendPkt(t, queue, 2) - require.Equal(t, 8, cap(queue.buffer)) - - sendPkt(t, queue, 4) - require.Equal(t, 16, cap(queue.buffer)) - - sendPkt(t, queue, 7) - err := queue.Push(fakePkt{}) - require.EqualError(t, err, "queue is at maximum capacity") - - queue.running = true - go queue.pushAndWait() - - for i := 0; i < 17; i++ { - waitPkt(t, queue) - } - - require.Equal(t, 0, cap(queue.buffer)) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func sendPkt(t *testing.T, queue Queue, n int) { - for i := 0; i < n; i++ { - require.NoError(t, queue.Push(fakePkt{})) - } -} - -func waitPkt(t *testing.T, queue Queue) { - select { - case <-queue.Channel(): - case <-time.After(time.Second): - t.Fatal("packet expected") - } -} diff --git a/dela/mino/minogrpc/tokens/mod.go b/dela/mino/minogrpc/tokens/mod.go deleted file mode 100644 index bc5a203..0000000 --- a/dela/mino/minogrpc/tokens/mod.go +++ /dev/null @@ -1,67 +0,0 @@ -// Package tokens defines a token holder to generate and validate access tokens. -// -// The package also provides an in-memory implementation. -// -// Documentation Last Review: 07.10.2020 -// -package tokens - -import ( - "crypto/rand" - "encoding/base64" - "sync" - "time" -) - -// Holder is a store for access tokens. -type Holder interface { - // Generate creates a new token that is valid for the provided amount of - // time. - Generate(expiration time.Duration) string - - // Verify checks that the given token exists and is still valid. - Verify(token string) bool -} - -// InMemoryHolder stores access token in memory. -// -// - implements tokens.Holder -type InMemoryHolder struct { - sync.Mutex - tokens map[string]time.Time -} - -// NewInMemoryHolder creates a new empty token holder. -func NewInMemoryHolder() *InMemoryHolder { - return &InMemoryHolder{ - tokens: make(map[string]time.Time), - } -} - -// Generate implements tokens.Holder. It generates a token that will expire -// after a given amount of time. -func (holder *InMemoryHolder) Generate(expiration time.Duration) string { - buffer := make([]byte, 16) - rand.Read(buffer) - - str := base64.StdEncoding.EncodeToString(buffer) - - holder.Lock() - holder.tokens[str] = time.Now().Add(expiration) - holder.Unlock() - - return str -} - -// Verify implements tokens.Holder. It returns true if the token is valid. -func (holder *InMemoryHolder) Verify(token string) bool { - holder.Lock() - defer holder.Unlock() - - deadline, ok := holder.tokens[token] - if !ok { - return false - } - - return deadline.After(time.Now()) -} diff --git a/dela/mino/minogrpc/tokens/mod_test.go b/dela/mino/minogrpc/tokens/mod_test.go deleted file mode 100644 index 5998dc6..0000000 --- a/dela/mino/minogrpc/tokens/mod_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package tokens - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestInMemoryHolder_Generate(t *testing.T) { - holder := NewInMemoryHolder() - - token := holder.Generate(time.Minute) - require.NotEmpty(t, token) - require.Len(t, holder.tokens, 1) - require.True(t, time.Now().Before(holder.tokens[token])) - require.True(t, time.Now().Add(time.Minute+1).After(holder.tokens[token])) - - holder.Generate(time.Millisecond) - require.Len(t, holder.tokens, 2) -} - -func TestInMemoryHolder_Verify(t *testing.T) { - holder := NewInMemoryHolder() - - holder.tokens["abc"] = time.Now().Add(time.Minute) - holder.tokens["def"] = time.Now() - - require.True(t, holder.Verify("abc")) - require.False(t, holder.Verify("abcd")) - require.False(t, holder.Verify("def")) -} diff --git a/dela/mino/option.go b/dela/mino/option.go deleted file mode 100644 index cc98589..0000000 --- a/dela/mino/option.go +++ /dev/null @@ -1,97 +0,0 @@ -// -// Documentation Last Review: 07.10.2020 -// - -package mino - -import ( - "sort" -) - -// Filter is a set of parameters for the Players.Take function. -type Filter struct { - // Indices indicates the indexes of the elements that must be included. This - // list if updated based on the filter that we apply. For example, [0,3] - // tells that this filter keeps 2 elements from the underlying data - // structure we filter that are stored at indexes 0, 3. This list is always - // sorted and can be shifted in a circular way. - Indices []int -} - -// ApplyFilters applies the filters and return the result. -func ApplyFilters(filters []FilterUpdater) *Filter { - f := &Filter{ - Indices: []int{}, - } - - for _, filter := range filters { - filter(f) - } - - return f -} - -// FilterUpdater is a function to update the filters. -type FilterUpdater func(*Filter) - -// RotateFilter is a filter to rotate the indices. When n is above zero, it will -// rotate by n steps on the left and when n is below, it will do the same on the -// right. The behaviour is unknown if not used as the last filter as next -// updaters could change the order. -func RotateFilter(n int) FilterUpdater { - return func(filter *Filter) { - if len(filter.Indices) == 0 { - return - } - - n = n % len(filter.Indices) - if n < 0 { - n += len(filter.Indices) - } - - filter.Indices = append(filter.Indices[n:], filter.Indices[:n]...) - } -} - -// IndexFilter is a filter to include a given index. -func IndexFilter(index int) FilterUpdater { - return func(filters *Filter) { - arr := filters.Indices - i := sort.IntSlice(arr).Search(index) - // do nothing if the element is already there - if i < len(arr) && arr[i] == index { - return - } - - filters.Indices = append(arr, index) - sort.Ints(filters.Indices) - } -} - -// RangeFilter is a filter to include a range of indices. -func RangeFilter(start, end int) FilterUpdater { - return func(filters *Filter) { - arr := filters.Indices - queue := []int{} - - i := sort.IntSlice(arr).Search(start) - for k := start; k < end; k++ { - if i < len(arr) && arr[i] == k { - i++ - } else { - queue = append(queue, k) - } - } - - filters.Indices = append(arr, queue...) - sort.Ints(filters.Indices) - } -} - -// ListFilter is a filter to set the list of indices. It will override any index -// previously set. -func ListFilter(indices []int) FilterUpdater { - return func(filters *Filter) { - filters.Indices = indices - } -} diff --git a/dela/mino/option_test.go b/dela/mino/option_test.go deleted file mode 100644 index 0c1b63e..0000000 --- a/dela/mino/option_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package mino - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestFilter_ParseFilters(t *testing.T) { - filters := ApplyFilters([]FilterUpdater{IndexFilter(1)}) - require.Equal(t, []int{1}, filters.Indices) -} - -func TestFilter_RotateFilter(t *testing.T) { - filters := &Filter{Indices: []int{1, 2, 3, 4, 5}} - - RotateFilter(-2)(filters) - require.Equal(t, filters.Indices, []int{4, 5, 1, 2, 3}) - - RotateFilter(3)(filters) - require.Equal(t, filters.Indices, []int{2, 3, 4, 5, 1}) - - RotateFilter(10)(filters) - require.Equal(t, filters.Indices, []int{2, 3, 4, 5, 1}) - - RotateFilter(-7)(filters) - require.Equal(t, filters.Indices, []int{5, 1, 2, 3, 4}) - - filters = &Filter{} - RotateFilter(3)(filters) - require.Equal(t, filters.Indices, []int(nil)) -} - -func TestFilter_IndexFilter(t *testing.T) { - filters := &Filter{Indices: []int{}} - - IndexFilter(1)(filters) - require.Equal(t, filters.Indices, []int{1}) - - IndexFilter(2)(filters) - require.Equal(t, filters.Indices, []int{1, 2}) - - IndexFilter(0)(filters) - require.Equal(t, filters.Indices, []int{0, 1, 2}) - - IndexFilter(0)(filters) - IndexFilter(1)(filters) - IndexFilter(2)(filters) - require.Equal(t, filters.Indices, []int{0, 1, 2}) -} - -func TestFilter_RangeFilter(t *testing.T) { - filters := &Filter{Indices: []int{}} - - RangeFilter(2, 5)(filters) - require.Equal(t, []int{2, 3, 4}, filters.Indices) - - RangeFilter(0, 3)(filters) - require.Equal(t, []int{0, 1, 2, 3, 4}, filters.Indices) - - RangeFilter(3, 7)(filters) - require.Equal(t, []int{0, 1, 2, 3, 4, 5, 6}, filters.Indices) - - RangeFilter(2, 5)(filters) - require.Equal(t, []int{0, 1, 2, 3, 4, 5, 6}, filters.Indices) - - filters = &Filter{Indices: []int{0, 1, 4, 5}} - RangeFilter(1, 7)(filters) - require.Equal(t, []int{0, 1, 2, 3, 4, 5, 6}, filters.Indices) -} - -func TestFilter_ListFilter(t *testing.T) { - filters := &Filter{Indices: []int{1, 2, 3}} - - ListFilter([]int{3, 4, 7})(filters) - require.Equal(t, []int{3, 4, 7}, filters.Indices) -} diff --git a/dela/mino/proxy/http/controller/action.go b/dela/mino/proxy/http/controller/action.go deleted file mode 100644 index 45954bb..0000000 --- a/dela/mino/proxy/http/controller/action.go +++ /dev/null @@ -1,72 +0,0 @@ -package controller - -import ( - "fmt" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "go.dedis.ch/dela" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/mino/proxy" - "go.dedis.ch/dela/mino/proxy/http" - "golang.org/x/xerrors" -) - -var defaultRetry = 10 -var proxyFac func(string) proxy.Proxy = http.NewHTTP - -type startAction struct{} - -// Execute implements node.ActionTemplate. It starts and injects the proxy http -// server. -func (a startAction) Execute(ctx node.Context) error { - - addr := ctx.Flags.String("clientaddr") - - proxyhttp := proxyFac(addr) - - ctx.Injector.Inject(proxyhttp) - - go proxyhttp.Listen() - - for i := 0; i < defaultRetry && proxyhttp.GetAddr() == nil; i++ { - time.Sleep(time.Second) - } - - if proxyhttp.GetAddr() == nil { - return xerrors.Errorf("failed to start proxy server") - } - - // We assume the listen worked proprely, however it might not be the case. - // The log should inform the user about that. - fmt.Fprintf(ctx.Out, "started proxy server on %s", proxyhttp.GetAddr().String()) - - return nil -} - -type promAction struct{} - -// Execute implements node.ActionTemplate. It registers the Prometheus handler. -func (a promAction) Execute(ctx node.Context) error { - var proxyhttp proxy.Proxy - - err := ctx.Injector.Resolve(&proxyhttp) - if err != nil { - return xerrors.Errorf("failed to resolve the proxy: %v", err) - } - - path := ctx.Flags.String("path") - - for _, c := range dela.PromCollectors { - err = prometheus.DefaultRegisterer.Register(c) - if err != nil { - fmt.Fprintf(ctx.Out, "ERROR: failed to register: %v", err) - } - } - - proxyhttp.RegisterHandler(path, promhttp.Handler().ServeHTTP) - fmt.Fprintf(ctx.Out, "registered prometheus service on %q", path) - - return nil -} diff --git a/dela/mino/proxy/http/controller/action_test.go b/dela/mino/proxy/http/controller/action_test.go deleted file mode 100644 index c070e77..0000000 --- a/dela/mino/proxy/http/controller/action_test.go +++ /dev/null @@ -1,176 +0,0 @@ -package controller - -import ( - "bytes" - "fmt" - "net" - nhttp "net/http" - "testing" - - "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/require" - "go.dedis.ch/dela" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/mino/proxy" - "go.dedis.ch/dela/mino/proxy/http" -) - -func TestStartAction_Happy(t *testing.T) { - out := new(bytes.Buffer) - flags := make(node.FlagSet) - addr := "127.0.0.1:3000" - - flags["clientaddr"] = addr - - ctx := node.Context{ - Injector: node.NewInjector(), - Flags: flags, - Out: out, - } - - action := startAction{} - err := action.Execute(ctx) - require.NoError(t, err) - - require.Equal(t, "started proxy server on "+addr, out.String()) - - var proxy *http.HTTP - err = ctx.Injector.Resolve(&proxy) - require.NoError(t, err) - - require.Equal(t, addr, proxy.GetAddr().String()) - - proxy.Stop() -} - -func TestStartAction_Error(t *testing.T) { - defaultRetry = 1 - - oldFac := proxyFac - defer func() { - proxyFac = oldFac - }() - - proxyFac = newFake - - out := new(bytes.Buffer) - flags := make(node.FlagSet) - addr := "fake" - - flags["clientaddr"] = addr - - ctx := node.Context{ - Injector: node.NewInjector(), - Flags: flags, - Out: out, - } - - action := startAction{} - err := action.Execute(ctx) - require.Error(t, err, "failed to start proxy server") -} - -func TestPromAction_Happy(t *testing.T) { - out := new(bytes.Buffer) - flags := make(node.FlagSet) - path := "/fake" - - flags["path"] = path - - inj := node.NewInjector() - - proxy := fakeProxy{} - inj.Inject(&proxy) - - ctx := node.Context{ - Injector: inj, - Flags: flags, - Out: out, - } - - action := promAction{} - err := action.Execute(ctx) - require.NoError(t, err) - - require.Equal(t, fmt.Sprintf("registered prometheus service on %q", path), out.String()) - require.Len(t, proxy.handlers, 1) - require.Equal(t, proxy.handlers[0], path) -} - -func TestPromAction_ErrorInjector(t *testing.T) { - out := new(bytes.Buffer) - flags := make(node.FlagSet) - - inj := node.NewInjector() - - ctx := node.Context{ - Injector: inj, - Flags: flags, - Out: out, - } - - action := promAction{} - err := action.Execute(ctx) - require.EqualError(t, err, "failed to resolve the proxy: couldn't find dependency for 'proxy.Proxy'") -} - -func TestPromAction_ErrorCollector(t *testing.T) { - out := new(bytes.Buffer) - flags := make(node.FlagSet) - path := "/fake" - - flags["path"] = path - - inj := node.NewInjector() - - proxy := fakeProxy{} - inj.Inject(&proxy) - - ctx := node.Context{ - Injector: inj, - Flags: flags, - Out: out, - } - - dela.PromCollectors = []prometheus.Collector{fakeCollector{}, fakeCollector{}} - - action := promAction{} - err := action.Execute(ctx) - require.NoError(t, err) - - require.Equal(t, "ERROR: failed to register: duplicate metrics collector "+ - "registration attemptedregistered prometheus service on \"/fake\"", out.String()) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func newFake(addr string) proxy.Proxy { - return &fakeProxy{} -} - -type fakeProxy struct { - proxy.Proxy - - handlers []string -} - -func (fakeProxy) Listen() {} - -func (fakeProxy) Stop() {} - -func (fakeProxy) GetAddr() net.Addr { - return nil -} - -func (f *fakeProxy) RegisterHandler(path string, handler func(nhttp.ResponseWriter, *nhttp.Request)) { - f.handlers = append(f.handlers, path) -} - -type fakeCollector struct { - prometheus.Collector -} - -func (fakeCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- prometheus.NewDesc("fake", "help", nil, nil) -} diff --git a/dela/mino/proxy/http/controller/mod.go b/dela/mino/proxy/http/controller/mod.go deleted file mode 100644 index b193d41..0000000 --- a/dela/mino/proxy/http/controller/mod.go +++ /dev/null @@ -1,66 +0,0 @@ -package controller - -import ( - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/mino/proxy/http" -) - -const defaultAddr = "127.0.0.1:8080" - -const defaultProm = "/metrics" - -// NewController returns a new minimal initializer -func NewController() node.Initializer { - return minimal{} -} - -// minimal is an initializer with the minimum set of commands. Indeed it only -// creates and injects a new client proxy -// -// - implements node.Initializer -type minimal struct{} - -// Build implements node.Initializer. In this case we don't need any command. -func (m minimal) SetCommands(builder node.Builder) { - cmd := builder.SetCommand("proxy") - sub := cmd.SetSubCommand("start") - - sub.SetDescription("start the proxy http server") - sub.SetFlags(cli.StringFlag{ - Name: "clientaddr", - Required: false, - Usage: "the address of the http client", - Value: defaultAddr, - }) - sub.SetAction(builder.MakeAction(startAction{})) - - sub = cmd.SetSubCommand("prom") - - sub.SetDescription("registers the collectors and starts a prometheus handler. " + - "Will panic if the path is used more than once.") - sub.SetFlags(cli.StringFlag{ - Name: "path", - Required: false, - Usage: "the handler path", - Value: defaultProm, - }) - sub.SetAction(builder.MakeAction(promAction{})) -} - -// OnStart implements node.Initializer. It creates, starts, and registers a -// client proxy. -func (m minimal) OnStart(ctx cli.Flags, inj node.Injector) error { - return nil -} - -// OnStop implements node.Initializer. It stops the http server. -func (m minimal) OnStop(inj node.Injector) error { - var proxy *http.HTTP - err := inj.Resolve(&proxy) - if err == nil { - proxy.Stop() - } - - return nil -} diff --git a/dela/mino/proxy/http/controller/mod_test.go b/dela/mino/proxy/http/controller/mod_test.go deleted file mode 100644 index 0cbf8b0..0000000 --- a/dela/mino/proxy/http/controller/mod_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package controller - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/cli" - "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino/proxy/http" -) - -func TestMinimal_SetCommands(t *testing.T) { - minimal := NewController() - call := fake.Call{} - builder := &fakeBuilder{call: &call} - minimal.SetCommands(builder) - - require.Equal(t, call.Len(), 11) -} - -func TestMinimal_OnStart(t *testing.T) { - minimal := NewController() - - err := minimal.OnStart(nil, nil) - require.NoError(t, err) -} - -func TestMinimal_OnStop(t *testing.T) { - minimal := NewController() - - inj := node.NewInjector() - - proxy := http.NewHTTP("127.0.0.1:0") - go proxy.Listen() - - inj.Inject(proxy) - - err := minimal.OnStop(inj) - require.NoError(t, err) - - err = minimal.OnStop(node.NewInjector()) - require.NoError(t, err) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -// fakeCommandBuilder is a fake command builder -// -// - implements cli.CommandBuilder -type fakeCommandBuilder struct { - call *fake.Call -} - -func (b fakeCommandBuilder) SetSubCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return b -} - -func (b fakeCommandBuilder) SetDescription(value string) { - b.call.Add(value) -} - -func (b fakeCommandBuilder) SetFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeCommandBuilder) SetAction(a cli.Action) { - b.call.Add(a) -} - -// fakeBuilder is a fake builders -// -// - implements node.Builder -type fakeBuilder struct { - call *fake.Call -} - -func (b fakeBuilder) SetCommand(name string) cli.CommandBuilder { - b.call.Add(name) - return fakeCommandBuilder(b) -} - -func (b fakeBuilder) SetStartFlags(flags ...cli.Flag) { - b.call.Add(flags) -} - -func (b fakeBuilder) MakeAction(tmpl node.ActionTemplate) cli.Action { - b.call.Add(tmpl) - return nil -} diff --git a/dela/mino/proxy/http/mod.go b/dela/mino/proxy/http/mod.go deleted file mode 100644 index 404523f..0000000 --- a/dela/mino/proxy/http/mod.go +++ /dev/null @@ -1,180 +0,0 @@ -package http - -import ( - "context" - "fmt" - "net" - "net/http" - "os" - "time" - - "github.com/rs/zerolog" - "go.dedis.ch/dela" - "go.dedis.ch/dela/mino/proxy" -) - -type key int - -const ( - requestIDKey key = 0 -) - -var ( - // defaultLevel can be changed to set the desired level of the logger - defaultLevel = zerolog.ErrorLevel -) - -func init() { - setLogLevel() -} - -func setLogLevel() { - switch os.Getenv("PROXY_LOG") { - case "warn": - defaultLevel = zerolog.WarnLevel - case "no": - defaultLevel = zerolog.Disabled - case "info": - defaultLevel = zerolog.InfoLevel - } -} - -// NewHTTP creates a new proxy http -func NewHTTP(listenAddr string) proxy.Proxy { - logger := dela.Logger.With().Timestamp().Str("role", "http proxy").Logger(). - Level(defaultLevel) - - nextRequestID := func() string { - return fmt.Sprintf("%d", time.Now().UnixNano()) - } - - mux := http.NewServeMux() - - return &HTTP{ - mux: mux, - server: &http.Server{ - Addr: listenAddr, - Handler: tracing(nextRequestID)(logging(logger)(mux)), - }, - logger: logger, - listenAddr: listenAddr, - quit: make(chan struct{}), - } -} - -// HTTP defines a proxy http -// -// - implements proxy.Proxy -type HTTP struct { - mux *http.ServeMux - server *http.Server - logger zerolog.Logger - listenAddr string - quit chan struct{} - - ln net.Listener -} - -// Listen implements proxy.Proxy. This function can be called multiple times -// provided the server is not running, ie. Stop() has been called. -func (h *HTTP) Listen() { - h.logger.Info().Msg("Client server is starting...") - - done := make(chan struct{}) - - go func() { - <-h.quit - h.logger.Info().Msg("Server is shutting down...") - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - h.server.SetKeepAlivesEnabled(false) - err := h.server.Shutdown(ctx) - if err != nil { - h.logger.Fatal().Msgf("Could not gracefully shutdown the server: %v", err) - } - close(done) - }() - - addr := h.listenAddr - // if the address is empty, we use a random free port - if addr == "" { - addr = ":0" - } - - ln, err := net.Listen("tcp", addr) - if err != nil { - h.logger.Panic().Msgf("failed to create conn '%s': %v", addr, err) - return - } - - h.ln = ln - h.logger.Info().Msgf("Server is ready to handle requests at %s", ln.Addr()) - - err = h.server.Serve(ln) - if err != nil && err != http.ErrServerClosed { - h.logger.Fatal().Msgf("Could not listen on %s: %v", h.listenAddr, err) - } - - <-done - h.logger.Info().Msg("Server stopped") -} - -// Stop implements proxy.Proxy. It should be called only once in order to make a -// new Listen() successful. -func (h HTTP) Stop() { - // we don't close it so it can be called multiple times without harm - h.quit <- struct{}{} -} - -// GetAddr implements proxy.Proxy. -func (h HTTP) GetAddr() net.Addr { - if h.ln == nil { - return nil - } - - return h.ln.Addr() -} - -// RegisterHandler implements proxy.Proxy -func (h HTTP) RegisterHandler(path string, handler func(http.ResponseWriter, - *http.Request)) { - - h.mux.HandleFunc(path, handler) -} - -// logging is a utility function that logs the http server events -func logging(logger zerolog.Logger) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer func() { - requestID, ok := r.Context().Value(requestIDKey).(string) - if !ok { - requestID = "unknown" - } - logger.Info().Str("requestID", requestID). - Str("method", r.Method). - Str("url", r.URL.Path). - Str("remoteAddr", r.RemoteAddr). - Str("agent", r.UserAgent()).Msg("") - }() - next.ServeHTTP(w, r) - }) - } -} - -// tracing is a utility function that adds header tracing -func tracing(nextRequestID func() string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestID := r.Header.Get("X-Request-Id") - if requestID == "" { - requestID = nextRequestID() - } - ctx := context.WithValue(r.Context(), requestIDKey, requestID) - w.Header().Set("X-Request-Id", requestID) - next.ServeHTTP(w, r.WithContext(ctx)) - }) - } -} diff --git a/dela/mino/proxy/http/mod_test.go b/dela/mino/proxy/http/mod_test.go deleted file mode 100644 index 1f18e0e..0000000 --- a/dela/mino/proxy/http/mod_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package http - -import ( - "bytes" - "io" - "net/http" - "os" - "testing" - "time" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/require" -) - -func TestInit(t *testing.T) { - os.Setenv("PROXY_LOG", "warn") - setLogLevel() - require.Equal(t, defaultLevel, zerolog.WarnLevel) - - os.Setenv("PROXY_LOG", "no") - setLogLevel() - require.Equal(t, defaultLevel, zerolog.Disabled) - - os.Setenv("PROXY_LOG", "info") - setLogLevel() - require.Equal(t, defaultLevel, zerolog.InfoLevel) -} - -func TestHTTP_Listen(t *testing.T) { - proxy := NewHTTP("127.0.0.1:2010") - go proxy.Listen() - time.Sleep(200 * time.Millisecond) - - defer proxy.Stop() - - proxy.RegisterHandler("/fake", fakeHandler) - - res, err := http.Get("http://127.0.0.1:2010/fake") - require.NoError(t, err) - - output, err := io.ReadAll(res.Body) - require.NoError(t, err) - - require.Equal(t, "hello", string(output)) -} - -func TestHTPP_Listen_EmptyAddr(t *testing.T) { - // in this case it will use a random free port - proxy := NewHTTP("") - httpProxy := proxy.(*HTTP) - - require.Nil(t, httpProxy.ln) - - go proxy.Listen() - time.Sleep(200 * time.Millisecond) - - require.NotNil(t, httpProxy.ln) - - proxy.Stop() -} - -func TestHTPP_Listen_BadAddr(t *testing.T) { - proxy := NewHTTP("bad://xx") - httpProxy := proxy.(*HTTP) - - out := new(bytes.Buffer) - httpProxy.logger = zerolog.New(zerolog.ConsoleWriter{Out: out}) - - go func() { - defer func() { - res := recover() - require.Regexp(t, "^failed to create conn 'bad://xx':", res) - }() - - proxy.Listen() - }() - time.Sleep(400 * time.Millisecond) - - defer proxy.Stop() - - require.Regexp(t, "failed to create conn 'bad://xx':", out.String()) -} - -func TestGetAddr_Happy(t *testing.T) { - proxy := NewHTTP("127.0.0.1:2010") - go proxy.Listen() - time.Sleep(200 * time.Millisecond) - - defer proxy.Stop() - - addr := proxy.GetAddr() - require.Equal(t, "127.0.0.1:2010", addr.String()) -} - -func TestGetAddr_Nil(t *testing.T) { - proxy := NewHTTP("127.0.0.1:2010") - require.Nil(t, proxy.GetAddr()) -} - -func fakeHandler(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("hello")) -} diff --git a/dela/mino/proxy/mod.go b/dela/mino/proxy/mod.go deleted file mode 100644 index fc4d07d..0000000 --- a/dela/mino/proxy/mod.go +++ /dev/null @@ -1,25 +0,0 @@ -package proxy - -import ( - "net" - "net/http" -) - -// Proxy defines the primitives to implement an http client that handles -// client side requests -type Proxy interface { - // Listen starts the proxy server. This call is assumed to be blocking - Listen() - - // Stop stops the proxy server - Stop() - - // GetAddr returns the connection's address. This is useful in the case one - // use the default :0 port, which makes the system use a random free port. - // The returned value can be nil if the server is not listening and the - // connection hasn't been created. - GetAddr() net.Addr - - // RegisterHandler registers a new handler - RegisterHandler(path string, handler func(http.ResponseWriter, *http.Request)) -} diff --git a/dela/mino/response.go b/dela/mino/response.go deleted file mode 100644 index 38ed08b..0000000 --- a/dela/mino/response.go +++ /dev/null @@ -1,46 +0,0 @@ -// -// Documentation Last Review: 07.10.2020 -// - -package mino - -import "go.dedis.ch/dela/serde" - -// SimpleResponse is a response that can either return a message, or an error if -// the request has failed. -// -// - implements mino.Response -type simpleResponse struct { - from Address - msg serde.Message - err error -} - -// NewResponse creates a response that will return the message. -func NewResponse(from Address, msg serde.Message) Response { - return simpleResponse{ - from: from, - msg: msg, - } -} - -// NewResponseWithError creates a response that will return an error instead of -// a message. -func NewResponseWithError(from Address, err error) Response { - return simpleResponse{ - from: from, - err: err, - } -} - -// GetFrom implements mino.Response. It returns the address the response -// originates from. -func (resp simpleResponse) GetFrom() Address { - return resp.from -} - -// GetMessageOrError implements mino.Response. It returns either a message or an -// error. -func (resp simpleResponse) GetMessageOrError() (serde.Message, error) { - return resp.msg, resp.err -} diff --git a/dela/mino/response_test.go b/dela/mino/response_test.go deleted file mode 100644 index fcf1d03..0000000 --- a/dela/mino/response_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package mino - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func TestResponse_GetFrom(t *testing.T) { - resp := NewResponse(fakeAddr{}, fakeMsg{}) - - require.Equal(t, fakeAddr{}, resp.GetFrom()) -} - -func TestResponse_GetMessageOrError(t *testing.T) { - resp := NewResponse(fakeAddr{}, fakeMsg{}) - msg, err := resp.GetMessageOrError() - require.NoError(t, err) - require.Equal(t, fakeMsg{}, msg) - - resp = NewResponseWithError(fakeAddr{}, xerrors.New("oops")) - _, err = resp.GetMessageOrError() - require.EqualError(t, err, "oops") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type fakeMsg struct { - serde.Message -} diff --git a/dela/mino/router/router.go b/dela/mino/router/router.go deleted file mode 100644 index ccfc974..0000000 --- a/dela/mino/router/router.go +++ /dev/null @@ -1,123 +0,0 @@ -// Package router defines the primitives to route a packet among a set of -// participants. -// -// Documentation Last Review: 06.10.2020 -package router - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/serde" -) - -// Packet is the type of message processed by the router. It contains -// information that will allow the message to be routed. -type Packet interface { - serde.Message - - // GetSource returns the source address of the message. - GetSource() mino.Address - - // GetDestination returns the destination address of the message. - GetDestination() []mino.Address - - // GetMessage returns the message to be transmitted to the application when - // the source address is the current node. Message is only deserialized when - // needed. - GetMessage() []byte - - // Slice removes the address from the destination if found and return a new - // packet with the addr as the destination. If not found return nil. - Slice(addr mino.Address) Packet -} - -// PacketFactory describes the primitives to deserialize a packet -type PacketFactory interface { - serde.Factory - - // PacketOf returns the packet for the data if appropriate, otherwise an - // error. - PacketOf(serde.Context, []byte) (Packet, error) -} - -// Handshake is the message sent to the relay as the very first message. It is a -// way to provide parameters to the relays. -type Handshake interface { - serde.Message -} - -// HandshakeFactory is the factory to serialize and deserialize handshakes. -type HandshakeFactory interface { - serde.Factory - - // HandshakeOf returns the handshake of the data if appropriate, otherwise - // an error. - HandshakeOf(serde.Context, []byte) (Handshake, error) -} - -// Router is the interface of the routing service. It provides the primitives to -// route a packet among a set of participants. The orchestrator address (if any) -// is not handled by the router. For that matter, the Packet.Slice function can -// be used to handle special cases with that address. -type Router interface { - // GetPacketFactory returns the packet factory. - GetPacketFactory() PacketFactory - - // GetHandshakeFactory returns the handshake factory. - GetHandshakeFactory() HandshakeFactory - - // New creates a new routing table that will forward packets to the players. - New(players mino.Players, me mino.Address) (RoutingTable, error) - - // GenerateTableFrom returns the routing table associated to the handshake. - // A node should be able to route any incoming packet after receiving one. - GenerateTableFrom(Handshake) (RoutingTable, error) -} - -// Routes is a set of relay addresses where to send the packet. Key is the relay -// address. It can be nil, and in that case minogrpc sends the message to its -// parent relay. This is the case when the destination address is the -// orchestrator address. -type Routes map[mino.Address]Packet - -// Void is the structure that describes a void route. -type Void struct { - Error error -} - -// Voids is a set of addresses that cannot be addressed by the routing table. -type Voids map[mino.Address]Void - -// RoutingTable is built by the router and provides information about the -// routing of the packets. -type RoutingTable interface { - // Make creates and returns a packet with the given source, destination and - // payload. - Make(src mino.Address, to []mino.Address, msg []byte) Packet - - // PrepareHandshakeFor is called before a relay is opened to generate the - // handshake that will be sent. - PrepareHandshakeFor(mino.Address) Handshake - - // Forward takes the destination address, unmarshal the packet, and, based - // on its content, return a map of packets, where each element of the map - // represents the destination as the key, and the packet to send as the - // value. The simplest forward would be, if the destination is A,B,C, to - // create a map with 3 entries: - // - // {A: packet{to: [A]}, B: packet{to: [B]}, C: packet{to: [C]}}. - // - // For a tree routing, the first message will send a packet with all the - // recipients to the root address (A in the following), like: - // - // {A: packet{to: [A, B, C]}} - // - // The second return of the function contains the list of addresses that are - // known the be broken. - // - Forward(packet Packet) (Routes, Voids) - - // OnFailure is used to announce that a packet failed to be routed. It - // allows the router to find a different route. Forward can be called - // afterwards to find an alternative route. - OnFailure(to mino.Address) error -} diff --git a/dela/mino/router/tree/example_test.go b/dela/mino/router/tree/example_test.go deleted file mode 100644 index 03b81da..0000000 --- a/dela/mino/router/tree/example_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package tree - -import ( - "fmt" - - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/minogrpc/session" -) - -func ExampleRouter_New() { - router := NewRouter(session.AddressFactory{}) - - addrA := session.NewAddress("127.0.0.1:2000") - addrB := session.NewAddress("127.0.0.1:3000") - - players := mino.NewAddresses(addrA, addrB) - - table, err := router.New(players, addrA) - if err != nil { - panic("routing table failed: " + err.Error()) - } - - routes, voids := table.Forward(table.Make(addrA, []mino.Address{addrB}, []byte{})) - fmt.Println(voids) - - for to := range routes { - fmt.Println(to) - } - - // Output: map[] - // 127.0.0.1:3000 -} - -func ExampleTable_PrepareHandshakeFor() { - routerA := NewRouter(session.AddressFactory{}) - - addrA := session.NewAddress("127.0.0.1:2000") - addrB := session.NewAddress("127.0.0.1:3000") - - players := mino.NewAddresses(addrA, addrB) - - table, err := routerA.New(players, addrA) - if err != nil { - panic("routing table failed: " + err.Error()) - } - - handshake := table.PrepareHandshakeFor(addrB) - - // Send the handshake to the address B.. - - routerB := NewRouter(session.AddressFactory{}) - - tableB, err := routerB.GenerateTableFrom(handshake) - if err != nil { - panic("malformed handshake: " + err.Error()) - } - - packet := tableB.Make(addrB, []mino.Address{addrA}, []byte{}) - - fmt.Println(packet.GetSource()) - fmt.Println(packet.GetDestination()) - - // Output: 127.0.0.1:3000 - // [127.0.0.1:2000] -} diff --git a/dela/mino/router/tree/json/json.go b/dela/mino/router/tree/json/json.go deleted file mode 100644 index 822d429..0000000 --- a/dela/mino/router/tree/json/json.go +++ /dev/null @@ -1,158 +0,0 @@ -package json - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router/tree/types" - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -func init() { - types.RegisterPacketFormat(serde.FormatJSON, packetFormat{}) - types.RegisterHandshakeFormat(serde.FormatJSON, hsFormat{}) -} - -// PacketJSON describes a JSON formatted packet -type PacketJSON struct { - Source []byte - Dest [][]byte - Message []byte -} - -// HandshakeJSON is the JSON message for the handshake. -type HandshakeJSON struct { - Height int - Addresses [][]byte -} - -// packetFormat is the engine to encode and decode Packets in JSON format. -// -// - implements serde.FormatEngine -type packetFormat struct{} - -// Encode implements serde.FormatEngine. It returns the serialized data for the -// packet if appropriate, otherwise it returns an error. -func (f packetFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - packet, ok := msg.(*types.Packet) - if !ok { - return nil, xerrors.Errorf("unsupported message '%T'", msg) - } - - source, err := packet.GetSource().MarshalText() - if err != nil { - return nil, xerrors.Errorf("failed to marshal source addr: %v", err) - } - - dest := make([][]byte, len(packet.GetDestination())) - - for i, addr := range packet.GetDestination() { - addBuf, err := addr.MarshalText() - if err != nil { - return nil, xerrors.Errorf("failed to marshal dest addr: %v", err) - } - - dest[i] = addBuf - } - - p := PacketJSON{ - Source: source, - Dest: dest, - Message: packet.GetMessage(), - } - - data, err := ctx.Marshal(p) - if err != nil { - return nil, xerrors.Errorf("failed to marshal packet: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the packet if appropriate, -// otherwise it returns an error. -func (f packetFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - p := PacketJSON{} - - err := ctx.Unmarshal(data, &p) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal packet: %v", err) - } - - factory := ctx.GetFactory(types.AddrKey{}) - - fac, ok := factory.(mino.AddressFactory) - if !ok { - return nil, xerrors.Errorf("invalid address factory '%T'", factory) - } - - source := fac.FromText(p.Source) - dest := make([]mino.Address, len(p.Dest)) - - for i, buf := range p.Dest { - dest[i] = fac.FromText(buf) - } - - packet := types.NewPacket(source, p.Message, dest...) - - return packet, nil -} - -// HandshakeFormat is the format engine to encode and decode handshake messages. -// -// - implements serde.FormatEngine -type hsFormat struct{} - -// Encode implements serde.FormatEngine. It returns the serialized data for the -// handshake if appropriate, otherwise it returns an error. -func (hsFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - hs, ok := msg.(types.Handshake) - if !ok { - return nil, xerrors.Errorf("unsupported message '%T'", msg) - } - - addrs := make([][]byte, len(hs.GetAddresses())) - for i, addr := range hs.GetAddresses() { - raw, err := addr.MarshalText() - if err != nil { - return nil, xerrors.Errorf("failed to marshal address: %v", err) - } - - addrs[i] = raw - } - - m := HandshakeJSON{ - Height: hs.GetHeight(), - Addresses: addrs, - } - - data, err := ctx.Marshal(m) - if err != nil { - return nil, xerrors.Errorf("failed to marshal handshake: %v", err) - } - - return data, nil -} - -// Decode implements serde.FormatEngine. It populates the handshake if -// appropriate, otherwise it returns an error. -func (hsFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - m := HandshakeJSON{} - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, xerrors.Errorf("failed to unmarshal: %v", err) - } - - fac := ctx.GetFactory(types.AddrKey{}) - - factory, ok := fac.(mino.AddressFactory) - if !ok { - return nil, xerrors.Errorf("invalid address factory '%T'", fac) - } - - addrs := make([]mino.Address, len(m.Addresses)) - for i, raw := range m.Addresses { - addrs[i] = factory.FromText(raw) - } - - return types.NewHandshake(m.Height, addrs...), nil -} diff --git a/dela/mino/router/tree/json/json_test.go b/dela/mino/router/tree/json/json_test.go deleted file mode 100644 index d5a5583..0000000 --- a/dela/mino/router/tree/json/json_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino/router/tree/types" - "go.dedis.ch/dela/serde" -) - -func TestPacketFormat_Encode(t *testing.T) { - fmt := packetFormat{} - - ctx := fake.NewContext() - pkt := types.NewPacket(fake.NewAddress(0), []byte("data"), fake.NewAddress(1)) - - data, err := fmt.Encode(ctx, pkt) - require.NoError(t, err) - require.Equal(t, `{"Source":"AAAAAA==","Dest":["AQAAAA=="],"Message":"ZGF0YQ=="}`, string(data)) - - _, err = fmt.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message 'fake.Message'") - - _, err = fmt.Encode(ctx, types.NewPacket(fake.NewBadAddress(), nil)) - require.EqualError(t, err, fake.Err("failed to marshal source addr")) - - _, err = fmt.Encode(ctx, types.NewPacket(fake.NewAddress(0), nil, fake.NewBadAddress())) - require.EqualError(t, err, fake.Err("failed to marshal dest addr")) - - _, err = fmt.Encode(fake.NewBadContext(), pkt) - require.EqualError(t, err, fake.Err("failed to marshal packet")) -} - -func TestPacketFormat_Decode(t *testing.T) { - fmt := packetFormat{} - - pkt := types.NewPacket(fake.NewAddress(0), []byte{}, fake.NewAddress(1)) - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.AddrKey{}, fake.AddressFactory{}) - - msg, err := fmt.Decode(ctx, []byte(`{"Message":"","Dest":["AQAAAA=="]}`)) - require.NoError(t, err) - require.Equal(t, pkt, msg) - - _, err = fmt.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal packet")) - - badCtx := serde.WithFactory(ctx, types.AddrKey{}, nil) - _, err = fmt.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "invalid address factory ''") -} - -func TestHandshakeFormat_Encode(t *testing.T) { - fmt := hsFormat{} - - ctx := fake.NewContext() - hs := types.NewHandshake(5, fake.NewAddress(1)) - - data, err := fmt.Encode(ctx, hs) - require.NoError(t, err) - require.Equal(t, `{"Height":5,"Addresses":["AQAAAA=="]}`, string(data)) - - _, err = fmt.Encode(ctx, fake.Message{}) - require.EqualError(t, err, "unsupported message 'fake.Message'") - - _, err = fmt.Encode(ctx, types.NewHandshake(1, fake.NewBadAddress())) - require.EqualError(t, err, fake.Err("failed to marshal address")) - - _, err = fmt.Encode(fake.NewBadContext(), hs) - require.EqualError(t, err, fake.Err("failed to marshal handshake")) -} - -func TestHandshakeFormat_Decode(t *testing.T) { - fmt := hsFormat{} - - ctx := fake.NewContext() - ctx = serde.WithFactory(ctx, types.AddrKey{}, fake.AddressFactory{}) - - msg, err := fmt.Decode(ctx, []byte(`{"Addresses":["AQAAAA=="]}`)) - require.NoError(t, err) - require.Equal(t, types.NewHandshake(0, fake.NewAddress(1)), msg) - - _, err = fmt.Decode(fake.NewBadContext(), []byte(`{}`)) - require.EqualError(t, err, fake.Err("failed to unmarshal")) - - badCtx := serde.WithFactory(ctx, types.AddrKey{}, nil) - _, err = fmt.Decode(badCtx, []byte(`{}`)) - require.EqualError(t, err, "invalid address factory ''") -} diff --git a/dela/mino/router/tree/mod.go b/dela/mino/router/tree/mod.go deleted file mode 100644 index 334df7c..0000000 --- a/dela/mino/router/tree/mod.go +++ /dev/null @@ -1,162 +0,0 @@ -// Package tree is an implementation of a tree-based routing algorithm. -// -// The router creates a routing table for each protocol that will progressively -// build a tree. The tree will adapt to unresponsive participants but it is not -// resilient to faults happening after the table has been generated. -// -// The resulting tree will be balanced to limit the number of hops you need to -// send a message, while also trying to limit the number of connections per -// node. The routes are built upon requests so that the interior nodes of the -// tree are the first participants to be contacted. -// -// Documentation Last Review: 06.10.2020 -package tree - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/mino/router/tree/types" - "golang.org/x/xerrors" -) - -const defaultHeight = 3 - -// Router is an implementation of a router producing routes with an algorithm -// based on tree. -// -// - implements router.Router -type Router struct { - maxHeight int - packetFac router.PacketFactory - hsFac router.HandshakeFactory -} - -// RouterOption is the signature of the option constructor -type RouterOption func(*Router) - -// WithHeight allows to specify the maximum height of the tree -// when calling NewRouter -func WithHeight(maxHeight int) RouterOption { - return func(r *Router) { - r.maxHeight = maxHeight - } -} - -// NewRouter returns a new router -func NewRouter(f mino.AddressFactory, options ...RouterOption) Router { - fac := types.NewPacketFactory(f) - hsFac := types.NewHandshakeFactory(f) - - r := Router{ - maxHeight: defaultHeight, - packetFac: fac, - hsFac: hsFac, - } - - // Loop through each option - for _, opt := range options { - opt(&r) - } - return r -} - -// GetPacketFactory implements router.Router. It returns the packet factory. -func (r Router) GetPacketFactory() router.PacketFactory { - return r.packetFac -} - -// GetHandshakeFactory implements router.Router. It returns the handshake -// factory. -func (r Router) GetHandshakeFactory() router.HandshakeFactory { - return r.hsFac -} - -// New implements router.Router. It creates the routing table for the node that -// is booting the protocol. This node will be the root of the tree. -func (r Router) New(players mino.Players, _ mino.Address) (router.RoutingTable, error) { - addrs := make([]mino.Address, 0, players.Len()) - iter := players.AddressIterator() - for iter.HasNext() { - addrs = append(addrs, iter.GetNext()) - } - - return NewTable(r.maxHeight, addrs), nil -} - -// GenerateTableFrom implements router.Router. It creates the routing table -// associated with the handshake that can contain some parameter. -func (r Router) GenerateTableFrom(h router.Handshake) (router.RoutingTable, error) { - treeH := h.(types.Handshake) - - return NewTable(treeH.GetHeight(), treeH.GetAddresses()), nil -} - -// Table is a routing table that is using a tree structure to communicate -// between the nodes. -// -// - implements router.RoutingTable -type Table struct { - tree Tree -} - -// NewTable creates a new routing table for the given addresses. -func NewTable(height int, expected []mino.Address) Table { - return Table{ - tree: NewTree(height, expected), - } -} - -// Make implements router.RoutingTable. It creates a packet with the source -// address, the destination addresses and the payload. -func (t Table) Make(src mino.Address, to []mino.Address, msg []byte) router.Packet { - return types.NewPacket(src, msg, to...) -} - -// PrepareHandshakeFor implements router.RoutingTable. It creates a handshake -// message that should be sent to the distant peer when opening a relay to it. -// The peer will then generate its own routing table based on the handshake. -func (t Table) PrepareHandshakeFor(to mino.Address) router.Handshake { - newHeight := t.tree.GetMaxHeight() - 1 - - return types.NewHandshake(newHeight, t.tree.GetChildren(to)...) -} - -// Forward implements router.RoutingTable. It takes a packet and split it into -// the different routes it should be forwarded to. -func (t Table) Forward(packet router.Packet) (router.Routes, router.Voids) { - routes := make(router.Routes) - voids := make(router.Voids) - - for _, dest := range packet.GetDestination() { - gateway, err := t.tree.GetRoute(dest) - if err != nil { - voids[dest] = router.Void{Error: err} - continue - } - - p, ok := routes[gateway] - if !ok { - p = types.NewPacket(packet.GetSource(), packet.GetMessage()) - routes[gateway] = p - } - - p.(*types.Packet).Add(dest) - } - - return routes, voids -} - -// OnFailure implements router.Router. The tree will try to adapt itself to -// reach the address, but it will return an error if the address is a direct -// branch of the tree. -func (t Table) OnFailure(to mino.Address) error { - if t.tree.GetMaxHeight() <= 1 { - // When the node does only have leafs, it will simply return an error to - // announce the address as unreachable. - return xerrors.New("address is unreachable") - } - - t.tree.Remove(to) - - return nil -} diff --git a/dela/mino/router/tree/mod_test.go b/dela/mino/router/tree/mod_test.go deleted file mode 100644 index d56aac4..0000000 --- a/dela/mino/router/tree/mod_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package tree - -import ( - "testing" - "testing/quick" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" - minoRouter "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/mino/router/tree/types" -) - -func TestRouter_GetPacketFactory(t *testing.T) { - router := NewRouter(fake.AddressFactory{}) - - require.NotNil(t, router.GetPacketFactory()) -} - -func TestRouter_GetHandshakeFactory(t *testing.T) { - router := NewRouter(fake.AddressFactory{}) - - require.NotNil(t, router.GetHandshakeFactory()) -} - -func TestRouter_New(t *testing.T) { - f := func(height, nbNodes uint8) bool { - h := int(height) - n := int(nbNodes) - - router := NewRouter(fake.AddressFactory{}) - router.maxHeight = h - - fakeAddrs := makeAddrs(n) - - var table minoRouter.RoutingTable - var err error - - if n > 0 { - table, err = router.New(mino.NewAddresses(fakeAddrs...), fakeAddrs[0]) - } else { - table, err = router.New(mino.NewAddresses(fakeAddrs...), nil) - } - require.NoError(t, err) - - return table.(Table).tree.GetMaxHeight() == h - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestRouter_OptionWithHeight(t *testing.T) { - f := func(height, nbNodes uint8) bool { - h := int(height) - n := int(nbNodes) - - router := NewRouter(fake.AddressFactory{}, WithHeight(h)) - - fakeAddrs := makeAddrs(n) - - var table minoRouter.RoutingTable - var err error - - if n > 0 { - table, err = router.New(mino.NewAddresses(fakeAddrs...), fakeAddrs[0]) - } else { - table, err = router.New(mino.NewAddresses(fakeAddrs...), nil) - } - require.NoError(t, err) - - return table.(Table).tree.GetMaxHeight() == h - } - - err := quick.Check(f, nil) - require.NoError(t, err) -} - -func TestRouter_GenerateTableFrom(t *testing.T) { - router := NewRouter(fake.AddressFactory{}) - - hs := types.NewHandshake(3, makeAddrs(5)...) - table, err := router.GenerateTableFrom(hs) - require.NoError(t, err) - require.NotNil(t, table) -} - -func TestTable_Make(t *testing.T) { - table := NewTable(3, makeAddrs(5)) - - pkt := table.Make(fake.NewAddress(0), makeAddrs(3), []byte{1, 2, 3}) - require.NotNil(t, pkt) - require.Equal(t, fake.NewAddress(0), pkt.GetSource()) - require.Len(t, pkt.GetDestination(), 3) - require.Equal(t, []byte{1, 2, 3}, pkt.GetMessage()) -} - -func TestTable_PrepareHandshakeFor(t *testing.T) { - table := NewTable(3, makeAddrs(5)) - - hs := table.PrepareHandshakeFor(fake.NewAddress(1)) - require.Equal(t, 2, hs.(types.Handshake).GetHeight()) -} - -func TestTable_Forward(t *testing.T) { - table := NewTable(3, makeAddrs(20)) - - pkt := types.NewPacket(fake.NewAddress(0), []byte{1, 2, 3}, makeAddrs(20)...) - - routes, voids := table.Forward(pkt) - require.Empty(t, voids) - require.Len(t, routes, 5) - - table.tree.(*dynTree).offline[fake.NewAddress(1)] = struct{}{} - routes, voids = table.Forward(pkt) - require.Len(t, voids, 1) - require.Len(t, routes, 5) -} - -func TestTable_OnFailure(t *testing.T) { - table := NewTable(1, makeAddrs(5)) - err := table.OnFailure(fake.NewAddress(3)) - require.EqualError(t, err, "address is unreachable") - - table = NewTable(3, makeAddrs(20)) - err = table.OnFailure(fake.NewAddress(12)) - require.NoError(t, err) - require.Len(t, table.tree.(*dynTree).offline, 1) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeAddrs(n int) []mino.Address { - addrs := make([]mino.Address, n) - for i := range addrs { - addrs[i] = fake.NewAddress(i) - } - - return addrs -} diff --git a/dela/mino/router/tree/tree.go b/dela/mino/router/tree/tree.go deleted file mode 100644 index db34ec6..0000000 --- a/dela/mino/router/tree/tree.go +++ /dev/null @@ -1,215 +0,0 @@ -// -// Documentation Last Review: 06.10.2020 -// - -package tree - -import ( - "math" - "sync" - - "go.dedis.ch/dela/mino" - "golang.org/x/xerrors" -) - -const minNumChildren = float64(5) - -// Tree is the interface used by the router to determine the routes. -type Tree interface { - // GetMaxHeight returns the maximum height for this tree. - GetMaxHeight() int - - // GetRoute returns the address to route the provided target. It will return - // a nil value if no route is found in this tree. - GetRoute(to mino.Address) (mino.Address, error) - - // GetChildren returns the children of a direct branch of this tree. It - // represents the list of routable addresses for a given branch. - GetChildren(to mino.Address) []mino.Address - - // Remove marks the address as unreachable and fixes the tree if - // appropriate. - Remove(addr mino.Address) -} - -// AddrSet is a set of unique addresses. -type AddrSet map[mino.Address]struct{} - -// Search returns true if the provided address is in the set. -func (set AddrSet) Search(to mino.Address) bool { - _, found := set[to] - return found -} - -// GetRandom returns a member of the set randomly. -func (set AddrSet) GetRandom() mino.Address { - for addr := range set { - return addr - } - - return nil -} - -// Branches is a partial representation of a tree which shows only the direct -// branches of the node and children's branch, but unstructured. -type Branches map[mino.Address]AddrSet - -// Search returns the address of the direct branch that leads to the provided -// address if any, otherwise it will return nil. -func (c Branches) Search(to mino.Address) mino.Address { - for gateway, set := range c { - if gateway.Equal(to) || set.Search(to) { - return gateway - } - } - - return nil -} - -// DynamicTree is a tree that will optimistically build the tree according to -// route requests. It will adjust the branches and their children when -// necessary. -// -// The tree is built according to the theory of m-ary trees to find the minimum -// value of m in order to have a fixed maximum depth. The structure itself is -// not the complete tree but only the first level of branches with their -// unstructured children. Each router is responsible to build its own level. -// -// - implements tree.Tree -type dynTree struct { - sync.Mutex - height int - m int - branches Branches - expected AddrSet - offline AddrSet -} - -// NewTree creates a new empty tree that will spawn to a maximum depth and route -// only the given addresses. -func NewTree(height int, addrs []mino.Address) Tree { - N := float64(len(addrs)) - // m finds the minimum number of branches needed to not go deeper than the - // given height. - m := math.Exp(math.Log(N) / float64(height)) - - // ... but we use a minimal value to avoid unnecessary deep trees. - m = math.Max(m, minNumChildren) - - expected := make(AddrSet) - for _, addr := range addrs { - expected[addr] = struct{}{} - } - - return &dynTree{ - height: height, - m: int(m), - branches: make(Branches), - expected: expected, - offline: make(AddrSet), - } -} - -// GetMaxHeight implements tree.Tree. It returns the maximum depth for this -// tree. -func (t *dynTree) GetMaxHeight() int { - return t.height -} - -// GetRoute implements tree.Tree. It returns the address to route the target, or -// nil if no route is found. -func (t *dynTree) GetRoute(to mino.Address) (mino.Address, error) { - t.Lock() - defer t.Unlock() - - if t.offline.Search(to) { - return nil, xerrors.Errorf("address is unreachable") - } - - gateway := t.branches.Search(to) - if gateway != nil { - return gateway, nil - } - - if t.expected.Search(to) { - // Add the address as a branch of the tree and optimistically attribute - // it some children. - t.updateTree(to) - - return to, nil - } - - return nil, nil -} - -// GetChildren implements tree.Tree. It returns the children of a branch. -func (t *dynTree) GetChildren(to mino.Address) []mino.Address { - t.Lock() - defer t.Unlock() - - set := t.branches[to] - addrs := make([]mino.Address, 0, len(set)) - - for addr := range set { - addrs = append(addrs, addr) - } - - return addrs -} - -// Remove implements tree.Tree. It marks the address as unreachable if it is an -// known one. It also makes sure the address is not a branch, otherwise it uses -// one of the child to route the packets instead. -func (t *dynTree) Remove(addr mino.Address) { - t.Lock() - defer t.Unlock() - - if t.expected.Search(addr) || t.branches.Search(addr) != nil { - // If the address is supposed to be routed by the tree, it ends up - // in the list of unreachable addresses. - t.offline[addr] = struct{}{} - } - - // It is also necessary to make sure the address is not a branch, otherwise - // it needs to be replaced. - branch, found := t.branches[addr] - if !found { - return - } - - delete(t.branches, addr) - - // Pick a random child and grant it the parent role. - newParent := branch.GetRandom() - if newParent == nil { - return - } - - delete(branch, newParent) - t.branches[newParent] = branch -} - -func (t *dynTree) updateTree(to mino.Address) { - // Remove the direct child from the list of waiting peers. - delete(t.expected, to) - - // Find how many children this direct child should have. - remain := t.m - len(t.branches) - num := math.Ceil(float64(len(t.expected)-remain+1) / float64(remain)) - - set := make(AddrSet) - for addr := range t.expected { - if len(set) >= int(num) { - break - } - - set[addr] = struct{}{} - delete(t.expected, addr) - } - - // Optimistic creation of a branch for this node. It assumes that none of - // the thoses addresses will come before the branches are created but this - // is not true. The tree will correct itself if that happens. - // TODO: auto-update by updates in types.Packet - t.branches[to] = set -} diff --git a/dela/mino/router/tree/tree_test.go b/dela/mino/router/tree/tree_test.go deleted file mode 100644 index 8972592..0000000 --- a/dela/mino/router/tree/tree_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package tree - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" -) - -func TestAddrSet_Search(t *testing.T) { - set := AddrSet{ - fake.NewAddress(0): {}, - fake.NewAddress(1): {}, - fake.NewAddress(2): {}, - } - - require.True(t, set.Search(fake.NewAddress(0))) - require.True(t, set.Search(fake.NewAddress(2))) - require.False(t, set.Search(fake.NewAddress(5))) -} - -func TestBranches_Search(t *testing.T) { - branches := Branches{ - fake.NewAddress(0): AddrSet{fake.NewAddress(1): {}}, - fake.NewAddress(3): AddrSet{fake.NewAddress(2): {}}, - } - - require.Equal(t, fake.NewAddress(0), branches.Search(fake.NewAddress(0))) - require.Equal(t, fake.NewAddress(0), branches.Search(fake.NewAddress(1))) - require.Equal(t, fake.NewAddress(3), branches.Search(fake.NewAddress(3))) - require.Equal(t, fake.NewAddress(3), branches.Search(fake.NewAddress(2))) - require.Nil(t, branches.Search(fake.NewAddress(5))) -} - -func TestDynTree_GetMaxHeight(t *testing.T) { - tree := NewTree(3, makeAddrs(4)) - - require.Equal(t, 3, tree.GetMaxHeight()) -} - -func TestDynTree_GetRoute(t *testing.T) { - addrs := makeAddrs(200) - tree := NewTree(5, addrs) - - for _, addr := range addrs { - gateway, err := tree.GetRoute(fake.NewAddress(500)) - require.NoError(t, err) - require.Nil(t, gateway) - - gateway, err = tree.GetRoute(addr) - require.NoError(t, err) - require.NotNil(t, gateway) - } -} - -func TestDynTree_GetChildren(t *testing.T) { - tree := NewTree(3, makeAddrs(20)) - require.Empty(t, tree.GetChildren(fake.NewAddress(0))) - - tree.GetRoute(fake.NewAddress(0)) - require.Len(t, tree.GetChildren(fake.NewAddress(0)), 3) -} - -func TestDynTree_Remove(t *testing.T) { - tree := NewTree(3, makeAddrs(20)).(*dynTree) - tree.GetRoute(fake.NewAddress(1)) - - tree.Remove(fake.NewAddress(1)) - require.Len(t, tree.offline, 1) - require.Len(t, tree.branches, 1) - - tree = NewTree(3, makeAddrs(1)).(*dynTree) - tree.GetRoute(fake.NewAddress(0)) - - tree.Remove(fake.NewAddress(0)) - require.Len(t, tree.offline, 1) - require.Len(t, tree.branches, 0) -} diff --git a/dela/mino/router/tree/types/handshake.go b/dela/mino/router/tree/types/handshake.go deleted file mode 100644 index fe785a3..0000000 --- a/dela/mino/router/tree/types/handshake.go +++ /dev/null @@ -1,97 +0,0 @@ -// The file contains the implementation of a handshake message that is sent to -// create the routing table. -// -// Documentation Last Review: 06.10.2020 -// - -package types - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var hsFormats = registry.NewSimpleRegistry() - -// Handshake is a message to send the initial parameters of a routing table. -// -// - implements serde.Message -type Handshake struct { - height int - expected []mino.Address -} - -// NewHandshake returns a new handshake message. -func NewHandshake(height int, expected ...mino.Address) Handshake { - return Handshake{ - height: height, - expected: expected, - } -} - -// GetHeight returns the maximum height of the tree. -func (h Handshake) GetHeight() int { - return h.height -} - -// GetAddresses returns the list of addresses to route. -func (h Handshake) GetAddresses() []mino.Address { - return h.expected -} - -// Serialize implements serde.Message. It returns the serialized data for the -// handshake. -func (h Handshake) Serialize(ctx serde.Context) ([]byte, error) { - format := hsFormats.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, h) - if err != nil { - return nil, xerrors.Errorf("encode: %v", err) - } - - return data, nil -} - -// HandshakeFactory is a factory to serialize and deserialize handshake -// messages. -// -// - implements router.HandshakeFactory -type HandshakeFactory struct { - addrFac mino.AddressFactory -} - -// NewHandshakeFactory creates a new factory. -func NewHandshakeFactory(addrFac mino.AddressFactory) HandshakeFactory { - return HandshakeFactory{ - addrFac: addrFac, - } -} - -// Deserialize implements serde.Factory. It populates the handshake if -// appropriate, otherwise it returns an error. -func (fac HandshakeFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return fac.HandshakeOf(ctx, data) -} - -// HandshakeOf implements router.HandshakeFactory. It populates the handshake if -// appropriate, otherwise it returns an error. -func (fac HandshakeFactory) HandshakeOf(ctx serde.Context, data []byte) (router.Handshake, error) { - format := hsFormats.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, AddrKey{}, fac.addrFac) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("decode: %v", err) - } - - hs, ok := msg.(Handshake) - if !ok { - return nil, xerrors.Errorf("invalid handshake '%T'", msg) - } - - return hs, nil -} diff --git a/dela/mino/router/tree/types/handshake_test.go b/dela/mino/router/tree/types/handshake_test.go deleted file mode 100644 index b792a49..0000000 --- a/dela/mino/router/tree/types/handshake_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func init() { - RegisterHandshakeFormat(fake.GoodFormat, fake.Format{Msg: Handshake{}}) - RegisterHandshakeFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterHandshakeFormat(fake.MsgFormat, fake.NewMsgFormat()) -} - -func TestHandshake_GetHeight(t *testing.T) { - hs := NewHandshake(3, nil) - - require.Equal(t, 3, hs.GetHeight()) -} - -func TestHandshake_GetAddresses(t *testing.T) { - hs := NewHandshake(3, makeAddrs(5)...) - - require.Len(t, hs.GetAddresses(), 5) -} - -func TestHandshake_Serialize(t *testing.T) { - hs := NewHandshake(3, makeAddrs(5)...) - - ctx := fake.NewContext() - - data, err := hs.Serialize(ctx) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = hs.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("encode")) -} - -func TestHandshakeFactory_Deserialize(t *testing.T) { - fac := NewHandshakeFactory(fake.AddressFactory{}) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, Handshake{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("decode")) - - _, err = fac.Deserialize(fake.NewMsgContext(), nil) - require.EqualError(t, err, "invalid handshake 'fake.Message'") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func makeAddrs(n int) []mino.Address { - addrs := make([]mino.Address, n) - for i := range addrs { - addrs[i] = fake.NewAddress(i) - } - - return addrs -} diff --git a/dela/mino/router/tree/types/packet.go b/dela/mino/router/tree/types/packet.go deleted file mode 100644 index d006f92..0000000 --- a/dela/mino/router/tree/types/packet.go +++ /dev/null @@ -1,145 +0,0 @@ -// This file contains the implementation of the packet message. -// -// Documentation Last Review: 06.10.2020 -// - -package types - -import ( - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/router" - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/registry" - "golang.org/x/xerrors" -) - -var packetFormat = registry.NewSimpleRegistry() - -// Packet describes a tree routing packet -// -// - implements router.Packet -type Packet struct { - src mino.Address - dest []mino.Address - msg []byte -} - -// NewPacket creates a new packet. -func NewPacket(src mino.Address, msg []byte, dest ...mino.Address) *Packet { - return &Packet{ - src: src, - dest: dest, - msg: msg, - } -} - -// GetSource implements router.Packet. It returns the source address of the -// packet. -func (p *Packet) GetSource() mino.Address { - return p.src -} - -// GetDestination implements router.Packet. It returns a list of addresses where -// the packet should be send to. -func (p *Packet) GetDestination() []mino.Address { - return append([]mino.Address{}, p.dest...) -} - -// GetMessage implements router.Packet. It returns the byte buffer of the -// message. -func (p *Packet) GetMessage() []byte { - return append([]byte{}, p.msg...) -} - -// Add appends the address to the destination list, only if it does not exist -// already. -func (p *Packet) Add(to mino.Address) { - for _, addr := range p.dest { - if addr.Equal(to) { - return - } - } - - p.dest = append(p.dest, to) -} - -// Slice implements router.Packet. It removes the address from the destination -// list and returns a packet with this single destination, if it exists. -// Otherwise the packet stays unchanged. -func (p *Packet) Slice(addr mino.Address) router.Packet { - removed := false - - // in reverse order to remove from the slice "in place" - for i := len(p.dest) - 1; i >= 0; i-- { - if p.dest[i].Equal(addr) { - p.dest = append(p.dest[:i], p.dest[i+1:]...) - removed = true - } - } - - if !removed { - return nil - } - - return &Packet{ - src: p.src, - dest: []mino.Address{addr}, - msg: p.msg, - } -} - -// Serialize implements serde.Message. It returns the serialized data of the -// packet. -func (p *Packet) Serialize(ctx serde.Context) ([]byte, error) { - format := packetFormat.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, p) - if err != nil { - return nil, xerrors.Errorf("packet format: %v", err) - } - - return data, nil -} - -// AddrKey is the key for the address factory. -type AddrKey struct{} - -// PacketFactory is a factory for the packet. -// -// - implements serde.Factory -type PacketFactory struct { - addrFactory mino.AddressFactory -} - -// NewPacketFactory returns a factory for the packet. -func NewPacketFactory(f mino.AddressFactory) PacketFactory { - return PacketFactory{ - addrFactory: f, - } -} - -// PacketOf implements router.PacketFactory. It populates the packet associated -// with the data if appropriate, otherwise it returns an error. -func (f PacketFactory) PacketOf(ctx serde.Context, data []byte) (router.Packet, error) { - format := packetFormat.Get(ctx.GetFormat()) - - ctx = serde.WithFactory(ctx, AddrKey{}, f.addrFactory) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, xerrors.Errorf("packet format: %v", err) - } - - packet, ok := msg.(*Packet) - if !ok { - return nil, xerrors.Errorf("invalid packet '%T'", msg) - } - - return packet, nil -} - -// Deserialize implements serde.Factory. It populates the packet associated -// with the data if appropriate, otherwise it returns an error. -func (f PacketFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - return f.PacketOf(ctx, data) -} diff --git a/dela/mino/router/tree/types/packet_test.go b/dela/mino/router/tree/types/packet_test.go deleted file mode 100644 index 12bc19c..0000000 --- a/dela/mino/router/tree/types/packet_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/mino" -) - -func init() { - RegisterPacketFormat(fake.GoodFormat, fake.Format{Msg: &Packet{}}) - RegisterPacketFormat(fake.BadFormat, fake.NewBadFormat()) - RegisterPacketFormat(fake.MsgFormat, fake.NewMsgFormat()) -} - -func TestPacket_GetSource(t *testing.T) { - pkt := NewPacket(fake.NewAddress(0), nil) - - require.Equal(t, fake.NewAddress(0), pkt.GetSource()) -} - -func TestPacket_GetDestination(t *testing.T) { - pkt := NewPacket(fake.NewAddress(0), nil, makeAddrs(5)...) - - require.Len(t, pkt.GetDestination(), 5) -} - -func TestPacket_GetMessage(t *testing.T) { - pkt := NewPacket(fake.NewAddress(0), []byte{1, 2, 3}) - - require.Equal(t, []byte{1, 2, 3}, pkt.GetMessage()) -} - -func TestPacket_Add(t *testing.T) { - pkt := NewPacket(fake.NewAddress(0), nil) - require.Len(t, pkt.dest, 0) - - pkt.Add(fake.NewAddress(0)) - require.Len(t, pkt.dest, 1) - - pkt.Add(fake.NewAddress(0)) - require.Len(t, pkt.dest, 1) - - pkt.Add(fake.NewAddress(1)) - require.Len(t, pkt.dest, 2) -} - -func TestPacket_Slice(t *testing.T) { - pkt := NewPacket(fake.NewAddress(0), []byte{0xaa}, makeAddrs(10)...) - - newPkt := pkt.Slice(fake.NewAddress(500)) - require.Nil(t, newPkt) - - newPkt = pkt.Slice(fake.NewAddress(6)) - require.Equal(t, []mino.Address{fake.NewAddress(6)}, newPkt.GetDestination()) - require.Len(t, pkt.dest, 9) -} - -func TestPacket_Serialize(t *testing.T) { - pkt := NewPacket(fake.NewAddress(0), nil) - - data, err := pkt.Serialize(fake.NewContext()) - require.NoError(t, err) - require.Equal(t, fake.GetFakeFormatValue(), data) - - _, err = pkt.Serialize(fake.NewBadContext()) - require.EqualError(t, err, fake.Err("packet format")) -} - -func TestPacketFactory_Deserialize(t *testing.T) { - fac := NewPacketFactory(fake.AddressFactory{}) - - msg, err := fac.Deserialize(fake.NewContext(), nil) - require.NoError(t, err) - require.Equal(t, &Packet{}, msg) - - _, err = fac.Deserialize(fake.NewBadContext(), nil) - require.EqualError(t, err, fake.Err("packet format")) - - _, err = fac.Deserialize(fake.NewMsgContext(), nil) - require.EqualError(t, err, "invalid packet 'fake.Message'") -} diff --git a/dela/mino/router/tree/types/types.go b/dela/mino/router/tree/types/types.go deleted file mode 100644 index 848d4bd..0000000 --- a/dela/mino/router/tree/types/types.go +++ /dev/null @@ -1,20 +0,0 @@ -// Package types implements the packet and handshake messages for the tree -// routing algorithm. -// -// The messages have been implemented in this isolated package so that it does -// not create cycle imports when importing the serde formats. -// -// Documentation Last Review: 06.10.2020 -package types - -import "go.dedis.ch/dela/serde" - -// RegisterHandshakeFormat registers the engine for the provided format. -func RegisterHandshakeFormat(f serde.Format, e serde.FormatEngine) { - hsFormats.Register(f, e) -} - -// RegisterPacketFormat registers the engine for the provided format. -func RegisterPacketFormat(c serde.Format, f serde.FormatEngine) { - packetFormat.Register(c, f) -} diff --git a/dela/mod.go b/dela/mod.go deleted file mode 100644 index 302e32a..0000000 --- a/dela/mod.go +++ /dev/null @@ -1,122 +0,0 @@ -// Package dela defines the logger. -// -// Dela stands for DEDIS Ledger Architecture. It defines the modules that will -// be combined to deploy a distributed public ledger. -// -// Dela is using a global logger with some default parameters. It is disabled by -// default and the level can be increased using a environment variable: -// -// LLVL=trace go test ./... -// LLVL=info go test ./... -// LLVL=debug LOGF=$HOME/dela.log go test ./... -package dela - -import ( - "os" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/rs/zerolog" -) - -// EnvLogLevel is the name of the environment variable to change the logging -// level. -const EnvLogLevel = "LLVL" - -// EnvLogFile is the name of the environment variable to log in a given file. -const EnvLogFile = "LOGF" - -// PromCollectors exposes Prometheus collectors created in Dela. By default Dela -// doesn't register the metrics. It is left to the user to use the registry of -// its choice and register the collectors. For example with the default: -// -// prometheus.DefaultRegisterer.MustRegister(PromCollectors...) -// -// Note that the collectors can be registered only once and will panic -// otherwise. This slice is not thread-safe and should only be initialized in -// init() functions. -var PromCollectors []prometheus.Collector - -// defines prometheus metrics -var ( - promWarns = prometheus.NewCounter(prometheus.CounterOpts{ - Name: "dela_log_warns", - Help: "total number of warnings from the log", - }) - - promErrs = prometheus.NewCounter(prometheus.CounterOpts{ - Name: "dela_log_errs", - Help: "total number of errors from the log", - }) -) - -const defaultLevel = zerolog.NoLevel - -func init() { - logLevel := os.Getenv(EnvLogLevel) - - var level zerolog.Level - - switch logLevel { - case "error": - level = zerolog.ErrorLevel - case "warn": - level = zerolog.WarnLevel - case "info": - level = zerolog.InfoLevel - case "debug": - level = zerolog.DebugLevel - case "trace": - level = zerolog.TraceLevel - case "": - level = defaultLevel - default: - level = zerolog.TraceLevel - } - - Logger = Logger.Level(level) - PromCollectors = append(PromCollectors, promWarns, promErrs) - - logFile := os.Getenv(EnvLogFile) - if len(logFile) > 3 { - fileOut, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) - if err != nil { - Logger.Error().Msgf("COULD NOT OPEN %v", logFile) - os.Exit(2) - } - - multiWriter := zerolog.MultiLevelWriter(fileOut, consoleOut) - Logger = Logger.Output(multiWriter) - Logger.Info().Msgf("Using log file: %v", logFile) - } - - Logger.Info().Msgf("DELA Logger initialized!") -} - -var consoleOut = zerolog.ConsoleWriter{ - Out: os.Stdout, - TimeFormat: time.RFC3339, -} - -// Logger is a globally available logger instance. By default, it only prints -// error level messages but it can be changed through a environment variable. -var Logger = zerolog.New(consoleOut).Level(defaultLevel). - With().Timestamp().Logger(). - With().Caller().Logger(). - Hook(promHook{}) - -// promHook defines a zerolog hook that logs Prometheus metrics. Note that the -// log level MUST be set to at least the WARN level to get metrics. -// -// - implements zerolog.Hook -type promHook struct{} - -// Run implements zerolog.Hook -func (promHook) Run(e *zerolog.Event, level zerolog.Level, message string) { - switch level { - case zerolog.WarnLevel: - promWarns.Inc() - case zerolog.ErrorLevel: - promErrs.Inc() - } -} diff --git a/dela/serde/context.go b/dela/serde/context.go deleted file mode 100644 index 8b630ae..0000000 --- a/dela/serde/context.go +++ /dev/null @@ -1,56 +0,0 @@ -// -// Documentation Last Review: 07.10.2020 -// - -package serde - -// ContextEngine is the interface to implement to create a context. -type ContextEngine interface { - // GetFormat returns the name of the format for this context. - GetFormat() Format - - // Marshal returns the bytes of the message according to the format of the - // context. - Marshal(message interface{}) ([]byte, error) - - // Unmarshal populates the message with the data according to the format of - // the context. - Unmarshal(data []byte, message interface{}) error -} - -// Context is the context passed to the serialization/deserialization requests. -type Context struct { - ContextEngine - - factories map[interface{}]Factory -} - -// NewContext returns a new empty context. -func NewContext(engine ContextEngine) Context { - return Context{ - ContextEngine: engine, - factories: make(map[interface{}]Factory), - } -} - -// GetFactory returns the factory associated to the key or nil. -func (ctx Context) GetFactory(key interface{}) Factory { - return ctx.factories[key] -} - -// WithFactory adds a factory to the context. The factory will then be availble -// with the key when deserializing. -func WithFactory(ctx Context, key interface{}, f Factory) Context { - factories := map[interface{}]Factory{} - - for key, value := range ctx.factories { - factories[key] = value - } - - factories[key] = f - - // Prevent parent context from being contaminated with the new factory. - ctx.factories = factories - - return ctx -} diff --git a/dela/serde/context_test.go b/dela/serde/context_test.go deleted file mode 100644 index 279e4cd..0000000 --- a/dela/serde/context_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package serde - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestContext_GetFactory(t *testing.T) { - ctx := NewContext(nil) - - ctx.factories[testKey{}] = fakeFactory{} - - factory := ctx.GetFactory(testKey{}) - require.Equal(t, fakeFactory{}, factory) - - factory = ctx.GetFactory(struct{}{}) - require.Nil(t, factory) -} - -func TestContext_WithFactory(t *testing.T) { - ctx := NewContext(nil) - - ctx2 := WithFactory(ctx, testKey{}, fakeFactory{}) - require.Len(t, ctx.factories, 0) - require.Len(t, ctx2.factories, 1) - - ctx3 := WithFactory(ctx2, testKey{}, fakeFactory{}) - require.Len(t, ctx3.factories, 1) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type testKey struct{} - -type fakeFactory struct { - Factory -} diff --git a/dela/serde/example_test.go b/dela/serde/example_test.go deleted file mode 100644 index b11daab..0000000 --- a/dela/serde/example_test.go +++ /dev/null @@ -1,188 +0,0 @@ -package serde_test - -import ( - "errors" - "fmt" - - "go.dedis.ch/dela/serde" - "go.dedis.ch/dela/serde/json" - "go.dedis.ch/dela/serde/registry" - "go.dedis.ch/dela/serde/xml" -) - -func ExampleMessage_Serialize() { - // Register a JSON format engine for the message type. - exampleRegistry.Register(serde.FormatJSON, exampleJSONFormat{}) - // Register a Gob format engine for the message type. - exampleRegistry.Register(serde.FormatXML, exampleXMLFormat{}) - - msg := exampleMessage{ - value: 42, - } - - data, err := msg.Serialize(json.NewContext()) - if err != nil { - panic("serialization failed: " + err.Error()) - } - - fmt.Println("JSON", string(data)) - - data, err = msg.Serialize(xml.NewContext()) - if err != nil { - panic("serialization failed: " + err.Error()) - } - - fmt.Println("XML", string(data)) - - // Output: JSON {"value":42} - // XML 42 -} - -func ExampleFactory_Deserialize() { - factory := exampleFactory{} - - msg, err := factory.Deserialize(json.NewContext(), []byte(`{"Value":42}`)) - if err != nil { - panic("deserialization failed: " + err.Error()) - } - - fmt.Printf("%+v\n", msg) - - msg, err = factory.Deserialize(xml.NewContext(), []byte("12")) - if err != nil { - panic("deserialization failed: " + err.Error()) - } - - fmt.Printf("%+v", msg) - - // Output: {value:42} - // {value:12} -} - -var exampleRegistry = registry.NewSimpleRegistry() - -// exampleMessage is the data model for a message example. -// -// - implements serde.Message -type exampleMessage struct { - value int -} - -// Serialize implements serde.Message. It returns the JSON serialization of a -// message example. -func (m exampleMessage) Serialize(ctx serde.Context) ([]byte, error) { - format := exampleRegistry.Get(ctx.GetFormat()) - - data, err := format.Encode(ctx, m) - if err != nil { - return nil, err - } - - return data, nil -} - -// exampleFactory is a example of a message factory. -// -// - implements serde.Factory -type exampleFactory struct{} - -// Deserialize implements serde.Factory. It populates the example message. -func (exampleFactory) Deserialize(ctx serde.Context, data []byte) (serde.Message, error) { - format := exampleRegistry.Get(ctx.GetFormat()) - - msg, err := format.Decode(ctx, data) - if err != nil { - return nil, err - } - - m, ok := msg.(exampleMessage) - if !ok { - return nil, errors.New("invalid message") - } - - return m, nil -} - -// exampleMessageJSON is a JSON message for a message example. -type exampleMessageJSON struct { - Value int `json:"value"` -} - -// exampleJSONFormat is an example of a format to serialize a message example -// using a JSON encoding. -// -// - implements serde.FormatEngine -type exampleJSONFormat struct{} - -// Encode implements serde.FormatEngine. It populates a message that complies -// the JSON encoding and marshal it. -func (exampleJSONFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - example, ok := msg.(exampleMessage) - if !ok { - return nil, errors.New("unsupported message") - } - - m := exampleMessageJSON{ - Value: example.value, - } - - return ctx.Marshal(m) -} - -// Decode implements serde.FormatEngine. It populates a message example if -// appropritate, otherwise it returns an error. -func (exampleJSONFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - var m exampleMessageJSON - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, err - } - - msg := exampleMessage{ - value: m.Value, - } - - return msg, nil -} - -// exampleMessageXML is an XML message for a message example. -type exampleMessageXML struct { - Value int `xml:"value"` -} - -// exampleXMLFormat is an example if a format to serialize a message example -// using the XML encoding. -// -// - implements serde.FormatEngine -type exampleXMLFormat struct{} - -// Encode implements serde.FormatEngine. It formats the message to comply with -// the XML encoding and marshal it. -func (exampleXMLFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) { - example, ok := msg.(exampleMessage) - if !ok { - return nil, errors.New("unsupported message") - } - - m := exampleMessageXML{ - Value: example.value, - } - - return ctx.Marshal(m) -} - -// Decode implements serde.FormatEngine. It populates a message example if -// appropritate, otherwise it returns an error. -func (exampleXMLFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) { - var m exampleMessageXML - err := ctx.Unmarshal(data, &m) - if err != nil { - return nil, err - } - - msg := exampleMessage{ - value: m.Value, - } - - return msg, nil -} diff --git a/dela/serde/json/json.go b/dela/serde/json/json.go deleted file mode 100644 index e898c20..0000000 --- a/dela/serde/json/json.go +++ /dev/null @@ -1,51 +0,0 @@ -// Package json implements the context engine for a the JSON format. -// -// Documentation Last Review: 07.10.2020 -package json - -import ( - "encoding/json" - - // Static registration of the JSON formats. By having them here, it ensures - // that an import of the JSON context engine will import the definitions. - _ "go.dedis.ch/dela/core/access/darc/json" - _ "go.dedis.ch/dela/core/ordering/cosipbft/authority/json" - _ "go.dedis.ch/dela/core/ordering/cosipbft/blocksync/json" - _ "go.dedis.ch/dela/core/ordering/cosipbft/json" - _ "go.dedis.ch/dela/core/txn/signed/json" - _ "go.dedis.ch/dela/core/validation/simple/json" - _ "go.dedis.ch/dela/cosi/json" - _ "go.dedis.ch/dela/cosi/threshold/json" - _ "go.dedis.ch/dela/crypto/bls/json" - _ "go.dedis.ch/dela/crypto/ed25519/json" - _ "go.dedis.ch/dela/dkg/pedersen_bn256/json" - _ "go.dedis.ch/dela/mino/router/tree/json" - "go.dedis.ch/dela/serde" -) - -// JSONEngine is a context engine to marshal and unmarshal in JSON format. -// -// - implements serde.ContextEngine -type jsonEngine struct{} - -// NewContext returns a JSON context. -func NewContext() serde.Context { - return serde.NewContext(jsonEngine{}) -} - -// GetFormat implements serde.FormatEngine. It returns the JSON format name. -func (ctx jsonEngine) GetFormat() serde.Format { - return serde.FormatJSON -} - -// Marshal implements serde.FormatEngine. It returns the bytes of the message -// marshaled in JSON format. -func (ctx jsonEngine) Marshal(m interface{}) ([]byte, error) { - return json.Marshal(m) -} - -// Unmarshal implements serde.FormatEngine. It populates the message using the -// JSON format definition. -func (ctx jsonEngine) Unmarshal(data []byte, m interface{}) error { - return json.Unmarshal(data, m) -} diff --git a/dela/serde/json/json_test.go b/dela/serde/json/json_test.go deleted file mode 100644 index 691794e..0000000 --- a/dela/serde/json/json_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package json - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestJSONEngine_GetFormat(t *testing.T) { - ctx := NewContext() - require.Equal(t, serde.FormatJSON, ctx.GetFormat()) -} - -func TestJSONEngine_Marshal(t *testing.T) { - ctx := NewContext() - - data, err := ctx.Marshal(struct{}{}) - require.NoError(t, err) - require.Equal(t, `{}`, string(data)) - - _, err = ctx.Marshal(badObject{}) - require.EqualError(t, err, fake.Err("json: error calling MarshalJSON for type json.badObject")) -} - -func TestJSONEngine_Unmarshal(t *testing.T) { - ctx := NewContext() - - var m interface{} - err := ctx.Unmarshal([]byte(`{"A":"B"}`), &m) - require.NoError(t, err) - require.Equal(t, map[string]interface{}{"A": "B"}, m) - - err = ctx.Unmarshal(nil, &m) - require.EqualError(t, err, "unexpected end of JSON input") -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type badObject struct{} - -func (o badObject) MarshalJSON() ([]byte, error) { - return nil, fake.GetError() -} diff --git a/dela/serde/registry/registry.go b/dela/serde/registry/registry.go deleted file mode 100644 index 5d0b389..0000000 --- a/dela/serde/registry/registry.go +++ /dev/null @@ -1,23 +0,0 @@ -// Package registry defines the format registry mechanism. -// -// It also provides a default implementation that will always return a format -// using an empty format when appropriate. This format will always return an -// error. -// -// Documentation Last Review: 07.10.2020 -package registry - -import ( - "go.dedis.ch/dela/serde" -) - -// Registry is an interface to register and get format engines for a specific -// format. -type Registry interface { - // Register takes a format and its engine and it registers them so that the - // engine can be looked up later. - Register(serde.Format, serde.FormatEngine) - - // Get returns the engine associated with the format. - Get(serde.Format) serde.FormatEngine -} diff --git a/dela/serde/registry/simple.go b/dela/serde/registry/simple.go deleted file mode 100644 index 9ec90cd..0000000 --- a/dela/serde/registry/simple.go +++ /dev/null @@ -1,65 +0,0 @@ -// This file contains the implementation of a format registry. -// -// Documentation Last Review: 07.10.2020 -// - -package registry - -import ( - "go.dedis.ch/dela/serde" - "golang.org/x/xerrors" -) - -// SimpleRegistry is a default implementation of the Registry interface. It will -// always return a format which means an empty one is returned if the key is -// unknown. -// -// - implements registry.Registry -type SimpleRegistry struct { - store map[serde.Format]serde.FormatEngine -} - -// NewSimpleRegistry returns a new empty registry. -func NewSimpleRegistry() *SimpleRegistry { - return &SimpleRegistry{ - store: make(map[serde.Format]serde.FormatEngine), - } -} - -// Register implements registry.Registry. It registers the engine for the given -// format. -func (r *SimpleRegistry) Register(name serde.Format, f serde.FormatEngine) { - r.store[name] = f -} - -// Get implements registry.Registry. It returns the format engine associated -// with the format if it exists, otherwise it returns an empty format. -func (r *SimpleRegistry) Get(name serde.Format) serde.FormatEngine { - fmt := r.store[name] - if fmt == nil { - return emptyFormat{name: name} - } - - return fmt -} - -// EmptyFormat is an implementation of the FormatEngine interface. It implements -// the functions but always returns an error so that the serialization and -// deserialization can fail with meaningful errors without checking the format -// existance. -// -// - implements serde.FormatEngine -type emptyFormat struct { - serde.FormatEngine - name serde.Format -} - -// Encode implements serde.FormatEngine. It always returns an error. -func (f emptyFormat) Encode(serde.Context, serde.Message) ([]byte, error) { - return nil, xerrors.Errorf("format '%s' is not implemented", f.name) -} - -// Decode implements serde.FormatEngine. It always returns an error. -func (f emptyFormat) Decode(serde.Context, []byte) (serde.Message, error) { - return nil, xerrors.Errorf("format '%s' is not implemented", f.name) -} diff --git a/dela/serde/registry/simple_test.go b/dela/serde/registry/simple_test.go deleted file mode 100644 index a428d74..0000000 --- a/dela/serde/registry/simple_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package registry - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" - "go.dedis.ch/dela/serde" -) - -func TestSimpleRegistry_Register(t *testing.T) { - registry := NewSimpleRegistry() - - registry.Register(serde.FormatJSON, fake.Format{}) - require.Len(t, registry.store, 1) - - registry.Register(serde.FormatJSON, fake.Format{}) - require.Len(t, registry.store, 1) - - registry.Register(serde.Format("A"), fake.Format{}) - require.Len(t, registry.store, 2) -} - -func TestSimpleRegistry_Get(t *testing.T) { - registry := NewSimpleRegistry() - - registry.Register(serde.FormatJSON, fake.Format{}) - - format := registry.Get(serde.FormatJSON) - require.Equal(t, fake.Format{}, format) - - format = registry.Get(serde.Format("unknown")) - require.NotNil(t, format) - - _, err := format.Encode(serde.NewContext(nil), nil) - require.EqualError(t, err, "format 'unknown' is not implemented") - - _, err = format.Decode(serde.NewContext(nil), nil) - require.EqualError(t, err, "format 'unknown' is not implemented") -} diff --git a/dela/serde/serde.go b/dela/serde/serde.go deleted file mode 100644 index 80d1f96..0000000 --- a/dela/serde/serde.go +++ /dev/null @@ -1,54 +0,0 @@ -// Package serde defines the serialization and deserialization mechanisms. -// -// The serialization works through the implementation of the Message interface -// that can either support a single format, or a dynamic registration of a -// format engine for each format. -// -// The deserialization works in a similar fashion but through the Factory -// interface. -// -// See dela/serde/registry for more advanced control of the formats. -// -// Documentation Last Review: 07.10.2020 -package serde - -import "io" - -// Format is the identifier type of a format implementation. -type Format string - -const ( - // FormatJSON is the identifier for JSON formats. - FormatJSON Format = "JSON" - - // FormatXML is the identifier for XML formats. - FormatXML Format = "XML" -) - -// Message is the interface that a message must implement. -type Message interface { - // Serialize serializes the object by complying to the context format. - Serialize(ctx Context) ([]byte, error) -} - -// Factory is the interface that a message factory must implement. -type Factory interface { - // Deserialize deserializes the message instantiated from the data. - Deserialize(ctx Context, data []byte) (Message, error) -} - -// FormatEngine is the interface that a format implementation must implement. -type FormatEngine interface { - // Encode marshals the message according to the format definition. - Encode(ctx Context, message Message) ([]byte, error) - - // Decode unmarshal a message according to the format definition. - Decode(ctx Context, data []byte) (Message, error) -} - -// Fingerprinter is an interface to fingerprint an object. -type Fingerprinter interface { - // Fingerprint writes a deterministic binary representation of the object - // into the writer. - Fingerprint(writer io.Writer) error -} diff --git a/dela/serde/xml/mod.go b/dela/serde/xml/mod.go deleted file mode 100644 index 0ae41af..0000000 --- a/dela/serde/xml/mod.go +++ /dev/null @@ -1,39 +0,0 @@ -// Package xml implements the context engine for the XML encoding. -// -// Documentation Last Review: 14.10.2020 -// -package xml - -import ( - "encoding/xml" - - "go.dedis.ch/dela/serde" -) - -// xmlEngine is a context engine that uses the XML encoding. See encoding/xml. -// -// - implements serde.ContextEngine -type xmlEngine struct{} - -// NewContext returns a new serde context that is using the XML encoding. -func NewContext() serde.Context { - return serde.NewContext(xmlEngine{}) -} - -// GetFormat implements serde.ContextEngine. It returns the XML format -// identifier. -func (xmlEngine) GetFormat() serde.Format { - return serde.FormatXML -} - -// Marshal implements serde.ContextEngine. It marshals the message using the XML -// encoding. -func (xmlEngine) Marshal(m interface{}) ([]byte, error) { - return xml.Marshal(m) -} - -// Unmarshal implements serde.ContextEngine. It unmarshals the data into the -// message using the XML encoding. -func (xmlEngine) Unmarshal(data []byte, m interface{}) error { - return xml.Unmarshal(data, m) -} diff --git a/dela/serde/xml/xml_test.go b/dela/serde/xml/xml_test.go deleted file mode 100644 index 170fe7b..0000000 --- a/dela/serde/xml/xml_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package xml - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.dedis.ch/dela/serde" -) - -func TestXMLEngine_GetFormat(t *testing.T) { - ctx := NewContext() - - require.Equal(t, serde.FormatXML, ctx.GetFormat()) -} - -func TestXMLEngine_Marshal(t *testing.T) { - ctx := NewContext() - - data, err := ctx.Marshal(testMessage{Value: 42}) - require.NoError(t, err) - require.Equal(t, "42", string(data)) -} - -func TestXMLEngine_Unmarshal(t *testing.T) { - ctx := NewContext() - - var m testMessage - - err := ctx.Unmarshal([]byte("42"), &m) - require.NoError(t, err) - require.Equal(t, 42, m.Value) -} - -// ----------------------------------------------------------------------------- -// Utility functions - -type testMessage struct { - Value int -} diff --git a/dela/test/README.md b/dela/test/README.md deleted file mode 100644 index aa02449..0000000 --- a/dela/test/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Manual testing - -## Prerequisites - -- `tmux` -- `memcoin` (from the root: `go install ./cli/node/memcoin`) -- `crypto` (from the root: `go install ./cli/crypto`) - -## Create a private key - -Create a new private key (can be re-used in successive tests). - -```sh -./testcreatekey.sh -``` - -## Start the environment - -Setup the manual test environment in a tmux multi-pane window - -```sh -./teststart.sh -``` - -Use CTRL + b and arrows to move around panes. Also -CTRL + b + [ to scroll a window. - -## Add transactions - -Following commands are given as examples: - -- Store a key/value on the blockchain via node 1 using default nonce -1 - -```sh -./teststore.sh key1 value1 1 -1 -``` - -- List the values stored on the blockchain via node 3 using nonce 7 - -```sh -./testlist.sh 3 7 -``` - -## Stop the environment - -Once finished with testing, it is highly recommended to cleanly shutdown this -test setup and remove the temporary files: - -```sh -./teststop.sh -``` diff --git a/dela/test/cosidela_test.go b/dela/test/cosidela_test.go deleted file mode 100644 index 7f50548..0000000 --- a/dela/test/cosidela_test.go +++ /dev/null @@ -1,413 +0,0 @@ -package integration - -import ( - "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/x509" - "io" - "math/rand" - "net/url" - "os" - "path/filepath" - "time" - - "github.com/stretchr/testify/require" - accessContract "go.dedis.ch/dela/contracts/access" - "go.dedis.ch/dela/contracts/value" - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/access/darc" - "go.dedis.ch/dela/core/execution/native" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/ordering/cosipbft" - "go.dedis.ch/dela/core/ordering/cosipbft/authority" - "go.dedis.ch/dela/core/ordering/cosipbft/blockstore" - "go.dedis.ch/dela/core/ordering/cosipbft/types" - "go.dedis.ch/dela/core/store/hashtree" - "go.dedis.ch/dela/core/store/hashtree/binprefix" - "go.dedis.ch/dela/core/store/kv" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/pool" - poolimpl "go.dedis.ch/dela/core/txn/pool/gossip" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/core/validation" - "go.dedis.ch/dela/core/validation/simple" - "go.dedis.ch/dela/cosi/threshold" - "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/crypto/loader" - "go.dedis.ch/dela/mino" - "go.dedis.ch/dela/mino/gossip" - "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" - "go.dedis.ch/dela/serde/json" - "golang.org/x/xerrors" -) - -const certKeyName = "cert.key" -const privateKeyFile = "private.key" - -var aKey = [32]byte{1} -var valueAccessKey = [32]byte{2} - -// cosiDela defines the interface needed to use a Dela node using cosi. -type cosiDela interface { - dela - - GetPublicKey() crypto.PublicKey - GetPool() pool.Pool - GetAccessStore() accessstore - GetTree() hashtree.Tree -} - -// cosiDelaNode represents a Dela node using cosi pbft -// -// - implements dela -type cosiDelaNode struct { - t require.TestingT - onet mino.Mino - ordering ordering.Service - cosi *threshold.Threshold - txManager txn.Manager - pool pool.Pool - accessService access.Service - accessStore accessstore - tree hashtree.Tree -} - -func newDelaNode(t require.TestingT, path string, port int) dela { - err := os.MkdirAll(path, 0700) - require.NoError(t, err) - - // store - db, err := kv.New(filepath.Join(path, "dela.db")) - require.NoError(t, err) - - // mino - router := tree.NewRouter(minogrpc.NewAddressFactory()) - addr := minogrpc.ParseAddress("127.0.0.1", uint16(port)) - - certs := certs.NewDiskStore(db, session.AddressFactory{}) - - fload := loader.NewFileLoader(filepath.Join(path, certKeyName)) - - keydata, err := fload.LoadOrCreate(newCertGenerator(rand.New(rand.NewSource(0)), elliptic.P521())) - require.NoError(t, err) - - key, err := x509.ParseECPrivateKey(keydata) - require.NoError(t, err) - - opts := []minogrpc.Option{ - minogrpc.WithStorage(certs), - minogrpc.WithCertificateKey(key, key.Public()), - } - - onet, err := minogrpc.NewMinogrpc(addr, nil, router, opts...) - require.NoError(t, err) - onet.GetAddress() - - // ordering + validation + execution - fload = loader.NewFileLoader(filepath.Join(path, privateKeyFile)) - - signerdata, err := fload.LoadOrCreate(newKeyGenerator()) - require.NoError(t, err) - - signer, err := bls.NewSignerFromBytes(signerdata) - require.NoError(t, err) - - cosi := threshold.NewThreshold(onet.WithSegment("cosi"), signer) - cosi.SetThreshold(threshold.ByzantineThreshold) - - exec := native.NewExecution() - accessService := darc.NewService(json.NewContext()) - - rosterFac := authority.NewFactory(onet.GetAddressFactory(), cosi.GetPublicKeyFactory()) - cosipbft.RegisterRosterContract(exec, rosterFac, accessService) - - value.RegisterContract(exec, value.NewContract(valueAccessKey[:], accessService)) - - txFac := signed.NewTransactionFactory() - vs := simple.NewService(exec, txFac) - - pool, err := poolimpl.NewPool(gossip.NewFlat(onet.WithSegment("pool"), txFac)) - require.NoError(t, err) - - tree := binprefix.NewMerkleTree(db, binprefix.Nonce{}) - - param := cosipbft.ServiceParam{ - Mino: onet, - Cosi: cosi, - Validation: vs, - Access: accessService, - Pool: pool, - DB: db, - Tree: tree, - } - - err = tree.Load() - require.NoError(t, err) - - genstore := blockstore.NewGenesisDiskStore(db, types.NewGenesisFactory(rosterFac)) - - err = genstore.Load() - require.NoError(t, err) - - blockFac := types.NewBlockFactory(vs.GetFactory()) - csFac := authority.NewChangeSetFactory(onet.GetAddressFactory(), cosi.GetPublicKeyFactory()) - linkFac := types.NewLinkFactory(blockFac, cosi.GetSignatureFactory(), csFac) - - blocks := blockstore.NewDiskStore(db, linkFac) - - err = blocks.Load() - require.NoError(t, err) - - srvc, err := cosipbft.NewService(param) - require.NoError(t, err) - - // tx - mgr := signed.NewManager(cosi.GetSigner(), client{ - srvc: srvc, - mgr: vs, - }) - - // access - accessStore := newAccessStore() - contract := accessContract.NewContract(aKey[:], accessService, accessStore) - accessContract.RegisterContract(exec, contract) - - return cosiDelaNode{ - t: t, - onet: onet, - ordering: srvc, - cosi: cosi, - txManager: mgr, - pool: pool, - accessService: accessService, - accessStore: accessStore, - tree: tree, - } -} - -// Setup implements dela. It creates the roster, shares the certificate, and -// create an new chain. -func (c cosiDelaNode) Setup(delas ...dela) { - // share the certificates - joinable, ok := c.onet.(minogrpc.Joinable) - require.True(c.t, ok) - - addrURL, err := url.Parse("//" + c.onet.GetAddress().String()) - require.NoError(c.t, err, addrURL) - - token := joinable.GenerateToken(time.Hour) - - certHash, err := joinable.GetCertificateStore().Hash(joinable.GetCertificateChain()) - require.NoError(c.t, err) - - for _, dela := range delas { - otherJoinable, ok := dela.GetMino().(minogrpc.Joinable) - require.True(c.t, ok) - - err = otherJoinable.Join(addrURL, token, certHash) - require.NoError(c.t, err) - } - - type extendedService interface { - GetRoster() (authority.Authority, error) - Setup(ctx context.Context, ca crypto.CollectiveAuthority) error - } - - // make roster - extended, ok := c.GetOrdering().(extendedService) - require.True(c.t, ok) - - minoAddrs := make([]mino.Address, len(delas)+1) - pubKeys := make([]crypto.PublicKey, len(delas)+1) - - for i, dela := range delas { - minoAddr := dela.GetMino().GetAddress() - - d, ok := dela.(cosiDela) - require.True(c.t, ok) - - pubkey := d.GetPublicKey() - - minoAddrs[i+1] = minoAddr - pubKeys[i+1] = pubkey - } - - minoAddrs[0] = c.onet.GetAddress() - pubKeys[0] = c.cosi.GetSigner().GetPublicKey() - - roster := authority.New(minoAddrs, pubKeys) - - // create chain - err = extended.Setup(context.Background(), roster) - require.NoError(c.t, err) -} - -// GetMino implements dela -func (c cosiDelaNode) GetMino() mino.Mino { - return c.onet -} - -// GetOrdering implements dela -func (c cosiDelaNode) GetOrdering() ordering.Service { - return c.ordering -} - -// GetTxManager implements dela -func (c cosiDelaNode) GetTxManager() txn.Manager { - return c.txManager -} - -// GetAccessService implements dela -func (c cosiDelaNode) GetAccessService() access.Service { - return c.accessService -} - -// GetPublicKey implements cosiDela -func (c cosiDelaNode) GetPublicKey() crypto.PublicKey { - return c.cosi.GetSigner().GetPublicKey() -} - -// GetPool implements cosiDela -func (c cosiDelaNode) GetPool() pool.Pool { - return c.pool -} - -// GetAccessStore implements cosiDela -func (c cosiDelaNode) GetAccessStore() accessstore { - return c.accessStore -} - -// GetTree implements cosiDela -func (c cosiDelaNode) GetTree() hashtree.Tree { - return c.tree -} - -// generator can generate a private key compatible with the x509 certificate. -// -// - implements loader.Generator -type certGenerator struct { - random io.Reader - curve elliptic.Curve -} - -func newCertGenerator(r io.Reader, c elliptic.Curve) loader.Generator { - return certGenerator{ - random: r, - curve: c, - } -} - -// certGenerator 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 certGenerator) 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 -} - -func newKeyGenerator() loader.Generator { - return keyGenerator{} -} - -// keyGenerator is an implementation to generate a private key. -// -// - implements loader.Generator -type keyGenerator struct { -} - -// Generate implements loader.Generator. It returns the marshaled data of a -// private key. -func (g keyGenerator) Generate() ([]byte, error) { - signer := bls.NewSigner() - - data, err := signer.MarshalBinary() - if err != nil { - return nil, xerrors.Errorf("failed to marshal signer: %v", err) - } - - return data, nil -} - -// Client is a local client for the manager to read the current identity's nonce -// from the ordering service. -// -// - implements signed.Client -type client struct { - srvc ordering.Service - mgr validation.Service -} - -// GetNonce implements signed.Client. It reads the store of the ordering service -// to get the next nonce of the identity and returns it. -func (c client) GetNonce(ident access.Identity) (uint64, error) { - store := c.srvc.GetStore() - - nonce, err := c.mgr.GetNonce(store, ident) - if err != nil { - return 0, err - } - - return nonce, nil -} - -// newAccessStore returns a new access store -func newAccessStore() accessstore { - return accessstore{ - bucket: make(map[string][]byte), - } -} - -// accessstore is an in-memory store access -// -// - implements store.Readable -// - implements store.Writable -type accessstore struct { - bucket map[string][]byte -} - -// Get implements store.Readable -func (a accessstore) Get(key []byte) ([]byte, error) { - return a.bucket[string(key)], nil -} - -// Set implements store.Writable -func (a accessstore) Set(key, value []byte) error { - a.bucket[string(key)] = value - - return nil -} - -// Delete implements store.Writable -func (a accessstore) Delete(key []byte) error { - delete(a.bucket, string(key)) - - return nil -} - -// txClient return monotically increasing nonce -// -// - implements signed.Client -type txClient struct { - nonce uint64 -} - -// GetNonce implements signed.Client -func (c *txClient) GetNonce(access.Identity) (uint64, error) { - res := c.nonce - c.nonce++ - return res, nil -} diff --git a/dela/test/dela_test.go b/dela/test/dela_test.go deleted file mode 100644 index 9d9c8f6..0000000 --- a/dela/test/dela_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package integration - -import ( - "go.dedis.ch/dela/core/access" - "go.dedis.ch/dela/core/ordering" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/mino" -) - -// dela defines the common interface for a Dela node. -type dela interface { - Setup(...dela) - GetMino() mino.Mino - GetOrdering() ordering.Service - GetTxManager() txn.Manager - GetAccessService() access.Service -} diff --git a/dela/test/integration_test.go b/dela/test/integration_test.go deleted file mode 100644 index e4882e7..0000000 --- a/dela/test/integration_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package integration - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/hex" - "math/rand" - "os" - "path/filepath" - "strconv" - "testing" - "time" - - "github.com/stretchr/testify/require" - accessContract "go.dedis.ch/dela/contracts/access" - "go.dedis.ch/dela/core/txn" - "go.dedis.ch/dela/core/txn/signed" - "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/crypto/loader" - "golang.org/x/xerrors" -) - -func init() { - rand.Seed(0) -} - -// Start 3 nodes -// Use the value contract -// Check the state -func TestIntegration_Value_Simple(t *testing.T) { - t.Run("3 nodes", getTest[*testing.T](3, 2)) -} - -func BenchmarkValue(b *testing.B) { - getTest[*testing.B](5, b.N)(b) -} - -func getTest[T require.TestingT](numNode, numTx int) func(t T) { - return func(t T) { - dir, err := os.MkdirTemp(os.TempDir(), "dela-integration-test") - require.NoError(t, err) - - timeout := time.Second * 10 // transaction inclusion timeout - - defer os.RemoveAll(dir) - - nodes := make([]dela, numNode) - - for i := range nodes { - node := newDelaNode(t, filepath.Join(dir, "node"+strconv.Itoa(i)), 0) - nodes[i] = node - } - - nodes[0].Setup(nodes[1:]...) - - l := loader.NewFileLoader(filepath.Join(dir, "private.key")) - - signerdata, err := l.LoadOrCreate(newKeyGenerator()) - require.NoError(t, err) - - signer, err := bls.NewSignerFromBytes(signerdata) - require.NoError(t, err) - - pubKey := signer.GetPublicKey() - cred := accessContract.NewCreds(aKey[:]) - - for _, node := range nodes { - node.GetAccessService().Grant(node.(cosiDelaNode).GetAccessStore(), cred, pubKey) - } - - manager := signed.NewManager(signer, &txClient{}) - - pubKeyBuf, err := signer.GetPublicKey().MarshalBinary() - require.NoError(t, err) - - args := []txn.Arg{ - {Key: "go.dedis.ch/dela.ContractArg", Value: []byte("go.dedis.ch/dela.Access")}, - {Key: "access:grant_id", Value: []byte(hex.EncodeToString(valueAccessKey[:]))}, - {Key: "access:grant_contract", Value: []byte("go.dedis.ch/dela.Value")}, - {Key: "access:grant_command", Value: []byte("all")}, - {Key: "access:identity", Value: []byte(base64.StdEncoding.EncodeToString(pubKeyBuf))}, - {Key: "access:command", Value: []byte("GRANT")}, - } - - err = addAndWait(t, timeout, manager, nodes[0].(cosiDelaNode), args...) - require.NoError(t, err) - - for i := 0; i < numTx; i++ { - key := make([]byte, 32) - - _, err = rand.Read(key) - require.NoError(t, err) - - args = []txn.Arg{ - {Key: "go.dedis.ch/dela.ContractArg", Value: []byte("go.dedis.ch/dela.Value")}, - {Key: "value:key", Value: key}, - {Key: "value:value", Value: []byte("value1")}, - {Key: "value:command", Value: []byte("WRITE")}, - } - - err = addAndWait(t, timeout, manager, nodes[0].(cosiDelaNode), args...) - require.NoError(t, err) - - proof, err := nodes[0].GetOrdering().GetProof(key) - require.NoError(t, err) - require.Equal(t, []byte("value1"), proof.GetValue()) - } - } -} - -// ----------------------------------------------------------------------------- -// Utility functions - -func addAndWait(t require.TestingT, to time.Duration, manager txn.Manager, node cosiDelaNode, args ...txn.Arg) error { - manager.Sync() - - tx, err := manager.Make(args...) - if err != nil { - return xerrors.Errorf("failed to make tx: %v", err) - } - - err = node.GetPool().Add(tx) - if err != nil { - return xerrors.Errorf("failed to add tx: %v", err) - } - - ctx, cancel := context.WithTimeout(context.Background(), to) - defer cancel() - - events := node.GetOrdering().Watch(ctx) - - for event := range events { - for _, result := range event.Transactions { - tx := result.GetTransaction() - - if bytes.Equal(tx.GetID(), tx.GetID()) { - accepted, err := event.Transactions[0].GetStatus() - require.Empty(t, err) - - require.True(t, accepted) - return nil - } - } - } - - return xerrors.Errorf("transaction not found") -} diff --git a/dela/test/testcreatekey.sh b/dela/test/testcreatekey.sh deleted file mode 100755 index beba279..0000000 --- a/dela/test/testcreatekey.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -set -e - -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -echo -e "${GREEN}[PK]${NC} create a private key for the manual test" -crypto bls signer new --save private.key -crypto bls signer read --path private.key --format BASE64 diff --git a/dela/test/testlist.sh b/dela/test/testlist.sh deleted file mode 100755 index 18bbc03..0000000 --- a/dela/test/testlist.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -# This script lists all values on the blockchain. - -set -e - -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -echo -e "${GREEN}[LIST]${NC} list all values via node $1 using nonce $2" -memcoin --config /tmp/node$1 pool add\ - --key private.key\ - --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Value\ - --args value:command --args LIST\ - --nonce $2 diff --git a/dela/test/testsetup.sh b/dela/test/testsetup.sh deleted file mode 100755 index cd767bb..0000000 --- a/dela/test/testsetup.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# This script is creating a new chain and setting up the services needed to run -# an evoting system. It ends by starting the http server needed by the frontend -# to communicate with the blockchain. This operation is blocking. - -set -e - -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -# allow nodes to be up before connecting -sleep 1 - -echo -e "${GREEN}[CONNECT]${NC} connect nodes" -memcoin --config /tmp/node2 minogrpc join \ - --address //127.0.0.1:2001 $(memcoin --config /tmp/node1 minogrpc token) -memcoin --config /tmp/node3 minogrpc join \ - --address //127.0.0.1:2001 $(memcoin --config /tmp/node1 minogrpc token) -memcoin --config /tmp/node4 minogrpc join \ - --address //127.0.0.1:2001 $(memcoin --config /tmp/node1 minogrpc token) - -echo -e "${GREEN}[CHAIN]${NC} create a chain" -memcoin --config /tmp/node1 ordering setup\ - --member $(memcoin --config /tmp/node1 ordering export)\ - --member $(memcoin --config /tmp/node2 ordering export)\ - --member $(memcoin --config /tmp/node3 ordering export)\ - --member $(memcoin --config /tmp/node4 ordering export) - -echo -e "${GREEN}[ACCESS]${NC} setup access rights on each node" -memcoin --config /tmp/node1 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) -memcoin --config /tmp/node2 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) -memcoin --config /tmp/node3 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) -memcoin --config /tmp/node4 access add \ - --identity $(crypto bls signer read --path private.key --format BASE64_PUBKEY) - -echo -e "${GREEN}[GRANT]${NC} grant access node 1 on the chain" -memcoin --config /tmp/node1 pool add\ - --key private.key\ - --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Access\ - --args access:grant_id --args 0200000000000000000000000000000000000000000000000000000000000000\ - --args access:grant_contract --args go.dedis.ch/dela.Value\ - --args access:grant_command --args all\ - --args access:identity --args $(crypto bls signer read --path private.key --format BASE64_PUBKEY)\ - --args access:command --args GRANT diff --git a/dela/test/teststart.sh b/dela/test/teststart.sh deleted file mode 100755 index b4c0ba3..0000000 --- a/dela/test/teststart.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -# This script creates a new tmux session and starts nodes according to the -# instructions is README.md. The test session can be killed with teststop.sh. - -set -o errexit - -command -v tmux >/dev/null 2>&1 || { echo >&2 "tmux is not on your PATH!"; exit 1; } - -# Launch session -s="dela-nodes-test" - -tmux list-sessions | rg "^$s:" >/dev/null 2>&1 && { echo >&2 "A session with the name $s already exists; kill it and try again"; exit 1; } - -tmux new -s $s -d - -tmux split-window -t $s -h -tmux split-window -t $s:0.%1 -tmux split-window -t $s:0.%2 -tmux split-window -t $s:0.%3 - -# session s, window 0, panes 0 to 4 -master="tmux send-keys -t $s:0.%0" -node1="tmux send-keys -t $s:0.%1" -node2="tmux send-keys -t $s:0.%2" -node3="tmux send-keys -t $s:0.%3" -node4="tmux send-keys -t $s:0.%4" - -$node1 "LLVL=info memcoin --config /tmp/node1 start --listen //127.0.0.1:2001" C-m -$node2 "LLVL=info memcoin --config /tmp/node2 start --listen //127.0.0.1:2002" C-m -$node3 "LLVL=info memcoin --config /tmp/node3 start --listen //127.0.0.1:2003" C-m -$node4 "LLVL=info memcoin --config /tmp/node4 start --listen //127.0.0.1:2004" C-m - -tmux select-pane -t 0 - -$master "./testsetup.sh" C-m - -tmux a diff --git a/dela/test/teststop.sh b/dela/test/teststop.sh deleted file mode 100755 index 2c452e5..0000000 --- a/dela/test/teststop.sh +++ /dev/null @@ -1,13 +0,0 @@ -#! /bin/sh - -set -e - -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -echo -e "${GREEN}[STOP]${NC} kills nodes" -rm -rf /tmp/node* - -sleep 1 - -tmux kill-session -t dela-nodes-test diff --git a/dela/test/teststore.sh b/dela/test/teststore.sh deleted file mode 100755 index 1d5307f..0000000 --- a/dela/test/teststore.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -# This script stores a key/value pair on the blockchain. - -set -e - -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -echo -e "${GREEN}[STORE]${NC} store [key $1: value $2] via node $3 using nonce $4" -memcoin --config /tmp/node$3 pool add\ - --key private.key\ - --args go.dedis.ch/dela.ContractArg --args go.dedis.ch/dela.Value\ - --args value:key --args "key"$1\ - --args value:value --args $2\ - --args value:command --args WRITE\ - --nonce $4 diff --git a/deps.go b/deps.go new file mode 100644 index 0000000..66f32a9 --- /dev/null +++ b/deps.go @@ -0,0 +1,7 @@ +package f3b +// This file just pins the packages with executables we need + +import ( + _ "go.dedis.ch/f3b/smc/dkgcli" + _ "github.com/ethereum/go-ethereum/cmd/geth" +) diff --git a/go-ethereum/f3b/vdf/vdf_test.go b/go-ethereum/f3b/vdf/vdf_test.go index 8386e3a..9a9feed 100644 --- a/go-ethereum/f3b/vdf/vdf_test.go +++ b/go-ethereum/f3b/vdf/vdf_test.go @@ -32,7 +32,7 @@ func TestRecoverSecretFromProof(t *testing.T) { } func BenchmarkRecoverSecret(b *testing.B) { - for log2t := 5; log2t <= 20; log2t += 5 { + for log2t := 5; log2t <= 20; log2t++ { b.Run(fmt.Sprintf("log2t=%d", log2t), func(b *testing.B) { label := []byte("test") n, _, _, _ := ShareSecret(label, log2t) diff --git a/go-ethereum/go.mod b/go-ethereum/go.mod index 28187f5..0256435 100644 --- a/go-ethereum/go.mod +++ b/go-ethereum/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 github.com/VictoriaMetrics/fastcache v1.6.0 + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/aws/aws-sdk-go-v2 v1.2.0 github.com/aws/aws-sdk-go-v2/config v1.1.1 github.com/aws/aws-sdk-go-v2/credentials v1.1.1 @@ -56,6 +57,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef github.com/urfave/cli/v2 v2.10.2 + go.dedis.ch/kyber/v3 v3.0.14 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c @@ -71,7 +73,6 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.1.1 // indirect @@ -83,11 +84,9 @@ require ( github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect - github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect - github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect @@ -101,7 +100,6 @@ require ( github.com/tklauser/numcpus v0.2.2 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.dedis.ch/fixbuf v1.0.3 // indirect - go.dedis.ch/kyber/v3 v3.0.14 // indirect golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect @@ -110,6 +108,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace go.dedis.ch/dela => ../dela/ - replace go.dedis.ch/kyber/v3 => ../kyber/ diff --git a/go-ethereum/go.sum b/go-ethereum/go.sum index 508ccd3..8a00e74 100644 --- a/go-ethereum/go.sum +++ b/go-ethereum/go.sum @@ -129,9 +129,8 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlK github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= @@ -158,7 +157,6 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5Nq github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -206,7 +204,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -233,7 +230,6 @@ github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= github.com/influxdata/influxdb v1.8.3 h1:WEypI1BQFTT4teLM+1qkEcvUi0dAvopAI/ir0vAiBg8= @@ -322,29 +318,19 @@ github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hz github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= @@ -410,14 +396,8 @@ github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 h1:m+8fKfQwCAy1QjzINvKe/pYtLjo2dl59x2w9YSEJxuY= github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= -github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= @@ -439,12 +419,7 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= -go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= -go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg= -go.dedis.ch/kyber/v3 v3.0.14 h1:vnHb/Q5ape/e98oYZdbOrs3CkQ1bUIZFANmOxb/3zyo= -go.dedis.ch/kyber/v3 v3.0.14/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U= -go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= -go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= +go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -521,11 +496,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -573,7 +546,6 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -582,8 +554,6 @@ golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -631,7 +601,6 @@ golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI= golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= diff --git a/go.mod b/go.mod index 5ba8a9d..b27836c 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module github.com/dedis/f3b-ethereum -go 1.21 - -replace go.dedis.ch/dela => ./dela/ +go 1.22.1 replace go.dedis.ch/kyber/v3 => ./kyber/ replace github.com/ethereum/go-ethereum => ./go-ethereum/ +replace go.dedis.ch/f3b/smc => ./smc + require ( github.com/ethereum/go-ethereum v0.0.0-00010101000000-000000000000 - go.dedis.ch/dela v0.0.0-00010101000000-000000000000 + go.dedis.ch/f3b/smc v0.0.0-00010101000000-000000000000 golang.org/x/perf v0.0.0-20240305160248-5eefbfdba9dd gonum.org/v1/gonum v0.11.0 ) @@ -27,7 +27,6 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/dedis/debugtools v0.0.0-20221206213939-0bc3bacd3042 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf // indirect @@ -39,9 +38,9 @@ require ( github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang-jwt/jwt/v4 v4.3.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect @@ -55,55 +54,56 @@ require ( github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e // indirect github.com/karalabe/usb v0.0.2 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/opentracing-contrib/go-grpc v0.0.0-20200813121455-4a6760c71486 // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.5.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.10.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/tsdb v0.7.1 // indirect github.com/rjeczalik/notify v0.9.1 // indirect github.com/rs/cors v1.7.0 // indirect - github.com/rs/xid v1.4.0 // indirect - github.com/rs/zerolog v1.28.0 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/rs/zerolog v1.32.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect - github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect - github.com/uber/jaeger-lib v2.4.0+incompatible // indirect - github.com/urfave/cli/v2 v2.10.2 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/urfave/cli/v2 v2.27.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + go.dedis.ch/debugtools v0.1.1 // indirect + go.dedis.ch/dela v0.0.1-alpha // indirect go.dedis.ch/fixbuf v1.0.3 // indirect go.dedis.ch/kyber/v3 v3.0.14 // indirect go.dedis.ch/protobuf v1.0.11 // indirect - go.etcd.io/bbolt v1.3.5 // indirect - go.uber.org/atomic v1.7.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + go.etcd.io/bbolt v1.3.9 // indirect + go.uber.org/atomic v1.11.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/grpc v1.63.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 8830a4c..9705f07 100644 --- a/go.sum +++ b/go.sum @@ -34,9 +34,7 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= @@ -63,7 +61,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -78,8 +76,6 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/dedis/debugtools v0.0.0-20221206213939-0bc3bacd3042 h1:poR/D0ZoNGzZSbQZNgzDiwXTFJsBuM3dOEToNDh4gd4= -github.com/dedis/debugtools v0.0.0-20221206213939-0bc3bacd3042/go.mod h1:d0B8cSk0nY+sXvY5UOxIKcQoUZo29cOCsEsuYS+AMsQ= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -116,12 +112,12 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -154,11 +150,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -171,18 +164,15 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -227,13 +217,13 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= @@ -245,11 +235,11 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM52 github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -266,28 +256,26 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -307,8 +295,8 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/opentracing-contrib/go-grpc v0.0.0-20200813121455-4a6760c71486 h1:K35HCWaOTJIPW6cDHK4yj3QfRY/NhE0pBbfoc0M2NMQ= -github.com/opentracing-contrib/go-grpc v0.0.0-20200813121455-4a6760c71486/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -329,36 +317,36 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= @@ -367,7 +355,6 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -377,16 +364,17 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -396,12 +384,12 @@ github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefld github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= -github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaWxXbjwyYwsNaLQ= -github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= -github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -409,24 +397,22 @@ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +go.dedis.ch/debugtools v0.1.1 h1:p/arQ7o9QLZ0JI0soA4CdQh9i3Enphr+O2bs5pWLqLA= +go.dedis.ch/debugtools v0.1.1/go.mod h1:Yp+5sHo3/Pfvl1cq70nhbZs8sv1WlUyk1LWph7xFntg= +go.dedis.ch/dela v0.0.1-alpha h1:328ugttGpJgbU3yKvinPZMgET3SVuHilhvKdyqO7WW8= +go.dedis.ch/dela v0.0.1-alpha/go.mod h1:uFDET0WctO22NxqycVcgZhJM6lH9n1wFiGn2GyMx/+I= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= -go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= -go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg= -go.dedis.ch/kyber/v3 v3.0.14 h1:vnHb/Q5ape/e98oYZdbOrs3CkQ1bUIZFANmOxb/3zyo= -go.dedis.ch/kyber/v3 v3.0.14/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U= -go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= -go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -439,10 +425,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -453,6 +437,8 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -489,10 +475,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -506,9 +490,9 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -519,7 +503,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -534,8 +517,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -545,12 +526,11 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -559,10 +539,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -597,10 +575,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= @@ -635,38 +611,27 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -678,7 +643,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/setup.sh b/setup.sh index 364f5e8..a826d6f 100755 --- a/setup.sh +++ b/setup.sh @@ -1,4 +1,4 @@ #!/bin/sh go install github.com/ethereum/go-ethereum/cmd/geth \ - go.dedis.ch/dela/dkg/pedersen_bn256/dkgcli + go.dedis.ch/f3b/smc/dkgcli diff --git a/dela/dkg/pedersen_bn256/controller/action.go b/smc/dkg/controller/action.go similarity index 62% rename from dela/dkg/pedersen_bn256/controller/action.go rename to smc/dkg/controller/action.go index c0d4e32..2a6c37c 100644 --- a/dela/dkg/pedersen_bn256/controller/action.go +++ b/smc/dkg/controller/action.go @@ -12,18 +12,15 @@ import ( "go.dedis.ch/dela/core/ordering/cosipbft/authority" "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/dkg" - "go.dedis.ch/dela/dkg/ibe" "go.dedis.ch/dela/mino" + "go.dedis.ch/f3b/smc/dkg" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/suites" - "go.dedis.ch/kyber/v3/pairing" "golang.org/x/xerrors" ) // suite is the Kyber suite for Pedersen. -var suite = suites.MustFind("BN256.G2") -var pairingSuite = suite.(pairing.Suite) +var suite = suites.MustFind("Ed25519") const separator = ":" const authconfig = "dkgauthority" @@ -224,142 +221,6 @@ func (a extractAction) Execute(ctx node.Context) error { return nil } -type verifyAction struct{} - -func (a verifyAction) Execute(ctx node.Context) error { - var actor dkg.Actor - - err := ctx.Injector.Resolve(&actor) - if err != nil { - return xerrors.Errorf(resolveActorFailed, err) - } - - label, err := hex.DecodeString(ctx.Flags.String("label")) - if err != nil { - return xerrors.Errorf("failed to decode label: %v", err) - } - - identityBytes, err := hex.DecodeString(ctx.Flags.String("identity")) - if err != nil { - return xerrors.Errorf("failed to decode identity: %v", err) - } - - identity := pairingSuite.G1().Point() - identity.UnmarshalBinary(identityBytes) - - pk, err := actor.GetPublicKey() - if err != nil { - return xerrors.Errorf("failed to query public key: %v", err) - } - - ok, err := ibe.VerifyIdentityOnG2(pairingSuite, pk, identity, label) - if err != nil { - return xerrors.Errorf("failed to verify identity: %v", err) - } - if !ok { - return xerrors.Errorf("invalid identity") - } - - return nil -} - -type encryptAction struct{} - -func (a encryptAction) Execute(ctx node.Context) error { - var actor dkg.Actor - - err := ctx.Injector.Resolve(&actor) - if err != nil { - return xerrors.Errorf(resolveActorFailed, err) - } - - label, err := hex.DecodeString(ctx.Flags.String("label")) - if err != nil { - return xerrors.Errorf("failed to decode label: %v", err) - } - - message, err := hex.DecodeString(ctx.Flags.String("message")) - if err != nil { - return xerrors.Errorf("failed to decode message: %v", err) - } - - pk, err := actor.GetPublicKey() - if err != nil { - return xerrors.Errorf("failed to query public key: %v", err) - } - - ek, err := ibe.DeriveEncryptionKeyOnG2(suite.(pairing.Suite), pk, label) - if err != nil { - return xerrors.Errorf("failed to derive encryption key: %v", err) - } - - ct, err := ibe.EncryptCPAonG2(suite.(pairing.Suite), ek, message) - if err != nil { - return xerrors.Errorf("failed to encrypt: %v", err) - } - - ctBytes, err := ct.Serialize(pairingSuite) - if err != nil { - return xerrors.Errorf("failed to serialize ciphertext: %v", err) - } - - ctHex := hex.EncodeToString(ctBytes) - - fmt.Fprint(ctx.Out, ctHex) - - return nil -} - -type decryptAction struct{} - -func (a decryptAction) Execute(ctx node.Context) error { - var actor dkg.Actor - - err := ctx.Injector.Resolve(&actor) - if err != nil { - return xerrors.Errorf(resolveActorFailed, err) - } - - label, err := hex.DecodeString(ctx.Flags.String("label")) - if err != nil { - return xerrors.Errorf("failed to decode label: %v", err) - } - - ctBytes, err := hex.DecodeString(ctx.Flags.String("ciphertext")) - if err != nil { - return xerrors.Errorf("failed to decode ct: %v", err) - } - - var ct ibe.CiphertextCPA - err = ct.Deserialize(pairingSuite, ctBytes) - if err != nil { - return xerrors.Errorf("failed to unmarshal ct: %v", err) - } - - dkBytes, err := actor.Extract(label) - if err != nil { - return xerrors.Errorf("failed to derive decryption key: %v", err) - } - dk := pairingSuite.G1().Point() - err = dk.UnmarshalBinary(dkBytes) - if err != nil { - return xerrors.Errorf("failed to unmarshal: %v", err) - } - - message, err := ibe.DecryptCPAonG2(suite.(pairing.Suite), dk, &ct) - if err != nil { - return xerrors.Errorf("failed to decrypt: %v", err) - } - - messageHex := hex.EncodeToString(message) - - fmt.Fprint(ctx.Out, messageHex) - - return nil -} - -// reshare - type reshareAction struct{} func (a reshareAction) Execute(ctx node.Context) error { diff --git a/dela/dkg/pedersen_bn256/controller/action_test.go b/smc/dkg/controller/action_test.go similarity index 88% rename from dela/dkg/pedersen_bn256/controller/action_test.go rename to smc/dkg/controller/action_test.go index 13076d2..e23c4d3 100644 --- a/dela/dkg/pedersen_bn256/controller/action_test.go +++ b/smc/dkg/controller/action_test.go @@ -8,9 +8,9 @@ import ( "github.com/stretchr/testify/require" "go.dedis.ch/dela/cli/node" "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/dkg" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" + "go.dedis.ch/f3b/smc/dkg" "go.dedis.ch/kyber/v3" ) @@ -282,54 +282,7 @@ func TestDecodeAuthority_badUnmarshalPubkey(t *testing.T) { } _, _, err := decodeAuthority(ctx, pubKey) - require.EqualError(t, err, "failed to decode pubkey: bn256.G2: not enough data") -} - -func TestEncryptAction_noActor(t *testing.T) { - a := encryptAction{} - - inj := node.NewInjector() - - ctx := node.Context{ - Injector: inj, - } - - err := a.Execute(ctx) - require.EqualError(t, err, "failed to resolve actor, did you call listen?: "+ - "couldn't find dependency for 'dkg.Actor'") -} - -func TestEncryptAction_badMessage(t *testing.T) { - a := encryptAction{} - - inj := node.NewInjector() - inj.Inject(fakeActor{}) - - flags := node.FlagSet{ - "message": "not hex", - } - - ctx := node.Context{ - Injector: inj, - Flags: flags, - } - - err := a.Execute(ctx) - require.Regexp(t, "^failed to decode message:", err.Error()) -} - -func TestDecryptAction_noActor(t *testing.T) { - a := decryptAction{} - - inj := node.NewInjector() - - ctx := node.Context{ - Injector: inj, - } - - err := a.Execute(ctx) - require.EqualError(t, err, "failed to resolve actor, did you call listen?:"+ - " couldn't find dependency for 'dkg.Actor'") + require.EqualError(t, err, "failed to decode pubkey: invalid Ed25519 curve point") } func TestReshareAction_noActor(t *testing.T) { diff --git a/dela/dkg/pedersen_bn256/controller/controller.go b/smc/dkg/controller/controller.go similarity index 69% rename from dela/dkg/pedersen_bn256/controller/controller.go rename to smc/dkg/controller/controller.go index 9c13d85..c55e959 100644 --- a/dela/dkg/pedersen_bn256/controller/controller.go +++ b/smc/dkg/controller/controller.go @@ -4,7 +4,7 @@ import ( "go.dedis.ch/dela" "go.dedis.ch/dela/cli" "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/dkg/pedersen_bn256" + "go.dedis.ch/f3b/smc/dkg/pedersen" "go.dedis.ch/dela/mino" "golang.org/x/xerrors" ) @@ -74,49 +74,6 @@ func (m minimal) SetCommands(builder node.Builder) { Usage: "the signature, encoded in hex", }, ) - sub.SetAction(builder.MakeAction(verifyAction{})) - - sub = cmd.SetSubCommand("encrypt") - sub.SetDescription("encrypt a message. Outputs ciphertext in hex") - sub.SetFlags( - cli.StringFlag{ - Name: "label", - Usage: "the IBE label to encrypt to, encoded in hex", - }, - cli.StringFlag{ - Name: "message", - Usage: "the message to encrypt, encoded in hex", - }, - ) - sub.SetAction(builder.MakeAction(encryptAction{})) - - sub = cmd.SetSubCommand("decrypt") - sub.SetDescription("decrypt a ciphertext. Outputs message in hex") - sub.SetFlags( - cli.StringFlag{ - Name: "label", - Usage: "the IBE label to encrypt to, encoded in hex", - }, - cli.StringFlag{ - Name: "ciphertext", - Usage: "the ciphertext to decrypt, encoded in hex", - }, - ) - sub.SetAction(builder.MakeAction(decryptAction{})) - - sub = cmd.SetSubCommand("reshare") - sub.SetDescription("reshare the DKG secret") - sub.SetFlags( - cli.StringSliceFlag{ - Name: "authority", - Usage: ": string, where each token is encoded in base64", - }, - cli.IntFlag{ - Name: "thresholdNew", - Usage: "the threshold of the new committee", - Required: true, - }, - ) sub.SetAction(builder.MakeAction(reshareAction{})) } diff --git a/dela/dkg/pedersen_bn256/controller/controller_test.go b/smc/dkg/controller/controller_test.go similarity index 95% rename from dela/dkg/pedersen_bn256/controller/controller_test.go rename to smc/dkg/controller/controller_test.go index 9e433ff..3653d1b 100644 --- a/dela/dkg/pedersen_bn256/controller/controller_test.go +++ b/smc/dkg/controller/controller_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/require" "go.dedis.ch/dela/cli/node" - "go.dedis.ch/dela/dkg/pedersen_bn256" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/f3b/smc/dkg/pedersen" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" "golang.org/x/xerrors" ) diff --git a/dela/dkg/dkg.go b/smc/dkg/interfaces.go similarity index 100% rename from dela/dkg/dkg.go rename to smc/dkg/interfaces.go diff --git a/dela/dkg/pedersen_bn256/json/json.go b/smc/dkg/json/json.go similarity index 99% rename from dela/dkg/pedersen_bn256/json/json.go rename to smc/dkg/json/json.go index a967a96..40ef651 100644 --- a/dela/dkg/pedersen_bn256/json/json.go +++ b/smc/dkg/json/json.go @@ -1,7 +1,7 @@ package json import ( - "go.dedis.ch/dela/dkg/pedersen_bn256/types" + "go.dedis.ch/f3b/smc/dkg/pedersen/types" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" @@ -113,7 +113,7 @@ type msgFormat struct { func newMsgFormat() msgFormat { return msgFormat{ - suite: suites.MustFind("BN256.G2"), + suite: suites.MustFind("Ed25519"), } } diff --git a/dela/dkg/pedersen_bn256/json/json_test.go b/smc/dkg/json/json_test.go similarity index 94% rename from dela/dkg/pedersen_bn256/json/json_test.go rename to smc/dkg/json/json_test.go index 86f295b..366fd4b 100644 --- a/dela/dkg/pedersen_bn256/json/json_test.go +++ b/smc/dkg/json/json_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.dedis.ch/dela/dkg/pedersen_bn256/types" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/f3b/smc/dkg/pedersen/types" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" @@ -14,7 +14,7 @@ import ( ) // suite is the Kyber suite for Pedersen. -var suite = suites.MustFind("bn256.G2") +var suite = suites.MustFind("Ed25519") func TestMessageFormat_Start_Encode(t *testing.T) { start := types.NewStart(1, []mino.Address{fake.NewAddress(0)}, []kyber.Point{suite.Point()}) @@ -171,7 +171,7 @@ func TestMessageFormat_Decode(t *testing.T) { _, err = format.Decode(ctx, []byte(`{"Start":{"PublicKeys":[[]]}}`)) require.EqualError(t, err, - "couldn't unmarshal public key: bn256.G2: not enough data") + "couldn't unmarshal public key: invalid Ed25519 curve point") badCtx := serde.WithFactory(ctx, types.AddrKey{}, nil) _, err = format.Decode(badCtx, []byte(`{"Start":{}}`)) @@ -196,7 +196,7 @@ func TestMessageFormat_Decode(t *testing.T) { data = []byte(`{"StartDone":{"PublicKey":[]}}`) _, err = format.Decode(ctx, data) require.EqualError(t, err, - "couldn't unmarshal public key: bn256.G2: not enough data") + "couldn't unmarshal public key: invalid Ed25519 curve point") // Decode sign request messages. data = []byte(`{"ExtractRequest":{}}`) @@ -247,11 +247,11 @@ func TestMessageFormat_Decode_StartResharing(t *testing.T) { _, err = format.Decode(ctx, []byte(`{"StartResharing":{"PubkeysNew":[[]]}}`)) require.EqualError(t, err, - "couldn't unmarshal new public key: bn256.G2: not enough data") + "couldn't unmarshal new public key: invalid Ed25519 curve point") _, err = format.Decode(ctx, []byte(`{"StartResharing":{"PubkeysOld":[[]]}}`)) require.EqualError(t, err, - "couldn't unmarshal old public key: bn256.G2: not enough data") + "couldn't unmarshal old public key: invalid Ed25519 curve point") } func TestMessageFormat_Decode_Reshare(t *testing.T) { @@ -273,13 +273,13 @@ func TestMessageFormat_Decode_Reshare(t *testing.T) { require.Equal(t, expected.GetDeal(), reshare.(types.Reshare).GetDeal()) _, err = format.Decode(ctx, []byte(`{"Reshare":{"PublicCoeff":[[]]}}`)) - require.EqualError(t, err, "couldn't unmarshal public coeff key: bn256.G2: not enough data") + require.EqualError(t, err, "couldn't unmarshal public coeff key: invalid Ed25519 curve point") } // ----------------------------------------------------------------------------- // Utility functions -const testPoint = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" +const testPoint = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" type badPoint struct { kyber.Point diff --git a/dela/dkg/pedersen_bn256/dkg.go b/smc/dkg/pedersen/dkg.go similarity index 99% rename from dela/dkg/pedersen_bn256/dkg.go rename to smc/dkg/pedersen/dkg.go index 9ddd082..e384bbf 100644 --- a/dela/dkg/pedersen_bn256/dkg.go +++ b/smc/dkg/pedersen/dkg.go @@ -5,10 +5,10 @@ import ( "strconv" "sync" - "github.com/dedis/debugtools/channel" + "go.dedis.ch/debugtools/channel" "github.com/rs/zerolog" "go.dedis.ch/dela" - "go.dedis.ch/dela/dkg/pedersen_bn256/types" + "go.dedis.ch/f3b/smc/dkg/pedersen/types" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" diff --git a/dela/dkg/pedersen_bn256/dkg_test.go b/smc/dkg/pedersen/dkg_test.go similarity index 99% rename from dela/dkg/pedersen_bn256/dkg_test.go rename to smc/dkg/pedersen/dkg_test.go index cf05374..b667d9f 100644 --- a/dela/dkg/pedersen_bn256/dkg_test.go +++ b/smc/dkg/pedersen/dkg_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "github.com/dedis/debugtools/channel" + "go.dedis.ch/debugtools/channel" "github.com/rs/zerolog" "github.com/stretchr/testify/require" "go.dedis.ch/dela" - "go.dedis.ch/dela/dkg/pedersen_bn256/types" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/f3b/smc/dkg/pedersen/types" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" diff --git a/dela/dkg/pedersen_bn256/handler.go b/smc/dkg/pedersen/handler.go similarity index 100% rename from dela/dkg/pedersen_bn256/handler.go rename to smc/dkg/pedersen/handler.go diff --git a/dela/dkg/pedersen_bn256/handler_test.go b/smc/dkg/pedersen/handler_test.go similarity index 97% rename from dela/dkg/pedersen_bn256/handler_test.go rename to smc/dkg/pedersen/handler_test.go index 7c6a677..fd6b387 100644 --- a/dela/dkg/pedersen_bn256/handler_test.go +++ b/smc/dkg/pedersen/handler_test.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" ) diff --git a/dela/dkg/pedersen_bn256/pedersen.go b/smc/dkg/pedersen/pedersen.go similarity index 91% rename from dela/dkg/pedersen_bn256/pedersen.go rename to smc/dkg/pedersen/pedersen.go index 1c95330..3e8392d 100644 --- a/dela/dkg/pedersen_bn256/pedersen.go +++ b/smc/dkg/pedersen/pedersen.go @@ -7,17 +7,16 @@ import ( "go.dedis.ch/dela" "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/dkg" + "go.dedis.ch/dela/crypto/ed25519" + "go.dedis.ch/f3b/smc/dkg" "go.dedis.ch/dela/crypto" - "go.dedis.ch/dela/dkg/pedersen_bn256/types" - "go.dedis.ch/dela/internal/tracing" + "go.dedis.ch/f3b/smc/dkg/pedersen/types" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/pairing" "go.dedis.ch/kyber/v3/share" - kyber_bls "go.dedis.ch/kyber/v3/sign/bls" "go.dedis.ch/kyber/v3/sign/tbls" "go.dedis.ch/kyber/v3/suites" "golang.org/x/net/context" @@ -34,8 +33,8 @@ const failedStreamCreation = "failed to create stream: %v" const unexpectedStreamStop = "stream stopped unexpectedly: %v" // suite is the Kyber suite for Pedersen. -var suite = suites.MustFind("bn256.G2") -var pairingSuite = suite.(pairing.Suite) +var suite = suites.MustFind("Ed25519") +var pairingSuite = suites.MustFind("bn256.G2").(pairing.Suite) var ( // protocolNameSetup denotes the value of the protocol span tag associated @@ -70,7 +69,9 @@ type Pedersen struct { func NewPedersen(m mino.Mino) (*Pedersen, kyber.Point) { factory := types.NewMessageFactory(m.GetAddressFactory()) - privkey, pubkey := kyber_bls.NewKeyPair(pairingSuite, suite.RandomStream()) + + privkey := suite.Scalar().Pick(suite.RandomStream()) + pubkey := suite.Point().Mul(privkey, nil) return &Pedersen{ privKey: privkey, @@ -114,7 +115,6 @@ func (a *Actor) Setup(co crypto.CollectiveAuthority, threshold int) (kyber.Point ctx, cancel := context.WithTimeout(context.Background(), setupTimeout) defer cancel() - ctx = context.WithValue(ctx, tracing.ProtocolKey, protocolNameSetup) sender, receiver, err := a.rpc.Stream(ctx, co) if err != nil { @@ -131,12 +131,12 @@ func (a *Actor) Setup(co crypto.CollectiveAuthority, threshold int) (kyber.Point addrs = append(addrs, addrIter.GetNext()) pubkey := pubkeyIter.GetNext() - blsKey, ok := pubkey.(bls.PublicKey) + edKey, ok := pubkey.(ed25519.PublicKey) if !ok { - return nil, xerrors.Errorf("expected bls.PublicKey, got '%T'", pubkey) + return nil, xerrors.Errorf("expected ed25519.PublicKey, got '%T'", pubkey) } - pubkeys = append(pubkeys, blsKey.GetPoint()) + pubkeys = append(pubkeys, edKey.GetPoint()) } message := types.NewStart(threshold, addrs, pubkeys) @@ -199,7 +199,6 @@ func (a *Actor) Extract(label []byte) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), decryptTimeout) defer cancel() - ctx = context.WithValue(ctx, tracing.ProtocolKey, protocolNameDecrypt) sender, receiver, err := a.rpc.Stream(ctx, players) if err != nil { @@ -221,7 +220,7 @@ func (a *Actor) Extract(label []byte) ([]byte, error) { return nil, xerrors.Errorf("failed to send decrypt request: %v", err) } - pubPoly := share.NewPubPoly(suite, nil, a.startRes.Commits) + pubPoly := share.NewPubPoly(pairingSuite.G2(), nil, a.startRes.Commits) var n = len(addrs) var t = a.startRes.getThreshold() @@ -244,7 +243,7 @@ func (a *Actor) Extract(label []byte) ([]byte, error) { sigShares[i] = signReply.Share } - signature, err := tbls.Recover(suite.(pairing.Suite), pubPoly, label, sigShares, t, n) + signature, err := tbls.Recover(pairingSuite, pubPoly, label, sigShares, t, n) if err != nil { return []byte{}, xerrors.Errorf("failed to recover signature: %v", err) } @@ -284,12 +283,13 @@ func (a *Actor) Reshare(co crypto.CollectiveAuthority, thresholdNew int) error { pubkey := pubkeyIter.GetNext() - blsKey, ok := pubkey.(bls.PublicKey) + edKey, ok := pubkey.(ed25519.PublicKey) if !ok { - return xerrors.Errorf("expected bls.PublicKey, got '%T'", pubkey) + return xerrors.Errorf("expected ed25519.PublicKey, got '%T'", pubkey) } - pubkeysNew = append(pubkeysNew, blsKey.GetPoint()) + + pubkeysNew = append(pubkeysNew, edKey.GetPoint()) } // Get the union of the new members and the old members @@ -299,8 +299,6 @@ func (a *Actor) Reshare(co crypto.CollectiveAuthority, thresholdNew int) error { ctx, cancel := context.WithTimeout(context.Background(), resharingTimeout) defer cancel() - ctx = context.WithValue(ctx, tracing.ProtocolKey, protocolNameResharing) - dela.Logger.Info().Msgf("resharing with the following participants: %v", addrsAll) sender, receiver, err := a.rpc.Stream(ctx, players) diff --git a/dela/dkg/pedersen_bn256/pedersen_test.go b/smc/dkg/pedersen/pedersen_test.go similarity index 92% rename from dela/dkg/pedersen_bn256/pedersen_test.go rename to smc/dkg/pedersen/pedersen_test.go index 6578cf9..47b8547 100644 --- a/dela/dkg/pedersen_bn256/pedersen_test.go +++ b/smc/dkg/pedersen/pedersen_test.go @@ -8,12 +8,14 @@ import ( "go.dedis.ch/dela" "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/crypto/bls" - "go.dedis.ch/dela/dkg" - "go.dedis.ch/dela/dkg/pedersen_bn256/types" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/dela/crypto/ed25519" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/mino/minogrpc" "go.dedis.ch/dela/mino/router/tree" + "go.dedis.ch/f3b/smc/dkg" + "go.dedis.ch/f3b/smc/dkg/json" + "go.dedis.ch/f3b/smc/dkg/pedersen/types" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/pairing/bn256" "go.dedis.ch/kyber/v3/share" @@ -44,9 +46,9 @@ func TestPedersen_Setup(t *testing.T) { actor.rpc = rpc _, err = actor.Setup(fakeAuthority, 0) - require.EqualError(t, err, "expected bls.PublicKey, got 'fake.PublicKey'") + require.EqualError(t, err, "expected ed25519.PublicKey, got 'fake.PublicKey'") - fakeAuthority = fake.NewAuthority(2, bls.Generate) + fakeAuthority = fake.NewAuthority(2, ed25519.NewSigner) _, err = actor.Setup(fakeAuthority, 1) require.EqualError(t, err, fake.Err("failed to send start")) @@ -90,8 +92,8 @@ func TestPedersen_GetPublicKey(t *testing.T) { func TestPedersen_Extract(t *testing.T) { priShares := []*share.PriShare{ - &share.PriShare{0, suite.Scalar().Pick(suite.RandomStream())}, - &share.PriShare{1, suite.Scalar().Pick(suite.RandomStream())}, + &share.PriShare{0, bn256.NewSuite().G2().Scalar().Pick(suite.RandomStream())}, + &share.PriShare{1, bn256.NewSuite().G2().Scalar().Pick(suite.RandomStream())}, } priPoly, err := share.RecoverPriPoly(bn256.NewSuite().G2(), priShares, 2, 2) require.NoError(t, err) @@ -140,6 +142,9 @@ func TestPedersen_Scenario(t *testing.T) { // traffic.SaveEvents("events.dot") // }() + // ensure JSON format is registered + var _ json.Ciphertext + oldLog := dela.Logger defer func() { dela.Logger = oldLog @@ -223,7 +228,7 @@ func Test_Reshare_WrongPK(t *testing.T) { co := fake.NewAuthority(1, fake.NewSigner) err := a.Reshare(co, 0) - require.EqualError(t, err, "expected bls.PublicKey, got 'fake.PublicKey'") + require.EqualError(t, err, "expected ed25519.PublicKey, got 'fake.PublicKey'") } func Test_Reshare_BadRPC(t *testing.T) { @@ -334,5 +339,5 @@ type fakeSigner struct { // GetPublicKey implements crypto.Signer func (s fakeSigner) GetPublicKey() crypto.PublicKey { - return bls.NewPublicKeyFromPoint(s.pubkey) + return ed25519.NewPublicKeyFromPoint(s.pubkey) } diff --git a/dela/dkg/pedersen_bn256/resharing_test.go b/smc/dkg/pedersen/resharing_test.go similarity index 99% rename from dela/dkg/pedersen_bn256/resharing_test.go rename to smc/dkg/pedersen/resharing_test.go index 9ca5496..421a551 100644 --- a/dela/dkg/pedersen_bn256/resharing_test.go +++ b/smc/dkg/pedersen/resharing_test.go @@ -10,7 +10,7 @@ import ( "go.dedis.ch/dela/mino/minogrpc" "go.dedis.ch/dela/mino/router/tree" - "go.dedis.ch/dela/dkg" + "go.dedis.ch/f3b/smc/dkg" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/mino/minoch" diff --git a/dela/dkg/pedersen_bn256/state.go b/smc/dkg/pedersen/state.go similarity index 100% rename from dela/dkg/pedersen_bn256/state.go rename to smc/dkg/pedersen/state.go diff --git a/dela/dkg/pedersen_bn256/state_test.go b/smc/dkg/pedersen/state_test.go similarity index 100% rename from dela/dkg/pedersen_bn256/state_test.go rename to smc/dkg/pedersen/state_test.go diff --git a/dela/dkg/pedersen_bn256/types/messages.go b/smc/dkg/pedersen/types/messages.go similarity index 100% rename from dela/dkg/pedersen_bn256/types/messages.go rename to smc/dkg/pedersen/types/messages.go diff --git a/dela/dkg/pedersen_bn256/types/messages_test.go b/smc/dkg/pedersen/types/messages_test.go similarity index 99% rename from dela/dkg/pedersen_bn256/types/messages_test.go rename to smc/dkg/pedersen/types/messages_test.go index c57d1fd..9adcbd3 100644 --- a/dela/dkg/pedersen_bn256/types/messages_test.go +++ b/smc/dkg/pedersen/types/messages_test.go @@ -6,7 +6,7 @@ import ( "testing/quick" "github.com/stretchr/testify/require" - "go.dedis.ch/dela/internal/testing/fake" + "go.dedis.ch/dela/testing/fake" "go.dedis.ch/dela/mino" "go.dedis.ch/dela/serde" "go.dedis.ch/kyber/v3" diff --git a/dela/dkg/pedersen_bn256/dkgcli/dkgcli.go b/smc/dkgcli/main.go similarity index 93% rename from dela/dkg/pedersen_bn256/dkgcli/dkgcli.go rename to smc/dkgcli/main.go index 00389b6..5fe294d 100644 --- a/dela/dkg/pedersen_bn256/dkgcli/dkgcli.go +++ b/smc/dkgcli/main.go @@ -8,7 +8,7 @@ import ( "go.dedis.ch/dela/cli/node" db "go.dedis.ch/dela/core/store/kv/controller" - dkg "go.dedis.ch/dela/dkg/pedersen_bn256/controller" + dkg "go.dedis.ch/f3b/smc/dkg/controller" mino "go.dedis.ch/dela/mino/minogrpc/controller" ) diff --git a/smc/go.mod b/smc/go.mod new file mode 100644 index 0000000..9fa991f --- /dev/null +++ b/smc/go.mod @@ -0,0 +1,51 @@ +module go.dedis.ch/f3b/smc + +go 1.22.1 + +require ( + github.com/rs/zerolog v1.32.0 + github.com/stretchr/testify v1.9.0 + go.dedis.ch/debugtools v0.1.1 + go.dedis.ch/dela v0.0.1-alpha + go.dedis.ch/kyber/v3 v3.0.14 + golang.org/x/net v0.25.0 + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/urfave/cli/v2 v2.27.1 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + go.dedis.ch/fixbuf v1.0.3 // indirect + go.dedis.ch/protobuf v1.0.11 // indirect + go.etcd.io/bbolt v1.3.9 // indirect + go.uber.org/atomic v1.11.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/grpc v1.63.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.dedis.ch/kyber/v3 => ../kyber/ diff --git a/smc/go.sum b/smc/go.sum new file mode 100644 index 0000000..a77afbf --- /dev/null +++ b/smc/go.sum @@ -0,0 +1,131 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw= +github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +go.dedis.ch/debugtools v0.1.1 h1:p/arQ7o9QLZ0JI0soA4CdQh9i3Enphr+O2bs5pWLqLA= +go.dedis.ch/debugtools v0.1.1/go.mod h1:Yp+5sHo3/Pfvl1cq70nhbZs8sv1WlUyk1LWph7xFntg= +go.dedis.ch/dela v0.0.1-alpha h1:328ugttGpJgbU3yKvinPZMgET3SVuHilhvKdyqO7WW8= +go.dedis.ch/dela v0.0.1-alpha/go.mod h1:uFDET0WctO22NxqycVcgZhJM6lH9n1wFiGn2GyMx/+I= +go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= +go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= +go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= +go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= +google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=