@sei-js/cosmjs
Version:
TypeScript library for CosmJS interactions on the Sei blockchain
142 lines (141 loc) • 5.76 kB
JavaScript
;
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;