/**
* Sets the `signature` property of the block to a precalculated value.
*
- * @param {string} [key] - 64-byte hexadecimal signature
+ * @param {string} signature - 64-byte hexadecimal signature
+ * @returns Block with `signature` value set
*/
sign (signature: string): Block
/**
* the `signature` property of the block.
*
* @param {string} [key] - 32-byte hexadecimal private key to use for signing
+ * @returns Block with `signature` value set
*/
async sign (key: string): Promise<Block>
/**
- * Signs the block using a Wallet. If successful, the result is stored in
- * the `signature` property of the block. The wallet must be unlocked prior to
- * signing.
- *
- * @param {Wallet} wallet - Wallet to use for signing
- * @param {number} index - Account in wallet to use for signing
- */
- async sign (wallet: Wallet, index: number): Promise<Block>
- /**
* Signs the block using a Ledger hardware wallet. If that fails, an error is
* thrown with the status code from the device. If successful, the result is
* stored in the `signature` property of the block. The wallet must be unlocked
*
* @param {number} index - Account index between 0x0 and 0x7fffffff
* @param {object} [frontier] - JSON of frontier block for offline signing
+ * @returns Block with `signature` value set
*/
async sign (index: number, frontier?: Block): Promise<Block>
+ /**
+ * Signs the block using a Wallet. If successful, the result is stored in
+ * the `signature` property of the block. The wallet must be unlocked prior to
+ * signing.
+ *
+ * @param {Wallet} wallet - Wallet to use for signing
+ * @param {number} index - Account in wallet to use for signing
+ * @returns Block with `signature` value set
+ */
+ async sign (wallet: Wallet, index: number): Promise<Block>
sign (input: unknown, param?: unknown): Block | Promise<Block> {
if (typeof input === 'string' && /^[A-F0-9]{128}$/.test(input)) {
this.signature = input
}
return new Promise(async (resolve, reject) => {
try {
- if (typeof input !== 'number' && typeof input !== 'string' && !(input instanceof Wallet)) {
+ if (typeof input === 'string' && /^[A-F0-9]{64}$/.test(input)) {
+ const signature = await NanoNaCl.detached(this.#hash(), hex.toBytes(input))
+ this.signature = bytes.toHex(signature)
+ } else if (typeof input !== 'number' && typeof input !== 'string' && !(input instanceof Wallet)) {
throw new TypeError('Invalid signing input')
- } else if (typeof input === 'string' && /^[A-F0-9]{64}$/.test(input)) {
- const sig = await NanoNaCl.detached(this.#hash(), hex.toBytes(input))
- this.signature = bytes.toHex(sig)
} else if (input instanceof Wallet && typeof param === 'number') {
const wallet = input
- await wallet.sign(param, this, 'hex')
+ await wallet.sign(param, this)
} else if (typeof input === 'number') {
const index = input
const { Ledger } = await import('./wallet/ledger')
console.warn('Error updating Ledger cache of previous block, attempting signature anyway', err)
}
}
- this.signature = await ledger.sign(index, this, 'hex')
+ await ledger.sign(index, this)
} else {
- throw new TypeError('invalid key for block signature', { cause: typeof input })
+ throw new TypeError('Invalid key for block signature', { cause: typeof input })
}
resolve(this)
} catch (err) {
*\r
* @param {number} index - Account to use for signing\r
* @param {(Block)} block - Block data to be hashed and signed\r
- * @returns {Promise<string>} Hexadecimal-formatted 64-byte signature\r
*/\r
- async sign (index: number, block: Block): Promise<Uint8Array<ArrayBuffer>>\r
- /**\r
- * Signs a block using the private key of the account at the wallet index\r
- * specified. The signature is appended to the signature field of the block\r
- * before being returned. The wallet must be unlocked prior to signing.\r
- *\r
- * @param {number} index - Account to use for signing\r
- * @param {(Block)} block - Block data to be hashed and signed\r
- * @returns {Promise<string>} Hexadecimal-formatted 64-byte signature\r
- */\r
- async sign (index: number, block: Block, format: 'hex'): Promise<string>\r
- async sign (index: number, block: Block, format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
+ async sign (index: number, block: Block): Promise<void>\r
+ async sign (index: unknown, block: unknown): Promise<void> {\r
try {\r
+ if (typeof index !== 'number') {\r
+ throw new TypeError('Index must be a number', { cause: index })\r
+ }\r
+ if (!(block instanceof Block)) {\r
+ throw new TypeError('Invalid Block', { cause: block })\r
+ }\r
const { signature } = await this.#vault.request<ArrayBuffer>({\r
action: 'sign',\r
index,\r
data: hex.toBuffer(block.hash)\r
})\r
- const sig = new Uint8Array(signature)\r
- block.signature = bytes.toHex(sig)\r
+ block.signature = bytes.toHex(new Uint8Array(signature))\r
clearTimeout(this.#lockTimer)\r
this.#lockTimer = setTimeout(() => this.lock(), 300000)\r
- return format === 'hex' ? block.signature : sig\r
} catch (err) {\r
throw new Error(`Failed to sign block`, { cause: err })\r
}\r
* @param {string} password Used previously to lock the wallet\r
* @returns True if successfully unlocked\r
*/\r
- async unlock (password: string): Promise<boolean> {\r
+ async unlock (password: string): Promise<boolean>\r
+ async unlock (password: unknown): Promise<boolean> {\r
try {\r
+ if (typeof password !== 'string') {\r
+ throw new TypeError('Password must be a string')\r
+ }\r
const { iv, salt, encrypted } = await Wallet.#get(this.#id)\r
const { isUnlocked } = await this.#vault.request<boolean>({\r
action: 'unlock',\r
* Sign a block with the Ledger device.\r
*\r
* @param {number} index - Account number\r
- * @param {object} block - Block data to sign\r
- * @returns {Promise<string>} Signature\r
+ * @param {Block} block - Block data to sign\r
+ * @param {Block} [frontier] - Previous block data to cache in the device\r
*/\r
- async sign (index: number, block: Block): Promise<Uint8Array<ArrayBuffer>>\r
- /**\r
- * Sign a block with the Ledger device.\r
- *\r
- * @param {number} index - Account number\r
- * @param {object} block - Block data to sign\r
- * @returns {Promise<string>} Signature\r
- */\r
- async sign (index: number, block: Block, format?: 'hex', frontier?: Block): Promise<string>\r
- async sign (index: number, block: Block, format?: 'hex', frontier?: Block): Promise<string | Uint8Array<ArrayBuffer>> {\r
- if (typeof index !== 'number' || index < 0 || index >= HARDENED_OFFSET) {\r
- throw new TypeError('Invalid account index')\r
- }\r
- if (frontier != null) {\r
- const { status } = await this.#cacheBlock(index, frontier)\r
+ async sign (index: number, block: Block, frontier?: Block): Promise<void>\r
+ async sign (index: number, block: Block, frontier?: Block): Promise<void> {\r
+ try {\r
+ if (typeof index !== 'number') {\r
+ throw new TypeError('Index must be a number', { cause: index })\r
+ }\r
+ if (index < 0 || index >= HARDENED_OFFSET) {\r
+ throw new RangeError(`Index outside allowed range 0-${HARDENED_OFFSET}`, { cause: index })\r
+ }\r
+ if (frontier != null) {\r
+ const { status } = await this.#cacheBlock(index, frontier)\r
+ if (status !== 'OK') {\r
+ throw new Error('Failed to cache frontier block in ledger', { cause: status })\r
+ }\r
+ }\r
+ console.log('Waiting for signature confirmation on Ledger device...')\r
+ const { status, signature, hash } = await this.#signBlock(index, block)\r
if (status !== 'OK') {\r
- throw new Error('failed to cache frontier block in ledger', { cause: status })\r
+ throw new Error('Signing with ledger failed', { cause: status })\r
}\r
+ if (hash !== block.hash) {\r
+ throw new Error('Hash from ledger does not match hash from block', { cause: `${hash} | ${block.hash}` })\r
+ }\r
+ if (signature == null) {\r
+ throw new Error('Ledger silently failed to return signature')\r
+ }\r
+ block.signature = signature\r
+ } catch (err) {\r
+ console.error(err)\r
+ throw new Error('Failed to sign block with Ledger', { cause: err })\r
}\r
- console.log('Waiting for signature confirmation on Ledger device...')\r
- const { status, signature, hash } = await this.#signBlock(index, block)\r
- if (status !== 'OK') {\r
- throw new Error('signing with ledger failed', { cause: status })\r
- }\r
- if (hash !== block.hash) {\r
- throw new Error('hash from ledger does not match hash from block', { cause: `${hash} | ${block.hash}` })\r
- }\r
- if (signature == null) {\r
- throw new Error('ledger failed to return signature')\r
- }\r
- block.signature = signature\r
- return format === 'hex' ? signature : hex.toBytes(signature)\r
}\r
\r
/**\r
.sign(testWallet, 0)\r
const testSendBlock = new Block(testAccount.address, '0', testOpenBlock.hash, testAccount.address)\r
.send(testAccount.address, 0)\r
- const testSignature = await testWallet.sign(0, testOpenBlock, 'hex')\r
+ await testWallet.sign(0, testOpenBlock)\r
try {\r
- const signature = await this.sign(0, testSendBlock, 'hex', testOpenBlock)\r
- return signature === testSignature\r
+ await this.sign(0, testSendBlock, testOpenBlock)\r
+ return testSendBlock.signature === testOpenBlock.signature\r
} catch (err) {\r
throw new Error('Failed to verify wallet', { cause: err })\r
}\r