Forráskód Böngészése

App-actions functionality (#75)

* add support for AppAction signatures (#51)

fixes https://github.com/Joystream/orion/issues/50

* New way to generate commitment and algorithm switch (#66)

* New way to generate commitment and algorithm switch

* Comment update

* Update secret key comments

Co-authored-by: l1.media <31551045+traumschule@users.noreply.github.com>

---------

Co-authored-by: l1.media <31551045+traumschule@users.noreply.github.com>

---------

Co-authored-by: WRadoslaw <92513933+WRadoslaw@users.noreply.github.com>
Co-authored-by: l1.media <31551045+traumschule@users.noreply.github.com>
attemka 1 éve
szülő
commit
a79cd449f2
8 módosított fájl, 328 hozzáadás és 5 törlés
  1. 1 0
      package.json
  2. 6 0
      schema.graphql
  3. 11 1
      src/config.ts
  4. 4 1
      src/main.ts
  5. 7 0
      src/models/Admin.ts
  6. 36 3
      src/resolvers/admin.ts
  7. 12 0
      src/resolvers/helpers.ts
  8. 251 0
      yarn.lock

+ 1 - 0
package.json

@@ -37,6 +37,7 @@
     "@graphql-tools/schema": "^8.3.1",
     "@graphql-tools/stitch": "^8.4.1",
     "@graphql-tools/url-loader": "^7.5.2",
+    "@polkadot/util-crypto": "^10.2.1",
     "@typegoose/auto-increment": "^1.3.0",
     "@typegoose/typegoose": "^9.8.1",
     "apollo-server-core": "^3.8.1",

+ 6 - 0
schema.graphql

@@ -57,6 +57,11 @@ input FeaturedVideoInput {
   videoId: ID!
 }
 
+type GeneratedSignature {
+  """App signature converted to hexadecimal string."""
+  signature: String!
+}
+
 type Mutation {
   """Add a single view to the target video's count"""
   addVideoView(categoryId: ID, channelId: ID!, videoId: ID!): EntityViewsInfo!
@@ -72,6 +77,7 @@ type Mutation {
   setCategoryFeaturedVideos(categoryId: ID!, videos: [FeaturedVideoInput!]!): [FeaturedVideo!]!
   setKillSwitch(isKilled: Boolean!): Admin!
   setVideoHero(newVideoHero: VideoHeroInput!): VideoHero!
+  signAppActionCommitment(assets: String!, creatorId: String!, rawAction: String!, rawAppActionMetadata: String!): GeneratedSignature!
 
   """Remove a single follow from the target channel"""
   unfollowChannel(channelId: ID!): ChannelFollowsInfo!

+ 11 - 1
src/config.ts

@@ -1,4 +1,6 @@
 import dotenv from 'dotenv'
+import { ed25519PairFromString } from '@polkadot/util-crypto'
+import { Keypair } from '@polkadot/util-crypto/types'
 
 const isDev = process.env.NODE_ENV === 'development'
 export const ADMIN_ROLE = 'ADMIN'
@@ -29,6 +31,7 @@ export class Config {
   private _mongoDBUri: string
   private _featuredContentSecret: string
   private _adminSecret: string
+  private _appKeypair: Keypair
   private _queryNodeUrl: string
   private _isDebugging: boolean
 
@@ -48,6 +51,10 @@ export class Config {
     return this._adminSecret
   }
 
+  get appKeypair(): Keypair {
+    return this._appKeypair
+  }
+
   get queryNodeUrl(): string {
     return this._queryNodeUrl
   }
@@ -62,7 +69,7 @@ export class Config {
     const rawPort = loadEnvVar('ORION_PORT', { defaultValue: '6116' })
     this._port = parseInt(rawPort)
 
-    const mongoHostname = loadEnvVar('ORION_MONGO_HOSTNAME', { devDefaultValue: 'localhost' })
+    const mongoHostname = loadEnvVar('ORION_MONGO_HOSTNAME', { devDefaultValue: '127.0.0.1' })
     const rawMongoPort = loadEnvVar('ORION_MONGO_PORT', { defaultValue: '27017' })
     const mongoDatabase = loadEnvVar('ORION_MONGO_DATABASE', { defaultValue: 'orion' })
 
@@ -70,6 +77,9 @@ export class Config {
 
     this._featuredContentSecret = loadEnvVar('ORION_FEATURED_CONTENT_SECRET')
     this._adminSecret = loadEnvVar('ORION_ADMIN_SECRET')
+    // Secret string that will be used to generate ED25519 key pair
+    const appPrivateKey = loadEnvVar('APP_PRIVATE_KEY')
+    this._appKeypair = ed25519PairFromString(appPrivateKey)
     this._queryNodeUrl = loadEnvVar('ORION_QUERY_NODE_URL')
 
     this._isDebugging = loadEnvVar('ORION_DEBUGGING', { defaultValue: 'false' }) === 'true'

+ 4 - 1
src/main.ts

@@ -1,6 +1,7 @@
 import config from './config'
 import Express from 'express'
 import { buildAggregates, connectMongoose, createServer } from './server'
+import { cryptoWaitReady } from '@polkadot/util-crypto'
 
 const main = async () => {
   config.loadConfig()
@@ -40,4 +41,6 @@ const wrapTask = async <T>(message: string, task: () => Promise<T>): Promise<T>
   }
 }
 
-main()
+wrapTask('Initializing WASM bridge for @polkadot/util-crypto', cryptoWaitReady).then(() => {
+  main()
+})

+ 7 - 0
src/models/Admin.ts

@@ -9,6 +9,13 @@ export class Admin {
   isKilled: boolean
 }
 
+@ObjectType()
+@ArgsType()
+export class GeneratedSignature {
+  @Field({ description: 'App signature converted to hexadecimal string.' })
+  signature: string
+}
+
 export const AdminModel = getModelForClass(Admin, { schemaOptions: { collection: 'admin' } })
 
 export const getAdminDoc = async (): Promise<DocumentType<Admin>> => {

+ 36 - 3
src/resolvers/admin.ts

@@ -1,6 +1,9 @@
-import { Authorized, Field, Mutation, Query, Resolver, Args, ArgsType } from 'type-graphql'
-import { getAdminDoc, Admin } from '../models/Admin'
-import { ADMIN_ROLE } from '../config'
+import { Args, ArgsType, Authorized, Field, Mutation, Query, Resolver } from 'type-graphql'
+import { Admin, GeneratedSignature, getAdminDoc } from '../models/Admin'
+import config, { ADMIN_ROLE } from '../config'
+import { ed25519Sign } from '@polkadot/util-crypto'
+import { u8aToHex, hexToU8a, isHex } from '@polkadot/util'
+import { generateAppActionCommitment } from './helpers'
 
 @ArgsType()
 class AdminInput implements Admin {
@@ -8,6 +11,18 @@ class AdminInput implements Admin {
   isKilled: boolean
 }
 
+@ArgsType()
+class AppActionSignatureInput {
+  @Field()
+  creatorId: string
+  @Field({ description: 'Hex string from UInt8Array' })
+  assets: string
+  @Field({ description: 'Hex string from UInt8Array' })
+  rawAction: string
+  @Field({ description: 'Hex string from UInt8Array' })
+  rawAppActionMetadata: string
+}
+
 @Resolver()
 export class AdminResolver {
   @Query(() => Admin, { nullable: false, description: 'Set killed instance' })
@@ -23,4 +38,22 @@ export class AdminResolver {
     await killSwitch.save()
     return { isKilled }
   }
+
+  @Mutation(() => GeneratedSignature)
+  async signAppActionCommitment(
+    @Args() { rawAppActionMetadata, rawAction, assets, creatorId }: AppActionSignatureInput
+  ) {
+    if (!isHex(assets) || !isHex(rawAction) || !isHex(rawAppActionMetadata)) {
+      throw new Error('One of input is not hex: assets, rawAction, rawAppActionMetadata')
+    }
+
+    const message = generateAppActionCommitment(
+      creatorId,
+      hexToU8a(assets),
+      hexToU8a(rawAction),
+      hexToU8a(rawAppActionMetadata)
+    )
+    const signature = ed25519Sign(message, config.appKeypair)
+    return { signature: u8aToHex(signature) }
+  }
 }

+ 12 - 0
src/resolvers/helpers.ts

@@ -1,3 +1,4 @@
+import { stringToHex, u8aToHex } from '@polkadot/util'
 import { OrionContext } from '../types'
 import { mapPeriods } from '../helpers'
 
@@ -46,3 +47,14 @@ export const getMostFollowedChannelsIds = (context: OrionContext, { period, limi
 
   return limitedFollows.filter((entity) => entity.follows).map((entity) => entity.id)
 }
+
+// preferably this would be imported from @joystream/js -> https://github.com/Joystream/joystream/pull/4586
+export const generateAppActionCommitment = (
+  creatorId: string,
+  assets: Uint8Array,
+  rawAction: Uint8Array,
+  rawAppActionMetadata: Uint8Array
+): string => {
+  const rawCommitment = [creatorId, u8aToHex(assets), u8aToHex(rawAction), u8aToHex(rawAppActionMetadata)]
+  return stringToHex(JSON.stringify(rawCommitment))
+}

+ 251 - 0
yarn.lock

@@ -455,6 +455,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@babel/runtime@npm:^7.20.6":
+  version: 7.20.6
+  resolution: "@babel/runtime@npm:7.20.6"
+  dependencies:
+    regenerator-runtime: ^0.13.11
+  checksum: 42a8504db21031b1859fbc0f52d698a3d2f5ada9519eb76c6f96a7e657d8d555732a18fe71ef428a67cc9fc81ca0d3562fb7afdc70549c5fec343190cbaa9b03
+  languageName: node
+  linkType: hard
+
 "@babel/template@npm:^7.16.7, @babel/template@npm:^7.3.3":
   version: 7.16.7
   resolution: "@babel/template@npm:7.16.7"
@@ -1087,6 +1096,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@noble/hashes@npm:1.1.3":
+  version: 1.1.3
+  resolution: "@noble/hashes@npm:1.1.3"
+  checksum: a6f9783d2a33fc528c8709532b1c26cc3f5866f79c66256e881b28c61a1585be3899b008aa4e5e2b4e01b95c713722f52591cbb18ec51aa0ec63e7eaece1b89c
+  languageName: node
+  linkType: hard
+
+"@noble/secp256k1@npm:1.7.0":
+  version: 1.7.0
+  resolution: "@noble/secp256k1@npm:1.7.0"
+  checksum: 540a2b8e527ee1e5522af1c430e54945ad373883cac983b115136cd0950efa1f2c473ee6a36d8e69b6809b3ee586276de62f5fa705c77a9425721e81bada8116
+  languageName: node
+  linkType: hard
+
 "@nodelib/fs.scandir@npm:2.1.3":
   version: 2.1.3
   resolution: "@nodelib/fs.scandir@npm:2.1.3"
@@ -1134,6 +1157,180 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@polkadot/networks@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/networks@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/util": 10.2.1
+    "@substrate/ss58-registry": ^1.35.0
+  checksum: e0c741731facc15edbe62b8dc84844cae651d27020af3586a0bc29307e3b9b21b4394d70f170826d69531b58ab45a86857b655f0e9bb7e29161aed1d0f624170
+  languageName: node
+  linkType: hard
+
+"@polkadot/util-crypto@npm:^10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/util-crypto@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@noble/hashes": 1.1.3
+    "@noble/secp256k1": 1.7.0
+    "@polkadot/networks": 10.2.1
+    "@polkadot/util": 10.2.1
+    "@polkadot/wasm-crypto": ^6.4.1
+    "@polkadot/x-bigint": 10.2.1
+    "@polkadot/x-randomvalues": 10.2.1
+    "@scure/base": 1.1.1
+    ed2curve: ^0.3.0
+    tweetnacl: ^1.0.3
+  peerDependencies:
+    "@polkadot/util": 10.2.1
+  checksum: 159860330435550eb3266c87a36d8076a602adb82724aa8109e32028f1148abbf2082f8eb89ebdbabb708e7190a746c328750b192b8e990d6bd06d1e3f7bf582
+  languageName: node
+  linkType: hard
+
+"@polkadot/util@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/util@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/x-bigint": 10.2.1
+    "@polkadot/x-global": 10.2.1
+    "@polkadot/x-textdecoder": 10.2.1
+    "@polkadot/x-textencoder": 10.2.1
+    "@types/bn.js": ^5.1.1
+    bn.js: ^5.2.1
+  checksum: e4ee46762e36410f8fd3cfd61b340030a72081387acae5cf4fdb14de4bb57fde81c2df02e78d8dc9f1df88ca3a606de1e85945d735ac0ac7996740fbb7970c0f
+  languageName: node
+  linkType: hard
+
+"@polkadot/wasm-bridge@npm:6.4.1":
+  version: 6.4.1
+  resolution: "@polkadot/wasm-bridge@npm:6.4.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+  peerDependencies:
+    "@polkadot/util": "*"
+    "@polkadot/x-randomvalues": "*"
+  checksum: 02d9cd1b5c2f6d0261004229751137ef829b38c12e0e844548ef356f9b65dc9a82ec4dcad32f4a156e3c8666b21ef4a8e0c2e5e0e1c51a51a2d7d00373f6f65e
+  languageName: node
+  linkType: hard
+
+"@polkadot/wasm-crypto-asmjs@npm:6.4.1":
+  version: 6.4.1
+  resolution: "@polkadot/wasm-crypto-asmjs@npm:6.4.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+  peerDependencies:
+    "@polkadot/util": "*"
+  checksum: 6c2bba5014c373dfc18ec82bb7779141bfaea7d90e3e198fee0bc8ba3078238fee9bf1bb7138a3cbb8b5ad01ade603c44ce838e17940a610fbeec6341a17a0f3
+  languageName: node
+  linkType: hard
+
+"@polkadot/wasm-crypto-init@npm:6.4.1":
+  version: 6.4.1
+  resolution: "@polkadot/wasm-crypto-init@npm:6.4.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/wasm-bridge": 6.4.1
+    "@polkadot/wasm-crypto-asmjs": 6.4.1
+    "@polkadot/wasm-crypto-wasm": 6.4.1
+  peerDependencies:
+    "@polkadot/util": "*"
+    "@polkadot/x-randomvalues": "*"
+  checksum: e1d30cae9588607cbbe35f539df2cb3fca6b69d65ab7907ca24183931953de0e8d7e61be4af7c30a05295a16a1a9255256a6420a049ddf38c155400f91187956
+  languageName: node
+  linkType: hard
+
+"@polkadot/wasm-crypto-wasm@npm:6.4.1":
+  version: 6.4.1
+  resolution: "@polkadot/wasm-crypto-wasm@npm:6.4.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/wasm-util": 6.4.1
+  peerDependencies:
+    "@polkadot/util": "*"
+  checksum: 21c72028d2e4333b54fb212980e3dc51827ffaf90364df1932205162859eab9b1be3a7767e1c3c5e8cfcf6ad2bc8cb9dafd3be59ada250b77679fa7ade67c646
+  languageName: node
+  linkType: hard
+
+"@polkadot/wasm-crypto@npm:^6.4.1":
+  version: 6.4.1
+  resolution: "@polkadot/wasm-crypto@npm:6.4.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/wasm-bridge": 6.4.1
+    "@polkadot/wasm-crypto-asmjs": 6.4.1
+    "@polkadot/wasm-crypto-init": 6.4.1
+    "@polkadot/wasm-crypto-wasm": 6.4.1
+    "@polkadot/wasm-util": 6.4.1
+  peerDependencies:
+    "@polkadot/util": "*"
+    "@polkadot/x-randomvalues": "*"
+  checksum: 2892834aa2357e5974257810be625b0f08a35a3ba1def4a87e4989636dc7a43691357fdbfbeab4595eb47cd90177dba3c0ce95e593219db7c488fdf450d86357
+  languageName: node
+  linkType: hard
+
+"@polkadot/wasm-util@npm:6.4.1":
+  version: 6.4.1
+  resolution: "@polkadot/wasm-util@npm:6.4.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+  peerDependencies:
+    "@polkadot/util": "*"
+  checksum: 6d5ef0aa9af7ca9fe23149793bd1fa9f864b41695b49ab5ae5c23b3ac761c310edf382fe0d0a0d812dc07b10a2d0b056de5750947867a94ab87ab51e176d94b3
+  languageName: node
+  linkType: hard
+
+"@polkadot/x-bigint@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/x-bigint@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/x-global": 10.2.1
+  checksum: 46e104ed1d3dc30fa4eda10e128e465a04a9a5dfced4e203dd16f50840ce8af44e60a351844afc26005c7c803a244d8101bd26a7d85c0df5efede3d9c823a752
+  languageName: node
+  linkType: hard
+
+"@polkadot/x-global@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/x-global@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+  checksum: 7032f7677916b402ff6bc0ee438ae18aa860909310aec7de535ddd45c40a4b46b26f2ddf78f2178a9a978f97ab8110b9e5fbce3701ea36183eb122cb4136c366
+  languageName: node
+  linkType: hard
+
+"@polkadot/x-randomvalues@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/x-randomvalues@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/x-global": 10.2.1
+  checksum: e7f32d7f432fb0fdc9386eb379012edb0f6836135222d4750a2858845c4f8188c297070fa79d947bf3b199e57b20aa23f1dc1cd318ff371f42ae929c90f2c151
+  languageName: node
+  linkType: hard
+
+"@polkadot/x-textdecoder@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/x-textdecoder@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/x-global": 10.2.1
+  checksum: e7edcb4f0321bf474ce47c71c89fa2b4685e3fa2b77c17bc346a9034d204a48a4855ff8c882fb1047531bd1e0893fd9367085ea223c555ea51fe509a44347826
+  languageName: node
+  linkType: hard
+
+"@polkadot/x-textencoder@npm:10.2.1":
+  version: 10.2.1
+  resolution: "@polkadot/x-textencoder@npm:10.2.1"
+  dependencies:
+    "@babel/runtime": ^7.20.6
+    "@polkadot/x-global": 10.2.1
+  checksum: 758daf0f192e88ceeb331e82a97fc2a06db9cb88fcbefb906cf7bda1b5be988ec9c3ca0ffe599852e1b167cd3b95309c16d3fa09cb9279b0f2d8eb2b017edda7
+  languageName: node
+  linkType: hard
+
 "@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2":
   version: 1.1.2
   resolution: "@protobufjs/aspromise@npm:1.1.2"
@@ -1207,6 +1404,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@scure/base@npm:1.1.1":
+  version: 1.1.1
+  resolution: "@scure/base@npm:1.1.1"
+  checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309
+  languageName: node
+  linkType: hard
+
 "@shelf/jest-mongodb@npm:^3.0.0":
   version: 3.0.0
   resolution: "@shelf/jest-mongodb@npm:3.0.0"
@@ -1246,6 +1450,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@substrate/ss58-registry@npm:^1.35.0":
+  version: 1.36.0
+  resolution: "@substrate/ss58-registry@npm:1.36.0"
+  checksum: 4a804142d8f8cc693c2816e3eb4b5b195cc1d612f0f935b51ed9c77f980064b56b8001aae4aab7ec04d13f9ff7cde8346ac4d5e69ebabe52309713257dafb216
+  languageName: node
+  linkType: hard
+
 "@tootallnate/once@npm:2":
   version: 2.0.0
   resolution: "@tootallnate/once@npm:2.0.0"
@@ -1358,6 +1569,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/bn.js@npm:^5.1.1":
+  version: 5.1.1
+  resolution: "@types/bn.js@npm:5.1.1"
+  dependencies:
+    "@types/node": "*"
+  checksum: e50ed2dd3abe997e047caf90e0352c71e54fc388679735217978b4ceb7e336e51477791b715f49fd77195ac26dd296c7bad08a3be9750e235f9b2e1edb1b51c2
+  languageName: node
+  linkType: hard
+
 "@types/body-parser@npm:*, @types/body-parser@npm:1.19.2":
   version: 1.19.2
   resolution: "@types/body-parser@npm:1.19.2"
@@ -2333,6 +2553,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"bn.js@npm:^5.2.1":
+  version: 5.2.1
+  resolution: "bn.js@npm:5.2.1"
+  checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3
+  languageName: node
+  linkType: hard
+
 "body-parser@npm:1.20.0, body-parser@npm:^1.19.0":
   version: 1.20.0
   resolution: "body-parser@npm:1.20.0"
@@ -3053,6 +3280,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"ed2curve@npm:^0.3.0":
+  version: 0.3.0
+  resolution: "ed2curve@npm:0.3.0"
+  dependencies:
+    tweetnacl: 1.x.x
+  checksum: 6dfbe2310aa5a47372c9dd2fd920be140c8d52aea5793d716a3e3865d2ceaeaf639a7653e5492dfe3b4910eaf65c09a1d5132580afe2fdca18a75ebb428a52f2
+  languageName: node
+  linkType: hard
+
 "ee-first@npm:1.1.1":
   version: 1.1.1
   resolution: "ee-first@npm:1.1.1"
@@ -5625,6 +5861,7 @@ __metadata:
     "@graphql-tools/stitch": ^8.4.1
     "@graphql-tools/url-loader": ^7.5.2
     "@joystream/prettier-config": ^1.0.0
+    "@polkadot/util-crypto": ^10.2.1
     "@shelf/jest-mongodb": ^3.0.0
     "@typegoose/auto-increment": ^1.3.0
     "@typegoose/typegoose": ^9.8.1
@@ -6003,6 +6240,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"regenerator-runtime@npm:^0.13.11":
+  version: 0.13.11
+  resolution: "regenerator-runtime@npm:0.13.11"
+  checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4
+  languageName: node
+  linkType: hard
+
 "regexpp@npm:^3.2.0":
   version: 3.2.0
   resolution: "regexpp@npm:3.2.0"
@@ -6917,6 +7161,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"tweetnacl@npm:1.x.x, tweetnacl@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "tweetnacl@npm:1.0.3"
+  checksum: e4a57cac188f0c53f24c7a33279e223618a2bfb5fea426231991652a13247bea06b081fd745d71291fcae0f4428d29beba1b984b1f1ce6f66b06a6d1ab90645c
+  languageName: node
+  linkType: hard
+
 "type-check@npm:^0.4.0, type-check@npm:~0.4.0":
   version: 0.4.0
   resolution: "type-check@npm:0.4.0"