-
Notifications
You must be signed in to change notification settings - Fork 1
/
KeyTrackerB.ts
131 lines (114 loc) · 4.72 KB
/
KeyTrackerB.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// AKM -> Advanced Key Managagement
// import { mk_key_pair } from "./functions"
import { ethers } from "ethers"
import BaseKeyTracker from "./BaseKeyTracker"
import { hash, pubFromPri } from "./functions"
import { KeyPair, PubPair, RandPair } from "./types"
export type CompressedKeyPair = {
secret: string,
pkh: string,
}
export type AdvancedKeyPair = CompressedKeyPair & KeyPair
// FOR EASY READING
const COMBINE = (a: string, b: string) => ethers.utils.solidityPack(['uint256', 'uint256'], [a, b])
const HASH = (a: string) => ethers.utils.keccak256(a)
const GENERATE_INITIAL_SECRET = () => ethers.utils.keccak256(ethers.utils.toUtf8Bytes(ethers.BigNumber.from(ethers.utils.randomBytes(32)).toHexString()))
const dropFirstTwoChars = (a: string) => a.slice(2)
/**
* @name uncompressLamport
* @description Uncompresses a compressed key pair
* @date Febuary 15th 2023
* @author William Doyle
*/
export function uncompressLamport(compressed: CompressedKeyPair): AdvancedKeyPair {
// 1. generate 512 intermediate secrets
const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => HASH(COMBINE(compressed.secret, index.toString())))
// const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => dropFirstTwoChars(HASH(COMBINE(compressed.secret, index.toString()))))
// 2. pair them up
const leftIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 0)
const rightIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 1)
const pri: RandPair[] = leftIntermediateSecrets.map((l, i) => [l, rightIntermediateSecrets[i]]) as RandPair[]
// 3. derive public key
const pub: PubPair[] = pubFromPri(pri.map(p => [p[0], p[1]]))
// 4. derive hash of public key
const pkh = BaseKeyTracker.pkhFromPublicKey(pub)
// 5. verify hash matches
if (pkh !== compressed.pkh)
throw new Error('Public Key Hash Does Not Match Secret')
// 6. return key pair
return {
...compressed,
pri,
pub
} as AdvancedKeyPair
}
/**
* @name compressLamport
* @description Compresses a key pair to only the secret and the public key hash
* @date Febuary 15th 2023
* @author William Doyle
*/
export function compressLamport(keyPair: AdvancedKeyPair): CompressedKeyPair {
return {
secret: keyPair.secret,
pkh: keyPair.pkh
} as CompressedKeyPair
}
export function mk_compressed_key_pair(): AdvancedKeyPair {
// generate single 32 bytes secret
const secret: string = GENERATE_INITIAL_SECRET()
// derive 512 intermediate secrets
const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => HASH(COMBINE(secret, index.toString())))
// const intermediate_secrets: string[] = Array.from({ length: 512 }).map((_, index: number) => dropFirstTwoChars(HASH(COMBINE(secret, index.toString()))))
// pair them up
const leftIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 0)
const rightIntermediateSecrets: string[] = intermediate_secrets.filter((_, i) => i % 2 === 1)
// zip them up
const pri: RandPair[] = leftIntermediateSecrets.map((l, i) => [l, rightIntermediateSecrets[i]]) as RandPair[]
// derive public key
const pub: PubPair[] = pubFromPri(pri.map(p => [p[0], p[1]]))
// derive hash of public key
const pkh = BaseKeyTracker.pkhFromPublicKey(pub)
return {
pri,
pub,
secret,
pkh
} as AdvancedKeyPair
}
/**
* @name KeyTrackerB
* @description A class that keeps track of keys and allows you to get them
* @date Febuary 15th 2023
* @author William Doyle
*/
export default class KeyTrackerB extends BaseKeyTracker {
keys: CompressedKeyPair[] = []
redeemedKeys : string[] = [] // only the public key hashes
get count() {
return this.keys.length
}
get exhausted(): boolean {
return this.count === 0
}
more(amount: number = 2): AdvancedKeyPair[] {
const keys = Array.from({ length: amount }, () => mk_compressed_key_pair())
const asCompressed = keys.map(k => compressLamport(k))
this.keys.push(...asCompressed) // save as compressed
return keys // return as uncompressed
}
getOne() {
const returnValue = this.keys.shift()
if (returnValue === undefined)
throw new Error('No keys left')
this.redeemedKeys.push(returnValue.pkh)
return uncompressLamport(returnValue)
}
getN(amount: number) {
// return this.keys.splice(0, amount).map(k => uncompressLamport(k))
return this.keys.splice(0, amount).map(k => {
this.redeemedKeys.push(k.pkh)
return uncompressLamport(k)
})
}
}