mirror of
https://github.com/LORDBABUINO/stealth.git
synced 2026-06-11 14:53:30 -07:00
Feat: Backend commit
This commit is contained in:
@@ -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
|
||||
|
||||
+127
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+189
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+231
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.backend.stealth.service.dto;
|
||||
|
||||
/**
|
||||
* DTO for address response with instructions
|
||||
*/
|
||||
public record AddressResponseDTO(
|
||||
String address,
|
||||
String instructions
|
||||
) {
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package org.backend.stealth.service.dto;
|
||||
|
||||
/**
|
||||
* DTO for block height
|
||||
*/
|
||||
public record BlockHeightDTO(
|
||||
long height
|
||||
) {
|
||||
}
|
||||
|
||||
+13
@@ -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
|
||||
) {
|
||||
}
|
||||
|
||||
+13
@@ -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
|
||||
) {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user