@aztec/p2p 0.51.0 → 0.52.0
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/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +1 -1
- package/dest/client/p2p_client.d.ts +6 -0
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +6 -1
- package/dest/mocks/index.d.ts +28 -0
- package/dest/mocks/index.d.ts.map +1 -0
- package/dest/mocks/index.js +78 -0
- package/dest/service/dummy_service.d.ts +12 -0
- package/dest/service/dummy_service.d.ts.map +1 -1
- package/dest/service/dummy_service.js +14 -1
- package/dest/service/libp2p_service.d.ts +24 -1
- package/dest/service/libp2p_service.d.ts.map +1 -1
- package/dest/service/libp2p_service.js +52 -4
- package/dest/service/reqresp/handlers.d.ts +12 -2
- package/dest/service/reqresp/handlers.d.ts.map +1 -1
- package/dest/service/reqresp/handlers.js +13 -3
- package/dest/service/reqresp/interface.d.ts +56 -8
- package/dest/service/reqresp/interface.d.ts.map +1 -1
- package/dest/service/reqresp/interface.js +57 -8
- package/dest/service/reqresp/reqresp.d.ts +16 -4
- package/dest/service/reqresp/reqresp.d.ts.map +1 -1
- package/dest/service/reqresp/reqresp.js +22 -14
- package/dest/service/service.d.ts +9 -0
- package/dest/service/service.d.ts.map +1 -1
- package/dest/service/service.js +1 -1
- package/package.json +6 -6
- package/src/client/index.ts +2 -0
- package/src/client/p2p_client.ts +12 -0
- package/src/mocks/index.ts +101 -0
- package/src/service/dummy_service.ts +18 -0
- package/src/service/libp2p_service.ts +75 -1
- package/src/service/reqresp/handlers.ts +14 -4
- package/src/service/reqresp/interface.ts +95 -8
- package/src/service/reqresp/reqresp.ts +36 -19
- package/src/service/service.ts +14 -0
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
TopicType,
|
|
7
7
|
TopicTypeMap,
|
|
8
8
|
Tx,
|
|
9
|
+
TxHash,
|
|
9
10
|
} from '@aztec/circuit-types';
|
|
10
11
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
11
12
|
import { SerialQueue } from '@aztec/foundation/queue';
|
|
@@ -30,6 +31,18 @@ import { type TxPool } from '../tx_pool/index.js';
|
|
|
30
31
|
import { convertToMultiaddr } from '../util.js';
|
|
31
32
|
import { AztecDatastore } from './data_store.js';
|
|
32
33
|
import { PeerManager } from './peer_manager.js';
|
|
34
|
+
import { pingHandler, statusHandler } from './reqresp/handlers.js';
|
|
35
|
+
import {
|
|
36
|
+
DEFAULT_SUB_PROTOCOL_HANDLERS,
|
|
37
|
+
PING_PROTOCOL,
|
|
38
|
+
type ReqRespSubProtocol,
|
|
39
|
+
type ReqRespSubProtocolHandlers,
|
|
40
|
+
STATUS_PROTOCOL,
|
|
41
|
+
type SubProtocolMap,
|
|
42
|
+
TX_REQ_PROTOCOL,
|
|
43
|
+
subProtocolMap,
|
|
44
|
+
} from './reqresp/interface.js';
|
|
45
|
+
import { ReqResp } from './reqresp/reqresp.js';
|
|
33
46
|
import type { P2PService, PeerDiscoveryService } from './service.js';
|
|
34
47
|
|
|
35
48
|
export interface PubSubLibp2p extends Libp2p {
|
|
@@ -61,6 +74,14 @@ export class LibP2PService implements P2PService {
|
|
|
61
74
|
private peerManager: PeerManager;
|
|
62
75
|
private discoveryRunningPromise?: RunningPromise;
|
|
63
76
|
|
|
77
|
+
// Request and response sub service
|
|
78
|
+
private reqresp: ReqResp;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Callback for when a block is received from a peer.
|
|
82
|
+
* @param block - The block received from the peer.
|
|
83
|
+
* @returns The attestation for the block, if any.
|
|
84
|
+
*/
|
|
64
85
|
private blockReceivedCallback: (block: BlockProposal) => Promise<BlockAttestation | undefined>;
|
|
65
86
|
|
|
66
87
|
constructor(
|
|
@@ -69,9 +90,11 @@ export class LibP2PService implements P2PService {
|
|
|
69
90
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
70
91
|
private txPool: TxPool,
|
|
71
92
|
private attestationPool: AttestationPool,
|
|
93
|
+
private requestResponseHandlers: ReqRespSubProtocolHandlers = DEFAULT_SUB_PROTOCOL_HANDLERS,
|
|
72
94
|
private logger = createDebugLogger('aztec:libp2p_service'),
|
|
73
95
|
) {
|
|
74
96
|
this.peerManager = new PeerManager(node, peerDiscoveryService, config, logger);
|
|
97
|
+
this.reqresp = new ReqResp(node);
|
|
75
98
|
|
|
76
99
|
this.blockReceivedCallback = (block: BlockProposal): Promise<BlockAttestation | undefined> => {
|
|
77
100
|
this.logger.verbose(
|
|
@@ -124,6 +147,7 @@ export class LibP2PService implements P2PService {
|
|
|
124
147
|
this.peerManager.discover();
|
|
125
148
|
}, this.config.peerCheckIntervalMS);
|
|
126
149
|
this.discoveryRunningPromise.start();
|
|
150
|
+
await this.reqresp.start(this.requestResponseHandlers);
|
|
127
151
|
}
|
|
128
152
|
|
|
129
153
|
/**
|
|
@@ -140,6 +164,9 @@ export class LibP2PService implements P2PService {
|
|
|
140
164
|
this.logger.debug('Stopping LibP2P...');
|
|
141
165
|
await this.stopLibP2P();
|
|
142
166
|
this.logger.info('LibP2P service stopped');
|
|
167
|
+
this.logger.debug('Stopping request response service...');
|
|
168
|
+
await this.reqresp.stop();
|
|
169
|
+
this.logger.debug('Request response service stopped...');
|
|
143
170
|
}
|
|
144
171
|
|
|
145
172
|
/**
|
|
@@ -206,9 +233,56 @@ export class LibP2PService implements P2PService {
|
|
|
206
233
|
},
|
|
207
234
|
});
|
|
208
235
|
|
|
209
|
-
|
|
236
|
+
// Create request response protocol handlers
|
|
237
|
+
/**
|
|
238
|
+
* Handler for tx requests
|
|
239
|
+
* @param msg - the tx request message
|
|
240
|
+
* @returns the tx response message
|
|
241
|
+
*/
|
|
242
|
+
const txHandler = (msg: Buffer): Promise<Uint8Array> => {
|
|
243
|
+
const txHash = TxHash.fromBuffer(msg);
|
|
244
|
+
const foundTx = txPool.getTxByHash(txHash);
|
|
245
|
+
const asUint8Array = Uint8Array.from(foundTx ? foundTx.toBuffer() : Buffer.alloc(0));
|
|
246
|
+
return Promise.resolve(asUint8Array);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const requestResponseHandlers = {
|
|
250
|
+
[PING_PROTOCOL]: pingHandler,
|
|
251
|
+
[STATUS_PROTOCOL]: statusHandler,
|
|
252
|
+
[TX_REQ_PROTOCOL]: txHandler,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return new LibP2PService(config, node, peerDiscoveryService, txPool, attestationPool, requestResponseHandlers);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Send Request via the ReqResp service
|
|
260
|
+
* The subprotocol defined will determine the request and response types
|
|
261
|
+
*
|
|
262
|
+
* See the subProtocolMap for the mapping of subprotocols to request/response types in `interface.ts`
|
|
263
|
+
*
|
|
264
|
+
* @param protocol The request response protocol to use
|
|
265
|
+
* @param request The request type to send
|
|
266
|
+
* @returns
|
|
267
|
+
*/
|
|
268
|
+
async sendRequest<SubProtocol extends ReqRespSubProtocol>(
|
|
269
|
+
protocol: SubProtocol,
|
|
270
|
+
request: InstanceType<SubProtocolMap[SubProtocol]['request']>,
|
|
271
|
+
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined> {
|
|
272
|
+
const pair = subProtocolMap[protocol];
|
|
273
|
+
|
|
274
|
+
const res = await this.reqresp.sendRequest(protocol, request.toBuffer());
|
|
275
|
+
if (!res) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return pair.response.fromBuffer(res!);
|
|
210
280
|
}
|
|
211
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Get the ENR of the node
|
|
284
|
+
* @returns The ENR of the node
|
|
285
|
+
*/
|
|
212
286
|
public getEnr(): ENR | undefined {
|
|
213
287
|
return this.peerDiscoveryService.getEnr();
|
|
214
288
|
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Handles the ping request.
|
|
3
|
+
* @param _msg - The ping request message.
|
|
4
|
+
* @returns A resolved promise with the pong response.
|
|
5
|
+
*/
|
|
6
|
+
export function pingHandler(_msg: any): Promise<Uint8Array> {
|
|
7
|
+
return Promise.resolve(Uint8Array.from(Buffer.from('pong')));
|
|
3
8
|
}
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Handles the status request.
|
|
12
|
+
* @param _msg - The status request message.
|
|
13
|
+
* @returns A resolved promise with the ok response.
|
|
14
|
+
*/
|
|
15
|
+
export function statusHandler(_msg: any): Promise<Uint8Array> {
|
|
16
|
+
return Promise.resolve(Uint8Array.from(Buffer.from('ok')));
|
|
7
17
|
}
|
|
@@ -1,13 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
Status = 'status',
|
|
3
|
-
Ping = 'ping',
|
|
4
|
-
/** Ask peers for specific transactions */
|
|
5
|
-
TxsByHash = 'txs_by_hash',
|
|
6
|
-
}
|
|
1
|
+
import { Tx, TxHash } from '@aztec/circuit-types';
|
|
7
2
|
|
|
3
|
+
/*
|
|
4
|
+
* Request Response Sub Protocols
|
|
5
|
+
*/
|
|
8
6
|
export const PING_PROTOCOL = '/aztec/ping/0.1.0';
|
|
9
7
|
export const STATUS_PROTOCOL = '/aztec/status/0.1.0';
|
|
8
|
+
export const TX_REQ_PROTOCOL = '/aztec/tx_req/0.1.0';
|
|
9
|
+
|
|
10
|
+
// Sum type for sub protocols
|
|
11
|
+
export type ReqRespSubProtocol = typeof PING_PROTOCOL | typeof STATUS_PROTOCOL | typeof TX_REQ_PROTOCOL;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A handler for a sub protocol
|
|
15
|
+
* The message will arrive as a buffer, and the handler must return a buffer
|
|
16
|
+
*/
|
|
17
|
+
export type ReqRespSubProtocolHandler = (msg: Buffer) => Promise<Uint8Array>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A type mapping from supprotocol to it's handling funciton
|
|
21
|
+
*/
|
|
22
|
+
export type ReqRespSubProtocolHandlers = Record<ReqRespSubProtocol, ReqRespSubProtocolHandler>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sub protocol map determines the request and response types for each
|
|
26
|
+
* Req Resp protocol
|
|
27
|
+
*/
|
|
28
|
+
export type SubProtocolMap = {
|
|
29
|
+
[S in ReqRespSubProtocol]: RequestResponsePair<any, any>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default handler for unimplemented sub protocols, this SHOULD be overwritten
|
|
34
|
+
* by the service, but is provided as a fallback
|
|
35
|
+
*/
|
|
36
|
+
const defaultHandler = (_msg: any): Promise<Uint8Array> => {
|
|
37
|
+
return Promise.resolve(Uint8Array.from(Buffer.from('unimplemented')));
|
|
38
|
+
};
|
|
10
39
|
|
|
11
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Default sub protocol handlers - this SHOULD be overwritten by the service,
|
|
42
|
+
*/
|
|
43
|
+
export const DEFAULT_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = {
|
|
44
|
+
[PING_PROTOCOL]: defaultHandler,
|
|
45
|
+
[STATUS_PROTOCOL]: defaultHandler,
|
|
46
|
+
[TX_REQ_PROTOCOL]: defaultHandler,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The Request Response Pair interface defines the methods that each
|
|
51
|
+
* request response pair must implement
|
|
52
|
+
*/
|
|
53
|
+
interface RequestResponsePair<Req, Res> {
|
|
54
|
+
request: new (...args: any[]) => Req;
|
|
55
|
+
/**
|
|
56
|
+
* The response must implement the static fromBuffer method (generic serialisation)
|
|
57
|
+
*/
|
|
58
|
+
response: {
|
|
59
|
+
new (...args: any[]): Res;
|
|
60
|
+
fromBuffer(buffer: Buffer): Res;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* RequestableBuffer is a wrapper around a buffer that allows it to be
|
|
66
|
+
* used in generic request response protocols
|
|
67
|
+
*
|
|
68
|
+
* An instance of the RequestResponsePair defined above
|
|
69
|
+
*/
|
|
70
|
+
export class RequestableBuffer {
|
|
71
|
+
constructor(public buffer: Buffer) {}
|
|
72
|
+
|
|
73
|
+
toBuffer() {
|
|
74
|
+
return this.buffer;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static fromBuffer(buffer: Buffer) {
|
|
78
|
+
return new RequestableBuffer(buffer);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
12
81
|
|
|
13
|
-
|
|
82
|
+
/**
|
|
83
|
+
* A mapping from each protocol to their request and response types
|
|
84
|
+
* This defines the request and response types for each sub protocol, used primarily
|
|
85
|
+
* as a type rather than an object
|
|
86
|
+
*/
|
|
87
|
+
export const subProtocolMap: SubProtocolMap = {
|
|
88
|
+
[PING_PROTOCOL]: {
|
|
89
|
+
request: RequestableBuffer,
|
|
90
|
+
response: RequestableBuffer,
|
|
91
|
+
},
|
|
92
|
+
[STATUS_PROTOCOL]: {
|
|
93
|
+
request: RequestableBuffer,
|
|
94
|
+
response: RequestableBuffer,
|
|
95
|
+
},
|
|
96
|
+
[TX_REQ_PROTOCOL]: {
|
|
97
|
+
request: TxHash,
|
|
98
|
+
response: Tx,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
@@ -6,22 +6,30 @@ import { pipe } from 'it-pipe';
|
|
|
6
6
|
import { type Libp2p } from 'libp2p';
|
|
7
7
|
import { type Uint8ArrayList } from 'uint8arraylist';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_SUB_PROTOCOL_HANDLERS,
|
|
11
|
+
type ReqRespSubProtocol,
|
|
12
|
+
type ReqRespSubProtocolHandlers,
|
|
13
|
+
} from './interface.js';
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
16
|
+
* The Request Response Service
|
|
17
|
+
*
|
|
18
|
+
* It allows nodes to request specific information from their peers, its use case covers recovering
|
|
19
|
+
* information that was missed during a syncronisation or a gossip event.
|
|
20
|
+
*
|
|
21
|
+
* This service implements the request response sub protocol, it is heavily inspired from
|
|
22
|
+
* ethereum implementations of the same name.
|
|
23
|
+
*
|
|
24
|
+
* see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md#the-reqresp-domain
|
|
14
25
|
*/
|
|
15
|
-
const SUB_PROTOCOL_HANDLERS: Record<SubProtocol, SubProtocolHandler> = {
|
|
16
|
-
[PING_PROTOCOL]: pingHandler,
|
|
17
|
-
[STATUS_PROTOCOL]: statusHandler,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
26
|
export class ReqResp {
|
|
21
27
|
protected readonly logger: Logger;
|
|
22
28
|
|
|
23
29
|
private abortController: AbortController = new AbortController();
|
|
24
30
|
|
|
31
|
+
private subProtocolHandlers: ReqRespSubProtocolHandlers = DEFAULT_SUB_PROTOCOL_HANDLERS;
|
|
32
|
+
|
|
25
33
|
constructor(protected readonly libp2p: Libp2p) {
|
|
26
34
|
this.logger = createDebugLogger('aztec:p2p:reqresp');
|
|
27
35
|
}
|
|
@@ -29,10 +37,11 @@ export class ReqResp {
|
|
|
29
37
|
/**
|
|
30
38
|
* Start the reqresp service
|
|
31
39
|
*/
|
|
32
|
-
async start() {
|
|
40
|
+
async start(subProtocolHandlers: ReqRespSubProtocolHandlers) {
|
|
41
|
+
this.subProtocolHandlers = subProtocolHandlers;
|
|
33
42
|
// Register all protocol handlers
|
|
34
|
-
for (const subProtocol of Object.keys(
|
|
35
|
-
await this.libp2p.handle(subProtocol, this.streamHandler.bind(this, subProtocol as
|
|
43
|
+
for (const subProtocol of Object.keys(this.subProtocolHandlers)) {
|
|
44
|
+
await this.libp2p.handle(subProtocol, this.streamHandler.bind(this, subProtocol as ReqRespSubProtocol));
|
|
36
45
|
}
|
|
37
46
|
}
|
|
38
47
|
|
|
@@ -41,7 +50,7 @@ export class ReqResp {
|
|
|
41
50
|
*/
|
|
42
51
|
async stop() {
|
|
43
52
|
// Unregister all handlers
|
|
44
|
-
for (const protocol of Object.keys(
|
|
53
|
+
for (const protocol of Object.keys(this.subProtocolHandlers)) {
|
|
45
54
|
await this.libp2p.unhandle(protocol);
|
|
46
55
|
}
|
|
47
56
|
await this.libp2p.stop();
|
|
@@ -55,7 +64,7 @@ export class ReqResp {
|
|
|
55
64
|
* @param payload - The payload to send
|
|
56
65
|
* @returns - The response from the peer, otherwise undefined
|
|
57
66
|
*/
|
|
58
|
-
async sendRequest(subProtocol:
|
|
67
|
+
async sendRequest(subProtocol: ReqRespSubProtocol, payload: Buffer): Promise<Buffer | undefined> {
|
|
59
68
|
// Get active peers
|
|
60
69
|
const peers = this.libp2p.getPeers();
|
|
61
70
|
|
|
@@ -64,7 +73,8 @@ export class ReqResp {
|
|
|
64
73
|
const response = await this.sendRequestToPeer(peer, subProtocol, payload);
|
|
65
74
|
|
|
66
75
|
// If we get a response, return it, otherwise we iterate onto the next peer
|
|
67
|
-
if
|
|
76
|
+
// We do not consider it a success if we have an empty buffer
|
|
77
|
+
if (response && response.length > 0) {
|
|
68
78
|
return response;
|
|
69
79
|
}
|
|
70
80
|
}
|
|
@@ -79,7 +89,11 @@ export class ReqResp {
|
|
|
79
89
|
* @param payload - The payload to send
|
|
80
90
|
* @returns If the request is successful, the response is returned, otherwise undefined
|
|
81
91
|
*/
|
|
82
|
-
async sendRequestToPeer(
|
|
92
|
+
async sendRequestToPeer(
|
|
93
|
+
peerId: PeerId,
|
|
94
|
+
subProtocol: ReqRespSubProtocol,
|
|
95
|
+
payload: Buffer,
|
|
96
|
+
): Promise<Buffer | undefined> {
|
|
83
97
|
try {
|
|
84
98
|
const stream = await this.libp2p.dialProtocol(peerId, subProtocol);
|
|
85
99
|
|
|
@@ -109,14 +123,17 @@ export class ReqResp {
|
|
|
109
123
|
*
|
|
110
124
|
* @param param0 - The incoming stream data
|
|
111
125
|
*/
|
|
112
|
-
private async streamHandler(protocol:
|
|
126
|
+
private async streamHandler(protocol: ReqRespSubProtocol, { stream }: IncomingStreamData) {
|
|
127
|
+
// Store a reference to from this for the async generator
|
|
128
|
+
const handler = this.subProtocolHandlers[protocol];
|
|
129
|
+
|
|
113
130
|
try {
|
|
114
131
|
await pipe(
|
|
115
132
|
stream,
|
|
116
|
-
async function* (source) {
|
|
133
|
+
async function* (source: any) {
|
|
117
134
|
for await (const chunkList of source) {
|
|
118
|
-
const msg = Buffer.from(chunkList.subarray())
|
|
119
|
-
yield
|
|
135
|
+
const msg = Buffer.from(chunkList.subarray());
|
|
136
|
+
yield handler(msg);
|
|
120
137
|
}
|
|
121
138
|
},
|
|
122
139
|
stream,
|
package/src/service/service.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { ENR } from '@chainsafe/enr';
|
|
|
4
4
|
import type { PeerId } from '@libp2p/interface';
|
|
5
5
|
import type EventEmitter from 'events';
|
|
6
6
|
|
|
7
|
+
import { type ReqRespSubProtocol, type SubProtocolMap } from './reqresp/interface.js';
|
|
8
|
+
|
|
7
9
|
export enum PeerDiscoveryState {
|
|
8
10
|
RUNNING = 'running',
|
|
9
11
|
STOPPED = 'stopped',
|
|
@@ -31,6 +33,18 @@ export interface P2PService {
|
|
|
31
33
|
*/
|
|
32
34
|
propagate<T extends Gossipable>(message: T): void;
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Request information from peers via the request response protocol.
|
|
38
|
+
*
|
|
39
|
+
* @param protocol - The request response protocol to use
|
|
40
|
+
* @param request - The request type, corresponding to the protocol
|
|
41
|
+
* @returns The response type, corresponding to the protocol
|
|
42
|
+
*/
|
|
43
|
+
sendRequest<Protocol extends ReqRespSubProtocol>(
|
|
44
|
+
protocol: Protocol,
|
|
45
|
+
request: InstanceType<SubProtocolMap[Protocol]['request']>,
|
|
46
|
+
): Promise<InstanceType<SubProtocolMap[Protocol]['response']> | undefined>;
|
|
47
|
+
|
|
34
48
|
// Leaky abstraction: fix https://github.com/AztecProtocol/aztec-packages/issues/7963
|
|
35
49
|
registerBlockReceivedCallback(callback: (block: BlockProposal) => Promise<BlockAttestation>): void;
|
|
36
50
|
|