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

[ALSP] Synchronization Engine SyncRequest spam detection (Permissionless-related engine level spam detection) #4590

Merged
merged 40 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
45557b0
validateSyncRequestForALSP() empty implementation
gomisha Aug 1, 2023
21130f8
empty validate*ForALSP() methods
gomisha Aug 2, 2023
ad83ebf
split TestOnSyncRequest() into 3 tests
gomisha Aug 12, 2023
6a9976d
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 12, 2023
046b6e0
rename TestOnSyncRequest_ tests for clarity
gomisha Aug 15, 2023
f5b3684
start load test WIP TestOnSyncRequest_HigherThanReceiver_OutsideToler…
gomisha Aug 15, 2023
5d9dfbe
initial loader
gomisha Aug 16, 2023
41ed223
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 16, 2023
34fd2e7
load test using full engine
gomisha Aug 16, 2023
0a486bb
test reporting SyncRequest misbehavior
gomisha Aug 16, 2023
92eb5ae
clean up
gomisha Aug 16, 2023
abfe232
potential spam misbehavior report
gomisha Aug 16, 2023
c625aba
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 17, 2023
3bab944
misbehavior report randomizer WIP
gomisha Aug 17, 2023
80c51b6
misbehavior report randomizer - initial implementation
gomisha Aug 17, 2023
045ae40
removed mini load tests
gomisha Aug 18, 2023
2addde9
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 18, 2023
7007422
load test WIP
gomisha Aug 18, 2023
7837dfb
lint fix - Randomizer returns error
gomisha Aug 18, 2023
b4b0849
load test implemented
gomisha Aug 18, 2023
c3fc495
probability factor WIP
gomisha Aug 18, 2023
188f55e
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 21, 2023
29e9889
Merge branch 'misha/6812-alsp-engine-spam-detection' of https://githu…
gomisha Aug 21, 2023
f217595
using synchronization.Alsp probability factor
gomisha Aug 21, 2023
33aa3f9
remove Randomizer interface
gomisha Aug 21, 2023
500d415
load test refactoring - WIP
gomisha Aug 22, 2023
82aa009
load test refactor, more generic for different loads
gomisha Aug 22, 2023
759570e
const probabilityFactorMultiplier
gomisha Aug 22, 2023
918c623
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 23, 2023
73ce6b6
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 23, 2023
3026377
rename to SpamReportConfig
gomisha Aug 24, 2023
80bc0cf
moved spam config to config.go
gomisha Aug 24, 2023
8b7341c
clean up
gomisha Aug 24, 2023
3e15e0a
load test for probability factor=1.0
gomisha Aug 24, 2023
dbb0db4
clean up
gomisha Aug 24, 2023
6e9dfb4
Merge branch 'master' into misha/6812-alsp-engine-spam-detection
gomisha Aug 28, 2023
9985772
clean up logging
gomisha Aug 28, 2023
1aa198a
add network security logging flag
gomisha Aug 28, 2023
2967f4a
removed potential-spam misbehavior type
gomisha Aug 28, 2023
bdb5899
renamed to SpamDetectionConfig; SpamDetectionConfig received by New()
gomisha Aug 28, 2023
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
16 changes: 16 additions & 0 deletions engine/common/synchronization/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,19 @@ func WithScanInterval(interval time.Duration) OptionFunc {
cfg.ScanInterval = interval
}
}

// spamProbabilityMultiplier is used to convert probability factor to an integer as well as a maximum value - 1
// random number that can be generated by the random number generator.
const spamProbabilityMultiplier = 1001

type SpamReportConfig struct {
syncRequestProbability float32
}

func NewSpamReportConfig() *SpamReportConfig {
return &SpamReportConfig{
// create misbehavior report 1/100 message requests
// TODO: make this configurable as a start up flag for the engine
syncRequestProbability: 0.01,
}
}
110 changes: 107 additions & 3 deletions engine/common/synchronization/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import (
"github.com/onflow/flow-go/module/irrecoverable"
"github.com/onflow/flow-go/module/metrics"
"github.com/onflow/flow-go/network"
"github.com/onflow/flow-go/network/alsp"
"github.com/onflow/flow-go/network/channels"
"github.com/onflow/flow-go/state/protocol"
"github.com/onflow/flow-go/storage"
"github.com/onflow/flow-go/utils/logging"
"github.com/onflow/flow-go/utils/rand"
)

Expand Down Expand Up @@ -54,7 +56,8 @@ type Engine struct {
core module.SyncCore
participantsProvider module.IdentifierProvider

requestHandler *RequestHandler // component responsible for handling requests
requestHandler *RequestHandler // component responsible for handling requests
spamReportConfig *SpamReportConfig

pendingSyncResponses engine.MessageStore // message store for *message.SyncResponse
pendingBlockResponses engine.MessageStore // message store for *message.BlockResponse
Expand Down Expand Up @@ -105,6 +108,7 @@ func New(
pollInterval: opt.PollInterval,
scanInterval: opt.ScanInterval,
participantsProvider: participantsProvider,
spamReportConfig: NewSpamReportConfig(),
gomisha marked this conversation as resolved.
Show resolved Hide resolved
}

// register the engine with the network layer and store the conduit
Expand Down Expand Up @@ -202,9 +206,53 @@ func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, eve
// - All other errors are potential symptoms of internal state corruption or bugs (fatal).
func (e *Engine) process(channel channels.Channel, originID flow.Identifier, event interface{}) error {
switch event.(type) {
case *messages.RangeRequest, *messages.BatchRequest, *messages.SyncRequest:
case *messages.BatchRequest:
report, misbehavior := e.validateBatchRequestForALSP(channel, originID, event)
if misbehavior {
e.con.ReportMisbehavior(report) // report misbehavior to ALSP
e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid batch request from %x: %v", originID[:], misbehavior)
gomisha marked this conversation as resolved.
Show resolved Hide resolved
e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageBatchRequest)
return nil
}
return e.requestHandler.Process(channel, originID, event)
case *messages.RangeRequest:
report, misbehavior := e.validateRangeRequestForALSP(channel, originID, event)
if misbehavior {
e.con.ReportMisbehavior(report) // report misbehavior to ALSP
e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid range request from %x: %v", originID[:], misbehavior)
gomisha marked this conversation as resolved.
Show resolved Hide resolved
e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageRangeRequest)
return nil
}
return e.requestHandler.Process(channel, originID, event)

case *messages.SyncRequest:
report, misbehavior := e.validateSyncRequestForALSP(channel, originID, event)
if misbehavior {
e.con.ReportMisbehavior(report) // report misbehavior to ALSP
e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync request from %x: %v", originID[:], misbehavior)
gomisha marked this conversation as resolved.
Show resolved Hide resolved
e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageSyncRequest)
return nil
}
return e.requestHandler.Process(channel, originID, event)
case *messages.SyncResponse, *messages.BlockResponse:

case *messages.BlockResponse:
report, misbehavior := e.validateBlockResponseForALSP(channel, originID, event)
if misbehavior {
e.con.ReportMisbehavior(report) // report misbehavior to ALSP
e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid block response from %x: %v", originID[:], misbehavior)
gomisha marked this conversation as resolved.
Show resolved Hide resolved
e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageBlockResponse)
return nil
}
return e.responseMessageHandler.Process(originID, event)

case *messages.SyncResponse:
report, misbehavior := e.validateSyncResponseForALSP(channel, originID, event)
if misbehavior {
e.con.ReportMisbehavior(report) // report misbehavior to ALSP
e.log.Warn().Hex("origin_id", logging.ID(originID)).Str(logging.KeySuspicious, "true").Msgf("received invalid sync response from %x: %v", originID[:], misbehavior)
gomisha marked this conversation as resolved.
Show resolved Hide resolved
e.metrics.InboundMessageDropped(metrics.EngineSynchronization, metrics.MessageSyncResponse)
return nil
}
return e.responseMessageHandler.Process(originID, event)
default:
return fmt.Errorf("received input with type %T from %x: %w", event, originID[:], engine.IncompatibleInputTypeError)
Expand Down Expand Up @@ -424,3 +472,59 @@ func (e *Engine) sendRequests(participants flow.IdentifierList, ranges []chainsy
e.log.Warn().Err(err).Msg("sending range and batch requests failed")
}
}

// TODO: implement spam reporting similar to validateSyncRequestForALSP
func (e *Engine) validateBatchRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) {
return nil, false
}

// TODO: implement spam reporting similar to validateSyncRequestForALSP
func (e *Engine) validateBlockResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) {
return nil, false
}

// TODO: implement spam reporting similar to validateSyncRequestForALSP
func (e *Engine) validateRangeRequestForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) {
return nil, false
}

// validateSyncRequestForALSP checks if a sync request should be reported as spam. It returns a misbehavior report and a boolean indicating whether the request should be reported.
func (e *Engine) validateSyncRequestForALSP(channel channels.Channel, originID flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) {
// Generate a random integer between 1 and spamProbabilityMultiplier (exclusive)
n, err := rand.Uint32n(spamProbabilityMultiplier)

if err != nil {
// failing to generate a random number is unlikely. If an error is encountered while
// generating a random number it indicates a bug and processing can not proceed.
e.log.Fatal().
Err(err).
gomisha marked this conversation as resolved.
Show resolved Hide resolved
Str("originID", originID.String()).
Msg("failed to generate random number")
}

// to avoid creating a misbehavior report for every sync request received, use a probabilistic approach.
// Create a report with a probability of spamReportConfig.syncRequestProbability
if float32(n) < e.spamReportConfig.syncRequestProbability*spamProbabilityMultiplier {
// create a misbehavior report
e.log.Info().Str("originID", originID.String()).Msg("creating misbehavior report")
report, err := alsp.NewMisbehaviorReport(originID, alsp.PotentialSpam)

if err != nil {
// failing to create the misbehavior report is unlikely. If an error is encountered while
// creating the misbehavior report it indicates a bug and processing can not proceed.
e.log.Fatal().
Err(err).
gomisha marked this conversation as resolved.
Show resolved Hide resolved
Str("originID", originID.String()).
Msg("failed to create misbehavior report")
}
return report, true
}

// most of the time, don't report a misbehavior
return nil, false
}

// TODO: implement spam reporting similar to validateSyncRequestForALSP
func (e *Engine) validateSyncResponseForALSP(channel channels.Channel, id flow.Identifier, event interface{}) (*alsp.MisbehaviorReport, bool) {
return nil, false
}
Loading