@holochain/client
Advanced tools
Comparing version 0.11.5 to 0.11.6
@@ -0,1 +1,22 @@ | ||
/** | ||
* Defines AppAgentWebsocket, an easy-to-use websocket implementation of the | ||
* Conductor API for apps, restricted to a single app provided on initialization | ||
* | ||
* const appWs = AppWebsocket.connect('ws://127.0.0.1:9000') | ||
* | ||
* const client = new AppAgentWebsocket(appWs, 'my_installed_app_id') | ||
* | ||
* client.callZome({ | ||
* role_name: 'my_role_name' // role_name is unique per app, so you can unambiguously identify your dna with role_name in this client, | ||
* zome_name: 'zome', | ||
* fn_name: 'fn', | ||
* payload: { value: 'v' } | ||
* }) | ||
* .then(result => { | ||
* console.log('callZome returned with:', result) | ||
* }) | ||
* .catch(err => { | ||
* console.error('callZome errored with:', err) | ||
* }) | ||
*/ | ||
import Emittery, { UnsubscribeFunction } from "emittery"; | ||
@@ -2,0 +23,0 @@ import { AgentPubKey, InstalledAppId, RoleName } from "../../types.js"; |
@@ -0,1 +1,22 @@ | ||
/** | ||
* Defines AppAgentWebsocket, an easy-to-use websocket implementation of the | ||
* Conductor API for apps, restricted to a single app provided on initialization | ||
* | ||
* const appWs = AppWebsocket.connect('ws://127.0.0.1:9000') | ||
* | ||
* const client = new AppAgentWebsocket(appWs, 'my_installed_app_id') | ||
* | ||
* client.callZome({ | ||
* role_name: 'my_role_name' // role_name is unique per app, so you can unambiguously identify your dna with role_name in this client, | ||
* zome_name: 'zome', | ||
* fn_name: 'fn', | ||
* payload: { value: 'v' } | ||
* }) | ||
* .then(result => { | ||
* console.log('callZome returned with:', result) | ||
* }) | ||
* .catch(err => { | ||
* console.error('callZome errored with:', err) | ||
* }) | ||
*/ | ||
import Emittery from "emittery"; | ||
@@ -2,0 +23,0 @@ import { omit } from "lodash-es"; |
export * from "./types.js"; | ||
export * from "./websocket.js"; | ||
export { hashZomeCall } from "@holochain/serialization"; | ||
export * from "./util.js"; |
export * from "./types.js"; | ||
export * from "./websocket.js"; | ||
export { hashZomeCall } from "@holochain/serialization"; | ||
export * from "./util.js"; | ||
//# sourceMappingURL=index.js.map |
@@ -1,7 +0,9 @@ | ||
import { decode } from "@msgpack/msgpack"; | ||
import { decode, encode } from "@msgpack/msgpack"; | ||
import { invoke } from "@tauri-apps/api/tauri"; | ||
import Emittery from "emittery"; | ||
import { getLauncherEnvironment, isLauncher, signZomeCallTauri, } from "../../environments/launcher.js"; | ||
import { getLauncherEnvironment, isLauncher, } from "../../environments/launcher.js"; | ||
import { WsClient } from "../client.js"; | ||
import { catchError, DEFAULT_TIMEOUT, promiseTimeout, requesterTransformer, } from "../common.js"; | ||
import { getSigningCredentials, signZomeCall } from "../zome-call-signing.js"; | ||
import { getSigningCredentials } from "../zome-call-signing.js"; | ||
import { getNonceExpiration, randomNonce, signZomeCall } from "./util.js"; | ||
export class AppWebsocket extends Emittery { | ||
@@ -46,11 +48,50 @@ client; | ||
if (isLauncher) { | ||
const signedZomeCall = await signZomeCallTauri(req); | ||
const zomeCallUnsigned = { | ||
provenance: Array.from(req.provenance), | ||
cell_id: [Array.from(req.cell_id[0]), Array.from(req.cell_id[1])], | ||
zome_name: req.zome_name, | ||
fn_name: req.fn_name, | ||
payload: Array.from(encode(req.payload)), | ||
nonce: Array.from(randomNonce()), | ||
expires_at: getNonceExpiration(), | ||
}; | ||
const signedZomeCallTauri = await invoke("sign_zome_call", { zomeCallUnsigned }); | ||
const signedZomeCall = { | ||
provenance: Uint8Array.from(signedZomeCallTauri.provenance), | ||
cap_secret: null, | ||
cell_id: [ | ||
Uint8Array.from(signedZomeCallTauri.cell_id[0]), | ||
Uint8Array.from(signedZomeCallTauri.cell_id[1]), | ||
], | ||
zome_name: signedZomeCallTauri.zome_name, | ||
fn_name: signedZomeCallTauri.fn_name, | ||
payload: Uint8Array.from(signedZomeCallTauri.payload), | ||
signature: Uint8Array.from(signedZomeCallTauri.signature), | ||
expires_at: signedZomeCallTauri.expires_at, | ||
nonce: Uint8Array.from(signedZomeCallTauri.nonce), | ||
}; | ||
return signedZomeCall; | ||
} | ||
else { | ||
const signingCredentials = getSigningCredentials(req.cell_id); | ||
if (!signingCredentials) { | ||
throw new Error("cannot sign zome call: no signing credentials have been authorized"); | ||
const signingPropsForCell = getSigningCredentials(req.cell_id); | ||
if (!signingPropsForCell) { | ||
throw new Error("cannot sign zome call: signing properties have not been set"); | ||
} | ||
const signedZomeCall = await signZomeCall(signingCredentials, req.payload); | ||
// const unsignedZomeCall: CallZomeRequestUnsigned = { | ||
// ...req, | ||
// cap_secret: signingPropsForCell.capSecret, | ||
// provenance: signingPropsForCell.signingKey, | ||
// payload: encode(req.payload), | ||
// nonce: randomNonce(), | ||
// expires_at: getNonceExpiration(), | ||
// }; | ||
// const hashedZomeCall = await hashZomeCall(unsignedZomeCall); | ||
// const signature = nacl | ||
// .sign(hashedZomeCall, signingPropsForCell.keyPair.secretKey) | ||
// .subarray(0, nacl.sign.signatureLength); | ||
// const signedZomeCall: CallZomeRequestSigned = { | ||
// ...unsignedZomeCall, | ||
// signature, | ||
// }; | ||
const signedZomeCall = await signZomeCall(signingPropsForCell.capSecret, signingPropsForCell.signingKey, signingPropsForCell.keyPair, req); | ||
return signedZomeCall; | ||
@@ -57,0 +98,0 @@ } |
import nacl from "tweetnacl"; | ||
import { CallZomeRequest, CallZomeRequestSigned, Nonce256Bit } from "../api/app/index.js"; | ||
import type { CapSecret } from "../hdk/capabilities.js"; | ||
import type { AgentPubKey, CellId } from "../types.js"; | ||
import type { FunctionName, ZomeName } from "./admin/types.js"; | ||
import { CapSecret } from "../hdk/capabilities.js"; | ||
import { AgentPubKey, CellId } from "../types.js"; | ||
import { FunctionName, ZomeName } from "./admin/types.js"; | ||
import { AdminWebsocket } from "./admin/websocket.js"; | ||
export interface SigningCredentials { | ||
capSecret: CapSecret; | ||
keyPair: nacl.SignKeyPair; | ||
signingKey: AgentPubKey; | ||
} | ||
/** | ||
* Generate signing credentials for signing zome calls. | ||
* Generate and authorize a new key pair for signing zome calls. | ||
* | ||
@@ -20,5 +14,5 @@ * @param adminWs - An admin websocket connection to use for granting a | ||
*/ | ||
export declare const authorizeSigningCredentials: (adminWs: AdminWebsocket, cellId: CellId, functions: [ZomeName, FunctionName][]) => Promise<void>; | ||
export declare const authorizeSigningCredentials: (adminWs: Pick<AdminWebsocket, "grantZomeCallCapability">, cellId: CellId, functions: [ZomeName, FunctionName][]) => Promise<void>; | ||
/** | ||
* Get properties for signing a zome call to a cell. | ||
* Get properties for signing a zome call made to a cell. | ||
* | ||
@@ -28,13 +22,6 @@ * @param cellId - Cell id to be called. | ||
*/ | ||
export declare const getSigningCredentials: (cellId: CellId) => SigningCredentials | undefined; | ||
/** | ||
* Generates a key pair for signing zome calls. | ||
* | ||
* @returns The signing key pair and an agent pub key based on the public key. | ||
*/ | ||
export declare const generateSigningKeyPair: () => [nacl.SignKeyPair, Uint8Array]; | ||
export declare const randomCapSecret: () => CapSecret; | ||
export declare const randomNonce: () => Nonce256Bit; | ||
export declare const randomByteArray: (length: number) => Uint8Array; | ||
export declare const getNonceExpiration: () => number; | ||
export declare const signZomeCall: (signingCredentials: SigningCredentials, request: CallZomeRequest) => Promise<CallZomeRequestSigned>; | ||
export declare const getSigningCredentials: (cellId: CellId) => { | ||
capSecret: CapSecret; | ||
keyPair: nacl.SignKeyPair; | ||
signingKey: AgentPubKey; | ||
} | undefined; |
@@ -1,9 +0,6 @@ | ||
import { hashZomeCall } from "@holochain/serialization"; | ||
import { encode } from "@msgpack/msgpack"; | ||
import crypto from "crypto"; | ||
import nacl from "tweetnacl"; | ||
import { encodeHashToBase64 } from "../utils/base64.js"; | ||
const signingCredentials = new Map(); | ||
import { generateSigningKeyPair, grantSigningKey } from "./app/util.js"; | ||
const signingProps = new Map(); | ||
/** | ||
* Generate signing credentials for signing zome calls. | ||
* Generate and authorize a new key pair for signing zome calls. | ||
* | ||
@@ -16,9 +13,9 @@ * @param adminWs - An admin websocket connection to use for granting a | ||
export const authorizeSigningCredentials = async (adminWs, cellId, functions) => { | ||
const cellIdBase64 = encodeHashToBase64(cellId[0]).concat(encodeHashToBase64(cellId[1])); | ||
const [keyPair, signingKey] = generateSigningKeyPair(); | ||
const capSecret = await grantSigningKey(adminWs, cellId, functions, signingKey); | ||
signingCredentials.set(cellIdBase64, { capSecret, keyPair, signingKey }); | ||
const cellIdBase64 = encodeHashToBase64(cellId[0]).concat(encodeHashToBase64(cellId[1])); | ||
signingProps.set(cellIdBase64, { capSecret, keyPair, signingKey }); | ||
}; | ||
/** | ||
* Get properties for signing a zome call to a cell. | ||
* Get properties for signing a zome call made to a cell. | ||
* | ||
@@ -30,66 +27,4 @@ * @param cellId - Cell id to be called. | ||
const cellIdB64 = encodeHashToBase64(cellId[0]).concat(encodeHashToBase64(cellId[1])); | ||
return signingCredentials.get(cellIdB64); | ||
return signingProps.get(cellIdB64); | ||
}; | ||
/** | ||
* Generates a key pair for signing zome calls. | ||
* | ||
* @returns The signing key pair and an agent pub key based on the public key. | ||
*/ | ||
export const generateSigningKeyPair = () => { | ||
const keyPair = nacl.sign.keyPair(); | ||
const signingKey = new Uint8Array([132, 32, 36].concat(...keyPair.publicKey).concat(...[0, 0, 0, 0])); | ||
const keys = [keyPair, signingKey]; | ||
return keys; | ||
}; | ||
export const randomCapSecret = () => randomByteArray(64); | ||
export const randomNonce = () => randomByteArray(32); | ||
export const randomByteArray = (length) => { | ||
if (typeof window === "object" && | ||
"crypto" in window && | ||
"getRandomValues" in window.crypto) { | ||
return window.crypto.getRandomValues(new Uint8Array(length)); | ||
} | ||
else { | ||
return new Uint8Array(crypto.randomBytes(length)); | ||
} | ||
}; | ||
export const getNonceExpiration = () => (Date.now() + 5 * 60 * 1000) * 1000; // 5 mins from now in microseconds | ||
export const signZomeCall = async (signingCredentials, request) => { | ||
const unsignedZomeCallPayload = { | ||
cap_secret: signingCredentials.capSecret, | ||
cell_id: request.cell_id, | ||
zome_name: request.zome_name, | ||
fn_name: request.fn_name, | ||
provenance: signingCredentials.signingKey, | ||
payload: encode(request.payload), | ||
nonce: randomNonce(), | ||
expires_at: getNonceExpiration(), | ||
}; | ||
const hashedZomeCall = await hashZomeCall(unsignedZomeCallPayload); | ||
const signature = nacl | ||
.sign(hashedZomeCall, signingCredentials.keyPair.secretKey) | ||
.subarray(0, nacl.sign.signatureLength); | ||
const signedZomeCall = { | ||
...unsignedZomeCallPayload, | ||
signature, | ||
}; | ||
return signedZomeCall; | ||
}; | ||
const grantSigningKey = async (admin, cellId, functions, signingKey) => { | ||
const capSecret = randomCapSecret(); | ||
await admin.grantZomeCallCapability({ | ||
cell_id: cellId, | ||
cap_grant: { | ||
tag: `signing-key-${encodeHashToBase64(cellId[0])}-${encodeHashToBase64(cellId[1])}`, | ||
functions, | ||
access: { | ||
Assigned: { | ||
secret: capSecret, | ||
assignees: [signingKey], | ||
}, | ||
}, | ||
}, | ||
}); | ||
return capSecret; | ||
}; | ||
//# sourceMappingURL=zome-call-signing.js.map |
@@ -1,2 +0,1 @@ | ||
import { CallZomeRequest, CallZomeRequestSigned } from "../api/index.js"; | ||
import { InstalledAppId } from "../types.js"; | ||
@@ -16,3 +15,2 @@ export interface LauncherEnvironment { | ||
} | ||
export declare const signZomeCallTauri: (req: CallZomeRequest) => Promise<CallZomeRequestSigned>; | ||
export {}; |
@@ -1,34 +0,4 @@ | ||
import { encode } from "@msgpack/msgpack"; | ||
import { getNonceExpiration, randomNonce, } from "../api/index.js"; | ||
const __HC_LAUNCHER_ENV__ = "__HC_LAUNCHER_ENV__"; | ||
export const isLauncher = typeof window === "object" && __HC_LAUNCHER_ENV__ in window; | ||
export const getLauncherEnvironment = () => isLauncher ? window[__HC_LAUNCHER_ENV__] : undefined; | ||
export const signZomeCallTauri = async (req) => { | ||
const zomeCallUnsigned = { | ||
provenance: Array.from(req.provenance), | ||
cell_id: [Array.from(req.cell_id[0]), Array.from(req.cell_id[1])], | ||
zome_name: req.zome_name, | ||
fn_name: req.fn_name, | ||
payload: Array.from(encode(req.payload)), | ||
nonce: Array.from(randomNonce()), | ||
expires_at: getNonceExpiration(), | ||
}; | ||
const { invoke } = await import("@tauri-apps/api"); | ||
const signedZomeCallTauri = await invoke("sign_zome_call", { zomeCallUnsigned }); | ||
const signedZomeCall = { | ||
provenance: Uint8Array.from(signedZomeCallTauri.provenance), | ||
cap_secret: null, | ||
cell_id: [ | ||
Uint8Array.from(signedZomeCallTauri.cell_id[0]), | ||
Uint8Array.from(signedZomeCallTauri.cell_id[1]), | ||
], | ||
zome_name: signedZomeCallTauri.zome_name, | ||
fn_name: signedZomeCallTauri.fn_name, | ||
payload: Uint8Array.from(signedZomeCallTauri.payload), | ||
signature: Uint8Array.from(signedZomeCallTauri.signature), | ||
expires_at: signedZomeCallTauri.expires_at, | ||
nonce: Uint8Array.from(signedZomeCallTauri.nonce), | ||
}; | ||
return signedZomeCall; | ||
}; | ||
//# sourceMappingURL=launcher.js.map |
@@ -1,2 +0,2 @@ | ||
import { randomByteArray } from "../api/zome-call-signing.js"; | ||
import { randomByteArray } from "../api/app/util.js"; | ||
/** From https://github.com/holochain/holochain/blob/develop/crates/holo_hash/src/hash_type/primitive.rs */ | ||
@@ -3,0 +3,0 @@ export function fakeEntryHash() { |
{ | ||
"name": "@holochain/client", | ||
"version": "0.11.5", | ||
"version": "0.11.6", | ||
"description": "A JavaScript client for the Holochain Conductor API", | ||
@@ -5,0 +5,0 @@ "author": "Holochain Foundation <info@holochain.org> (http://holochain.org)", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
101101
83
1775