{"id":13652693,"url":"https://github.com/nervosnetwork/keyper","last_synced_at":"2025-04-23T03:31:15.207Z","repository":{"id":42929553,"uuid":"239942479","full_name":"nervosnetwork/keyper","owner":"nervosnetwork","description":null,"archived":true,"fork":false,"pushed_at":"2022-03-26T04:29:00.000Z","size":1874,"stargazers_count":8,"open_issues_count":13,"forks_count":6,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-19T10:39:14.494Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nervosnetwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-12T06:26:18.000Z","updated_at":"2023-01-27T21:20:17.000Z","dependencies_parsed_at":"2022-08-30T07:40:16.286Z","dependency_job_id":null,"html_url":"https://github.com/nervosnetwork/keyper","commit_stats":null,"previous_names":["ququzone/keyper"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nervosnetwork%2Fkeyper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nervosnetwork%2Fkeyper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nervosnetwork%2Fkeyper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nervosnetwork%2Fkeyper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nervosnetwork","download_url":"https://codeload.github.com/nervosnetwork/keyper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250365508,"owners_count":21418697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T02:01:01.753Z","updated_at":"2025-04-23T03:31:14.713Z","avatar_url":"https://github.com/nervosnetwork.png","language":"TypeScript","funding_links":[],"categories":["Tools"],"sub_categories":["Experimental Projects \u0026 Demos"],"readme":"Keyper\n======\n\n\u003e Keyper is still under development and NOT production ready. Future versions may introduce interface changes which are not backwards compatibile with this version.\n\nKeyper is an ownership layer for the Nervos CKB blockchain.\n\nNervos LockScripts provide a high level of flexibility, but it can be challenging for wallets to support all the different variations. Keyper is specification for the efficient management of LockScripts. The Keyper reference implementation is written in TypeScript.\n\nKeyper's interface specification provides developers with a standardized way to interact with any Keyper-enabled wallet. This can include mainstream wallets of any type, including integrated wallets in applications, web browser based wallets, and hardware wallets.\n\nThe Keyper project is divided into two sub-projects: `specs` and `container`. \n\nThe `specs` subproject contains all specification definitions and tool class support.\n\nThe `container` subproject is designed to support the loading of custom LockScripts within wallets.\n\n## Key Manager (Wallet) Integration\n\nA Key Manager is the component of a wallet responsible for managing the user's private keys. Private keys can be either raw private keys or HD wallet keys.\n\nA Key Manager should integrate the `@nervosnetwork/keyper-container` module or the `@nervosnetwork/keyper-container` protocol interface in order to support the Keyper architecture.\n\n```\ninterface PublicKey {\n  payload: Bytes\n  algorithm: SignatureAlgorithm\n}\n\ninterface SignProvider {\n  sign(context: SignContext, message: Bytes): Promise\u003cBytes\u003e\n}\n\ninterface KeyManager {\n  addLockScript(lockScript: LockScript): void\n  addPublicKey(publicKey: PublicKey): void\n  removePublicKey(publicKey: PublicKey): void\n}\n```\n\n## dApp Integration\n\n```\ninterface TransactionMeta {\n  name: string\n  script: Script\n  deps: CellDep[]\n  headers?: Hash256[]\n}\n\ninterface LockHashWithMeta {\n  hash: Hash256\n  meta: TransactionMeta\n}\n\ninterface ContainerService {\n  getAllLockHashesAndMeta(): Promise\u003cLockHashWithMeta[]\u003e\n  sign(context: SignContext, rawTx: RawTransaction, config: Config): Promise\u003cRawTransaction\u003e\n  send(tx: RawTransaction): Promise\u003cHash256\u003e\n}\n```\n\n## LockScript Specification\n\n```\ninterface LockScript {\n  readonly name: string;\n  readonly codeHash: Hash256;\n  readonly hashType: ScriptHashType;\n  setProvider(provider: SignProvider): void;\n  script(publicKey: string): Script;\n  deps(): CellDep[];\n  headers?(): Hash256[];\n  signatureAlgorithm(): SignatureAlgorithm;\n  sign(context: SignContext, rawTx: RawTransaction, config: Config): Promise\u003cRawTransaction\u003e;\n}\n```\n\nThe LockScript basic information keys are `name`, `codeHash` and `hashType`.\n\nThe `setProvider` key is a callback function for implementation of the underlying signature algorithm. This is provided by Keyper `container`.\n\nFor example, below is the Keyper Scatter `secp256k1` signature algorithm implementation:\n\n```\npublic sign(context: SignContext, message: Bytes): Promise\u003cBytes\u003e {\n  const key = keys[context.publicKey];\n  if (!key) {\n    throw new Error(`no key for address: ${context.address}`);\n  }\n  const privateKey = keystore.decrypt(key, context.password);\n\n  const ec = new EC('secp256k1');\n  const keypair = ec.keyFromPrivate(privateKey);\n  const msg = typeof message === 'string' ? hexToBytes(message) : message;\n  let { r, s, recoveryParam } = keypair.sign(msg, {\n    canonical: true,\n  });\n  if (recoveryParam === null){\n    throw new Error('Fail to sign the message');\n  }\n  const fmtR = r.toString(16).padStart(64, '0');\n  const fmtS = s.toString(16).padStart(64, '0');\n  const signature = `0x${fmtR}${fmtS}${this.padToEven(recoveryParam.toString(16))}`;\n  return signature;\n}\n```\n\nThe `script` key is a method that implments public key to Script transfer. Below is a Secp256k1 implementation:\n\n```\npublic script(publicKey: string): Script {\n  const args = utils.blake160(publicKey);\n  return {\n    codeHash: this.codeHash,\n    hashType: this.hashType,\n    args: `0x${Buffer.from(args).toString(\"hex\")}`\n  };\n}\n```\n\nThe `deps` and `headers` keys contain LockScript source details. Below is secp256k1 implementation:\n\n```\npublic deps(): CellDep[] {\n  return [{\n    outPoint: {\n      txHash: \"0x84dcb061adebff4ef93d57c975ba9058a9be939d79ea12ee68003f6492448890\",\n      index: \"0x0\",\n    },\n    depType: \"depGroup\",\n  }];\n}\n```\n\nThe `signatureAlgorithm` key returns the supported signature algorithm for this LockScript.\n\nThe `sign` key is a method that implements signing functionality for a transaction. Partial signatures can be accomplished using the `config` parameter. Below is seck256k1 signing example:\n\n```\npublic async sign(context: SignContext, rawTx: RawTransaction, config: Config = {index: 0, length: -1}): Promise\u003cRawTransaction\u003e {\n  const txHash = utils.rawTransactionToHash(rawTx);\n\n  if (config.length  === -1) {\n    config.length = rawTx.witnesses.length;\n  }\n\n  if (config.length + config.index \u003e rawTx.witnesses.length) {\n    throw new Error(\"request config error\");\n  }\n  if (typeof rawTx.witnesses[config.index] !== 'object') {\n    throw new Error(\"first witness in the group should be type of WitnessArgs\");\n  }\n\n  const emptyWitness = {\n    // @ts-ignore\n    ...rawTx.witnesses[config.index],\n    lock: `0x${'0'.repeat(130)}`,\n  };\n\n  const serializedEmptyWitnessBytes = utils.hexToBytes(utils.serializeWitnessArgs(emptyWitness));\n  const serialziedEmptyWitnessSize = serializedEmptyWitnessBytes.length;\n\n  const s = utils.blake2b(32, null, null, utils.PERSONAL);\n  s.update(utils.hexToBytes(txHash));\n  s.update(utils.hexToBytes(utils.toHexInLittleEndian(`0x${numberToBN(serialziedEmptyWitnessSize).toString(16)}`, 8)));\n  s.update(serializedEmptyWitnessBytes);\n\n  for (let i = config.index + 1; i \u003c config.index + config.length; i++) {\n    const w = rawTx.witnesses[i];\n    // @ts-ignore\n    const bytes = utils.hexToBytes(typeof w === 'string' ? w : utils.serializeWitnessArgs(w));\n    s.update(utils.hexToBytes(utils.toHexInLittleEndian(`0x${numberToBN(bytes.length).toString(16)}`, 8)));\n    s.update(bytes);\n  }\n\n  const message = `0x${s.digest('hex')}`;\n  const signd = await this.provider.sign(context, message);\n  // @ts-ignore\n  rawTx.witnesses[config.index].lock = signd;\n  // @ts-ignore\n  rawTx.witnesses[config.index] = utils.serializeWitnessArgs(rawTx.witnesses[config.index]);\n\n  return rawTx;\n}\n```\n\n## Development of Keyper\n\n### Prerequisites\n\nThe following must be installed available to build this project.\n\n- NPM https://docs.npmjs.com/downloading-and-installing-node-js-and-npm\n- Yarn https://classic.yarnpkg.com/en/docs/install\n\n### Setup and Building\n\nInstall all dependencies. This must be run once after cloning the repository.\n```\nyarn install\n```\n\nClean old builds, install dependencies, and bootstrap the project. This should be run after cloning, and can be run repeatedly when needed.\n```\nyarn run reboot\n```\n\nBuild all project components.\n```\nyarn run build\n```\n\nTest all project components.\n```\nyarn run test\n```\n\n## Installing as a Dependency\n\nTo install Keyper as a dependency in another project without manually building use the following.\n\n```\nnpm i @nervosnetwork/keyper-specs\nnpm i @nervosnetwork/keyper-container\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnervosnetwork%2Fkeyper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnervosnetwork%2Fkeyper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnervosnetwork%2Fkeyper/lists"}