debugging

This commit is contained in:
Danish Arora 2025-08-18 13:00:35 +05:30
parent 1dfd790b11
commit d9d24dfc4f
No known key found for this signature in database
GPG Key ID: 1C6EF37CDAE1426E
9 changed files with 410 additions and 48 deletions

View File

@ -125,11 +125,27 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
};
const getVerificationStatus = (user: User): VerificationStatus => {
console.log(`🔍 Checking verification status for user:`, {
address: user.address,
walletType: user.walletType,
ordinalOwnership: user.ordinalOwnership,
ensOwnership: user.ensOwnership,
ensName: user.ensName,
hasDelegation: !!user.delegationSignature,
delegationExpiry: user.delegationExpiry ? new Date(user.delegationExpiry).toISOString() : 'none'
});
if (user.walletType === 'bitcoin') {
return user.ordinalOwnership ? 'verified-owner' : 'verified-none';
const status = user.ordinalOwnership ? 'verified-owner' : 'verified-none';
console.log(`📊 Bitcoin verification result:`, { status, hasOrdinal: !!user.ordinalOwnership });
return status;
} else if (user.walletType === 'ethereum') {
return user.ensOwnership ? 'verified-owner' : 'verified-none';
const status = user.ensOwnership ? 'verified-owner' : 'verified-none';
console.log(`📊 Ethereum verification result:`, { status, hasENS: !!user.ensOwnership, ensName: user.ensName });
return status;
}
console.log(`📊 Default verification result: unverified`);
return 'unverified';
};
@ -303,10 +319,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const messageSigning = {
signMessage: async (message: OpchanMessage): Promise<OpchanMessage | null> => {
return authServiceRef.current.signMessage(message);
return authServiceRef.current.messageSigning.signMessage(message);
},
verifyMessage: async (message: OpchanMessage): Promise<boolean> => {
return authServiceRef.current.verifyMessage(message);
return authServiceRef.current.messageSigning.verifyMessage(message);
}
};

View File

@ -94,7 +94,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
const updateStateFromCache = useCallback(() => {
// Use the verifyMessage function from authService if available
const verifyFn = isAuthenticated ?
(message: OpchanMessage) => authService.verifyMessage(message) :
(message: OpchanMessage) => authService.messageSigning.verifyMessage(message) :
undefined;
// Build user verification status for relevance calculation

View File

@ -15,13 +15,13 @@ export interface AuthResult {
export class AuthService {
private walletService: WalletService;
private ordinalApi: OrdinalAPI;
private messageSigning: MessageSigning;
private ordinalAPI: OrdinalAPI;
public messageSigning: MessageSigning;
private keyDelegation: KeyDelegation;
constructor() {
this.walletService = new WalletService();
this.ordinalApi = new OrdinalAPI();
this.ordinalAPI = new OrdinalAPI();
this.keyDelegation = new KeyDelegation();
this.messageSigning = new MessageSigning(this.keyDelegation);
}
@ -280,19 +280,7 @@ export class AuthService {
}
}
/**
* Sign a message using delegated key
*/
async signMessage(message: OpchanMessage): Promise<OpchanMessage | null> {
return this.messageSigning.signMessage(message);
}
/**
* Verify a message signature
*/
async verifyMessage(message: OpchanMessage): Promise<boolean> {
return this.messageSigning.verifyMessage(message);
}
/**
* Check if delegation is valid

View File

@ -20,7 +20,7 @@ export class MessageService {
*/
async signAndSendMessage(message: OpchanMessage): Promise<MessageResult> {
try {
const signedMessage = await this.authService.signMessage(message);
const signedMessage = this.authService.messageSigning.signMessage(message);
if (!signedMessage) {
// Check if delegation exists but is expired
@ -66,6 +66,6 @@ export class MessageService {
* Verify a message signature
*/
async verifyMessage(message: OpchanMessage): Promise<boolean> {
return this.authService.verifyMessage(message);
return this.authService.messageSigning.verifyMessage(message);
}
}

View File

@ -145,17 +145,49 @@ export class KeyDelegation {
// If a current address is provided, validate it matches the delegation
if (currentAddress && delegation.walletAddress !== currentAddress) {
console.warn(`⚠️ Delegation address mismatch detected:`, {
storedAddress: delegation.walletAddress,
currentAddress: currentAddress,
suggestion: 'Consider clearing the delegation and creating a new one'
});
return false;
}
// If a current wallet type is provided, validate it matches the delegation
if (currentWalletType && delegation.walletType !== currentWalletType) {
console.warn(`⚠️ Delegation wallet type mismatch detected:`, {
storedType: delegation.walletType,
currentType: currentWalletType,
suggestion: 'Consider clearing the delegation and creating a new one'
});
return false;
}
return true;
}
/**
* Check if the current wallet address matches the stored delegation
* @param currentAddress The current wallet address to check
* @returns boolean indicating if addresses match
*/
isAddressMatch(currentAddress: string): boolean {
const delegation = this.retrieveDelegation();
if (!delegation) return false;
const matches = delegation.walletAddress === currentAddress;
if (!matches) {
console.warn(`⚠️ Wallet address mismatch:`, {
storedAddress: delegation.walletAddress,
currentAddress: currentAddress,
timeRemaining: this.getDelegationTimeRemaining()
});
}
return matches;
}
/**
* Signs a message using the browser-generated private key
* @param message The message to sign

View File

@ -10,13 +10,26 @@ export class MessageSigning {
}
signMessage<T extends OpchanMessage>(message: T): T | null {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.log(`✍️ Starting message signing for: ${messageId}`);
if (!this.keyDelegation.isDelegationValid()) {
console.error('No valid key delegation found. Cannot sign message.');
console.error(`❌ No valid key delegation found. Cannot sign message: ${messageId}`);
return null;
}
const delegation = this.keyDelegation.retrieveDelegation();
if (!delegation) return null;
if (!delegation) {
console.error(`❌ No delegation found. Cannot sign message: ${messageId}`);
return null;
}
console.log(`🔑 Using delegation for signing:`, {
walletAddress: delegation.walletAddress,
walletType: delegation.walletType,
browserPubKey: delegation.browserPublicKey.slice(0, 16) + '...',
expiresAt: new Date(delegation.expiryTimestamp).toISOString()
});
const messageToSign = JSON.stringify({
...message,
@ -27,10 +40,23 @@ export class MessageSigning {
delegationExpiry: undefined
});
const signature = this.keyDelegation.signMessage(messageToSign);
if (!signature) return null;
console.log(`📝 Signing message content for ${messageId}:`, {
contentLength: messageToSign.length,
messageType: message.type
});
return {
const signature = this.keyDelegation.signMessage(messageToSign);
if (!signature) {
console.error(`❌ Failed to sign message: ${messageId}`);
return null;
}
console.log(`✅ Message signed successfully for ${messageId}:`, {
signatureLength: signature.length,
signaturePrefix: signature.slice(0, 16) + '...'
});
const signedMessage = {
...message,
signature,
browserPubKey: delegation.browserPublicKey,
@ -42,6 +68,10 @@ export class MessageSigning {
),
delegationExpiry: delegation.expiryTimestamp
};
console.log(`🎉 Message signing completed for ${messageId} with delegation chain`);
return signedMessage;
}
async verifyMessage(message: OpchanMessage & {
@ -51,20 +81,42 @@ export class MessageSigning {
delegationMessage?: string;
delegationExpiry?: number;
}): Promise<boolean> {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.log(`🔍 Starting verification for message: ${messageId}`);
// Check for required signature fields
if (!message.signature || !message.browserPubKey) {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.warn('Message is missing signature information', messageId);
console.warn(`❌ Message ${messageId} missing signature information:`, {
hasSignature: !!message.signature,
hasBrowserPubKey: !!message.browserPubKey
});
return false;
}
// Check for required delegation fields
if (!message.delegationSignature || !message.delegationMessage || !message.delegationExpiry) {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.warn('Message is missing delegation information', messageId);
console.warn(`❌ Message ${messageId} missing delegation information:`, {
hasDelegationSignature: !!message.delegationSignature,
hasDelegationMessage: !!message.delegationMessage,
hasDelegationExpiry: !!message.delegationExpiry
});
return false;
}
console.log(`✅ Message ${messageId} has all required fields`);
// Log delegation details for debugging
const delegation = this.keyDelegation.retrieveDelegation();
if (delegation) {
console.log(`🔍 Current delegation details:`, {
storedWalletAddress: delegation.walletAddress,
messageAuthorAddress: message.author,
addressesMatch: delegation.walletAddress === message.author,
walletType: delegation.walletType,
hasWalletPublicKey: !!delegation.walletPublicKey
});
}
// 1. Verify the message signature
const signedContent = JSON.stringify({
...message,
@ -75,6 +127,8 @@ export class MessageSigning {
delegationExpiry: undefined
});
console.log(`🔐 Verifying message signature for ${messageId} with browser key: ${message.browserPubKey.slice(0, 16)}...`);
const isValidMessageSignature = this.keyDelegation.verifySignature(
signedContent,
message.signature,
@ -82,19 +136,31 @@ export class MessageSigning {
);
if (!isValidMessageSignature) {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.warn(`Invalid message signature for message ${messageId}`);
console.warn(`❌ Invalid message signature for ${messageId}`);
return false;
}
console.log(`✅ Message signature verified for ${messageId}`);
// 2. Verify delegation hasn't expired
const now = Date.now();
const timeUntilExpiry = message.delegationExpiry - now;
console.log(`⏰ Checking delegation expiry for ${messageId}:`, {
currentTime: new Date(now).toISOString(),
expiryTime: new Date(message.delegationExpiry).toISOString(),
timeUntilExpiry: `${Math.round(timeUntilExpiry / 1000 / 60)} minutes`
});
if (now >= message.delegationExpiry) {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.warn(`Delegation expired for message ${messageId}`);
console.warn(`❌ Delegation expired for ${messageId}`, {
expiredBy: `${Math.round(-timeUntilExpiry / 1000 / 60)} minutes`
});
return false;
}
console.log(`✅ Delegation not expired for ${messageId}`);
// 3. Verify delegation message integrity
const expectedDelegationMessage = this.keyDelegation.createDelegationMessage(
message.browserPubKey,
@ -102,25 +168,71 @@ export class MessageSigning {
message.delegationExpiry
);
console.log(`🔗 Verifying delegation message integrity for ${messageId}:`, {
expected: expectedDelegationMessage,
actual: message.delegationMessage,
matches: message.delegationMessage === expectedDelegationMessage
});
if (message.delegationMessage !== expectedDelegationMessage) {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.warn(`Delegation message tampered for message ${messageId}`);
console.warn(`❌ Delegation message tampered for ${messageId}`);
return false;
}
console.log(`✅ Delegation message integrity verified for ${messageId}`);
// 4. Verify wallet signature of delegation
console.log(`🔑 Verifying wallet signature for ${messageId} from author: ${message.author}`);
const isValidDelegationSignature = await this.verifyWalletSignature(
message.delegationMessage,
message.delegationSignature,
message.author
);
console.log(`🔍 Delegation verification details:`, {
delegationMessage: message.delegationMessage,
delegationSignature: message.delegationSignature,
signatureLength: message.delegationSignature?.length,
messageAuthor: message.author,
storedDelegation: delegation ? {
signature: delegation.signature,
signatureLength: delegation.signature?.length,
walletAddress: delegation.walletAddress
} : 'no delegation found'
});
if (!isValidDelegationSignature) {
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
console.warn(`Invalid delegation signature for message ${messageId}`);
console.warn(`❌ Invalid delegation signature for ${messageId}`);
console.log(`🔍 Delegation signature verification failed. This could indicate:`, {
reason: 'Address mismatch between delegation creation and current wallet',
suggestion: 'User may have switched wallets or accounts. Try creating a new delegation.',
delegationMessage: message.delegationMessage,
delegationSignature: message.delegationSignature.slice(0, 32) + '...',
expectedAuthor: message.author
});
// Show a user-friendly error message
console.error(`🚨 SECURITY ALERT: Delegation signature verification failed for message ${messageId}.
This usually means:
1. You switched wallet accounts since creating the delegation
2. You're using a different wallet than the one that created the delegation
3. Your wallet address changed (e.g., switching networks)
To fix this:
1. Go to your profile/settings
2. Click "Clear Delegation"
3. Create a new delegation with your current wallet
This ensures your messages are properly authenticated with your current wallet address.`);
return false;
}
console.log(`✅ Wallet signature verified for ${messageId}`);
console.log(`🎉 All verifications passed for message: ${messageId}`);
return true;
}

View File

@ -0,0 +1,138 @@
/**
* Wallet signature verification for delegation messages
*
* This module handles cryptographic verification of wallet signatures
* for Bitcoin and Ethereum wallets when they sign delegation messages.
*/
import * as secp256k1 from '@noble/secp256k1';
import { sha256 } from '@noble/hashes/sha256';
import { bytesToHex, hexToBytes } from '@/lib/utils';
import { recoverMessageAddress, getAddress, verifyMessage as viemVerifyMessage } from 'viem';
export class WalletSignatureVerifier {
/**
* Verify a Bitcoin wallet signature
* @param message The original message that was signed
* @param signature The signature in hex format
* @param publicKey The public key in hex format
* @returns boolean indicating if the signature is valid
*/
static verifyBitcoinSignature(
message: string,
signature: string,
publicKey: string
): boolean {
console.log(`🔐 Verifying Bitcoin signature:`, {
messageLength: message.length,
signatureLength: signature.length,
publicKeyLength: publicKey.length,
publicKeyPrefix: publicKey.slice(0, 16) + '...'
});
try {
const messageBytes = new TextEncoder().encode(message);
const messageHash = sha256(messageBytes);
const signatureBytes = hexToBytes(signature);
const publicKeyBytes = hexToBytes(publicKey);
const isValid = secp256k1.verify(signatureBytes, messageHash, publicKeyBytes);
console.log(`✅ Bitcoin signature verification result:`, {
isValid,
messageHash: bytesToHex(messageHash).slice(0, 16) + '...'
});
return isValid;
} catch (error) {
console.error('❌ Error verifying Bitcoin signature:', error);
return false;
}
}
/**
* Verify an Ethereum wallet signature using viem (EIP-191 personal_sign)
* @param message The original message that was signed
* @param signature The signature in hex format (0x...)
* @param address The Ethereum address expected to have signed the message
*/
static async verifyEthereumSignature(
message: string,
signature: string,
address: string
): Promise<boolean> {
console.log(`🔐 Verifying Ethereum signature:`, {
messageLength: message.length,
signatureLength: signature.length,
expectedAddress: address,
signaturePrefix: signature.slice(0, 16) + '...',
fullMessage: message // Log the full message for debugging
});
try {
// First, use viem's built-in verifier (handles prefixing correctly)
const isValid = await viemVerifyMessage({ message, signature: signature as `0x${string}`, address: getAddress(address) as `0x${string}` });
// For diagnostics only, attempt recovery
try {
const recovered = await recoverMessageAddress({ message, signature: signature as `0x${string}` });
console.log(`🔍 Ethereum signature recovery details:`, {
recoveredAddress: recovered,
expectedAddress: address,
recoveredNormalized: getAddress(recovered),
expectedNormalized: getAddress(address),
addressesMatch: getAddress(recovered) === getAddress(address),
rawComparison: recovered === address,
messageBytes: new TextEncoder().encode(message).length,
signatureBytes: signature.length
});
} catch (e) {
console.warn('⚠️ Non-fatal: recoverMessageAddress failed during diagnostics:', e);
}
console.log(`✅ Ethereum signature verification result:`, { isValid });
return isValid;
} catch (error) {
console.error('❌ Error verifying Ethereum signature:', error);
return false;
}
}
/**
* Verify wallet signature based on wallet type
* @param message The original message that was signed
* @param signature The signature in hex format
* @param walletAddress The wallet address
* @param walletType The type of wallet (bitcoin or ethereum)
* @param publicKey Optional public key for Bitcoin verification
*/
static async verifyWalletSignature(
message: string,
signature: string,
walletAddress: string,
walletType: 'bitcoin' | 'ethereum',
publicKey?: string
): Promise<boolean> {
console.log(`🔑 Starting wallet signature verification:`, {
walletType,
walletAddress,
hasPublicKey: !!publicKey,
messageLength: message.length
});
if (walletType === 'bitcoin') {
if (!publicKey) {
console.warn(`❌ Bitcoin verification requires public key but none provided`);
return false;
}
console.log(`🔐 Using Bitcoin verification path`);
return this.verifyBitcoinSignature(message, signature, publicKey);
}
// Ethereum path (no stored public key required)
console.log(`🔐 Using Ethereum verification path`);
return this.verifyEthereumSignature(message, signature, walletAddress);
}
}

View File

@ -89,6 +89,13 @@ export class ReOwnWalletService {
// Convert message bytes to string for signing
const messageString = new TextDecoder().decode(messageBytes);
console.log(`🔐 Wallet signing request:`, {
walletType,
requestedAddress: account.address,
messageLength: messageString.length,
messagePreview: messageString.slice(0, 100) + '...'
});
try {
// Access the adapter through the appKit instance
// The adapter is available through the appKit's chainAdapters property
@ -112,9 +119,15 @@ export class ReOwnWalletService {
provider: provider as Provider
});
console.log(`✅ Wallet signing completed:`, {
requestedAddress: account.address,
signatureLength: result.signature.length,
signaturePrefix: result.signature.slice(0, 16) + '...'
});
return result.signature;
} catch (error) {
console.error(`Error signing message with ${walletType} wallet:`, error);
console.error(`Error signing message with ${walletType} wallet:`, error);
throw new Error(`Failed to sign message with ${walletType} wallet: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
@ -129,26 +142,87 @@ export class ReOwnWalletService {
throw new Error(`No ${walletType} wallet connected`);
}
console.log(`🔑 Starting key delegation creation:`, {
walletType,
accountAddress: account.address,
duration
});
// Generate a new browser keypair
const keypair = this.keyDelegation.generateKeypair();
// Create delegation message with expiry
const expiryHours = KeyDelegation.getDurationHours(duration);
const expiryTimestamp = Date.now() + (expiryHours * 60 * 60 * 1000);
const delegationMessage = this.keyDelegation.createDelegationMessage(
let delegationMessage = this.keyDelegation.createDelegationMessage(
keypair.publicKey,
account.address,
expiryTimestamp
);
console.log(`📝 Delegation message created:`, {
message: delegationMessage,
browserPublicKey: keypair.publicKey.slice(0, 16) + '...',
expiryTimestamp: new Date(expiryTimestamp).toISOString()
});
const messageBytes = new TextEncoder().encode(delegationMessage);
// Sign the delegation message
const signature = await this.signMessage(messageBytes, walletType);
console.log(`🔍 Wallet signing details:`, {
originalMessage: delegationMessage,
messageBytes: messageBytes.length,
signature: signature,
signatureLength: signature.length
});
// Verify the signature matches the expected address
let recoveredAddress: string | null = null;
if (walletType === 'ethereum') {
try {
const { recoverMessageAddress } = await import('viem');
console.log(`🔍 Detailed signature analysis:`, {
originalMessage: delegationMessage,
signature: signature,
signatureLength: signature.length,
expectedAddress: account.address
});
recoveredAddress = await recoverMessageAddress({
message: delegationMessage,
signature: signature as `0x${string}`
});
console.log(`🔍 Signature verification check:`, {
expectedAddress: account.address,
recoveredAddress: recoveredAddress,
addressesMatch: recoveredAddress.toLowerCase() === account.address.toLowerCase(),
message: delegationMessage
});
// Diagnostics only; do NOT mutate the message after signing
console.log(`🔍 Additional verification:`, {
recoveredAddressLowerCase: recoveredAddress.toLowerCase(),
expectedAddressLowerCase: account.address.toLowerCase(),
exactMatch: recoveredAddress === account.address,
caseInsensitiveMatch: recoveredAddress.toLowerCase() === account.address.toLowerCase()
});
} catch (error) {
console.error(`❌ Error verifying delegation signature:`, error);
}
}
console.log(`✅ Delegation signature received:`, {
signatureLength: signature.length,
signaturePrefix: signature.slice(0, 16) + '...'
});
// Create and store the delegation
const delegationInfo = this.keyDelegation.createDelegation(
account.address,
account.address, // store the original address used to build the message
signature,
keypair.publicKey,
keypair.privateKey,
@ -158,9 +232,11 @@ export class ReOwnWalletService {
this.keyDelegation.storeDelegation(delegationInfo);
console.log(`🎉 Key delegation created and stored successfully`);
return true;
} catch (error) {
console.error(`Error creating key delegation for ${walletType}:`, error);
console.error(`Error creating key delegation for ${walletType}:`, error);
return false;
}
}

View File

@ -5,11 +5,11 @@ import { MessageType } from "./types";
* Content topics for different message types
*/
export const CONTENT_TOPICS: Record<MessageType, string> = {
[MessageType.CELL]: '/opchan/1/cell/proto',
[MessageType.POST]: '/opchan/1/post/proto',
[MessageType.COMMENT]: '/opchan/1/comment/proto',
[MessageType.VOTE]: '/opchan/1/vote/proto',
[MessageType.MODERATE]: '/opchan/1/moderate/proto'
[MessageType.CELL]: '/opcxzzhzaxsn221-aß/1/cell/proto',
[MessageType.POST]: '/opchan-x/1/post/proto',
[MessageType.COMMENT]: '/opchan-x/1/comment/proto',
[MessageType.VOTE]: '/opchan-x/1/vote/proto',
[MessageType.MODERATE]: '/opchan-x/1/moderate/proto'
};
export const NETWORK_CONFIG: NetworkConfig = {