Skip to content

Commit

Permalink
Handle multiple hook instances with same hash
Browse files Browse the repository at this point in the history
  • Loading branch information
zgrguric committed Dec 29, 2023
1 parent ded90df commit f1cfe39
Show file tree
Hide file tree
Showing 9 changed files with 609 additions and 30 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ $TxHookParser = new TxHookParser($tx);
$hooks = $TxHookParser->hooks();
# List of all accounts that are affected by hooks in transaction
$accounts = $TxHookParser->accounts();
# List of hooks by account
# List of hooks by account*
$accountHooks = $TxHookParser->accountHooks('raddress...');
# List of accounts by hook
$hookAccounts = $TxHookParser->hookAccounts('5EDF6...2DC77');
Expand All @@ -61,20 +61,25 @@ $destroyedHooks = $TxHookParser->destroyedHooks();
# Check if specific hook is destroyed
$isDestroyed = $TxHookParser->isHookDestroyed('5EDF6...2DC77');

# List of uninstalled hooks (eg. SetHook transaction)
# List of uninstalled hooks* (eg. SetHook transaction)
$uninstalledHooks = $TxHookParser->uninstalledHooks();
# List of uninstalled hooks with num uninstalls
$uninstalledHooksStats = $TxHookParser->uninstalledHooksStats();
# List of installed hooks (eg. SetHook transaction)
# List of installed hooks* (eg. SetHook transaction)
$installedHooks = $TxHookParser->installedHooks();
# List of installed hooks with num installs
$installedHooksStats = $TxHookParser->installedHooksStats();
# List of modified hooks
# List of modified hooks*
$modifiedHooks = $TxHookParser->modifiedHooks();

# Manual data lookup (lookup any combination of mapped data)
$lookup = $TxHookParser->lookup('raddress...','Hook','installed');


# * Methods that can return multiple same hook hashes, for example if
# account has same hook on two or more positions installed, it is
# important to differentiate hook "instance" on account vs hook
# create and destroy actions. List of hooks always returns unique hashes.
```

### HookOn field
Expand Down
74 changes: 52 additions & 22 deletions src/TxHookParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ private function extractHooksFromEmitDetails(): void
($this->tx->TransactionType == 'EmitFailure' ? 'emitfail':'emitsuccess'),
);
}
//dd($this->tx->EmitDetails);
}

private function extractHooksFromMeta(): void
Expand Down Expand Up @@ -94,8 +93,9 @@ private function extractHooksFromMeta(): void
);
if(count($parsed['added'])) {
foreach($parsed['added'] as $h => $hv) {
$h = \explode('_',$h);
$this->addHook(
$h,
$h[0],
$AffectedNode->ModifiedNode->FinalFields->Account, //affected account
'Hook',
'installed'
Expand All @@ -105,8 +105,9 @@ private function extractHooksFromMeta(): void

if(count($parsed['removed'])) {
foreach($parsed['removed'] as $h => $hv) {
$h = \explode('_',$h);
$this->addHook(
$h,
$h[0],
$AffectedNode->ModifiedNode->FinalFields->Account, //affected account
'Hook',
'uninstalled'
Expand All @@ -116,26 +117,27 @@ private function extractHooksFromMeta(): void

if(count($parsed['modified'])) {
foreach($parsed['modified'] as $h => $hv) {
$h = \explode('_',$h);
$this->addHook(
$h,
$h[0],
$AffectedNode->ModifiedNode->FinalFields->Account, //affected account
'Hook',
'modified'
);
}
}

//dd($parsed);
/*foreach($AffectedNode->ModifiedNode->FinalFields->Hooks as $hook) {
if(isset($hook->Hook->HookHash)) {
//dd($parsed['unmodified']);
if(count($parsed['unmodified'])) {
foreach($parsed['unmodified'] as $h => $hv) {
$h = \explode('_',$h);
$this->addHook(
$hook->Hook->HookHash,
$h[0],
$AffectedNode->ModifiedNode->FinalFields->Account, //affected account
'Hook',
'updated'
'unmodified'
);
}
}*/
}
break;
}
}
Expand Down Expand Up @@ -198,31 +200,59 @@ private function hookChangesFromModifiedHookNode(?array $prev, ?array $final, bo
//flag to indicate modification of hook (prev does not exist in modified node)
//ledger does not include previous fields when there is no changes
$is_modify = !$prevFieldsSet;

$prev = $prev === null?[]:$prev;
$final = $final === null?[]:$final;

$r = ['added' => [], 'removed' => [], 'unmodified' => [], 'modified' => []];
$tracker = [];
$postracker = ['prev' => [],'final' => []];

# POSTRACKER, eg hook index per hook, each first hook has index of 0
# if there is two same hook hashes they will have indexes 0 and 1
# Differentiate same hooks in different positions
foreach($prev as $p) {
if(!isset($p->Hook->HookHash)) continue;
if(!isset($postracker['prev'][$p->Hook->HookHash]))
$postracker['prev'][$p->Hook->HookHash] = -1;
$postracker['prev'][$p->Hook->HookHash]++;
}

foreach($final as $p) {
if(!isset($p->Hook->HookHash)) continue;
if(!isset($postracker['final'][$p->Hook->HookHash]))
$postracker['final'][$p->Hook->HookHash] = -1;
$postracker['final'][$p->Hook->HookHash]++;
}
# POSTRACKER END

foreach($prev as $p) {
if(!isset($p->Hook->HookHash)) continue;
$h = $p->Hook->HookHash;
//Index:
$hi = $postracker['prev'][$h];
$postracker['prev'][$h]--; //one index exausted
//Index end
$contents = $this->normalizeHookNode($p->Hook);
$tracker[$h] = ['state' => 1, 'hsh' => [\json_encode($contents)]];
$tracker[$h.'_'.$hi] = ['state' => 1, 'hsh' => [\json_encode($contents)]];
}

foreach($final as $p) {
if(!isset($p->Hook->HookHash)) continue;
$h = $p->Hook->HookHash;
//Index:
$hi = $postracker['final'][$h];
$postracker['final'][$h]--; //one index exausted
//Index end
$contents = $this->normalizeHookNode($p->Hook);
if(!isset($tracker[$h])) {
$tracker[$h] = ['state' => 0, 'hsh' => [\json_encode($contents)]];
if(!isset($tracker[$h.'_'.$hi])) {
$tracker[$h.'_'.$hi] = ['state' => 0, 'hsh' => [\json_encode($contents)]];
} else {
$tracker[$h]['hsh'][] = \json_encode($contents);
$tracker[$h]['state']++;
$tracker[$h.'_'.$hi]['hsh'][] = \json_encode($contents);
$tracker[$h.'_'.$hi]['state']++;
}
}

foreach($tracker as $h => $data) {
$state = $data['state'];
if($state === 0) {
Expand Down Expand Up @@ -335,7 +365,7 @@ public function accountHooks(string $address): array
{
if(!isset($this->map_account_hash[$address]))
return [];
return \array_values(\array_unique($this->map_account_hash[$address]));
return \array_values($this->map_account_hash[$address]);
}

/**
Expand Down Expand Up @@ -403,7 +433,7 @@ public function installedHooks(): array
{
if(!isset($this->map_typeevent_hashes['Hook_installed']))
return [];
return \array_values(\array_unique($this->map_typeevent_hashes['Hook_installed']));
return \array_values($this->map_typeevent_hashes['Hook_installed']);
}


Expand Down Expand Up @@ -431,7 +461,7 @@ public function uninstalledHooks(): array
{
if(!isset($this->map_typeevent_hashes['Hook_uninstalled']))
return [];
return \array_values(\array_unique($this->map_typeevent_hashes['Hook_uninstalled']));
return \array_values($this->map_typeevent_hashes['Hook_uninstalled']);
}

/**
Expand All @@ -455,7 +485,7 @@ public function modifiedHooks(): array
{
if(!isset($this->map_typeevent_hashes['Hook_modified']))
return [];
return \array_values(\array_unique($this->map_typeevent_hashes['Hook_modified']));
return \array_values($this->map_typeevent_hashes['Hook_modified']);
}

# Static
Expand Down
12 changes: 11 additions & 1 deletion tests/Tx01Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function testEnableAmendment()

# List of all hooks
$hooks = $TxHookParser->hooks();

$this->assertIsArray($hooks);
$this->assertEquals([
'610F33B8EBF7EC795F822A454FB852156AEFE50BE0CB8326338A81CD74801864',
Expand All @@ -26,13 +27,19 @@ public function testEnableAmendment()

# Installed hooks
$installedHooks = $TxHookParser->installedHooks();

$this->assertIsArray($hooks);
$this->assertEquals([
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77',
'610F33B8EBF7EC795F822A454FB852156AEFE50BE0CB8326338A81CD74801864'
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77',
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77',
'610F33B8EBF7EC795F822A454FB852156AEFE50BE0CB8326338A81CD74801864',
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77',
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77',
], $installedHooks);

$installedHooksStats = $TxHookParser->installedHooksStats();

$this->assertEquals([
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77' => 5,
'610F33B8EBF7EC795F822A454FB852156AEFE50BE0CB8326338A81CD74801864' => 1
Expand All @@ -43,6 +50,7 @@ public function testEnableAmendment()

# List of newly created hooks
$createdHooks = $TxHookParser->createdHooks();

$this->assertIsArray($createdHooks);
$this->assertEquals([
'610F33B8EBF7EC795F822A454FB852156AEFE50BE0CB8326338A81CD74801864',
Expand All @@ -51,6 +59,7 @@ public function testEnableAmendment()

# List of all accounts
$accounts = $TxHookParser->accounts();

$this->assertIsArray($accounts);
$this->assertEquals([
'rwyypATD1dQxDbdQjMvrqnsHr2cQw5rjMh',
Expand All @@ -62,6 +71,7 @@ public function testEnableAmendment()

# Specific account
$accountHooks = $TxHookParser->accountHooks('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
//dd($accountHooks);
$this->assertIsArray($accountHooks);
$this->assertEquals([
'5EDF6439C47C423EAC99C1061EE2A0CE6A24A58C8E8A66E4B3AF91D76772DC77',
Expand Down
1 change: 0 additions & 1 deletion tests/Tx03Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public function testPayment()
$TxHookParser = new TxHookParser($transaction->result);

# List of all hooks

$hooks = $TxHookParser->hooks();
$this->assertIsArray($hooks);
$this->assertEquals([
Expand Down
3 changes: 1 addition & 2 deletions tests/Tx09Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function testSetHookExtractDestroyedHook()
'B2B2892A2E738D8C074C5C5B40253BDAB5E4AD3D45113144EAC5E933457AF648',
'DCA4B765D3E1372B10CC641940E4C053C23862C32E9D838D4EE98853A05C9202',
], $hooks);

# List of newly created hooks
$createdHooks = $TxHookParser->createdHooks();
$this->assertIsArray($createdHooks);
Expand All @@ -50,7 +50,6 @@ public function testSetHookExtractDestroyedHook()

# List of modified hooks
$modifiedHooks = $TxHookParser->modifiedHooks();
//dd($modifiedHooks);
$this->assertIsArray($modifiedHooks);
$this->assertEquals([
'DCA4B765D3E1372B10CC641940E4C053C23862C32E9D838D4EE98853A05C9202',
Expand Down
93 changes: 93 additions & 0 deletions tests/Tx14Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php declare(strict_types=1);

namespace XRPLWin\XRPLHookParser\Tests;

use PHPUnit\Framework\TestCase;
use XRPLWin\XRPLHookParser\TxHookParser;

/***
* SetHook
* This transaction installs exact same hook that already exists.
* Metadata reflects this as modified hook.
*/
final class Tx14Test extends TestCase
{
public function testSetHookExtractDoubleHookBothReinstalled()
{
$transaction = file_get_contents(__DIR__.'/fixtures/tx14.json');
$transaction = \json_decode($transaction);
$TxHookParser = new TxHookParser($transaction->result);

# List of all hooks
$hooks = $TxHookParser->hooks();
$this->assertIsArray($hooks);
$this->assertEquals([
'ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7',
], $hooks);

# List of all accounts that are affected by hooks in transaction
$accounts = $TxHookParser->accounts();
$this->assertIsArray($accounts);
$this->assertEquals([
'rU52Rrh1K1X7Muvx4PnpjRq8nACrfHXAy6',
], $accounts);

# List of newly created hooks
$createdHooks = $TxHookParser->createdHooks();
$this->assertIsArray($createdHooks);
$this->assertEquals([], $createdHooks);

# List of newly created hooks (detailed)
$createdHooksDetailed = $TxHookParser->createdHooksDetailed();
$this->assertIsArray($createdHooksDetailed);
$this->assertEquals([], $createdHooksDetailed);

# List of newly destroyed hooks
$destroyedHooks = $TxHookParser->destroyedHooks();
$this->assertIsArray($destroyedHooks);
$this->assertEquals([], $destroyedHooks);

# List of installed hooks
$installedHooks = $TxHookParser->installedHooks();
$this->assertIsArray($installedHooks);
$this->assertEquals([], $installedHooks);

# List of uninstalled hooks
$uninstalledHooks = $TxHookParser->uninstalledHooks();
$this->assertIsArray($uninstalledHooks);
$this->assertEquals([], $uninstalledHooks);

# Uninstalled hooks stats
$uninstalledHooksStats = $TxHookParser->uninstalledHooksStats();
$this->assertIsArray($uninstalledHooksStats);
$this->assertEquals([], $uninstalledHooksStats);

# List of modified hooks
# In this case two installed hooks but they are exactly the same
$modifiedHooks = $TxHookParser->modifiedHooks();
$this->assertIsArray($modifiedHooks);
$this->assertEquals([
'ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7',
'ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7',
], $modifiedHooks);

# List of hooks by account
$accountHooks = $TxHookParser->accountHooks('rU52Rrh1K1X7Muvx4PnpjRq8nACrfHXAy6');
$this->assertIsArray($accountHooks);
$this->assertEquals([
'ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7',
'ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7',
], $accountHooks);

# List of accounts by hook
$hookAccounts = $TxHookParser->hookAccounts('ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7');
$this->assertIsArray($hookAccounts);
$this->assertEquals([
'rU52Rrh1K1X7Muvx4PnpjRq8nACrfHXAy6',
], $hookAccounts);

$this->assertFalse($TxHookParser->isHookCreated('ACD3E29170EB82FFF9F31A067566CD15F3A328F873F34A5D9644519C33D55EB7'));


}
}
Loading

0 comments on commit f1cfe39

Please sign in to comment.