Skip to content

Commit

Permalink
defines MultisigHardwareSigner for Ledger multisig keys (#5366)
Browse files Browse the repository at this point in the history
a multisig account generated using a Ledger device will have a access to the
participant identity, but not the secret or keyPackage

uses a separate interface for MultisigHardwareSigner to cover this case and
expands the MultisigKeys type to cover this interface

the distinct interface allows us to store multisig keys for the Ledger case
without a database migration for existing multisig keys
  • Loading branch information
hughy authored Sep 12, 2024
1 parent 18f1bea commit 1b640a2
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 3 deletions.
7 changes: 6 additions & 1 deletion ironfish/src/wallet/interfaces/multisigKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ export interface MultisigSigner {
publicKeyPackage: string
}

export interface MultisigHardwareSigner {
identity: string
publicKeyPackage: string
}

export interface MultisigCoordinator {
publicKeyPackage: string
}

export type MultisigKeys = MultisigSigner | MultisigCoordinator
export type MultisigKeys = MultisigSigner | MultisigHardwareSigner | MultisigCoordinator
20 changes: 19 additions & 1 deletion ironfish/src/wallet/walletdb/multiSigKeys.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { MultisigCoordinator, MultisigSigner } from '../interfaces/multisigKeys'
import {
MultisigCoordinator,
MultisigHardwareSigner,
MultisigSigner,
} from '../interfaces/multisigKeys'
import { MultisigKeysEncoding } from './multisigKeys'

describe('multisigKeys encoder', () => {
Expand Down Expand Up @@ -32,4 +36,18 @@ describe('multisigKeys encoder', () => {
expect(deserializedValue).toEqual(value)
})
})

describe('with a hardware multisig', () => {
it('serializes the value into a buffer and deserialized to the original value', () => {
const encoder = new MultisigKeysEncoding()

const value: MultisigHardwareSigner = {
publicKeyPackage: 'aaaa',
identity: 'c0ffee',
}
const buffer = encoder.serialize(value)
const deserializedValue = encoder.deserialize(buffer)
expect(deserializedValue).toEqual(value)
})
})
})
24 changes: 23 additions & 1 deletion ironfish/src/wallet/walletdb/multisigKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@
import bufio from 'bufio'
import { Assert } from '../../assert'
import { IDatabaseEncoding } from '../../storage'
import { MultisigKeys, MultisigSigner } from '../interfaces/multisigKeys'
import {
MultisigHardwareSigner,
MultisigKeys,
MultisigSigner,
} from '../interfaces/multisigKeys'

export class MultisigKeysEncoding implements IDatabaseEncoding<MultisigKeys> {
serialize(value: MultisigKeys): Buffer {
const bw = bufio.write(this.getSize(value))

let flags = 0
flags |= Number(!!isSignerMultisig(value)) << 0
flags |= Number(!!isHardwareSignerMultisig(value)) << 1
bw.writeU8(flags)

bw.writeVarBytes(Buffer.from(value.publicKeyPackage, 'hex'))
if (isSignerMultisig(value)) {
bw.writeVarBytes(Buffer.from(value.secret, 'hex'))
bw.writeVarBytes(Buffer.from(value.keyPackage, 'hex'))
} else if (isHardwareSignerMultisig(value)) {
bw.writeVarBytes(Buffer.from(value.identity, 'hex'))
}

return bw.render()
Expand All @@ -28,6 +35,7 @@ export class MultisigKeysEncoding implements IDatabaseEncoding<MultisigKeys> {

const flags = reader.readU8()
const isSigner = flags & (1 << 0)
const isHardwareSigner = flags & (1 << 1)

const publicKeyPackage = reader.readVarBytes().toString('hex')
if (isSigner) {
Expand All @@ -38,6 +46,12 @@ export class MultisigKeysEncoding implements IDatabaseEncoding<MultisigKeys> {
secret,
keyPackage,
}
} else if (isHardwareSigner) {
const identity = reader.readVarBytes().toString('hex')
return {
publicKeyPackage,
identity,
}
}

return {
Expand All @@ -53,6 +67,8 @@ export class MultisigKeysEncoding implements IDatabaseEncoding<MultisigKeys> {
if (isSignerMultisig(value)) {
size += bufio.sizeVarString(value.secret, 'hex')
size += bufio.sizeVarString(value.keyPackage, 'hex')
} else if (isHardwareSignerMultisig(value)) {
size += bufio.sizeVarString(value.identity, 'hex')
}

return size
Expand All @@ -63,6 +79,12 @@ export function isSignerMultisig(multisigKeys: MultisigKeys): multisigKeys is Mu
return 'keyPackage' in multisigKeys && 'secret' in multisigKeys
}

export function isHardwareSignerMultisig(
multisigKeys: MultisigKeys,
): multisigKeys is MultisigHardwareSigner {
return 'identity' in multisigKeys
}

export function AssertIsSignerMultisig(
multisigKeys: MultisigKeys,
): asserts multisigKeys is MultisigSigner {
Expand Down

0 comments on commit 1b640a2

Please sign in to comment.