@@ -3,6 +3,7 @@ import { Secp256k1Signer } from '@aztec/foundation/crypto';
33import { Fr } from '@aztec/foundation/fields' ;
44import { PeerErrorSeverity } from '@aztec/stdlib/p2p' ;
55import { makeBlockProposal , makeL2BlockHeader } from '@aztec/stdlib/testing' ;
6+ import { TxHash } from '@aztec/stdlib/tx' ;
67
78import { mock } from 'jest-mock-extended' ;
89
@@ -14,7 +15,7 @@ describe('BlockProposalValidator', () => {
1415
1516 beforeEach ( ( ) => {
1617 epochCache = mock < EpochCache > ( ) ;
17- validator = new BlockProposalValidator ( epochCache ) ;
18+ validator = new BlockProposalValidator ( epochCache , { txsPermitted : true } ) ;
1819 } ) ;
1920
2021 it ( 'returns high tolerance error if slot number is not current or next slot' , async ( ) => {
@@ -146,4 +147,75 @@ describe('BlockProposalValidator', () => {
146147 const result = await validator . validate ( mockProposal ) ;
147148 expect ( result ) . toBeUndefined ( ) ;
148149 } ) ;
150+
151+ describe ( 'transaction permission validation' , ( ) => {
152+ it ( 'returns mid tolerance error if txs not permitted and proposal contains txHashes' , async ( ) => {
153+ const currentProposer = Secp256k1Signer . random ( ) ;
154+ const validatorWithTxsDisabled = new BlockProposalValidator ( epochCache , { txsPermitted : false } ) ;
155+
156+ // Create a block proposal with transaction hashes
157+ const mockProposal = makeBlockProposal ( {
158+ header : makeL2BlockHeader ( 1 , 100 , 100 ) ,
159+ signer : currentProposer ,
160+ txHashes : [ TxHash . random ( ) , TxHash . random ( ) ] , // Include some tx hashes
161+ } ) ;
162+
163+ // Mock epoch cache to return valid proposer (so only tx permission check fails)
164+ ( epochCache . getProposerAttesterAddressInCurrentOrNextSlot as jest . Mock ) . mockResolvedValue ( {
165+ currentSlot : 100n ,
166+ nextSlot : 101n ,
167+ currentProposer : currentProposer . address ,
168+ nextProposer : Fr . random ( ) ,
169+ } ) ;
170+
171+ const result = await validatorWithTxsDisabled . validate ( mockProposal ) ;
172+ expect ( result ) . toBe ( PeerErrorSeverity . MidToleranceError ) ;
173+ } ) ;
174+
175+ it ( 'returns undefined if txs not permitted but proposal has no txHashes' , async ( ) => {
176+ const currentProposer = Secp256k1Signer . random ( ) ;
177+ const validatorWithTxsDisabled = new BlockProposalValidator ( epochCache , { txsPermitted : false } ) ;
178+
179+ // Create a block proposal without transaction hashes
180+ const mockProposal = makeBlockProposal ( {
181+ header : makeL2BlockHeader ( 1 , 100 , 100 ) ,
182+ signer : currentProposer ,
183+ txHashes : [ ] , // Empty tx hashes array
184+ } ) ;
185+
186+ // Mock epoch cache for valid case
187+ ( epochCache . getProposerAttesterAddressInCurrentOrNextSlot as jest . Mock ) . mockResolvedValue ( {
188+ currentSlot : 100n ,
189+ nextSlot : 101n ,
190+ currentProposer : currentProposer . address ,
191+ nextProposer : Fr . random ( ) ,
192+ } ) ;
193+
194+ const result = await validatorWithTxsDisabled . validate ( mockProposal ) ;
195+ expect ( result ) . toBeUndefined ( ) ;
196+ } ) ;
197+
198+ it ( 'returns undefined if txs permitted and proposal contains txHashes' , async ( ) => {
199+ const currentProposer = Secp256k1Signer . random ( ) ;
200+ // validator already created with txsPermitted = true in beforeEach
201+
202+ // Create a block proposal with transaction hashes
203+ const mockProposal = makeBlockProposal ( {
204+ header : makeL2BlockHeader ( 1 , 100 , 100 ) ,
205+ signer : currentProposer ,
206+ txHashes : [ TxHash . random ( ) , TxHash . random ( ) ] , // Include some tx hashes
207+ } ) ;
208+
209+ // Mock epoch cache for valid case
210+ ( epochCache . getProposerAttesterAddressInCurrentOrNextSlot as jest . Mock ) . mockResolvedValue ( {
211+ currentSlot : 100n ,
212+ nextSlot : 101n ,
213+ currentProposer : currentProposer . address ,
214+ nextProposer : Fr . random ( ) ,
215+ } ) ;
216+
217+ const result = await validator . validate ( mockProposal ) ;
218+ expect ( result ) . toBeUndefined ( ) ;
219+ } ) ;
220+ } ) ;
149221} ) ;
0 commit comments