Skip to content

Commit

Permalink
Spend auth signature (#5)
Browse files Browse the repository at this point in the history
* add spend auth and delegator vote signatures
  • Loading branch information
abenso authored Dec 23, 2024
1 parent 1f6323e commit e030e74
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Main"
name: 'Main'
on:
- push

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Publish packages"
name: 'Publish packages'

on:
release:
Expand All @@ -25,8 +25,8 @@ jobs:
- name: Install node
uses: actions/setup-node@v4
with:
registry-url: "https://registry.npmjs.org"
scope: "@zondax"
registry-url: 'https://registry.npmjs.org'
scope: '@zondax'
- run: mv README-npm.md README.md
- name: Install yarn
run: npm install -g yarn
Expand Down
69 changes: 60 additions & 9 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@
import BaseApp, {
BIP32Path,
ConstructorParams,
LedgerError,
PAYLOAD_TYPE,
ResponsePayload,
Transport,
processErrorResponse,
processResponse,
} from '@zondax/ledger-js'

import { DEFAULT_PATH, P2_VALUES, PREHASH_LEN, RANDOMIZER_LEN, SIGRSLEN } from './consts'
import { processGetAddrResponse, processGetFvkResponse } from './helper'
import { AddressIndex, PenumbraIns, ResponseAddress, ResponseFvk, ResponseSign } from './types'
import { DEFAULT_PATH, P2_VALUES, RANDOMIZER_LEN, SPEND_AUTH_SIGNATURE_LEN, DELEGATOR_VOTE_SIGNATURE_LEN } from './consts'
import { processGetAddrResponse, processGetFvkResponse, processEffectHashResponse } from './helper'
import { AddressIndex, PenumbraIns, ResponseAddress, ResponseFvk, ResponseSign, ResponseEffectHash } from './types'
import { ByteStream } from '@zondax/ledger-js/dist/byteStream'

// https://buf.build/penumbra-zone/penumbra/docs/main:penumbra.custody.v1#penumbra.custody.v1.ConfirmAddressRequest

Expand All @@ -46,6 +44,8 @@ export class PenumbraApp extends BaseApp {
SIGN: 0x02,
FVK: 0x03,
TX_METADATA: 0x04,
GET_SPEND_AUTH_SIGNATURES: 0x05,
GET_DELEGATOR_VOTE_SIGNATURES: 0x06,
},
p1Values: {
ONLY_RETRIEVE: 0x00,
Expand Down Expand Up @@ -126,9 +126,60 @@ export class PenumbraApp extends BaseApp {
for (let i = 1; i < chunks.length; i += 1) {
signatureResponse = await this.signSendChunk(this.INS.SIGN, 1 + i, chunks.length, chunks[i])
}

// | 64 bytes | 2 bytes | 2 bytes |
// | effect hash | spend auth signature qty | delegator vote signature qty |
let effectHashSignatures = processEffectHashResponse(signatureResponse)

// Get spend auth signatures
let spendAuthSignatures = []
for (let i = 0; i < effectHashSignatures.spendAuthSignatureQty; i++) {
spendAuthSignatures.push(await this._getSpendAuthSignatures(i))
}

// Get delegator vote signatures
let delegatorVoteSignatures = []
for (let i = 0; i < effectHashSignatures.delegatorVoteSignatureQty; i++) {
delegatorVoteSignatures.push(await this._getDelegatorVoteSignatures(i))
}

return {
signature: signatureResponse.readBytes(signatureResponse.length()),
effectHash: effectHashSignatures.effectHash,
spendAuthSignatures,
delegatorVoteSignatures,
}

} catch (e) {
throw processErrorResponse(e)
}
}

private async _getSpendAuthSignatures(index: number): Promise<Buffer> {
try {
if (index > 255) {
throw new Error('Index for obtaining spend authorization signatures cannot exceed 255')
}

const responseBuffer = await this.transport.send(this.CLA, this.INS.GET_SPEND_AUTH_SIGNATURES, index, P2_VALUES.DEFAULT)

const payload = processResponse(responseBuffer)
const spendAuthSignature = payload.readBytes(SPEND_AUTH_SIGNATURE_LEN)

return spendAuthSignature
} catch (e) {
throw processErrorResponse(e)
}
}

private async _getDelegatorVoteSignatures(index: number): Promise<Buffer> {
try {
if (index > 255) {
throw new Error('Index for obtaining delegator vote signatures cannot exceed 255')
}
const responseBuffer = await this.transport.send(this.CLA, this.INS.GET_DELEGATOR_VOTE_SIGNATURES, index, P2_VALUES.DEFAULT)
const payload = processResponse(responseBuffer)
const delegatorVoteSignature = payload.readBytes(DELEGATOR_VOTE_SIGNATURE_LEN)
return delegatorVoteSignature
} catch (e) {
throw processErrorResponse(e)
}
Expand Down Expand Up @@ -199,15 +250,15 @@ export class PenumbraApp extends BaseApp {
// Enforce exactly 3 levels
// this was set in our class constructor [3]
const serializedPath = this.serializePath(path)
const accountBuffer = this.serializeAccountIndex(addressIndex)
const accountBuffer = this._serializeAccountIndex(addressIndex)

// concatenate data
const concatenatedBuffer: Buffer = Buffer.concat([serializedPath, accountBuffer])

return concatenatedBuffer
}

private serializeAccountIndex(accountIndex: AddressIndex): Buffer {
private _serializeAccountIndex(accountIndex: AddressIndex): Buffer {
const accountBuffer = Buffer.alloc(4)
accountBuffer.writeUInt32LE(accountIndex.account)

Expand Down
3 changes: 3 additions & 0 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ export const FVKLEN = AK_LEN + NK_LEN
export const SIGRSLEN = 64
export const PREHASH_LEN = 32
export const RANDOMIZER_LEN = 12
export const EFFECT_HASH_LEN = 64
export const SPEND_AUTH_SIGNATURE_LEN = 64
export const DELEGATOR_VOTE_SIGNATURE_LEN = 64
16 changes: 14 additions & 2 deletions src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ResponsePayload } from '@zondax/ledger-js/dist/payload'

import { ADDRLEN, AK_LEN, FVKLEN, NK_LEN } from './consts'
import { ResponseAddress, ResponseFvk } from './types'
import { ADDRLEN, AK_LEN, EFFECT_HASH_LEN, FVKLEN } from './consts'
import { ResponseAddress, ResponseEffectHash, ResponseFvk } from './types'

export function processGetAddrResponse(response: ResponsePayload): ResponseAddress {
const address = response.readBytes(ADDRLEN)
Expand All @@ -23,3 +23,15 @@ export function processGetFvkResponse(response: ResponsePayload): ResponseFvk {
nk,
}
}

export function processEffectHashResponse(response: ResponsePayload): ResponseEffectHash {
const effectHash = response.readBytes(EFFECT_HASH_LEN)
const spendAuthSignatureQty = response.readBytes(2).readUInt16LE(0)
const delegatorVoteSignatureQty = response.readBytes(2).readUInt16LE(0)

return {
effectHash,
spendAuthSignatureQty,
delegatorVoteSignatureQty,
}
}
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./app";
export * from "./types";
export * from "./consts";
export * from "./helper";
export * from './app'
export * from './types'
export * from './consts'
export * from './helper'
14 changes: 13 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface PenumbraIns extends INSGeneric {
SIGN: 0x02
FVK: 0x03
TX_METADATA: 0x04
GET_SPEND_AUTH_SIGNATURES: 0x05
GET_DELEGATOR_VOTE_SIGNATURES: 0x06
}

export interface AddressIndex {
Expand All @@ -27,6 +29,16 @@ export interface ResponseFvk {
nk: Buffer
}

// Effect hash response: First response for the sign procedure
export interface ResponseEffectHash {
effectHash: Buffer
spendAuthSignatureQty: number
delegatorVoteSignatureQty: number
}

// API signature response
export interface ResponseSign {
signature: Buffer
effectHash: Buffer
spendAuthSignatures: Buffer[]
delegatorVoteSignatures: Buffer[]
}

0 comments on commit e030e74

Please sign in to comment.