@@ -353,6 +353,65 @@ test('durable native ledger retains charges and refuses unresolved or revoked re
353353 }
354354} ) ;
355355
356+ test ( 'separate native processes cannot reset a held ledger or replay its settled effects' ,
357+ { skip : process . platform !== 'linux' || process . version !== 'v24.18.0' } , ( ) => {
358+ const { spawnSync} = require ( 'node:child_process' ) ;
359+ const parent = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'jev-node-contention-' ) ) ;
360+ fs . chmodSync ( parent , 0o700 ) ;
361+ const ledgerPath = path . join ( parent , 'ledger.sqlite' ) ;
362+ const limits = { max_calls : 2 , max_cost : 2 } ;
363+ const identity = 'a' . repeat ( 64 ) ;
364+ const runtime = require . resolve ( '../jev_integration_evaluator/data/native_js_runtime.cjs' ) ;
365+ const child = mode => {
366+ const result = spawnSync ( process . execPath , [ '-e' , `
367+ const assert = require('node:assert/strict');
368+ const {DurableSharedBudget} = require(process.argv[1]);
369+ const args = JSON.parse(process.argv[2]);
370+ if (process.argv[3] === 'contend') {
371+ assert.throws(() => new DurableSharedBudget(args), /locked/);
372+ } else {
373+ const ledger = new DurableSharedBudget(args);
374+ try {
375+ assert.equal(ledger.calls, 1);
376+ assert.equal(ledger.cost, 1);
377+ assert.throws(() => ledger.claimInvocation('one', 'first', 'candidate'),
378+ /effect_replay_denied/);
379+ const reservation = ledger.reserve('two', 1);
380+ ledger.finishReservation(reservation);
381+ assert.throws(() => ledger.reserve('three', 1), /budget_denied/);
382+ assert.equal(ledger.calls, 2);
383+ assert.equal(ledger.cost, 2);
384+ } finally { ledger.close(); }
385+ }
386+ process.stdout.write('verified');
387+ ` , runtime , JSON . stringify ( { limits, ledgerPath, identity} ) , mode ] ,
388+ { encoding : 'utf8' , timeout : 10000 , env : { PATH : process . env . PATH } } ) ;
389+ assert . equal ( result . error , undefined ) ;
390+ assert . equal ( result . status , 0 , result . stderr ) ;
391+ assert . equal ( result . stdout , 'verified' ) ;
392+ } ;
393+ let ledger ;
394+ try {
395+ ledger = new DurableSharedBudget ( { limits, ledgerPath, identity} ) ;
396+ const reservation = ledger . reserve ( 'one' , 1 ) ;
397+ ledger . finishReservation ( reservation ) ;
398+ ledger . claimInvocation ( 'one' , 'first' , 'candidate' ) ;
399+ ledger . settleInvocation ( 'one' , 'first' , 'candidate' ) ;
400+ child ( 'contend' ) ; // a real peer process must fail before it can reserve
401+ assert . equal ( ledger . calls , 1 ) ;
402+ assert . equal ( ledger . cost , 1 ) ;
403+ ledger . close ( ) ; ledger = null ;
404+ child ( 'resume' ) ; // release transfers ownership, never a fresh budget
405+ ledger = new DurableSharedBudget ( { limits, ledgerPath, identity} ) ;
406+ assert . equal ( ledger . calls , 2 ) ;
407+ assert . equal ( ledger . cost , 2 ) ;
408+ assert . throws ( ( ) => ledger . reserve ( 'four' , 1 ) , / b u d g e t _ d e n i e d / ) ;
409+ } finally {
410+ if ( ledger ) ledger . close ( ) ;
411+ fs . rmSync ( parent , { recursive : true , force : true } ) ;
412+ }
413+ } ) ;
414+
356415test ( 'connected shadow owner checks independent grant and installed bytes before fake transport' ,
357416 { skip : process . platform !== 'linux' || process . version !== 'v24.18.0' } , async ( ) => {
358417 const parent = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'jev-connected-shadow-' ) ) ;
0 commit comments