Mainnet
This commit is contained in:
@@ -11,7 +11,7 @@ import { setTimeout as delay } from 'node:timers/promises';
|
||||
import { Ledger } from '../server/testnet/ledger.ts';
|
||||
import { PeerNetwork } from '../server/testnet/peers.ts';
|
||||
import { CHAIN_ID, GENESIS_HASH, GENESIS_TIME, MAX_TARGET, address, makeTemplate, miningBlob,
|
||||
mintId, sha256, stateRoot, transferId, transition, workFor } from '../server/testnet/consensus.ts';
|
||||
mintId, sha256, stateRoot, transferId, transition, validateBlock, workFor } from '../server/testnet/consensus.ts';
|
||||
import { NETWORK, CONSENSUS, blockClaimMessage, nftTransferMessage, testnetOwnerOf, transactionCardIds,
|
||||
nftTradeMessage, type MiningTemplate, type NftState, type NftStateLayer, type NftStateView,
|
||||
type NftTrade, type NftTransfer, type TestnetBlock } from '../shared/testnet.ts';
|
||||
@@ -152,6 +152,51 @@ test('timestamp attack boundaries reject at median and beyond future limit befor
|
||||
assert.equal(engine.calls,calls);
|
||||
});
|
||||
|
||||
test('historical replay survives a local clock rollback after accepting the future-skew boundary',async t=>{
|
||||
const dir=await mkdtemp(join(tmpdir(),'wikideck-clock-replay-'));
|
||||
const admissionTime=GENESIS_TIME+CONSENSUS.targetSeconds*20;
|
||||
const powEngine=new FakePow();
|
||||
const ledger=await Ledger.open(dir,powEngine,()=>admissionTime);
|
||||
const template=makeTemplate([],{},alice.owner,alice.publicKey,[],articleFor([]),statsSnapshot(),
|
||||
admissionTime+CONSENSUS.maxFutureSeconds);
|
||||
const block=await mine(template);
|
||||
assert.equal(block.createdAt,admissionTime+CONSENSUS.maxFutureSeconds);
|
||||
assert.deepEqual(await ledger.submit(block),{accepted:true,reorg:false});
|
||||
const accepted=ledger.snapshot();ledger.close();
|
||||
const restarted=await Ledger.open(dir,new FakePow(),()=>admissionTime-CONSENSUS.maxFutureSeconds-1);
|
||||
t.after(async()=>{restarted.close();await rm(dir,{recursive:true,force:true});});
|
||||
assert.deepEqual(restarted.snapshot(),accepted);
|
||||
});
|
||||
|
||||
test('legacy JSON import cannot seed future-dated blocks, even after restart',async t=>{
|
||||
const dir=await mkdtemp(join(tmpdir(),'wikideck-future-legacy-'));
|
||||
const admissionTime=GENESIS_TIME+CONSENSUS.targetSeconds*20;
|
||||
const networkDir=join(dir,'testnet');await mkdir(networkDir,{recursive:true});
|
||||
const template=makeTemplate([],{},alice.owner,alice.publicKey,[],articleFor([]),statsSnapshot(),
|
||||
admissionTime+CONSENSUS.maxFutureSeconds+1);
|
||||
const futureBlock=await mine(template);
|
||||
await writeFile(join(networkDir,'ledger.json'),JSON.stringify({version:1,network:NETWORK,chainId:CHAIN_ID,
|
||||
blocks:[futureBlock],pendingTransfers:[]}));
|
||||
for(let attempt=0;attempt<2;attempt++) {
|
||||
await assert.rejects(Ledger.open(dir,new FakePow(),()=>admissionTime),/timestamp/);
|
||||
}
|
||||
t.after(async()=>{await rm(dir,{recursive:true,force:true});});
|
||||
});
|
||||
|
||||
test('invalid claimed RandomX proof is rejected before full NFT-state enumeration',async()=>{
|
||||
const template=makeTemplate([],{},alice.owner,alice.publicKey,[],articleFor([]),statsSnapshot());
|
||||
const badHeader={...template.header,stateRoot:'f'.repeat(64)};
|
||||
const blob=miningBlob(badHeader),hash=sha256(blob);
|
||||
const candidate:TestnetBlock={...badHeader,hash,tokenId:mintId(badHeader),powHash:'0'.repeat(64),
|
||||
signature:alice.sign(blockClaimMessage(hash)),transactions:[]};
|
||||
let enumerations=0,hashCalls=0;
|
||||
const parentState=new Proxy(Object.create(null) as NftState,{ownKeys(target){enumerations++;return Reflect.ownKeys(target);}});
|
||||
const verifier:PowEngine={async hash(){hashCalls++;return 'f'.repeat(64);},close(){}};
|
||||
await assert.rejects(validateBlock(candidate,[],parentState,verifier),/Invalid RandomX proof/);
|
||||
assert.equal(hashCalls,1);
|
||||
assert.equal(enumerations,0,'invalid work must be rejected before materializing the full NFT state');
|
||||
});
|
||||
|
||||
test('DAG retains equal-work branches, serializes duplicates and selects strictly greater cumulative work', async t => {
|
||||
const { ledger, dir } = await fixture(t);
|
||||
t.mock.method(Date, 'now', () => (GENESIS_TIME + CONSENSUS.targetSeconds * 20) * 1000);
|
||||
@@ -182,6 +227,25 @@ test('DAG retains equal-work branches, serializes duplicates and selects strictl
|
||||
assert.deepEqual(restart.allBlocks, stored);
|
||||
});
|
||||
|
||||
test('a deeper competing fork disconnects the old chain only after cumulative work wins',async t=>{
|
||||
const {ledger}=await fixture(t);const original=branch(),competitor=branch();
|
||||
t.mock.method(Date,'now',()=>(GENESIS_TIME+CONSENSUS.targetSeconds*100)*1000);
|
||||
for(let i=0;i<30;i++)await ledger.submit(await extend(original,alice,[],CONSENSUS.targetSeconds));
|
||||
assert.equal(ledger.snapshot().tip,original.blocks.at(-1)!.hash);
|
||||
for(let i=0;i<15;i++){
|
||||
const result=await ledger.submit(await extend(competitor,bob,[],1));
|
||||
assert.equal(result.reorg,false);
|
||||
assert.equal(ledger.snapshot().tip,original.blocks.at(-1)!.hash);
|
||||
}
|
||||
assert.equal(competitor.blocks.at(-1)!.height,15);
|
||||
assert.notEqual(competitor.blocks.at(-1)!.target,MAX_TARGET);
|
||||
const winning=await ledger.submit(await extend(competitor,bob,[],1));
|
||||
assert.deepEqual(winning,{accepted:true,reorg:true});
|
||||
assert.ok(ledger.snapshot().cumulativeWork===competitor.blocks.reduce((sum,block)=>sum+workFor(block.target),0n).toString());
|
||||
assert.deepEqual(ledger.snapshot().blocks,competitor.blocks);
|
||||
assert.equal(ledger.size,46,'both branches remain available in the DAG');
|
||||
});
|
||||
|
||||
test('equal-work ties preserve the current tip across restart and only extension triggers reorg', async t => {
|
||||
const { dir, ledger } = await fixture(t);
|
||||
const a = branch(), b = branch();
|
||||
@@ -473,7 +537,7 @@ test('SQLite startup rechecks every proof and fails closed on database corruptio
|
||||
tampered.prepare('UPDATE blocks SET block_json = ? WHERE hash = ?')
|
||||
.run(JSON.stringify({ ...originalBlock, stateRoot: '00'.repeat(32) }), row.hash);
|
||||
tampered.close();
|
||||
await assert.rejects(Ledger.open(dir, new FakePow()), /state commitment/);
|
||||
await assert.rejects(Ledger.open(dir, new FakePow()), /block ID/);
|
||||
const restoreTampered = new DatabaseSync(file);
|
||||
restoreTampered.prepare('UPDATE blocks SET block_json = ? WHERE hash = ?').run(row.block_json, row.hash);
|
||||
restoreTampered.prepare("UPDATE metadata SET value = 'wrong' WHERE key = 'network'").run();
|
||||
|
||||
Reference in New Issue
Block a user