@gluneau/hive-mcp-server
Version:
An MCP server that enables AI assistants to interact with the Hive blockchain
230 lines • 12.1 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.encryptMessage = encryptMessage;
exports.decryptMessage = decryptMessage;
exports.sendEncryptedMessage = sendEncryptedMessage;
exports.getEncryptedMessages = getEncryptedMessages;
// Updated messaging tools implementation with fix for encryption/decryption
const dhive_1 = require("@hiveio/dhive");
const client_1 = __importDefault(require("../config/client"));
const config_1 = __importDefault(require("../config"));
const error_1 = require("../utils/error");
const response_1 = require("../utils/response");
const logger_1 = __importDefault(require("../utils/logger"));
// Helper function to get a public memo key for a Hive account
function getMemoPublicKey(username) {
return __awaiter(this, void 0, void 0, function* () {
try {
const accounts = yield client_1.default.database.getAccounts([username]);
if (accounts.length === 0) {
throw new Error(`User ${username} not found`);
}
return accounts[0].memo_key;
}
catch (error) {
throw new Error(`Error fetching memo key for ${username}: ${error instanceof Error ? error.message : String(error)}`);
}
});
}
// Encrypt a message
function encryptMessage(params) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Get credentials from environment variables
const senderPrivateKey = config_1.default.hive.memoKey;
if (!senderPrivateKey) {
return (0, response_1.errorResponse)('Error: HIVE_MEMO_KEY environment variable is not set. Encryption requires your private memo key.');
}
// Get recipient's public memo key
const recipientPublicKey = yield getMemoPublicKey(params.recipient);
// Encrypt message - FIX: prepend # to message since Memo.encode checks for this prefix
const encryptedMessage = dhive_1.Memo.encode(dhive_1.PrivateKey.fromString(senderPrivateKey), recipientPublicKey, '#' + params.message // Add the # prefix required by Memo.encode
);
return (0, response_1.successJson)({
success: true,
recipient: params.recipient,
encrypted_message: encryptedMessage,
note: "This encrypted message can only be decrypted by the recipient using their private memo key."
});
}
catch (error) {
return (0, response_1.errorResponse)((0, error_1.handleError)(error, 'encrypt_message'));
}
});
}
// Decrypt a message
function decryptMessage(params) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Get credentials from environment variables
const recipientPrivateKey = config_1.default.hive.memoKey;
if (!recipientPrivateKey) {
return (0, response_1.errorResponse)('Error: HIVE_MEMO_KEY environment variable is not set. Decryption requires your private memo key.');
}
// Get sender's public memo key
const senderPublicKey = yield getMemoPublicKey(params.sender);
try {
// Attempt to decrypt message
const decryptedMessage = dhive_1.Memo.decode(dhive_1.PrivateKey.fromString(recipientPrivateKey), params.encrypted_message);
// FIX: Remove the # prefix that Memo.decode adds to the decrypted message
const cleanMessage = decryptedMessage.startsWith('#')
? decryptedMessage.substring(1)
: decryptedMessage;
return (0, response_1.successJson)({
success: true,
sender: params.sender,
decrypted_message: cleanMessage
});
}
catch (decryptError) {
return (0, response_1.errorResponse)(`Failed to decrypt message: ${decryptError instanceof Error ? decryptError.message : String(decryptError)}. This could be because the message was not encrypted for you, or the sender information is incorrect.`);
}
}
catch (error) {
return (0, response_1.errorResponse)((0, error_1.handleError)(error, 'decrypt_message'));
}
});
}
// Send an encrypted message (combines encryption with token sending)
function sendEncryptedMessage(params) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Get credentials from environment variables
const username = config_1.default.hive.username;
const activeKey = config_1.default.hive.activeKey;
const memoKey = config_1.default.hive.memoKey;
if (!username || !activeKey) {
return (0, response_1.errorResponse)('Error: HIVE_USERNAME or HIVE_ACTIVE_KEY environment variables are not set. Sending requires an active key.');
}
if (!memoKey) {
return (0, response_1.errorResponse)('Error: HIVE_MEMO_KEY environment variable is not set. Encryption requires your private memo key.');
}
// Get recipient's public memo key
const recipientPublicKey = yield getMemoPublicKey(params.recipient);
// Encrypt message - FIX: prepend # to message since Memo.encode checks for this prefix
const encryptedMessage = dhive_1.Memo.encode(dhive_1.PrivateKey.fromString(memoKey), recipientPublicKey, '#' + params.message // Add the # prefix required by Memo.encode
);
// Format the amount with 3 decimal places and append HIVE
const formattedAmount = `${params.amount.toFixed(3)} HIVE`;
// Create the transfer operation
const transfer = {
from: username,
to: params.recipient,
amount: formattedAmount,
memo: encryptedMessage,
};
// Broadcast the transfer using active key
const result = yield client_1.default.broadcast.transfer(transfer, dhive_1.PrivateKey.fromString(activeKey));
return (0, response_1.successJson)({
success: true,
transaction_id: result.id,
transaction_url: `https://www.hiveblockexplorer.com/tx/${result.id}`,
block_num: result.block_num,
from: username,
to: params.recipient,
amount: formattedAmount,
encrypted_message: encryptedMessage,
});
}
catch (error) {
return (0, response_1.errorResponse)((0, error_1.handleError)(error, 'send_encrypted_message'));
}
});
}
// Get encrypted messages from account history
function getEncryptedMessages(params) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Use the provided username or fall back to the configured username
const username = params.username || config_1.default.hive.username;
if (!username) {
return (0, response_1.errorResponse)('Error: No username provided and HIVE_USERNAME environment variable is not set.');
}
// The getAccountHistory method needs a starting point (from) parameter
// We'll use -1 to get the most recent transactions
const from = -1;
// Get account history
const history = yield client_1.default.database.getAccountHistory(username, from, params.limit * 3 // Request more than needed to filter for encrypted messages
);
if (!history || !Array.isArray(history)) {
return (0, response_1.successJson)({
account: params.username,
messages_count: 0,
messages: [],
});
}
// Filter for transfer operations with encrypted memos
let encryptedMessages = history
.filter(([_index, operation]) => {
// Only include transfer operations
if (operation.op[0] !== 'transfer')
return false;
const opData = operation.op[1];
// Check if memo starts with '#' (encrypted memos start with '#')
return opData.memo && opData.memo.startsWith('#');
})
.map(([index, operation]) => {
const { timestamp, trx_id } = operation;
const opData = operation.op[1];
// Determine if this is an incoming or outgoing message
const direction = opData.to === params.username ? 'received' : 'sent';
const otherParty = direction === 'received' ? opData.from : opData.to;
return {
index,
transaction_id: trx_id,
timestamp,
direction,
counterparty: otherParty,
amount: opData.amount,
encrypted_message: opData.memo,
decrypted_message: null, // Will be populated later if decryption is requested
};
})
.slice(0, params.limit); // Limit to requested number of messages
// Decrypt messages if requested
if (params.decrypt && config_1.default.hive.memoKey) {
const memoPrivateKey = dhive_1.PrivateKey.fromString(config_1.default.hive.memoKey);
for (let i = 0; i < encryptedMessages.length; i++) {
const message = encryptedMessages[i];
try {
// Decrypt the message using our private memo key
// The memo format already contains the necessary information about the sender/recipient
const decryptedWithHash = dhive_1.Memo.decode(memoPrivateKey, message.encrypted_message);
// FIX: Remove the # prefix that Memo.decode adds to the decrypted message
message.decrypted_message = decryptedWithHash.startsWith('#')
? decryptedWithHash.substring(1)
: decryptedWithHash;
}
catch (decryptError) {
logger_1.default.warn(`Failed to decrypt message ${i}: ${decryptError instanceof Error ? decryptError.message : String(decryptError)}`);
message.decrypted_message = "[Decryption failed]";
}
}
}
return (0, response_1.successJson)({
account: params.username,
messages_count: encryptedMessages.length,
messages: encryptedMessages,
note: params.decrypt ?
"Messages were decrypted using your private memo key" :
"Set 'decrypt' parameter to true to attempt decryption of messages"
});
}
catch (error) {
return (0, response_1.errorResponse)((0, error_1.handleError)(error, 'get_encrypted_messages'));
}
});
}
//# sourceMappingURL=messaging.js.map