Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
23 changes: 22 additions & 1 deletion ci/apiv2/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import test_user
from hashtopolis import Agent, Config, Helper
from hashtopolis import HashtopolisError
from hashtopolis_agent import ProcessState

from utils import BaseTest
from utils import BaseTest, do_create_dummy_agent, do_create_agentassignent
Comment thread
Copilot marked this conversation as resolved.
Outdated


class AgentTest(BaseTest):
Expand Down Expand Up @@ -110,3 +111,23 @@ def test_hide_ip_info(self):
def test_acl(self):
model_obj = self.create_test_object()
self._test_acl_list(model_obj, {'permAgentRead': True})

def test_active_chunk(self):
dummy_agent, agent, _, task = self.create_agent_with_task().values()
dummy_agent.get_chunk()
dummy_agent.send_process(progress=50, state=ProcessState.RUNNING)
agent_resp = Agent.objects.get(pk=agent.id)
chunks = agent_resp._Model__relationships['chunks'].get('data', [])
active_chunk_id = next(iter(chunks), {}).get('id', None)

self.assertEqual(dummy_agent.chunk['chunkId'], active_chunk_id, "Active chunk is reported incorrectly")

dummy_agent.get_chunk()
# To simulate a stop instruction (e.g. after a hashlist has been completed by other agents),
# report completeness on current chunk, but leave state as RUNNING.
dummy_agent.send_process(progress=100, state=ProcessState.RUNNING)
helper = Helper()
result = helper.unassign_agent(agent=agent)
agent_resp = Agent.objects.get(pk=agent.id)

self.assertNotIn('data', agent_resp._Model__relationships['chunks'], "Chunks of a completed hashlist should not be returned as active")
10 changes: 3 additions & 7 deletions src/inc/apiv2/model/AgentAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Exception;
use Hashtopolis\dba\AbstractModel;
use Hashtopolis\dba\OrderFilter;
use Hashtopolis\inc\utils\AccessUtils;
Comment thread
Copilot marked this conversation as resolved.
use Hashtopolis\inc\utils\AgentUtils;
use Hashtopolis\inc\defines\DHashcatStatus;
Expand Down Expand Up @@ -92,15 +93,10 @@ protected function getAggregateCrackingTime(AbstractModel $object): int {
*/
function aggregateData(AbstractModel $object, array &$includedData = [], ?array $aggregateFieldsets = null): array {
$agentId = $object->getId();
$qFs = [];
$qFs[] = new QueryFilter(Chunk::AGENT_ID, $agentId, "=");
$qFs[] = new QueryFilter(Chunk::STATE, DHashcatStatus::RUNNING, "=");

$active_chunk = Factory::getChunkFactory()->filter([Factory::FILTER => $qFs], true);
if ($active_chunk !== NULL) {
$active_chunk = AgentUtils::getActiveChunk($agentId);
if ($active_chunk !== null) {
$includedData["chunks"][$agentId] = [$active_chunk];
}

return parent::aggregateData($object, $includedData, $aggregateFieldsets);
}

Expand Down
8 changes: 2 additions & 6 deletions src/inc/apiv2/model/AgentAssignmentAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,8 @@ protected function getAggregateCurrentSpeed(AbstractModel $object): int {
* @throws Exception
*/
protected function getAggregateCurrentChunkId(AbstractModel $object): ?int {
$qF1 = new QueryFilter(Chunk::TASK_ID, $object->getTaskId(), "=");
$qF2 = new QueryFilter(Chunk::AGENT_ID, $object->getAgentId(), "=");
$qF3 = new QueryFilter(Chunk::SOLVE_TIME, time() - SConfig::getInstance()->getVal(DConfig::CHUNK_TIMEOUT), ">");
$qF4 = new QueryFilter(Chunk::PROGRESS, 10000, "<");
$chunk = Factory::getChunkFactory()->filter([Factory::FILTER => array_filter([$qF1, $qF2, $qF3, $qF4])], true);
return $chunk?->getId();
$active_chunk = AgentUtils::getActiveChunk($object->getAgentId(), $object->getTaskId());
return $active_chunk?->getId();
Comment thread
novasam23 marked this conversation as resolved.
}

/**
Expand Down
21 changes: 21 additions & 0 deletions src/inc/utils/AgentUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Hashtopolis\inc\apiv2\error\HttpError;
use Hashtopolis\inc\defines\DAgentStatsType;
use Hashtopolis\inc\defines\DConfig;
use Hashtopolis\inc\defines\DHashcatStatus;
use Hashtopolis\inc\defines\DLogEntry;
use Hashtopolis\inc\defines\DLogEntryIssuer;
use Hashtopolis\inc\defines\DNotificationObjectType;
Expand Down Expand Up @@ -644,4 +645,24 @@ public static function getAggregateCracked(int $agentId, ?int $taskId = null): i
$results = Factory::getChunkFactory()->multicolAggregationFilter([Factory::FILTER => array_filter([$qF1, $qF2])], [$agg1]);
return (int)($results[$agg1->getName()] ?? 0);
}

/**
* Get the active chunk being worked on by an agent or
* (if task ID is specified) by an agent on a specific task.
*
* @param int $agentId
* @param int|null $taskId
* @return Chunk|null
* @throws Exception
*/
public static function getActiveChunk(int $agentId, ?int $taskId = null): ?Chunk {
$qFs = [];
$qFs[] = new QueryFilter(Chunk::AGENT_ID, $agentId, "=");
$qFs[] = $taskId !== null ? new QueryFilter(Chunk::TASK_ID, $taskId, "=") : null;

@jessevz jessevz Aug 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line looks a bit like a code smell to me, this will add null to the qFs array when when taskId is null. But we dont really need that null value. I think it is better to use a normal if statement instead of a tenary to only add this to the qFs[] when taskId is not null and otherwise dont add anything

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see now that you filter the null value away later by doing array_filter(). Still for readability I think it is better to just do a normal if statement

$qFs[] = new QueryFilter(Chunk::STATE, DHashcatStatus::RUNNING, "=");
$qFs[] = new QueryFilter(Chunk::SOLVE_TIME, time() - SConfig::getInstance()->getVal(DConfig::CHUNK_TIMEOUT), ">");
$qFs[] = new QueryFilter(Chunk::PROGRESS, 10000, "<");
$oF = new OrderFilter(Chunk::SOLVE_TIME, "DESC");
return Factory::getChunkFactory()->filter([Factory::FILTER => array_filter($qFs), Factory::ORDER => $oF], true);
}
}