2020-03-09 15:35:54 +00:00
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
2021-07-07 20:20:23 +01:00
package reputation_test
2020-03-09 15:35:54 +00:00
import (
2020-08-26 21:26:10 +01:00
"context"
2020-03-09 15:35:54 +00:00
"testing"
"time"
"github.com/stretchr/testify/require"
2020-04-14 17:49:45 +01:00
"go.uber.org/zap"
2020-03-09 15:35:54 +00:00
2020-04-14 17:49:45 +01:00
"storj.io/common/storj"
2020-03-09 15:35:54 +00:00
"storj.io/common/testcontext"
"storj.io/storj/private/testplanet"
2020-04-14 17:49:45 +01:00
"storj.io/storj/satellite"
"storj.io/storj/satellite/audit"
2021-11-08 20:51:04 +00:00
"storj.io/storj/satellite/overlay"
2021-06-23 00:09:39 +01:00
"storj.io/storj/satellite/reputation"
2020-03-09 15:35:54 +00:00
)
2020-08-26 21:26:10 +01:00
// TestAuditSuspendBasic ensures that we can suspend a node using overlayService.SuspendNode and that we can unsuspend a node using overlayservice.UnsuspendNode.
func TestAuditSuspendBasic ( t * testing . T ) {
2020-03-09 15:35:54 +00:00
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 1 , UplinkCount : 0 ,
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
nodeID := planet . StorageNodes [ 0 ] . ID ( )
2021-06-23 00:09:39 +01:00
repService := planet . Satellites [ 0 ] . Reputation . Service
2020-03-09 15:35:54 +00:00
oc := planet . Satellites [ 0 ] . Overlay . DB
node , err := oc . Get ( ctx , nodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . Nil ( t , node . UnknownAuditSuspended )
2020-03-09 15:35:54 +00:00
timeToSuspend := time . Now ( ) . UTC ( ) . Truncate ( time . Second )
2021-07-07 20:20:23 +01:00
err = repService . TestSuspendNodeUnknownAudit ( ctx , nodeID , timeToSuspend )
2020-03-09 15:35:54 +00:00
require . NoError ( t , err )
node , err = oc . Get ( ctx , nodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . NotNil ( t , node . UnknownAuditSuspended )
require . True ( t , node . UnknownAuditSuspended . Equal ( timeToSuspend ) )
2020-03-09 15:35:54 +00:00
2021-07-07 20:20:23 +01:00
err = repService . TestUnsuspendNodeUnknownAudit ( ctx , nodeID )
2020-03-09 15:35:54 +00:00
require . NoError ( t , err )
node , err = oc . Get ( ctx , nodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . Nil ( t , node . UnknownAuditSuspended )
2020-03-09 15:35:54 +00:00
} )
}
2020-08-26 21:26:10 +01:00
// TestAuditSuspendWithUpdateStats ensures that a node goes into suspension node from getting enough unknown audits, and gets removed from getting enough successful audits.
func TestAuditSuspendWithUpdateStats ( t * testing . T ) {
2020-03-09 15:35:54 +00:00
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 1 , UplinkCount : 0 ,
2021-06-23 00:09:39 +01:00
Reconfigure : testplanet . Reconfigure {
Satellite : func ( log * zap . Logger , index int , config * satellite . Config ) {
config . Reputation . AuditWeight = 1
config . Reputation . AuditDQ = 0.6
} ,
} ,
2020-03-09 15:35:54 +00:00
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
nodeID := planet . StorageNodes [ 0 ] . ID ( )
2022-08-11 15:17:12 +01:00
satellite := planet . Satellites [ 0 ]
oc := satellite . Overlay . Service
repService := satellite . Reputation . Service
2020-03-09 15:35:54 +00:00
node , err := oc . Get ( ctx , nodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . Nil ( t , node . UnknownAuditSuspended )
2020-03-09 15:35:54 +00:00
testStartTime := time . Now ( )
// give node one unknown audit - bringing unknown audit rep to 0.5, and suspending node
2021-11-08 20:51:04 +00:00
err = repService . ApplyAudit ( ctx , nodeID , node . Reputation . Status , reputation . AuditUnknown )
2020-03-09 15:35:54 +00:00
require . NoError ( t , err )
2021-06-23 00:09:39 +01:00
reputationInfo , err := repService . Get ( ctx , nodeID )
2020-03-09 15:35:54 +00:00
require . NoError ( t , err )
2020-04-23 15:06:06 +01:00
// expect unknown audit alpha/beta to change and suspended to be set
2021-06-23 00:09:39 +01:00
require . True ( t , reputationInfo . UnknownAuditReputationAlpha < 1 )
require . True ( t , reputationInfo . UnknownAuditReputationBeta > 0 )
require . NotNil ( t , reputationInfo . UnknownAuditSuspended )
require . True ( t , reputationInfo . UnknownAuditSuspended . After ( testStartTime ) )
// expect normal audit alpha/beta remain unchanged
2022-08-11 15:17:12 +01:00
require . EqualValues ( t , reputationInfo . AuditReputationAlpha , satellite . Config . Reputation . InitialAlpha )
require . EqualValues ( t , reputationInfo . AuditReputationBeta , satellite . Config . Reputation . InitialBeta )
2021-06-23 00:09:39 +01:00
// expect node is not disqualified
node , err = oc . Get ( ctx , nodeID )
require . NoError ( t , err )
2020-03-09 15:35:54 +00:00
require . Nil ( t , node . Disqualified )
// give node two successful audits - bringing unknown audit rep to 0.75, and unsuspending node
for i := 0 ; i < 2 ; i ++ {
2021-11-08 20:51:04 +00:00
err = repService . ApplyAudit ( ctx , nodeID , node . Reputation . Status , reputation . AuditSuccess )
2020-03-09 15:35:54 +00:00
require . NoError ( t , err )
}
node , err = oc . Get ( ctx , nodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . Nil ( t , node . UnknownAuditSuspended )
2020-03-09 15:35:54 +00:00
} )
}
2020-08-26 21:26:10 +01:00
// TestAuditSuspendFailedAudit ensures that a node is not suspended for a failed audit.
func TestAuditSuspendFailedAudit ( t * testing . T ) {
2020-03-09 15:35:54 +00:00
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 1 , UplinkCount : 0 ,
2022-08-11 15:17:12 +01:00
Reconfigure : testplanet . Reconfigure {
Satellite : func ( log * zap . Logger , index int , config * satellite . Config ) {
config . Reputation . InitialAlpha = 1.0
config . Reputation . AuditLambda = 1.0
} ,
} ,
2020-03-09 15:35:54 +00:00
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
nodeID := planet . StorageNodes [ 0 ] . ID ( )
oc := planet . Satellites [ 0 ] . Overlay . DB
2021-07-07 20:20:23 +01:00
repService := planet . Satellites [ 0 ] . Reputation . Service
2020-03-09 15:35:54 +00:00
node , err := oc . Get ( ctx , nodeID )
require . NoError ( t , err )
require . Nil ( t , node . Disqualified )
2020-06-10 17:11:25 +01:00
require . Nil ( t , node . UnknownAuditSuspended )
2020-03-09 15:35:54 +00:00
// give node one failed audit - bringing audit rep to 0.5, and disqualifying node
// expect that suspended field and unknown audit reputation remain unchanged
2021-11-08 20:51:04 +00:00
err = repService . ApplyAudit ( ctx , nodeID , node . Reputation . Status , reputation . AuditFailure )
2020-03-09 15:35:54 +00:00
require . NoError ( t , err )
node , err = oc . Get ( ctx , nodeID )
require . NoError ( t , err )
require . NotNil ( t , node . Disqualified )
2020-06-10 17:11:25 +01:00
require . Nil ( t , node . UnknownAuditSuspended )
2021-07-07 20:20:23 +01:00
reputationInfo , err := repService . Get ( ctx , nodeID )
require . NoError ( t , err )
require . EqualValues ( t , reputationInfo . UnknownAuditReputationAlpha , 1 )
require . EqualValues ( t , reputationInfo . UnknownAuditReputationBeta , 0 )
2020-03-09 15:35:54 +00:00
} )
}
2020-04-14 17:49:45 +01:00
2020-08-26 21:26:10 +01:00
// TestAuditSuspendExceedGracePeriod ensures that a node is disqualified when it receives a failing or unknown audit after the grace period expires.
func TestAuditSuspendExceedGracePeriod ( t * testing . T ) {
2020-04-14 17:49:45 +01:00
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 4 , UplinkCount : 0 ,
Reconfigure : testplanet . Reconfigure {
Satellite : func ( log * zap . Logger , index int , config * satellite . Config ) {
2021-06-23 00:09:39 +01:00
config . Reputation . SuspensionGracePeriod = time . Hour
2022-08-11 15:17:12 +01:00
config . Reputation . InitialAlpha = 1
config . Reputation . AuditLambda = 0.95
config . Reputation . AuditDQ = 0.6
2020-04-14 17:49:45 +01:00
} ,
} ,
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
successNodeID := planet . StorageNodes [ 0 ] . ID ( )
failNodeID := planet . StorageNodes [ 1 ] . ID ( )
offlineNodeID := planet . StorageNodes [ 2 ] . ID ( )
unknownNodeID := planet . StorageNodes [ 3 ] . ID ( )
// suspend each node two hours ago (more than grace period)
2021-06-23 00:09:39 +01:00
repService := planet . Satellites [ 0 ] . Reputation . Service
2020-04-14 17:49:45 +01:00
for _ , node := range ( storj . NodeIDList { successNodeID , failNodeID , offlineNodeID , unknownNodeID } ) {
2021-07-07 20:20:23 +01:00
err := repService . TestSuspendNodeUnknownAudit ( ctx , node , time . Now ( ) . Add ( - 2 * time . Hour ) )
2020-04-14 17:49:45 +01:00
require . NoError ( t , err )
}
2021-11-08 20:51:04 +00:00
nodesStatus := make ( map [ storj . NodeID ] overlay . ReputationStatus )
2020-04-14 17:49:45 +01:00
// no nodes should be disqualified
for _ , node := range ( storj . NodeIDList { successNodeID , failNodeID , offlineNodeID , unknownNodeID } ) {
2021-06-23 00:09:39 +01:00
n , err := repService . Get ( ctx , node )
2020-04-14 17:49:45 +01:00
require . NoError ( t , err )
require . Nil ( t , n . Disqualified )
2021-11-08 20:51:04 +00:00
nodesStatus [ node ] = overlay . ReputationStatus {
Disqualified : n . Disqualified ,
UnknownAuditSuspended : n . UnknownAuditSuspended ,
OfflineSuspended : n . OfflineSuspended ,
VettedAt : n . VettedAt ,
}
2020-04-14 17:49:45 +01:00
}
// give one node a successful audit, one a failed audit, one an offline audit, and one an unknown audit
report := audit . Report {
2021-11-08 20:51:04 +00:00
Successes : storj . NodeIDList { successNodeID } ,
Fails : storj . NodeIDList { failNodeID } ,
Offlines : storj . NodeIDList { offlineNodeID } ,
Unknown : storj . NodeIDList { unknownNodeID } ,
NodesReputation : nodesStatus ,
2020-04-14 17:49:45 +01:00
}
auditService := planet . Satellites [ 0 ] . Audit
2020-12-14 12:54:22 +00:00
_ , err := auditService . Reporter . RecordAudits ( ctx , report )
2020-04-14 17:49:45 +01:00
require . NoError ( t , err )
// success and offline nodes should not be disqualified
// fail and unknown nodes should be disqualified
for _ , node := range ( storj . NodeIDList { successNodeID , offlineNodeID } ) {
2021-06-23 00:09:39 +01:00
n , err := repService . Get ( ctx , node )
2020-04-14 17:49:45 +01:00
require . NoError ( t , err )
require . Nil ( t , n . Disqualified )
}
for _ , node := range ( storj . NodeIDList { failNodeID , unknownNodeID } ) {
2021-06-23 00:09:39 +01:00
n , err := repService . Get ( ctx , node )
2020-04-14 17:49:45 +01:00
require . NoError ( t , err )
require . NotNil ( t , n . Disqualified )
}
} )
}
2020-04-23 15:06:06 +01:00
2020-08-26 21:26:10 +01:00
// TestAuditSuspendDQDisabled ensures that a node is not disqualified from suspended mode if the suspension DQ enabled flag is false.
func TestAuditSuspendDQDisabled ( t * testing . T ) {
2020-05-04 17:32:06 +01:00
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 4 , UplinkCount : 0 ,
Reconfigure : testplanet . Reconfigure {
Satellite : func ( log * zap . Logger , index int , config * satellite . Config ) {
2021-07-07 20:20:23 +01:00
config . Reputation . SuspensionGracePeriod = time . Hour
config . Reputation . SuspensionDQEnabled = false
2022-08-11 15:17:12 +01:00
config . Reputation . InitialAlpha = 1
2020-05-04 17:32:06 +01:00
} ,
} ,
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
successNodeID := planet . StorageNodes [ 0 ] . ID ( )
failNodeID := planet . StorageNodes [ 1 ] . ID ( )
offlineNodeID := planet . StorageNodes [ 2 ] . ID ( )
unknownNodeID := planet . StorageNodes [ 3 ] . ID ( )
// suspend each node two hours ago (more than grace period)
oc := planet . Satellites [ 0 ] . DB . OverlayCache ( )
2021-07-07 20:20:23 +01:00
repService := planet . Satellites [ 0 ] . Reputation . Service
2020-05-04 17:32:06 +01:00
for _ , node := range ( storj . NodeIDList { successNodeID , failNodeID , offlineNodeID , unknownNodeID } ) {
2021-07-07 20:20:23 +01:00
err := repService . TestSuspendNodeUnknownAudit ( ctx , node , time . Now ( ) . Add ( - 2 * time . Hour ) )
2020-05-04 17:32:06 +01:00
require . NoError ( t , err )
}
2021-11-08 20:51:04 +00:00
nodesStatus := make ( map [ storj . NodeID ] overlay . ReputationStatus )
2020-05-04 17:32:06 +01:00
// no nodes should be disqualified
for _ , node := range ( storj . NodeIDList { successNodeID , failNodeID , offlineNodeID , unknownNodeID } ) {
n , err := oc . Get ( ctx , node )
require . NoError ( t , err )
require . Nil ( t , n . Disqualified )
2021-11-08 20:51:04 +00:00
nodesStatus [ node ] = overlay . ReputationStatus {
Disqualified : n . Disqualified ,
UnknownAuditSuspended : n . UnknownAuditSuspended ,
OfflineSuspended : n . OfflineSuspended ,
VettedAt : n . Reputation . Status . VettedAt ,
}
2020-05-04 17:32:06 +01:00
}
// give one node a successful audit, one a failed audit, one an offline audit, and one an unknown audit
report := audit . Report {
2021-11-08 20:51:04 +00:00
Successes : storj . NodeIDList { successNodeID } ,
Fails : storj . NodeIDList { failNodeID } ,
Offlines : storj . NodeIDList { offlineNodeID } ,
Unknown : storj . NodeIDList { unknownNodeID } ,
NodesReputation : nodesStatus ,
2020-05-04 17:32:06 +01:00
}
auditService := planet . Satellites [ 0 ] . Audit
2020-12-14 12:54:22 +00:00
_ , err := auditService . Reporter . RecordAudits ( ctx , report )
2020-05-04 17:32:06 +01:00
require . NoError ( t , err )
// successful node should not be suspended or disqualified
n , err := oc . Get ( ctx , successNodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . Nil ( t , n . UnknownAuditSuspended )
2020-05-04 17:32:06 +01:00
require . Nil ( t , n . Disqualified )
// failed node should not be suspended but should be disqualified
// (disqualified because of a failed audit, not because of exceeding suspension grace period)
n , err = oc . Get ( ctx , failNodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . Nil ( t , n . UnknownAuditSuspended )
2020-05-04 17:32:06 +01:00
require . NotNil ( t , n . Disqualified )
2020-10-22 22:02:48 +01:00
// offline node should not be suspended or disqualified
2020-05-04 17:32:06 +01:00
n , err = oc . Get ( ctx , offlineNodeID )
require . NoError ( t , err )
2020-10-22 22:02:48 +01:00
require . Nil ( t , n . UnknownAuditSuspended )
2020-05-04 17:32:06 +01:00
require . Nil ( t , n . Disqualified )
// unknown node should still be suspended but not disqualified
n , err = oc . Get ( ctx , unknownNodeID )
require . NoError ( t , err )
2020-06-10 17:11:25 +01:00
require . NotNil ( t , n . UnknownAuditSuspended )
2020-05-04 17:32:06 +01:00
require . Nil ( t , n . Disqualified )
} )
}
2021-02-25 20:00:00 +00:00
// TestOfflineAuditSuspensionDisabled ensures that a node is not suspended if the offline suspension enabled flag is false.
func TestOfflineAuditSuspensionDisabled ( t * testing . T ) {
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 1 , UplinkCount : 0 ,
Reconfigure : testplanet . Reconfigure {
Satellite : func ( log * zap . Logger , index int , config * satellite . Config ) {
2021-07-07 20:20:23 +01:00
config . Reputation . AuditHistory . OfflineSuspensionEnabled = false
config . Reputation . AuditHistory . WindowSize = time . Hour
config . Reputation . AuditHistory . TrackingPeriod = 2 * time . Hour
2021-02-25 20:00:00 +00:00
} ,
} ,
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
nodeID := planet . StorageNodes [ 0 ] . ID ( )
oc := planet . Satellites [ 0 ] . Overlay . DB
2021-07-07 20:20:23 +01:00
reputationdb := planet . Satellites [ 0 ] . DB . Reputation ( )
config := planet . Satellites [ 0 ] . Config . Reputation . AuditHistory
2021-02-25 20:00:00 +00:00
node , err := oc . Get ( ctx , nodeID )
require . NoError ( t , err )
require . Nil ( t , node . OfflineSuspended )
require . Nil ( t , node . OfflineUnderReview )
require . Nil ( t , node . Disqualified )
windowSize := config . WindowSize
trackingPeriodLength := config . TrackingPeriod
currentWindow := time . Now ( )
2021-07-07 20:20:23 +01:00
req := reputation . UpdateRequest {
NodeID : nodeID ,
AuditOutcome : reputation . AuditOffline ,
2022-05-07 02:34:56 +01:00
Config : reputation . Config {
AuditHistory : config ,
} ,
2021-07-07 20:20:23 +01:00
}
2021-02-25 20:00:00 +00:00
// check that unsuspended node does not get suspended
for i := 0 ; i <= int ( trackingPeriodLength / windowSize ) ; i ++ {
2021-11-08 20:51:04 +00:00
_ , err = reputationdb . Update ( ctx , req , currentWindow )
2021-02-25 20:00:00 +00:00
require . NoError ( t , err )
currentWindow = currentWindow . Add ( windowSize )
}
2021-07-07 20:20:23 +01:00
reputationInfo , err := reputationdb . Get ( ctx , nodeID )
2021-02-25 20:00:00 +00:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . Nil ( t , reputationInfo . OfflineSuspended )
require . Nil ( t , reputationInfo . UnderReview )
require . Less ( t , reputationInfo . OnlineScore , config . OfflineThreshold )
2021-02-25 20:00:00 +00:00
// check that enabling flag suspends the node
req . AuditHistory . OfflineSuspensionEnabled = true
2021-11-08 20:51:04 +00:00
_ , err = reputationdb . Update ( ctx , req , currentWindow )
2021-02-25 20:00:00 +00:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2021-02-25 20:00:00 +00:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . NotNil ( t , reputationInfo . OfflineSuspended )
require . NotNil ( t , reputationInfo . UnderReview )
require . Less ( t , reputationInfo . OnlineScore , config . OfflineThreshold )
2021-02-25 20:00:00 +00:00
// check that disabling flag clears suspension and under review
req . AuditHistory . OfflineSuspensionEnabled = false
2021-11-08 20:51:04 +00:00
_ , err = reputationdb . Update ( ctx , req , currentWindow )
2020-04-23 15:06:06 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-04-23 15:06:06 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . Less ( t , reputationInfo . OnlineScore , config . OfflineThreshold )
require . Nil ( t , reputationInfo . OfflineSuspended )
require . Nil ( t , reputationInfo . UnderReview )
2020-04-23 15:06:06 +01:00
} )
}
2020-08-26 21:26:10 +01:00
// TestOfflineSuspend tests that a node enters offline suspension and "under review" when online score passes below threshold.
// The node should be able to enter and exit suspension while remaining under review.
// The node should be reinstated if it has a good online score after the review period.
2020-10-15 17:00:08 +01:00
// The node should be disqualified if it has a bad online score after the review period.
2020-08-26 21:26:10 +01:00
func TestOfflineSuspend ( t * testing . T ) {
testplanet . Run ( t , testplanet . Config {
SatelliteCount : 1 , StorageNodeCount : 1 , UplinkCount : 0 ,
2021-07-07 20:20:23 +01:00
Reconfigure : testplanet . Reconfigure {
Satellite : func ( log * zap . Logger , index int , config * satellite . Config ) {
config . Reputation . AuditHistory . OfflineSuspensionEnabled = false
config . Reputation . AuditHistory . WindowSize = time . Hour
config . Reputation . AuditHistory . TrackingPeriod = 2 * time . Hour
} ,
} ,
2020-08-26 21:26:10 +01:00
} , func ( t * testing . T , ctx * testcontext . Context , planet * testplanet . Planet ) {
nodeID := planet . StorageNodes [ 0 ] . ID ( )
2021-07-07 20:20:23 +01:00
reputationdb := planet . Satellites [ 0 ] . DB . Reputation ( )
oc := planet . Satellites [ 0 ] . DB . OverlayCache ( )
2020-08-26 21:26:10 +01:00
node , err := oc . Get ( ctx , nodeID )
require . NoError ( t , err )
require . Nil ( t , node . OfflineSuspended )
require . Nil ( t , node . Disqualified )
2021-07-07 20:20:23 +01:00
updateReq := reputation . UpdateRequest {
2020-09-29 23:08:48 +01:00
NodeID : nodeID ,
2021-07-07 20:20:23 +01:00
AuditOutcome : reputation . AuditOffline ,
2022-05-07 02:34:56 +01:00
Config : reputation . Config {
AuditHistory : reputation . AuditHistoryConfig {
WindowSize : time . Hour ,
TrackingPeriod : 2 * time . Hour ,
GracePeriod : time . Hour ,
OfflineThreshold : 0.6 ,
OfflineDQEnabled : true ,
OfflineSuspensionEnabled : true ,
} ,
AuditLambda : 0.95 ,
AuditWeight : 1 ,
AuditDQ : 0.6 ,
2022-08-11 15:17:12 +01:00
InitialAlpha : 1000 ,
InitialBeta : 0 ,
UnknownAuditDQ : 0.6 ,
UnknownAuditLambda : 0.95 ,
2022-05-07 02:34:56 +01:00
SuspensionGracePeriod : time . Hour ,
SuspensionDQEnabled : true ,
AuditCount : 0 ,
2020-09-29 23:08:48 +01:00
} ,
}
// give node an offline audit
// node's score is still 1 until its first window is complete
2020-08-26 21:26:10 +01:00
nextWindowTime := time . Now ( )
2021-11-08 20:51:04 +00:00
_ , err = reputationdb . Update ( ctx , updateReq , nextWindowTime )
2020-09-29 23:08:48 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err := reputationdb . Get ( ctx , nodeID )
2020-09-29 23:08:48 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . Nil ( t , reputationInfo . OfflineSuspended )
require . Nil ( t , reputationInfo . UnderReview )
require . Nil ( t , reputationInfo . Disqualified )
require . EqualValues ( t , 1 , reputationInfo . OnlineScore )
2020-09-29 23:08:48 +01:00
nextWindowTime = nextWindowTime . Add ( updateReq . AuditHistory . WindowSize )
// node now has one full window, so its score should be 0
// should not be suspended or DQ since it only has 1 window out of 2 for tracking period
2021-11-08 20:51:04 +00:00
_ , err = reputationdb . Update ( ctx , updateReq , nextWindowTime )
2020-09-29 23:08:48 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-09-29 23:08:48 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . Nil ( t , reputationInfo . OfflineSuspended )
require . Nil ( t , reputationInfo . UnderReview )
require . Nil ( t , reputationInfo . Disqualified )
require . EqualValues ( t , 0 , reputationInfo . OnlineScore )
2020-09-29 23:08:48 +01:00
nextWindowTime = nextWindowTime . Add ( updateReq . AuditHistory . WindowSize )
2021-07-07 20:20:23 +01:00
nextWindowTime , err = setOnlineScore ( ctx , updateReq , 0.5 , time . Hour , nextWindowTime , reputationdb )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
// node should be offline suspended and under review
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . NotNil ( t , reputationInfo . OfflineSuspended )
require . NotNil ( t , reputationInfo . UnderReview )
2020-08-26 21:26:10 +01:00
require . Nil ( t , node . Disqualified )
2021-07-07 20:20:23 +01:00
require . EqualValues ( t , 0.5 , reputationInfo . OnlineScore )
2020-08-26 21:26:10 +01:00
// set online score to be good, but use a long grace period so that node remains under review
2021-07-07 20:20:23 +01:00
nextWindowTime , err = setOnlineScore ( ctx , updateReq , 1 , 100 * time . Hour , nextWindowTime , reputationdb )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . Nil ( t , reputationInfo . OfflineSuspended )
require . NotNil ( t , reputationInfo . UnderReview )
require . Nil ( t , reputationInfo . Disqualified )
oldUnderReview := reputationInfo . UnderReview
require . EqualValues ( t , 1 , reputationInfo . OnlineScore )
2020-08-26 21:26:10 +01:00
// suspend again, under review should be the same
2021-07-07 20:20:23 +01:00
nextWindowTime , err = setOnlineScore ( ctx , updateReq , 0.5 , 100 * time . Hour , nextWindowTime , reputationdb )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . NotNil ( t , reputationInfo . OfflineSuspended )
require . NotNil ( t , reputationInfo . UnderReview )
2020-08-26 21:26:10 +01:00
require . Nil ( t , node . Disqualified )
2021-07-07 20:20:23 +01:00
require . Equal ( t , oldUnderReview , reputationInfo . UnderReview )
require . EqualValues ( t , 0.5 , reputationInfo . OnlineScore )
2020-08-26 21:26:10 +01:00
// node will exit review after grace period + 1 tracking window, so set grace period to be time since put under review
// subtract one hour so that review window ends when setOnlineScore adds the last window
2021-07-07 20:20:23 +01:00
gracePeriod := nextWindowTime . Sub ( * reputationInfo . UnderReview ) - time . Hour
nextWindowTime , err = setOnlineScore ( ctx , updateReq , 1 , gracePeriod , nextWindowTime , reputationdb )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . Nil ( t , reputationInfo . OfflineSuspended )
require . Nil ( t , reputationInfo . UnderReview )
require . Nil ( t , reputationInfo . Disqualified )
require . EqualValues ( t , 1 , reputationInfo . OnlineScore )
2020-08-26 21:26:10 +01:00
// put into suspension and under review again
2021-07-07 20:20:23 +01:00
nextWindowTime , err = setOnlineScore ( ctx , updateReq , 0.5 , 100 * time . Hour , nextWindowTime , reputationdb )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . NotNil ( t , reputationInfo . OfflineSuspended )
require . NotNil ( t , reputationInfo . UnderReview )
2020-08-26 21:26:10 +01:00
require . Nil ( t , node . Disqualified )
2021-07-07 20:20:23 +01:00
require . EqualValues ( t , 0.5 , reputationInfo . OnlineScore )
2020-08-26 21:26:10 +01:00
// if grace period + 1 tracking window passes and online score is still bad, expect node to be DQed
2021-07-07 20:20:23 +01:00
_ , err = setOnlineScore ( ctx , updateReq , 0.5 , 0 , nextWindowTime , reputationdb )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
reputationInfo , err = reputationdb . Get ( ctx , nodeID )
2020-08-26 21:26:10 +01:00
require . NoError ( t , err )
2021-07-07 20:20:23 +01:00
require . NotNil ( t , reputationInfo . OfflineSuspended )
require . NotNil ( t , reputationInfo . UnderReview )
require . NotNil ( t , reputationInfo . Disqualified )
require . EqualValues ( t , 0.5 , reputationInfo . OnlineScore )
2020-08-26 21:26:10 +01:00
} )
}
2021-07-07 20:20:23 +01:00
func setOnlineScore ( ctx context . Context , reqPtr reputation . UpdateRequest , desiredScore float64 , gracePeriod time . Duration , startTime time . Time , reputationdb reputation . DB ) ( nextWindowTime time . Time , err error ) {
2020-08-26 21:26:10 +01:00
// for our tests, we are only using values of 1 and 0.5, so two audits per window is sufficient
totalAudits := 2
onlineAudits := int ( float64 ( totalAudits ) * desiredScore )
nextWindowTime = startTime
2020-09-29 23:08:48 +01:00
windowsPerTrackingPeriod := int ( reqPtr . AuditHistory . TrackingPeriod . Seconds ( ) / reqPtr . AuditHistory . WindowSize . Seconds ( ) )
2020-08-26 21:26:10 +01:00
for window := 0 ; window < windowsPerTrackingPeriod + 1 ; window ++ {
for i := 0 ; i < totalAudits ; i ++ {
2021-07-07 20:20:23 +01:00
updateReq := reqPtr
updateReq . AuditOutcome = reputation . AuditSuccess
2020-08-26 21:26:10 +01:00
if i >= onlineAudits {
2021-07-07 20:20:23 +01:00
updateReq . AuditOutcome = reputation . AuditOffline
2020-08-26 21:26:10 +01:00
}
2020-09-29 23:08:48 +01:00
updateReq . AuditHistory . GracePeriod = gracePeriod
2021-11-08 20:51:04 +00:00
_ , err = reputationdb . Update ( ctx , updateReq , nextWindowTime )
2021-07-07 20:20:23 +01:00
if err != nil {
return nextWindowTime , err
}
2020-08-26 21:26:10 +01:00
}
// increment nextWindowTime so in the next iteration, we are adding to a different window
2020-09-29 23:08:48 +01:00
nextWindowTime = nextWindowTime . Add ( reqPtr . AuditHistory . WindowSize )
2020-08-26 21:26:10 +01:00
}
return nextWindowTime , err
}