caravan-x
Version:
A terminal-based utility for managing Caravan multisig wallets in regtest mode. This tool simplifies development and testing with Caravan by providing an easy-to-use interface
497 lines (496 loc) • 18.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VisualizationServer = void 0;
const http = __importStar(require("http"));
const express_1 = __importDefault(require("express"));
const socket_io_1 = require("socket.io");
const terminal_1 = require("../../utils/terminal");
// Content types for different file extensions
const CONTENT_TYPES = {
".html": "text/html",
".css": "text/css",
".js": "text/javascript",
".json": "application/json",
".png": "image/png",
".jpg": "image/jpeg",
".svg": "image/svg+xml",
".ico": "image/x-icon",
};
/**
* Enhanced HTTP Server for blockchain visualization with WebSockets
*/
class VisualizationServer {
constructor(blockchainData, staticDir, port = 3000) {
this.updateInterval = null;
this.connectedClients = 0;
this.blockchainData = blockchainData;
this.staticDir = staticDir;
this.port = port;
// Initialize Express app
this.app = (0, express_1.default)();
// Configure middleware
this.configureMiddleware();
// Create HTTP server
this.server = http.createServer(this.app);
// Initialize Socket.io
this.io = new socket_io_1.Server(this.server);
// Configure Socket.io events
this.configureSocketEvents();
// Configure API routes
this.configureRoutes();
}
/**
* Configure Express middleware
*/
configureMiddleware() {
// Serve static files
this.app.use(express_1.default.static(this.staticDir));
// Parse JSON body
this.app.use(express_1.default.json());
}
/**
* Configure Socket.io events
*/
configureSocketEvents() {
this.io.on("connection", (socket) => {
this.connectedClients++;
console.log(`Client connected. Total clients: ${this.connectedClients}`);
// Send initial data
this.sendInitialData(socket);
// Handle disconnection
socket.on("disconnect", () => {
this.connectedClients--;
console.log(`Client disconnected. Total clients: ${this.connectedClients}`);
});
// Handle client events
this.configureClientEvents(socket);
});
}
/**
* Configure client-specific events
*/
configureClientEvents(socket) {
// Handle mining request
socket.on("mine_block", async (data) => {
try {
console.log("Mining request received:", data);
// Notify all clients that mining has started
this.io.emit("mining_started", {
blocks: data.blocks,
address: data.address || "default_address",
});
// Call the mining method (this would be implemented in your blockchain service)
const blockHashes = await this.blockchainData.mineBlocks(data.blocks, data.address);
// Notify all clients that mining is complete
this.io.emit("mining_complete", {
blockHashes,
});
// Update all clients with new blockchain data
this.broadcastBlockchainUpdate();
}
catch (error) {
console.error("Error mining blocks:", error);
socket.emit("error", {
message: "Error mining blocks",
error: error.message,
});
}
});
// Handle transaction creation request
socket.on("create_transaction", async (data) => {
try {
console.log("Transaction creation request received:", data);
// Call the transaction creation method (this would be implemented in your blockchain service)
const result = await this.blockchainData.createTransaction(data.fromWallet, data.toAddress, data.amount);
// Notify the client that the transaction was created
socket.emit("transaction_created", result);
// Notify all clients about the new transaction
this.io.emit("new_transaction", {
txid: result.txid,
fromWallet: data.fromWallet,
toAddress: data.toAddress,
amount: data.amount,
});
// Update all clients with new blockchain data
this.broadcastBlockchainUpdate();
}
catch (error) {
console.error("Error creating transaction:", error);
socket.emit("error", {
message: "Error creating transaction",
error: error.message,
});
}
});
}
/**
* Configure API routes
*/
configureRoutes() {
// Get blockchain data
this.app.get("/api/blockchain", async (req, res) => {
try {
const data = await this.blockchainData.getBlockchainVisualizationData();
res.json(data);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get block details
this.app.get("/api/block/:hash", async (req, res) => {
try {
const block = await this.blockchainData.getBlock(req.params.hash);
console.log(terminal_1.colors.info("/api/blockchain2"));
res.json(block);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get transaction details
this.app.get("/api/tx/:txid", async (req, res) => {
try {
const tx = await this.blockchainData.getTransaction(req.params.txid);
console.log(terminal_1.colors.info("/api/blockchain3"));
res.json(tx);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get mempool data
this.app.get("/api/mempool", async (req, res) => {
try {
const txids = await this.blockchainData.getMempoolTransactions();
const info = await this.blockchainData.getMempoolInfo();
res.json({ txids, info });
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get chain info
this.app.get("/api/chain-info", async (req, res) => {
try {
const info = await this.blockchainData.getChainInfo();
res.json(info);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get recent blocks
this.app.get("/api/recent-blocks", async (req, res) => {
try {
const count = parseInt(req.query.count || "10");
const blocks = await this.blockchainData.getRecentBlocks(count);
res.json(blocks);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Mine blocks (POST endpoint)
this.app.post("/api/mine-block", async (req, res) => {
try {
const { blocks, address } = req.body;
// Validate input
if (!blocks || isNaN(blocks) || blocks <= 0) {
return res.status(400).json({ error: "Invalid number of blocks" });
}
// Notify all clients that mining has started
this.io.emit("mining_started", {
blocks,
address: address || "default_address",
});
// Call the mining method (this would be implemented in your blockchain service)
const blockHashes = await this.blockchainData.mineBlocks(blocks, address);
// Notify all clients that mining is complete
this.io.emit("mining_complete", {
blockHashes,
});
// Update all clients with new blockchain data
this.broadcastBlockchainUpdate();
res.json({ success: true, blockHashes });
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Create transaction (POST endpoint)
this.app.post("/api/create-transaction", async (req, res) => {
try {
const { fromWallet, toAddress, amount } = req.body;
// Validate input
if (!fromWallet ||
!toAddress ||
!amount ||
isNaN(amount) ||
amount <= 0) {
return res.status(400).json({ error: "Invalid transaction data" });
}
// Call the transaction creation method (this would be implemented in your blockchain service)
const result = await this.blockchainData.createTransaction(fromWallet, toAddress, amount);
// Notify all clients about the new transaction
this.io.emit("new_transaction", {
txid: result.txid,
fromWallet,
toAddress,
amount,
});
// Update all clients with new blockchain data
this.broadcastBlockchainUpdate();
res.json(result);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get a new address from a wallet
this.app.get("/api/new-address", async (req, res) => {
try {
const wallet = req.query.wallet;
if (!wallet) {
return res
.status(400)
.json({ error: "Wallet parameter is required" });
}
// Call the Bitcoin service to get a new address
const address = await this.blockchainData.getNewAddress(wallet);
res.json({ address });
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// Get wallet list
this.app.get("/api/wallets", async (req, res) => {
try {
const wallets = await this.blockchainData.getWalletList();
res.json({ wallets });
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
this.app.get("/api/minecraft/blockchain", async (req, res) => {
try {
const data = await this.blockchainData.getBlockchainVisualizationData();
res.json(data);
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// API endpoint for mining blocks in Minecraft view
this.app.post("/api/minecraft/mine", async (req, res) => {
try {
const { x, y, z, tool } = req.body;
// Simulate mining success based on tool and block type
const success = Math.random() > 0.3; // 70% success rate
if (success) {
// Return mining result
res.json({
success: true,
material: "stone",
amount: Math.floor(Math.random() * 3) + 1,
});
}
else {
// Mining failed
res.json({
success: false,
message: "Mining failed. Try a different tool or location.",
});
}
}
catch (error) {
console.error("API error:", error);
res.status(500).json({ error: "Internal server error" });
}
});
// API endpoint for player inventory
this.app.get("/api/minecraft/inventory", (req, res) => {
// Return default inventory
res.json({
bitcoin: 0,
tokens: {
BlockToken: 10,
ChainCoin: 5,
},
tools: {
pickaxe: { level: 1, durability: 100 },
shovel: { level: 1, durability: 100 },
},
materials: {
stone: 0,
goldOre: 0,
},
});
});
// API endpoint for quests
this.app.get("/api/minecraft/quests", (req, res) => {
// Return available quests
res.json([
{
id: "mining_101",
title: "Mining 101",
description: "Mine your first block to earn a reward",
reward: { bitcoin: 0.1 },
progress: 0,
goal: 1,
complete: false,
},
{
id: "transaction_tracker",
title: "Transaction Tracker",
description: "Interact with 5 transaction characters",
reward: { tokens: { ChainCoin: 10 } },
progress: 0,
goal: 5,
complete: false,
},
{
id: "block_explorer",
title: "Block Explorer",
description: "Visit 10 different block buildings",
reward: { materials: { goldOre: 5 } },
progress: 0,
goal: 10,
complete: false,
},
]);
});
}
/**
* Send initial data to a newly connected client
*/
async sendInitialData(socket) {
try {
const data = await this.blockchainData.getBlockchainVisualizationData();
socket.emit("blockchain_update", data);
}
catch (error) {
console.error("Error sending initial data:", error);
}
}
/**
* Broadcast blockchain update to all connected clients
*/
async broadcastBlockchainUpdate() {
try {
const data = await this.blockchainData.getBlockchainVisualizationData();
this.io.emit("blockchain_update", data);
}
catch (error) {
console.error("Error broadcasting blockchain update:", error);
}
}
/**
* Start the real-time updates
*/
startRealTimeUpdates() {
// Clear any existing interval
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
// Set up periodic updates (every 5 seconds)
this.updateInterval = setInterval(async () => {
// Only send updates if there are connected clients
if (this.connectedClients > 0) {
try {
await this.broadcastBlockchainUpdate();
}
catch (error) {
console.error("Error during periodic update:", error);
}
}
}, 5000);
}
/**
* Stop the real-time updates
*/
stopRealTimeUpdates() {
if (this.updateInterval) {
clearInterval(this.updateInterval);
this.updateInterval = null;
}
}
/**
* Start the server
*/
async start() {
return new Promise((resolve) => {
this.server.listen(this.port, () => {
console.log(`Visualization server running at http://localhost:${this.port}/`);
this.startRealTimeUpdates();
resolve();
});
});
}
/**
* Stop the server
*/
async stop() {
this.stopRealTimeUpdates();
return new Promise((resolve, reject) => {
this.server.close((err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
}
}
exports.VisualizationServer = VisualizationServer;