// Copyright 2017 Tendermint. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package trust import ( "fmt" "io/ioutil" "os" "testing" "time" "github.com/stretchr/testify/assert" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" ) func TestTrustMetricStoreSaveLoad(t *testing.T) { dir, err := ioutil.TempDir("", "trust_test") if err != nil { panic(err) } defer os.Remove(dir) historyDB := dbm.NewDB("trusthistory", "goleveldb", dir) config := TrustMetricConfig{ TrackingWindow: 5 * time.Minute, IntervalLength: 50 * time.Millisecond, } // 0 peers saved store := NewTrustMetricStore(historyDB, config) store.SetLogger(log.TestingLogger()) store.saveToDB() // Load the data from the file store = NewTrustMetricStore(historyDB, config) store.SetLogger(log.TestingLogger()) store.loadFromDB() // Make sure we still have 0 entries assert.Zero(t, store.Size()) // 100 peers for i := 0; i < 100; i++ { key := fmt.Sprintf("peer_%d", i) tm := store.GetPeerTrustMetric(key) tm.BadEvents(10) tm.GoodEvents(1) } // Check that we have 100 entries and save assert.Equal(t, 100, store.Size()) // Give the metrics time to process the history data time.Sleep(1 * time.Second) // Stop all the trust metrics and save for _, tm := range store.peerMetrics { tm.Stop() } store.saveToDB() // Load the data from the DB store = NewTrustMetricStore(historyDB, config) store.SetLogger(log.TestingLogger()) store.loadFromDB() // Check that we still have 100 peers with imperfect trust values assert.Equal(t, 100, store.Size()) for _, tm := range store.peerMetrics { assert.NotEqual(t, 1.0, tm.TrustValue()) } // Stop all the trust metrics for _, tm := range store.peerMetrics { tm.Stop() } } func TestTrustMetricStoreConfig(t *testing.T) { historyDB := dbm.NewDB("", "memdb", "") config := TrustMetricConfig{ ProportionalWeight: 0.5, IntegralWeight: 0.5, } // Create a store with custom config store := NewTrustMetricStore(historyDB, config) store.SetLogger(log.TestingLogger()) // Have the store make us a metric with the config tm := store.GetPeerTrustMetric("TestKey") // Check that the options made it to the metric assert.Equal(t, 0.5, tm.proportionalWeight) assert.Equal(t, 0.5, tm.integralWeight) tm.Stop() } func TestTrustMetricStoreLookup(t *testing.T) { historyDB := dbm.NewDB("", "memdb", "") store := NewTrustMetricStore(historyDB, DefaultConfig()) store.SetLogger(log.TestingLogger()) // Create 100 peers in the trust metric store for i := 0; i < 100; i++ { key := fmt.Sprintf("peer_%d", i) store.GetPeerTrustMetric(key) // Check that the trust metric was successfully entered ktm := store.peerMetrics[key] assert.NotNil(t, ktm, "Expected to find TrustMetric %s but wasn't there.", key) } // Stop all the trust metrics for _, tm := range store.peerMetrics { tm.Stop() } } func TestTrustMetricStorePeerScore(t *testing.T) { historyDB := dbm.NewDB("", "memdb", "") store := NewTrustMetricStore(historyDB, DefaultConfig()) store.SetLogger(log.TestingLogger()) key := "TestKey" tm := store.GetPeerTrustMetric(key) // This peer is innocent so far first := tm.TrustScore() assert.Equal(t, 100, first) // Add some undesirable events and disconnect tm.BadEvents(1) first = tm.TrustScore() assert.NotEqual(t, 100, first) tm.BadEvents(10) second := tm.TrustScore() if second > first { t.Errorf("A greater number of bad events should lower the trust score") } store.PeerDisconnected(key) // We will remember our experiences with this peer tm = store.GetPeerTrustMetric(key) assert.NotEqual(t, 100, tm.TrustScore()) tm.Stop() } func TestTrustMetricScores(t *testing.T) { tm := NewMetric() // Perfect score tm.GoodEvents(1) score := tm.TrustScore() assert.Equal(t, 100, score) // Less than perfect score tm.BadEvents(10) score = tm.TrustScore() assert.NotEqual(t, 100, score) tm.Stop() } func TestTrustMetricConfig(t *testing.T) { // 7 days window := time.Minute * 60 * 24 * 7 config := TrustMetricConfig{ TrackingWindow: window, IntervalLength: 2 * time.Minute, } tm := NewMetricWithConfig(config) // The max time intervals should be the TrackingWindow / IntervalLen assert.Equal(t, int(config.TrackingWindow/config.IntervalLength), tm.maxIntervals) dc := DefaultConfig() // These weights should still be the default values assert.Equal(t, dc.ProportionalWeight, tm.proportionalWeight) assert.Equal(t, dc.IntegralWeight, tm.integralWeight) tm.Stop() config.ProportionalWeight = 0.3 config.IntegralWeight = 0.7 tm = NewMetricWithConfig(config) // These weights should be equal to our custom values assert.Equal(t, config.ProportionalWeight, tm.proportionalWeight) assert.Equal(t, config.IntegralWeight, tm.integralWeight) tm.Stop() } func TestTrustMetricStopPause(t *testing.T) { // Cause time intervals to pass quickly config := TrustMetricConfig{ TrackingWindow: 5 * time.Minute, IntervalLength: 10 * time.Millisecond, } tm := NewMetricWithConfig(config) // Allow some time intervals to pass and pause time.Sleep(50 * time.Millisecond) tm.Pause() // Give the pause some time to take place time.Sleep(10 * time.Millisecond) first := tm.Copy().numIntervals // Allow more time to pass and check the intervals are unchanged time.Sleep(50 * time.Millisecond) assert.Equal(t, first, tm.numIntervals) // Get the trust metric activated again tm.GoodEvents(5) // Allow some time intervals to pass and stop time.Sleep(50 * time.Millisecond) tm.Stop() // Give the stop some time to take place time.Sleep(10 * time.Millisecond) second := tm.Copy().numIntervals // Allow more time to pass and check the intervals are unchanged time.Sleep(50 * time.Millisecond) assert.Equal(t, second, tm.numIntervals) if first >= second { t.Fatalf("numIntervals should always increase or stay the same over time") } }