Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial peer scoring implementation to detect problematic nodes #36

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ac952ea
Add basic integration test using the simulator
devonh Jan 28, 2022
7b02958
Add basic integration test for snake neighbours
devonh Jan 28, 2022
3a055b0
Fix event api race condition when first subscribing to a node
devonh Jan 29, 2022
51e0672
Move integration tests to live alongside router code
devonh Jan 29, 2022
1d8ddb6
Refactor integration tests to be able to reuse validation functions
devonh Jan 29, 2022
f40c3d1
Add simple adversary integration tests
devonh Jan 30, 2022
1eabba5
Annotate integration test functions with param names
devonh Jan 30, 2022
54033d7
Add null events to sim command interface
devonh Jan 31, 2022
f153699
Add ping/pong packet types to drop packets filter
devonh Jan 31, 2022
61d48ce
Update router filter logging for more details
devonh Jan 31, 2022
4f9086f
Don't update sim graph if not being used
devonh Jan 31, 2022
67e9c1f
Add logging on successful sim snek ping/pong
devonh Jan 31, 2022
58555a2
Add adversarial integration tests
devonh Jan 31, 2022
a7133f7
Allow for running specific test cases since using run isn't working
devonh Feb 10, 2022
d0b3cd8
Rename adversary test to better align with new test parameters
devonh Feb 10, 2022
02a66ca
Fix bug where bootstrap ack was handled incorrectly (#31)
devonh Feb 23, 2022
32cd2d9
Fix buffer size checks for un/marshal snake bootstrap (#32)
devonh Feb 24, 2022
70b73bb
Initial prototype to assist nodes failing to bootstrap
devonh Feb 26, 2022
b01af6d
Test out peer scoring on bootstrap setup frames
devonh Mar 8, 2022
23b411b
Add peer scoring to failing bootstrap frames
devonh Mar 8, 2022
3a583ce
Rearrange bootstrap ack signature verification
devonh Mar 8, 2022
624c122
Limit number of neglected nodes being tracked
devonh Mar 9, 2022
be09fc3
Fix scoring for acknowledged bootstrap frames
devonh Mar 9, 2022
55dcc7d
Remove exploit due to manipulating bootstrap attempt count
devonh Mar 9, 2022
16648c4
Add ack settling time to prevent preemptive peer scoring
devonh Mar 9, 2022
a6578b7
Cleanup peer scoring reset call sites
devonh Mar 9, 2022
03f3fc8
Swap attempt cout for single failing byte
devonh Mar 22, 2022
b5de1e3
Update snake frame size check
devonh Mar 23, 2022
9a8eba7
Switch to per peer score accumulation
devonh Mar 24, 2022
244d399
Silently drop duplicate frames with bootstrap failures
devonh Mar 26, 2022
06f1be7
Ensure peer is still running before resetting the score accumulator
devonh Mar 26, 2022
fd97796
Don't crash simulator when root node cannot be interpreted
devonh Mar 26, 2022
ea63fa2
Add back in logging when negative peer scoring is being applied
devonh Mar 26, 2022
b596ff7
Add simulator sequences for testing sybil attacks
devonh Mar 26, 2022
8a41db8
Merge main into resilient-snek
devonh Mar 26, 2022
a4fbd6c
Merge branch 'main' into resilient-snek
neilalexander Mar 29, 2022
e071b42
Disable peer scoring by default behind a feature flag
devonh Apr 26, 2022
8225a1b
Merge remote-tracking branch 'origin/main' into resilient-snek
devonh Apr 26, 2022
f7a7f0d
Cleanup unused code
devonh Apr 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add basic integration test using the simulator
  • Loading branch information
devonh committed Jan 28, 2022
commit ac952eae418163b52453682d7bf2dc31b3323800
92 changes: 92 additions & 0 deletions cmd/pineconesim/tests/basic_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package integration

import (
"log"
"testing"
"time"

"github.com/matrix-org/pinecone/cmd/pineconesim/simulator"
)

type TreeValidationState struct {
roots map[string]string
correctRoot string
}

func TestNodesAgreeOnCorrectTreeRoot(t *testing.T) {
t.Parallel()
// Arrange
scenario := NewScenarioFixture(t)
nodes := []string{"Alice", "Bob"}
scenario.AddStandardNodes(nodes)

// Act
scenario.AddPeerConnections([]NodePair{NodePair{"Alice", "Bob"}})

// Assert
stateCapture := func(state simulator.State) interface{} {
lastRoots := make(map[string]string)
lastRoots["Alice"] = state.Nodes["Alice"].Announcement.Root
lastRoots["Bob"] = state.Nodes["Bob"].Announcement.Root

correctRoot := ""
if state.Nodes["Alice"].PeerID > state.Nodes["Bob"].PeerID {
correctRoot = "Alice"
} else {
correctRoot = "Bob"
}

return TreeValidationState{roots: lastRoots, correctRoot: correctRoot}
}

nodesAgreeOnCorrectTreeRoot := func(prevState interface{}, event simulator.SimEvent) (interface{}, EventHandlerResult) {
switch state := prevState.(type) {
case TreeValidationState:
action := DoNothing
switch e := event.(type) {
case simulator.TreeRootAnnUpdate:
if state.roots[e.Node] != e.Root {
log.Printf("Root changed for %s to %s", e.Node, e.Root)
state.roots[e.Node] = e.Root
} else {
log.Printf("Got duplicate root info for %s", e.Node)
break
}

if state.roots["Alice"] == state.roots["Bob"] {
log.Println("Nodes agree on root")
if state.roots["Alice"] == state.correctRoot {
log.Println("The agreed root is the correct root")
action = StartSettlingTimer
} else {
log.Println("The agreed root is not the correct root")
action = StopSettlingTimer
}
} else {
log.Println("Nodes disagree on root")
action = StopSettlingTimer
}
}

return state, action
}

return prevState, StopSettlingTimer
}

scenario.Validate(stateCapture, nodesAgreeOnCorrectTreeRoot, 2*time.Second, 5*time.Second)
}
151 changes: 151 additions & 0 deletions cmd/pineconesim/tests/scenario_fixture.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package integration

import (
"fmt"
"log"
"os"
"testing"
"time"

"github.com/matrix-org/pinecone/cmd/pineconesim/simulator"
)

type EventHandlerResult int

const (
DoNothing EventHandlerResult = iota
StopSettlingTimer
StartSettlingTimer
)

type InitialStateCapture func(state simulator.State) interface{}
type EventHandler func(prevState interface{}, event simulator.SimEvent) (interface{}, EventHandlerResult)

type NodePair struct {
A string
B string
}

type ScenarioFixture struct {
t *testing.T
log *log.Logger
sim *simulator.Simulator
}

func NewScenarioFixture(t *testing.T) ScenarioFixture {
log := log.New(os.Stdout, "\u001b[36m***\u001b[0m ", 0)
useSockets := false
runPing := false
acceptCommands := true
simulator := simulator.NewSimulator(log, useSockets, runPing, acceptCommands)

return ScenarioFixture{
t: t,
log: log,
sim: simulator,
}
}

func (s *ScenarioFixture) AddStandardNodes(nodes []string) {
for _, node := range nodes {
cmd := simulator.AddNode{
Node: node,
NodeType: simulator.DefaultNode,
}
cmd.Run(s.log, s.sim)
}
}

func (s *ScenarioFixture) AddAdversaryNodes(nodes []string) {
for _, node := range nodes {
cmd := simulator.AddNode{
Node: node,
NodeType: simulator.GeneralAdversaryNode,
}
cmd.Run(s.log, s.sim)
}
}

func (s *ScenarioFixture) AddPeerConnections(conns []NodePair) {
for _, pair := range conns {
cmd := simulator.AddPeer{
Node: pair.A,
Peer: pair.B,
}
cmd.Run(s.log, s.sim)
}
}

func (s *ScenarioFixture) SubscribeToSimState(ch chan simulator.SimEvent) simulator.State {
return s.sim.State.Subscribe(ch)
}

func (s *ScenarioFixture) Validate(initialState InitialStateCapture, eventHandler EventHandler, settlingTime time.Duration, timeout time.Duration) {
testTimeout := time.NewTimer(timeout)
defer testTimeout.Stop()

quit := make(chan bool)
output := make(chan string)
go assertState(s, initialState, eventHandler, quit, output, settlingTime)

failed := false

select {
case <-testTimeout.C:
failed = true
quit <- true
case <-output:
log.Println("Test passed")
}

if failed {
state := <-output
s.t.Fatalf("Test timeout reached. Current State: %s", state)
}
}

func assertState(scenario *ScenarioFixture, stateCapture InitialStateCapture, eventHandler EventHandler, quit chan bool, output chan string, settlingTime time.Duration) {
settlingTimer := time.NewTimer(settlingTime)
settlingTimer.Stop()

simUpdates := make(chan simulator.SimEvent)
state := scenario.SubscribeToSimState(simUpdates)

prevState := stateCapture(state)

for {
select {
case <-quit:
output <- fmt.Sprintf("Root Map: %v", prevState)
return
case <-settlingTimer.C:
output <- "PASS"
case event := <-simUpdates:
newState, newResult := eventHandler(prevState, event)
switch newResult {
case StartSettlingTimer:
log.Println("Starting settling timer")
settlingTimer.Reset(settlingTime)
case StopSettlingTimer:
log.Println("Stopping settling timer")
settlingTimer.Stop()
}

prevState = newState
}
}
}