mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-04 05:43:10 +00:00
debugging
This commit is contained in:
parent
1dfd790b11
commit
d9d24dfc4f
@ -125,11 +125,27 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getVerificationStatus = (user: User): VerificationStatus => {
|
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') {
|
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') {
|
} 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';
|
return 'unverified';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -303,10 +319,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
const messageSigning = {
|
const messageSigning = {
|
||||||
signMessage: async (message: OpchanMessage): Promise<OpchanMessage | null> => {
|
signMessage: async (message: OpchanMessage): Promise<OpchanMessage | null> => {
|
||||||
return authServiceRef.current.signMessage(message);
|
return authServiceRef.current.messageSigning.signMessage(message);
|
||||||
},
|
},
|
||||||
verifyMessage: async (message: OpchanMessage): Promise<boolean> => {
|
verifyMessage: async (message: OpchanMessage): Promise<boolean> => {
|
||||||
return authServiceRef.current.verifyMessage(message);
|
return authServiceRef.current.messageSigning.verifyMessage(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -94,7 +94,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const updateStateFromCache = useCallback(() => {
|
const updateStateFromCache = useCallback(() => {
|
||||||
// Use the verifyMessage function from authService if available
|
// Use the verifyMessage function from authService if available
|
||||||
const verifyFn = isAuthenticated ?
|
const verifyFn = isAuthenticated ?
|
||||||
(message: OpchanMessage) => authService.verifyMessage(message) :
|
(message: OpchanMessage) => authService.messageSigning.verifyMessage(message) :
|
||||||
undefined;
|
undefined;
|
||||||
|
|
||||||
// Build user verification status for relevance calculation
|
// Build user verification status for relevance calculation
|
||||||
|
|||||||
@ -15,13 +15,13 @@ export interface AuthResult {
|
|||||||
|
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
private walletService: WalletService;
|
private walletService: WalletService;
|
||||||
private ordinalApi: OrdinalAPI;
|
private ordinalAPI: OrdinalAPI;
|
||||||
private messageSigning: MessageSigning;
|
public messageSigning: MessageSigning;
|
||||||
private keyDelegation: KeyDelegation;
|
private keyDelegation: KeyDelegation;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.walletService = new WalletService();
|
this.walletService = new WalletService();
|
||||||
this.ordinalApi = new OrdinalAPI();
|
this.ordinalAPI = new OrdinalAPI();
|
||||||
this.keyDelegation = new KeyDelegation();
|
this.keyDelegation = new KeyDelegation();
|
||||||
this.messageSigning = new MessageSigning(this.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
|
* Check if delegation is valid
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export class MessageService {
|
|||||||
*/
|
*/
|
||||||
async signAndSendMessage(message: OpchanMessage): Promise<MessageResult> {
|
async signAndSendMessage(message: OpchanMessage): Promise<MessageResult> {
|
||||||
try {
|
try {
|
||||||
const signedMessage = await this.authService.signMessage(message);
|
const signedMessage = this.authService.messageSigning.signMessage(message);
|
||||||
|
|
||||||
if (!signedMessage) {
|
if (!signedMessage) {
|
||||||
// Check if delegation exists but is expired
|
// Check if delegation exists but is expired
|
||||||
@ -66,6 +66,6 @@ export class MessageService {
|
|||||||
* Verify a message signature
|
* Verify a message signature
|
||||||
*/
|
*/
|
||||||
async verifyMessage(message: OpchanMessage): Promise<boolean> {
|
async verifyMessage(message: OpchanMessage): Promise<boolean> {
|
||||||
return this.authService.verifyMessage(message);
|
return this.authService.messageSigning.verifyMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,17 +145,49 @@ export class KeyDelegation {
|
|||||||
|
|
||||||
// If a current address is provided, validate it matches the delegation
|
// If a current address is provided, validate it matches the delegation
|
||||||
if (currentAddress && delegation.walletAddress !== currentAddress) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a current wallet type is provided, validate it matches the delegation
|
// If a current wallet type is provided, validate it matches the delegation
|
||||||
if (currentWalletType && delegation.walletType !== currentWalletType) {
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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
|
* Signs a message using the browser-generated private key
|
||||||
* @param message The message to sign
|
* @param message The message to sign
|
||||||
|
|||||||
@ -10,13 +10,26 @@ export class MessageSigning {
|
|||||||
}
|
}
|
||||||
|
|
||||||
signMessage<T extends OpchanMessage>(message: T): T | null {
|
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()) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const delegation = this.keyDelegation.retrieveDelegation();
|
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({
|
const messageToSign = JSON.stringify({
|
||||||
...message,
|
...message,
|
||||||
@ -27,10 +40,23 @@ export class MessageSigning {
|
|||||||
delegationExpiry: undefined
|
delegationExpiry: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const signature = this.keyDelegation.signMessage(messageToSign);
|
console.log(`📝 Signing message content for ${messageId}:`, {
|
||||||
if (!signature) return null;
|
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,
|
...message,
|
||||||
signature,
|
signature,
|
||||||
browserPubKey: delegation.browserPublicKey,
|
browserPubKey: delegation.browserPublicKey,
|
||||||
@ -42,6 +68,10 @@ export class MessageSigning {
|
|||||||
),
|
),
|
||||||
delegationExpiry: delegation.expiryTimestamp
|
delegationExpiry: delegation.expiryTimestamp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(`🎉 Message signing completed for ${messageId} with delegation chain`);
|
||||||
|
|
||||||
|
return signedMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyMessage(message: OpchanMessage & {
|
async verifyMessage(message: OpchanMessage & {
|
||||||
@ -51,20 +81,42 @@ export class MessageSigning {
|
|||||||
delegationMessage?: string;
|
delegationMessage?: string;
|
||||||
delegationExpiry?: number;
|
delegationExpiry?: number;
|
||||||
}): Promise<boolean> {
|
}): 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
|
// Check for required signature fields
|
||||||
if (!message.signature || !message.browserPubKey) {
|
if (!message.signature || !message.browserPubKey) {
|
||||||
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
|
console.warn(`❌ Message ${messageId} missing signature information:`, {
|
||||||
console.warn('Message is missing signature information', messageId);
|
hasSignature: !!message.signature,
|
||||||
|
hasBrowserPubKey: !!message.browserPubKey
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for required delegation fields
|
// Check for required delegation fields
|
||||||
if (!message.delegationSignature || !message.delegationMessage || !message.delegationExpiry) {
|
if (!message.delegationSignature || !message.delegationMessage || !message.delegationExpiry) {
|
||||||
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
|
console.warn(`❌ Message ${messageId} missing delegation information:`, {
|
||||||
console.warn('Message is missing delegation information', messageId);
|
hasDelegationSignature: !!message.delegationSignature,
|
||||||
|
hasDelegationMessage: !!message.delegationMessage,
|
||||||
|
hasDelegationExpiry: !!message.delegationExpiry
|
||||||
|
});
|
||||||
return false;
|
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
|
// 1. Verify the message signature
|
||||||
const signedContent = JSON.stringify({
|
const signedContent = JSON.stringify({
|
||||||
...message,
|
...message,
|
||||||
@ -75,6 +127,8 @@ export class MessageSigning {
|
|||||||
delegationExpiry: undefined
|
delegationExpiry: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(`🔐 Verifying message signature for ${messageId} with browser key: ${message.browserPubKey.slice(0, 16)}...`);
|
||||||
|
|
||||||
const isValidMessageSignature = this.keyDelegation.verifySignature(
|
const isValidMessageSignature = this.keyDelegation.verifySignature(
|
||||||
signedContent,
|
signedContent,
|
||||||
message.signature,
|
message.signature,
|
||||||
@ -82,19 +136,31 @@ export class MessageSigning {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!isValidMessageSignature) {
|
if (!isValidMessageSignature) {
|
||||||
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
|
console.warn(`❌ Invalid message signature for ${messageId}`);
|
||||||
console.warn(`Invalid message signature for message ${messageId}`);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Message signature verified for ${messageId}`);
|
||||||
|
|
||||||
// 2. Verify delegation hasn't expired
|
// 2. Verify delegation hasn't expired
|
||||||
const now = Date.now();
|
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) {
|
if (now >= message.delegationExpiry) {
|
||||||
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
|
console.warn(`❌ Delegation expired for ${messageId}`, {
|
||||||
console.warn(`Delegation expired for message ${messageId}`);
|
expiredBy: `${Math.round(-timeUntilExpiry / 1000 / 60)} minutes`
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Delegation not expired for ${messageId}`);
|
||||||
|
|
||||||
// 3. Verify delegation message integrity
|
// 3. Verify delegation message integrity
|
||||||
const expectedDelegationMessage = this.keyDelegation.createDelegationMessage(
|
const expectedDelegationMessage = this.keyDelegation.createDelegationMessage(
|
||||||
message.browserPubKey,
|
message.browserPubKey,
|
||||||
@ -102,25 +168,71 @@ export class MessageSigning {
|
|||||||
message.delegationExpiry
|
message.delegationExpiry
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(`🔗 Verifying delegation message integrity for ${messageId}:`, {
|
||||||
|
expected: expectedDelegationMessage,
|
||||||
|
actual: message.delegationMessage,
|
||||||
|
matches: message.delegationMessage === expectedDelegationMessage
|
||||||
|
});
|
||||||
|
|
||||||
if (message.delegationMessage !== expectedDelegationMessage) {
|
if (message.delegationMessage !== expectedDelegationMessage) {
|
||||||
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
|
console.warn(`❌ Delegation message tampered for ${messageId}`);
|
||||||
console.warn(`Delegation message tampered for message ${messageId}`);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Delegation message integrity verified for ${messageId}`);
|
||||||
|
|
||||||
// 4. Verify wallet signature of delegation
|
// 4. Verify wallet signature of delegation
|
||||||
|
console.log(`🔑 Verifying wallet signature for ${messageId} from author: ${message.author}`);
|
||||||
|
|
||||||
const isValidDelegationSignature = await this.verifyWalletSignature(
|
const isValidDelegationSignature = await this.verifyWalletSignature(
|
||||||
message.delegationMessage,
|
message.delegationMessage,
|
||||||
message.delegationSignature,
|
message.delegationSignature,
|
||||||
message.author
|
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) {
|
if (!isValidDelegationSignature) {
|
||||||
const messageId = 'id' in message ? message.id : `${message.type}-${message.timestamp}`;
|
console.warn(`❌ Invalid delegation signature for ${messageId}`);
|
||||||
console.warn(`Invalid delegation signature for message ${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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`✅ Wallet signature verified for ${messageId}`);
|
||||||
|
console.log(`🎉 All verifications passed for message: ${messageId}`);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
138
src/lib/identity/signatures/wallet-signature-verifier.ts
Normal file
138
src/lib/identity/signatures/wallet-signature-verifier.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -89,6 +89,13 @@ export class ReOwnWalletService {
|
|||||||
// Convert message bytes to string for signing
|
// Convert message bytes to string for signing
|
||||||
const messageString = new TextDecoder().decode(messageBytes);
|
const messageString = new TextDecoder().decode(messageBytes);
|
||||||
|
|
||||||
|
console.log(`🔐 Wallet signing request:`, {
|
||||||
|
walletType,
|
||||||
|
requestedAddress: account.address,
|
||||||
|
messageLength: messageString.length,
|
||||||
|
messagePreview: messageString.slice(0, 100) + '...'
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Access the adapter through the appKit instance
|
// Access the adapter through the appKit instance
|
||||||
// The adapter is available through the appKit's chainAdapters property
|
// The adapter is available through the appKit's chainAdapters property
|
||||||
@ -112,9 +119,15 @@ export class ReOwnWalletService {
|
|||||||
provider: provider as Provider
|
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;
|
return result.signature;
|
||||||
} catch (error) {
|
} 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'}`);
|
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`);
|
throw new Error(`No ${walletType} wallet connected`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`🔑 Starting key delegation creation:`, {
|
||||||
|
walletType,
|
||||||
|
accountAddress: account.address,
|
||||||
|
duration
|
||||||
|
});
|
||||||
|
|
||||||
// Generate a new browser keypair
|
// Generate a new browser keypair
|
||||||
const keypair = this.keyDelegation.generateKeypair();
|
const keypair = this.keyDelegation.generateKeypair();
|
||||||
|
|
||||||
// Create delegation message with expiry
|
// Create delegation message with expiry
|
||||||
const expiryHours = KeyDelegation.getDurationHours(duration);
|
const expiryHours = KeyDelegation.getDurationHours(duration);
|
||||||
const expiryTimestamp = Date.now() + (expiryHours * 60 * 60 * 1000);
|
const expiryTimestamp = Date.now() + (expiryHours * 60 * 60 * 1000);
|
||||||
const delegationMessage = this.keyDelegation.createDelegationMessage(
|
let delegationMessage = this.keyDelegation.createDelegationMessage(
|
||||||
keypair.publicKey,
|
keypair.publicKey,
|
||||||
account.address,
|
account.address,
|
||||||
expiryTimestamp
|
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);
|
const messageBytes = new TextEncoder().encode(delegationMessage);
|
||||||
|
|
||||||
// Sign the delegation message
|
// Sign the delegation message
|
||||||
const signature = await this.signMessage(messageBytes, walletType);
|
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
|
// Create and store the delegation
|
||||||
const delegationInfo = this.keyDelegation.createDelegation(
|
const delegationInfo = this.keyDelegation.createDelegation(
|
||||||
account.address,
|
account.address, // store the original address used to build the message
|
||||||
signature,
|
signature,
|
||||||
keypair.publicKey,
|
keypair.publicKey,
|
||||||
keypair.privateKey,
|
keypair.privateKey,
|
||||||
@ -158,9 +232,11 @@ export class ReOwnWalletService {
|
|||||||
|
|
||||||
this.keyDelegation.storeDelegation(delegationInfo);
|
this.keyDelegation.storeDelegation(delegationInfo);
|
||||||
|
|
||||||
|
console.log(`🎉 Key delegation created and stored successfully`);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error creating key delegation for ${walletType}:`, error);
|
console.error(`❌ Error creating key delegation for ${walletType}:`, error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import { MessageType } from "./types";
|
|||||||
* Content topics for different message types
|
* Content topics for different message types
|
||||||
*/
|
*/
|
||||||
export const CONTENT_TOPICS: Record<MessageType, string> = {
|
export const CONTENT_TOPICS: Record<MessageType, string> = {
|
||||||
[MessageType.CELL]: '/opchan/1/cell/proto',
|
[MessageType.CELL]: '/opcxzzhzaxsn221-aß/1/cell/proto',
|
||||||
[MessageType.POST]: '/opchan/1/post/proto',
|
[MessageType.POST]: '/opchan-x/1/post/proto',
|
||||||
[MessageType.COMMENT]: '/opchan/1/comment/proto',
|
[MessageType.COMMENT]: '/opchan-x/1/comment/proto',
|
||||||
[MessageType.VOTE]: '/opchan/1/vote/proto',
|
[MessageType.VOTE]: '/opchan-x/1/vote/proto',
|
||||||
[MessageType.MODERATE]: '/opchan/1/moderate/proto'
|
[MessageType.MODERATE]: '/opchan-x/1/moderate/proto'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NETWORK_CONFIG: NetworkConfig = {
|
export const NETWORK_CONFIG: NetworkConfig = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user