Как проверить с помощью Python (PyNaCl) сообщение, подписанное адаптером кошелька Solana (javascript)
Я подписал сообщение, используя пример адаптера кошелька Solana :
import { useWallet } from '@solana/wallet-adapter-react';
import bs58 from 'bs58';
import React, { FC, useCallback } from 'react';
import { sign } from 'tweetnacl';
export const SignMessageButton: FC = () => {
const { publicKey, signMessage } = useWallet();
const onClick = useCallback(async () => {
try {
// `publicKey` will be null if the wallet isn't connected
if (!publicKey) throw new Error('Wallet not connected!');
// `signMessage` will be undefined if the wallet doesn't support it
if (!signMessage) throw new Error('Wallet does not support message signing!');
// Encode anything as bytes
const message = new TextEncoder().encode('Hello, world!');
// Sign the bytes using the wallet
const signature = await signMessage(message);
// Verify that the bytes were signed using the private key that matches the known public key
if (!sign.detached.verify(message, signature, publicKey.toBytes())) throw new Error('Invalid signature!');
alert(`Message signature: ${bs58.encode(signature)}`);
} catch (error: any) {
alert(`Signing failed: ${error?.message}`);
}
}, [publicKey, signMessage]);
return signMessage ? (<button onClick={onClick} disabled={!publicKey}>Sign Message</button>) : null;
};
Но я не могу проверить подписанное сообщение с помощью PyNaCl и Solana-py. Я пробовал следующее:
from nacl.signing import VerifyKey
from solana.publickey import PublicKey
result = VerifyKey(bytes(PublicKey("HERE_THE_PUB_KEY"))
).verify(
smessage=bytes("HERE_THE_MESSAGE", "utf8"),
signature=bytes("HERE_THE_SIGNATURE", "utf8"),
)
Но проверка возвращается:
nacl.exceptions.BadSignatureError: Signature was forged or corrupt.
Кто-нибудь знает, что не так? Может быть проблема с кодировкой? Похоже, что JS использует следующие типы байтов:
Pubkey: Uint8Array(32) [144, 188, 240, ...
Message: Uint8Array(44) [57, 85, 65, ...
Signed: Uint8Array(64) [82, 220, 242, ...
И в питоне:
Pubkey: <class 'bytes'> b'\x90\xbc\xf...'
Message: <class 'bytes'> b'9UA...'
Signed: <class 'bytes'> b'2f68j5...'
Нужно ли мне использовать другую кодировку на Python?
1 ответ
Спасибо за конкретный пример, вы очень близки! Кодировка здесь является абсолютной проблемой - публичный ключ правильно закодирован в Python как байты. Этот первый байт
\x90
, закодированный как два шестнадцатеричных значения,
144
в JS, и вы можете проверить это в Python с помощью:
int('90', 16) = 144
.
Таким образом, чтобы проверить свой ключ, вы можете вместо этого использовать
base58
пакет https://github.com/keis/base58 и выполните:
from nacl.signing import VerifyKey
from solana.publickey import PublicKey
import base58
pubkey = bytes(PublicKey("DKpHyR1WjWE23E3xizPUhefZKmpMrMXNBVfoxQ7WXCRR"))
msg = bytes("hello", 'utf8')
signed = bytes("3EWDdtU1w8pWkr6fg8faJvKn1wBZmNjgf5kUx4Pn5gw4HeBPYVDm7cTHNqpRVMami6yX36jdaeZacv9GXR19Jzye", 'utf8')
result = VerifyKey(
pubkey
).verify(
smessage=msg,
signature=base58.b58decode(signed)
)
Примечание . В smessage вам не нужно использовать b58, потому что он был закодирован с помощью
new TextEncoder().encode("hello")
.
Второй вариант: если у вас уже есть UInt8Array из JS, вы можете сделать:
result = VerifyKey(bytes(PublicKey("HERE_THE_PUB_KEY"))
).verify(
smessage=bytes([byte1, byte2, byte3, ...])
signature=bytes([byte1, byte2, byte3, ...])
)