Mainnet
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { createHash, createPublicKey, verify } from 'node:crypto';
|
||||
import { z } from 'zod';
|
||||
import { NETWORK, CONSENSUS, headerMessage, blockClaimMessage, nftTransferMessage, nftTradeMessage, statsSnapshotTuple,
|
||||
import { NETWORK, CONSENSUS, MAX_TARGET, headerMessage, blockClaimMessage, nftTransferMessage, nftTradeMessage, statsSnapshotTuple,
|
||||
type BlockHeader, type TestnetBlock, type NftTransfer, type NftState, type NftStateLayer,
|
||||
isNftTrade, type NftStateView, type NftTrade, type TestnetTransaction, type MiningTemplate } from '../../shared/testnet.ts';
|
||||
import type { Article } from '../../shared/protocol.ts';
|
||||
@@ -9,7 +9,7 @@ import type { PowEngine } from './randomx.ts';
|
||||
import { NETWORK_GENESIS_TIME } from '../../shared/network.ts';
|
||||
|
||||
export const sha256 = (data: string | Uint8Array) => createHash('sha256').update(data).digest('hex');
|
||||
export const MAX_TARGET = `0fff${'f'.repeat(60)}`;
|
||||
export { MAX_TARGET };
|
||||
// Consensus manifest: an independently running node derives exactly the same genesis.
|
||||
export const GENESIS_TIME = NETWORK_GENESIS_TIME;
|
||||
export const CHAIN_ID = sha256(JSON.stringify([NETWORK, CONSENSUS, GENESIS_TIME, MAX_TARGET]));
|
||||
@@ -261,19 +261,24 @@ export function makeTemplate(ancestors: TestnetBlock[], state: NftStateView, own
|
||||
return { header, transactions, blob: miningBlob(header).toString('hex'), nonceOffset: 32 };
|
||||
}
|
||||
export async function validateBlock(raw: unknown, ancestors: TestnetBlock[], parentState: NftStateView,
|
||||
pow: PowEngine, now = Math.floor(Date.now()/1000)): Promise<{block:TestnetBlock;state:NftStateView}> {
|
||||
pow: PowEngine, now = Math.floor(Date.now()/1000), allowHistoricalFutureTimestamp = false): Promise<{block:TestnetBlock;state:NftStateView}> {
|
||||
if (Buffer.byteLength(JSON.stringify(raw)) > CONSENSUS.maxBlockBytes) throw new Error('Block size limit exceeded');
|
||||
const b = blockSchema.parse(raw);
|
||||
if (b.height !== ancestors.length + 1 || b.previousHash !== (ancestors.at(-1)?.hash ?? GENESIS_HASH)) throw new Error('Invalid parent/height');
|
||||
if (b.createdAt <= medianTime(ancestors) || b.createdAt > now + CONSENSUS.maxFutureSeconds) throw new Error('Invalid block timestamp');
|
||||
if (b.createdAt <= medianTime(ancestors) || (!allowHistoricalFutureTimestamp && b.createdAt > now + CONSENSUS.maxFutureSeconds)) {
|
||||
throw new Error('Invalid block timestamp');
|
||||
}
|
||||
if (b.target !== expectedTarget(ancestors) || b.key !== expectedKey(ancestors, b.height)) throw new Error('Invalid difficulty or RandomX epoch key');
|
||||
if (b.transactionsRoot !== merkleRoot(b.transactions.map(transactionId))) throw new Error('Invalid transaction root');
|
||||
const next = transition(parentState, b, b.transactions);
|
||||
if (b.stateRoot !== stateRoot(next) || b.tokenId !== mintId(b)) throw new Error('Invalid NFT state commitment');
|
||||
if (b.tokenId !== mintId(b)) throw new Error('Invalid NFT state commitment');
|
||||
const blob = miningBlob(b);
|
||||
if (b.hash !== sha256(blob)) throw new Error('Invalid block ID');
|
||||
if (!verifySignature(b.publicKey, b.owner, blockClaimMessage(b.hash), b.signature)) throw new Error('Invalid miner signature');
|
||||
if (BigInt(`0x${b.powHash}`) > BigInt(`0x${b.target}`)) throw new Error('Insufficient work');
|
||||
if (await pow.hash(b.key, blob) !== b.powHash) throw new Error('Invalid RandomX proof');
|
||||
// Verify native work before materializing/sorting the complete NFT state. A malformed
|
||||
// low-work block must not force O(total NFTs) commitment work on the request path.
|
||||
const next = transition(parentState, b, b.transactions);
|
||||
if (b.stateRoot !== stateRoot(next)) throw new Error('Invalid NFT state commitment');
|
||||
return { block: b, state: next };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user