Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
35 changes: 33 additions & 2 deletions ci/apiv2/test_agentassignment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from hashtopolis import AgentAssignment
import time
from hashtopolis import AgentAssignment, Chunk

from utils import BaseTest, do_create_dummy_agent
from utils import BaseTest, do_create_dummy_agent, do_create_agentassignent


class AgentStatTest(BaseTest):
Expand Down Expand Up @@ -50,3 +51,33 @@ def test_agent_assign_task(self):
def test_acl(self):
model_obj = self.create_test_object()
self._test_acl_list(model_obj, {'permAgentAssignmentRead': True})

def test_cracking_time_aggregation(self):
dummy_agent, agent, _, task = self.create_agent_with_task().values()
Comment thread
novasam23 marked this conversation as resolved.
time.sleep(1) # Simulate cracking time
dummy_agent.send_process(progress=100)
dummy_agent.get_chunk()
time.sleep(1) # Simulate cracking time
dummy_agent.send_process(progress=100)
dummy_agent.get_chunk()
time.sleep(1) # Simulate cracking time
dummy_agent.send_process(progress=100)
dummy_agent.get_chunk() # Leave the last chunk unfinished for a more meaningful test

# Calculate a reference value for the cracking time by applying an interval merge algorithm
chunks = Chunk.objects.filter(agentId=agent.id, taskId=task.id)
totalEnd = totalSum = 0
for chunk in sorted(chunks, key=lambda c: c.dispatchTime): # Expects list to be sorted by time
if chunk.dispatchTime > 0 and chunk.solveTime > 0:
totalSum = (
totalSum + (chunk.solveTime - chunk.dispatchTime)
- max(0, totalEnd - chunk.dispatchTime)
+ max(0, totalEnd - chunk.solveTime)
)
totalEnd = max(totalEnd, chunk.solveTime)

# Get the aggregate cracking time from the application
aggregate_attrs = ['crackingTime']
assignment = AgentAssignment.objects.params(**{"aggregate[assignment]": ','.join(aggregate_attrs)}).get(agentId=agent.id, taskId=task.id)

self.assertEqual(totalSum, assignment.crackingTime)
17 changes: 17 additions & 0 deletions ci/phpunit/TestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,23 @@ protected function createAgent(string $prefix, int $isTrusted = 1): Agent {
return $agent;
}

public function createTaskHelper(): array {
$user = $this->createUser("phpunit");
$accessGroup = $this->createAccessGroup("phpunit");
$this->createAccessGroupUser($user, $accessGroup);

$hashType = $this->createHashType();
$hashlist = $this->createHashlist($accessGroup, $hashType);

$taskWrapper = $this->createTaskWrapper($accessGroup, $hashlist);

$crackerBinaryType = $this->createCrackerBinaryType();
$crackerBinary = $this->createCrackerBinary($crackerBinaryType);
$task = $this->createTask($taskWrapper, $crackerBinary, $crackerBinaryType);

return array("user"=> $user, "accessGroup"=>$accessGroup, "hashType"=>$hashType, "hashlist"=>$hashlist, "taskWrapper"=>$taskWrapper, "crackerBinaryType"=>$crackerBinaryType, "crackerBinary"=>$crackerBinary, "task"=>$task);
}

/**
* used to create an object in the database and then register it directly for deletion to be cleaned up after the test
*
Expand Down
59 changes: 59 additions & 0 deletions ci/phpunit/inc/utils/AgentUtilsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Hashtopolis\inc\utils;

use Exception;
use Hashtopolis\dba\Factory;
use Hashtopolis\TestBase;

require_once(dirname(__FILE__) . '/../../TestBase.php');

final class AgentUtilsTest extends TestBase {

protected function setUp(): void {
parent::setUp();
}

/**
* Test cracking time aggregation for an agent on a task.
*
* @return void
* @throws Exception
*/
public function testCrackingTimeAggregation(): void {
$task = $this->createTaskHelper()["task"];
$agent1 = $this->createAgent("test");
$agent2 = $this->createAgent("test");
$timeSpans = (array) [
[1000, 2000, $agent1],
[3000, 5000, $agent2],
[3000, 8000, $agent1],
[8000, 10000, $agent1],
[15000, 20000, $agent1],
];

foreach ($timeSpans as [$start, $end, $agent]) {
$chunk = $this->createChunk($task, $agent, 4);
$chunk->setDispatchTime($start);
$chunk->setSolveTime($end);
Factory::getChunkFactory()->update($chunk);
}

// Calculate reference value via an interval merge algorithm
$totalEnd = $referenceSum = 0;
usort($timeSpans, fn($a, $b) => $a[0] <=> $b[0]); // Make sure time spans are sorted
foreach ($timeSpans as [$currentStart, $currentEnd, $agent]) { // Expects list to be sorted by time
if ($agent == $agent1) {
$referenceSum = $referenceSum + ($currentEnd - $currentStart) // Add current time span to running total
- max(0, $totalEnd - $currentStart) // Correct for potential overlapping when current start before previous end
+ max(0, $totalEnd - $currentEnd); // Correct for potential overcorrection when current end before previous end
$totalEnd = max($totalEnd, $currentEnd); // Extend window if current end exceeds previous end
}
}

// Calculate aggregate cracking time via AgentUtils
$crackingTime = AgentUtils::getAggregateCrackingTime($agent1->getId(), $task->getId());

$this->assertEquals($referenceSum, $crackingTime);
}
}
17 changes: 0 additions & 17 deletions ci/phpunit/inc/utils/TaskUtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,21 +250,4 @@ public function testSetCpuTask(): void {
$taskUpdated = Factory::getTaskFactory()->get($taskObjects["task"]->getId());
$this->assertEquals(0, $taskUpdated->getIsCpuTask());
}

public function createTaskHelper(): array {
$user = $this->createUser("phpunit");
$accessGroup = $this->createAccessGroup("phpunit");
$this->createAccessGroupUser($user, $accessGroup);

$hashType = $this->createHashType();
$hashlist = $this->createHashlist($accessGroup, $hashType);

$taskWrapper = $this->createTaskWrapper($accessGroup, $hashlist);

$crackerBinaryType = $this->createCrackerBinaryType();
$crackerBinary = $this->createCrackerBinary($crackerBinaryType);
$task = $this->createTask($taskWrapper, $crackerBinary, $crackerBinaryType);

return array("user"=> $user, "accessGroup"=>$accessGroup, "hashType"=>$hashType, "hashlist"=>$hashlist, "taskWrapper"=>$taskWrapper, "crackerBinaryType"=>$crackerBinaryType, "crackerBinary"=>$crackerBinary, "task"=>$task);
}
}
10 changes: 1 addition & 9 deletions src/inc/apiv2/model/AgentAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Exception;
use Hashtopolis\dba\AbstractModel;
use Hashtopolis\dba\Aggregation;
use Hashtopolis\inc\utils\AccessUtils;
use Hashtopolis\inc\utils\AgentUtils;
use Hashtopolis\inc\defines\DHashcatStatus;
Expand Down Expand Up @@ -65,14 +64,7 @@ public function getAggregateFieldsets(): array {
* @throws Exception
*/
protected function getAggregateCrackingTime(AbstractModel $object): int {
// in order to make sense of the diff, we need to make sure that both values solve time and dispatch time are set (i.e. >0).
$qF1 = new QueryFilter(Chunk::AGENT_ID, $object->getId(), "=");
$qF2 = new QueryFilter(Chunk::SOLVE_TIME, 0, ">");
$qF3 = new QueryFilter(Chunk::DISPATCH_TIME, 0, ">");
$agg1 = new Aggregation(Chunk::SOLVE_TIME, Aggregation::SUM);
$agg2 = new Aggregation(Chunk::DISPATCH_TIME, Aggregation::SUM);
$results = Factory::getChunkFactory()->multicolAggregationFilter([Factory::FILTER => [$qF1, $qF2, $qF3]], [$agg1, $agg2]);
return $results[$agg1->getName()] - $results[$agg2->getName()];
return AgentUtils::getAggregateCrackingTime($object->getId());
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/inc/apiv2/model/AgentAssignmentAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ protected function getUpdateHandlers($id, $current_user): array {
];
}

public function getAggregateFieldsets(): array {
return [
'assignment' => [
'crackingTime' => [$this, 'getAggregateCrackingTime'],
]
Comment thread
novasam23 marked this conversation as resolved.
];
Comment thread
novasam23 marked this conversation as resolved.
}

/**
* @param Assignment $object
* @return int
* @throws Exception
*/
protected function getAggregateCrackingTime(AbstractModel $object): int {
return AgentUtils::getAggregateCrackingTime($object->getAgentId(), $object->getTaskId());
}

/**
* @param Assignment $object
* @throws HTException
Expand Down
24 changes: 24 additions & 0 deletions src/inc/utils/AgentUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Hashtopolis\inc\utils;

use Exception;
use Hashtopolis\dba\Aggregation;
use Hashtopolis\inc\DataSet;
use Hashtopolis\dba\models\Agent;
use Hashtopolis\dba\QueryFilter;
Expand Down Expand Up @@ -603,4 +604,27 @@ public static function deleteVoucher(int|string $voucher): void {
}
Factory::getRegVoucherFactory()->delete($voucher);
}

/**
* Get the aggregate cracking time of an agent or more specifically
* (if task ID is specified) of an agent on a specific task.
*
* @param int $agentId
* @param int|null $taskId
* @return int
* @throws Exception
*/
public static function getAggregateCrackingTime(int $agentId, ?int $taskId = null): int {
// In order to make sense of the diff, we need to make sure that both values solve time and dispatch time are set (i.e. > 0).
// Since an agent can only work on one chunk at a time, there inherently are no overlapping time spans to consider.
// We can therefore simply sum up the corresponding time spans in the database already.
$qF1 = new QueryFilter(Chunk::AGENT_ID, $agentId, "=");
$qF2 = $taskId !== null ? new QueryFilter(Chunk::TASK_ID, $taskId, "=") : null;
$qF3 = new QueryFilter(Chunk::SOLVE_TIME, 0, ">");
$qF4 = new QueryFilter(Chunk::DISPATCH_TIME, 0, ">");
$agg1 = new Aggregation(Chunk::SOLVE_TIME, Aggregation::SUM);
$agg2 = new Aggregation(Chunk::DISPATCH_TIME, Aggregation::SUM);
$results = Factory::getChunkFactory()->multicolAggregationFilter([Factory::FILTER => array_filter([$qF1, $qF2, $qF3, $qF4])], [$agg1, $agg2]);
return $results[$agg1->getName()] - $results[$agg2->getName()];
}
}
Loading