randomx
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$BuildDirectory = (Join-Path $PSScriptRoot '../native/build'),
|
||||
[string]$Generator = '',
|
||||
[ValidateRange(1, 64)][int]$Jobs = 2,
|
||||
[switch]$Test
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$SourceDirectory = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../native'))
|
||||
$BuildDirectory = [System.IO.Path]::GetFullPath($BuildDirectory)
|
||||
if (-not (Get-Command cmake -ErrorAction SilentlyContinue)) { throw 'CMake 3.24+ is required.' }
|
||||
if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw 'Git is required to fetch pinned RandomX.' }
|
||||
$NativeTests = if ($Test) { 'ON' } else { 'OFF' }
|
||||
$ConfigureArguments = @('-S', $SourceDirectory, '-B', $BuildDirectory,
|
||||
'-DCMAKE_BUILD_TYPE=Release', "-DWIKIDECK_RANDOMX_TESTS=$NativeTests")
|
||||
if ($Generator) { $ConfigureArguments += @('-G', $Generator) }
|
||||
& cmake @ConfigureArguments
|
||||
if ($LASTEXITCODE -ne 0) { throw 'RandomX CMake configuration failed.' }
|
||||
& cmake --build $BuildDirectory --config Release --target wikideck-randomx --parallel $Jobs
|
||||
if ($LASTEXITCODE -ne 0) { throw 'RandomX native build failed.' }
|
||||
if ($Test) {
|
||||
& ctest --test-dir $BuildDirectory -C Release --output-on-failure
|
||||
if ($LASTEXITCODE -ne 0) { throw 'RandomX known-vector test failed.' }
|
||||
}
|
||||
Write-Host "Built native RandomX engine in $BuildDirectory"
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
# Usage: sh scripts/build-randomx.sh [--test]; JOBS defaults to a modest two compiler jobs.
|
||||
tests=OFF
|
||||
case "${1-}" in
|
||||
'') ;;
|
||||
--test) tests=ON; shift ;;
|
||||
*) printf '%s\n' 'usage: build-randomx.sh [--test]' >&2; exit 2 ;;
|
||||
esac
|
||||
[ "$#" -eq 0 ] || { printf '%s\n' 'unexpected argument' >&2; exit 2; }
|
||||
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
build=${RANDOMX_BUILD_DIR:-"$root/native/build"}
|
||||
jobs=${JOBS:-2}
|
||||
case "$jobs" in ''|*[!0-9]*) printf '%s\n' 'JOBS must be 1..64' >&2; exit 2 ;; esac
|
||||
[ "$jobs" -ge 1 ] && [ "$jobs" -le 64 ] || { printf '%s\n' 'JOBS must be 1..64' >&2; exit 2; }
|
||||
command -v cmake >/dev/null 2>&1 || { printf '%s\n' 'CMake 3.24+ is required' >&2; exit 1; }
|
||||
command -v git >/dev/null 2>&1 || { printf '%s\n' 'Git is required' >&2; exit 1; }
|
||||
cmake -S "$root/native" -B "$build" -DCMAKE_BUILD_TYPE=Release "-DWIKIDECK_RANDOMX_TESTS=$tests"
|
||||
cmake --build "$build" --config Release --target wikideck-randomx --parallel "$jobs"
|
||||
if [ "$tests" = ON ]; then ctest --test-dir "$build" -C Release --output-on-failure; fi
|
||||
printf 'Built native RandomX engine in %s\n' "$build"
|
||||
@@ -0,0 +1,59 @@
|
||||
// Explicit opt-in live-network test. Creates disposable wallets and mines three test NFTs.
|
||||
import assert from 'node:assert/strict';
|
||||
import { generateKeyPairSync, sign } from 'node:crypto';
|
||||
import { setTimeout as delay } from 'node:timers/promises';
|
||||
import { RandomX } from '../server/testnet/randomx.ts';
|
||||
import { address, CHAIN_ID, miningBlob, mintId, sha256 } from '../server/testnet/consensus.ts';
|
||||
import { blockClaimMessage, nftTransferMessage, NETWORK, testnetOwnerOf, type MiningTemplate, type TestnetSnapshot } from '../shared/testnet.ts';
|
||||
|
||||
if(!process.argv.includes('--consent'))throw new Error('This test mines three blocks with a native CPU worker. Pass --consent explicitly. Disposable NFT rewards are not recoverable.');
|
||||
const nodes=(process.env.WIKIDECK_SMOKE_NODES??'http://127.0.0.1:3101,http://127.0.0.1:3102,http://127.0.0.1:3103').split(',');
|
||||
for(const endpoint of nodes)if(!/^http:\/\/127\.0\.0\.1:\d+$/.test(endpoint))throw new Error('Smoke test only accepts explicit loopback test nodes');
|
||||
async function api(node:string,path:string,body?:unknown){
|
||||
const r=await fetch(node+path,{method:body?'POST':'GET',headers:body?{'Content-Type':'application/json'}:{},
|
||||
body:body?JSON.stringify(body):undefined,signal:AbortSignal.timeout(15000),redirect:'error'});
|
||||
const value=await r.json();if(!r.ok)throw new Error(value.error??`HTTP${r.status}`);return value;
|
||||
}
|
||||
const keys=generateKeyPairSync('ec',{namedCurve:'prime256v1'});
|
||||
const publicKey=keys.publicKey.export({format:'der',type:'spki'}).toString('base64'),owner=address(publicKey);
|
||||
const receiver=generateKeyPairSync('ec',{namedCurve:'prime256v1'});
|
||||
const to=address(receiver.publicKey.export({format:'der',type:'spki'}).toString('base64'));
|
||||
const signed=(m:string)=>sign('sha256',Buffer.from(m),{key:keys.privateKey,dsaEncoding:'ieee-p1363'}).toString('base64');
|
||||
const engine=new RandomX();
|
||||
try {
|
||||
const initial=await api(nodes[0],'/api/state') as TestnetSnapshot;
|
||||
assert.equal(initial.chainId,CHAIN_ID);
|
||||
console.log(`Testing ${nodes.length} independent nodes on ${CHAIN_ID}; starting height ${initial.blocks.length}`);
|
||||
let first='';
|
||||
for(let count=0;count<3;count++) {
|
||||
if(count===2){
|
||||
const tx={network:NETWORK,chainId:CHAIN_ID,cardHash:first,from:owner,to,sequence:0};
|
||||
await api(nodes[0],'/api/transfer',{...tx,publicKey,signature:signed(nftTransferMessage(tx))});
|
||||
const pending=await api(nodes[0],'/api/state') as TestnetSnapshot;
|
||||
assert.equal(testnetOwnerOf(pending,first),owner);
|
||||
assert.ok(pending.pendingTransfers.some(t=>t.cardHash===first));
|
||||
}
|
||||
const template=await api(nodes[0],'/api/mining/template',{owner,publicKey}) as MiningTemplate;
|
||||
let found;
|
||||
for(let batch=0;batch<100;batch++){
|
||||
found=await engine.mine(template.header.key,miningBlob(template.header),template.header.target,32,32);
|
||||
if(found)break;template.header.nonce+=32;
|
||||
}
|
||||
assert.ok(found,'Native proof not found within smoke budget');
|
||||
template.header.nonce=found.nonce;
|
||||
const hash=sha256(miningBlob(template.header)),tokenId=mintId(template.header);
|
||||
await api(nodes[0],'/api/blocks',{...template.header,hash,tokenId,powHash:found.hash,signature:signed(blockClaimMessage(hash)),transactions:template.transactions});
|
||||
if(count===0)first=tokenId;
|
||||
console.log(`Native RandomX block #${template.header.height} accepted: ${hash}`);
|
||||
}
|
||||
const canonical=await api(nodes[0],'/api/state') as TestnetSnapshot;
|
||||
const deadline=Date.now()+60000;
|
||||
for(;;){
|
||||
const states=await Promise.all(nodes.map(n=>api(n,'/api/state') as Promise<TestnetSnapshot>));
|
||||
if(states.every(s=>s.tip===canonical.tip&&s.cumulativeWork===canonical.cumulativeWork&&testnetOwnerOf(s,first)===to)){
|
||||
console.log(`PASS: ${states.length} independent nodes agreed on work ${canonical.cumulativeWork}, tip, NFT issuance and mined transfer.`);break;
|
||||
}
|
||||
if(Date.now()>deadline)throw new Error('Peer convergence timeout');
|
||||
await delay(500);
|
||||
}
|
||||
}finally{engine.close();}
|
||||
Reference in New Issue
Block a user