merge 0.4.0

This commit is contained in:
CaiHQ 2023-03-18 15:48:45 +08:00
parent 08da9c63f0
commit d75feb3ee1
3 changed files with 205 additions and 156 deletions

View File

@ -7,9 +7,9 @@ BDContract SDK for Node.js and browsers.
`npm install @bdware/bdcontract-sdk` `npm install @bdware/bdcontract-sdk`
```typescript ```typescript
import { Client } from '@bdware/bdcontract-sdk' import { WsClient } from '@bdware/bdcontract-sdk'
import { sm2 } from 'sm-crypto' import { sm2 } from 'sm-crypto'
const client = new Client( const client = new WsClient(
url, url,
(ev) => { (ev) => {
console.log(JSON.stringify(ev)) console.log(JSON.stringify(ev))
@ -42,7 +42,7 @@ function print(string) {
const url = 'ws://021.node.internetapi.cn:21030/SCIDE/SCExecutor' const url = 'ws://021.node.internetapi.cn:21030/SCIDE/SCExecutor'
const client = new bdcontract.Client( const client = new bdcontract.WsClient(
url, url,
(ev) => { (ev) => {
console.log(JSON.stringify(ev)) console.log(JSON.stringify(ev))
@ -76,4 +76,4 @@ async function anotherPlace() {
// In another place, wait for login to complete // In another place, wait for login to complete
const success = await client.Loggedin() const success = await client.Loggedin()
}() }()
``` ```

View File

@ -1,6 +1,6 @@
{ {
"name": "@bdware/bdcontract-sdk", "name": "@bdware/bdcontract-sdk",
"version": "0.3.0-alpha.15", "version": "0.4.0-beta",
"description": "BDContract SDK for Node.js and browsers", "description": "BDContract SDK for Node.js and browsers",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
@ -47,14 +47,5 @@
"shx": "^0.3.4", "shx": "^0.3.4",
"sucrase": "^3.25.0", "sucrase": "^3.25.0",
"typescript": "^4.8.2" "typescript": "^4.8.2"
}, }
"directories": {
"lib": "lib",
"test": "test"
},
"repository": {
"type": "git",
"url": "git@gitea.internetapi.cn:bdware/bdcontract-sdk-javascript.git"
},
"author": "caihq@pku.edu.cn"
} }

View File

@ -26,19 +26,40 @@ const genUrlParamsFromObject = (obj: Record<string, unknown>): string => {
export * from './types' export * from './types'
export const retryAsyncN =
(maxRetries: number) =>
async <Ret>(fn: () => Promise<Ret>) => {
let i = 0
while (true)
try {
return await fn()
} catch (e) {
if (i++ === maxRetries) {
throw e
}
}
}
export class HttpClient { export class HttpClient {
private fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response> private fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>
private retryAsync: <Ret>(fn: () => Promise<Ret>) => Promise<Ret>
constructor( constructor(
private baseUrl: string, private baseUrl: string,
private sm2Key: KeyPairHex, private sm2Key: KeyPairHex,
config = {} as AxiosRequestConfig, options: {
axios: AxiosRequestConfig
maxRetries: number
} = {
axios: {},
maxRetries: 3,
},
) { ) {
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: 'https://some-domain.com/api/', baseURL: 'https://some-domain.com/api/',
timeout: 10000, timeout: 10000,
// headers: {'X-Custom-Header': 'foobar'} // headers: {'X-Custom-Header': 'foobar'}
...config, ...options.axios,
}) })
this.fetch = buildAxiosFetch( this.fetch = buildAxiosFetch(
@ -47,6 +68,8 @@ export class HttpClient {
return config; return config;
} */, } */,
) )
this.retryAsync = retryAsyncN(options.maxRetries)
} }
async requestWithSignature<Data>( async requestWithSignature<Data>(
@ -107,7 +130,6 @@ export class HttpClient {
.catch((err) => { .catch((err) => {
// const clientRes = err as unknown as ClientResponse<Data> // const clientRes = err as unknown as ClientResponse<Data>
// clientRes.data = (await err.text()) as Data // clientRes.data = (await err.text()) as Data
// console.log(err,'errerrerrerrerrerrerrerrerrerrerrerrerrerrerr')
setTimeout(() => { setTimeout(() => {
reject(err) reject(err)
}, 1) }, 1)
@ -115,22 +137,6 @@ export class HttpClient {
}) })
} }
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 { sign(data: string, privateKey?: string): string {
return sm2.doSignature(data, privateKey ?? this.sm2Key.privateKey, { return sm2.doSignature(data, privateKey ?? this.sm2Key.privateKey, {
hash: true, hash: true,
@ -141,15 +147,17 @@ export class HttpClient {
// ping // ping
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id15 // https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id15
ping(): Promise<PingResponse> { ping(): Promise<PingResponse> {
return this.requestWithSignature('/SCManager?action=ping') return this.retryAsync(() =>
this.requestWithSignature('/SCManager?action=ping'),
)
} }
// 启动合约 // 启动合约
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id60 // https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id60
startContract(code: string): Promise<ClientResponse<string>> { startContract(code: string): Promise<ClientResponse<string>> {
const params = { action: 'startContract', script: code } const params = { action: 'startContract', script: code }
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject(params)}`, this.requestWithSignature(`/SCManager?${genUrlParamsFromObject(params)}`),
) )
} }
@ -158,21 +166,23 @@ export class HttpClient {
startContractByYPK( startContractByYPK(
_request: StartContractByYpkRequest, _request: StartContractByYpkRequest,
): Promise<ClientResponse<string>> { ): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'startContractByYPK', `/SCManager?${genUrlParamsFromObject({
..._request, action: 'startContractByYPK',
owner: this.sm2Key.publicKey, ..._request,
aim: 'onStartContract', owner: this.sm2Key.publicKey,
signature: sm2.doSignature( aim: 'onStartContract',
`Fixed|${_request.path}|${this.sm2Key.publicKey}`, signature: sm2.doSignature(
this.sm2Key.privateKey, `Fixed|${_request.path}|${this.sm2Key.publicKey}`,
{ this.sm2Key.privateKey,
hash: true, {
der: true, hash: true,
}, der: true,
), },
})}`, ),
})}`,
),
) )
} }
@ -214,17 +224,17 @@ export class HttpClient {
), ),
} }
} }
return this.retryAsync(() =>
return this.retryRequestWithSignature( this.requestWithSignature(
3, `/SCManager${
`/SCManager${ method === 'GET' ? `?${genUrlParamsFromObject(request)}` : ''
method === 'GET' ? `?${genUrlParamsFromObject(request)}` : '' }`,
}`, {
{ method,
method, // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions ...(method === 'POST' ? ({ body: request } as Request) : {}),
...(method === 'POST' ? ({ body: request } as Request) : {}), },
}, ),
) )
} }
@ -239,8 +249,10 @@ export class HttpClient {
id: contractID, id: contractID,
} }
requestID && (_request.requestID = requestID!) requestID && (_request.requestID = requestID!)
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject(_request)}`, this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject(_request)}`,
),
) )
} }
@ -248,7 +260,9 @@ export class HttpClient {
// 地址 http://39.104.205.122:18010/SCIDE // 地址 http://39.104.205.122:18010/SCIDE
// https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id131 // https://public.internetapi.cn/docs/bdcontract/doc/ContractAPI.html#id131
killAllContract(): Promise<ClientResponse<string>> { killAllContract(): Promise<ClientResponse<string>> {
return this.requestWithSignature('/SCManager?action=killAllContract') return this.retryAsync(() =>
this.requestWithSignature('/SCManager?action=killAllContract'),
)
} }
// 申请角色 // 申请角色
@ -260,8 +274,10 @@ export class HttpClient {
action: 'applyNodeRole', action: 'applyNodeRole',
role, role,
} }
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject(_request)}`, this.requestWithSignature(
`/SCManager?${genUrlParamsFromObject(_request)}`,
),
) )
} }
@ -277,10 +293,12 @@ export class HttpClient {
isAccept, isAccept,
authorizedPubKey, authorizedPubKey,
} }
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject(_request)}`, this.requestWithSignature(
undefined, `/SCManager?${genUrlParamsFromObject(_request)}`,
managerPair, undefined,
managerPair,
),
) )
} }
@ -332,22 +350,26 @@ export class HttpClient {
} }
saveFile(_request: SaveFileRequest): Promise<ClientResponse<string>> { saveFile(_request: SaveFileRequest): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'saveFile', `/SCManager?${genUrlParamsFromObject({
..._request, action: 'saveFile',
})}`, ..._request,
})}`,
),
) )
} }
listProjectPermission( listProjectPermission(
_request: ListProjectPermissionRequest, _request: ListProjectPermissionRequest,
): Promise<ClientResponse<ListProjectPermissionResponseData>> { ): Promise<ClientResponse<ListProjectPermissionResponseData>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'listProjectPermission', `/SCManager?${genUrlParamsFromObject({
..._request, action: 'listProjectPermission',
})}`, ..._request,
})}`,
),
) )
} }
@ -368,22 +390,31 @@ export class HttpClient {
isPrivate, isPrivate,
sponsorPeerID, sponsorPeerID,
} }
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'startContractMultiPoint', `/SCManager?${genUrlParamsFromObject({
..._request, action: 'startContractMultiPoint',
})}`, ..._request,
})}`,
),
) )
} }
// 获取节点配置信息 // 获取节点配置信息
async loadNodeConfig(): Promise<ClientResponse<LoadNodeConfigResponseData>> { async loadNodeConfig(): Promise<ClientResponse<LoadNodeConfigResponseData>> {
const res = await this.requestWithSignature<string>( const res = await this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature<string>(
action: 'loadNodeConfig', `/SCManager?${genUrlParamsFromObject({
})}`, action: 'loadNodeConfig',
})}`,
),
) )
return { ...res, data: (res.status === 200 && res.data ? JSON.parse( res.data).data : {}) as LoadNodeConfigResponseData} return {
...res,
data: (res.status === 200 && res.data
? JSON.parse(res.data).data
: {}) as LoadNodeConfigResponseData,
}
} }
// 支持的key包括{licenceprojectDiryjsPathdataChaindoipConfignodeCenternodeNamemasterAddressresetNodeCenterWS} // 支持的key包括{licenceprojectDiryjsPathdataChaindoipConfignodeCenternodeNamemasterAddressresetNodeCenterWS}
@ -391,21 +422,25 @@ export class HttpClient {
key: string, key: string,
val: string, val: string,
): Promise<ClientResponse<boolean>> { ): Promise<ClientResponse<boolean>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'updateConfig', `/SCManager?${genUrlParamsFromObject({
key, action: 'updateConfig',
val, key,
})}`, val,
})}`,
),
) )
} }
// 设置pubkey为node manager // 设置pubkey为node manager
async resetNodeManager(): Promise<boolean> { async resetNodeManager(): Promise<boolean> {
const res = await this.requestWithSignature<string>( const res = await this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature<string>(
action: 'resetNodeManager', `/SCManager?${genUrlParamsFromObject({
})}`, action: 'resetNodeManager',
})}`,
),
) )
if (!res.data) { if (!res.data) {
return false return false
@ -416,38 +451,46 @@ export class HttpClient {
// 锁定某个用户的的私有目录编辑功能 // 锁定某个用户的的私有目录编辑功能
lockEdit(): Promise<ClientResponse<string>> { lockEdit(): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'lockEdit', `/SCManager?${genUrlParamsFromObject({
})}`, action: 'lockEdit',
})}`,
),
) )
} }
// 解锁某个用户的的私有目录编辑功能 // 解锁某个用户的的私有目录编辑功能
unlockEdit(): Promise<ClientResponse<string>> { unlockEdit(): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'unlockEdit', `/SCManager?${genUrlParamsFromObject({
})}`, action: 'unlockEdit',
})}`,
),
) )
} }
addNode(nodePubKey: string): Promise<ClientResponse<string>> { addNode(nodePubKey: string): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'addNode', `?${genUrlParamsFromObject({
nodePubKey, action: 'addNode',
})}`, nodePubKey,
})}`,
),
) )
} }
// 申请角色 // 申请角色
applyRole(role: string): Promise<ClientResponse<string>> { applyRole(role: string): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'applyRole', `?${genUrlParamsFromObject({
role, action: 'applyRole',
})}`, role,
})}`,
),
) )
} }
@ -455,29 +498,35 @@ export class HttpClient {
isAccept: boolean, isAccept: boolean,
authorizedPubKey: string, authorizedPubKey: string,
): Promise<ClientResponse<string>> { ): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'authNodeManager', `?${genUrlParamsFromObject({
isAccept, action: 'authNodeManager',
authorizedPubKey, isAccept,
})}`, authorizedPubKey,
})}`,
),
) )
} }
listAllUsers(): Promise<ClientResponse<ListAllUsersResponseData>> { listAllUsers(): Promise<ClientResponse<ListAllUsersResponseData>> {
return this.requestWithSignature( return this.retryAsync(() =>
`?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'listAllUsers', `?${genUrlParamsFromObject({
})}`, action: 'listAllUsers',
})}`,
),
) )
} }
// 查看节点列表 // 查看节点列表
listNodes(): Promise<ListNodesResponse> { listNodes(): Promise<ListNodesResponse> {
return this.requestWithSignature( return this.retryAsync(() =>
`?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'listNodes', `?${genUrlParamsFromObject({
})}`, action: 'listNodes',
})}`,
),
) as unknown as Promise<ListNodesResponse> ) as unknown as Promise<ListNodesResponse>
} }
@ -502,30 +551,36 @@ export class HttpClient {
'04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27', '04303718771b9323c204e607639f14469f9a94e55b0964a408ad3b3864b0493b645d7070da0d550f0c54b934275a8e88dedc3024467b0566db5c1108b1baeaae27',
sign: sig, sign: sig,
} }
return (await this.requestWithSignature('', { return (await this.retryAsync(() =>
method: 'POST', this.requestWithSignature('', {
headers: { method: 'POST',
'Content-Type': 'application/json', headers: {
}, 'Content-Type': 'application/json',
body: JSON.stringify(body), },
})) as unknown as { action: string; status: string } body: JSON.stringify(body),
}),
)) as unknown as { action: string; status: string }
} }
// 查看可信执行集群列表 // 查看可信执行集群列表
listTrustUnits(): Promise<ClientResponse<{ key: string; value: string }[]>> { listTrustUnits(): Promise<ClientResponse<{ key: string; value: string }[]>> {
return this.requestWithSignature( return this.retryAsync(() =>
`?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'listTrustUnits', `?${genUrlParamsFromObject({
})}`, action: 'listTrustUnits',
})}`,
),
) )
} }
// listContractProcess 查询合约实例列表 // listContractProcess 查询合约实例列表
listContractProcess(): Promise<ClientResponse<string>> { listContractProcess(): Promise<ClientResponse<string>> {
return this.requestWithSignature( return this.retryAsync(() =>
`/SCManager?${genUrlParamsFromObject({ this.requestWithSignature(
action: 'listContractProcess', `/SCManager?${genUrlParamsFromObject({
})}`, action: 'listContractProcess',
})}`,
),
) )
} }
@ -533,7 +588,7 @@ export class HttpClient {
downloadContract( downloadContract(
projectName: string, projectName: string,
isPrivate: boolean, isPrivate: boolean,
timestamp: number timestamp: number,
): Promise<ClientResponse<string>> { ): Promise<ClientResponse<string>> {
const _request = { const _request = {
action: 'downloadContract', action: 'downloadContract',
@ -541,19 +596,22 @@ export class HttpClient {
isPrivate, isPrivate,
timestamp, timestamp,
} }
return this.requestWithSignature( return this.retryAsync(() =>
`/CMManager?${genUrlParamsFromObject(_request)}`) this.requestWithSignature(
`/CMManager?${genUrlParamsFromObject(_request)}`,
),
)
} }
// 配置合约引擎节点若节点没有设置过node manager将当前key设置为node manager // 配置合约引擎节点若节点没有设置过node manager将当前key设置为node manager
async configNode( async configNode(args: ConfigNodeArgs): Promise<boolean> {
args: ConfigNodeArgs,
): Promise<boolean> {
if (!(await this.resetNodeManager())) { if (!(await this.resetNodeManager())) {
return false return false
} }
const kvs: [string, string][] = Object.entries(args).filter(([_k, v]) => v) as [string, string][] const kvs: [string, string][] = Object.entries(args).filter(
([_k, v]) => v,
) as [string, string][]
await Promise.all([ await Promise.all([
...kvs.map(([k, v]) => this.updateConfig(k, v!)), ...kvs.map(([k, v]) => this.updateConfig(k, v!)),