@polkadot/keyring/types#KeyringOptions TypeScript Examples
The following examples show how to use
@polkadot/keyring/types#KeyringOptions.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: verifyAddress.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
verifySignature = async (
models: DB,
chain: ChainInstance,
addressModel: AddressInstance,
user_id: number,
signatureString: string
): Promise<boolean> => {
if (!chain) {
log.error('no chain provided to verifySignature');
return false;
}
let isValid: boolean;
if (chain.base === ChainBase.Substrate) {
//
// substrate address handling
//
const address = decodeAddress(addressModel.address);
const keyringOptions: KeyringOptions = { type: 'sr25519' };
if (
!addressModel.keytype ||
addressModel.keytype === 'sr25519' ||
addressModel.keytype === 'ed25519'
) {
if (addressModel.keytype) {
keyringOptions.type = addressModel.keytype as KeypairType;
}
keyringOptions.ss58Format = chain.ss58_prefix ?? 42;
const signerKeyring = new Keyring(keyringOptions).addFromAddress(address);
const signedMessageNewline = stringToU8a(
`${addressModel.verification_token}\n`
);
const signedMessageNoNewline = stringToU8a(
addressModel.verification_token
);
const signatureU8a =
signatureString.slice(0, 2) === '0x'
? hexToU8a(signatureString)
: hexToU8a(`0x${signatureString}`);
isValid =
signerKeyring.verify(signedMessageNewline, signatureU8a, address) ||
signerKeyring.verify(signedMessageNoNewline, signatureU8a, address);
} else {
log.error('Invalid keytype.');
isValid = false;
}
} else if (
chain.base === ChainBase.CosmosSDK &&
addressModel.wallet_id === WalletId.CosmosEvmMetamask
) {
//
// ethereum address handling on cosmos chains
//
const msgBuffer = Buffer.from(addressModel.verification_token.trim());
// toBuffer() doesn't work if there is a newline
const msgHash = ethUtil.hashPersonalMessage(msgBuffer);
const ethSignatureParams = ethUtil.fromRpcSig(signatureString.trim());
const publicKey = ethUtil.ecrecover(
msgHash,
ethSignatureParams.v,
ethSignatureParams.r,
ethSignatureParams.s
);
const addressBuffer = ethUtil.publicToAddress(publicKey);
const lowercaseAddress = ethUtil.bufferToHex(addressBuffer);
try {
// const ethAddress = Web3.utils.toChecksumAddress(lowercaseAddress);
const injAddrBuf = ethUtil.Address.fromString(
lowercaseAddress.toString()
).toBuffer();
const injAddress = bech32.encode(
chain.bech32_prefix,
bech32.toWords(injAddrBuf)
);
if (addressModel.address === injAddress) isValid = true;
} catch (e) {
isValid = false;
}
} else if (chain.base === ChainBase.CosmosSDK && chain.bech32_prefix === 'terra') {
//
// cosmos-sdk address handling
//
// provided string should be serialized AminoSignResponse object
const { signature: stdSignature } =
JSON.parse(signatureString);
// we generate an address from the actual public key and verify that it matches,
// this prevents people from using a different key to sign the message than
// the account they registered with.
// TODO: ensure ion works
const bech32Prefix = chain.bech32_prefix;
if (!bech32Prefix) {
log.error('No bech32 prefix found.');
isValid = false;
} else {
const generatedAddress = pubkeyToAddress(
stdSignature.pub_key,
bech32Prefix
);
if (generatedAddress === addressModel.address) {
try {
// directly verify the generated signature, generated via SignBytes
const { pubkey, signature } = decodeSignature(stdSignature)
const secpSignature = Secp256k1Signature.fromFixedLength(signature);
const messageHash = new Sha256(
Buffer.from(addressModel.verification_token.trim())
).digest();
isValid = await Secp256k1.verifySignature(
secpSignature,
messageHash,
pubkey
);
} catch (e) {
isValid = false;
}
}
}
} else if (chain.base === ChainBase.CosmosSDK) {
//
// cosmos-sdk address handling
//
// provided string should be serialized AminoSignResponse object
const { signed, signature: stdSignature }: AminoSignResponse =
JSON.parse(signatureString);
// we generate an address from the actual public key and verify that it matches,
// this prevents people from using a different key to sign the message than
// the account they registered with.
// TODO: ensure ion works
const bech32Prefix = chain.bech32_prefix;
if (!bech32Prefix) {
log.error('No bech32 prefix found.');
isValid = false;
} else {
const generatedAddress = pubkeyToAddress(
stdSignature.pub_key,
bech32Prefix
);
const generatedAddressWithCosmosPrefix = pubkeyToAddress(
stdSignature.pub_key,
'cosmos'
);
if (
generatedAddress === addressModel.address ||
generatedAddressWithCosmosPrefix === addressModel.address
) {
let generatedSignDoc: StdSignDoc;
try {
// query chain ID from URL
const node = await chain.getChainNode();
const client = await StargateClient.connect(node.url);
const chainId = await client.getChainId();
client.disconnect();
generatedSignDoc = validationTokenToSignDoc(
chainId,
addressModel.verification_token.trim(),
signed.fee,
signed.memo,
<any>signed.msgs
);
} catch (e) {
log.info(e.message);
}
// ensure correct document was signed
if (
generatedSignDoc &&
serializeSignDoc(signed).toString() ===
serializeSignDoc(generatedSignDoc).toString()
) {
// ensure valid signature
const { pubkey, signature } = decodeSignature(stdSignature);
const secpSignature = Secp256k1Signature.fromFixedLength(signature);
const messageHash = new Sha256(
serializeSignDoc(generatedSignDoc)
).digest();
isValid = await Secp256k1.verifySignature(
secpSignature,
messageHash,
pubkey
);
if (!isValid) {
log.error('Signature verification failed.');
}
} else {
log.error(
`Sign doc not matched. Generated: ${JSON.stringify(
generatedSignDoc
)}, found: ${JSON.stringify(signed)}.`
);
isValid = false;
}
} else {
log.error(
`Address not matched. Generated ${generatedAddress}, found ${addressModel.address}.`
);
isValid = false;
}
}
} else if (chain.base === ChainBase.Ethereum) {
//
// ethereum address handling
//
try {
const node = await chain.getChainNode();
const typedMessage = constructTypedMessage(node.eth_chain_id || 1, addressModel.verification_token.trim());
const address = recoverTypedSignature({
data: typedMessage,
signature: signatureString.trim(),
version: SignTypedDataVersion.V4,
});
isValid = addressModel.address.toLowerCase() === address.toLowerCase();
if (!isValid) {
log.info(
`Eth verification failed for ${addressModel.address}: does not match recovered address ${address}`
);
}
} catch (e) {
log.info(
`Eth verification failed for ${addressModel.address}: ${e.message}`
);
isValid = false;
}
} else if (chain.base === ChainBase.NEAR) {
//
// near address handling
//
// both in base64 encoding
const { signature: sigObj, publicKey } = JSON.parse(signatureString);
isValid = nacl.sign.detached.verify(
Buffer.from(`${addressModel.verification_token}\n`),
Buffer.from(sigObj, 'base64'),
Buffer.from(publicKey, 'base64')
);
} else if (chain.base === ChainBase.Solana) {
//
// solana address handling
//
// ensure address is base58 string length 32, cf @solana/web3 impl
try {
const decodedAddress = bs58.decode(addressModel.address);
if (decodedAddress.length === 32) {
isValid = nacl.sign.detached.verify(
Buffer.from(`${addressModel.verification_token}`),
Buffer.from(signatureString, 'base64'),
decodedAddress
);
} else {
isValid = false;
}
} catch (e) {
isValid = false;
}
} else {
// invalid network
log.error(`invalid network: ${chain.network}`);
isValid = false;
}
addressModel.last_active = new Date();
if (isValid && user_id === null) {
// mark the address as verified, and if it doesn't have an associated user, create a new user
addressModel.verification_token_expires = null;
addressModel.verified = new Date();
if (!addressModel.user_id) {
const user = await models.User.createWithProfile(models, { email: null });
addressModel.profile_id = (user.Profiles[0] as ProfileAttributes).id;
await models.Subscription.create({
subscriber_id: user.id,
category_id: NotificationCategories.NewMention,
object_id: `user-${user.id}`,
is_active: true,
});
await models.Subscription.create({
subscriber_id: user.id,
category_id: NotificationCategories.NewCollaboration,
object_id: `user-${user.id}`,
is_active: true,
});
addressModel.user_id = user.id;
}
} else if (isValid) {
// mark the address as verified
addressModel.verification_token_expires = null;
addressModel.verified = new Date();
addressModel.user_id = user_id;
const profile = await models.Profile.findOne({ where: { user_id } });
addressModel.profile_id = profile.id;
}
await addressModel.save();
return isValid;
}