Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions server/src/config-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ test('jdc config uses shared shares-per-minute and miner signature', () => {

assert.ok(config);
assert.match(config, /shares_per_minute = 12\.5/);
assert.match(config, /reserved_downstream_rollable_extranonce_size = 8/);
assert.match(config, /jdc_signature = "custom-miner-tag"/);
assert.match(config, /monitoring_cache_refresh_secs = 5/);
assert.match(config, /\[miner_telemetry\]\ncidrs = \["192\.168\.1\.0\/24"\]/);
Expand Down Expand Up @@ -333,6 +334,32 @@ test('jdc in pool mode puts user_identity inside [[upstreams]]', () => {
assert.match(config, /\[\[upstreams\]\][\s\S]*user_identity = "miner\.worker1"/);
});

test('jdc config uses a custom JD port and defaults fallbacks to 3334', () => {
const config = generateJdcConfig({
...BASE_DATA_30,
pool: {
...BASE_DATA_30.pool!,
jds_port: 3337,
},
fallbackPools: [
{
name: 'Fallback Pool',
address: 'fallback.pool.com',
port: 4444,
authority_public_key: 'fallback-key',
user_identity: 'miner.fallback',
},
],
});

assert.ok(config);
const fallbackIndex = config.indexOf('pool_address = "fallback.pool.com"');

assert.ok(fallbackIndex > 0);
assert.match(config.slice(0, fallbackIndex), /jds_port = 3337/);
assert.match(config.slice(fallbackIndex), /jds_port = 3334/);
});

test('jdc config writes Bitcoin Core IPC version', () => {
const config30 = generateJdcConfig(BASE_DATA_30);
const config31 = generateJdcConfig(BASE_DATA_31);
Expand Down
8 changes: 7 additions & 1 deletion server/src/config-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ function normalizePool(pool: PoolConfig | null | undefined, fallbackIdentity: st
name: legacyPool.name ?? 'Custom Pool',
address: legacyPool.address ?? '',
port: legacyPool.port ?? 0,
jds_port: legacyPool.jds_port,
authority_public_key: legacyPool.authority_public_key ?? '',
user_identity: legacyPool.user_identity || fallbackIdentity,
};
Expand Down Expand Up @@ -287,6 +288,9 @@ export function generateJdcConfig(data: SetupData): string | null {
DEFAULT_SHARES_PER_MINUTE,
).toFixed(1);
const shareBatchSize = '5';
const reservedDownstreamRollableExtranonceSize = downstreamExtranonce2Size(
normalizedData.translator?.downstream_extranonce2_size,
);
// Fee threshold and min interval for template provider
const feeThreshold = '1000';
const minInterval = '5';
Expand All @@ -300,7 +304,7 @@ authority_pubkey = "${upstream.authority_public_key}"
pool_address = "${upstream.address}"
pool_port = ${upstream.port}
jds_address = "${upstream.address}"
jds_port = 3334
jds_port = ${upstream.jds_port ?? 3334}
user_identity = "${upstream.user_identity}"
`).join('\n')}
`
Expand All @@ -326,6 +330,8 @@ cert_validity_sec = 3600
# Shares configuration
shares_per_minute = ${sharesPerMinute}
share_batch_size = ${shareBatchSize}
# Extranonce bytes reserved for downstream miners
reserved_downstream_rollable_extranonce_size = ${reservedDownstreamRollableExtranonceSize}

# JDC mode: FULLTEMPLATE, COINBASEONLY, or SOLOMINING
mode = "${isSovereignSolo ? 'SOLOMINING' : 'FULLTEMPLATE'}"
Expand Down
11 changes: 11 additions & 0 deletions server/src/pool-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,14 @@ test('rejects invalid ports and oversized identities', () => {
/username/i,
);
});

test('accepts an omitted JD port and rejects invalid specified JD ports', () => {
assert.equal(getPoolConfigError(VALID_POOL, 'Primary pool'), null);

for (const jds_port of [0, 65536, 3334.5]) {
assert.match(
getPoolConfigError({ ...VALID_POOL, jds_port }, 'Primary pool') ?? '',
/JD port must be between/i,
);
}
});
5 changes: 5 additions & 0 deletions server/src/pool-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export function getPoolConfigError(pool: PoolConfig, label: string): string | nu
if (!Number.isInteger(pool.port) || pool.port <= 0 || pool.port > 65535) {
return `${label} port must be between 1 and 65535`;
}
if (pool.jds_port !== undefined && (
!Number.isInteger(pool.jds_port) || pool.jds_port <= 0 || pool.jds_port > 65535
)) {
return `${label} JD port must be between 1 and 65535`;
}
if (!isValidAuthorityPublicKey(pool.authority_public_key)) {
return `${label} authority public key is invalid`;
}
Expand Down
1 change: 1 addition & 0 deletions shared/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface PoolConfig {
name: string;
address: string;
port: number;
jds_port?: number;
authority_public_key: string;
user_identity: string;
}
Expand Down
38 changes: 34 additions & 4 deletions src/components/pools/PoolPriorityEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface PoolPriorityEditorProps {
presets: KnownPool[];
pools: PoolConfig[];
miningMode: MiningMode | null;
isJdMode: boolean;
onChange: (pools: PoolConfig[]) => void;
}

Expand All @@ -26,6 +27,7 @@ export function PoolPriorityEditor({
presets,
pools,
miningMode,
isJdMode,
onChange,
}: PoolPriorityEditorProps) {
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
Expand Down Expand Up @@ -210,6 +212,7 @@ export function PoolPriorityEditor({
<CustomPoolFields
pool={pool}
idPrefix={`custom-pool-${index}`}
isJdMode={isJdMode}
onChange={(nextPool) => updatePool(index, nextPool)}
/>
)}
Expand Down Expand Up @@ -284,10 +287,12 @@ export function PoolPriorityEditor({
function CustomPoolFields({
pool,
idPrefix,
isJdMode,
onChange,
}: {
pool: PoolConfig;
idPrefix: string;
isJdMode: boolean;
onChange: (pool: PoolConfig) => void;
}) {
const updateField = (field: keyof PoolConfig, value: string | number) => {
Expand All @@ -301,10 +306,14 @@ function CustomPoolFields({

return (
<div className="border-t border-border bg-muted/20 p-3">
<div className="grid gap-2 md:grid-cols-[minmax(0,1fr)_7rem_minmax(0,1.4fr)]">
<div className={`grid gap-2 ${
isJdMode
? 'md:grid-cols-[minmax(0,1fr)_7rem_minmax(0,0.65fr)]'
: 'md:grid-cols-[minmax(0,1fr)_7rem_minmax(0,1.4fr)]'
}`}>
<div>
<label htmlFor={`${idPrefix}-address`} className="mb-1 block text-xs font-medium text-muted-foreground">
Address
Address (without stratum2+tcp://)
</label>
<input
id={`${idPrefix}-address`}
Expand All @@ -320,7 +329,7 @@ function CustomPoolFields({

<div>
<label htmlFor={`${idPrefix}-port`} className="mb-1 block text-xs font-medium text-muted-foreground">
Port
{isJdMode ? 'Pool Port' : 'Port'}
</label>
<input
id={`${idPrefix}-port`}
Expand All @@ -334,7 +343,28 @@ function CustomPoolFields({
/>
</div>

<div>
{isJdMode && (
<div>
<label htmlFor={`${idPrefix}-jds-port`} className="mb-1 block text-xs font-medium text-muted-foreground">
JD Port (optional)
</label>
<input
id={`${idPrefix}-jds-port`}
type="number"
min={1}
max={65535}
value={pool.jds_port ?? ''}
onChange={(event) => onChange({
...pool,
jds_port: event.target.value === '' ? undefined : Number(event.target.value),
})}
placeholder="3334"
className="h-9 w-full rounded-lg border border-input bg-background px-3 text-sm outline-none transition-all focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15"
/>
</div>
)}

<div className={isJdMode ? 'md:col-span-3' : undefined}>
<label htmlFor={`${idPrefix}-pubkey`} className="mb-1 block text-xs font-medium text-muted-foreground">
Authority Public Key
</label>
Expand Down
1 change: 1 addition & 0 deletions src/components/settings/ConfigurationTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ export function ConfigurationTab() {
presets={pools}
pools={editPools ?? []}
miningMode={activeMiningMode}
isJdMode={isJdMode}
onChange={(nextPools) => {
setEditPools((currentPools) => normalizePoolPriorityIdentities(
nextPools,
Expand Down
1 change: 1 addition & 0 deletions src/components/setup/steps/PoolConfigStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export function PoolConfigStep({ data, updateData, onNext }: PoolConfigStepProps
presets={pools}
pools={selectedPools}
miningMode={data.miningMode}
isJdMode={data.mode === 'jd'}
onChange={updateSelectedPools}
/>
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/lib/poolValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export function isPoolConnectionComplete(pool: PoolConfig | null | undefined): b
Number.isInteger(pool.port) &&
pool.port > 0 &&
pool.port <= 65535 &&
(pool.jds_port === undefined || (
Number.isInteger(pool.jds_port) && pool.jds_port > 0 && pool.jds_port <= 65535
)) &&
isValidPoolAuthorityPubkey(pool.authority_public_key),
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/pools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ test('CKPool preset matches its SV2 solo endpoint', () => {
assert.ok(ckPool);
assert.deepEqual(knownPoolToConfig(ckPool), {
name: 'CKPool',
address: 'sv2solo.ckpool.org',
address: 'stratum.ckpool.org',
port: 3336,
authority_public_key: '9anrRNhBh7869XtNnFcCuGBRZP51E635qGbu457J5kHdszhfRc3',
user_identity: '',
});
assert.equal(
`stratum2+tcp://${ckPool.address}:${ckPool.port}/${ckPool.authority_public_key}`,
'stratum2+tcp://sv2solo.ckpool.org:3336/9anrRNhBh7869XtNnFcCuGBRZP51E635qGbu457J5kHdszhfRc3',
'stratum2+tcp://stratum.ckpool.org:3336/9anrRNhBh7869XtNnFcCuGBRZP51E635qGbu457J5kHdszhfRc3',
);
assert.equal(isValidPoolAuthorityPubkey(ckPool.authority_public_key), true);
assert.equal(ckPool.monogram, 'CK');
Expand Down
2 changes: 1 addition & 1 deletion src/lib/pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const SOLO_POOLS: KnownPool[] = [
{
id: 'ckpool',
name: 'CKPool',
address: 'sv2solo.ckpool.org',
address: 'stratum.ckpool.org',
port: 3336,
authority_public_key: '9anrRNhBh7869XtNnFcCuGBRZP51E635qGbu457J5kHdszhfRc3',
description: 'CKPool',
Expand Down
9 changes: 9 additions & 0 deletions src/pages/UnifiedDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,15 @@ export function UnifiedDashboard() {

return <span className={colorClass}>{label}</span>;
})()}
subtitle={(() => {
const { submitted, rejected } = shareStats;
if (submitted === 0) return undefined;
const rejectionRate = (rejected / submitted) * 100;
const rejRateLabel = rejected === 0
? '0%'
: `${Math.max(rejectionRate, 0.01).toFixed(2)}%`;
return `${submitted.toLocaleString()} submitted · ${rejected.toLocaleString()} rejected (${rejRateLabel})`;
})()}
/>
)}

Expand Down
Loading