UNPKG

@sei-js/cosmjs

Version:

TypeScript library for CosmJS interactions on the Sei blockchain

142 lines (141 loc) 5.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.verifyArbitrary = void 0; exports.makeADR36AminoSignDoc = makeADR36AminoSignDoc; const signdoc_1 = require("@cosmjs/amino/build/signdoc"); const encoding_1 = require("@cosmjs/encoding"); const address_1 = require("./address"); const hash_1 = require("./hash"); /** * Creates a StdSignDoc for an [ADR-36](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md) object. * @param signer A string representing the address of the signer. * @param data A string or byte array representing the payload of the tx to be signed. * Data is arbitrary bytes which can represent text, files, objects. It's applications developers decision how Data should be deserialized, serialized and the object it can represent in their context * It's applications developers decision how Data should be treated, by treated we mean the serialization and deserialization process and the Object Data should represent. * @returns A StdSignDoc object. * @category Utils */ function makeADR36AminoSignDoc(signer, data) { // If data is already a base64 string, convert it to a Buffer and back to a string. //@ts-ignore const base64Data = Buffer.from(data).toString('base64'); //According to ADR-36 specifications https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md return { // chain-id must be equal to “” chain_id: '', // must be invalid value account_number: '0', // nonce, sequence number must be equal to 0 sequence: '0', fee: { // fee gas must be equal to 0 gas: '0', //fee amount must be an empty array amount: [] }, msgs: [ { type: 'sign/MsgSignData', value: { signer, // Data is arbitrary bytes which can represent text, files, objects. It's applications developers decision how Data should be deserialized, serialized and the object it can represent in their context // It's applications developers decision how Data should be treated, by treated we mean the serialization and deserialization process and the Object Data should represent. data: base64Data } } ], // the memo must be empty memo: '' }; } function checkAndValidateADR36AminoSignDoc(signDoc) { const hasOnlyMsgSignData = (() => { if (signDoc?.msgs && Array.isArray(signDoc.msgs) && signDoc.msgs.length === 1) { const msg = signDoc.msgs[0]; return msg.type === 'sign/MsgSignData'; } else { return false; } })(); if (!hasOnlyMsgSignData) { return false; } if (signDoc.chain_id !== '') { throw new Error('Chain id should be empty string for ADR-36 signing'); } if (signDoc.memo !== '') { throw new Error('Memo should be empty string for ADR-36 signing'); } if (signDoc.account_number !== '0') { throw new Error('Account number should be "0" for ADR-36 signing'); } if (signDoc.sequence !== '0') { throw new Error('Sequence should be "0" for ADR-36 signing'); } if (signDoc.fee.gas !== '0') { throw new Error('Gas should be "0" for ADR-36 signing'); } if (signDoc.fee.amount.length !== 0) { throw new Error('Fee amount should be empty array for ADR-36 signing'); } const msg = signDoc.msgs[0]; if (msg.type !== 'sign/MsgSignData') { throw new Error(`Invalid type of ADR-36 sign msg: ${msg.type}`); } if (!msg.value) { throw new Error('Empty value in the msg'); } const signer = msg.value.signer; if (!signer) { throw new Error('Empty signer in the ADR-36 msg'); } (0, address_1.isValidSeiCosmosAddress)(signer); const data = msg.value.data; if (!data) { throw new Error('Empty data in the ADR-36 msg'); } const rawData = Buffer.from(data, 'base64'); // Validate the data is encoded as base64. if (rawData.toString('base64') !== data) { throw new Error('Data is not encoded by base64'); } if (rawData.length === 0) { throw new Error('Empty data in the ADR-36 msg'); } return true; } function verifyADR36AminoSignDoc(signDoc, pubKey, signature) { if (!checkAndValidateADR36AminoSignDoc(signDoc)) { throw new Error('Invalid sign doc for ADR-36'); } const expectedSigner = (0, address_1.compressedPubKeyToAddress)(pubKey); const signer = signDoc.msgs[0].value.signer; if (expectedSigner !== signer) { throw new Error('Unmatched signer'); } const msg = (0, signdoc_1.serializeSignDoc)(signDoc); return (0, address_1.verifyDigest32)((0, hash_1.sha256)(msg), signature, pubKey); } /** * @category Utils */ function verifyADR36Amino(signer, data, pubKey, signature) { const signDoc = makeADR36AminoSignDoc(signer, data); return verifyADR36AminoSignDoc(signDoc, pubKey, signature); } /** * Verifies a StdSignature object against the given signer address and expected message. * @category Utils */ const verifyArbitrary = async (signerAddress, expectedMessage, signatureToVerify) => { try { const { pub_key: pubKey, signature } = signatureToVerify; return verifyADR36Amino(signerAddress, expectedMessage, (0, encoding_1.fromBase64)(pubKey.value), (0, encoding_1.fromBase64)(signature)); } catch (e) { console.log('error verifying signature', e); return false; } }; exports.verifyArbitrary = verifyArbitrary;