Skip to content

Commit 1f86775

Browse files
hashAction
1 parent 4895b68 commit 1f86775

File tree

3 files changed

+181
-17
lines changed

3 files changed

+181
-17
lines changed

packages/utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@holochain-open-dev/utils",
3-
"version": "0.300.3",
3+
"version": "0.300.4",
44
"description": "Common utilities to build Holochain web applications",
55
"author": "[email protected]",
66
"main": "dist/index.js",

packages/utils/src/hash.ts

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import {
2+
Action,
3+
ActionHash,
4+
ActionType,
25
Entry,
36
EntryHash,
47
HoloHash,
@@ -63,10 +66,141 @@ export function isHash(hash: string): boolean {
6366
].find(prefix => hash.startsWith(`u${prefix}`));
6467
}
6568

69+
// The hash of an action depends on the order of keys
70+
// To make it deterministic and aligned with hashes of actions coming from holochain
71+
// we need to enforce the order of actions to align with the holochain ones
72+
function sortActionKeys(action: Action): Action {
73+
const weight = (action as any).weight;
74+
switch (action.type) {
75+
case ActionType.Dna:
76+
return {
77+
type: ActionType.Dna,
78+
author: action.author,
79+
timestamp: action.timestamp,
80+
hash: action.hash,
81+
};
82+
case ActionType.AgentValidationPkg:
83+
return {
84+
type: ActionType.AgentValidationPkg,
85+
author: action.author,
86+
timestamp: action.timestamp,
87+
action_seq: action.action_seq,
88+
prev_action: action.prev_action,
89+
membrane_proof: action.membrane_proof,
90+
};
91+
case ActionType.InitZomesComplete:
92+
return {
93+
type: ActionType.InitZomesComplete,
94+
author: action.author,
95+
timestamp: action.timestamp,
96+
action_seq: action.action_seq,
97+
prev_action: action.prev_action,
98+
};
99+
case ActionType.CreateLink:
100+
return {
101+
type: ActionType.CreateLink,
102+
author: action.author,
103+
timestamp: action.timestamp,
104+
action_seq: action.action_seq,
105+
prev_action: action.prev_action,
106+
107+
base_address: action.base_address,
108+
target_address: action.target_address,
109+
zome_index: action.zome_index,
110+
link_type: action.link_type,
111+
tag: action.tag,
112+
weight: {
113+
bucket_id: action.weight.bucket_id,
114+
units: action.weight.units,
115+
},
116+
};
117+
case ActionType.DeleteLink:
118+
return {
119+
type: ActionType.DeleteLink,
120+
author: action.author,
121+
timestamp: action.timestamp,
122+
action_seq: action.action_seq,
123+
prev_action: action.prev_action,
124+
base_address: action.base_address,
125+
link_add_address: action.link_add_address,
126+
};
127+
case ActionType.OpenChain:
128+
return {
129+
type: ActionType.OpenChain,
130+
author: action.author,
131+
timestamp: action.timestamp,
132+
action_seq: action.action_seq,
133+
prev_action: action.prev_action,
134+
prev_dna_hash: action.prev_dna_hash,
135+
};
136+
case ActionType.CloseChain:
137+
return {
138+
type: ActionType.CloseChain,
139+
author: action.author,
140+
timestamp: action.timestamp,
141+
action_seq: action.action_seq,
142+
prev_action: action.prev_action,
143+
new_dna_hash: action.new_dna_hash,
144+
};
145+
case ActionType.Create:
146+
return {
147+
type: ActionType.Create,
148+
author: action.author,
149+
timestamp: action.timestamp,
150+
action_seq: action.action_seq,
151+
prev_action: action.prev_action,
152+
entry_type: action.entry_type,
153+
entry_hash: action.entry_hash,
154+
weight: {
155+
bucket_id: weight.bucket_id,
156+
units: weight.units,
157+
rate_bytes: weight.rate_bytes,
158+
},
159+
} as any;
160+
case ActionType.Update:
161+
return {
162+
type: ActionType.Update,
163+
author: action.author,
164+
timestamp: action.timestamp,
165+
action_seq: action.action_seq,
166+
prev_action: action.prev_action,
167+
168+
original_action_address: action.original_action_address,
169+
original_entry_address: action.original_entry_address,
170+
171+
entry_type: action.entry_type,
172+
entry_hash: action.entry_hash,
173+
weight: {
174+
bucket_id: weight.bucket_id,
175+
units: weight.units,
176+
rate_bytes: weight.rate_bytes,
177+
},
178+
} as any;
179+
case ActionType.Delete:
180+
return {
181+
type: ActionType.Delete,
182+
author: action.author,
183+
timestamp: action.timestamp,
184+
action_seq: action.action_seq,
185+
prev_action: action.prev_action,
186+
187+
deletes_address: action.deletes_address,
188+
deletes_entry_address: action.deletes_address,
189+
weight: {
190+
bucket_id: weight.bucket_id,
191+
units: weight.units,
192+
},
193+
} as any;
194+
}
195+
}
196+
197+
export function hashAction(action: Action): ActionHash {
198+
return hash(sortActionKeys(action), HashType.ACTION);
199+
}
200+
66201
// From https://github.com/holochain/holochain/blob/dc0cb61d0603fa410ac5f024ed6ccfdfc29715b3/crates/holo_hash/src/encode.rs
67202
export function hash(content: any, type: HashType): HoloHash {
68-
const obj = isPlainObject(content) ? sortKeys(content) : content;
69-
const bytesHash: Uint8Array = blake.blake2b(encode(obj), null, 32);
203+
const bytesHash: Uint8Array = blake.blake2b(encode(content), null, 32);
70204

71205
const fullhash = new Uint8Array([
72206
...Base64.toUint8Array(getPrefix(type)),

packages/utils/tests/hash.test.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,48 @@
1-
import { encodeHashToBase64 } from "@holochain/client";
2-
import { test, assert } from "vitest";
3-
import { hash, HashType } from "../src/index.js";
1+
import {
2+
ActionType,
3+
decodeHashFromBase64,
4+
encodeHashToBase64,
5+
fakeAgentPubKey,
6+
fakeDnaHash,
7+
} from '@holochain/client';
8+
import { assert, test } from 'vitest';
49

5-
test("test hash", async () => {
6-
const h = hash("asdfsadf", HashType.ACTION);
7-
assert.equal(
8-
encodeHashToBase64(h),
9-
"uhCkkboYJIWR7sh8l4i7OTWdOm3iY9ikij1-D9JLoqar86YF4KNht"
10-
);
10+
import {
11+
HashType,
12+
fakeCreateAction,
13+
fakeDeleteLinkAction,
14+
hash,
15+
hashAction,
16+
} from '../src/index.js';
17+
18+
test('test hash', async () => {
19+
const h = hash('asdfsadf', HashType.ENTRY);
20+
assert.equal(
21+
encodeHashToBase64(h),
22+
'uhCEkboYJIWR7sh8l4i7OTWdOm3iY9ikij1-D9JLoqar86YF4KNht',
23+
);
1124
});
1225

13-
test("test ordering", async () => {
14-
assert.deepEqual(
15-
hash({ first: 1, second: 2 }, HashType.ACTION),
16-
hash({ second: 2, first: 1 }, HashType.ACTION)
17-
);
26+
test('test action hash', async () => {
27+
const h = hashAction({
28+
type: ActionType.Dna,
29+
timestamp: 1728551987340000,
30+
hash: decodeHashFromBase64(
31+
'uhC0kxfhaMcQ5m73u42s3tiWXWOeS2mTy7Ovj8oHn0Bilvmndtzun',
32+
),
33+
author: decodeHashFromBase64(
34+
'uhCAky816aQkHDHniTt-F0ccT0bAZFz6wUAq5jL7H1q8Ii8h62MG8',
35+
),
36+
});
37+
assert.equal(
38+
encodeHashToBase64(h),
39+
'uhCkkABYPHZNDn-U4AVhuev41b54I4xCPuLMOD-eLxy8lRNT9Fd7f',
40+
);
1841
});
42+
43+
// test("test ordering", async () => {
44+
// assert.deepEqual(
45+
// hash({ first: 1, second: 2 }, HashType.ACTION),
46+
// hash({ second: 2, first: 1 }, HashType.ACTION)
47+
// );
48+
// });

0 commit comments

Comments
 (0)