@aztec-labs/stdlib 6.0.0-rc.1 → 6.0.0-rc.3
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/avm/avm.d.ts +210 -210
- package/dest/avm/message_pack.d.ts +1 -1
- package/dest/avm/message_pack.d.ts.map +1 -1
- package/dest/avm/message_pack.js +10 -0
- package/dest/block/block_parameter.d.ts +10 -1
- package/dest/block/block_parameter.d.ts.map +1 -1
- package/dest/block/block_parameter.js +8 -3
- package/dest/checkpoint/checkpoint_reexecution_tracker.d.ts +11 -1
- package/dest/checkpoint/checkpoint_reexecution_tracker.d.ts.map +1 -1
- package/dest/checkpoint/checkpoint_reexecution_tracker.js +12 -0
- package/dest/checkpoint/validate.d.ts +2 -1
- package/dest/checkpoint/validate.d.ts.map +1 -1
- package/dest/checkpoint/validate.js +9 -0
- package/dest/config/network-consensus-config.d.ts +17 -5
- package/dest/config/network-consensus-config.d.ts.map +1 -1
- package/dest/config/network-consensus-config.js +54 -8
- package/dest/contract/contract_address.d.ts +1 -1
- package/dest/contract/contract_address.js +1 -1
- package/dest/gas/fee_math.d.ts +2 -2
- package/dest/gas/fee_math.js +2 -2
- package/dest/gas/gas_fees.d.ts +27 -1
- package/dest/gas/gas_fees.d.ts.map +1 -1
- package/dest/gas/gas_fees.js +3 -0
- package/dest/interfaces/aztec-node-admin.d.ts +2 -1
- package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
- package/dest/interfaces/aztec-node.d.ts +27 -5
- package/dest/interfaces/aztec-node.d.ts.map +1 -1
- package/dest/interfaces/aztec-node.js +8 -1
- package/dest/interfaces/configs.d.ts +9 -1
- package/dest/interfaces/configs.d.ts.map +1 -1
- package/dest/interfaces/configs.js +2 -1
- package/dest/interfaces/prover-broker.d.ts +3 -1
- package/dest/interfaces/prover-broker.d.ts.map +1 -1
- package/dest/interfaces/proving-job.d.ts +70 -70
- package/dest/interfaces/server_circuit_prover.d.ts +4 -3
- package/dest/interfaces/server_circuit_prover.d.ts.map +1 -1
- package/dest/keys/derivation.js +2 -2
- package/dest/messaging/l1_to_l2_message.d.ts +20 -1
- package/dest/messaging/l1_to_l2_message.d.ts.map +1 -1
- package/dest/messaging/l1_to_l2_message.js +30 -8
- package/dest/p2p/block_proposal.d.ts +1 -1
- package/dest/p2p/block_proposal.d.ts.map +1 -1
- package/dest/p2p/block_proposal.js +5 -0
- package/dest/p2p/checkpoint_proposal.d.ts +1 -1
- package/dest/p2p/checkpoint_proposal.d.ts.map +1 -1
- package/dest/p2p/checkpoint_proposal.js +5 -0
- package/dest/p2p/gossipable.d.ts +1 -1
- package/dest/p2p/gossipable.d.ts.map +1 -1
- package/dest/p2p/gossipable.js +6 -0
- package/package.json +7 -7
- package/src/avm/message_pack.ts +10 -0
- package/src/block/block_parameter.ts +13 -1
- package/src/checkpoint/checkpoint_reexecution_tracker.ts +14 -0
- package/src/checkpoint/validate.ts +14 -0
- package/src/config/network-consensus-config.ts +70 -11
- package/src/contract/contract_address.ts +1 -1
- package/src/gas/README.md +2 -2
- package/src/gas/fee_math.ts +2 -2
- package/src/gas/gas_fees.ts +35 -0
- package/src/interfaces/aztec-node.ts +41 -4
- package/src/interfaces/configs.ts +8 -0
- package/src/interfaces/prover-broker.ts +2 -0
- package/src/interfaces/server_circuit_prover.ts +3 -2
- package/src/keys/derivation.ts +2 -2
- package/src/messaging/l1_to_l2_message.ts +39 -11
- package/src/p2p/block_proposal.ts +6 -0
- package/src/p2p/checkpoint_proposal.ts +6 -0
- package/src/p2p/gossipable.ts +6 -0
package/src/gas/gas_fees.ts
CHANGED
|
@@ -90,6 +90,14 @@ export class GasFees {
|
|
|
90
90
|
return new GasFees(Math.floor(Math.random() * 1e9), Math.floor(Math.random() * 1e9));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/** The lower of the two fees on each dimension independently. */
|
|
94
|
+
static min(a: GasFees, b: GasFees) {
|
|
95
|
+
return new GasFees(
|
|
96
|
+
a.feePerDaGas < b.feePerDaGas ? a.feePerDaGas : b.feePerDaGas,
|
|
97
|
+
a.feePerL2Gas < b.feePerL2Gas ? a.feePerL2Gas : b.feePerL2Gas,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
static empty() {
|
|
94
102
|
return new GasFees(0, 0);
|
|
95
103
|
}
|
|
@@ -138,3 +146,30 @@ export class GasFees {
|
|
|
138
146
|
export interface BlockMinFeesProvider {
|
|
139
147
|
getCurrentMinFees(): Promise<GasFees>;
|
|
140
148
|
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Provides the mana min fee the next block this node would build is going to charge.
|
|
152
|
+
*
|
|
153
|
+
* Distinct from {@link BlockMinFeesProvider}, which answers the L1-anchored price after the on-chain pending
|
|
154
|
+
* checkpoint and therefore cannot see the fee a locally proposed in-progress checkpoint already froze.
|
|
155
|
+
*/
|
|
156
|
+
export interface NextBlockMinFeesProvider {
|
|
157
|
+
/**
|
|
158
|
+
* The fee the next block will charge, or undefined when the node cannot resolve it right now. For callers that
|
|
159
|
+
* must not act on a stand-in price, such as evicting transactions the next block could not include.
|
|
160
|
+
*/
|
|
161
|
+
getNextBlockMinFees(): Promise<GasFees | undefined>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Provides the mana min fees a transaction is held to on its way into this node, over gossip or into the pool. */
|
|
165
|
+
export interface TxAdmissionMinFeesProvider extends NextBlockMinFeesProvider {
|
|
166
|
+
/**
|
|
167
|
+
* The fee a transaction must be able to pay to enter this node, over gossip or into the pending pool. The
|
|
168
|
+
* next-block fee when it resolves, otherwise the L1-forward fee, so admission never runs without a floor: a
|
|
169
|
+
* zero `maxFeesPerGas` has a zero fee limit, which a fee payer with no balance also covers.
|
|
170
|
+
*/
|
|
171
|
+
getAdmissionMinFees(): Promise<GasFees>;
|
|
172
|
+
|
|
173
|
+
/** The L1-anchored fee projected after the on-chain pending checkpoint, as {@link BlockMinFeesProvider} reports. */
|
|
174
|
+
getL1ForwardMinFees(): Promise<GasFees>;
|
|
175
|
+
}
|
|
@@ -27,7 +27,13 @@ import { z } from 'zod';
|
|
|
27
27
|
import type { AztecAddress } from '../aztec-address/index.js';
|
|
28
28
|
import { type BlockData, BlockDataSchema } from '../block/block_data.js';
|
|
29
29
|
import { BlockHash } from '../block/block_hash.js';
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
type ArchiveBlockParameter,
|
|
32
|
+
ArchiveBlockParameterSchema,
|
|
33
|
+
type BlockParameter,
|
|
34
|
+
BlockParameterSchema,
|
|
35
|
+
BlockTagWithoutLatestSchema,
|
|
36
|
+
} from '../block/block_parameter.js';
|
|
31
37
|
import { type DataInBlock, dataInBlockSchemaFor } from '../block/in_block.js';
|
|
32
38
|
import {
|
|
33
39
|
type CheckpointsQuery,
|
|
@@ -169,22 +175,48 @@ export interface AztecNode {
|
|
|
169
175
|
getPublicDataWitness(referenceBlock: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined>;
|
|
170
176
|
|
|
171
177
|
/**
|
|
172
|
-
* Returns a membership witness for a given block hash in the archive tree
|
|
178
|
+
* Returns a membership witness for a given block hash in the archive tree, against the reference block header's
|
|
179
|
+
* `lastArchive.root`: the archive as it stood before the reference block itself was appended. This is the root a
|
|
180
|
+
* circuit anchored on that header checks archive membership proofs against.
|
|
173
181
|
*
|
|
174
182
|
* Block hashes are the leaves of the archive tree. Each time a new block is added to the chain,
|
|
175
183
|
* its block hash is appended as a new leaf to the archive tree. This method finds the membership
|
|
176
184
|
* witness (leaf index and sibling path) for a given block hash, which can be used to prove that
|
|
177
185
|
* a specific block exists in the chain's history.
|
|
178
186
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
187
|
+
* The reference block must exist. Every `BlockParameter` form selects that reference block, including
|
|
188
|
+
* `{ archive }`, which identifies the block by its post-block archive root, so the witness is still against the
|
|
189
|
+
* archive before that block. To prove membership against a supplied archive root itself, use
|
|
190
|
+
* `getBlockHashMembershipWitnessAtArchive`.
|
|
191
|
+
*
|
|
192
|
+
* @param referenceBlock - The block whose header's `lastArchive` the witness is computed against.
|
|
181
193
|
* @param blockHash - The block hash to find in the archive tree.
|
|
194
|
+
* @returns The witness, or undefined if the block hash is not in that archive.
|
|
195
|
+
* @throws If the reference block is unknown, or the archive it commits to is not available on this node.
|
|
182
196
|
*/
|
|
183
197
|
getBlockHashMembershipWitness(
|
|
184
198
|
referenceBlock: BlockParameter,
|
|
185
199
|
blockHash: BlockHash,
|
|
186
200
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined>;
|
|
187
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Returns a membership witness for a given block hash in the archive tree, against exactly the supplied archive
|
|
204
|
+
* root. The root is the archive after some block's own hash was appended, so a block is a member of its own archive
|
|
205
|
+
* and no later block needs to exist.
|
|
206
|
+
*
|
|
207
|
+
* Choosing a root does not establish that it is canonical or final: a node that has not yet seen a reorg can serve
|
|
208
|
+
* a witness against a root that has been reorged away. The caller must pick a root its verifier accepts.
|
|
209
|
+
*
|
|
210
|
+
* @param reference - The archive root to prove membership against.
|
|
211
|
+
* @param blockHash - The block hash to find in the archive tree.
|
|
212
|
+
* @returns The witness, or undefined if the block hash is not in that archive.
|
|
213
|
+
* @throws If the node does not know the archive root, or cannot serve the archive state at it.
|
|
214
|
+
*/
|
|
215
|
+
getBlockHashMembershipWitnessAtArchive(
|
|
216
|
+
reference: ArchiveBlockParameter,
|
|
217
|
+
blockHash: BlockHash,
|
|
218
|
+
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined>;
|
|
219
|
+
|
|
188
220
|
/**
|
|
189
221
|
* Returns a membership witness for a given note hash at a given block.
|
|
190
222
|
* @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
@@ -632,6 +664,11 @@ export const AztecNodeApiSchema: ApiSchemaFor<AztecNode> = {
|
|
|
632
664
|
output: MembershipWitness.schemaFor(ARCHIVE_HEIGHT).optional(),
|
|
633
665
|
}),
|
|
634
666
|
|
|
667
|
+
getBlockHashMembershipWitnessAtArchive: z.function({
|
|
668
|
+
input: z.tuple([ArchiveBlockParameterSchema, BlockHash.schema]),
|
|
669
|
+
output: MembershipWitness.schemaFor(ARCHIVE_HEIGHT).optional(),
|
|
670
|
+
}),
|
|
671
|
+
|
|
635
672
|
getNoteHashMembershipWitness: z.function({
|
|
636
673
|
input: z.tuple([BlockParameterSchema, schemas.Fr]),
|
|
637
674
|
output: MembershipWitness.schemaFor(NOTE_HASH_TREE_HEIGHT).optional(),
|
|
@@ -137,6 +137,13 @@ export interface SequencerConfig {
|
|
|
137
137
|
* check. Ignored when p2p is disabled by config.
|
|
138
138
|
*/
|
|
139
139
|
minPeersToPropose?: number;
|
|
140
|
+
/**
|
|
141
|
+
* Allow starting with fewer block opportunities per checkpoint than `MIN_BLOCKS_FOR_INBOX_CATCHUP`, which on a
|
|
142
|
+
* real network leaves a proposer that can never clear a mandatory streaming-Inbox backlog and loses all of its
|
|
143
|
+
* slots. Only sandbox, e2e and benchmark fixtures set this: they run one or two blocks per slot against an Inbox
|
|
144
|
+
* nobody is filling (for testing only).
|
|
145
|
+
*/
|
|
146
|
+
allowUnsafeInboxCatchupCapacity?: boolean;
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
export const SequencerConfigSchema = zodFor<SequencerConfig>()(
|
|
@@ -191,6 +198,7 @@ export const SequencerConfigSchema = zodFor<SequencerConfig>()(
|
|
|
191
198
|
skipBroadcastCheckpointProposal: z.boolean().optional(),
|
|
192
199
|
pauseProposingForSlots: z.array(SlotNumberSchema).optional(),
|
|
193
200
|
minPeersToPropose: z.number().nonnegative().optional(),
|
|
201
|
+
allowUnsafeInboxCatchupCapacity: z.boolean().optional(),
|
|
194
202
|
}),
|
|
195
203
|
);
|
|
196
204
|
|
|
@@ -35,6 +35,8 @@ export interface ProvingJobProducer {
|
|
|
35
35
|
|
|
36
36
|
export type ProvingJobFilter = {
|
|
37
37
|
allowList: ProvingRequestType[];
|
|
38
|
+
/** Whether to acquire new work. Set false to report progress or results while draining. Defaults to true. */
|
|
39
|
+
allowNewJobs?: boolean;
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
export type GetProvingJobResponse = {
|
|
@@ -182,9 +182,10 @@ export type IVCProofVerificationResult = {
|
|
|
182
182
|
*/
|
|
183
183
|
export interface ClientProtocolCircuitVerifier {
|
|
184
184
|
/**
|
|
185
|
-
* Verifies the private protocol circuit's proof.
|
|
185
|
+
* Verifies the private protocol circuit's proof. Rejects, instead of reporting the proof invalid, when the proof
|
|
186
|
+
* could not be checked (for example, when the proving backend is unavailable).
|
|
186
187
|
* @param tx - The tx to verify the proof of
|
|
187
|
-
* @returns
|
|
188
|
+
* @returns Whether the proof is valid, with verification timings
|
|
188
189
|
*/
|
|
189
190
|
verifyProof(tx: Tx): Promise<IVCProofVerificationResult>;
|
|
190
191
|
|
package/src/keys/derivation.ts
CHANGED
|
@@ -51,12 +51,12 @@ export function deriveMasterFallbackSecretKey(secretKey: Fr): GrumpkinScalar {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function computePreaddress(publicKeysHash: Fr, partialAddress: Fr) {
|
|
54
|
-
return poseidon2HashWithSeparator([publicKeysHash, partialAddress], DomainSeparator.
|
|
54
|
+
return poseidon2HashWithSeparator([publicKeysHash, partialAddress], DomainSeparator.CONTRACT_ADDRESS_V3);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export async function computeAddress(publicKeys: PublicKeys, partialAddress: Fr): Promise<AztecAddress> {
|
|
58
58
|
// Given public keys and a partial address, we can compute our address in the following steps.
|
|
59
|
-
// 1. preaddress = poseidon2([publicKeysHash, partialAddress], DomainSeparator.
|
|
59
|
+
// 1. preaddress = poseidon2([publicKeysHash, partialAddress], DomainSeparator.CONTRACT_ADDRESS_V3);
|
|
60
60
|
// 2. addressPoint = (preaddress * G) + ivpk_m
|
|
61
61
|
// 3. address = addressPoint.x
|
|
62
62
|
const preaddress = await computePreaddress(await publicKeys.hash(), partialAddress);
|
|
@@ -95,16 +95,24 @@ export function computeFeeJuiceMessageNullifier(messageHash: Fr, secret: Fr): Pr
|
|
|
95
95
|
return poseidon2HashWithSeparator([messageHash, secret], DomainSeparator.MESSAGE_NULLIFIER);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/** The outcome of {@link lookUpL1ToL2MessageWitness}. */
|
|
99
|
+
export type L1ToL2MessageWitnessLookup =
|
|
100
|
+
/** The message is in the tree and, if a nullifier was given, has not been consumed. */
|
|
101
|
+
| { type: 'found'; witness: [bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] }
|
|
102
|
+
/** The message is not in the tree. */
|
|
103
|
+
| { type: 'missing' }
|
|
104
|
+
/** A nullifier was given and the message has been consumed. */
|
|
105
|
+
| { type: 'nullified' };
|
|
106
|
+
|
|
98
107
|
/**
|
|
99
|
-
*
|
|
100
|
-
* additionally required to be un-nullified.
|
|
108
|
+
* Looks up the membership witness of an L1 to L2 message in the L1 to L2 message tree at `referenceBlock`.
|
|
101
109
|
*/
|
|
102
|
-
export async function
|
|
110
|
+
export async function lookUpL1ToL2MessageWitness(
|
|
103
111
|
node: AztecNode,
|
|
104
112
|
messageHash: Fr,
|
|
105
|
-
unsiloedNullifier
|
|
106
|
-
referenceBlock: BlockParameter
|
|
107
|
-
): Promise<
|
|
113
|
+
unsiloedNullifier: UnsiloedMessageNullifier | undefined,
|
|
114
|
+
referenceBlock: BlockParameter,
|
|
115
|
+
): Promise<L1ToL2MessageWitnessLookup> {
|
|
108
116
|
// Both requests are dispatched before awaiting so they run concurrently.
|
|
109
117
|
const l1ToL2ResponsePromise = node.getL1ToL2MessageMembershipWitness(referenceBlock, messageHash);
|
|
110
118
|
const nullifierResponsePromise = unsiloedNullifier
|
|
@@ -113,17 +121,37 @@ export async function getL1ToL2MessageWitness(
|
|
|
113
121
|
)
|
|
114
122
|
: undefined;
|
|
115
123
|
|
|
116
|
-
const l1ToL2Response = await l1ToL2ResponsePromise;
|
|
124
|
+
const [l1ToL2Response, nullifierResponse] = await Promise.all([l1ToL2ResponsePromise, nullifierResponsePromise]);
|
|
117
125
|
if (!l1ToL2Response) {
|
|
118
|
-
|
|
126
|
+
return { type: 'missing' };
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
const nullifierResponse = await nullifierResponsePromise;
|
|
122
129
|
if (nullifierResponse?.[0] !== undefined) {
|
|
123
|
-
|
|
130
|
+
return { type: 'nullified' };
|
|
124
131
|
}
|
|
125
132
|
|
|
126
|
-
return l1ToL2Response;
|
|
133
|
+
return { type: 'found', witness: l1ToL2Response };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Fetches the membership witness of an L1 to L2 message. When `unsiloedNullifier` is provided, the message is
|
|
138
|
+
* additionally required to be un-nullified.
|
|
139
|
+
*/
|
|
140
|
+
export async function getL1ToL2MessageWitness(
|
|
141
|
+
node: AztecNode,
|
|
142
|
+
messageHash: Fr,
|
|
143
|
+
unsiloedNullifier?: UnsiloedMessageNullifier,
|
|
144
|
+
referenceBlock: BlockParameter = 'latest',
|
|
145
|
+
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>]> {
|
|
146
|
+
const lookup = await lookUpL1ToL2MessageWitness(node, messageHash, unsiloedNullifier, referenceBlock);
|
|
147
|
+
switch (lookup.type) {
|
|
148
|
+
case 'missing':
|
|
149
|
+
throw new Error(`No L1 to L2 message found for message hash ${messageHash.toString()}`);
|
|
150
|
+
case 'nullified':
|
|
151
|
+
throw new Error(`No non-nullified L1 to L2 message found for message hash ${messageHash.toString()}`);
|
|
152
|
+
case 'found':
|
|
153
|
+
return lookup.witness;
|
|
154
|
+
}
|
|
127
155
|
}
|
|
128
156
|
|
|
129
157
|
// This functionality is not on the node because we do not want to pass the node the secret, and give the node the ability to derive a valid nullifer for an L1 to L2 message.
|
|
@@ -281,6 +281,12 @@ export class BlockProposal extends Gossipable implements Signable {
|
|
|
281
281
|
static fromBuffer(buf: Buffer | BufferReader): BlockProposal {
|
|
282
282
|
const reader = BufferReader.asReader(buf);
|
|
283
283
|
|
|
284
|
+
// Decoding is lenient on purpose, for forward compatibility: trailing bytes are ignored and any non-zero
|
|
285
|
+
// presence flag reads as present, so a newer version can append fields that older nodes skip over. Rejecting
|
|
286
|
+
// them would make every wire extension a coordinated upgrade. Lenient decoding does not let one proposal take
|
|
287
|
+
// several identities, since the P2P message identifier and the signature both cover the signed payload rather
|
|
288
|
+
// than the raw bytes.
|
|
289
|
+
|
|
284
290
|
const blockHeader = reader.readObject(BlockHeader);
|
|
285
291
|
const indexWithinCheckpoint = IndexWithinCheckpoint(reader.readNumber());
|
|
286
292
|
const archiveRoot = reader.readObject(Fr);
|
|
@@ -308,6 +308,12 @@ export class CheckpointProposal extends Gossipable implements Signable {
|
|
|
308
308
|
static fromBuffer(buf: Buffer | BufferReader): CheckpointProposal {
|
|
309
309
|
const reader = BufferReader.asReader(buf);
|
|
310
310
|
|
|
311
|
+
// Decoding is lenient on purpose, for forward compatibility: trailing bytes are ignored and any non-zero
|
|
312
|
+
// presence flag reads as present, so a newer version can append fields that older nodes skip over. Rejecting
|
|
313
|
+
// them would make every wire extension a coordinated upgrade. Lenient decoding does not let one proposal take
|
|
314
|
+
// several identities, since the P2P message identifier and the signature both cover the signed payload rather
|
|
315
|
+
// than the raw bytes.
|
|
316
|
+
|
|
311
317
|
const checkpointHeader = reader.readObject(CheckpointHeader);
|
|
312
318
|
const archive = reader.readObject(Fr);
|
|
313
319
|
const feeAssetPriceModifier = reader.readInt256();
|
package/src/p2p/gossipable.ts
CHANGED
|
@@ -26,6 +26,12 @@ export class P2PMessage {
|
|
|
26
26
|
traceContext = reader.readString();
|
|
27
27
|
}
|
|
28
28
|
const payload = reader.readBuffer();
|
|
29
|
+
// Reject trailing bytes: padding leaves the same payload but a new message id, so a copy evades
|
|
30
|
+
// both the seen-message dedup and the duplicate-delivery peer scoring, letting a peer replay one
|
|
31
|
+
// valid message (e.g. a proposal it cannot forge) as fresh deliveries.
|
|
32
|
+
if (!reader.isEmpty()) {
|
|
33
|
+
throw new Error('P2PMessage has trailing bytes after its payload');
|
|
34
|
+
}
|
|
29
35
|
return new P2PMessage(payload, timestamp, traceContext);
|
|
30
36
|
}
|
|
31
37
|
|