diff --git a/CHANGELOG.md b/CHANGELOG.md index f94c67e96..a08edfc36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - http-client: a caller can bound the response body (`http_request_bounded`, `PrpcClient::with_max_response_bytes`). Nothing is bounded by default — `dstack vmm logs --lines 100000` is a legitimate multi-megabyte fetch — but every client that talks to a guest agent opts in, in the gateway and in the VMM, because a CVM is untrusted and one of them polls on a timer against the whole fleet ### Fixed +- sdk: Python and JavaScript blockchain adapters now reject TLS-key responses. The deprecated conversion path read fixed PKCS#8 framing as private-key bytes, causing different TLS keys to derive the same Ethereum and Solana wallets. Use `get_key()` / `getKey()` for wallet keys. - data disks: discard now propagates through ZFS or ext4, dm-crypt, virtio-blk, and QEMU so encrypted qcow2 images release deleted blocks instead of growing with lifetime writes. Discard defaults on and can be disabled with `storage_discard: false` when allocation-pattern leakage is unacceptable; upgrading an existing ZFS pool also starts a one-time trim for historical free space - certbot: a certificate covering both a name and its wildcard (`example.com` and `*.example.com`) could never be issued over dns-01. The two authorizations are answered under one `_acme-challenge.example.com`, each with its own TXT value, and the publish step cleared every TXT record at that name before writing its own -- so the second authorization deleted the record answering the first, and the order failed with `Correct value not found for DNS challenge`. Clearing leftovers from an aborted run is now done once per challenge name per issuance, and the records for one name accumulate instead of replacing each other; cleanup afterwards is unchanged, deleting each record this run created by id - certbot: editing `domains` in `certbot.toml` had no effect once a certificate existed. Issuance was skipped whenever `live/cert.pem` was present, whatever names it carried, and renewal read its name list back off that certificate rather than the configuration -- so an added or removed name never reached the CA, and the mismatch survived every renewal. The live certificate's DNS names are now compared against the configured list (as sets, case- and trailing-dot-insensitive) and a mismatch reissues, logging both lists. A reissue that fails does not take the renewal check down with it: a name the CA will not validate is reported on every cycle, while the certificate actually being served keeps renewing, and the failure is still what the run returns unless the renewal committed something of its own diff --git a/sdk/js/src/__tests__/solana.test.ts b/sdk/js/src/__tests__/solana.test.ts index 87436b3bb..f9eb612b5 100644 --- a/sdk/js/src/__tests__/solana.test.ts +++ b/sdk/js/src/__tests__/solana.test.ts @@ -2,83 +2,37 @@ // // SPDX-License-Identifier: Apache-2.0 - -import { expect, describe, it, vi } from 'vitest' +import { expect, describe, it } from 'vitest' import { Keypair } from '@solana/web3.js' - -import { DstackClientV0, TappdClient } from '../index' +import type { GetKeyResponse, GetTlsKeyResponse } from '../client-v0' import { toKeypair, toKeypairSecure } from '../solana' describe('solana support', () => { - describe('toKeypair (legacy)', () => { - it('should able to get keypair from getKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const result = await client.getKey('/', 'test') - const keypair = toKeypair(result) - expect(keypair).toBeInstanceOf(Keypair) - expect(keypair.secretKey.length).toBe(64) - }) - - it('should able to get keypair from deriveKey with TappdClient', async () => { - const client = new TappdClient() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.deriveKey('/', 'test') - const keypair = toKeypair(result) - expect(keypair).toBeInstanceOf(Keypair) - expect(keypair.secretKey.length).toBe(64) - expect(consoleSpy).toHaveBeenCalledWith('toKeypair: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() - }) - - it('should able to get keypair from getTlsKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.getTlsKey() - const keypair = toKeypair(result) - expect(keypair).toBeInstanceOf(Keypair) - expect(keypair.secretKey.length).toBe(64) - expect(consoleSpy).toHaveBeenCalledWith('toKeypair: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() - }) - }) - - describe('toKeypairSecure', () => { - it('should able to get keypair from getKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const result = await client.getKey('/', 'test') - const keypair = toKeypairSecure(result) - expect(keypair).toBeInstanceOf(Keypair) - expect(keypair.secretKey.length).toBe(64) - }) - - it('should able to get keypair from deriveKey with TappdClient', async () => { - const client = new TappdClient() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.deriveKey('/', 'test') - const keypair = toKeypairSecure(result) - expect(keypair).toBeInstanceOf(Keypair) - expect(keypair.secretKey.length).toBe(64) - expect(consoleSpy).toHaveBeenCalledWith('toKeypairSecure: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() - }) - - it('should able to get keypair from getTlsKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.getTlsKey() - const keypair = toKeypairSecure(result) - expect(keypair).toBeInstanceOf(Keypair) - expect(keypair.secretKey.length).toBe(64) - expect(consoleSpy).toHaveBeenCalledWith('toKeypairSecure: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() + for (const [name, adapter] of [ + ['toKeypair', toKeypair], + ['toKeypairSecure', toKeypairSecure], + ] as const) { + describe(name, () => { + it('creates a keypair from getKey', async () => { + const result: GetKeyResponse = { + __name__: 'GetKeyResponse', + key: new Uint8Array(32).fill(1), + signature_chain: [], + } + const keypair = adapter(result) + expect(keypair).toBeInstanceOf(Keypair) + expect(keypair.secretKey.length).toBe(64) + }) + + it('rejects TLS keys', async () => { + const result: GetTlsKeyResponse = { + __name__: 'GetTlsKeyResponse', + key: 'not used', + certificate_chain: [], + asUint8Array: () => new Uint8Array(), + } + expect(() => adapter(result as never)).toThrow(/TLS keys cannot be used/) + }) }) - }) + } }) diff --git a/sdk/js/src/__tests__/viem.test.ts b/sdk/js/src/__tests__/viem.test.ts index 1dbd705de..083a566e2 100644 --- a/sdk/js/src/__tests__/viem.test.ts +++ b/sdk/js/src/__tests__/viem.test.ts @@ -2,93 +2,36 @@ // // SPDX-License-Identifier: Apache-2.0 - -import { expect, describe, it, vi } from 'vitest' -import { DstackClientV0, TappdClient } from '../index' +import { expect, describe, it } from 'vitest' +import type { GetKeyResponse, GetTlsKeyResponse } from '../client-v0' import { toViemAccount, toViemAccountSecure } from '../viem' describe('viem support', () => { - describe('toViemAccount (legacy)', () => { - it('should able to get account from getKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const result = await client.getKey('/', 'test') - const account = toViemAccount(result) - - expect(account.source).toBe('privateKey') - expect(typeof account.sign).toBe('function') - expect(typeof account.signMessage).toBe('function') - }) - - it('should able to get account from deriveKey with TappdClient', async () => { - const client = new TappdClient() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.deriveKey('/', 'test') - const account = toViemAccount(result) - - expect(account.source).toBe('privateKey') - expect(typeof account.sign).toBe('function') - expect(typeof account.signMessage).toBe('function') - expect(consoleSpy).toHaveBeenCalledWith('toViemAccount: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() - }) - - it('should able to get account from getTlsKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.getTlsKey() - const account = toViemAccount(result) - - expect(account.source).toBe('privateKey') - expect(typeof account.sign).toBe('function') - expect(typeof account.signMessage).toBe('function') - expect(consoleSpy).toHaveBeenCalledWith('toViemAccount: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() - }) - }) - - describe('toViemAccountSecure', () => { - it('should able to get account from getKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const result = await client.getKey('/', 'test') - const account = toViemAccountSecure(result) - - expect(account.source).toBe('privateKey') - expect(typeof account.sign).toBe('function') - expect(typeof account.signMessage).toBe('function') - }) - - it('should able to get account from deriveKey with TappdClient', async () => { - const client = new TappdClient() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.deriveKey('/', 'test') - const account = toViemAccountSecure(result) - - expect(account.source).toBe('privateKey') - expect(typeof account.sign).toBe('function') - expect(typeof account.signMessage).toBe('function') - expect(consoleSpy).toHaveBeenCalledWith('toViemAccountSecure: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() - }) - - it('should able to get account from getTlsKey with DstackClientV0', async () => { - const client = new DstackClientV0() - const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - - const result = await client.getTlsKey() - const account = toViemAccountSecure(result) - - expect(account.source).toBe('privateKey') - expect(typeof account.sign).toBe('function') - expect(typeof account.signMessage).toBe('function') - expect(consoleSpy).toHaveBeenCalledWith('toViemAccountSecure: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - - consoleSpy.mockRestore() + for (const [name, adapter] of [ + ['toViemAccount', toViemAccount], + ['toViemAccountSecure', toViemAccountSecure], + ] as const) { + describe(name, () => { + it('creates an account from getKey', async () => { + const result: GetKeyResponse = { + __name__: 'GetKeyResponse', + key: new Uint8Array(32).fill(1), + signature_chain: [], + } + const account = adapter(result) + expect(account.source).toBe('privateKey') + expect(typeof account.sign).toBe('function') + }) + + it('rejects TLS keys', async () => { + const result: GetTlsKeyResponse = { + __name__: 'GetTlsKeyResponse', + key: 'not used', + certificate_chain: [], + asUint8Array: () => new Uint8Array(), + } + expect(() => adapter(result as never)).toThrow(/TLS keys cannot be used/) + }) }) - }) + } }) diff --git a/sdk/js/src/solana.ts b/sdk/js/src/solana.ts index 36e633451..057ccafa7 100644 --- a/sdk/js/src/solana.ts +++ b/sdk/js/src/solana.ts @@ -2,35 +2,25 @@ // // SPDX-License-Identifier: Apache-2.0 -import { sha256 } from '@noble/hashes/sha256' -import { type GetKeyResponse, type GetTlsKeyResponse } from './client-v0' +import { type GetKeyResponse } from './client-v0' import { Keypair } from '@solana/web3.js' -/** - * @deprecated use toKeypairSecure instead. This method has security concerns. - * Current implementation uses raw key material without proper hashing. - */ -export function toKeypair(keyResponse: GetTlsKeyResponse | GetKeyResponse) { - // Keep legacy behavior for GetTlsKeyResponse, but with warning. +function rejectTlsKey(keyResponse: { readonly __name__: string }): void { if (keyResponse.__name__ === 'GetTlsKeyResponse') { - console.warn('toKeypair: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - // Restored original behavior: using first 32 bytes directly - const bytes = keyResponse.asUint8Array(32) - return Keypair.fromSeed(bytes) + throw new TypeError('TLS keys cannot be used to derive Solana keypairs; use getKey()') } - return Keypair.fromSeed(keyResponse.key) } /** - * Creates a Solana Keypair from DeriveKeyResponse using secure key derivation. - * This method applies SHA256 hashing to the complete key material for enhanced security. + * @deprecated use toKeypairSecure instead. */ -export function toKeypairSecure(keyResponse: GetTlsKeyResponse | GetKeyResponse) { - // Keep legacy behavior for GetTlsKeyResponse, but with warning. - if (keyResponse.__name__ === 'GetTlsKeyResponse') { - console.warn('toKeypairSecure: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - const buf = sha256(keyResponse.asUint8Array()) - return Keypair.fromSeed(buf) - } +export function toKeypair(keyResponse: GetKeyResponse) { + rejectTlsKey(keyResponse) + return Keypair.fromSeed(keyResponse.key) +} + +/** Creates a Solana keypair from a getKey() response. */ +export function toKeypairSecure(keyResponse: GetKeyResponse) { + rejectTlsKey(keyResponse) return Keypair.fromSeed(keyResponse.key) } diff --git a/sdk/js/src/viem.ts b/sdk/js/src/viem.ts index 4f74bc524..e2bad3be8 100644 --- a/sdk/js/src/viem.ts +++ b/sdk/js/src/viem.ts @@ -2,37 +2,27 @@ // // SPDX-License-Identifier: Apache-2.0 -import { sha256 } from '@noble/hashes/sha256' -import { bytesToHex } from '@noble/hashes/utils' -import { type GetKeyResponse, type GetTlsKeyResponse } from './client-v0' +import { type GetKeyResponse } from './client-v0' import { privateKeyToAccount } from 'viem/accounts' -/** - * @deprecated use toViemAccountSecure instead. This method has security concerns. - * Current implementation uses raw key material without proper hashing. - */ -export function toViemAccount(keyResponse: GetKeyResponse | GetTlsKeyResponse) { - // Keep legacy behavior for GetTlsKeyResponse, but with warning. +function rejectTlsKey(keyResponse: { readonly __name__: string }): void { if (keyResponse.__name__ === 'GetTlsKeyResponse') { - console.warn('toViemAccount: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - const hex = Array.from(keyResponse.asUint8Array(32)).map(b => b.toString(16).padStart(2, '0')).join('') - return privateKeyToAccount(`0x${hex}`) + throw new TypeError('TLS keys cannot be used to derive Viem accounts; use getKey()') } - const hex = Array.from(keyResponse.key).map(b => b.toString(16).padStart(2, '0')).join('') - return privateKeyToAccount(`0x${hex}`) } /** - * Creates a Viem account from DeriveKeyResponse using secure key derivation. - * This method applies SHA256 hashing to the complete key material for enhanced security. + * @deprecated use toViemAccountSecure instead. */ -export function toViemAccountSecure(keyResponse: GetKeyResponse | GetTlsKeyResponse) { - // Keep legacy behavior for GetTlsKeyResponse, but with warning. - if (keyResponse.__name__ === 'GetTlsKeyResponse') { - console.warn('toViemAccountSecure: Please don\'t use `deriveKey` method to get key, use `getKey` instead.') - const hex = bytesToHex(sha256(keyResponse.asUint8Array())) - return privateKeyToAccount(`0x${hex}`) - } +export function toViemAccount(keyResponse: GetKeyResponse) { + rejectTlsKey(keyResponse) + const hex = Array.from(keyResponse.key).map(b => b.toString(16).padStart(2, '0')).join('') + return privateKeyToAccount(`0x${hex}`) +} + +/** Creates a Viem account from a getKey() response. */ +export function toViemAccountSecure(keyResponse: GetKeyResponse) { + rejectTlsKey(keyResponse) const hex = Array.from(keyResponse.key).map(b => b.toString(16).padStart(2, '0')).join('') return privateKeyToAccount(`0x${hex}`) } diff --git a/sdk/python/src/dstack_sdk/ethereum.py b/sdk/python/src/dstack_sdk/ethereum.py index 060bafb97..d673c329a 100644 --- a/sdk/python/src/dstack_sdk/ethereum.py +++ b/sdk/python/src/dstack_sdk/ethereum.py @@ -10,9 +10,6 @@ yourself. """ -import hashlib -import warnings - from eth_account import Account from eth_account.signers.local import LocalAccount @@ -20,49 +17,23 @@ from .dstack_client_v0 import GetTlsKeyResponse -def to_account(get_key_response: GetKeyResponse | GetTlsKeyResponse) -> LocalAccount: - """Create an Ethereum account from a DstackClientV0 key response. - - DEPRECATED: Use to_account_secure instead. This method has security concerns. - Current implementation uses raw key material without proper hashing. +def _reject_tls_key(response: GetKeyResponse | GetTlsKeyResponse) -> None: + if isinstance(response, GetTlsKeyResponse): + raise TypeError( + "TLS keys cannot be used to derive Ethereum accounts; use get_key()" + ) - Args: - get_key_response: Response from get_key() or get_tls_key() - Returns: - Account: Ethereum account object +def to_account(get_key_response: GetKeyResponse) -> LocalAccount: + """Create an Ethereum account from a DstackClientV0 get_key() response. + DEPRECATED: Use to_account_secure instead. """ - if isinstance(get_key_response, GetTlsKeyResponse): - warnings.warn( - "to_account: Please don't use getTlsKey method to get key, use getKey instead.", - DeprecationWarning, - stacklevel=2, - ) - key_bytes = get_key_response.as_uint8array(32) - return Account.from_key(key_bytes) # type: ignore[no-any-return] - else: # GetKeyResponse - return Account.from_key(get_key_response.decode_key()) # type: ignore[no-any-return] + _reject_tls_key(get_key_response) + return Account.from_key(get_key_response.decode_key()) # type: ignore[no-any-return] -def to_account_secure( - get_key_response: GetKeyResponse | GetTlsKeyResponse, -) -> LocalAccount: - """Create an Ethereum account using SHA256 of full key material for security.""" - if isinstance(get_key_response, GetTlsKeyResponse): - warnings.warn( - "to_account_secure: Please don't use getTlsKey method to get key, use getKey instead.", - DeprecationWarning, - stacklevel=2, - ) - try: - # Hash the complete key material with SHA256 - key_bytes = get_key_response.as_uint8array() - hashed_key = hashlib.sha256(key_bytes).digest() - return Account.from_key(hashed_key) # type: ignore[no-any-return] - except Exception as e: - raise RuntimeError( - "to_account_secure: missing SHA256 support, please upgrade your system" - ) from e - else: # GetKeyResponse - return Account.from_key(get_key_response.decode_key()) # type: ignore[no-any-return] +def to_account_secure(get_key_response: GetKeyResponse) -> LocalAccount: + """Create an Ethereum account from a DstackClientV0 get_key() response.""" + _reject_tls_key(get_key_response) + return Account.from_key(get_key_response.decode_key()) # type: ignore[no-any-return] diff --git a/sdk/python/src/dstack_sdk/solana.py b/sdk/python/src/dstack_sdk/solana.py index a33fdc953..c6acb6a8a 100644 --- a/sdk/python/src/dstack_sdk/solana.py +++ b/sdk/python/src/dstack_sdk/solana.py @@ -10,57 +10,29 @@ ``Keypair.from_seed`` yourself. """ -import hashlib -import warnings - from solders.keypair import Keypair from .dstack_client_v0 import GetKeyResponse from .dstack_client_v0 import GetTlsKeyResponse -def to_keypair(get_key_response: GetKeyResponse | GetTlsKeyResponse) -> Keypair: - """Create a Solana Keypair from a DstackClientV0 key response. - - DEPRECATED: Use to_keypair_secure instead. This method has security concerns. - Current implementation uses raw key material without proper hashing. +def _reject_tls_key(response: GetKeyResponse | GetTlsKeyResponse) -> None: + if isinstance(response, GetTlsKeyResponse): + raise TypeError( + "TLS keys cannot be used to derive Solana keypairs; use get_key()" + ) - Args: - get_key_response: Response from get_key() or get_tls_key() - Returns: - Keypair: Solana keypair object +def to_keypair(get_key_response: GetKeyResponse) -> Keypair: + """Create a Solana keypair from a DstackClientV0 get_key() response. + DEPRECATED: Use to_keypair_secure instead. """ - if isinstance(get_key_response, GetTlsKeyResponse): - warnings.warn( - "to_keypair: Please don't use getTlsKey method to get key, use getKey instead.", - DeprecationWarning, - stacklevel=2, - ) - # Restored original behavior: using first 32 bytes directly - key_bytes = get_key_response.as_uint8array(32) - return Keypair.from_seed(key_bytes) - else: # GetKeyResponse - return Keypair.from_seed(get_key_response.decode_key()) + _reject_tls_key(get_key_response) + return Keypair.from_seed(get_key_response.decode_key()) -def to_keypair_secure(get_key_response: GetKeyResponse | GetTlsKeyResponse) -> Keypair: - """Create a Solana Keypair using SHA256 of full key material for security.""" - if isinstance(get_key_response, GetTlsKeyResponse): - warnings.warn( - "to_keypair_secure: Please don't use getTlsKey method to get key, use getKey instead.", - DeprecationWarning, - stacklevel=2, - ) - try: - # Hash the complete key material with SHA256 - key_bytes = get_key_response.as_uint8array() - hashed_key = hashlib.sha256(key_bytes).digest() - return Keypair.from_seed(hashed_key) - except Exception as e: - raise RuntimeError( - "to_keypair_secure: missing SHA256 support, please upgrade your system" - ) from e - else: # GetKeyResponse - return Keypair.from_seed(get_key_response.decode_key()) +def to_keypair_secure(get_key_response: GetKeyResponse) -> Keypair: + """Create a Solana keypair from a DstackClientV0 get_key() response.""" + _reject_tls_key(get_key_response) + return Keypair.from_seed(get_key_response.decode_key()) diff --git a/sdk/python/tests/test_ethereum.py b/sdk/python/tests/test_ethereum.py index c270ae5bc..30aaaff01 100644 --- a/sdk/python/tests/test_ethereum.py +++ b/sdk/python/tests/test_ethereum.py @@ -2,8 +2,6 @@ # # SPDX-License-Identifier: Apache-2.0 -import warnings - from eth_account.signers.local import LocalAccount import pytest @@ -58,49 +56,10 @@ def test_sync_to_account_secure(): assert isinstance(account, LocalAccount) -def test_to_account_with_tls_key(): - """Test to_account with TLS key response (should show warning).""" +@pytest.mark.parametrize("adapter", [to_account, to_account_secure]) +def test_account_adapters_reject_tls_keys(adapter): from dstack_sdk import GetTlsKeyResponse - # Use mock TLS key response instead of actual server call - mock_result = GetTlsKeyResponse( - key="""-----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKONKWRjMvhgxHDmr -SY7zfjPHe3Qp8vCO9HqjzjqhXNKhRANCAAT5XHKyj7JRGHl2nQ2SltGKjQ3A7MPJ -/7JDkUxMNYhTxKqYdJZ6l1C8XrjKc7SFsVJhYgdJjLzQ3xKJz6l5jKzQ ------END PRIVATE KEY-----""", - certificate_chain=["cert1", "cert2"], - ) - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - account = to_account(mock_result) - - assert isinstance(account, LocalAccount) - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "Please don't use getTlsKey method" in str(w[0].message) - - -def test_to_account_secure_with_tls_key(): - """Test to_account_secure with TLS key response (should show warning).""" - from dstack_sdk import GetTlsKeyResponse - - # Use mock TLS key response instead of actual server call - mock_result = GetTlsKeyResponse( - key="""-----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKONKWRjMvhgxHDmr -SY7zfjPHe3Qp8vCO9HqjzjqhXNKhRANCAAT5XHKyj7JRGHl2nQ2SltGKjQ3A7MPJ -/7JDkUxMNYhTxKqYdJZ6l1C8XrjKc7SFsVJhYgdJjLzQ3xKJz6l5jKzQ ------END PRIVATE KEY-----""", - certificate_chain=["cert1", "cert2"], - ) - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - account = to_account_secure(mock_result) - - assert isinstance(account, LocalAccount) - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "Please don't use getTlsKey method" in str(w[0].message) + response = GetTlsKeyResponse(key="not used", certificate_chain=[]) + with pytest.raises(TypeError, match="TLS keys cannot be used"): + adapter(response) # type: ignore[arg-type] diff --git a/sdk/python/tests/test_solana.py b/sdk/python/tests/test_solana.py index 27cb9bf30..b42d6a0e3 100644 --- a/sdk/python/tests/test_solana.py +++ b/sdk/python/tests/test_solana.py @@ -2,77 +2,23 @@ # # SPDX-License-Identifier: Apache-2.0 -import warnings - import pytest from solders.keypair import Keypair -from dstack_sdk import AsyncDstackClientV0 -from dstack_sdk import DstackClientV0 from dstack_sdk import GetKeyResponse +from dstack_sdk import GetTlsKeyResponse from dstack_sdk.solana import to_keypair from dstack_sdk.solana import to_keypair_secure -@pytest.mark.asyncio -async def test_async_to_keypair(): - client = AsyncDstackClientV0() - result = await client.get_key("test") - assert isinstance(result, GetKeyResponse) - keypair = to_keypair(result) - assert isinstance(keypair, Keypair) - - -def test_sync_to_keypair(): - client = DstackClientV0() - result = client.get_key("test") - assert isinstance(result, GetKeyResponse) - keypair = to_keypair(result) - assert isinstance(keypair, Keypair) - - -@pytest.mark.asyncio -async def test_async_to_keypair_secure(): - client = AsyncDstackClientV0() - result = await client.get_key("test") - assert isinstance(result, GetKeyResponse) - keypair = to_keypair_secure(result) - assert isinstance(keypair, Keypair) - - -def test_sync_to_keypair_secure(): - client = DstackClientV0() - result = client.get_key("test") - assert isinstance(result, GetKeyResponse) - keypair = to_keypair_secure(result) - assert isinstance(keypair, Keypair) - - -def test_to_keypair_with_tls_key(): - """Test to_keypair with TLS key response (should show warning).""" - client = DstackClientV0() - result = client.get_tls_key() - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - keypair = to_keypair(result) - - assert isinstance(keypair, Keypair) - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "Please don't use getTlsKey method" in str(w[0].message) - - -def test_to_keypair_secure_with_tls_key(): - """Test to_keypair_secure with TLS key response (should show warning).""" - client = DstackClientV0() - result = client.get_tls_key() +@pytest.mark.parametrize("adapter", [to_keypair, to_keypair_secure]) +def test_keypair_adapters_accept_derived_keys(adapter): + response = GetKeyResponse(key="01" * 32, signature_chain=[]) + assert isinstance(adapter(response), Keypair) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - keypair = to_keypair_secure(result) - assert isinstance(keypair, Keypair) - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "Please don't use getTlsKey method" in str(w[0].message) +@pytest.mark.parametrize("adapter", [to_keypair, to_keypair_secure]) +def test_keypair_adapters_reject_tls_keys(adapter): + response = GetTlsKeyResponse(key="not used", certificate_chain=[]) + with pytest.raises(TypeError, match="TLS keys cannot be used"): + adapter(response) # type: ignore[arg-type]