diff --git a/backend/src/StealthBackend/BITCOIN_BACKEND.md b/backend/src/StealthBackend/BITCOIN_BACKEND.md new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/StealthBackend/TESTNET4_CONNECTION.md b/backend/src/StealthBackend/TESTNET4_CONNECTION.md new file mode 100644 index 0000000..413fc2e --- /dev/null +++ b/backend/src/StealthBackend/TESTNET4_CONNECTION.md @@ -0,0 +1,231 @@ +# Conexão com Bitcoin Testnet4 + +Este guia mostra como conectar na blockchain Bitcoin testnet4 usando BDK-JVM. + +## 🚀 Como Funciona + +### 1. Executar o Exemplo de Conexão + +#### Opção A: Via Código Java +Execute a classe `BitcoinConnectionExample`: + +```bash +cd /home/herbe/src/stealth/backend/src/StealthBackend +mvn compile +mvn exec:java -Dexec.mainClass="org.backend.stealth.service.BitcoinConnectionExample" +``` + +#### Opção B: Via REST API +Inicie o servidor Quarkus: + +```bash +./mvnw quarkus:dev +``` + +Acesse os endpoints: + +**1. Conectar na blockchain testnet4:** +```bash +curl -X POST http://localhost:8080/api/testnet4/connect +``` + +**2. Obter informações da blockchain:** +```bash +curl http://localhost:8080/api/testnet4/info +``` + +**3. Gerar novo endereço:** +```bash +curl http://localhost:8080/api/testnet4/address +``` + +**4. Verificar saldo:** +```bash +curl http://localhost:8080/api/testnet4/balance +``` + +**5. Sincronizar wallet:** +```bash +curl -X POST http://localhost:8080/api/testnet4/sync +``` + +## 📊 O Que o Código Faz + +### 1. Configuração da Network +```java +Network network = Network.TESTNET; +``` +Define que vamos usar a testnet do Bitcoin. + +### 2. Configuração do Esplora +```java +String esploraUrl = "https://mempool.space/testnet4/api"; +EsploraConfig esploraConfig = new EsploraConfig( + esploraUrl, // URL do servidor Esplora + null, // Proxy (null = sem proxy) + 5L, // Timeout em segundos + null, // Stop gap + null // Timeout para requests longos +); +``` +Esplora é uma API que permite acessar dados da blockchain sem rodar um nó completo. + +### 3. Conexão com Blockchain +```java +BlockchainConfig blockchainConfig = BlockchainConfig.esplora(esploraConfig); +Blockchain blockchain = new Blockchain(blockchainConfig); +``` +Cria a conexão com a blockchain testnet4. + +### 4. Verificar Conexão +```java +long height = blockchain.getHeight(); +String blockHash = blockchain.getBlockHash(height); +``` +Obtém a altura atual (número de blocos) e o hash do último bloco. + +### 5. Criar Wallet +```java +Mnemonic mnemonic = new Mnemonic(WordCount.WORDS12); +DescriptorSecretKey descriptorSecretKey = new DescriptorSecretKey(network, mnemonic, null); + +String descriptor = "wpkh(" + descriptorSecretKey.asString() + "/84'/1'/0'/0/*)"; +String changeDescriptor = "wpkh(" + descriptorSecretKey.asString() + "/84'/1'/0'/1/*)"; + +Wallet wallet = new Wallet(descriptor, changeDescriptor, network, databaseConfig); +``` +Cria uma wallet HD (Hierarchical Deterministic) usando BIP84 (native segwit). + +### 6. Sincronizar Wallet +```java +wallet.sync(blockchain, null); +``` +Sincroniza a wallet com a blockchain para obter transações e saldo. + +### 7. Gerar Endereço +```java +AddressInfo addressInfo = wallet.getAddress(AddressIndex.NEW); +``` +Gera um novo endereço para receber bitcoins. + +## 🔑 Componentes Principais + +### Blockchain +- Representa a conexão com a rede Bitcoin +- Permite consultar blocos, altura, e broadcast de transações + +### Wallet +- Gerencia chaves privadas e endereços +- Rastreia saldo e transações +- Cria e assina transações + +### Mnemonic +- 12 palavras que permitem recuperar a wallet +- **MUITO IMPORTANTE**: Guarde com segurança! +- Qualquer pessoa com essas palavras tem acesso aos fundos + +### Descriptor +- Define a estrutura da wallet +- `wpkh` = Witness Public Key Hash (native segwit) +- `/84'/1'/0'/0/*` = Caminho BIP84 para testnet + +## 💰 Como Obter Testnet4 Bitcoins + +1. Execute o código para gerar um endereço +2. Copie o endereço gerado (começa com `tb1...`) +3. Acesse um faucet de testnet4: + - https://mempool.space/testnet4 + - Procure por "faucet" na página +4. Cole seu endereço e solicite bitcoins +5. Aguarde alguns minutos para confirmação +6. Sincronize a wallet e verifique o saldo + +## 🔧 Estrutura do Código + +``` +src/main/java/org/backend/stealth/ +├── service/ +│ ├── BitcoinController.java # Controller principal com lógica de conexão +│ └── BitcoinConnectionExample.java # Exemplo standalone +├── controller/ +│ └── BitcoinTestnet4Resource.java # REST API endpoints +└── service/dto/ + ├── BlockchainInfoDTO.java # DTO para info da blockchain + ├── AddressResponseDTO.java # DTO para endereços + ├── BalanceDTO.java # DTO para saldo + ├── ErrorDTO.java # DTO para erros + └── MessageDTO.java # DTO para mensagens +``` + +## 📝 Exemplo de Resposta + +### GET /api/testnet4/info +```json +{ + "network": "TESTNET4", + "height": 150234, + "latestBlockHash": "00000000000000123abc...", + "esploraUrl": "https://mempool.space/testnet4/api" +} +``` + +### GET /api/testnet4/address +```json +{ + "address": "tb1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", + "instructions": "Use um faucet para receber testnet4 bitcoins: https://mempool.space/testnet4" +} +``` + +### GET /api/testnet4/balance +```json +{ + "total": 100000, + "confirmed": 100000, + "immature": 0, + "trustedPending": 0, + "untrustedPending": 0 +} +``` + +## ⚠️ Notas Importantes + +1. **Testnet4**: Esta é uma rede de testes. Os bitcoins não têm valor real. +2. **Mnemonic**: Sempre guarde as 12 palavras em local seguro. +3. **Esplora**: Dependemos de um servidor externo. Se estiver lento, pode ser problema na API. +4. **Sincronização**: A primeira sincronização pode demorar alguns segundos. + +## 🐛 Troubleshooting + +### Erro: "Connection timeout" +- Verifique sua conexão com internet +- Tente usar outro servidor Esplora +- Aumente o timeout na configuração + +### Erro: "Invalid descriptor" +- Verifique se está usando Network.TESTNET +- Confirme que o descriptor está correto + +### Saldo sempre zero +- Aguarde a confirmação da transação (10-60 minutos) +- Sincronize a wallet novamente +- Verifique se usou o endereço correto no faucet + +## 📚 Recursos Adicionais + +- [BDK Documentation](https://bitcoindevkit.org/) +- [Mempool.space Testnet4](https://mempool.space/testnet4) +- [BIP84 Specification](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki) +- [Bitcoin Testnet Guide](https://developer.bitcoin.org/examples/testing.html) + +## ✅ Checklist de Teste + +- [ ] Executar `BitcoinConnectionExample` +- [ ] Ver log de conexão bem-sucedida +- [ ] Verificar altura da blockchain +- [ ] Salvar as 12 palavras do mnemonic +- [ ] Copiar endereço gerado +- [ ] Solicitar bitcoins no faucet +- [ ] Sincronizar wallet +- [ ] Verificar saldo atualizado + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/controller/Testnet4Resource.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/controller/Testnet4Resource.java new file mode 100644 index 0000000..8afd396 --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/controller/Testnet4Resource.java @@ -0,0 +1,127 @@ +package org.backend.stealth.controller; + +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.backend.stealth.domain.repository.BitcoinRepository; +import org.backend.stealth.service.dto.*; +import org.bitcoindevkit.Balance; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +/** + * REST API para conexão com Bitcoin Testnet4 + */ +@Path("/api/testnet4") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Tag(name = "Bitcoin Testnet4", description = "Operações com blockchain Bitcoin testnet4") +public class Testnet4Resource { + + @Inject + BitcoinRepository bitcoinRepository; + + @GET + @Path("/status") + @Operation(summary = "Verificar status da conexão", description = "Verifica se está conectado na blockchain testnet4") + public Response getStatus() { + try { + boolean connected = bitcoinRepository.isConnected(); + long height = connected ? bitcoinRepository.getHeight() : -1; + + String status = connected ? "✅ Conectado" : "❌ Desconectado"; + + return Response.ok(new Testnet4StatusDTO( + connected, + status, + height, + "https://mempool.space/testnet4/api" + )).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro: " + e.getMessage())) + .build(); + } + } + + @GET + @Path("/height") + @Operation(summary = "Obter altura da blockchain", description = "Retorna o número atual de blocos") + public Response getHeight() { + try { + long height = bitcoinRepository.getHeight(); + return Response.ok(new BlockHeightDTO(height)).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro: " + e.getMessage())) + .build(); + } + } + + @GET + @Path("/address") + @Operation(summary = "Gerar novo endereço", description = "Gera um novo endereço para receber testnet4 bitcoins") + public Response getNewAddress() { + try { + String address = bitcoinRepository.getNewAddress(); + + if (address == null) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro ao gerar endereço")) + .build(); + } + + return Response.ok(new AddressResponseDTO( + address, + "Use um faucet para receber testnet4 bitcoins: https://mempool.space/testnet4" + )).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro: " + e.getMessage())) + .build(); + } + } + + @GET + @Path("/balance") + @Operation(summary = "Verificar saldo", description = "Retorna o saldo da wallet") + public Response getBalance() { + try { + Balance balance = bitcoinRepository.getBalance(); + + if (balance == null) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro ao obter saldo")) + .build(); + } + + return Response.ok(new BalanceDTO( + balance.total(), + balance.confirmed(), + balance.immature(), + balance.trustedPending(), + balance.untrustedPending() + )).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro: " + e.getMessage())) + .build(); + } + } + + @POST + @Path("/sync") + @Operation(summary = "Sincronizar wallet", description = "Sincroniza a wallet com a blockchain") + public Response sync() { + try { + bitcoinRepository.syncWallet(); + return Response.ok(new MessageDTO("✅ Wallet sincronizada com sucesso!")).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO("Erro: " + e.getMessage())) + .build(); + } + } +} + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/domain/repository/BitcoinRepository.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/domain/repository/BitcoinRepository.java new file mode 100644 index 0000000..1451640 --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/domain/repository/BitcoinRepository.java @@ -0,0 +1,189 @@ +package org.backend.stealth.domain.repository; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import org.bitcoindevkit.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Repository for Bitcoin blockchain connection (Testnet4) + */ +@ApplicationScoped +public class BitcoinRepository { + + private static final Logger logger = LoggerFactory.getLogger(BitcoinRepository.class); + + private Blockchain blockchain; + private Wallet wallet; + private final Network network = Network.TESTNET; + private final String esploraUrl = "https://mempool.space/testnet4/api"; + + @PostConstruct + public void init() { + logger.info("🚀 Iniciando conexão com Bitcoin Testnet4..."); + connectBlockchain(); + } + + /** + * Connect to Bitcoin blockchain + */ + public void connectBlockchain() { + try { + logger.info("📡 Conectando em: {}", esploraUrl); + + // Create Esplora config - using correct Kotlin UByte conversion + EsploraConfig esploraConfig = new EsploraConfig( + esploraUrl, + null, + kotlin.UByte.valueOf((byte) 5), // timeout 5 seconds + (long) 100, + null + ); + + // Create blockchain config using correct method + BlockchainConfig blockchainConfig = new BlockchainConfig.Esplora(esploraConfig); + + // Create blockchain + blockchain = new Blockchain(blockchainConfig); + + logger.info("✅ Blockchain conectado com sucesso!"); + + // Get height + long height = blockchain.height(); + logger.info("📊 Altura da blockchain: {} blocos", height); + + // Initialize wallet + createWallet(); + + } catch (Exception e) { + logger.error("❌ Erro ao conectar: {}", e.getMessage(), e); + } + } + + /** + * Create a new wallet + */ + private void createWallet() { + try { + logger.info("🔑 Criando wallet..."); + + // Generate mnemonic + Mnemonic mnemonic = new Mnemonic(WordCount.WORDS12); + logger.warn("⚠️ GUARDE ESTAS PALAVRAS:"); + logger.warn("📝 {}", mnemonic.asString()); + + // Create descriptor secret key + DescriptorSecretKey descriptorSecretKey = new DescriptorSecretKey( + network, + mnemonic, + null + ); + + // Create descriptors (BIP84) + String descStr = "wpkh(" + descriptorSecretKey.asString() + "/84'/1'/0'/0/*)"; + String changeDescStr = "wpkh(" + descriptorSecretKey.asString() + "/84'/1'/0'/1/*)"; + + Descriptor descriptor = new Descriptor(descStr, network); + Descriptor changeDescriptor = new Descriptor(changeDescStr, network); + + // Create database config + DatabaseConfig databaseConfig = DatabaseConfig.memory(); + + // Create wallet + wallet = new Wallet(descriptor, changeDescriptor, network, databaseConfig); + + logger.info("✅ Wallet criada!"); + + // Sync and show address + syncWallet(); + showAddress(); + + } catch (Exception e) { + logger.error("❌ Erro ao criar wallet: {}", e.getMessage(), e); + } + } + + /** + * Sync wallet with blockchain + */ + public void syncWallet() { + try { + if (wallet != null && blockchain != null) { + logger.info("🔄 Sincronizando..."); + wallet.sync(blockchain, null); + logger.info("✅ Sincronizado!"); + + Balance balance = wallet.balance(); + logger.info("💵 Saldo: {} satoshis", balance.total()); + } + } catch (Exception e) { + logger.error("❌ Erro ao sincronizar: {}", e.getMessage(), e); + } + } + + /** + * Show a receiving address + */ + private void showAddress() { + try { + AddressInfo addressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL); + logger.info("💰 Endereço: {}", addressInfo.address().asString()); + logger.info("📍 Para receber testnet coins: https://mempool.space/testnet4"); + } catch (Exception e) { + logger.error("Erro ao gerar endereço: {}", e.getMessage(), e); + } + } + + /** + * Get blockchain height + */ + public long getHeight() { + return blockchain != null ? blockchain.height() : -1; + } + + /** + * Get new address + */ + public String getNewAddress() { + try { + if (wallet == null) return null; + AddressInfo addressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL); + return addressInfo.address().asString(); + } catch (Exception e) { + logger.error("Erro: {}", e.getMessage()); + return null; + } + } + + /** + * Get balance + */ + public Balance getBalance() { + try { + if (wallet == null) return null; + syncWallet(); + return wallet.balance(); + } catch (Exception e) { + logger.error("Erro: {}", e.getMessage()); + return null; + } + } + + public boolean isConnected() { + return blockchain != null; + } + + public Blockchain getBlockchain() { + return blockchain; + } + + public Wallet getWallet() { + return wallet; + } + + public Network getNetwork() { + return network; + } +} + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/Testnet4ConnectionService.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/Testnet4ConnectionService.java new file mode 100644 index 0000000..7834d98 --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/Testnet4ConnectionService.java @@ -0,0 +1,231 @@ +package org.backend.stealth.service; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import org.bitcoindevkit.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Service for connecting to Bitcoin Testnet4 blockchain + * This is a simplified version that connects to testnet4 + */ +@ApplicationScoped +public class Testnet4ConnectionService { + + private static final Logger logger = LoggerFactory.getLogger(Testnet4ConnectionService.class); + + private Blockchain blockchain; + private Wallet wallet; + private Network network = Network.TESTNET; + private String esploraUrl = "https://mempool.space/testnet4/api"; + + /** + * Initialize connection on startup + */ + @PostConstruct + public void init() { + logger.info("🚀 Iniciando conexão com Bitcoin Testnet4..."); + connectToBlockchain(); + } + + /** + * Connect to Bitcoin testnet4 blockchain + */ + public boolean connectToBlockchain() { + try { + logger.info("📡 Configurando conexão Esplora: {}", esploraUrl); + + // Create Esplora configuration + EsploraConfig esploraConfig = new EsploraConfig( + esploraUrl, + null, // proxy + (byte) 5, // timeout in seconds + (long) 100, // stop_gap + null // timeout for long requests + ); + + // Create blockchain config + BlockchainConfig blockchainConfig = BlockchainConfig.newEsploraConfig(esploraConfig); + + // Initialize blockchain connection + blockchain = new Blockchain(blockchainConfig); + + logger.info("✅ Blockchain conectado com sucesso!"); + + // Get blockchain info + long height = blockchain.getHeight(); + logger.info("📊 Altura atual da blockchain: {} blocos", height); + + // Initialize a simple wallet + initializeWallet(); + + return true; + + } catch (Exception e) { + logger.error("❌ Erro ao conectar na blockchain: {}", e.getMessage(), e); + return false; + } + } + + /** + * Initialize a simple wallet + */ + private void initializeWallet() { + try { + logger.info("🔑 Inicializando wallet..."); + + // Generate mnemonic + Mnemonic mnemonic = new Mnemonic(WordCount.WORDS12); + String mnemonicWords = mnemonic.asString(); + logger.warn("⚠️ IMPORTANTE - Guarde essas palavras com segurança:"); + logger.warn("📝 {}", mnemonicWords); + + // Create descriptor secret key + DescriptorSecretKey descriptorSecretKey = new DescriptorSecretKey( + network, + mnemonic, + null // no password + ); + + // Create descriptors (BIP84 - native segwit) + String descriptorStr = "wpkh(" + descriptorSecretKey.asString() + "/84'/1'/0'/0/*)"; + String changeDescriptorStr = "wpkh(" + descriptorSecretKey.asString() + "/84'/1'/0'/1/*)"; + + // Create descriptor objects + Descriptor descriptor = new Descriptor(descriptorStr, network); + Descriptor changeDescriptor = new Descriptor(changeDescriptorStr, network); + + // Create database config + DatabaseConfig databaseConfig = new DatabaseConfig.Memory(); + + // Create wallet + wallet = new Wallet( + descriptor, + changeDescriptor, + network, + databaseConfig + ); + + logger.info("✅ Wallet criada com sucesso!"); + + // Sync wallet + syncWallet(); + + // Generate and show address + AddressInfo addressInfo = wallet.getAddress(new AddressIndex.New()); + logger.info("💰 Endereço para receber: {}", addressInfo.getAddress()); + logger.info("📍 Índice: {}", addressInfo.getIndex()); + + } catch (Exception e) { + logger.error("❌ Erro ao criar wallet: {}", e.getMessage(), e); + } + } + + /** + * Synchronize wallet with blockchain + */ + public void syncWallet() { + try { + if (wallet == null || blockchain == null) { + logger.error("Wallet ou blockchain não inicializados"); + return; + } + + logger.info("🔄 Sincronizando wallet..."); + wallet.sync(blockchain, null); + logger.info("✅ Sincronização completa!"); + + // Show balance + Balance balance = wallet.getBalance(); + logger.info("💵 Saldo total: {} satoshis", balance.getTotal()); + logger.info("💵 Saldo confirmado: {} satoshis", balance.getConfirmed()); + + } catch (Exception e) { + logger.error("❌ Erro ao sincronizar: {}", e.getMessage(), e); + } + } + + /** + * Get blockchain height + */ + public long getBlockchainHeight() { + try { + if (blockchain == null) { + throw new IllegalStateException("Blockchain não conectado"); + } + return blockchain.getHeight(); + } catch (Exception e) { + logger.error("Erro ao obter altura: {}", e.getMessage()); + return -1; + } + } + + /** + * Get new receiving address + */ + public String getNewAddress() { + try { + if (wallet == null) { + throw new IllegalStateException("Wallet não inicializada"); + } + AddressInfo addressInfo = wallet.getAddress(new AddressIndex.New()); + return addressInfo.getAddress(); + } catch (Exception e) { + logger.error("Erro ao gerar endereço: {}", e.getMessage()); + return null; + } + } + + /** + * Get wallet balance + */ + public Balance getBalance() { + try { + if (wallet == null) { + throw new IllegalStateException("Wallet não inicializada"); + } + syncWallet(); + return wallet.getBalance(); + } catch (Exception e) { + logger.error("Erro ao obter saldo: {}", e.getMessage()); + return null; + } + } + + /** + * Check if connected + */ + public boolean isConnected() { + return blockchain != null; + } + + /** + * Get blockchain instance + */ + public Blockchain getBlockchain() { + return blockchain; + } + + /** + * Get wallet instance + */ + public Wallet getWallet() { + return wallet; + } + + /** + * Get network + */ + public Network getNetwork() { + return network; + } + + /** + * Get Esplora URL + */ + public String getEsploraUrl() { + return esploraUrl; + } +} + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/AddressResponseDTO.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/AddressResponseDTO.java new file mode 100644 index 0000000..26b49fe --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/AddressResponseDTO.java @@ -0,0 +1,11 @@ +package org.backend.stealth.service.dto; + +/** + * DTO for address response with instructions + */ +public record AddressResponseDTO( + String address, + String instructions +) { +} + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/BlockHeightDTO.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/BlockHeightDTO.java new file mode 100644 index 0000000..ef7c844 --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/BlockHeightDTO.java @@ -0,0 +1,10 @@ +package org.backend.stealth.service.dto; + +/** + * DTO for block height + */ +public record BlockHeightDTO( + long height +) { +} + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/BlockchainInfoDTO.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/BlockchainInfoDTO.java new file mode 100644 index 0000000..1e356f9 --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/BlockchainInfoDTO.java @@ -0,0 +1,13 @@ +package org.backend.stealth.service.dto; + +/** + * DTO for blockchain information + */ +public record BlockchainInfoDTO( + String network, + long height, + String latestBlockHash, + String esploraUrl +) { +} + diff --git a/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/Testnet4StatusDTO.java b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/Testnet4StatusDTO.java new file mode 100644 index 0000000..e911752 --- /dev/null +++ b/backend/src/StealthBackend/src/main/java/org/backend/stealth/service/dto/Testnet4StatusDTO.java @@ -0,0 +1,13 @@ +package org.backend.stealth.service.dto; + +/** + * DTO for testnet4 connection status + */ +public record Testnet4StatusDTO( + boolean connected, + String status, + long blockHeight, + String esploraUrl +) { +} +