initial commit

This commit is contained in:
CaiHQ 2023-03-15 01:15:57 +08:00
commit 3ee7b5bfc7
34 changed files with 4856 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.idea
node_modules
dist
package-lock.json
pnpm-lock.yaml

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 The BDWare Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

79
README.md Normal file
View File

@ -0,0 +1,79 @@
# BDContract SDK
BDContract SDK for Node.js and browsers.
## Install
`npm install @bdware/bdcontract-sdk`
```typescript
import { Client } from '@bdware/bdcontract-sdk'
import { sm2 } from 'sm-crypto'
const client = new Client(
url,
(ev) => {
console.log(JSON.stringify(ev))
},
(ev, ws) => {
console.log(ev.data)
},
sm2.generateKeyPairHex(),
)
```
To use with plain HTML and JavaScript, include [dist/bdcontract-sdk.umd.js](dist/bdcontract-sdk.umd.js) (example from [test/index.html](test/index.html)):
```HTML
<script type="text/javascript" src="./sm2.js"></script>
<script type="text/javascript" src="./cryptico.iife.js"></script>
<script type="text/javascript" src="../dist/bdcontract-sdk.iife.js"></script>
```
## Usage
```javascript
// Approach 1:
const wssocket = new WsSocket(url, wsHandler)
// Approach 2:
function print(string) {
document.write(string + '\n\n')
}
const url = 'ws://021.node.internetapi.cn:21030/SCIDE/SCExecutor'
const client = new bdcontract.Client(
url,
(ev) => {
console.log(JSON.stringify(ev))
},
(ev, ws) => {
console.log(ev.data)
},
)
setTimeout(async () => {
const status = client.status()
console.log(status)
try {
let data = await client.executeContract(
'AnnotationExample0608',
'main',
'abacd',
)
print(JSON.stringify(data))
} catch (data) {
print(JSON.stringify(data))
}
}, 1000)
// Wait for session receival
const session = await client.sessionReceived()
// Login and wait (will wait if session not yet received)
const success = await client.login()
async function anotherPlace() {
// In another place, wait for login to complete
const success = await client.Loggedin()
}()
```

238
lib/index.d.ts vendored Normal file
View File

@ -0,0 +1,238 @@
import { AESKey, RSAKey } from '@daotl/cryptico';
import { AxiosRequestConfig } from 'axios';
import { KeyPairHex } from 'sm-crypto';
declare function aesEncrypt(data: string, aesKey: AESKey): string;
declare function rsaEncrypt(data: string, rsaKey: {
n: string;
e1: string;
}): string;
declare function loadRSAKey(rsaKey: string): RSAKey;
declare function encryptReq(reqContent: {
contractID: string;
}, pubKey: RSAKey): {
action: string | null;
contractID: string;
arg: string;
requester: string;
};
type ClientResponse<Data> = Omit<Response, 'data'> & {
data?: Data;
};
type PingResponse = ClientResponse<'pong'>;
interface SaveFileRequest {
content: string;
isAppend: boolean;
isPrivate: boolean;
path: string;
}
interface ListProjectPermissionRequest {
isPrivate: boolean;
path: string;
}
interface ListProjectPermissionResponseData {
permissions: string[];
ypk: string;
}
interface StartContractByYpkRequest {
isPrivate: boolean;
path: string;
script: string;
}
interface ListAllUsersResponseDataListItem {
key: string;
value: string;
}
interface ListAllUsersResponseData {
kv: ListAllUsersResponseDataListItem[];
time: ListAllUsersResponseDataListItem[];
}
interface OnlineContractsItem {
contractID: string;
contractName: string;
isMaster: boolean;
type: string;
yjsType: string;
[key: string]: unknown;
}
interface OnlineItem {
cimanager: string;
contractVersion: number;
events: number;
ipPort: string;
masterAddress: string;
nodeName: string;
peerID: string;
pubKey: string;
contracts: OnlineContractsItem[];
}
interface ListNodesResponse {
action: string;
offline: string[];
online: OnlineItem[];
}
interface DistributeContractResponse {
action: string;
progress: string;
}
interface ExecuteContractArgs extends RequestInit {
method?: 'POST' | 'GET';
withSignature?: boolean;
withDynamicAnalysis?: boolean;
}
interface ExecuteContractResponse<Data> {
status?: boolean;
data?: Data;
executeTime?: number;
cid?: string;
isPrivate?: boolean;
[key: string]: unknown;
}
interface ConfigNodeArgs {
nodeName?: string;
dataChain?: string;
masterAddress?: string;
nodeCenter?: string;
LHSProxyAddress?: string;
[K: string]: string | undefined;
}
interface LoadNodeConfigResponseData {
doipConfig: string;
[K: string]: string;
}
declare class HttpClient {
private baseUrl;
private sm2Key;
private fetch;
constructor(baseUrl: string, sm2Key: KeyPairHex, config?: AxiosRequestConfig<any>);
requestWithSignature<Data>(path: string, init?: Partial<RequestInit>, sm2Key?: KeyPairHex): Promise<ClientResponse<Data>>;
retryRequestWithSignature<Data>(retryTimes: number, path: string, init?: Partial<RequestInit>, sm2Key?: KeyPairHex): Promise<ClientResponse<Data>>;
sign(data: string, privateKey?: string): string;
ping(): Promise<PingResponse>;
startContract(code: string): Promise<ClientResponse<string>>;
startContractByYPK(_request: StartContractByYpkRequest): Promise<ClientResponse<string>>;
executeContract(contractID: string, operation: string, arg: string, { method, withDynamicAnalysis, withSignature, }?: ExecuteContractArgs): Promise<ClientResponse<ExecuteContractResponse<string>>>;
killContractProcess(contractID: string, requestID?: string): Promise<ClientResponse<string>>;
killAllContract(): Promise<ClientResponse<string>>;
applyNodeRole(role: string): Promise<ClientResponse<{
action: string;
data: string;
role?: string;
}>>;
authNodeRole(isAccept: boolean, authorizedPubKey: string, managerPair?: KeyPairHex): Promise<ClientResponse<{
action: string;
data: string;
}>>;
distributeContract(nodeIDs: string, projectName: string, isPrivate: boolean): void;
saveFile(_request: SaveFileRequest): Promise<ClientResponse<string>>;
listProjectPermission(_request: ListProjectPermissionRequest): Promise<ClientResponse<ListProjectPermissionResponseData>>;
startContractMultiPoint(peersID: string, type: number, selectUnitNum: number, projectName: string, isPrivate: boolean, sponsorPeerID: string): Promise<ClientResponse<string>>;
loadNodeConfig(): Promise<ClientResponse<LoadNodeConfigResponseData>>;
updateConfig(key: string, val: string): Promise<ClientResponse<boolean>>;
resetNodeManager(): Promise<boolean>;
lockEdit(): Promise<ClientResponse<string>>;
unlockEdit(): Promise<ClientResponse<string>>;
addNode(nodePubKey: string): Promise<ClientResponse<string>>;
applyRole(role: string): Promise<ClientResponse<string>>;
authNodeManager(isAccept: boolean, authorizedPubKey: string): Promise<ClientResponse<string>>;
listAllUsers(): Promise<ClientResponse<ListAllUsersResponseData>>;
listNodes(): Promise<ListNodesResponse>;
createTrustUnit(data: {
nodeName: string;
pubkey: string;
}[], Msg: string): Promise<{
action: string;
status: string;
}>;
listTrustUnits(): Promise<ClientResponse<{
key: string;
value: string;
}[]>>;
listContractProcess(): Promise<ClientResponse<string>>;
downloadContract(projectName: string, isPrivate: boolean, timestamp: number): Promise<ClientResponse<string>>;
configNode(args: ConfigNodeArgs): Promise<boolean>;
}
interface WsEvent {
data: string;
}
type OnOpenHandler = (this: WebSocket, ev: Event) => void;
type WsHandler = (ev: WsEvent, ws?: WebSocket) => void;
interface SegmentData {
action: 'sendSeg';
cid: string;
data: string;
}
declare class WsSocket {
private handlerList;
private toSend;
private isSending;
private sendList;
private toReceive;
private wssocket;
constructor(wsurl: string, onopen: OnOpenHandler, handler?: WsHandler);
status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'];
sendNextSegment(): void;
receiveSeg(obj: SegmentData): void;
monitor(): void;
send(data: string): void;
addHandler(handler: WsHandler): void;
}
interface ResponseData {
action: string;
responseID?: string;
status: true | false | string;
result?: unknown;
data: string;
[K: string]: unknown;
}
declare class WsClient {
private readonly sm2Key;
private readonly wssocket;
private readonly promiseCallbackPairs;
private readonly sessionPromise;
private sessionResolve;
private readonly loginPromise;
private loginResolve;
constructor(url: string, onopen: OnOpenHandler, handler: WsHandler, sm2Key?: KeyPairHex);
status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'];
sessionReceived(): Promise<string>;
login(): Promise<boolean>;
loggedIn(): Promise<boolean>;
matchCID(contractID: string): Promise<ResponseData>;
getMetabyCID(contractID: string): Promise<ResponseData>;
getMetabyReadme(keyword: string, page?: string, pageSize?: string): Promise<ResponseData>;
getMetabyPubkey(pubkey: string): Promise<ResponseData>;
segmentWord(words: string): Promise<ResponseData>;
getMetabyOwner(owner: string, page?: string, pageSize?: string): Promise<ResponseData>;
getDependentContract(contractName: string): Promise<ResponseData>;
queryContractLogByDate(start: number): Promise<ResponseData>;
queryDataByHash(hash: string): Promise<ResponseData>;
executeContract(contractID: string, method: string, arg: unknown): Promise<ResponseData>;
getSessionID(): Promise<ResponseData>;
listTheContractProcess(contractID: string): Promise<ResponseData>;
getMask(contractID: string): Promise<ResponseData>;
setMask(contractID: string, operation: string, arg: string): Promise<ResponseData>;
getMock(contractID: string): Promise<ResponseData>;
setMock(contractID: string, operation: string, arg: string): Promise<ResponseData>;
queryHashByOffset(offset: number, count: number): Promise<ResponseData>;
loadNodeConfig(): Promise<ResponseData>;
queryUserStat(): Promise<ResponseData>;
listNodes(): Promise<ResponseData>;
killContractProcess(id: string): Promise<ResponseData>;
distributeYPK(projectName: string, nodeIDs: string): Promise<ResponseData>;
listYPKs(): Promise<ResponseData>;
deleteFile(file: string): Promise<ResponseData>;
startContractByYPK(project: string): Promise<ResponseData>;
initBDServer(host: string, username: string, password: string, name: string, clusterHost: string): Promise<ResponseData>;
initBDCluster(host: string, username: string, password: string, name: string, sm2Key: string, agents: []): Promise<ResponseData>;
listCompiledFiles(): Promise<ResponseData>;
getManagerPubkey(): Promise<ResponseData>;
getClusterName(): Promise<ResponseData>;
setClusterName(name: string): Promise<ResponseData>;
}
export { ClientResponse, ConfigNodeArgs, DistributeContractResponse, ExecuteContractArgs, ExecuteContractResponse, HttpClient, ListAllUsersResponseData, ListAllUsersResponseDataListItem, ListNodesResponse, ListProjectPermissionRequest, ListProjectPermissionResponseData, LoadNodeConfigResponseData, OnOpenHandler, OnlineContractsItem, OnlineItem, PingResponse, SaveFileRequest, StartContractByYpkRequest, WsClient, WsEvent, WsHandler, WsSocket, aesEncrypt, encryptReq, loadRSAKey, rsaEncrypt };

2
lib/index.js Normal file

File diff suppressed because one or more lines are too long

1
lib/index.js.map Normal file

File diff suppressed because one or more lines are too long

2
lib/index.mjs Normal file

File diff suppressed because one or more lines are too long

1
lib/index.mjs.map Normal file

File diff suppressed because one or more lines are too long

51
package.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "@bdware/bdcontract-sdk",
"version": "0.3.0-alpha.14",
"description": "BDContract SDK for Node.js and browsers",
"license": "MIT",
"keywords": [
"BDWare",
"BDContract",
"SDK",
"WebSocket",
"TypeScript",
"Node.js",
"node",
"nodejs",
"browser"
],
"main": "lib/index.js",
"module": "lib/index.mjs",
"iife": "dist/bdcontract-sdk.iife.js",
"types": "lib/index.d.ts",
"files": [
"lib"
],
"scripts": {
"build": "shx rm -rf lib dist && rollup -c rollup.config.js",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"pub": "npm run lint && npm publish --access public"
},
"prettier": "@daotl/prettier-config",
"dependencies": {
"@daotl/cryptico": "^2.0.3",
"@lifeomic/axios-fetch": "^3.0.0",
"axios": "^0.27.2",
"sm-crypto": "^0.3.11"
},
"devDependencies": {
"@daotl/eslint-config": "^0.3.31",
"@types/node": "^18.7.15",
"@types/sm-crypto": "^0.3.0",
"eslint": "^8.23.0",
"prettier": "^2.7.1",
"rollup": "^2.79.0",
"rollup-plugin-dts": "^4.2.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.33.0",
"shx": "^0.3.4",
"sucrase": "^3.25.0",
"typescript": "^4.8.2"
}
}

2
rollup.config.js Normal file
View File

@ -0,0 +1,2 @@
require('sucrase/register')
module.exports = require('./rollup.config.ts')

41
rollup.config.ts Normal file
View File

@ -0,0 +1,41 @@
import type { RollupOptions } from 'rollup'
import dts from 'rollup-plugin-dts'
import { terser } from 'rollup-plugin-terser'
import typescript from 'rollup-plugin-typescript2'
const pkg = require('./package.json')
const name = 'bdcontract'
export default [
{
input: 'src/index.ts',
output: [
{
file: `${pkg.main}`,
format: 'umd',
name,
sourcemap: true,
},
{ file: `${pkg.module}`, format: 'es', sourcemap: true },
{
file: `${pkg.iife}`,
format: 'iife',
name,
sourcemap: true,
},
],
plugins: [
typescript({
tsconfig: 'tsconfig.build.json',
useTsconfigDeclarationDir: true,
}),
terser(),
],
},
{
input: 'types/index.d.ts',
output: [{ file: 'lib/index.d.ts' }],
plugins: [dts()],
},
] as RollupOptions

77
src/crypto.ts Normal file
View File

@ -0,0 +1,77 @@
// export function aesDecrypt(data: base64) {
// const dataAscii = cryptico.b64to256(data)
// const encryptedBlocks = cryptico.string2bytes(dataAscii)
// const exkey = global.aesKey.slice(0)
// aes.ExpandKey(exkey)
// aes.Decrypt(encryptedBlocks, exkey)
// return cryptico.bytes2string(encryptedBlocks)
// }
import type { AESKey } from '@daotl/cryptico'
import { aes, cryptico, RSAKey } from '@daotl/cryptico'
export function aesEncrypt(data: string, aesKey: AESKey): string {
const exkey = aesKey.slice(0)
aes.ExpandKey(exkey)
let blocks = cryptico.string2bytes(data)
blocks = cryptico.pad16(blocks)
aes.Encrypt(blocks, exkey)
let ciphertext = cryptico.bytes2string(blocks)
ciphertext = cryptico.b256to64(ciphertext)
return ciphertext
}
export function rsaEncrypt(
data: string,
rsaKey: { n: string; e1: string },
): string {
const rsa = new RSAKey()
rsa.setPublic(rsaKey.n, rsaKey.e1)
return rsa.encrypt(data)
}
export function loadRSAKey(rsaKey: string): RSAKey {
const str = cryptico.b64to256(rsaKey)
const strs = str.split(',')
const key = new RSAKey()
key.setPrivate(strs[0], strs[0], strs[0])
return key
}
// function testRSA() {
// pubKey = loadRSAKey(global.privKey)
// reqContent = {}
// reqContent.action = 'main'
// reqContent.arg = '[{"score":20},{"score":20}]'
// reqContent.contractID = 'abc'
// eReq = encryptReq(reqContent, pubKey)
// url =
// 'http://localhost:8080/SCIDE/SCManager?action=executeContractEncrypted&contractRequest=' +
// encodeURIComponent(JSON.stringify(eReq))
// }
export function encryptReq(
reqContent: { contractID: string },
pubKey: RSAKey,
): {
action: string | null
contractID: string
arg: string
requester: string
} {
const aes = {
key: cryptico.generateAESKey(),
}
const aesObj = JSON.stringify(aes)
const { contractID } = reqContent
const reqContentNoId = { ...reqContent, contractID: undefined }
const encrypedReq = {
action: pubKey.decrypt(aesObj),
contractID,
arg: aesEncrypt(JSON.stringify(reqContentNoId), aes.key),
requester: cryptico.b256to64(
`${pubKey.n.toString(16)},${pubKey.e.toString(16)},0`,
),
}
return encrypedReq
}

582
src/httpClient.ts Normal file
View File

@ -0,0 +1,582 @@
import { buildAxiosFetch } from '@lifeomic/axios-fetch'
import axios, { type AxiosRequestConfig } from 'axios'
import type { KeyPairHex } from 'sm-crypto'
import { sm2 } from 'sm-crypto'
import type {
ClientResponse,
ConfigNodeArgs,
ExecuteContractArgs,
ListAllUsersResponseData,
ListNodesResponse,
ListProjectPermissionRequest,
ListProjectPermissionResponseData,
LoadNodeConfigResponseData,
ExecuteContractResponse,
PingResponse,
SaveFileRequest,
StartContractByYpkRequest,
} from './types'
const genUrlParamsFromObject = (obj: Record<string, unknown>): string => {
return Object.entries(obj)
.map(([key, value]) => `${key}=${String(value)}`)
.join('&')
}
export * from './types'
export class HttpClient {
private fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>
constructor(
private baseUrl: string,
private sm2Key: KeyPairHex,
config = {} as AxiosRequestConfig,
) {
const axiosInstance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 10000,
// headers: {'X-Custom-Header': 'foobar'}
...config,
})
this.fetch = buildAxiosFetch(
axiosInstance /* , function (config) {
config.timeout = 1000;
return config;
} */,
)
}
async requestWithSignature<Data>(
path: string,
init?: Partial<RequestInit>,
sm2Key?: KeyPairHex,
): Promise<ClientResponse<Data>> {
return new Promise<ClientResponse<Data>>((resolve, reject) => {
const rawUrl = this.baseUrl + path
const url = `${rawUrl}${path.includes('?') ? '&' : '?'}pubKey=${
sm2Key?.publicKey ?? this.sm2Key.publicKey
}`
// console.log('privateKey:', this.sm2Key.privateKey)
// console.log('publicKey:', this.sm2Key.publicKey)
// console.log('to sign:', url.substring(url.indexOf('?') + 1))
const sign = this.sign(
url.substring(url.indexOf('?') + 1),
sm2Key?.privateKey,
)
// console.log(
// 'url:',
// init?.method?.toLowerCase() === 'post'
// ? rawUrl
// : `${encodeURI(url)}&sign=${sign}`,
// )
this.fetch(
init?.method?.toLowerCase() === 'post'
? rawUrl
: `${encodeURI(url)}&sign=${sign}`,
{
...init,
...(init?.method === 'POST'
? {
body: {
...(init?.body as unknown as Record<string, string>),
sign,
} as unknown as Request['body'],
}
: {}),
},
)
.then(async (res) => {
// console.log(res.url,'请求成功')
const clientRes = res as unknown as ClientResponse<Data>
clientRes.data = (await res.text()) as Data
// Workaround for the issue causing the 2nd consecutive request to fail:
// https://github.com/nodejs/undici/issues/1415
setTimeout(() => {
resolve(clientRes)
}, 1)
})
.catch((err) => {
// const clientRes = err as unknown as ClientResponse<Data>
// clientRes.data = (await err.text()) as Data
// console.log(err,'errerrerrerrerrerrerrerrerrerrerrerrerrerrerr')
setTimeout(() => {
reject(err)
}, 1)
})
})
}
async retryRequestWithSignature<Data>(retryTimes: number, path: string, init?: Partial<RequestInit>, sm2Key?: KeyPairHex ) {
let err: Error = new Error()
for (let i = 0; i<retryTimes; i++) {
try{
return await this.requestWithSignature<Data>(path, init, sm2Key)
} catch (e) {
if (i < retryTimes) {
console.log('1')
continue
}
err = e as Error
}
}
throw err
}
sign(data: string, privateKey?: string): string {
return sm2.doSignature(data, privateKey ?? this.sm2Key.privateKey, {
hash: true,
der: true,
})
}
// ping
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id15
ping(): Promise<PingResponse> {
return this.requestWithSignature('/SCManager?action=ping')
}
// 启动合约
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id60
startContract(code: string): Promise<ClientResponse<string>> {
const params = { action: 'startContract', script: code }
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject(params)}`,
)
}
// 启动合约
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id13
startContractByYPK(
_request: StartContractByYpkRequest,
): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'startContractByYPK',
..._request,
owner: this.sm2Key.publicKey,
aim: 'onStartContract',
signature: sm2.doSignature(
`Fixed|${_request.path}|${this.sm2Key.publicKey}`,
this.sm2Key.privateKey,
{
hash: true,
der: true,
},
),
})}`,
)
}
// 调用合约
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id69
executeContract(
contractID: string,
operation: string,
arg: string,
{
method = 'POST',
withDynamicAnalysis = false,
withSignature = false,
} = {} as ExecuteContractArgs,
): Promise<ClientResponse<ExecuteContractResponse<string>>> {
let request = {}
if (withDynamicAnalysis === undefined) {
request = {
action: 'executeContract',
contractID,
operation,
arg,
}
} else {
request = {
action: 'executeContract',
contractID,
operation,
withDynamicAnalysis,
arg,
}
}
if (withSignature) {
request = {
...request,
pubkey: this.sm2Key.publicKey,
signature: this.sign(
`${contractID}|${operation}|${arg ?? ''}|${this.sm2Key.publicKey}`,
),
}
}
return this.retryRequestWithSignature(
3,
`/SCManager${
method === 'GET' ? `?${genUrlParamsFromObject(request)}` : ''
}`,
{
method,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
...(method === 'POST' ? ({ body: request } as Request) : {}),
},
)
}
// 停止合约
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id122
killContractProcess(
contractID: string,
requestID?: string,
): Promise<ClientResponse<string>> {
const _request: Record<string, string> = {
action: 'killContractProcess',
id: contractID,
}
requestID && (_request.requestID = requestID!)
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject(_request)}`,
)
}
// 停止所有合约
// 地址 http://39.104.205.122:18010/SCIDE
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id131
killAllContract(): Promise<ClientResponse<string>> {
return this.requestWithSignature('/SCManager?action=killAllContract')
}
// 申请角色
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html
applyNodeRole(
role: string,
): Promise<ClientResponse<{ action: string; data: string; role?: string }>> {
const _request = {
action: 'applyNodeRole',
role,
}
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject(_request)}`,
)
}
// 授权角色
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html
authNodeRole(
isAccept: boolean,
authorizedPubKey: string,
managerPair?: KeyPairHex,
): Promise<ClientResponse<{ action: string; data: string }>> {
const _request = {
action: 'authNodeRole',
isAccept,
authorizedPubKey,
}
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject(_request)}`,
undefined,
managerPair,
)
}
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html
// 用sse获取 问题未解决!!!
distributeContract(
nodeIDs: string,
projectName: string,
isPrivate: boolean,
): void {
const signature = sm2.doSignature(
`DistributeContract|${projectName}|${this.sm2Key.publicKey}`,
this.sm2Key.privateKey,
{ hash: true, der: true },
)
const _request = {
action: 'distributeContract',
nodeIDs,
projectName,
isPrivate,
signature,
}
const path = `/SCManager?${genUrlParamsFromObject(_request)}`
const url = `${this.baseUrl + path}${
path.includes('?') ? '&' : '?'
}pubKey=${this.sm2Key.publicKey}`
const sig = sm2.doSignature(
url.substring(url.indexOf('?') + 1),
this.sm2Key.privateKey,
{
hash: true,
der: true,
},
)
const source = new EventSource(
`${this.baseUrl}${path}&pubKey=${this.sm2Key.publicKey}&sign=${sig}`,
)
source.addEventListener(
'message',
function (_event) {
// const data = event.data as DistributeContractResponse[]
// console.log(data, 'dd')
// handle message
},
false,
)
}
saveFile(_request: SaveFileRequest): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'saveFile',
..._request,
})}`,
)
}
listProjectPermission(
_request: ListProjectPermissionRequest,
): Promise<ClientResponse<ListProjectPermissionResponseData>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'listProjectPermission',
..._request,
})}`,
)
}
startContractMultiPoint(
peersID: string,
type: number,
selectUnitNum: number,
projectName: string,
isPrivate: boolean,
sponsorPeerID: string,
): Promise<ClientResponse<string>> {
const _request = {
// peerID是节点的公钥,以逗号间隔开
peersID,
type,
selectUnitNum,
projectName,
isPrivate,
sponsorPeerID,
}
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'startContractMultiPoint',
..._request,
})}`,
)
}
// 获取节点配置信息
async loadNodeConfig(): Promise<ClientResponse<LoadNodeConfigResponseData>> {
const res = await this.requestWithSignature<string>(
`/SCManager?${genUrlParamsFromObject({
action: 'loadNodeConfig',
})}`,
)
return { ...res, data: (res.status === 200 && res.data ? JSON.parse( res.data).data : {}) as LoadNodeConfigResponseData}
}
// 支持的key包括{licenceprojectDiryjsPathdataChaindoipConfignodeCenternodeNamemasterAddressresetNodeCenterWS}
async updateConfig(
key: string,
val: string,
): Promise<ClientResponse<boolean>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'updateConfig',
key,
val,
})}`,
)
}
// 设置pubkey为node manager
async resetNodeManager(): Promise<boolean> {
const res = await this.requestWithSignature<string>(
`/SCManager?${genUrlParamsFromObject({
action: 'resetNodeManager',
})}`,
)
if (!res.data) {
return false
}
const parsed = JSON.parse(res.data)
return parsed.data === 'success'
}
// 锁定某个用户的的私有目录编辑功能
lockEdit(): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'lockEdit',
})}`,
)
}
// 解锁某个用户的的私有目录编辑功能
unlockEdit(): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'unlockEdit',
})}`,
)
}
addNode(nodePubKey: string): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`?${genUrlParamsFromObject({
action: 'addNode',
nodePubKey,
})}`,
)
}
// 申请角色
applyRole(role: string): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`?${genUrlParamsFromObject({
action: 'applyRole',
role,
})}`,
)
}
authNodeManager(
isAccept: boolean,
authorizedPubKey: string,
): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`?${genUrlParamsFromObject({
action: 'authNodeManager',
isAccept,
authorizedPubKey,
})}`,
)
}
listAllUsers(): Promise<ClientResponse<ListAllUsersResponseData>> {
return this.requestWithSignature(
`?${genUrlParamsFromObject({
action: 'listAllUsers',
})}`,
)
}
// 查看节点列表
listNodes(): Promise<ListNodesResponse> {
return this.requestWithSignature(
`?${genUrlParamsFromObject({
action: 'listNodes',
})}`,
) as unknown as Promise<ListNodesResponse>
}
// 建立可信执行集群
async createTrustUnit(
data: { nodeName: string; pubkey: string }[],
Msg: string,
): Promise<{ action: string; status: string }> {
const toSign = `action=createTrustUnit&data=${JSON.stringify(
data,
)}&msg=${Msg}&pubKey=04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27`
const sig = sm2.doSignature(toSign, this.sm2Key.privateKey, {
hash: true,
der: true,
})
const body = {
action: 'createTrustUnit',
data,
msg: Msg,
pubKey:
'04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
sign: sig,
}
return (await this.requestWithSignature('', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})) as unknown as { action: string; status: string }
}
// 查看可信执行集群列表
listTrustUnits(): Promise<ClientResponse<{ key: string; value: string }[]>> {
return this.requestWithSignature(
`?${genUrlParamsFromObject({
action: 'listTrustUnits',
})}`,
)
}
// listContractProcess 查询合约实例列表
listContractProcess(): Promise<ClientResponse<string>> {
return this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject({
action: 'listContractProcess',
})}`,
)
}
// 下载合约
downloadContract(
projectName: string,
isPrivate: boolean,
timestamp: number
): Promise<ClientResponse<string>> {
const _request = {
action: 'downloadContract',
projectName,
isPrivate,
timestamp,
}
return this.requestWithSignature(
`/CMManager?${genUrlParamsFromObject(_request)}`)
}
// 配置合约引擎节点若节点没有设置过node manager将当前key设置为node manager
async configNode(
args: ConfigNodeArgs,
): Promise<boolean> {
if (!(await this.resetNodeManager())) {
return false
}
const kvs: [string, string][] = Object.entries(args).filter(([_k, v]) => v) as [string, string][]
await Promise.all([
...kvs.map(([k, v]) => this.updateConfig(k, v!)),
this.applyNodeRole('ContractProvider'),
this.applyNodeRole('ContractUser'),
this.applyNodeRole('ContractInstanceManager'),
this.authNodeRole(true, this.sm2Key.publicKey),
])
const config = (await this.loadNodeConfig()).data
if (!config) {
return false
}
return kvs.every(([k, v]) => config[remapNodeConfigKey(k)] === v)
}
}
function remapNodeConfigKey(k: string) {
switch (k) {
case 'dataChain':
return 'bdledger'
default:
return k
}
}

4
src/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from './crypto'
export * from './httpClient'
export * from './wsClient'
export * from './wssocket'

101
src/types.ts Normal file
View File

@ -0,0 +1,101 @@
export type ClientResponse<Data> = Omit<Response, 'data'> & {
data?: Data
}
export type PingResponse = ClientResponse<'pong'>
export interface SaveFileRequest {
content: string
isAppend: boolean
isPrivate: boolean
path: string
}
export interface ListProjectPermissionRequest {
isPrivate: boolean
path: string
}
export interface ListProjectPermissionResponseData {
permissions: string[]
ypk: string
}
export interface StartContractByYpkRequest {
isPrivate: boolean
path: string
script: string
// signature: string
}
export interface ListAllUsersResponseDataListItem {
key: string
value: string
}
export interface ListAllUsersResponseData {
kv: ListAllUsersResponseDataListItem[]
time: ListAllUsersResponseDataListItem[]
}
export interface OnlineContractsItem {
contractID: string
contractName: string
isMaster: boolean
type: string
yjsType: string
[key: string]: unknown
}
export interface OnlineItem {
cimanager: string
contractVersion: number
events: number
ipPort: string
masterAddress: string
nodeName: string
peerID: string
pubKey: string
contracts: OnlineContractsItem[]
}
export interface ListNodesResponse {
action: string
offline: string[]
online: OnlineItem[]
}
export interface DistributeContractResponse {
action: string
progress: string
}
export interface ExecuteContractArgs extends RequestInit {
method?: 'POST' | 'GET'
// 是否使用签名
withSignature?: boolean
withDynamicAnalysis?: boolean
}
export interface ExecuteContractResponse<Data> {
// ok: boolean
status?: boolean
// statusText: string
data?: Data
executeTime?: number
cid?: string
isPrivate?: boolean
[key: string]: unknown
}
export interface ConfigNodeArgs {
nodeName?: string
dataChain?: string
masterAddress?: string
nodeCenter?: string
// 配置中心节点时使用
LHSProxyAddress?: string
[K: string]: string | undefined
}
export interface LoadNodeConfigResponseData {
doipConfig: string
[K: string]: string
}

741
src/wsClient.ts Normal file
View File

@ -0,0 +1,741 @@
/* eslint-disable no-cond-assign */
import type { KeyPairHex } from 'sm-crypto'
import { sm2 } from 'sm-crypto'
import type { OnOpenHandler, WsHandler } from './wssocket'
import { WsSocket } from './wssocket'
interface ResponseData {
action: string
responseID?: string
status: true | false | string // 'Success' | 'Exception' | 'Error' | 'failed: no session'
result?: unknown
data: string
[K: string]: unknown
}
// interface ListResponseData {
// action: string
// data: string
// status: true | false
// }
interface PromiseCallbackPair {
resolve: (value: ResponseData | PromiseLike<ResponseData>) => void
reject: (reason?: unknown) => void
}
export class WsClient {
private readonly sm2Key: KeyPairHex
private readonly wssocket: WsSocket
private readonly promiseCallbackPairs: Record<string, PromiseCallbackPair> =
{}
private readonly sessionPromise: Promise<string>
private sessionResolve!: (value: string | PromiseLike<string>) => void
private readonly loginPromise: Promise<boolean>
private loginResolve!: (value: boolean | PromiseLike<boolean>) => void
constructor(
url: string,
onopen: OnOpenHandler,
handler: WsHandler,
sm2Key: KeyPairHex = sm2.generateKeyPairHex(),
) {
this.sm2Key = sm2Key
this.sessionPromise = new Promise((resolve, _reject) => {
this.sessionResolve = resolve
})
this.loginPromise = new Promise((resolve, _reject) => {
this.loginResolve = resolve
})
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this
this.wssocket = new WsSocket(url, onopen, function (
this: WebSocket,
event,
_,
) {
const data = JSON.parse(event.data) as ResponseData
switch (data.action) {
case 'onSessionID':
that.sessionResolve(data.session as string)
break
case 'onLogin':
{
const failed =
typeof data.status === 'string' &&
data.status.toLowerCase().includes('failed')
that.loginResolve(!failed)
}
break
}
const reqId = data.responseID
let pc: PromiseCallbackPair | undefined
if (reqId && (pc = that.promiseCallbackPairs[reqId])) {
if (data) {
pc.resolve(data)
} else {
pc.reject(data)
}
}
handler(event, this)
})
}
status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'] {
return this.wssocket.status()
}
sessionReceived(): Promise<string> {
return this.sessionPromise
}
async login(): Promise<boolean> {
const session = await this.sessionPromise
const request = {
action: 'login',
pubKey: this.sm2Key.publicKey,
signature: sm2.doSignature(session, this.sm2Key.privateKey, {
hash: true,
der: true,
}),
}
this.wssocket.send(JSON.stringify(request))
return this.loginPromise
}
loggedIn(): Promise<boolean> {
return this.loginPromise
}
matchCID(contractID: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'matchCID',
contractID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getMetabyCID(contractID: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getMetabyCID',
contractID,
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getMetabyReadme(
keyword: string,
page?: string,
pageSize?: string,
): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getMetabyReadme',
page,
pageSize,
keyword,
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getMetabyPubkey(pubkey: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getMetabyPubkey',
pubkey,
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
segmentWord(words: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
words,
action: 'segmentWord',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getMetabyOwner(
owner: string,
page?: string,
pageSize?: string,
): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getMetabyOwner',
owner,
page,
pageSize,
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getDependentContract(contractName: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getDependentContract',
requestID,
contractName,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
queryContractLogByDate(start: number): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'queryContractLogByDate',
requestID,
start,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
queryDataByHash(hash: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'queryDataByHash',
requestID,
hash,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
executeContract(
contractID: string,
method: string,
arg: unknown,
): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const argStr = (typeof arg) == 'object' ? JSON.stringify(arg):arg+"";
const request = {
action: 'executeContract',
requestID,
contractID,
operation: method,
arg: arg,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(
`${contractID}|${method}|${argStr}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getSessionID(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getSessionID',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
listTheContractProcess(contractID: string): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'listTheContractProcess',
requestID,
contractID,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(
`${contractID}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getMask(
contractID: string,
// operation: string,
): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getMask',
requestID,
contractID,
// operation,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(
`${contractID}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
setMask(
contractID: string,
operation: string,
arg: string,
): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'setMask',
requestID,
contractID,
operation,
arg,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(
`${contractID}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getMock(
contractID: string,
// operation: string,
): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getMock',
requestID,
contractID,
// operation,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(
`${contractID}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
setMock(
contractID: string,
operation: string,
arg: string,
): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'setMock',
requestID,
contractID,
operation,
arg,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(
`${contractID}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
queryHashByOffset(offset: number, count: number): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'queryHashByOffset',
requestID,
offset,
count,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
/* signature: sm2.doSignature(
id + '|' + sm2Key.publicKey,
sm2Key.privateKey,
{ hash: true, der: true },
), */
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
loadNodeConfig(): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'loadNodeConfig',
requestID,
...(sm2Key
? {
pubkey: sm2Key.publicKey,
signature: sm2.doSignature(sm2Key.publicKey, sm2Key.privateKey, {
hash: true,
der: true,
}),
}
: {}),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
queryUserStat(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'queryUserStat',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
listNodes(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'listNodes',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
killContractProcess(id: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'killContractProcess',
requestID,
id,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
distributeYPK(projectName: string, nodeIDs: string): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'distributeYPK',
requestID,
pubKey: sm2Key.publicKey,
projectName,
nodeIDs,
signature: sm2.doSignature(
`DistributeYPK|${projectName}|${sm2Key.publicKey}`,
sm2Key.privateKey,
{ hash: true, der: true },
),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
listYPKs(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'listYPKs',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
deleteFile(file: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'deleteFile',
requestID,
file,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
startContractByYPK(project: string): Promise<ResponseData> {
const sm2Key = this.sm2Key
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'startContractByYPK',
isPrivate: true,
requestID,
owner: sm2Key.publicKey,
path: `/${project}`,
signature: sm2.doSignature(
`Fixed|${project}|${sm2Key.publicKey}`,
sm2Key.privateKey,
),
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
initBDServer(
host: string,
username: string,
password: string,
name: string,
clusterHost: string,
): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'initBDServer',
requestID,
host,
username,
password,
name,
sm2Key: JSON.stringify(this.sm2Key),
clusterHost,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
initBDCluster(
host: string,
username: string,
password: string,
name: string,
sm2Key: string,
agents: [],
): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'initBDCluster',
requestID,
host,
username,
password,
name,
sm2Key,
agents,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
listCompiledFiles(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'listCompiledFiles',
requestID,
isPrivate: true,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getManagerPubkey(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getManagerPubkey',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
getClusterName(): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'getClusterName',
requestID,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
setClusterName(name: string): Promise<ResponseData> {
const requestID = `${new Date().getTime()}_${Math.floor(
Math.random() * 10000,
)}`
const request = {
action: 'setClusterName',
requestID,
name,
}
this.wssocket.send(JSON.stringify(request))
return new Promise((resolve, reject) => {
this.promiseCallbackPairs[requestID] = { resolve, reject }
})
}
}

143
src/wssocket.ts Normal file
View File

@ -0,0 +1,143 @@
/* eslint-disable no-console,no-cond-assign */
export interface WsEvent {
data: string
}
export type OnOpenHandler = (this: WebSocket, ev: Event) => void
export type WsHandler = (ev: WsEvent, ws?: WebSocket) => void
interface SegmentData {
action: 'sendSeg'
cid: string
data: string
}
export class WsSocket {
private handlerList: WsHandler[] = []
private toSend = ''
private isSending = false
private sendList: string[] = []
private toReceive = ''
private wssocket: WebSocket
constructor(wsurl: string, onopen: OnOpenHandler, handler?: WsHandler) {
console.log(`[createWS.js] createWssocket : ${wsurl}`)
if (handler) {
this.handlerList.push(handler)
}
// TODO: we don't need monitor at all?
this.monitor()
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this
const wssocket = (this.wssocket = new WebSocket(wsurl))
wssocket.onerror = function (error): void {
console.log(error)
}
wssocket.onopen = onopen
const onmessage = function (this: WebSocket, event: MessageEvent): void {
const obj = JSON.parse(event.data as string) as { action: string }
switch (obj.action) {
case 'sendNextSegment':
that.sendNextSegment()
break
case 'sendSeg':
that.receiveSeg(obj as SegmentData)
break
default:
that.handlerList.forEach((h) => h(event, this))
}
}
wssocket.onmessage = onmessage
const reconnect = function (): void {
setTimeout(() => {
console.log('[createWS.js] try to reconnect')
const wssocket = (that.wssocket = new WebSocket(wsurl))
wssocket.onclose = reconnect
wssocket.onmessage = onmessage
wssocket.onopen = onopen
}, 1000)
}
wssocket.onclose = reconnect
}
status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'] {
return this.wssocket.readyState
}
sendNextSegment(): void {
const str = this.toSend
if (str.length > 1024) {
this.toSend = str.substr(1024)
const obj = {
isSegment: true,
data: str.substr(0, 1024),
}
this.wssocket.send(JSON.stringify(obj))
} else {
this.toSend = ''
const obj = {
isSegment: false,
data: str,
}
this.wssocket.send(JSON.stringify(obj))
this.isSending = false
let data
if ((data = this.sendList.pop())) {
this.send(data)
}
}
}
receiveSeg(obj: SegmentData): void {
if (obj.cid === 'start') {
this.toReceive = ''
}
this.toReceive += obj.data
if (obj.cid === 'done') {
console.log(`[receiveSeg] Received AllData:${this.toReceive}`)
const event: WsEvent = {
data: this.toReceive,
}
this.toReceive = ''
this.handlerList.forEach((h) => h(event, this.wssocket))
}
}
monitor(): void {
if (!this.isSending) {
let data
if ((data = this.sendList.pop())) {
this.send(data)
}
}
setTimeout(() => this.monitor(), 1000)
}
send(data: string): void {
if (this.isSending) {
this.sendList.push(data)
return
}
if (data.length > 1024) {
this.isSending = true
this.toSend = data.substr(1024)
const obj = {
isSegment: true,
data: data.substr(0, 1024),
}
this.wssocket.send(JSON.stringify(obj))
} else {
this.wssocket.send(data)
}
}
addHandler(handler: WsHandler): void {
this.handlerList.push(handler)
}
}

91
test-node/http.html Normal file
View File

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script src="./sm2.js"></script>
<script src="./cryptico.iife.js"></script>
<script src="../lib/index.js"></script>
<script>
console.log(11)
function print(string) {
document.write(`${string}\n\n`)
}
;(async () => {
console.log(33)
const url = 'https://041.node.internetapi.cn:21030/SCIDE'
// const url = 'http://022.node.internetapi.cn:18010/SCIDE'
// const url = 'http://39.104.201.40:18010/NodeCenter'
// const url = 'http://localhost/8080/api/sse'
// eslint-disable-next-line no-undef
const sm2Key = {
privateKey:
'd675782acf011dbc01a73c7967ccff9564486f7c3a9f5d5de151caffaa18936',
publicKey:
'04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
// '04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
}
// eslint-disable-next-line no-undef
const client = new bdcontract.HttpClient(url, sm2Key)
// await client.authNodeRole(true,'0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a')
// const dataa = [
// {
// nodeName: '18000',
// pubKey:
// '041016acfbc8f4068a24d38a99a1355449ba958f7e905d66617cec9eda311e3001f8d15a4440b6eb61d7b7bd94bb434b30a518623c1593540e7d32eb72fe8088e1',
// },
// ]
// await client.createTrustUnit(dataa, '12345')
// const data = client.startContractByYPK({
// // signature:
// // '3045022100e13a8a1493ebbbb31e0b4d387ea061fd73a362b1e9fa0000066e87c5e9612907022037e04cb5ffa31fca23260a144eff2cbc8c04f6aac5be0d141609da4c66be3996',
// isPrivate: true,
// script: `contract shorgc\\n{ \\nexport function hello(){return 1 } }`,
// path: '/data/tmp/hello.ypk',
// })
// 0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a
// 调用合约
// const data = client.executeContract('ControlProxy', 'getDoList', 'ss', false)
// const data = await client.applyRole('NodeManager')
// const data = await client.applyNodeRole('ContractInstanceManager')
const data = await client.applyNodeRole('ContractInstanceManager')
console.log(data, 'data')
// const data = client.startContractMultiPoint(
// '041016acfbc8f4068a24d38a99a1355449ba958f7e905d66617cec9eda311e3001f8d15a4440b6eb61d7b7bd94bb434b30a518623c1593540e7d32eb72fe8088e1,04a1bcfe25e973691fb055c5400d74cd722f05313149a89b9ae94fffe8a3f141a36193c43bf520f8260cac1a703a3b8f1f92e9c7c23ce61ef9a0cbecf670b07212,04870d138f9e6ffae2a0ae0b495c6d81ff6cca9c92c861156d7a5304558427588e05452394ce69c4894283d1b0ccf40ffdffbd13fa0884d9e559c355ac1672263b,',
// 6,
// 3,ß
// 'H2_2022-07-12-12_48_55_Auto.ypk',
// false,
// '',
// )
// const data = client.updateConfig('nodeName','Node_180')
// const code = `contract shorgc\\n{ \\nexport function hello(){return 1 } }`
// const data = client.startContract(code)
// const data = client.saveFile({
// content: 's',
// isAppend: false,
// isPrivate: true,
// path: '/data/tmp/hello.ypk',
// })
// const data = client.loadNodeConfig()
// const data = client.listContractProcess()
// const data = client.listNodes()
// const data = client.distributeContract(
// '0495d91b4dec0d14b067e8a3bd5d78f53f1389920284c0153547d9417ed37708f276e8ac3d8420e571106c9ab2c210192dd356b9757ea498fe578e5cc85182712e,',
// 'DoipDist-0.1.0.ypk',
// false,
// '04d1924329f72ced148f6f333fb985ccbaa31b1e3aacf10be5f43d4a4ff5ad88899a005e79e37fc06993e1d66ada8cf8b711cb36f59538bb',
// )
// const data = client.killContractProcess('1759263594')
// const data = client.killAllContract()
print(JSON.stringify(data))
})()
// console.log(data)
</script>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body></body>
</html>

103
test-node/index.js Normal file
View File

@ -0,0 +1,103 @@
const bdcontract = require('@bdcontract/sdk')
// const sm2 = require('sm-crypto')
// import { sm2 } from 'sm-crypto'
async function main() {
// const url = 'http://localhost:21030/SCIDE'
const url = 'http://39.104.201.40:18010/SCIDE'
// const url = 'http://39.104.201.40:18010/NodeCenter'
// const url = 'http://021.node.internetapi.cn:18060/NodeCenter'
// const url = 'http://localhost/8080/api/sse'
// eslint-disable-next-line no-undef
const sm2Key = {
publicKey:
'04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
privateKey:
'd675782acf011dbc01a73c7967ccff9564486f7c3a9f5d5de151caffaa18936',
}
// const keyPair = sm2.generateKeyPairHex()
// const url1 = 'http://023.node.internetapi.cn:18010/SCIDE/SCManager'
// const url1 = 'http://023.node.internetapi.cn:18010/SCIDE/SCManager?action=applyNodeRole&role=ContractProvider&pubKey=04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27&sign=3046022100d186df39542f44deb08ca9a6a33ea4c0eef4222c3d0a9821e836a231daa615fa022100d89b202b3bb44c8caf98b3c415c62359eab2607c391ad04ba39034a550a78db3'
// const res1 = await fetch(url1)
// console.log(res1)
// const res2 = await fetch(url1)
// console.log(res2)
// eslint-disable-next-line no-undef
const client = new bdcontract.HttpClient(url, sm2Key, { timeout: 20000 })
try {
// const result = await client.executeContract(
// 'ControlProxy',
// 'setName',
// { name: 'df' },
// 'POST',
// true,
// )
// const result = await client.updateConfig('dataChain', 'ddsjkdsf')
// const result = await client.resetNodeManager()
// const result = await client.applyNodeRole('ContractProvider')
// const result = await client.loadNodeConfig()
// const result = await client.configContract({
// nodeName: 'test',
// bdledgerAddr: 'bdledger:2401',
// masterAddr: 'localhost:21030',
// contractCenterAddr: 'localhost:21040',
// // 配置中心节点时使用
// LHSProxyAddress: 'http://localhost:21041',
// })
// console.log(result, 'res1')
const result = await client.executeContract(
'ControlProxy',
'getDoList',
1,
true,
)
console.log(result, 'res1')
} catch (e) {
console.log('error: ', e.message)
}
// const res2 = await client.applyNodeRole('ContractInstanceManager')
// console.log(res2.data, 'res2')
// const res2 = await client.authNodeManager(
// true,
// '0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a',
// )
// // const res3 = await client.applyRole('NodeManager')
// console.log(res2, 'res2res2res2res2res2res2res2res2res2res2res2')
// const res3 = await client.applyNodeRole(
// '0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a',
// 'ContractInstanceManager',
// )
// console.log(res3, 'res33333333333333333333333333333333')
// // console.log(res1.data, 'data')
// const res1 = await client.authNodeRole(
// true,
// '0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a',
// )
// console.log(res1, 'res11111111111111111111111111111111')
// console.log(res1.data, 'data')
// const res1 = await client.applyRole('ContractProvider')
// console.log(res1, 'res')
// console.log(res1.data, 'data')
// let i = 0
// for (i = 0; i < 3000000000; i++) {
// a = i + 1
// if (a > 100000000000000) {
// console.log('b')
// }
// }
// setTimeout(async ()=> {
// const client2 = new bdcontract.HttpClient(url, sm2Key)
// const res2 = await client.applyNodeRole('ContractInstanceManager')
// console.log(res2)
// }, 1)
}
void main().then(console.log)

9
test-node/package.json Normal file
View File

@ -0,0 +1,9 @@
{
"dependencies": {
"@bdcontract/sdk": "link:..",
"@formily/core": "^2.2.5",
"@formily/element-plus": "1.0.0-beta.6",
"@formily/vue": "^2.2.5",
"@vue/composition-api": "^1.7.1"
}
}

18
test-node/tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"allowJs": true,
"target": "esnext",
"module": "esnext",
"lib": ["esnext", "dom", "dom.iterable"],
"moduleResolution": "Node",
"types": ["node"],
"strict": true,
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true
}
}

2
test/cryptico.iife.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

110
test/http.html Normal file
View File

@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <script src="../node_modules/cross-fetch/dist/cross-fetch.js"></script> -->
<script src="../lib/index.js"></script>
<script src="./sm2.js"></script>
<script src="./cryptico.iife.js"></script>
<!-- <script src="../dist/bdcontract-sdk.iife.js"></script> -->
<script>
function print(string) {
document.write(`${string}\n\n`)
}
;(async () => {
// const url = 'http://021.node.internetapi.cn:18010/SCIDE'
// const url = 'http://022.node.internetapi.cn:18010/SCIDE'
const url = 'http://041.node.internetapi.cn:21030/SCIDE/SCIDE'
// const url = 'http://localhost/8080/api/sse'
const sm2Key = {
privateKey:
'd675782acf011dbc01a73c7967ccff9564486f7c3a9f5d5de151caffaa18936',
publicKey:
'04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
// '04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
}
// eslint-disable-next-line no-undef
const client = new bdcontract.HttpClient(url, sm2Key)
const data = await client.executeContract(
'ControlProxy',
'getDoList',
1,
true,
)
// await client.applyNodeRole('ContractProvider')
// await client.applyNodeRole('ContractInstanceManager')
// const data = await client.authNodeRole(
// true,
// '0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a',
// )
// await client.authNodeRole(true,'0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a')
// const dataa = [
// {
// nodeName: '18000',
// pubKey:
// '041016acfbc8f4068a24d38a99a1355449ba958f7e905d66617cec9eda311e3001f8d15a4440b6eb61d7b7bd94bb434b30a518623c1593540e7d32eb72fe8088e1',
// },
// ]
// await client.createTrustUnit(dataa, '12345')
// const data = client.startContractByYPK({
// // signature:
// // '3045022100e13a8a1493ebbbb31e0b4d387ea061fd73a362b1e9fa0000066e87c5e9612907022037e04cb5ffa31fca23260a144eff2cbc8c04f6aac5be0d141609da4c66be3996',
// isPrivate: true,
// script: `contract shorgc\\n{ \\nexport function hello(){return 1 } }`,
// path: '/data/tmp/hello.ypk',
// })
// 0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a
// 调用合约
// const data = client.executeContract('ControlProxy', 'getDoList', 'ss', false)
// const data = await client.applyNodeRole(
// '0430276215ff414e0047a532d4dea32048ad4ec60a729ce8e4248c98889d244c3b27b9549e5cb664ce324e2acac4a81590321f4196ca159cb577e06a3eb553ce6a',
// 'ContractProvider',
// )
// const data = await client.applyNodeRole('ContractInstanceManager')
// const data = client.startContractMultiPoint(
// '041016acfbc8f4068a24d38a99a1355449ba958f7e905d66617cec9eda311e3001f8d15a4440b6eb61d7b7bd94bb434b30a518623c1593540e7d32eb72fe8088e1,04a1bcfe25e973691fb055c5400d74cd722f05313149a89b9ae94fffe8a3f141a36193c43bf520f8260cac1a703a3b8f1f92e9c7c23ce61ef9a0cbecf670b07212,04870d138f9e6ffae2a0ae0b495c6d81ff6cca9c92c861156d7a5304558427588e05452394ce69c4894283d1b0ccf40ffdffbd13fa0884d9e559c355ac1672263b,',
// 6,
// 3,ß
// 'H2_2022-07-12-12_48_55_Auto.ypk',
// false,
// '',
// )
// const data = client.updateConfig('nodeName','Node_180')
// const code = `contract shorgc\\n{ \\nexport function hello(){return 1 } }`
// const data = client.startContract(code)
// const data = client.saveFile({
// content: 's',
// isAppend: false,
// isPrivate: true,
// path: '/data/tmp/hello.ypk',
// })
// const data = client.loadNodeConfig()
// const data = client.listContractProcess()
// const data = client.listNodes()
// const data = client.distributeContract(
// '0495d91b4dec0d14b067e8a3bd5d78f53f1389920284c0153547d9417ed37708f276e8ac3d8420e571106c9ab2c210192dd356b9757ea498fe578e5cc85182712e,',
// 'DoipDist-0.1.0.ypk',
// false,
// '04d1924329f72ced148f6f333fb985ccbaa31b1e3aacf10be5f43d4a4ff5ad88899a005e79e37fc06993e1d66ada8cf8b711cb36f59538bb',
// )
// const data = client.killContractProcess('1759263594')
// const data = client.killAllContract()
// const data = await client.downloadContract(
// 'r',
// false,
// new Date().getTime(),
// )
print(JSON.stringify(data))
})()
// console.log(data)
</script>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body></body>
</html>

2102
test/sm2.js Normal file

File diff suppressed because it is too large Load Diff

51
test/ws.html Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<script src="./sm2.js"></script>
<script src="./cryptico.iife.js"></script>
<script src="../lib/index.js"></script>
<script>
function print(string) {
document.write(`${string}\n\n`)
}
const url = 'ws://39.104.201.40:18010/SCIDE/SCExecutor'
const client = new bdcontract.WsClient(
url,
(ev) => {
console.log(JSON.stringify(ev))
},
(ev, ws) => {
console.log(ev.data)
},
)
setTimeout(async () => {
const status = client.status()
console.log(status)
try {
let data = await client.executeContract(
'AnnotationExample0608',
'main',
'abacd',
)
print(JSON.stringify(data))
data = await client.executeContract(
'NonExistentContract',
'random',
'abacd',
)
print(JSON.stringify(data))
} catch (data) {
print(JSON.stringify(data))
}
}, 1000)
</script>
<link rel="icon" type="image/svg+xml" href="favicon.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite App</title>
</head>
<body></body>
</html>

10
tsconfig.build.json Normal file
View File

@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"declaration": true,
"declarationDir": "types",
"outDir": "dist"
},
"exclude": ["test/**/*.ts", "test/**/*.d.ts", "rollup.config.ts"]
}

26
tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"allowJs": true,
"target": "esnext",
"module": "esnext",
"lib": ["esnext", "dom", "dom.iterable"],
"moduleResolution": "Node",
"types": ["node"],
"strict": true,
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"test/**/*.ts",
"test/**/*.d.ts",
"rollup.config.ts"
],
"exclude": ["src/**/*.js", "test/**/*.js"]
}

16
types/crypto.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
import type { AESKey } from '@daotl/cryptico';
import { RSAKey } from '@daotl/cryptico';
export declare function aesEncrypt(data: string, aesKey: AESKey): string;
export declare function rsaEncrypt(data: string, rsaKey: {
n: string;
e1: string;
}): string;
export declare function loadRSAKey(rsaKey: string): RSAKey;
export declare function encryptReq(reqContent: {
contractID: string;
}, pubKey: RSAKey): {
action: string | null;
contractID: string;
arg: string;
requester: string;
};

56
types/httpClient.d.ts vendored Normal file
View File

@ -0,0 +1,56 @@
import { type AxiosRequestConfig } from 'axios';
import type { KeyPairHex } from 'sm-crypto';
import type { ClientResponse, ConfigNodeArgs, ExecuteContractArgs, ListAllUsersResponseData, ListNodesResponse, ListProjectPermissionRequest, ListProjectPermissionResponseData, LoadNodeConfigResponseData, ExecuteContractResponse, PingResponse, SaveFileRequest, StartContractByYpkRequest } from './types';
export * from './types';
export declare class HttpClient {
private baseUrl;
private sm2Key;
private fetch;
constructor(baseUrl: string, sm2Key: KeyPairHex, config?: AxiosRequestConfig<any>);
requestWithSignature<Data>(path: string, init?: Partial<RequestInit>, sm2Key?: KeyPairHex): Promise<ClientResponse<Data>>;
retryRequestWithSignature<Data>(retryTimes: number, path: string, init?: Partial<RequestInit>, sm2Key?: KeyPairHex): Promise<ClientResponse<Data>>;
sign(data: string, privateKey?: string): string;
ping(): Promise<PingResponse>;
startContract(code: string): Promise<ClientResponse<string>>;
startContractByYPK(_request: StartContractByYpkRequest): Promise<ClientResponse<string>>;
executeContract(contractID: string, operation: string, arg: string, { method, withDynamicAnalysis, withSignature, }?: ExecuteContractArgs): Promise<ClientResponse<ExecuteContractResponse<string>>>;
killContractProcess(contractID: string, requestID?: string): Promise<ClientResponse<string>>;
killAllContract(): Promise<ClientResponse<string>>;
applyNodeRole(role: string): Promise<ClientResponse<{
action: string;
data: string;
role?: string;
}>>;
authNodeRole(isAccept: boolean, authorizedPubKey: string, managerPair?: KeyPairHex): Promise<ClientResponse<{
action: string;
data: string;
}>>;
distributeContract(nodeIDs: string, projectName: string, isPrivate: boolean): void;
saveFile(_request: SaveFileRequest): Promise<ClientResponse<string>>;
listProjectPermission(_request: ListProjectPermissionRequest): Promise<ClientResponse<ListProjectPermissionResponseData>>;
startContractMultiPoint(peersID: string, type: number, selectUnitNum: number, projectName: string, isPrivate: boolean, sponsorPeerID: string): Promise<ClientResponse<string>>;
loadNodeConfig(): Promise<ClientResponse<LoadNodeConfigResponseData>>;
updateConfig(key: string, val: string): Promise<ClientResponse<boolean>>;
resetNodeManager(): Promise<boolean>;
lockEdit(): Promise<ClientResponse<string>>;
unlockEdit(): Promise<ClientResponse<string>>;
addNode(nodePubKey: string): Promise<ClientResponse<string>>;
applyRole(role: string): Promise<ClientResponse<string>>;
authNodeManager(isAccept: boolean, authorizedPubKey: string): Promise<ClientResponse<string>>;
listAllUsers(): Promise<ClientResponse<ListAllUsersResponseData>>;
listNodes(): Promise<ListNodesResponse>;
createTrustUnit(data: {
nodeName: string;
pubkey: string;
}[], Msg: string): Promise<{
action: string;
status: string;
}>;
listTrustUnits(): Promise<ClientResponse<{
key: string;
value: string;
}[]>>;
listContractProcess(): Promise<ClientResponse<string>>;
downloadContract(projectName: string, isPrivate: boolean, timestamp: number): Promise<ClientResponse<string>>;
configNode(args: ConfigNodeArgs): Promise<boolean>;
}

4
types/index.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
export * from './crypto';
export * from './httpClient';
export * from './wsClient';
export * from './wssocket';

84
types/types.d.ts vendored Normal file
View File

@ -0,0 +1,84 @@
export type ClientResponse<Data> = Omit<Response, 'data'> & {
data?: Data;
};
export type PingResponse = ClientResponse<'pong'>;
export interface SaveFileRequest {
content: string;
isAppend: boolean;
isPrivate: boolean;
path: string;
}
export interface ListProjectPermissionRequest {
isPrivate: boolean;
path: string;
}
export interface ListProjectPermissionResponseData {
permissions: string[];
ypk: string;
}
export interface StartContractByYpkRequest {
isPrivate: boolean;
path: string;
script: string;
}
export interface ListAllUsersResponseDataListItem {
key: string;
value: string;
}
export interface ListAllUsersResponseData {
kv: ListAllUsersResponseDataListItem[];
time: ListAllUsersResponseDataListItem[];
}
export interface OnlineContractsItem {
contractID: string;
contractName: string;
isMaster: boolean;
type: string;
yjsType: string;
[key: string]: unknown;
}
export interface OnlineItem {
cimanager: string;
contractVersion: number;
events: number;
ipPort: string;
masterAddress: string;
nodeName: string;
peerID: string;
pubKey: string;
contracts: OnlineContractsItem[];
}
export interface ListNodesResponse {
action: string;
offline: string[];
online: OnlineItem[];
}
export interface DistributeContractResponse {
action: string;
progress: string;
}
export interface ExecuteContractArgs extends RequestInit {
method?: 'POST' | 'GET';
withSignature?: boolean;
withDynamicAnalysis?: boolean;
}
export interface ExecuteContractResponse<Data> {
status?: boolean;
data?: Data;
executeTime?: number;
cid?: string;
isPrivate?: boolean;
[key: string]: unknown;
}
export interface ConfigNodeArgs {
nodeName?: string;
dataChain?: string;
masterAddress?: string;
nodeCenter?: string;
LHSProxyAddress?: string;
[K: string]: string | undefined;
}
export interface LoadNodeConfigResponseData {
doipConfig: string;
[K: string]: string;
}

56
types/wsClient.d.ts vendored Normal file
View File

@ -0,0 +1,56 @@
import type { KeyPairHex } from 'sm-crypto';
import type { OnOpenHandler, WsHandler } from './wssocket';
interface ResponseData {
action: string;
responseID?: string;
status: true | false | string;
result?: unknown;
data: string;
[K: string]: unknown;
}
export declare class WsClient {
private readonly sm2Key;
private readonly wssocket;
private readonly promiseCallbackPairs;
private readonly sessionPromise;
private sessionResolve;
private readonly loginPromise;
private loginResolve;
constructor(url: string, onopen: OnOpenHandler, handler: WsHandler, sm2Key?: KeyPairHex);
status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'];
sessionReceived(): Promise<string>;
login(): Promise<boolean>;
loggedIn(): Promise<boolean>;
matchCID(contractID: string): Promise<ResponseData>;
getMetabyCID(contractID: string): Promise<ResponseData>;
getMetabyReadme(keyword: string, page?: string, pageSize?: string): Promise<ResponseData>;
getMetabyPubkey(pubkey: string): Promise<ResponseData>;
segmentWord(words: string): Promise<ResponseData>;
getMetabyOwner(owner: string, page?: string, pageSize?: string): Promise<ResponseData>;
getDependentContract(contractName: string): Promise<ResponseData>;
queryContractLogByDate(start: number): Promise<ResponseData>;
queryDataByHash(hash: string): Promise<ResponseData>;
executeContract(contractID: string, method: string, arg: unknown): Promise<ResponseData>;
getSessionID(): Promise<ResponseData>;
listTheContractProcess(contractID: string): Promise<ResponseData>;
getMask(contractID: string): Promise<ResponseData>;
setMask(contractID: string, operation: string, arg: string): Promise<ResponseData>;
getMock(contractID: string): Promise<ResponseData>;
setMock(contractID: string, operation: string, arg: string): Promise<ResponseData>;
queryHashByOffset(offset: number, count: number): Promise<ResponseData>;
loadNodeConfig(): Promise<ResponseData>;
queryUserStat(): Promise<ResponseData>;
listNodes(): Promise<ResponseData>;
killContractProcess(id: string): Promise<ResponseData>;
distributeYPK(projectName: string, nodeIDs: string): Promise<ResponseData>;
listYPKs(): Promise<ResponseData>;
deleteFile(file: string): Promise<ResponseData>;
startContractByYPK(project: string): Promise<ResponseData>;
initBDServer(host: string, username: string, password: string, name: string, clusterHost: string): Promise<ResponseData>;
initBDCluster(host: string, username: string, password: string, name: string, sm2Key: string, agents: []): Promise<ResponseData>;
listCompiledFiles(): Promise<ResponseData>;
getManagerPubkey(): Promise<ResponseData>;
getClusterName(): Promise<ResponseData>;
setClusterName(name: string): Promise<ResponseData>;
}
export {};

26
types/wssocket.d.ts vendored Normal file
View File

@ -0,0 +1,26 @@
export interface WsEvent {
data: string;
}
export type OnOpenHandler = (this: WebSocket, ev: Event) => void;
export type WsHandler = (ev: WsEvent, ws?: WebSocket) => void;
interface SegmentData {
action: 'sendSeg';
cid: string;
data: string;
}
export declare class WsSocket {
private handlerList;
private toSend;
private isSending;
private sendList;
private toReceive;
private wssocket;
constructor(wsurl: string, onopen: OnOpenHandler, handler?: WsHandler);
status(): WebSocket['CLOSED' | 'CLOSING' | 'CONNECTING' | 'OPEN'];
sendNextSegment(): void;
receiveSeg(obj: SegmentData): void;
monitor(): void;
send(data: string): void;
addHandler(handler: WsHandler): void;
}
export {};