private async Task HandleSingleHackAsync(IEnumerable <ActorInfo> actors, HackStatus hack, JudgeStatus judge, Problem problem, Guid?testCaseId, CancellationToken token) { var sub = new SubJudgeStatus(); var isBadData = false; if (actors.Count(x => x.Name == "CompareActor") == 0) { var rangeValidateActor = actors.FirstOrDefault(x => x.Stage == "ValidateData"); var rangeRunner = rangeValidateActor == null ? null : await _mgmt.ReadBlobAsObjectAsync <Runner>(rangeValidateActor.Outputs.Single(x => x.Name == "runner.json").Id, token); var runActor = actors.FirstOrDefault(x => x.Stage == "GenerateHackeeAnswer"); var runRunner = runActor == null ? null : await _mgmt.ReadBlobAsObjectAsync <Runner>(runActor.Outputs.Single(x => x.Name == "runner.json").Id, token); if (rangeRunner != null && rangeRunner.ExitCode != 0) { isBadData = true; sub.TimeUsedInMs = 0; sub.MemoryUsedInByte = 0; sub.Result = JudgeResult.Accepted; sub.Hint = string.Join(Environment.NewLine, rangeValidateActor.Exceptions) + Environment.NewLine + rangeRunner.Error; } else if (runRunner.IsTimeout) { sub.TimeUsedInMs = problem.TimeLimitationPerCaseInMs; sub.MemoryUsedInByte = runRunner.PeakMemory; sub.Result = JudgeResult.TimeExceeded; sub.Hint = string.Join(Environment.NewLine, runActor.Exceptions) + Environment.NewLine + runRunner.Error; } else if (runRunner.ExitCode == 139 || runActor.Exceptions.Any(x => x.Contains("May cause by out of memory")) || runRunner.Error.Contains("std::bad_alloc") || runRunner.PeakMemory > problem.MemoryLimitationPerCaseInByte) { sub.TimeUsedInMs = runRunner.UserTime; sub.MemoryUsedInByte = problem.MemoryLimitationPerCaseInByte; sub.Result = JudgeResult.MemoryExceeded; sub.Hint = string.Join(Environment.NewLine, runActor.Exceptions) + Environment.NewLine + runRunner.Error; } else if (runRunner.ExitCode != 0) { sub.TimeUsedInMs = runRunner.UserTime; sub.MemoryUsedInByte = runRunner.PeakMemory; sub.Result = JudgeResult.RuntimeError; sub.Hint = string.Join(Environment.NewLine, runActor.Exceptions) + Environment.NewLine + runRunner.Error + Environment.NewLine + $"User process exited with code { runRunner.ExitCode }"; } } else if (actors.Count(x => x.Name == "CompareActor") > 0) { var runners = actors.Where(x => x.Name == "CompareActor").Select(x => x.Outputs.Single(y => y.Name == "runner.json")); var runnerResults = runners.Select(x => _mgmt.ReadBlobAsObjectAsync <Runner>(x.Id, token).Result); var exitCodes = runnerResults.Select(x => x.ExitCode); if (exitCodes.Distinct().Count() > 1) { isBadData = true; } else { var runActor = actors.First(x => x.Stage == "GenerateHackeeAnswer"); var runRunner = await _mgmt.ReadBlobAsObjectAsync <Runner>(runActor.Outputs.Single(x => x.Name == "runner.json").Id, token); var compareActor = actors.Last(x => x.Name == "CompareActor"); var runner = await _mgmt.ReadBlobAsObjectAsync <Runner>(compareActor.Outputs.Single(x => x.Name == "runner.json").Id, token); if (runner.ExitCode < 0 || runner.ExitCode > 2) { sub.TimeUsedInMs = 0; sub.MemoryUsedInByte = 0; sub.Result = JudgeResult.SystemError; sub.Hint = string.Join(Environment.NewLine, runActor.Exceptions) + Environment.NewLine + runner.Error; } else { var validatorOutput = Encoding.UTF8.GetString((await _mgmt.GetBlobAsync(compareActor.Outputs.Single(x => x.Name == "stdout.txt").Id)).Body); sub.TimeUsedInMs = runRunner.UserTime; sub.MemoryUsedInByte = runRunner.PeakMemory; sub.Result = (JudgeResult)runner.ExitCode; sub.Hint = validatorOutput + Environment.NewLine + string.Join(Environment.NewLine, runActor.Exceptions) + Environment.NewLine + runner.Error; } } } else { isBadData = true; } if (hack == null && actors.Count(x => x.Stage == "GenerateHackeeAnswer") > 0 && testCaseId.HasValue) { var runActor = actors.First(x => x.Stage == "GenerateHackeeAnswer"); var statusId = Guid.Parse(runActor.Tag); var testCase = await _db.TestCases.SingleAsync(x => x.Id == testCaseId.Value, token); var testCases = await _db.TestCases .Where(x => x.ProblemId == problem.Id) .Select(x => x.Id) .ToListAsync(token); var status = await _db.JudgeStatuses .Include(x => x.SubStatuses) .SingleAsync(x => x.Id == statusId, token); sub.StatusId = statusId; sub.TestCaseId = testCaseId.Value; sub.InputBlobId = testCase.InputBlobId; sub.OutputBlobId = testCase.OutputBlobId; sub.SubId = testCases.IndexOf(testCaseId.Value); status.SubStatuses.Add(sub); status.MemoryUsedInByte = status.SubStatuses.Max(x => x.MemoryUsedInByte); status.TimeUsedInMs = status.SubStatuses.Sum(x => x.TimeUsedInMs); status.Result = status.SubStatuses.Max(x => x.Result); _db.SaveChanges(); } if (hack != null) { _db.HackStatuses .Where(x => x.Id == hack.Id) .SetField(x => x.HackeeResult).WithValue(isBadData ? JudgeResult.Accepted : sub.Result) .SetField(x => x.TimeUsedInMs).WithValue(sub.TimeUsedInMs) .SetField(x => x.MemoryUsedInByte).WithValue(sub.MemoryUsedInByte) .SetField(x => x.Result).WithValue(isBadData ? (HackResult.BadData) : (sub.Result == JudgeResult.Accepted ? HackResult.Failed : HackResult.Succeeded)) .SetField(x => x.Hint).WithValue(sub.Hint) .Update(); } }
public override void OnHackCompleted(HackStatus status) { if (status.Result == HackResult.Succeeded) { // 1. Set the status to be non-hackable DB.ContestProblemLastStatuses .Where(x => x.StatusId == status.JudgeStatusId && x.ContestId == ContestId) .SetField(x => x.IsHackable).WithValue(false) .SetField(x => x.IsHacked).WithValue(true) .Update(); // 2. Add the hack data to problem var input = status.HackDataBlobId.Value; var testCase = DB.TestCases.FirstOrDefault(x => x.InputBlobId == input && x.ProblemId == status.Status.ProblemId); var testCaseExisted = testCase != null; if (!testCaseExisted) { var inputLength = ManagementService.GetBlobAsync(input).Result.Body.Length; var stateMachine = ManagementService.GetStateMachineInstanceAsync(status.RelatedStateMachineIds.Last().StateMachineId).Result; var output = stateMachine.StartedActors.First(x => x.Tag == "Standard").Outputs.First(x => x.Name == "stdout.txt").Id; var outputLength = ManagementService.GetBlobAsync(output).Result.Body.Length; testCase = new TestCase { ContestId = ContestId, InputBlobId = input, InputSizeInByte = inputLength, OutputBlobId = output, OutputSizeInByte = outputLength, ProblemId = status.Status.ProblemId, Type = TestCaseType.Hack }; DB.TestCases.Add(testCase); DB.SaveChanges(); } // 3. Add the result into sub judge status if (!testCaseExisted) { var sub = new SubJudgeStatus { SubId = DB.SubJudgeStatuses.Where(x => x.StatusId == status.JudgeStatusId).Count(), Hint = status.Hint, MemoryUsedInByte = status.MemoryUsedInByte, TimeUsedInMs = status.TimeUsedInMs, Result = status.HackeeResult, InputBlobId = testCase.InputBlobId, OutputBlobId = testCase.OutputBlobId, TestCaseId = testCase.Id, StatusId = status.JudgeStatusId }; DB.SubJudgeStatuses.Add(sub); DB.SaveChanges(); } // 4. Add point for the hacker DB.ContestProblemLastStatuses .Where(x => x.ContestId == ContestId && x.ProblemId == status.Status.ProblemId && x.UserId == status.UserId) .SetField(x => x.Point2).Plus(1) .Update(); // 5. Hack all statuses if (!testCaseExisted && DB.Attendees.Any(x => x.ContestId == ContestId && x.UserId == status.UserId && !x.IsVirtual)) { var affectedStatuses = DB.ContestProblemLastStatuses .Include(x => x.Status) .Where(x => x.ProblemId == status.Status.ProblemId && x.ContestId == ContestId && x.Status.BinaryBlobId.HasValue && x.IsAccepted) .ToList(); var problem = DB.Problems.Single(x => x.Id == status.Status.ProblemId); var validatorId = problem.ValidatorBlobId.HasValue ? problem.ValidatorBlobId.Value : Guid.Parse(Configuration["JoyOI:StandardValidatorBlobId"]); var blobs = new List <BlobInfo>(affectedStatuses.Count + 10); blobs.Add(new BlobInfo { Id = ManagementService.PutBlobAsync("limit.json", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { UserTime = status.Status.Problem.TimeLimitationPerCaseInMs, PhysicalTime = status.Status.Problem.TimeLimitationPerCaseInMs * 4, Memory = status.Status.Problem.MemoryLimitationPerCaseInByte }))).Result, Name = "limit.json", Tag = "Problem=" + status.Status.ProblemId }); blobs.Add(new BlobInfo(validatorId, problem.ValidatorBlobId.HasValue ? "Validator" + Constants.GetBinaryExtension(problem.ValidatorLanguage) : "Validator.out")); blobs.Add(new BlobInfo(status.HackDataBlobId.Value, "data.txt", testCase.Id.ToString())); blobs.Add(new BlobInfo(testCase.OutputBlobId, "std.txt", testCase.Id.ToString())); foreach (var x in affectedStatuses) { blobs.Add(new BlobInfo(x.Status.BinaryBlobId.Value, "Hackee" + Constants.GetBinaryExtension(x.Status.Language), x.StatusId.ToString())); } ManagementService.PutStateMachineInstanceAsync("HackAllStateMachine", Configuration["ManagementService:CallBack"], blobs, 2); } } else if (status.Result == HackResult.Failed) { DB.ContestProblemLastStatuses .Where(x => x.ContestId == ContestId && x.ProblemId == status.Status.ProblemId && x.UserId == status.UserId) .SetField(x => x.Point2).Subtract(1) .SetField(x => x.Point3).Plus(1) .Update(); } }