@@ -327,7 +327,7 @@ test.describe("api cache and payments", { concurrency: false }, () => {
327327 const config = createConfig ( ) ;
328328 config . pplns . enable = false ;
329329 const mysql = createMysql ( async function handler ( sql , params , calls ) {
330- if ( sql . startsWith ( "SELECT * FROM transactions ORDER BY id DESC") ) {
330+ if ( sql . includes ( " FROM transactions ORDER BY id DESC") ) {
331331 return [
332332 { id : 11 , transaction_hash : "hash11" , mixin : 12 , payees : 1 , fees : 2 , xmr_amt : 3 , submitted_time : "2024-01-02T00:00:00Z" } ,
333333 { id : 10 , transaction_hash : "hash10" , mixin : 11 , payees : 2 , fees : 3 , xmr_amt : 4 , submitted_time : "2024-01-01T00:00:00Z" }
@@ -385,7 +385,7 @@ test.describe("api cache and payments", { concurrency: false }, () => {
385385
386386 test ( "block payment lookups stay parameterized when the wallet path contains injection text" , async ( ) => {
387387 const mysql = createMysql ( async function handler ( sql ) {
388- if ( sql . startsWith ( "SELECT * FROM paid_blocks") ) {
388+ if ( sql . includes ( " FROM paid_blocks WHERE ") ) {
389389 return [
390390 { id : 1 , paid_time : "2024-01-02T00:00:00Z" , found_time : "2024-01-01T00:00:00Z" , port : 18081 , hex : "hex1" , amount : 1000 } ,
391391 { id : 2 , paid_time : "2024-01-03T00:00:00Z" , found_time : "2024-01-02T00:00:00Z" , port : 18081 , hex : "hex2" , amount : 2000 }
@@ -414,4 +414,82 @@ test.describe("api cache and payments", { concurrency: false }, () => {
414414 } ) ;
415415 } ) ;
416416
417+ test ( "block payment lookups reuse the address-independent paid_blocks scan across miners" , async ( ) => {
418+ let paidBlocksCalls = 0 ;
419+ let balanceCalls = 0 ;
420+ const mysql = createMysql ( async function handler ( sql ) {
421+ if ( sql . includes ( "FROM paid_blocks WHERE" ) ) {
422+ paidBlocksCalls += 1 ;
423+ assert . doesNotMatch ( sql , / S E L E C T \* / ) ;
424+ return [ { id : 1 , paid_time : "2024-01-02T00:00:00Z" , found_time : "2024-01-01T00:00:00Z" , port : 18081 , hex : "hex1" , amount : 1000 } ] ;
425+ }
426+ if ( sql . startsWith ( "SELECT hex, amount FROM block_balance" ) ) {
427+ balanceCalls += 1 ;
428+ return [ { hex : "hex1" , amount : 0.5 } ] ;
429+ }
430+ throw new Error ( "Unexpected SQL: " + sql ) ;
431+ } ) ;
432+
433+ await withRuntime ( {
434+ blockTemplate : createBlockTemplate ( ) ,
435+ config : createConfig ( ) ,
436+ database : createDatabase ( { caches : { } } ) ,
437+ mysql : mysql ,
438+ support : createSupport ( )
439+ } , async ( port ) => {
440+ const a = await request ( port , { path : "/miner/walletA/block_payments?limit=15&page=0" } ) ;
441+ const b = await request ( port , { path : "/miner/walletB/block_payments?limit=15&page=0" } ) ;
442+ assert . equal ( a . statusCode , 200 ) ;
443+ assert . equal ( b . statusCode , 200 ) ;
444+ // The 7-day paid_blocks scan is identical for every miner, so it runs once;
445+ // only the per-miner block_balance lookup repeats.
446+ assert . equal ( paidBlocksCalls , 1 ) ;
447+ assert . equal ( balanceCalls , 2 ) ;
448+ } ) ;
449+ } ) ;
450+
451+ test ( "pool payments select explicit transaction columns, never SELECT *" , async ( ) => {
452+ const seen = [ ] ;
453+ const mysql = createMysql ( async function handler ( sql ) {
454+ seen . push ( sql ) ;
455+ if ( sql . includes ( "FROM transactions ORDER BY id DESC" ) ) {
456+ return [ { id : 1 , transaction_hash : "h1" , mixin : 7 , payees : 1 , fees : 2 , xmr_amt : 3 , submitted_time : "2024-01-02T00:00:00Z" } ] ;
457+ }
458+ throw new Error ( "Unexpected SQL: " + sql ) ;
459+ } ) ;
460+
461+ await withRuntime ( {
462+ blockTemplate : createBlockTemplate ( ) ,
463+ config : createConfig ( ) ,
464+ database : createDatabase ( { caches : { } } ) ,
465+ mysql : mysql ,
466+ support : createSupport ( )
467+ } , async ( port ) => {
468+ const res = await request ( port , { path : "/pool/payments/pplns?limit=15&page=0" } ) ;
469+ assert . equal ( res . statusCode , 200 ) ;
470+ const txnSql = seen . find ( ( sql ) => sql . includes ( "FROM transactions ORDER BY id DESC" ) ) ;
471+ assert . ok ( txnSql , "transactions query ran" ) ;
472+ assert . doesNotMatch ( txnSql , / S E L E C T \* / ) ;
473+ assert . doesNotMatch ( txnSql , / \b a d d r e s s \b / ) ;
474+ assert . doesNotMatch ( txnSql , / p a y m e n t _ i d / ) ;
475+ } ) ;
476+ } ) ;
477+
478+ test ( "coin altblocks cache key normalizes the port so equivalent spellings share one entry" , async ( ) => {
479+ const database = createDatabase ( { caches : { } } ) ;
480+ await withRuntime ( {
481+ blockTemplate : createBlockTemplate ( ) ,
482+ config : createConfig ( ) ,
483+ database : database ,
484+ mysql : createMysql ( async ( ) => [ ] ) ,
485+ support : createSupport ( )
486+ } , async ( port ) => {
487+ await request ( port , { path : "/pool/coin_altblocks/18081?page=0" } ) ;
488+ await request ( port , { path : "/pool/coin_altblocks/018081?page=0" } ) ;
489+ // Both spellings normalize to port 18081 -> one cache entry -> one DB call.
490+ assert . equal ( database . state . altBlockListCalls . length , 1 ) ;
491+ assert . equal ( database . state . altBlockListCalls [ 0 ] . coinPort , 18081 ) ;
492+ } ) ;
493+ } ) ;
494+
417495} ) ;
0 commit comments