From a1ec37930439ae0df585491aee2b736cfa3ef13b Mon Sep 17 00:00:00 2001 From: longxiaoLX <2411847892@qq.com> Date: Sun, 17 Mar 2024 18:49:33 +0800 Subject: [PATCH] Package program client --- .env | 2 + README.md | 23 ++++ package-lock.json | 200 +++++++++++++++++++++++++--- package.json | 4 +- scripts/device-did-ft.ts | 267 ++++++++++++++++++++++++++++++++++++++ scripts/device-did-run.ts | 142 ++++++++++++++++++++ tsconfig.json | 26 ++-- utils/utils.ts | 37 +++++- yarn.lock | 106 ++++++++++++++- 9 files changed, 774 insertions(+), 33 deletions(-) create mode 100644 .env create mode 100644 README.md create mode 100644 scripts/device-did-ft.ts create mode 100644 scripts/device-did-run.ts diff --git a/.env b/.env new file mode 100644 index 0000000..8e38229 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +ANCHOR_PROVIDER_URL="http://localhost:8899" +ANCHOR_WALLET="/home/xiaolong/.config/solana/id.json" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2f3c09 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Device did + +## In the localnet + +- First change the .env file, we need to the `ANCHOR_WALLET` to our local config wallet: +``` +ANCHOR_WALLET="/home/yourWalletConfig/.config/solana/id.json" +``` + +- Second, run `solana-test-validator` in a new terminal: +``` +solana-test-validator -r +``` + +- Third, `anchor deploy` our program which in the `solana_device_id` directory to the localnet. +``` +anchor depoly +``` + +- Fourth, run `device-did-run.ts` +``` +ts-node device-did-run.ts +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4fd82d0..0e78fcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@ethereumjs/util": "^9.0.2", + "dotenv": "^16.4.5", "ethereum-cryptography": "^2.1.3" }, "devDependencies": { @@ -17,6 +18,7 @@ "mocha": "^9.0.3", "prettier": "^2.6.2", "ts-mocha": "^10.0.0", + "ts-node": "^10.9.2", "typescript": "^4.3.5" } }, @@ -73,6 +75,18 @@ "@solana/web3.js": "^1.68.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@ethereumjs/rlp": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", @@ -104,6 +118,31 @@ } } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@noble/curves": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", @@ -202,6 +241,30 @@ "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==", "license": "MIT" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/bn.js": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", @@ -268,6 +331,27 @@ "dev": true, "license": "ISC" }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agentkeepalive": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", @@ -330,6 +414,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -342,7 +432,6 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -510,8 +599,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/buffer-layout": { "version": "1.2.2", @@ -683,6 +771,12 @@ "dev": true, "license": "MIT" }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -787,6 +881,17 @@ "tslib": "^2.0.3" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1332,7 +1437,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -1687,7 +1791,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -1697,7 +1800,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -1835,12 +1937,20 @@ "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" } }, - "node_modules/ts-node": { + "node_modules/ts-mocha/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ts-mocha/node_modules/ts-node": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", "dev": true, - "license": "MIT", "dependencies": { "arrify": "^1.0.0", "buffer-from": "^1.1.0", @@ -1858,12 +1968,63 @@ "node": ">=4.2.0" } }, + "node_modules/ts-mocha/node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/ts-node/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -1941,6 +2102,12 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -2092,13 +2259,12 @@ } }, "node_modules/yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index 23b24a7..15980a7 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@ethereumjs/util": "^9.0.2", + "dotenv": "^16.4.5", "ethereum-cryptography": "^2.1.3" }, "devDependencies": { @@ -16,6 +17,7 @@ "mocha": "^9.0.3", "prettier": "^2.6.2", "ts-mocha": "^10.0.0", + "ts-node": "^10.9.2", "typescript": "^4.3.5" } -} \ No newline at end of file +} diff --git a/scripts/device-did-ft.ts b/scripts/device-did-ft.ts new file mode 100644 index 0000000..8f2d4a4 --- /dev/null +++ b/scripts/device-did-ft.ts @@ -0,0 +1,267 @@ +import * as web3 from "@solana/web3.js"; +import { DeviceDid } from "../target/types/device_did"; +import { AnchorProvider, Program } from "@coral-xyz/anchor"; +import { BN } from "bn.js"; +import { numberToUnit8Array } from "../utils/utils"; + +export const PROGRAM_ID = new web3.PublicKey( + "1234WPYMnkT2bx5MB3uLmixeDuaCHDpd3mXNhZGimKWg" +); + +const adminPdaAddress = ( + programID: web3.PublicKey = PROGRAM_ID, +) => + web3.PublicKey.findProgramAddressSync([Buffer.from("admin")], programID); + +const globalPdaAddress = ( + programID: web3.PublicKey = PROGRAM_ID, +) => + web3.PublicKey.findProgramAddressSync([Buffer.from("global")], programID); + +const vendorPdaAddress = ( + vendorAuthority: web3.PublicKey, + programID: web3.PublicKey = PROGRAM_ID, +) => + web3.PublicKey.findProgramAddressSync([Buffer.from("vendor"), vendorAuthority.toBytes()], programID); + +const productPdaAddress = ( + productName: string, + vendorAuthority: web3.PublicKey, + programID: web3.PublicKey = PROGRAM_ID, +) => + web3.PublicKey.findProgramAddressSync( + [ + Buffer.from("product"), + Buffer.from(productName), + vendorAuthority.toBytes(), + ], programID) + +const deviceDidPdaAddress = ( + device: web3.PublicKey, + programID: web3.PublicKey = PROGRAM_ID, +) => + web3.PublicKey.findProgramAddressSync([Buffer.from("did"), device.toBytes()], programID); + +export function initializeAdminIx( + program: Program, + admin: web3.PublicKey, + adminAuthority: web3.PublicKey, + treasury: web3.PublicKey, + payer: web3.PublicKey, +): Promise { + const [adminPDA] = adminPdaAddress(); + return program.methods + .initializeAdmin({ + admin: admin, + authority: adminAuthority, + treasury: treasury, + }) + .accounts({ + payer: payer, + admin: adminPDA + }) + .instruction(); +} + +export function initializeGlobalIx( + program: Program, + regFee: number, + payer: web3.PublicKey, + admin: web3.PublicKey, +): Promise { + const [adminPDA] = adminPdaAddress(); + const [globalPDA] = globalPdaAddress(); + return program.methods + .initializeGlobal({ + regFee: new BN(regFee), + }) + .accounts({ + payer: payer, + admin: adminPDA, + adminKey: admin, + global: globalPDA, + }) + .instruction(); +} + +export function createVendorIx( + program: Program, + vendorName: string, + vendorAuthority: web3.PublicKey, + payer: web3.PublicKey, + admin: web3.PublicKey, +): Promise { + const [globalPDA] = globalPdaAddress(); + const [vendorPDA] = vendorPdaAddress(vendorAuthority); + return program.methods + .createVendor({ + name: vendorName, + authority: vendorAuthority, + }) + .accounts({ + payer: payer, + global: globalPDA, + adminKey: admin, + vendor: vendorPDA, + }) + .instruction(); +} + +export function createProductCollectionIx( + program: Program, + productName: string, + payer: web3.PublicKey, + vendorAuthority: web3.PublicKey, +): Promise { + const [vendorPDA] = vendorPdaAddress(vendorAuthority); + const [globalPDA] = globalPdaAddress(); + const [productPDA] = productPdaAddress(productName, vendorAuthority); + return program.methods + .createProductCollection({ + name: productName, + }) + .accounts({ + payer: payer, + vendor: vendorPDA, + vendorAuthority: vendorAuthority, + global: globalPDA, + product: productPDA, + }) + .instruction(); +} + +export function createDeviceAndDidIx( + program: Program, + deviceName: string, + serialNum: string, + payer: web3.PublicKey, + vendorAuthority: web3.PublicKey, + productName: string, + device: web3.PublicKey, + treasury: web3.PublicKey, +): Promise { + const [vendorPDA] = vendorPdaAddress(vendorAuthority); + const [productPDA] = productPdaAddress(productName, vendorAuthority); + const [deviceDidPDA] = deviceDidPdaAddress(device); + const [adminPDA] = adminPdaAddress(); + const [globalPDA] = globalPdaAddress(); + return program.methods + .createDeviceAndDid({ + name: deviceName, + serialNum: serialNum, + mintAt: new BN(Date.now()), + }) + .accounts({ + payer: payer, + vendor: vendorPDA, + vendorAuthority: vendorAuthority, + product: productPDA, + device: device, + deviceDid: deviceDidPDA, + treasury: treasury, + admin: adminPDA, + global: globalPDA, + }) + .instruction(); +} + +export function activateDeviceIx( + program: Program, + publicKey: Uint8Array, + messageTime: number, + signature: Uint8Array, + recoveryId: number, + device: web3.PublicKey, +): Promise { + return program.methods + .activateDevice({ + publicKey: Array.from(publicKey), + message: Buffer.from(numberToUnit8Array(messageTime)), + signature: Array.from(signature), + recoveryId: recoveryId, + }) + .accounts({ + device: device, + }) + .instruction(); +} + +async function sendV0Transaction( + connection: web3.Connection, + user: web3.Keypair, + instructions: web3.TransactionInstruction[], + lookupTableAccounts?: web3.AddressLookupTableAccount[], +) { + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); + const messageV0 = new web3.TransactionMessage({ + payerKey: user.publicKey, + recentBlockhash: blockhash, + instructions, + }).compileToV0Message(lookupTableAccounts ? lookupTableAccounts : undefined); + const tranction = new web3.VersionedTransaction(messageV0); + tranction.sign([user]); + const txid = await connection.sendTransaction(tranction); + await connection.confirmTransaction( + { + blockhash: blockhash, + lastValidBlockHeight: lastValidBlockHeight, + signature: txid, + }, + "finalized" + ); + console.log(`https://explorer.solana.com/tx/${txid}?cluster=devnet`); +} + +export async function sendLegacyTranctionProvider( + connection: web3.Connection, + provider: AnchorProvider, + instructions: web3.TransactionInstruction[], + otherSigner?: web3.Keypair[], +) { + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); + const message = new web3.TransactionMessage({ + payerKey: provider.publicKey, + recentBlockhash: blockhash, + instructions, + }).compileToLegacyMessage(); + let tranction = new web3.VersionedTransaction(message); + tranction = await provider.wallet.signTransaction(tranction); + if (otherSigner) { + tranction.sign(otherSigner); + } + const txid = await connection.sendTransaction(tranction); + await connection.confirmTransaction( + { + blockhash: blockhash, + lastValidBlockHeight: lastValidBlockHeight, + signature: txid, + }, + "finalized" + ); +} + +export async function sendLegacyTranction( + connection: web3.Connection, + payer: web3.Keypair, + instructions: web3.TransactionInstruction[], + otherSigner?: web3.Keypair[], +) { + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); + const message = new web3.TransactionMessage({ + payerKey: payer.publicKey, + recentBlockhash: blockhash, + instructions, + }).compileToLegacyMessage(); + const tranction = new web3.VersionedTransaction(message); + const signers = otherSigner ? [payer, ...otherSigner] : [payer]; + tranction.sign(signers); + const txid = await connection.sendTransaction(tranction); + await connection.confirmTransaction( + { + blockhash: blockhash, + lastValidBlockHeight: lastValidBlockHeight, + signature: txid, + }, + "finalized" + ); +} \ No newline at end of file diff --git a/scripts/device-did-run.ts b/scripts/device-did-run.ts new file mode 100644 index 0000000..3eefa5a --- /dev/null +++ b/scripts/device-did-run.ts @@ -0,0 +1,142 @@ +import * as anchor from "@coral-xyz/anchor"; +import * as dotenv from "dotenv"; +import fs from "fs"; +import { PROGRAM_ID, activateDeviceIx, createDeviceAndDidIx, createProductCollectionIx, createVendorIx, initializeAdminIx, initializeGlobalIx, sendLegacyTranction, sendLegacyTranctionProvider } from "./device-did-ft"; +import { airdropAndComfirm, loadKeypair, numberToUnit8Array } from "../utils/utils"; +import { DeviceDid } from "../target/types/device_did"; +import * as ethUtil from "@ethereumjs/util"; +import { keccak256 } from "ethereum-cryptography/keccak.js"; +dotenv.config(); + +async function deviceDidRun() { + const provider = anchor.AnchorProvider.env(); + anchor.setProvider(provider); + const idl = JSON.parse(fs.readFileSync("./target/idl/device_did.json", "utf-8")); + let program: anchor.Program; + program = new anchor.Program(idl, PROGRAM_ID); + + const adminKey = loadKeypair("./keypairs/admin.json"); + const treasury = loadKeypair("./keypairs/treasury.json"); + const adminAuthority = loadKeypair("./keypairs/admin-authority.json"); + const vendorAuthority = loadKeypair("./keypairs/vendor-authority.json"); + const device = loadKeypair("./keypairs/device.json"); + const vendorName = "IO Company"; + const productName = "Computer"; + + await airdropAndComfirm(provider.connection, vendorAuthority.publicKey, 10); + console.log(`Vendor airdrop success.`); + + await sendLegacyTranctionProvider( + provider.connection, + provider, + [ + await initializeAdminIx( + program, + adminKey.publicKey, + adminAuthority.publicKey, + treasury.publicKey, + provider.publicKey, + ) + ], + ); + console.log(`Initialize admin success.`) + + // Assume reg fee is 0.05 SOL + const regFee = anchor.web3.LAMPORTS_PER_SOL * 0.05; + await sendLegacyTranctionProvider( + provider.connection, + provider, + [ + await initializeGlobalIx( + program, + regFee, + provider.publicKey, + adminKey.publicKey, + ) + ], + [adminKey], + ); + console.log(`Initialize global success.`); + + await sendLegacyTranction( + provider.connection, + vendorAuthority, + [ + await createVendorIx( + program, + vendorName, + vendorAuthority.publicKey, + vendorAuthority.publicKey, + adminKey.publicKey, + ) + ], + [adminKey], + ); + console.log(`Create vendor success.`); + + await sendLegacyTranction( + provider.connection, + vendorAuthority, + [ + await createProductCollectionIx( + program, + productName, + vendorAuthority.publicKey, + vendorAuthority.publicKey, + ) + ], + ); + console.log(`Create product collection success.`); + + // The info of the device + const deviceName = "Banana"; + const serialNum = "123456789"; + await sendLegacyTranction( + provider.connection, + vendorAuthority, + [ + await createDeviceAndDidIx( + program, + deviceName, + serialNum, + vendorAuthority.publicKey, + vendorAuthority.publicKey, + productName, + device.publicKey, + treasury.publicKey + ) + ], + [device] + ); + console.log(`Create device and did success.`); + + // The info of activation + const privateKey = ethUtil.hexToBytes("0x1111111111111111111111111111111111111111111111111111111111111111"); + const publicKey = ethUtil.privateToPublic(privateKey); + + const slot = await provider.connection.getSlot(); + const messageTime = await provider.connection.getBlockTime(slot); + const hashedMessageForActivation = keccak256(numberToUnit8Array(messageTime)); + + const { r, s, v } = ethUtil.ecsign(hashedMessageForActivation, privateKey); + const signature = Uint8Array.from([...r, ...s]); + const recoveryId = Number(ethUtil.calculateSigRecovery(v)); + + await sendLegacyTranctionProvider( + provider.connection, + provider, + [ + await activateDeviceIx( + program, + publicKey, + messageTime, + signature, + recoveryId, + device.publicKey, + ) + ], + ); + console.log(`Activate device success.`); +} + +deviceDidRun(); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 558b83e..d8c8d75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,17 @@ { - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } - } - \ No newline at end of file + "compilerOptions": { + "types": [ + "mocha", + "chai" + ], + "typeRoots": [ + "./node_modules/@types" + ], + "lib": [ + "es2015" + ], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/utils/utils.ts b/utils/utils.ts index 793b0d9..f6e0660 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -1,8 +1,8 @@ -import { Keypair } from "@solana/web3.js"; -import fs from "fs" +import * as web3 from "@solana/web3.js"; +import fs from "fs"; -export function loadKeypair(jsonPath: string): Keypair { - return Keypair.fromSecretKey(new Uint8Array(JSON.parse(fs.readFileSync(jsonPath).toString()))); +export function loadKeypair(jsonPath: string): web3.Keypair { + return web3.Keypair.fromSecretKey(new Uint8Array(JSON.parse(fs.readFileSync(jsonPath).toString()))); } export function numberToUnit8Array(number: number): Uint8Array { @@ -10,4 +10,33 @@ export function numberToUnit8Array(number: number): Uint8Array { const view = new DataView(buffer); view.setBigInt64(0, BigInt(number), false); return new Uint8Array(buffer); +} + +export async function airdropAndComfirm( + connection: web3.Connection, + user: web3.PublicKey, + solAmount: number, +) { + const airdropSignature = await connection.requestAirdrop( + user, + web3.LAMPORTS_PER_SOL * solAmount, + ); + await checkStatus(connection, airdropSignature, "Airdrop") +} + +async function checkStatus( + connection: web3.Connection, + sig: string, + action: string, +) { + const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); + const status = await connection.confirmTransaction({ + signature: sig, + blockhash: blockhash, + lastValidBlockHeight: lastValidBlockHeight, + }, "finalized"); + if (status.value.err) { + throw new Error(action + "tranction failed: " + status.value.err); + } + console.log(action + "success: " + sig); } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index bc7c8d1..311e82f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37,6 +37,13 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@ethereumjs/rlp@^5.0.2": version "5.0.2" resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz" @@ -50,6 +57,24 @@ "@ethereumjs/rlp" "^5.0.2" ethereum-cryptography "^2.1.3" +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@noble/curves@^1.2.0", "@noble/curves@~1.3.0", "@noble/curves@1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz" @@ -112,6 +137,26 @@ rpc-websockets "^7.5.1" superstruct "^0.14.2" +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + "@types/bn.js@^5.1.0": version "5.1.5" resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz" @@ -165,6 +210,16 @@ resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +acorn-walk@^8.1.1: + version "8.3.2" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +acorn@^8.4.1: + version "8.11.3" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + agentkeepalive@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz" @@ -197,6 +252,11 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" @@ -393,6 +453,11 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-fetch@^3.1.5: version "3.1.8" resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz" @@ -434,6 +499,11 @@ diff@^3.1.0: resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diff@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" @@ -447,6 +517,11 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" @@ -1034,6 +1109,25 @@ ts-mocha@^10.0.0: optionalDependencies: tsconfig-paths "^3.5.0" +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + ts-node@7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz" @@ -1068,7 +1162,7 @@ type-detect@^4.0.0, type-detect@^4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -typescript@^4.3.5: +typescript@^4.3.5, typescript@>=2.7: version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -1090,6 +1184,11 @@ uuid@^8.3.2: resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" @@ -1182,6 +1281,11 @@ yn@^2.0.0: resolved "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz" integrity sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ== +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"