@dashevo/dapi-client 0.23.0-dev.9 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/lib/BlockHeadersProvider/BlockHeadersProvider.js +187 -50
- package/lib/BlockHeadersProvider/BlockHeadersReader.js +170 -88
- package/lib/BlockHeadersProvider/createBlockHeadersProviderFromOptions.js +31 -16
- package/lib/DAPIClient.js +0 -6
- package/lib/index.js +5 -0
- package/lib/networkConfigs.js +114 -150
- package/lib/test/bootstrap.js +8 -0
- package/lib/test/mocks/BlockHeadersWithChainLocksStreamMock.js +54 -0
- package/lib/test/mocks/mockHeadersChain.js +55 -0
- package/lib/transport/GrpcTransport/createGrpcTransportError.js +21 -34
- package/lib/transport/JsonRpcTransport/requestJsonRpc.js +21 -23
- package/lib/transport/ReconnectableStream.js +284 -0
- package/lib/{test/utils → utils}/wait.js +0 -0
- package/package.json +10 -10
package/lib/networkConfigs.js
CHANGED
|
@@ -12,156 +12,120 @@ module.exports = {
|
|
|
12
12
|
// that breaks test suite and application logic. Temporary solution is to hardcode
|
|
13
13
|
// reliable DCG testnet masternodes to connect. Should be removed when PoSe is introduced.
|
|
14
14
|
dapiAddressesWhiteList: [
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'54.
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'34.220.
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'34.
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'18.237.
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'35.
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'52.
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'54.
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'54.
|
|
62
|
-
'
|
|
63
|
-
'35.
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'54.
|
|
67
|
-
'54.
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'
|
|
73
|
-
'35.
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'34.
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'54.
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'54.
|
|
87
|
-
'
|
|
88
|
-
'35.
|
|
89
|
-
'
|
|
90
|
-
'
|
|
91
|
-
'
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'54.
|
|
101
|
-
'
|
|
102
|
-
'54.
|
|
103
|
-
'34.
|
|
104
|
-
'
|
|
105
|
-
'54.
|
|
106
|
-
'
|
|
107
|
-
'34.
|
|
108
|
-
'
|
|
109
|
-
'54.
|
|
110
|
-
'18.
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
'
|
|
114
|
-
'
|
|
115
|
-
'
|
|
116
|
-
'
|
|
117
|
-
'
|
|
118
|
-
'34.
|
|
119
|
-
'
|
|
120
|
-
'
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'54.
|
|
124
|
-
'
|
|
125
|
-
'
|
|
126
|
-
'34.
|
|
127
|
-
'
|
|
128
|
-
'
|
|
129
|
-
'34.222.6.55',
|
|
130
|
-
'54.184.7.184',
|
|
131
|
-
'52.12.47.86',
|
|
132
|
-
'34.217.28.248',
|
|
133
|
-
'54.149.252.146',
|
|
134
|
-
'54.149.80.193',
|
|
135
|
-
'34.215.55.0',
|
|
136
|
-
'34.220.74.48',
|
|
137
|
-
'34.209.124.112',
|
|
138
|
-
'34.217.23.70',
|
|
139
|
-
'34.222.102.137',
|
|
140
|
-
'34.209.166.42',
|
|
141
|
-
'18.236.128.49',
|
|
142
|
-
'35.163.99.20',
|
|
143
|
-
'34.215.67.224',
|
|
144
|
-
'34.211.244.117',
|
|
145
|
-
'18.236.199.232',
|
|
146
|
-
'54.191.237.52',
|
|
147
|
-
'34.223.226.224',
|
|
148
|
-
'54.149.133.143',
|
|
149
|
-
'52.41.198.242',
|
|
150
|
-
'54.148.215.161',
|
|
151
|
-
'54.188.193.70',
|
|
152
|
-
'54.218.104.194',
|
|
153
|
-
'34.209.73.208',
|
|
154
|
-
'34.220.88.70',
|
|
155
|
-
'52.40.101.104',
|
|
156
|
-
'18.236.68.153',
|
|
157
|
-
'34.218.76.179',
|
|
158
|
-
'34.219.94.178',
|
|
159
|
-
'54.218.127.128',
|
|
160
|
-
'52.27.198.246',
|
|
161
|
-
'18.237.204.153',
|
|
162
|
-
'35.166.57.113',
|
|
163
|
-
'54.191.15.3',
|
|
164
|
-
'18.237.128.46',
|
|
15
|
+
'34.216.69.175',
|
|
16
|
+
'34.218.75.96',
|
|
17
|
+
'18.237.220.27',
|
|
18
|
+
'54.191.38.234',
|
|
19
|
+
'35.93.95.157',
|
|
20
|
+
'35.166.240.246',
|
|
21
|
+
'35.91.186.151',
|
|
22
|
+
'35.161.123.225',
|
|
23
|
+
'54.212.169.115',
|
|
24
|
+
'54.187.168.12',
|
|
25
|
+
'18.236.191.199',
|
|
26
|
+
'18.237.165.224',
|
|
27
|
+
'52.36.42.1',
|
|
28
|
+
'34.220.32.216',
|
|
29
|
+
'34.222.238.250',
|
|
30
|
+
'35.93.34.215',
|
|
31
|
+
'34.221.10.165',
|
|
32
|
+
'54.203.241.195',
|
|
33
|
+
'35.92.138.231',
|
|
34
|
+
'52.38.169.156',
|
|
35
|
+
'54.187.21.108',
|
|
36
|
+
'34.219.48.219',
|
|
37
|
+
'34.217.38.221',
|
|
38
|
+
'35.92.157.107',
|
|
39
|
+
'18.236.82.230',
|
|
40
|
+
'54.184.225.22',
|
|
41
|
+
'18.237.144.120',
|
|
42
|
+
'18.237.254.214',
|
|
43
|
+
'35.88.29.196',
|
|
44
|
+
'35.89.126.245',
|
|
45
|
+
'34.217.148.69',
|
|
46
|
+
'35.89.193.46',
|
|
47
|
+
'35.89.119.37',
|
|
48
|
+
'54.186.161.236',
|
|
49
|
+
'52.13.63.7',
|
|
50
|
+
'18.236.233.225',
|
|
51
|
+
'54.191.138.72',
|
|
52
|
+
'54.191.59.75',
|
|
53
|
+
'18.237.160.84',
|
|
54
|
+
'54.218.236.193',
|
|
55
|
+
'54.148.178.140',
|
|
56
|
+
'35.161.168.136',
|
|
57
|
+
'35.161.33.22',
|
|
58
|
+
'35.160.130.44',
|
|
59
|
+
'34.212.104.247',
|
|
60
|
+
'52.13.29.83',
|
|
61
|
+
'54.149.39.172',
|
|
62
|
+
'35.89.93.255',
|
|
63
|
+
'35.85.46.137',
|
|
64
|
+
'54.188.154.252',
|
|
65
|
+
'54.218.11.172',
|
|
66
|
+
'54.186.47.235',
|
|
67
|
+
'54.244.145.48',
|
|
68
|
+
'18.236.232.153',
|
|
69
|
+
'34.220.226.135',
|
|
70
|
+
'34.220.30.59',
|
|
71
|
+
'18.237.160.97',
|
|
72
|
+
'18.236.146.99',
|
|
73
|
+
'35.88.169.18',
|
|
74
|
+
'35.164.17.198',
|
|
75
|
+
'54.218.80.158',
|
|
76
|
+
'34.220.102.107',
|
|
77
|
+
'35.165.60.113',
|
|
78
|
+
'34.215.234.63',
|
|
79
|
+
'35.89.146.226',
|
|
80
|
+
'35.89.86.26',
|
|
81
|
+
'35.88.160.231',
|
|
82
|
+
'54.245.8.155',
|
|
83
|
+
'52.36.221.94',
|
|
84
|
+
'52.35.46.188',
|
|
85
|
+
'34.221.153.67',
|
|
86
|
+
'54.212.153.15',
|
|
87
|
+
'35.85.55.192',
|
|
88
|
+
'35.89.105.90',
|
|
89
|
+
'52.24.22.52',
|
|
90
|
+
'54.212.33.96',
|
|
91
|
+
'52.10.19.238',
|
|
92
|
+
'35.160.190.165',
|
|
93
|
+
'34.219.3.61',
|
|
94
|
+
'54.187.32.187',
|
|
95
|
+
'35.162.221.77',
|
|
96
|
+
'35.91.64.183',
|
|
97
|
+
'18.236.189.15',
|
|
98
|
+
'35.91.145.195',
|
|
99
|
+
'54.186.251.30',
|
|
100
|
+
'54.245.9.46',
|
|
101
|
+
'35.91.48.128',
|
|
102
|
+
'54.244.75.85',
|
|
103
|
+
'34.221.159.53',
|
|
104
|
+
'35.89.88.166',
|
|
105
|
+
'54.185.249.165',
|
|
106
|
+
'54.245.9.209',
|
|
107
|
+
'34.222.167.162',
|
|
108
|
+
'54.191.88.232',
|
|
109
|
+
'54.191.111.112',
|
|
110
|
+
'18.237.146.37',
|
|
111
|
+
'35.89.148.27',
|
|
112
|
+
'35.91.8.48',
|
|
113
|
+
'18.236.208.138',
|
|
114
|
+
'54.213.60.60',
|
|
115
|
+
'35.92.151.94',
|
|
116
|
+
'18.237.241.37',
|
|
117
|
+
'54.202.209.229',
|
|
118
|
+
'34.221.9.70',
|
|
119
|
+
'35.89.38.62',
|
|
120
|
+
'54.218.81.193',
|
|
121
|
+
'34.221.158.21',
|
|
122
|
+
'35.88.97.57',
|
|
123
|
+
'54.68.247.40',
|
|
124
|
+
'35.88.168.176',
|
|
125
|
+
'35.89.202.171',
|
|
126
|
+
'34.220.106.205',
|
|
127
|
+
'34.210.86.23',
|
|
128
|
+
'44.230.40.184',
|
|
165
129
|
],
|
|
166
130
|
},
|
|
167
131
|
evonet: {
|
package/lib/test/bootstrap.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require('../../polyfills/fetch-polyfill');
|
|
2
|
+
|
|
1
3
|
const { expect, use } = require('chai');
|
|
2
4
|
const sinon = require('sinon');
|
|
3
5
|
const sinonChai = require('sinon-chai');
|
|
@@ -20,4 +22,10 @@ afterEach(function afterEach() {
|
|
|
20
22
|
this.sinon.restore();
|
|
21
23
|
});
|
|
22
24
|
|
|
25
|
+
before(function before() {
|
|
26
|
+
if (!this.sinon) {
|
|
27
|
+
this.sinon = sinon.createSandbox();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
23
31
|
global.expect = expect;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
|
|
3
|
+
class BlockHeadersWithChainLocksStreamMock extends EventEmitter {
|
|
4
|
+
constructor(sinon) {
|
|
5
|
+
super();
|
|
6
|
+
|
|
7
|
+
sinon.spy(this, 'on');
|
|
8
|
+
sinon.spy(this, 'removeListener');
|
|
9
|
+
sinon.spy(this, 'emit');
|
|
10
|
+
sinon.spy(this, 'removeAllListeners');
|
|
11
|
+
sinon.spy(this, 'cancel');
|
|
12
|
+
|
|
13
|
+
this.errored = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
cancel() {
|
|
17
|
+
if (!this.errored) {
|
|
18
|
+
const err = new Error('CANCELED_ON_CLIENT');
|
|
19
|
+
err.code = 1;
|
|
20
|
+
this.emit('error', err);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
emit(event, data) {
|
|
25
|
+
if (event === 'error') {
|
|
26
|
+
this.errored = true;
|
|
27
|
+
}
|
|
28
|
+
super.emit(event, data);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param headers {BlockHeader[]}
|
|
33
|
+
*/
|
|
34
|
+
sendHeaders(headers) {
|
|
35
|
+
this.emit('data', {
|
|
36
|
+
getBlockHeaders: () => ({
|
|
37
|
+
getHeadersList() {
|
|
38
|
+
return headers.map((header) => header.toBuffer());
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
errorHandler(e) {
|
|
45
|
+
this.emit('error', e);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
end() {
|
|
49
|
+
this.emit('end');
|
|
50
|
+
this.removeAllListeners();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = BlockHeadersWithChainLocksStreamMock;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const X11 = require('wasm-x11-hash');
|
|
2
|
+
const { BlockHeader, configure } = require('@dashevo/dashcore-lib');
|
|
3
|
+
const { genesis } = require('@dashevo/dash-spv');
|
|
4
|
+
|
|
5
|
+
const getRoot = (network) => {
|
|
6
|
+
switch (network) {
|
|
7
|
+
case 'testnet':
|
|
8
|
+
return genesis.getTestnetGenesis();
|
|
9
|
+
case 'devnet':
|
|
10
|
+
return genesis.getDevnetGenesis();
|
|
11
|
+
case 'regtest':
|
|
12
|
+
return genesis.getRegtestGenesis();
|
|
13
|
+
default:
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const BLOCK_TIME = 2.5 * 60;
|
|
21
|
+
|
|
22
|
+
let x11;
|
|
23
|
+
|
|
24
|
+
const mockHeadersChain = async (network, length, root) => {
|
|
25
|
+
if (!x11) {
|
|
26
|
+
x11 = await X11();
|
|
27
|
+
// Configure Dashcore lib to operate with wasm x11
|
|
28
|
+
configure({
|
|
29
|
+
x11hash: x11,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rootHeader = root || getRoot(network);
|
|
34
|
+
|
|
35
|
+
const chain = [rootHeader];
|
|
36
|
+
|
|
37
|
+
let prevHeader = rootHeader;
|
|
38
|
+
for (let i = 0; i < length - 1; i += 1) {
|
|
39
|
+
const header = new BlockHeader({
|
|
40
|
+
version: prevHeader.version,
|
|
41
|
+
prevHash: Buffer.from(prevHeader.hash, 'hex').reverse(),
|
|
42
|
+
merkleRoot: Buffer.alloc(32),
|
|
43
|
+
time: prevHeader.time + BLOCK_TIME,
|
|
44
|
+
bits: prevHeader.bits,
|
|
45
|
+
nonce: 3861367235,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
chain.push(header);
|
|
49
|
+
prevHeader = header;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return chain;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports = mockHeadersChain;
|
|
@@ -2,7 +2,7 @@ const cbor = require('cbor');
|
|
|
2
2
|
|
|
3
3
|
const createConsensusError = require('@dashevo/dpp/lib/errors/consensus/createConsensusError');
|
|
4
4
|
const GrpcErrorCodes = require('@dashevo/grpc-common/lib/server/error/GrpcErrorCodes');
|
|
5
|
-
const {
|
|
5
|
+
const { parseMetadata } = require('@dashevo/dapi-grpc');
|
|
6
6
|
|
|
7
7
|
const NotFoundError = require('./errors/NotFoundError');
|
|
8
8
|
const TimeoutError = require('./errors/TimeoutError');
|
|
@@ -50,32 +50,26 @@ function createGrpcTransportError(grpcError, dapiAddress) {
|
|
|
50
50
|
|
|
51
51
|
const message = grpcError.details || grpcError.message;
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Error stack
|
|
74
|
-
const driveErrorStack = metadataMap['stack-bin'];
|
|
75
|
-
if (driveErrorStack) {
|
|
76
|
-
const encodedStack = Buffer.from(driveErrorStack, 'base64');
|
|
77
|
-
data.stack = cbor.decode(encodedStack);
|
|
78
|
-
}
|
|
53
|
+
const metadata = parseMetadata(grpcError.metadata) || {};
|
|
54
|
+
|
|
55
|
+
// Error data
|
|
56
|
+
const driveErrorData = metadata['drive-error-data-bin'];
|
|
57
|
+
if (driveErrorData) {
|
|
58
|
+
const encodedData = Buffer.from(driveErrorData, 'base64');
|
|
59
|
+
data = cbor.decode(encodedData);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Error code
|
|
63
|
+
const driveErrorCode = metadata.code;
|
|
64
|
+
if (driveErrorCode) {
|
|
65
|
+
code = Number(driveErrorCode);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Error stack
|
|
69
|
+
const driveErrorStack = metadata['stack-bin'];
|
|
70
|
+
if (driveErrorStack) {
|
|
71
|
+
const encodedStack = Buffer.from(driveErrorStack, 'base64');
|
|
72
|
+
data.stack = cbor.decode(encodedStack);
|
|
79
73
|
}
|
|
80
74
|
|
|
81
75
|
// Specialized classes
|
|
@@ -100,13 +94,6 @@ function createGrpcTransportError(grpcError, dapiAddress) {
|
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
if (code === GrpcErrorCodes.INTERNAL) {
|
|
103
|
-
if (grpcError.metadata) {
|
|
104
|
-
const metaStack = grpcError.metadata.get('stack-bin');
|
|
105
|
-
if (metaStack && metaStack.length > 0) {
|
|
106
|
-
data.stack = cbor.decode(metaStack[0]);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
97
|
return new InternalServerError(
|
|
111
98
|
code,
|
|
112
99
|
message,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
|
|
3
1
|
const JsonRpcError = require('./errors/JsonRpcError');
|
|
4
2
|
const WrongHttpCodeError = require('./errors/WrongHttpCodeError');
|
|
5
3
|
|
|
@@ -24,11 +22,6 @@ async function requestJsonRpc(host, port, method, params, options = {}) {
|
|
|
24
22
|
id: 1,
|
|
25
23
|
};
|
|
26
24
|
|
|
27
|
-
const postOptions = {};
|
|
28
|
-
if (options.timeout !== undefined) {
|
|
29
|
-
postOptions.timeout = options.timeout;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
25
|
const requestInfo = {
|
|
33
26
|
host,
|
|
34
27
|
port,
|
|
@@ -37,27 +30,32 @@ async function requestJsonRpc(host, port, method, params, options = {}) {
|
|
|
37
30
|
options,
|
|
38
31
|
};
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
if (error.response && error.response.status >= 500) {
|
|
50
|
-
throw new WrongHttpCodeError(requestInfo, error.response.status, error.response.statusText);
|
|
51
|
-
}
|
|
33
|
+
const requestOptions = {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
let requestTimeoutId;
|
|
42
|
+
if (options.timeout) {
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
requestTimeoutId = setTimeout(() => controller.abort(), options.timeout);
|
|
45
|
+
Object.assign(requestOptions, { signal: controller.signal });
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
// eslint-disable-next-line
|
|
49
|
+
const response = await fetch(url, requestOptions);
|
|
50
|
+
|
|
51
|
+
if (typeof requestTimeoutId !== 'undefined') {
|
|
52
|
+
clearTimeout(requestTimeoutId);
|
|
58
53
|
}
|
|
54
|
+
const data = await response.json();
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new WrongHttpCodeError(requestInfo, response.status, response.statusText);
|
|
58
|
+
}
|
|
61
59
|
|
|
62
60
|
if (data.error) {
|
|
63
61
|
throw new JsonRpcError(requestInfo, data.error);
|