@tech-bureau/mijin-catapult-tools 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/.env/dev.ts +23 -0
  2. package/.env/prod.ts +23 -0
  3. package/.github/workflows/main.yml +37 -0
  4. package/.prettierrc.json +5 -0
  5. package/LICENSE +21 -0
  6. package/README.md +646 -0
  7. package/bin/mijin-catapult-tools.js +3 -0
  8. package/dist/main.js +2 -0
  9. package/jest.config.js +8 -0
  10. package/package.json +55 -0
  11. package/src/__tests__/service/AccountServices.spec.ts +63 -0
  12. package/src/__tests__/service/CertificateServices.spec.ts +20 -0
  13. package/src/__tests__/service/MessageServices.spec.ts +48 -0
  14. package/src/__tests__/service/MosaicServices.spec.ts +61 -0
  15. package/src/cli.ts +18 -0
  16. package/src/commands/account/account.ts +16 -0
  17. package/src/commands/account/accountGenerate.ts +19 -0
  18. package/src/commands/account/accountInfo.ts +16 -0
  19. package/src/commands/mosaic/mosaic.ts +16 -0
  20. package/src/commands/mosaic/mosaicCreate.ts +20 -0
  21. package/src/commands/mosaic/mosaicInfo.ts +13 -0
  22. package/src/commands/transaction/transaction.ts +18 -0
  23. package/src/commands/transaction/transactionStatus.ts +13 -0
  24. package/src/commands/transaction/transfer.ts +16 -0
  25. package/src/commands/votingkey/votingkey.ts +18 -0
  26. package/src/commands/votingkey/votingkeyCreate.ts +15 -0
  27. package/src/commands/votingkey/votingkeyInfo.ts +13 -0
  28. package/src/commands/votingkey/votingkeyUpdate.ts +15 -0
  29. package/src/infrastructure/account/accountGenerate.ts +183 -0
  30. package/src/infrastructure/account/accountInfo.ts +154 -0
  31. package/src/infrastructure/mosaic/mosaicCreate.ts +149 -0
  32. package/src/infrastructure/mosaic/mosaicInfo.ts +40 -0
  33. package/src/infrastructure/transaction/transactionStatus.ts +57 -0
  34. package/src/infrastructure/transaction/transfer.ts +168 -0
  35. package/src/infrastructure/voting/votingCreate.ts +88 -0
  36. package/src/infrastructure/voting/votingInfo.ts +44 -0
  37. package/src/infrastructure/voting/votingUpdate.ts +94 -0
  38. package/src/service/AccountServices.ts +68 -0
  39. package/src/service/CertificateServices.ts +165 -0
  40. package/src/service/CmdServices.ts +17 -0
  41. package/src/service/Logger.ts +26 -0
  42. package/src/service/MessageServices.ts +17 -0
  43. package/src/service/MosaicServices.ts +62 -0
  44. package/src/service/RepositoryFactory.ts +107 -0
  45. package/src/service/TransactionServices.ts +268 -0
  46. package/src/service/VotingServices.ts +184 -0
  47. package/src/types/AccountInfo.ts +19 -0
  48. package/src/types/AccountType.ts +9 -0
  49. package/src/types/ConfigFile.ts +24 -0
  50. package/src/types/Environment.ts +5 -0
  51. package/src/types/IAccountOption.ts +16 -0
  52. package/src/types/ILinkOption.ts +3 -0
  53. package/src/types/IMosaicOption.ts +18 -0
  54. package/src/types/ITransactionOption.ts +5 -0
  55. package/src/types/ITransferOption.ts +9 -0
  56. package/src/types/IVotingOption.ts +21 -0
  57. package/src/types/index.ts +9 -0
  58. package/src/utils.ts +38 -0
  59. package/tsconfig.json +24 -0
  60. package/webpack.config.js +24 -0
@@ -0,0 +1,62 @@
1
+ import {
2
+ Currency,
3
+ Mosaic,
4
+ MosaicId,
5
+ UInt64,
6
+ MosaicNonce,
7
+ Address,
8
+ MosaicFlags,
9
+ MosaicRepository,
10
+ MosaicInfo,
11
+ } from '@tech-bureau/symbol-sdk'
12
+ import { Observable, firstValueFrom } from 'rxjs'
13
+
14
+ export default class MosaicServices {
15
+ constructor() {}
16
+
17
+ static create(mosaicId: string, amount: number) {
18
+ return new Mosaic(new MosaicId(mosaicId), UInt64.fromUint(amount))
19
+ }
20
+
21
+ static createCurencyToAbsolute(currency: Currency, amount: number) {
22
+ return currency.createAbsolute(amount)
23
+ }
24
+
25
+ static createCurencyToRelative(currency: Currency, amount: number) {
26
+ return currency.createRelative(amount)
27
+ }
28
+
29
+ static createMosaicId(nonce: MosaicNonce, address: Address) {
30
+ return MosaicId.createFromNonce(nonce, address)
31
+ }
32
+
33
+ static createMosaicNonce() {
34
+ return MosaicNonce.createRandom()
35
+ }
36
+
37
+ static createMosaicFlags(
38
+ supplyMutable: boolean,
39
+ transferable: boolean,
40
+ restrictable?: boolean | undefined,
41
+ revokable?: boolean | undefined
42
+ ) {
43
+ return MosaicFlags.create(supplyMutable, transferable, restrictable, revokable)
44
+ }
45
+
46
+ static async getMosaic(mosaicId: string, mosaicRepository: MosaicRepository) {
47
+ try {
48
+ return await firstValueFrom(mosaicRepository.getMosaic(new MosaicId(mosaicId)))
49
+ } catch (error) {
50
+ throw new Error(`Mosaic Not Found`)
51
+ }
52
+ }
53
+
54
+ static mosaicInfo(mosaicInfo: MosaicInfo) {
55
+ return {
56
+ ownerAddress: mosaicInfo.ownerAddress.plain(),
57
+ mosaicId: mosaicInfo.id.toHex(),
58
+ supply: mosaicInfo.supply.compact(),
59
+ divisibility: mosaicInfo.divisibility,
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,107 @@
1
+ import {
2
+ ChainInfo,
3
+ FinalizationProof,
4
+ NetworkConfiguration,
5
+ NetworkCurrencies,
6
+ NetworkType,
7
+ RepositoryFactoryHttp,
8
+ TransactionFees,
9
+ } from '@tech-bureau/symbol-sdk'
10
+ import { firstValueFrom } from 'rxjs'
11
+
12
+ let networkType: NetworkType
13
+ let currency: NetworkCurrencies
14
+ let generationHash: string
15
+ let epoch: number
16
+ let repo: RepositoryFactoryHttp
17
+ let tfees: TransactionFees
18
+ let networkProperties: NetworkConfiguration
19
+ let finalizationEpoch: number
20
+
21
+ export default class RepositoryFactory {
22
+ constructor(public url: string) {}
23
+
24
+ async init() {
25
+ repo = new RepositoryFactoryHttp(this.url)
26
+ networkType = await firstValueFrom(repo.getNetworkType())
27
+ currency = await firstValueFrom(repo.getCurrencies())
28
+ generationHash = await firstValueFrom(repo.getGenerationHash())
29
+ epoch = await firstValueFrom(repo.getEpochAdjustment())
30
+ tfees = await firstValueFrom(repo.createNetworkRepository().getTransactionFees())
31
+ networkProperties = await firstValueFrom(this.createNetwork().getNetworkProperties())
32
+ finalizationEpoch = (await firstValueFrom(this.createChainRepository().getChainInfo())).latestFinalizedBlock
33
+ .finalizationEpoch
34
+ }
35
+
36
+ getNetwork() {
37
+ return networkType
38
+ }
39
+
40
+ getCurrency() {
41
+ return currency
42
+ }
43
+
44
+ getGnerationHash() {
45
+ return generationHash
46
+ }
47
+
48
+ getEpoch() {
49
+ return epoch
50
+ }
51
+
52
+ getEinalizationEpoch() {
53
+ return finalizationEpoch
54
+ }
55
+
56
+ getTransactionFees() {
57
+ return tfees
58
+ }
59
+
60
+ getNetworkProperties() {
61
+ return networkProperties
62
+ }
63
+
64
+ createNetwork() {
65
+ return repo.createNetworkRepository()
66
+ }
67
+
68
+ createLitener() {
69
+ return repo.createListener()
70
+ }
71
+
72
+ createTransactionRepository() {
73
+ return repo.createTransactionRepository()
74
+ }
75
+
76
+ createReceiptRepository() {
77
+ return repo.createReceiptRepository()
78
+ }
79
+
80
+ createAccountRepository() {
81
+ return repo.createAccountRepository()
82
+ }
83
+
84
+ createNamespaceRepository() {
85
+ return repo.createNamespaceRepository()
86
+ }
87
+
88
+ createMultisigRepository() {
89
+ return repo.createMultisigRepository()
90
+ }
91
+
92
+ createFinalizationRepository() {
93
+ return repo.createFinalizationRepository()
94
+ }
95
+
96
+ createBlockRepository() {
97
+ return repo.createBlockRepository()
98
+ }
99
+
100
+ createChainRepository() {
101
+ return repo.createChainRepository()
102
+ }
103
+
104
+ createMosaicRepository() {
105
+ return repo.createMosaicRepository()
106
+ }
107
+ }
@@ -0,0 +1,268 @@
1
+ import {
2
+ AggregateTransaction,
3
+ Deadline,
4
+ InnerTransaction,
5
+ NetworkType,
6
+ Account,
7
+ CosignatureTransaction,
8
+ TransactionAnnounceResponse,
9
+ TransactionHttp,
10
+ AccountKeyLinkTransaction,
11
+ LinkAction,
12
+ NodeKeyLinkTransaction,
13
+ VotingKeyLinkTransaction,
14
+ VrfKeyLinkTransaction,
15
+ HashLockTransaction,
16
+ Mosaic,
17
+ SignedTransaction,
18
+ UInt64,
19
+ MultisigAccountModificationTransaction,
20
+ Address,
21
+ Message,
22
+ TransferTransaction,
23
+ IListener,
24
+ ReceiptRepository,
25
+ TransactionRepository,
26
+ TransactionService,
27
+ MosaicDefinitionTransaction,
28
+ MosaicNonce,
29
+ MosaicId,
30
+ MosaicFlags,
31
+ MosaicSupplyChangeTransaction,
32
+ MosaicSupplyChangeAction,
33
+ TransactionGroup,
34
+ TransactionStatusHttp,
35
+ TransactionStatus,
36
+ Transaction,
37
+ } from '@tech-bureau/symbol-sdk'
38
+ import { ChronoUnit } from '@js-joda/core'
39
+ import { firstValueFrom } from 'rxjs'
40
+ import CreateAccount from './AccountServices'
41
+
42
+ export default class TransactionServices {
43
+ constructor() {}
44
+
45
+ static async getTransactionStatus(url: string, transactionId: string): Promise<TransactionStatus> {
46
+ const transactionStatusHttp = new TransactionStatusHttp(url)
47
+ return await firstValueFrom(transactionStatusHttp.getTransactionStatus(transactionId))
48
+ }
49
+
50
+ static async getTransaction(url: string, transactionId: string, transactionStatus: string): Promise<Transaction> {
51
+ const transactionHttp = new TransactionHttp(url)
52
+ return await firstValueFrom(
53
+ transactionHttp.getTransaction(
54
+ transactionId,
55
+ transactionStatus === 'confirmed'
56
+ ? TransactionGroup.Confirmed
57
+ : transactionStatus === 'partial'
58
+ ? TransactionGroup.Partial
59
+ : TransactionGroup.Unconfirmed
60
+ )
61
+ )
62
+ }
63
+
64
+ static async announceCosign(
65
+ url: string,
66
+ account: Account,
67
+ transaction: AggregateTransaction
68
+ ): Promise<TransactionAnnounceResponse> {
69
+ const transactionHttp = new TransactionHttp(url)
70
+ const signdTransaction = account.signCosignatureTransaction(CosignatureTransaction.create(transaction))
71
+ return await firstValueFrom(transactionHttp.announceAggregateBondedCosignature(signdTransaction))
72
+ }
73
+
74
+ static async announceHashLockAggregateBonded(
75
+ signedHashLockTransaction: SignedTransaction,
76
+ signedTransaction: SignedTransaction,
77
+ listener: IListener,
78
+ transactionRepository: TransactionRepository,
79
+ receiptRepository: ReceiptRepository
80
+ ) {
81
+ const service = new TransactionService(transactionRepository, receiptRepository)
82
+
83
+ await listener.open()
84
+ const aggtx = await firstValueFrom(
85
+ service.announceHashLockAggregateBonded(signedHashLockTransaction, signedTransaction, listener)
86
+ ).catch((err) => {
87
+ listener.close()
88
+ throw new Error(err)
89
+ })
90
+ listener.close()
91
+ return aggtx
92
+ }
93
+
94
+ static async announce(
95
+ signedTransaction: SignedTransaction,
96
+ listener: IListener,
97
+ transactionRepository: TransactionRepository,
98
+ receiptRepository: ReceiptRepository
99
+ ) {
100
+ const service = new TransactionService(transactionRepository, receiptRepository)
101
+
102
+ await listener.open()
103
+ const tx = await firstValueFrom(service.announce(signedTransaction, listener)).catch((err) => {
104
+ listener.close()
105
+ throw new Error(err)
106
+ })
107
+ listener.close()
108
+ return tx
109
+ }
110
+
111
+ static createHashLock(
112
+ epoch: number,
113
+ mosaic: Mosaic,
114
+ signedTransaction: SignedTransaction,
115
+ networkType: NetworkType
116
+ ): HashLockTransaction {
117
+ return HashLockTransaction.create(
118
+ Deadline.create(epoch, 6, ChronoUnit.HOURS),
119
+ mosaic,
120
+ UInt64.fromUint(5760),
121
+ signedTransaction,
122
+ networkType
123
+ )
124
+ }
125
+
126
+ static createBonded(epoch: number, transactions: InnerTransaction[], networkType: NetworkType): AggregateTransaction {
127
+ return AggregateTransaction.createBonded(
128
+ Deadline.create(epoch, 24, ChronoUnit.HOURS),
129
+ transactions,
130
+ networkType,
131
+ []
132
+ )
133
+ }
134
+
135
+ static createComplete(
136
+ epoch: number,
137
+ transactions: InnerTransaction[],
138
+ networkType: NetworkType
139
+ ): AggregateTransaction {
140
+ return AggregateTransaction.createComplete(
141
+ Deadline.create(epoch, 24, ChronoUnit.HOURS),
142
+ transactions,
143
+ networkType,
144
+ []
145
+ )
146
+ }
147
+
148
+ static createVrfKey(epoch: number, linkPublickey: string, networkType: NetworkType): VrfKeyLinkTransaction {
149
+ return VrfKeyLinkTransaction.create(Deadline.create(epoch), linkPublickey, LinkAction.Link, networkType)
150
+ }
151
+
152
+ static createVotingKey(epoch: number, linkPublickey: string, networkType: NetworkType): VotingKeyLinkTransaction {
153
+ return VotingKeyLinkTransaction.create(
154
+ Deadline.create(epoch),
155
+ linkPublickey,
156
+ 1,
157
+ 360,
158
+ LinkAction.Link,
159
+ networkType,
160
+ 1
161
+ )
162
+ }
163
+
164
+ static createAccountKey(epoch: number, linkPublickey: string, networkType: NetworkType): AccountKeyLinkTransaction {
165
+ return AccountKeyLinkTransaction.create(Deadline.create(epoch), linkPublickey, LinkAction.Link, networkType)
166
+ }
167
+
168
+ static createNodeKey(epoch: number, linkPublickey: string, networkType: NetworkType): NodeKeyLinkTransaction {
169
+ return NodeKeyLinkTransaction.create(Deadline.create(epoch), linkPublickey, LinkAction.Link, networkType)
170
+ }
171
+
172
+ static createMultisig(
173
+ epoch: number,
174
+ minApproval: number,
175
+ minRemoval: number,
176
+ addAddresses: string[] | [],
177
+ delAddresses: string[] | [],
178
+ networkType: NetworkType
179
+ ) {
180
+ const addressAdditions =
181
+ addAddresses.length > 0 ? CreateAccount.createPublicAccountArray(addAddresses, networkType) : []
182
+ const addressDeletions =
183
+ delAddresses.length > 0 ? CreateAccount.createPublicAccountArray(delAddresses, networkType) : []
184
+ return MultisigAccountModificationTransaction.create(
185
+ Deadline.create(epoch, 6, ChronoUnit.HOURS),
186
+ minApproval,
187
+ minRemoval,
188
+ addressAdditions,
189
+ addressDeletions,
190
+ networkType
191
+ )
192
+ }
193
+
194
+ static createTransfer(
195
+ epoch: number,
196
+ recipientAddress: Address,
197
+ mosaic: Mosaic[] | [],
198
+ message: Message,
199
+ networkType: NetworkType
200
+ ): TransferTransaction {
201
+ return TransferTransaction.create(
202
+ Deadline.create(epoch, 6, ChronoUnit.HOURS),
203
+ recipientAddress,
204
+ mosaic,
205
+ message,
206
+ networkType
207
+ )
208
+ }
209
+
210
+ static createMosaicDefinition(
211
+ epoch: number,
212
+ nonce: MosaicNonce,
213
+ mosaicId: MosaicId,
214
+ mosaicFlags: MosaicFlags,
215
+ divisibility: number,
216
+ networkType: NetworkType,
217
+ duration?: number | undefined
218
+ ): MosaicDefinitionTransaction {
219
+ return MosaicDefinitionTransaction.create(
220
+ Deadline.create(epoch),
221
+ nonce,
222
+ mosaicId,
223
+ mosaicFlags,
224
+ divisibility,
225
+ duration ? UInt64.fromUint(duration) : UInt64.fromUint(0),
226
+ networkType
227
+ )
228
+ }
229
+
230
+ static createMosaicSupplyChange(
231
+ epoch: number,
232
+ mosaicId: MosaicId,
233
+ mosaicSupply: number,
234
+ networkType: NetworkType
235
+ ): MosaicSupplyChangeTransaction {
236
+ return MosaicSupplyChangeTransaction.create(
237
+ Deadline.create(epoch),
238
+ mosaicId,
239
+ MosaicSupplyChangeAction.Increase,
240
+ UInt64.fromUint(mosaicSupply),
241
+ networkType
242
+ )
243
+ }
244
+
245
+ static deleteVrf(epoch: number, linkPublickey: string, networkType: NetworkType): VrfKeyLinkTransaction {
246
+ return VrfKeyLinkTransaction.create(Deadline.create(epoch), linkPublickey, LinkAction.Unlink, networkType)
247
+ }
248
+
249
+ static deleteVoting(epoch: number, linkPublickey: string, networkType: NetworkType): VotingKeyLinkTransaction {
250
+ return VotingKeyLinkTransaction.create(
251
+ Deadline.create(epoch),
252
+ linkPublickey,
253
+ 1,
254
+ 26280,
255
+ LinkAction.Unlink,
256
+ networkType,
257
+ 1
258
+ )
259
+ }
260
+
261
+ static deleteAccount(epoch: number, linkPublickey: string, networkType: NetworkType): AccountKeyLinkTransaction {
262
+ return AccountKeyLinkTransaction.create(Deadline.create(epoch), linkPublickey, LinkAction.Unlink, networkType)
263
+ }
264
+
265
+ static deleteNode(epoch: number, linkPublickey: string, networkType: NetworkType): NodeKeyLinkTransaction {
266
+ return NodeKeyLinkTransaction.create(Deadline.create(epoch), linkPublickey, LinkAction.Unlink, networkType)
267
+ }
268
+ }
@@ -0,0 +1,184 @@
1
+ /*
2
+ * Copyright 2022 Fernando Boucquez
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import * as noble from '@noble/ed25519'
17
+ import { existsSync, lstatSync, readdirSync, readFileSync } from 'fs'
18
+ import { join } from 'path'
19
+ import { Convert, Crypto } from '@tech-bureau/symbol-sdk'
20
+ import * as nacl from 'tweetnacl'
21
+
22
+ export interface KeyPair {
23
+ privateKey: Uint8Array
24
+ publicKey: Uint8Array
25
+ }
26
+ export interface CryptoImplementation {
27
+ name: string
28
+ createKeyPairFromPrivateKey: (privateKey: Uint8Array) => Promise<KeyPair>
29
+ sign: (keyPair: KeyPair, data: Uint8Array) => Promise<Uint8Array>
30
+ }
31
+
32
+ export interface VotingKeyAccount {
33
+ readonly startEpoch: number
34
+ readonly endEpoch: number
35
+ readonly publicKey: string
36
+ }
37
+
38
+ export type VotingKeyFile = VotingKeyAccount & { filename: string }
39
+
40
+ export class VotingUtils {
41
+ public static nobleImplementation: CryptoImplementation = {
42
+ name: 'Noble',
43
+ createKeyPairFromPrivateKey: async (privateKey: Uint8Array): Promise<KeyPair> => {
44
+ const publicKey = await noble.getPublicKey(privateKey)
45
+ return { privateKey, publicKey: publicKey }
46
+ },
47
+ sign: async (keyPair: KeyPair, data: Uint8Array): Promise<Uint8Array> => {
48
+ return await noble.sign(data, keyPair.privateKey)
49
+ },
50
+ }
51
+
52
+ public static tweetNaClImplementation: CryptoImplementation = {
53
+ name: 'TweetNaCl',
54
+ createKeyPairFromPrivateKey: async (privateKey: Uint8Array): Promise<KeyPair> => {
55
+ const { publicKey } = nacl.sign.keyPair.fromSeed(privateKey)
56
+ return { privateKey, publicKey }
57
+ },
58
+
59
+ sign: async (keyPair: KeyPair, data: Uint8Array): Promise<Uint8Array> => {
60
+ const secretKey = new Uint8Array(64)
61
+ secretKey.set(keyPair.privateKey)
62
+ secretKey.set(keyPair.publicKey, 32)
63
+ return nacl.sign.detached(data, secretKey)
64
+ },
65
+ }
66
+
67
+ public static implementations = [VotingUtils.nobleImplementation, VotingUtils.tweetNaClImplementation]
68
+
69
+ constructor(private readonly implementation: CryptoImplementation = VotingUtils.nobleImplementation) {}
70
+ public insert(result: Uint8Array, value: Uint8Array, index: number): number {
71
+ result.set(value, index)
72
+ return index + value.length
73
+ }
74
+
75
+ public async createVotingFile(
76
+ secret: string,
77
+ votingKeyStartEpoch: number,
78
+ votingKeyEndEpoch: number,
79
+ unitTestPrivateKeys: Uint8Array[] | undefined = undefined
80
+ ): Promise<Uint8Array> {
81
+ const items = votingKeyEndEpoch - votingKeyStartEpoch + 1
82
+ const headerSize = 64 + 16
83
+ const itemSize = 32 + 64
84
+ const totalSize = headerSize + items * itemSize
85
+ const rootPrivateKey = await this.implementation.createKeyPairFromPrivateKey(Convert.hexToUint8(secret))
86
+ const result = new Uint8Array(totalSize)
87
+ //start-epoch (8b),
88
+ let index = 0
89
+ index = this.insert(result, Convert.numberToUint8Array(votingKeyStartEpoch, 8), index)
90
+
91
+ //end-epoch (8b),
92
+ index = this.insert(result, Convert.numberToUint8Array(votingKeyEndEpoch, 8), index)
93
+
94
+ // could it have other values????
95
+ //last key identifier (8b) - for fresh file this is 0xFFFF'FFFF'FFFF'FFFF (a.k.a. Invalid_Id)
96
+ index = this.insert(result, Convert.hexToUint8('FFFFFFFFFFFFFFFF'), index)
97
+
98
+ //last wipe key identifier (8b) - again, for fresh file this is 0xFFFF'FFFF'FFFF'FFFF (Invalid_Id)
99
+ index = this.insert(result, Convert.hexToUint8('FFFFFFFFFFFFFFFF'), index)
100
+
101
+ // root public key (32b) - this is root public key that is getting announced via vote link tx
102
+ index = this.insert(result, rootPrivateKey.publicKey, index)
103
+ // start-epoch (8b), \ those two are exactly same one, as top level, reason is this was earlier a tree,
104
+ index = this.insert(result, Convert.numberToUint8Array(votingKeyStartEpoch, 8), index)
105
+
106
+ //end-epoch (8b), / and each level holds this separately, so we left it as is
107
+ index = this.insert(result, Convert.numberToUint8Array(votingKeyEndEpoch, 8), index)
108
+ /// what follows are bound keys, there are (end - start + 1) of them.
109
+
110
+ // each key is:
111
+ for (let i = 0; i < items; i++) {
112
+ // random PRIVATE key (32b)
113
+ const randomPrivateKey = unitTestPrivateKeys ? unitTestPrivateKeys[i] : Crypto.randomBytes(32)
114
+ if (randomPrivateKey.length != 32) {
115
+ throw new Error(`Invalid private key size ${randomPrivateKey.length}!`)
116
+ }
117
+ const randomKeyPar = await this.implementation.createKeyPairFromPrivateKey(randomPrivateKey)
118
+ index = this.insert(result, randomPrivateKey, index)
119
+ // signature (64b)
120
+ // now the signature is usual signature done using ROOT private key on a following data:
121
+ // (public key (32b), identifier (8b))
122
+ //
123
+ // identifier is simply epoch, but, most importantly keys are written in REVERSE order.
124
+ //
125
+ // i.e. say your start-epoch = 2, end-epoch = 42
126
+ const identifier = Convert.numberToUint8Array(votingKeyEndEpoch - i, 8)
127
+ const signature = await this.implementation.sign(
128
+ rootPrivateKey,
129
+ Uint8Array.from([...randomKeyPar.publicKey, ...identifier])
130
+ )
131
+ index = this.insert(result, signature, index)
132
+ }
133
+ //
134
+ // root private key is discarded after file is created.
135
+ // header:
136
+ // 2, 42, ff.., ff..., (root pub), 2, 42
137
+ // keys:
138
+ // (priv key 42, sig 42), (priv key 41, sig 31), ..., (priv key 2, sig 2)
139
+ //
140
+ // every priv key should be cryptographically random,
141
+
142
+ return result
143
+ }
144
+
145
+ public readVotingFile(file: Uint8Array): VotingKeyAccount {
146
+ //start-epoch (8b),
147
+ const votingKeyStartEpoch = Convert.uintArray8ToNumber(file.slice(0, 8))
148
+ //end-epoch (8b),
149
+ const votingKeyEndEpoch = Convert.uintArray8ToNumber(file.slice(8, 16))
150
+ const votingPublicKey = Convert.uint8ToHex(file.slice(32, 64))
151
+
152
+ const items = votingKeyEndEpoch - votingKeyStartEpoch + 1
153
+ const headerSize = 64 + 16
154
+ const itemSize = 32 + 64
155
+ const totalSize = headerSize + items * itemSize
156
+
157
+ if (file.length != totalSize) {
158
+ throw new Error(`Unexpected voting key file. Expected ${totalSize} but got ${file.length}`)
159
+ }
160
+ return {
161
+ publicKey: votingPublicKey,
162
+ startEpoch: votingKeyStartEpoch,
163
+ endEpoch: votingKeyEndEpoch,
164
+ }
165
+ }
166
+
167
+ public loadVotingFiles(folder: string): VotingKeyFile[] {
168
+ if (!existsSync(folder)) {
169
+ return []
170
+ }
171
+ return readdirSync(folder)
172
+ .map((filename: string) => {
173
+ const currentPath = join(folder, filename)
174
+ if (lstatSync(currentPath).isFile() && filename.startsWith('private_key_tree') && filename.endsWith('.dat')) {
175
+ return { ...this.readVotingFile(readFileSync(currentPath)), filename }
176
+ } else {
177
+ return undefined
178
+ }
179
+ })
180
+ .filter((i) => i)
181
+ .map((i) => i as VotingKeyFile)
182
+ .sort((a, b) => a.startEpoch - b.startEpoch)
183
+ }
184
+ }
@@ -0,0 +1,19 @@
1
+ export interface AccountInfo {
2
+ url: string,
3
+ network: string,
4
+ account: string,
5
+ address: string,
6
+ mosaics: mosaics[],
7
+ linkedKeys: {
8
+ linked: string,
9
+ node: string,
10
+ vrf: string,
11
+ voting: string
12
+ }
13
+ }
14
+
15
+ interface mosaics {
16
+ mosaic: string;
17
+ amount: string;
18
+ namespaceAlias: string;
19
+ }[]
@@ -0,0 +1,9 @@
1
+ export enum AccountType {
2
+ work = 'workAccount',
3
+ balance = 'balanceAccount',
4
+ main = 'mainAccount',
5
+ test1 = 'test1Account',
6
+ test2 = 'test2Account',
7
+ voting = 'votingAccount',
8
+ vrf = 'vrfAccount',
9
+ }
@@ -0,0 +1,24 @@
1
+ export interface ConfigFile {
2
+ url: string
3
+ workAccount: ConfigAccount
4
+ balanceAccount: ConfigAccount
5
+ mainAccount: ConfigAccount
6
+ keylink: {
7
+ vrf: ConfigAccount
8
+ voting: ConfigAccount
9
+ }
10
+ multisig?: {
11
+ minApproval?: number
12
+ minRemoval?: number
13
+ addCosigner?: ConfigAccount[]
14
+ delCosigner?: ConfigAccount[]
15
+ }
16
+ test1Account: ConfigAccount
17
+ test2Account: ConfigAccount
18
+ }
19
+
20
+ interface ConfigAccount {
21
+ publicKey: string
22
+ privateKey: string
23
+ address: string
24
+ }
@@ -0,0 +1,5 @@
1
+ export enum Environment {
2
+ 'dev' = 'dev',
3
+ 'test' = 'test',
4
+ 'prod' = 'prod',
5
+ }
@@ -0,0 +1,16 @@
1
+ export interface IAccountGenerateOption {
2
+ url?: string
3
+ nodename?: string
4
+ readfile?: string
5
+ writefile?: string
6
+ certsdir?: string
7
+ privatekey?: string
8
+ service: boolean
9
+ }
10
+
11
+ export interface IAccountInfoOption {
12
+ type?: string
13
+ url?: string
14
+ readfile?: string
15
+ address?: string
16
+ }
@@ -0,0 +1,3 @@
1
+ export interface ILinkOption {
2
+ publicKey: string
3
+ }