js-conflux-sdk
Version:
JavaScript Conflux Software Development Kit
158 lines (147 loc) • 4.57 kB
JavaScript
const { keccak256, ecdsaSign, ecdsaRecover, publicKeyToAddress } = require('./util/sign');
const format = require('./util/format');
/** Class provide message sign utilities. */
class Message {
/**
* Signs the hash with the privateKey.
*
* @param {string|Buffer} privateKey - Private key used to sign message
* @param {string|Buffer} messageHash - The message hash need to be signed
* @return {string} The signature as hex string.
*
* @example
* > Message.sign(
'0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', // privateKey
'0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba',
)
"0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01"
*/
static sign(privateKey, messageHash) {
const { r, s, v } = ecdsaSign(format.hexBuffer(messageHash), format.hexBuffer(privateKey));
const buffer = Buffer.concat([r, s, format.hexBuffer(v)]);
return format.hex(buffer);
}
/**
* Recovers the signers publicKey from the signature.
*
* @param {string|Buffer} signature
* @param {string|Buffer} messageHash
* @return {string} The publicKey as hex string.
*
* @example
* > Message.recover(
'0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01',
'0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba',
)
"0x4646ae5047316b4230d0086c8acec687f00b1cd9d1dc634f6cb358ac0a9a8ffffe77b4dd0a4bfb95851f3b7355c781dd60f8418fc8a65d14907aff47c903a559"
*/
static recover(signature, messageHash) {
const signatureBuffer = format.hexBuffer(signature);
const r = signatureBuffer.slice(0, 32);
const s = signatureBuffer.slice(32, 64);
const v = signatureBuffer[64];
const buffer = ecdsaRecover(format.hexBuffer(messageHash), { r, s, v });
return format.publicKey(buffer);
}
/**
* @param {string} message
* @return {Message}
*
* @example
* > msg = new Message('Hello World');
Message {
message: 'Hello World',
}
* > msg.sign('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef');
Message {
message: 'Hello World',
signature: '0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01'
}
* > msg.signature
"0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01"
* > msg.hash
"0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
* > msg.from
"cfxtest:aasm4c231py7j34fghntcfkdt2nm9xv1tu6jd3r1s7"
* > msg.r
"0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c"
* > msg.s
"0x29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f"
* > msg.v
1
*/
constructor(message) {
this.message = message;
}
/**
* Getter of message hash include signature.
*
* > Note: calculate every time.
*
* @return {string}
*/
get hash() {
return format.hex(keccak256(Buffer.from(this.message)));
}
/**
* Getter of sender address.
*
* > Note: calculate every time.
*
* @return {string|undefined} If ECDSA recover success return address, else return undefined.
*/
get from() {
try {
const publicKey = Message.recover(this.signature, this.hash);
return format.address(publicKeyToAddress(format.hexBuffer(publicKey)), this.networkId);
} catch (e) {
return undefined;
}
}
/**
* Sign message and set 'r','s','v' and 'hash'.
*
* @param {string} privateKey - Private key hex string.
* @param {number} networkId - Network id of account
* @return {Message}
*/
sign(privateKey, networkId) {
this.signature = Message.sign(privateKey, this.hash);
this.networkId = networkId;
return this;
}
/**
* Get signatures r
* @return {string}
*/
get r() {
try {
return this.signature.slice(0, 2 + 64);
} catch (e) {
return undefined;
}
}
/**
* Get signatures s
* @return {string}
*/
get s() {
try {
return `0x${this.signature.slice(2 + 64, 2 + 128)}`;
} catch (e) {
return undefined;
}
}
/**
* Get signatures v
* @return {number}
*/
get v() {
try {
return parseInt(this.signature.slice(2 + 128, 2 + 130), 16);
} catch (e) {
return undefined;
}
}
}
module.exports = Message;