Skip to content

Commit

Permalink
feat: target_canister to be handled only for method install_chunked_code
Browse files Browse the repository at this point in the history
  • Loading branch information
peterpeterparker committed Nov 27, 2024
1 parent 7b119b3 commit b96faf0
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 39 deletions.
8 changes: 4 additions & 4 deletions packages/agent/src/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,20 +657,20 @@ export type ManagementCanisterRecord = _SERVICE;
*/
export function getManagementCanister(config: CallConfig): ActorSubclass<ManagementCanisterRecord> {
function transform(
_methodName: string,
methodName: string,
args: Record<string, unknown> & { canister_id: string; target_canister?: unknown }[],
) {
if (config.effectiveCanisterId) {
return { effectiveCanisterId: Principal.from(config.effectiveCanisterId) };
}
const first = args[0];
let effectiveCanisterId = Principal.fromHex('');
if (first && typeof first === 'object' && first.target_canister && methodName === "install_chunked_code") {
effectiveCanisterId = Principal.from(first.target_canister);
}
if (first && typeof first === 'object' && first.canister_id) {
effectiveCanisterId = Principal.from(first.canister_id as unknown);
}
if (first && typeof first === 'object' && first.target_canister) {
effectiveCanisterId = Principal.from(first.target_canister);
}
return { effectiveCanisterId };
}

Expand Down
107 changes: 72 additions & 35 deletions packages/agent/src/agent/http/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
Actor,
AnonymousIdentity,
fromHex,
getManagementCanister,
getManagementCanister, type ManagementCanisterRecord, type ActorSubclass,
SignIdentity,
toHex,
} from '../..';
Expand Down Expand Up @@ -938,7 +938,7 @@ test('it should handle calls against the ic-management canister that succeed', a
expect(status).toMatchSnapshot();
});

test('it should use target_canister as effective canister id for calls against the ic-management canister', async () => {
describe("transform", () => {
const identity = new AnonymousIdentity();

// Response generated by calling a locally deployed replica of the management canister, cloned using fetchCloner
Expand All @@ -965,49 +965,86 @@ test('it should use target_canister as effective canister id for calls against t
});
});

// Mock time so certificates can be accurately decoded
jest.useFakeTimers();
jest.setSystemTime(mockResponse.now);
let management: ActorSubclass<ManagementCanisterRecord>;
let spy: jest.SpyInstance;

beforeEach(async () => {
// Mock time so certificates can be accurately decoded
jest.useFakeTimers();
jest.setSystemTime(mockResponse.now);

// Pass in rootKey from replica (used because test was written using local replica)
const agent = await HttpAgent.createSync({
identity,
fetch: mockFetch,
host: 'http://localhost:4943',
rootKey: fromHex(
'308182301d060d2b0601040182dc7c0503010201060c2b0601040182dc7c050302010361008be882f1985cccb53fd551571a42818014835ed8f8a27767669b67dd4a836eb0d62b327e3368a80615b0e4f472c73f7917c036dc9317dcb64b319a1efa43dd7c656225c061de359db6fdf7033ac1bff24c944c145e46ebdce2093680b6209a13',
),
});

// Pass in rootKey from replica (used because test was written using local replica)
const agent = await HttpAgent.createSync({
identity,
fetch: mockFetch,
host: 'http://localhost:4943',
rootKey: fromHex(
'308182301d060d2b0601040182dc7c0503010201060c2b0601040182dc7c050302010361008be882f1985cccb53fd551571a42818014835ed8f8a27767669b67dd4a836eb0d62b327e3368a80615b0e4f472c73f7917c036dc9317dcb64b319a1efa43dd7c656225c061de359db6fdf7033ac1bff24c944c145e46ebdce2093680b6209a13',
),
// Use management canister call
management = getManagementCanister({ agent });

// Important - override nonce when making request to ensure reproducible result
(Actor.agentOf(management) as HttpAgent).addTransform('update', async args => {
args.body.nonce = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]) as Nonce;
return args;
});

spy = jest.spyOn(Actor.agentOf(management) as HttpAgent, 'call');
});

// Use management canister call
const management = getManagementCanister({ agent });
test('it should use target_canister as effective canister id for calls against the ic-management canister', async () => {
const target_canister = Principal.from('bkyz2-fmaaa-aaaaa-qaaaq-cai');

await management.install_chunked_code({
arg: new Uint8Array([1, 2, 3]),
wasm_module_hash: new Uint8Array([4, 5, 6]),
mode: { install: null },
chunk_hashes_list: [],
target_canister,
store_canister: [],
sender_canister_version: [],
});

// Important - override nonce when making request to ensure reproducible result
(Actor.agentOf(management) as HttpAgent).addTransform('update', async args => {
args.body.nonce = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]) as Nonce;
return args;
expect(spy).toHaveBeenCalledWith(
Principal.fromHex(''),
expect.objectContaining({
effectiveCanisterId: target_canister,
}),
);
});

const spy = jest.spyOn(Actor.agentOf(management) as HttpAgent, 'call');
test('it should use canister_id as effective canister id for calls against the ic-management canister if target_canister is provided but install_chunked_code is not', async () => {
const target_canister = Principal.from('ryjl3-tyaaa-aaaaa-aaaba-cai');
const canister_id = Principal.from('bkyz2-fmaaa-aaaaa-qaaaq-cai');

const target_canister = Principal.from('bkyz2-fmaaa-aaaaa-qaaaq-cai');
await management.stop_canister({
canister_id,
target_canister
} as unknown as {canister_id: Principal, target_canister_id: Principal});

await management.install_chunked_code({
arg: new Uint8Array([1, 2, 3]),
wasm_module_hash: new Uint8Array([4, 5, 6]),
mode: { install: null },
chunk_hashes_list: [],
target_canister,
store_canister: [],
sender_canister_version: [],
expect(spy).toHaveBeenCalledWith(
Principal.fromHex(''),
expect.objectContaining({
effectiveCanisterId: canister_id,
}),
);
});

expect(spy).toHaveBeenCalledWith(
Principal.fromHex(''),
expect.objectContaining({
effectiveCanisterId: target_canister,
}),
);
test('it should use canister_id as effective canister id for calls against the ic-management canister', async () => {
const canister_id = Principal.from('bkyz2-fmaaa-aaaaa-qaaaq-cai');

await management.stop_canister({canister_id});

expect(spy).toHaveBeenCalledWith(
Principal.fromHex(''),
expect.objectContaining({
effectiveCanisterId: canister_id,
}),
);
});
});

/**
Expand Down

0 comments on commit b96faf0

Please sign in to comment.