mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
Compare commits
2
Commits
f31cdef7f0
...
f49e7b5b8f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f49e7b5b8f | ||
|
|
3cb4cbfbdf |
@@ -0,0 +1,62 @@
|
||||
import { CacheModule } from '@nestjs/cache-manager';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AddressController } from './controllers/address/address.controller';
|
||||
import { ClientController } from './controllers/client/client.controller';
|
||||
import { UserAgentReportModule } from './ORM/_views/user-agent-report/user-agent-report.module';
|
||||
import { AddressSettingsModule } from './ORM/address-settings/address-settings.module';
|
||||
import { BlocksModule } from './ORM/blocks/blocks.module';
|
||||
import { ClientStatisticsModule } from './ORM/client-statistics/client-statistics.module';
|
||||
import { ClientModule } from './ORM/client/client.module';
|
||||
import { PayoutSnapshotModule } from './ORM/payout-snapshot/payout-snapshot.module';
|
||||
import { RpcBlocksModule } from './ORM/rpc-block/rpc-block.module';
|
||||
import { ShareAccountingModule } from './ORM/share-accounting/share-accounting.module';
|
||||
import { createDatabaseOptions } from './database.config';
|
||||
import { BitcoinAddressValidator } from './models/validators/bitcoin-address.validator';
|
||||
import { BitcoinRpcService } from './services/bitcoin-rpc.service';
|
||||
import { RedisMessagingModule } from './services/redis-messaging.module';
|
||||
import { Sv2AuthorityService } from './services/sv2-authority.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forRootAsync({
|
||||
useFactory: (configService: ConfigService) => createDatabaseOptions({
|
||||
...process.env,
|
||||
DB_HOST: configService.get('DB_HOST'),
|
||||
DB_PORT: configService.get('DB_PORT'),
|
||||
DB_USERNAME: configService.get('DB_USERNAME'),
|
||||
DB_PASSWORD: configService.get('DB_PASSWORD'),
|
||||
DB_DATABASE: configService.get('DB_DATABASE'),
|
||||
DB_LOGGING: configService.get('DB_LOGGING'),
|
||||
DB_POOL_SIZE: configService.get('DB_POOL_SIZE'),
|
||||
}),
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
CacheModule.register(),
|
||||
RedisMessagingModule,
|
||||
ClientStatisticsModule,
|
||||
ClientModule,
|
||||
AddressSettingsModule,
|
||||
BlocksModule,
|
||||
RpcBlocksModule,
|
||||
UserAgentReportModule,
|
||||
ShareAccountingModule,
|
||||
PayoutSnapshotModule,
|
||||
],
|
||||
controllers: [
|
||||
AppController,
|
||||
ClientController,
|
||||
AddressController,
|
||||
],
|
||||
providers: [
|
||||
BitcoinRpcService,
|
||||
BitcoinAddressValidator,
|
||||
Sv2AuthorityService,
|
||||
],
|
||||
})
|
||||
export class ApiModule {}
|
||||
@@ -6,7 +6,7 @@ describe('AppController', () => {
|
||||
blocksService?: any;
|
||||
addressSettingsService?: any;
|
||||
userAgentReportService?: any;
|
||||
stratumV2Service?: any;
|
||||
sv2AuthorityService?: any;
|
||||
redisMessagingService?: any;
|
||||
} = {}) => {
|
||||
return new AppController(
|
||||
@@ -26,7 +26,7 @@ describe('AppController', () => {
|
||||
overrides.userAgentReportService ?? {
|
||||
getReport: jest.fn().mockResolvedValue([]),
|
||||
},
|
||||
overrides.stratumV2Service ?? {
|
||||
overrides.sv2AuthorityService ?? {
|
||||
getPoolAuthorityPublicKey: jest.fn().mockResolvedValue({
|
||||
publicKey: '',
|
||||
configured: false,
|
||||
|
||||
@@ -10,9 +10,9 @@ import { ClientStatisticsService } from './ORM/client-statistics/client-statisti
|
||||
import { ClientService } from './ORM/client/client.service';
|
||||
import { BitcoinRpcService } from './services/bitcoin-rpc.service';
|
||||
import { UserAgentReportView } from './ORM/_views/user-agent-report/user-agent-report.view';
|
||||
import { StratumV2Service } from './services/stratum-v2.service';
|
||||
import { ShareAccountingService } from './ORM/share-accounting/share-accounting.service';
|
||||
import { RedisMessagingService } from './services/redis-messaging.service';
|
||||
import { Sv2AuthorityService } from './services/sv2-authority.service';
|
||||
import { normalizePayoutMode } from './types/payout-mode';
|
||||
|
||||
@Controller()
|
||||
@@ -30,7 +30,7 @@ export class AppController {
|
||||
private readonly bitcoinRpcService: BitcoinRpcService,
|
||||
private readonly addressSettingsService: AddressSettingsService,
|
||||
private readonly userAgentReportService: UserAgentReportService,
|
||||
private readonly stratumV2Service: StratumV2Service,
|
||||
private readonly sv2AuthorityService: Sv2AuthorityService,
|
||||
private readonly shareAccountingService: ShareAccountingService,
|
||||
private readonly redisMessagingService: RedisMessagingService
|
||||
) { }
|
||||
@@ -78,7 +78,7 @@ export class AppController {
|
||||
const [blockData, highScores, poolAuthority, userAgentReport] = await Promise.all([
|
||||
withInfoTimeout('found blocks', this.blocksService.getFoundBlocks(), staleInfo?.blockData ?? []),
|
||||
withInfoTimeout('high scores', this.addressSettingsService.getHighScores(), staleInfo?.highScores ?? []),
|
||||
withInfoTimeout('SV2 authority', this.stratumV2Service.getPoolAuthorityPublicKey(), {
|
||||
withInfoTimeout('SV2 authority', this.sv2AuthorityService.getPoolAuthorityPublicKey(), {
|
||||
publicKey: staleInfo?.sv2?.poolAuthorityPublicKey ?? '',
|
||||
configured: staleInfo?.sv2?.authorityKeyConfigured ?? false
|
||||
}),
|
||||
|
||||
@@ -33,6 +33,7 @@ import { StratumV1Service } from './services/stratum-v1.service';
|
||||
import { Sv2JobDeclarationRegistryService } from './services/sv2-job-declaration-registry.service';
|
||||
import { Sv2JobDeclarationService } from './services/sv2-job-declaration.service';
|
||||
import { Sv2TemplateDistributionService } from './services/sv2-template-distribution.service';
|
||||
import { Sv2AuthorityService } from './services/sv2-authority.service';
|
||||
import { StratumV2Service } from './services/stratum-v2.service';
|
||||
import { TelegramService } from './services/telegram.service';
|
||||
import { TemplateProviderService } from './services/template-provider.service';
|
||||
@@ -94,6 +95,7 @@ const ORMModules = [
|
||||
Sv2JobDeclarationRegistryService,
|
||||
Sv2JobDeclarationService,
|
||||
Sv2TemplateDistributionService,
|
||||
Sv2AuthorityService,
|
||||
CustomWorkService,
|
||||
DatumService,
|
||||
BTCPayService,
|
||||
|
||||
+4
-2
@@ -7,6 +7,7 @@ import { readFileSync, watch } from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ecc from 'tiny-secp256k1';
|
||||
|
||||
import { ApiModule } from './api.module';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
@@ -32,7 +33,8 @@ async function bootstrap() {
|
||||
};
|
||||
}
|
||||
|
||||
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter(options));
|
||||
const appModule = process.env.API_ONLY === 'true' ? ApiModule : AppModule;
|
||||
const app = await NestFactory.create<NestFastifyApplication>(appModule, new FastifyAdapter(options));
|
||||
app.setGlobalPrefix('api');
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
@@ -54,7 +56,7 @@ async function bootstrap() {
|
||||
});
|
||||
|
||||
app.enableCors();
|
||||
useContainer(app.select(AppModule), { fallbackOnErrors: true });
|
||||
useContainer(app.select(appModule), { fallbackOnErrors: true });
|
||||
|
||||
// Taproot
|
||||
bitcoinjs.initEccLib(ecc);
|
||||
|
||||
@@ -991,7 +991,7 @@ describe('StratumV2Client extended channels', () => {
|
||||
.toBe(parseInt(activation.bits, 16));
|
||||
});
|
||||
|
||||
it('validates fixed/rolling versions, required bits, and the advertised minimum nTime', async () => {
|
||||
it('validates BIP320 version rolling, required bits, and the advertised minimum nTime', async () => {
|
||||
const { client } = await createClient();
|
||||
const now = process.hrtime.bigint();
|
||||
const context = {
|
||||
@@ -1007,11 +1007,12 @@ describe('StratumV2Client extended channels', () => {
|
||||
};
|
||||
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20000004, 100)).toBe(true);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20002004, 100)).toBe(false);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20002004, 100)).toBe(true);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20000004, 99)).toBe(false);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20000004, 101)).toBe(true);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20000004, 0xffffffff)).toBe(true);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20000004, 0x1_0000_0000)).toBe(false);
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x00000004, 100)).toBe(false);
|
||||
|
||||
(client as any).versionRollingEnabled = true;
|
||||
expect((client as any).isSubmissionHeaderValid(context, 0x20002004, 100)).toBe(true);
|
||||
|
||||
@@ -2088,13 +2088,13 @@ export class StratumV2Client {
|
||||
): boolean {
|
||||
const submitted = submittedVersion >>> 0;
|
||||
const base = headerContext.baseVersion >>> 0;
|
||||
if (this.versionRollingEnabled) {
|
||||
// Some SV2 clients roll BIP320 general-purpose version bits even when
|
||||
// they did not explicitly require version rolling during setup. Those
|
||||
// bits are non-consensus signalling space; reject only changes outside
|
||||
// that mask and continue enforcing required bits below.
|
||||
if (((submitted ^ base) & BIP320_CONSENSUS_VERSION_MASK) !== 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (submitted !== base) {
|
||||
return false;
|
||||
}
|
||||
if ((submitted & headerContext.requiredVersionBits) !== headerContext.requiredVersionBits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
import { encodeSv2AuthorityPublicKey } from '../models/sv2/sv2-authority-key';
|
||||
import { xOnlyPubKeyFromPriv } from '../models/sv2/sv2-noise';
|
||||
|
||||
@Injectable()
|
||||
export class Sv2AuthorityService {
|
||||
private authorityPublicKey: string | null = null;
|
||||
private authorityKeyConfigured = false;
|
||||
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
public async getPoolAuthorityPublicKey(): Promise<{ publicKey: string; configured: boolean }> {
|
||||
if (this.authorityPublicKey != null) {
|
||||
return {
|
||||
publicKey: this.authorityPublicKey,
|
||||
configured: this.authorityKeyConfigured,
|
||||
};
|
||||
}
|
||||
|
||||
const configuredAuthorityKey = this.configService.get<string>('SV2_AUTHORITY_PRIVKEY');
|
||||
this.authorityKeyConfigured = configuredAuthorityKey?.length === 64;
|
||||
const authorityPrivKey = configuredAuthorityKey?.length === 64
|
||||
? Buffer.from(configuredAuthorityKey, 'hex')
|
||||
: crypto.randomBytes(32);
|
||||
this.authorityPublicKey = encodeSv2AuthorityPublicKey(xOnlyPubKeyFromPriv(authorityPrivKey));
|
||||
|
||||
return {
|
||||
publicKey: this.authorityPublicKey,
|
||||
configured: this.authorityKeyConfigured,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user