Skip to content

Commit

Permalink
fix(back): allow 1000ms system clock desync in run validation
Browse files Browse the repository at this point in the history
  • Loading branch information
tsa96 committed Jan 5, 2025
1 parent 5f4520f commit b153cdc
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ describe('RunProcessor', () => {
const DefaultReplayHeader = ReplayFile.Stubs.ReplayHeaderStub;
const DefaultSplits = ReplayFile.Stubs.RunSplitsStub;

const Constants = RunProcessor.Constants;

interface ProcessorOverrides {
header?: Partial<ReplayHeader>;
splits?: Partial<RunSplits.Splits>;
Expand Down Expand Up @@ -654,11 +656,13 @@ describe('RunProcessor', () => {
expectFail(ErrorType.OUT_OF_SYNC);
});

it('should throw if run time is out of sync - startDelay > AllowedTimestampDelay', () => {
it('should throw if run time is out of sync - startDelay > AllowedTimestampDelay + AllowedClockDrift', () => {
processor = createProcessor({
session: {
timestamps,
createdAt: cat(RunProcessor.Constants.AllowedTimestampDelay + 100)
createdAt: cat(
Constants.AllowedTimestampDelay + Constants.AllowedClockDrift + 100
)
}
});

Expand All @@ -670,7 +674,9 @@ describe('RunProcessor', () => {
processor = createProcessor({
session: {
timestamps,
createdAt: cat(RunProcessor.Constants.AllowedTimestampDelay - 100)
createdAt: cat(
Constants.AllowedTimestampDelay + Constants.AllowedClockDrift - 100
)
}
});

Expand All @@ -691,12 +697,13 @@ describe('RunProcessor', () => {
it('should throw if run time is out of sync - submitDelay > acceptableSubmitDelay', () => {
const {
AllowedSubmitDelayBase: base,
AllowedClockDrift: drift,
AllowedSubmitDelayIncrement: incr
} = RunProcessor.Constants;
} = Constants;

processor = createProcessor({ session: { timestamps } });

const acceptableDelay = base + (runTimeMS / 60000) * incr;
const acceptableDelay = base + drift + (runTimeMS / 60000) * incr;
jest.advanceTimersByTime(runTimeMS + acceptableDelay - 100);
expectPass();

Expand Down Expand Up @@ -871,8 +878,16 @@ describe('RunProcessor', () => {
processor = createProcessor({
session: {
timestamps: [
// Note that `time` isn't really that significant in these tests,
// just making the data more realistic. The actual check is
// performed against the
{ createdAt: cat(0), time: 0, majorNum: 1, minorNum: 1 },
{ createdAt: cat(9999), time: 9.999, majorNum: 1, minorNum: 2 },
{
createdAt: cat(9999 - Constants.AllowedClockDrift),
time: 9.999 - Constants.AllowedClockDrift / 1000,
majorNum: 1,
minorNum: 2
},
{ createdAt: cat(20000), time: 20, majorNum: 2, minorNum: 1 },
{ createdAt: cat(30000), time: 30, majorNum: 2, minorNum: 2 }
]
Expand All @@ -889,7 +904,10 @@ describe('RunProcessor', () => {
{ createdAt: cat(0), time: 0, majorNum: 1, minorNum: 1 },
{
createdAt: cat(
10000 + RunProcessor.Constants.AllowedTimestampDelay - 100
10000 +
Constants.AllowedTimestampDelay +
Constants.AllowedClockDrift -
100
),
time: 10,
majorNum: 1,
Expand All @@ -911,7 +929,10 @@ describe('RunProcessor', () => {
{ createdAt: cat(0), time: 0, majorNum: 1, minorNum: 1 },
{
createdAt: cat(
10000 + RunProcessor.Constants.AllowedTimestampDelay + 100
10000 +
Constants.AllowedTimestampDelay +
Constants.AllowedClockDrift +
100
),
time: 10,
majorNum: 1,
Expand Down
86 changes: 53 additions & 33 deletions apps/backend/src/app/modules/session/run/run-processor.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export class RunProcessor {
AllowedSubmitDelayMax: 30_000,

// Allowed time diff between split time and backend timestamp created
AllowedTimestampDelay: 5000
AllowedTimestampDelay: 5000,

// Allowed system clock inaccuracy
AllowedClockDrift: 1000
};

static readonly Logger = new Logger('RunProcessor');
Expand Down Expand Up @@ -285,16 +288,17 @@ export class RunProcessor {

if (header.tickInterval !== TickIntervals.get(session.gamemode)) {
this.reject(
ErrorType.BAD_META,
'header.tickInterval != session.gamemode'
ErrorType.OUT_OF_SYNC,
'header.tickInterval != gamemode tick interval'
);
}

const {
AllowedSubmitDelayBase,
AllowedSubmitDelayIncrement,
AllowedSubmitDelayMax,
AllowedTimestampDelay
AllowedTimestampDelay,
AllowedClockDrift
} = RunProcessor.Constants;

// Check timestamps match up with replay start and end times
Expand All @@ -319,18 +323,20 @@ export class RunProcessor {
const submitDelay = now - header.timestamp;
const allowedSubmitDelay =
AllowedSubmitDelayBase +
AllowedClockDrift +
Math.min(
(AllowedSubmitDelayIncrement * headerRunTime) / 60_000,
AllowedSubmitDelayMax
);

const startDelay = sessionStart - runStart;

if (submitDelay < 0) {
this.reject(ErrorType.OUT_OF_SYNC, 'submitDelay < 0', {
if (submitDelay < -AllowedClockDrift) {
this.reject(ErrorType.OUT_OF_SYNC, 'submitDelay < -AllowedClockDrift', {
now,
headerTimestamp: header.timestamp,
submitDelay
submitDelay,
AllowedClockDrift
});
}

Expand All @@ -342,31 +348,38 @@ export class RunProcessor {
allowedSubmitDelay,
AllowedSubmitDelayBase,
AllowedSubmitDelayIncrement,
AllowedSubmitDelayMax
AllowedSubmitDelayMax,
AllowedClockDrift
});
}

if (startDelay < 0) {
this.reject(ErrorType.OUT_OF_SYNC, 'startDelay < 0', {
if (startDelay < -AllowedClockDrift) {
this.reject(ErrorType.OUT_OF_SYNC, 'startDelay < -AllowedClockDrift', {
headerRunTime,
headerTimestamp: header.timestamp,
runStart,
sessionStart,
now,
startDelay
startDelay,
AllowedClockDrift
});
}

if (startDelay > AllowedTimestampDelay) {
this.reject(ErrorType.OUT_OF_SYNC, 'startDelay > AllowedTimestampDelay', {
headerRunTime,
headerTimestamp: header.timestamp,
runStart,
sessionStart,
now,
startDelay,
AllowedTimestampDelay
});
if (startDelay > AllowedTimestampDelay + AllowedClockDrift) {
this.reject(
ErrorType.OUT_OF_SYNC,
'startDelay > AllowedTimestampDelay + AllowedClockDrift',
{
headerRunTime,
headerTimestamp: header.timestamp,
runStart,
sessionStart,
now,
startDelay,
AllowedTimestampDelay,
AllowedClockDrift
}
);
}
}

Expand Down Expand Up @@ -401,31 +414,38 @@ export class RunProcessor {
const unixTimeReached = replayStartTime + subseg.timeReached * 1000;

const timestampDelay = timestamp.createdAt.getTime() - unixTimeReached;
const { AllowedTimestampDelay } = RunProcessor.Constants;
const { AllowedTimestampDelay, AllowedClockDrift } =
RunProcessor.Constants;

if (timestampDelay > AllowedTimestampDelay) {
if (timestampDelay > AllowedTimestampDelay + AllowedClockDrift) {
this.reject(
ErrorType.OUT_OF_SYNC,
'timestampDelay > AllowedTimestampDelay',
'timestampDelay > AllowedTimestampDelay + AllowedClockDrift',
{
subseg,
timestamp,
replayStartTime,
unixTimeReached,
timestampDelay,
AllowedTimestampDelay
AllowedTimestampDelay,
AllowedClockDrift
}
);
}

if (timestampDelay < 0) {
this.reject(ErrorType.OUT_OF_SYNC, 'timestampDelay < 0', {
subseg,
timestamp,
replayStartTime,
unixTimeReached,
timestampDelay
});
if (timestampDelay < -AllowedClockDrift) {
this.reject(
ErrorType.OUT_OF_SYNC,
'timestampDelay < -AllowedClockDrift',
{
subseg,
timestamp,
replayStartTime,
unixTimeReached,
timestampDelay,
AllowedClockDrift
}
);
}
});
}
Expand Down

0 comments on commit b153cdc

Please sign in to comment.