algosdk 3.6.0 → 3.7.0-beta.1

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 (109) hide show
  1. package/dist/browser/algosdk.min.js +1 -1
  2. package/dist/browser/algosdk.min.js.map +1 -1
  3. package/dist/cjs/client/v2/algod/algod.js +0 -18
  4. package/dist/cjs/client/v2/algod/algod.js.map +1 -1
  5. package/dist/cjs/client/v2/algod/models/types.js +2 -434
  6. package/dist/cjs/client/v2/algod/models/types.js.map +1 -1
  7. package/dist/cjs/ed25519-signer.js +76 -0
  8. package/dist/cjs/ed25519-signer.js.map +1 -0
  9. package/dist/cjs/encoding/address.js +61 -1
  10. package/dist/cjs/encoding/address.js.map +1 -1
  11. package/dist/cjs/falcon-signer.js +17 -0
  12. package/dist/cjs/falcon-signer.js.map +1 -0
  13. package/dist/cjs/logicsig.js +201 -16
  14. package/dist/cjs/logicsig.js.map +1 -1
  15. package/dist/cjs/main.js +32 -63
  16. package/dist/cjs/main.js.map +1 -1
  17. package/dist/cjs/mnemonic/mnemonic.js +26 -1
  18. package/dist/cjs/mnemonic/mnemonic.js.map +1 -1
  19. package/dist/cjs/multisigSigning.js +78 -1
  20. package/dist/cjs/multisigSigning.js.map +1 -1
  21. package/dist/cjs/pq-signer.js +54 -0
  22. package/dist/cjs/pq-signer.js.map +1 -0
  23. package/dist/cjs/signedTransaction.js +15 -1
  24. package/dist/cjs/signedTransaction.js.map +1 -1
  25. package/dist/cjs/signer.js.map +1 -1
  26. package/dist/cjs/signing.js +94 -7
  27. package/dist/cjs/signing.js.map +1 -1
  28. package/dist/cjs/transaction.js +9 -1
  29. package/dist/cjs/transaction.js.map +1 -1
  30. package/dist/cjs/types/transactions/encoded.js +51 -1
  31. package/dist/cjs/types/transactions/encoded.js.map +1 -1
  32. package/dist/cjs/utils/ed25519-check.js +69 -0
  33. package/dist/cjs/utils/ed25519-check.js.map +1 -0
  34. package/dist/esm/client/v2/algod/algod.js +0 -18
  35. package/dist/esm/client/v2/algod/algod.js.map +1 -1
  36. package/dist/esm/client/v2/algod/models/types.js +0 -427
  37. package/dist/esm/client/v2/algod/models/types.js.map +1 -1
  38. package/dist/esm/ed25519-signer.js +72 -0
  39. package/dist/esm/ed25519-signer.js.map +1 -0
  40. package/dist/esm/encoding/address.js +58 -0
  41. package/dist/esm/encoding/address.js.map +1 -1
  42. package/dist/esm/falcon-signer.js +13 -0
  43. package/dist/esm/falcon-signer.js.map +1 -0
  44. package/dist/esm/logicsig.js +200 -15
  45. package/dist/esm/logicsig.js.map +1 -1
  46. package/dist/esm/main.js +8 -56
  47. package/dist/esm/main.js.map +1 -1
  48. package/dist/esm/mnemonic/mnemonic.js +25 -1
  49. package/dist/esm/mnemonic/mnemonic.js.map +1 -1
  50. package/dist/esm/multisigSigning.js +75 -0
  51. package/dist/esm/multisigSigning.js.map +1 -1
  52. package/dist/esm/pq-signer.js +50 -0
  53. package/dist/esm/pq-signer.js.map +1 -0
  54. package/dist/esm/signedTransaction.js +16 -2
  55. package/dist/esm/signedTransaction.js.map +1 -1
  56. package/dist/esm/signer.js.map +1 -1
  57. package/dist/esm/signing.js +91 -8
  58. package/dist/esm/signing.js.map +1 -1
  59. package/dist/esm/transaction.js +9 -1
  60. package/dist/esm/transaction.js.map +1 -1
  61. package/dist/esm/types/transactions/encoded.js +49 -1
  62. package/dist/esm/types/transactions/encoded.js.map +1 -1
  63. package/dist/esm/utils/ed25519-check.js +65 -0
  64. package/dist/esm/utils/ed25519-check.js.map +1 -0
  65. package/dist/types/client/v2/algod/algod.d.ts +0 -16
  66. package/dist/types/client/v2/algod/models/types.d.ts +0 -217
  67. package/dist/types/ed25519-signer.d.ts +7 -0
  68. package/dist/types/encoding/address.d.ts +37 -0
  69. package/dist/types/falcon-signer.d.ts +11 -0
  70. package/dist/types/logicsig.d.ts +72 -1
  71. package/dist/types/main.d.ts +8 -44
  72. package/dist/types/mnemonic/mnemonic.d.ts +13 -0
  73. package/dist/types/multisigSigning.d.ts +49 -0
  74. package/dist/types/pq-signer.d.ts +8 -0
  75. package/dist/types/signedTransaction.d.ts +7 -2
  76. package/dist/types/signer.d.ts +38 -0
  77. package/dist/types/signing.d.ts +45 -0
  78. package/dist/types/transaction.d.ts +9 -0
  79. package/dist/types/types/transactions/encoded.d.ts +27 -0
  80. package/dist/types/utils/ed25519-check.d.ts +10 -0
  81. package/package.json +2 -1
  82. package/src/client/v2/algod/algod.ts +0 -19
  83. package/src/client/v2/algod/models/types.ts +0 -700
  84. package/src/ed25519-signer.ts +125 -0
  85. package/src/encoding/address.ts +80 -0
  86. package/src/falcon-signer.ts +30 -0
  87. package/src/logicsig.ts +258 -16
  88. package/src/main.ts +30 -60
  89. package/src/mnemonic/mnemonic.ts +34 -1
  90. package/src/multisigSigning.ts +111 -0
  91. package/src/pq-signer.ts +90 -0
  92. package/src/signedTransaction.ts +24 -0
  93. package/src/signer.ts +53 -0
  94. package/src/signing.ts +109 -8
  95. package/src/transaction.ts +9 -1
  96. package/src/types/transactions/encoded.ts +88 -0
  97. package/src/utils/ed25519-check.ts +67 -0
  98. package/dist/cjs/client/v2/algod/dryrun.js +0 -34
  99. package/dist/cjs/client/v2/algod/dryrun.js.map +0 -1
  100. package/dist/cjs/dryrun.js +0 -214
  101. package/dist/cjs/dryrun.js.map +0 -1
  102. package/dist/esm/client/v2/algod/dryrun.js +0 -28
  103. package/dist/esm/client/v2/algod/dryrun.js.map +0 -1
  104. package/dist/esm/dryrun.js +0 -208
  105. package/dist/esm/dryrun.js.map +0 -1
  106. package/dist/types/client/v2/algod/dryrun.d.ts +0 -11
  107. package/dist/types/dryrun.d.ts +0 -28
  108. package/src/client/v2/algod/dryrun.ts +0 -38
  109. package/src/dryrun.ts +0 -292
@@ -10,6 +10,8 @@ import {
10
10
  addressFromMultisigPreImg,
11
11
  pksFromAddresses,
12
12
  } from './multisig.js';
13
+ import type { TransactionSigner } from './signer.js';
14
+ import { signTransactionWithSigner } from './signing.js';
13
15
 
14
16
  export const MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG =
15
17
  'Not enough multisig transactions to merge. Need at least two';
@@ -144,6 +146,36 @@ function partialSignTxn(
144
146
  );
145
147
  }
146
148
 
149
+ async function partialSignTxnWithSigner(
150
+ transaction: Transaction,
151
+ { version, threshold, pks }: MultisigMetadataWithPks,
152
+ txnSigner: TransactionSigner
153
+ ) {
154
+ const { stxn } = await signTransactionWithSigner(transaction, txnSigner);
155
+ const rawSig = stxn.sig;
156
+
157
+ if (rawSig === undefined) {
158
+ throw Error(
159
+ `Expected TransactionSigner to return a signed transaction with a sig, but sig was empty`
160
+ );
161
+ }
162
+
163
+ // A signer reports which key it signed with in `sgnr`, but omits it when that
164
+ // key is the transaction sender, so fall back to the sender. If neither is a
165
+ // member of the multisig, createMultisigTransactionWithSignature rejects it
166
+ // rather than attaching the signature to the wrong subsig.
167
+ const myPk = (stxn.sgnr ?? transaction.sender).publicKey;
168
+
169
+ return createMultisigTransactionWithSignature(
170
+ transaction,
171
+ {
172
+ rawSig,
173
+ myPk,
174
+ },
175
+ { version, threshold, pks }
176
+ );
177
+ }
178
+
147
179
  /**
148
180
  * partialSignWithMultisigSignature partially signs this transaction with an external raw multisig signature and returns
149
181
  * a partially-signed multisig transaction, encoded with msgpack as a typed array.
@@ -265,6 +297,8 @@ export function mergeMultisigTransactions(multisigTxnBlobs: Uint8Array[]) {
265
297
  }
266
298
 
267
299
  /**
300
+ * @deprecated Use {@link signMultisigTransactionWithSigner} instead
301
+ *
268
302
  * signMultisigTransaction takes a raw transaction (see signTransaction), a multisig preimage, a secret key, and returns
269
303
  * a multisig transaction, which is a blob representing a transaction and multisignature account preimage. The returned
270
304
  * multisig txn can accumulate additional signatures through mergeMultisigTransactions or appendSignMultisigTransaction.
@@ -291,6 +325,44 @@ export function signMultisigTransaction(
291
325
  }
292
326
 
293
327
  /**
328
+ * signMultisigTransactionWithSigner takes a raw transaction, a multisig preimage and a signer, and
329
+ * returns a multisig transaction: a blob representing a transaction and multisignature account
330
+ * preimage. The returned multisig txn can accumulate additional signatures through
331
+ * mergeMultisigTransactions or appendSignMultisigTransactionWithSigner.
332
+ *
333
+ * The signer must hold a key belonging to the multisig. That key is taken from the `sgnr` field of
334
+ * the transaction the signer returns, falling back to the transaction sender when `sgnr` is absent
335
+ * — which is the same convention signers use when writing it. Every signer this SDK provides
336
+ * follows it. Note that `AddressWithTransactionSigner.address` cannot be used for this: it is the
337
+ * address the signer sends transactions from, which for a rekeyed account is not the signing key.
338
+ *
339
+ * @param txn - the transaction to sign
340
+ * @param version - multisig version
341
+ * @param threshold - multisig threshold
342
+ * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important.
343
+ * @param txnSigner - the signer to sign with
344
+ * @returns an object with txID and blob properties
345
+ */
346
+ export async function signMultisigTransactionWithSigner(
347
+ txn: Transaction,
348
+ { version, threshold, addrs }: MultisigMetadata,
349
+ txnSigner: TransactionSigner
350
+ ) {
351
+ const pks = pksFromAddresses(addrs);
352
+ const blob = await partialSignTxnWithSigner(
353
+ txn,
354
+ { version, threshold, pks },
355
+ txnSigner
356
+ );
357
+ return {
358
+ txID: txn.txID(),
359
+ blob,
360
+ };
361
+ }
362
+
363
+ /**
364
+ * @deprecated Use {@link appendSignMultisigTransactionWithSigner} instead
365
+ *
294
366
  * appendSignMultisigTransaction takes a multisig transaction blob, and appends our signature to it.
295
367
  * While we could derive public key preimagery from the partially-signed multisig transaction,
296
368
  * we ask the caller to pass it back in, to ensure they know what they are signing.
@@ -323,6 +395,45 @@ export function appendSignMultisigTransaction(
323
395
  };
324
396
  }
325
397
 
398
+ /**
399
+ * appendSignMultisigTransactionWithSigner takes a multisig transaction blob, and appends the given
400
+ * signer's signature to it. While we could derive public key preimagery from the partially-signed
401
+ * multisig transaction, we ask the caller to pass it back in, to ensure they know what they are
402
+ * signing.
403
+ *
404
+ * As with {@link signMultisigTransactionWithSigner}, the signer's key is taken from the `sgnr`
405
+ * field of the transaction it returns, falling back to the transaction sender when `sgnr` is
406
+ * absent.
407
+ *
408
+ * @param multisigTxnBlob - an encoded multisig txn. Supports non-payment txn types.
409
+ * @param version - multisig version
410
+ * @param threshold - multisig threshold
411
+ * @param addrs - a list of Algorand addresses representing possible signers for this multisig. Order is important.
412
+ * @param txnSigner - the signer to sign with
413
+ * @returns an object with txID and blob properties
414
+ */
415
+ export async function appendSignMultisigTransactionWithSigner(
416
+ multisigTxnBlob: Uint8Array,
417
+ { version, threshold, addrs }: MultisigMetadata,
418
+ txnSigner: TransactionSigner
419
+ ) {
420
+ const pks = pksFromAddresses(addrs);
421
+ // obtain underlying txn, sign it, and merge it
422
+ const multisigTxObj = encoding.decodeMsgpack(
423
+ multisigTxnBlob,
424
+ SignedTransaction
425
+ );
426
+ const partialSignedBlob = await partialSignTxnWithSigner(
427
+ multisigTxObj.txn,
428
+ { version, threshold, pks },
429
+ txnSigner
430
+ );
431
+ return {
432
+ txID: multisigTxObj.txn.txID(),
433
+ blob: mergeMultisigTransactions([multisigTxnBlob, partialSignedBlob]),
434
+ };
435
+ }
436
+
326
437
  /**
327
438
  * appendMultisigTransactionSignature takes a multisig transaction blob, and appends a given raw signature to it.
328
439
  * This makes it possible to compile a multisig signature using only raw signatures from external methods.
@@ -0,0 +1,90 @@
1
+ import { Address, addressFromPQKey } from './encoding/address.js';
2
+ import { encodeMsgpack } from './encoding/encoding.js';
3
+ import { LogicSig, PQ_PROGRAM_TAG } from './logicsig.js';
4
+ import { MultisigMetadata } from './multisig.js';
5
+ import { SignedTransaction } from './signedTransaction.js';
6
+ import {
7
+ AddressWithDelegatedLsigSigner,
8
+ AddressWithTransactionSigner,
9
+ DelegatedLsigSigner,
10
+ TransactionSigner,
11
+ } from './signer.js';
12
+ import { Transaction } from './transaction.js';
13
+ import { EncodedPQSig } from './types/transactions/encoded.js';
14
+ import { concatArrays } from './utils/utils.js';
15
+
16
+ export interface PQSigningKey {
17
+ pqScheme: Uint8Array;
18
+ pqPublicKey: Uint8Array;
19
+ pqSigner: (bytesToSign: Uint8Array) => Promise<Uint8Array>;
20
+ }
21
+
22
+ export function addressWithSignersFromRawPQSigner(
23
+ signingKey: PQSigningKey,
24
+ sendingAddress?: Address
25
+ ): AddressWithTransactionSigner & AddressWithDelegatedLsigSigner {
26
+ const { pqPublicKey, pqSigner: rawSigner, pqScheme } = signingKey;
27
+ const { address: authAddress, salt } = addressFromPQKey(
28
+ pqScheme,
29
+ pqPublicKey
30
+ );
31
+
32
+ const txnSigner: TransactionSigner = async (
33
+ txnGroup: Transaction[],
34
+ indexesToSign: number[]
35
+ ) => {
36
+ const stxns: SignedTransaction[] = [];
37
+ for (const index of indexesToSign) {
38
+ const txn = txnGroup[index];
39
+ // eslint-disable-next-line no-await-in-loop
40
+ const sig = await rawSigner(txn.bytesToSign());
41
+ const pqsig: EncodedPQSig = {
42
+ sch: pqScheme,
43
+ slt: salt,
44
+ pk: pqPublicKey,
45
+ sig,
46
+ };
47
+ const stxn = new SignedTransaction({
48
+ txn,
49
+ pqsig,
50
+ sgnr: txn.sender.equals(authAddress) ? undefined : authAddress,
51
+ });
52
+
53
+ stxns.push(stxn);
54
+ }
55
+
56
+ return stxns.map((stxn) => encodeMsgpack(stxn));
57
+ };
58
+
59
+ const delegatedLsigSigner: DelegatedLsigSigner = async (
60
+ lsig: LogicSig,
61
+ msig?: MultisigMetadata
62
+ ) => {
63
+ if (msig) {
64
+ throw Error(
65
+ `post-quantum scheme ${new TextDecoder().decode(pqScheme)} does not support multisig signing`
66
+ );
67
+ }
68
+
69
+ const toBeSigned = new Uint8Array(
70
+ concatArrays(PQ_PROGRAM_TAG, authAddress.publicKey, lsig.logic)
71
+ );
72
+
73
+ const sig = await rawSigner(toBeSigned);
74
+
75
+ const pqsig: EncodedPQSig = {
76
+ sch: pqScheme,
77
+ slt: salt,
78
+ pk: pqPublicKey,
79
+ sig,
80
+ };
81
+
82
+ return { address: authAddress, pqsig };
83
+ };
84
+
85
+ return {
86
+ address: sendingAddress ?? authAddress,
87
+ txnSigner,
88
+ delegatedLsigSigner,
89
+ };
90
+ }
@@ -11,6 +11,10 @@ import {
11
11
  encodedMultiSigToEncodingData,
12
12
  encodedMultiSigFromEncodingData,
13
13
  ENCODED_MULTISIG_SCHEMA,
14
+ EncodedPQSig,
15
+ encodedPQSigToEncodingData,
16
+ encodedPQSigFromEncodingData,
17
+ ENCODED_PQSIG_SCHEMA,
14
18
  } from './types/transactions/index.js';
15
19
  import {
16
20
  AddressSchema,
@@ -39,6 +43,10 @@ export class SignedTransaction implements Encodable {
39
43
  key: 'lsig',
40
44
  valueSchema: new OptionalSchema(LogicSig.encodingSchema),
41
45
  },
46
+ {
47
+ key: 'pqsig',
48
+ valueSchema: new OptionalSchema(ENCODED_PQSIG_SCHEMA),
49
+ },
42
50
  {
43
51
  key: 'sgnr',
44
52
  valueSchema: new OptionalSchema(new AddressSchema()),
@@ -66,6 +74,11 @@ export class SignedTransaction implements Encodable {
66
74
  */
67
75
  public readonly lsig?: LogicSig;
68
76
 
77
+ /**
78
+ * Post-quantum signature
79
+ */
80
+ public readonly pqsig?: EncodedPQSig;
81
+
69
82
  /**
70
83
  * The signer, if signing with a different key than the Transaction type `sender` property indicates
71
84
  */
@@ -76,24 +89,28 @@ export class SignedTransaction implements Encodable {
76
89
  sig,
77
90
  msig,
78
91
  lsig,
92
+ pqsig,
79
93
  sgnr,
80
94
  }: {
81
95
  txn: Transaction;
82
96
  sig?: Uint8Array;
83
97
  msig?: EncodedMultisig;
84
98
  lsig?: LogicSig;
99
+ pqsig?: EncodedPQSig;
85
100
  sgnr?: Address;
86
101
  }) {
87
102
  this.txn = txn;
88
103
  this.sig = sig;
89
104
  this.msig = msig;
90
105
  this.lsig = lsig;
106
+ this.pqsig = pqsig;
91
107
  this.sgnr = sgnr;
92
108
 
93
109
  let numberOfSigs = 0;
94
110
  if (sig) numberOfSigs += 1;
95
111
  if (msig) numberOfSigs += 1;
96
112
  if (lsig) numberOfSigs += 1;
113
+ if (pqsig) numberOfSigs += 1;
97
114
  if (numberOfSigs > 1) {
98
115
  throw new Error(
99
116
  `SignedTransaction must not have more than 1 signature. Got ${numberOfSigs}`
@@ -115,6 +132,10 @@ export class SignedTransaction implements Encodable {
115
132
  this.msig ? encodedMultiSigToEncodingData(this.msig) : undefined,
116
133
  ],
117
134
  ['lsig', this.lsig ? this.lsig.toEncodingData() : undefined],
135
+ [
136
+ 'pqsig',
137
+ this.pqsig ? encodedPQSigToEncodingData(this.pqsig) : undefined,
138
+ ],
118
139
  ['sgnr', this.sgnr],
119
140
  ]);
120
141
  }
@@ -132,6 +153,9 @@ export class SignedTransaction implements Encodable {
132
153
  lsig: data.get('lsig')
133
154
  ? LogicSig.fromEncodingData(data.get('lsig'))
134
155
  : undefined,
156
+ pqsig: data.get('pqsig')
157
+ ? encodedPQSigFromEncodingData(data.get('pqsig'))
158
+ : undefined,
135
159
  sgnr: data.get('sgnr'),
136
160
  });
137
161
  }
package/src/signer.ts CHANGED
@@ -8,6 +8,20 @@ import {
8
8
  signMultisigTransaction,
9
9
  mergeMultisigTransactions,
10
10
  } from './multisigSigning.js';
11
+ import type { Address } from './encoding/address.js';
12
+ import type {
13
+ EncodedMultisig,
14
+ EncodedPQSig,
15
+ } from './types/transactions/encoded.js';
16
+ import type { LogicSig } from './logicsig.js';
17
+
18
+ /**
19
+ * Something that has an Algorand address associated with it.
20
+ * It could be logic sig, single-signer account, multisig, app, etc.
21
+ */
22
+ export interface Addressable {
23
+ address: Address;
24
+ }
11
25
 
12
26
  /**
13
27
  * This type represents a function which can sign transactions from an atomic transaction group.
@@ -22,6 +36,45 @@ export type TransactionSigner = (
22
36
  indexesToSign: number[]
23
37
  ) => Promise<Uint8Array[]>;
24
38
 
39
+ export interface AddressWithTransactionSigner extends Addressable {
40
+ txnSigner: TransactionSigner;
41
+ }
42
+
43
+ /** Function for signing logic signatures for delegation
44
+ * @param lsig - The logic signature that is being signed for delegation
45
+ * @param msig - Optional multisig account that should be set when a public key is signing as a subsigner of a multisig
46
+ * @returns The address of the delegator
47
+ * */
48
+ export type DelegatedLsigSigner = (
49
+ lsig: LogicSig,
50
+ msig?: MultisigMetadata
51
+ ) => Promise<
52
+ { address: Address } & (
53
+ | { sig: Uint8Array }
54
+ | { lmsig: EncodedMultisig }
55
+ | { pqsig: EncodedPQSig }
56
+ )
57
+ >;
58
+
59
+ export interface AddressWithDelegatedLsigSigner extends Addressable {
60
+ delegatedLsigSigner: DelegatedLsigSigner;
61
+ }
62
+
63
+ export type MxBytesSigner = (bytesToSign: Uint8Array) => Promise<Uint8Array>;
64
+
65
+ export interface AddressWithMxBytesSigner extends Addressable {
66
+ mxBytesSigner: MxBytesSigner;
67
+ }
68
+
69
+ export type ProgramDataSigner = (
70
+ data: Uint8Array,
71
+ lsig: LogicSig
72
+ ) => Promise<Uint8Array>;
73
+
74
+ export interface AddressWithProgramDataSigner extends Addressable {
75
+ programDataSigner: ProgramDataSigner;
76
+ }
77
+
25
78
  /**
26
79
  * Create a TransactionSigner that can sign transactions for the provided basic Account.
27
80
  */
package/src/signing.ts CHANGED
@@ -1,17 +1,112 @@
1
1
  import * as nacl from './nacl/naclWrappers.js';
2
- import { Address } from './encoding/address.js';
2
+ import { Address, addressFromPQSig } from './encoding/address.js';
3
3
  import * as encoding from './encoding/encoding.js';
4
+ import * as utils from './utils/utils.js';
4
5
  import { SignedTransaction } from './signedTransaction.js';
5
6
  import { Transaction } from './transaction.js';
6
- import { LogicSig, LogicSigAccount } from './logicsig.js';
7
+ import { LogicSig, LogicSigAccount, sanityCheckProgram } from './logicsig.js';
7
8
  import { addressFromMultisigPreImg } from './multisig.js';
9
+ import type { TransactionSigner } from './signer.js';
10
+
11
+ export const SIGN_BYTES_PREFIX = Uint8Array.from([77, 88]); // "MX"
12
+
13
+ /**
14
+ * @deprecated Use signTransactionWithSigner
15
+ *
16
+ * signTransaction takes an object with either payment or key registration fields and
17
+ * a secret key and returns a signed blob.
18
+ *
19
+ * Payment transaction fields: from, to, amount, fee, firstValid, lastValid, genesisHash,
20
+ * note(optional), GenesisID(optional), closeRemainderTo(optional)
21
+ *
22
+ * Key registration fields: fee, firstValid, lastValid, voteKey, selectionKey, voteFirst,
23
+ * voteLast, voteKeyDilution, genesisHash, note(optional), GenesisID(optional)
24
+ *
25
+ * If flatFee is not set and the final calculated fee is lower than the protocol minimum fee, the fee will be increased to match the minimum.
26
+ * @param txn - object with either payment or key registration fields
27
+ * @param sk - Algorand Secret Key
28
+ * @returns object contains the binary signed transaction and its txID
29
+ */
30
+ export function signTransaction(txn: Transaction, sk: Uint8Array) {
31
+ return {
32
+ txID: txn.txID(),
33
+ blob: txn.signTxn(sk),
34
+ };
35
+ }
36
+
37
+ export async function signTransactionWithSigner(
38
+ txn: Transaction,
39
+ signer: TransactionSigner
40
+ ): Promise<ReturnType<typeof signTransaction> & { stxn: SignedTransaction }> {
41
+ const [blob] = await signer([txn], [0]);
42
+
43
+ return {
44
+ blob,
45
+ txID: txn.txID(),
46
+ stxn: encoding.decodeMsgpack(blob, SignedTransaction),
47
+ };
48
+ }
49
+
50
+ /**
51
+ * signBytes takes arbitrary bytes and a secret key, prepends the bytes with "MX" for domain separation, signs the bytes
52
+ * with the private key, and returns the signature.
53
+ * @param bytes - Uint8array
54
+ * @param sk - Algorand secret key
55
+ * @returns binary signature
56
+ */
57
+ export function signBytes(bytes: Uint8Array, sk: Uint8Array) {
58
+ const toBeSigned = utils.concatArrays(SIGN_BYTES_PREFIX, bytes);
59
+ const sig = nacl.sign(toBeSigned, sk);
60
+ return sig;
61
+ }
62
+
63
+ /**
64
+ * verifyBytes takes array of bytes, an address, and a signature and verifies if the signature is correct for the public
65
+ * key and the bytes (the bytes should have been signed with "MX" prepended for domain separation).
66
+ * @param bytes - Uint8Array
67
+ * @param signature - binary signature
68
+ * @param addr - string address
69
+ * @returns bool
70
+ */
71
+ export function verifyBytes(
72
+ bytes: Uint8Array,
73
+ signature: Uint8Array,
74
+ addr: string | Address
75
+ ) {
76
+ const toBeVerified = utils.concatArrays(SIGN_BYTES_PREFIX, bytes);
77
+ const addrObj = typeof addr === 'string' ? Address.fromString(addr) : addr;
78
+ return nacl.verify(toBeVerified, signature, addrObj.publicKey);
79
+ }
8
80
 
9
81
  function signLogicSigTransactionWithAddress(
10
82
  txn: Transaction,
11
83
  lsig: LogicSig,
12
84
  lsigAddress: Address
13
85
  ) {
14
- if (!lsig.verify(lsigAddress.publicKey)) {
86
+ // `lsig.verify` enforces this for the non-PQ paths, but the PQ path below
87
+ // skips it, so check it up front for every path.
88
+ const sigCount = [lsig.sig, lsig.msig, lsig.lmsig, lsig.pqsig].filter(
89
+ Boolean
90
+ ).length;
91
+ if (sigCount > 1) {
92
+ throw new Error(
93
+ 'LogicSig has too many signatures. At most one of sig, msig, lmsig, or pqsig may be present'
94
+ );
95
+ }
96
+
97
+ if (lsig.pqsig) {
98
+ // This SDK cannot validate the post-quantum signature itself, so it checks
99
+ // what it can and leaves the signature to the network: the program must be
100
+ // well-formed, and the signature's scheme, salt and public key must derive
101
+ // the delegating address.
102
+ try {
103
+ sanityCheckProgram(lsig.logic);
104
+ } catch (e) {
105
+ throw new Error(
106
+ `Logic signature verification failed. Ensure the program is valid: ${(e as Error).message}`
107
+ );
108
+ }
109
+ } else if (!lsig.verify(lsigAddress.publicKey)) {
15
110
  throw new Error(
16
111
  'Logic signature verification failed. Ensure the program and signature are valid.'
17
112
  );
@@ -56,11 +151,17 @@ export function signLogicSigTransactionObject(
56
151
  } else {
57
152
  lsig = lsigObject;
58
153
 
59
- if (lsig.sig) {
60
- // For a LogicSig with a non-multisig delegating account, we cannot derive
61
- // the address of that account from only its signature, so assume the
62
- // delegating account is the sender. If that's not the case, the signing
63
- // will fail.
154
+ if (lsig.pqsig) {
155
+ // A PQ signature carries the scheme, salt and public key of the
156
+ // delegating account, so its address is derivable. This is what lets a
157
+ // bare PQ-delegated LogicSig authorize a transaction whose sender was
158
+ // rekeyed to the delegating account.
159
+ lsigAddress = addressFromPQSig(lsig.pqsig);
160
+ } else if (lsig.sig) {
161
+ // For an ed25519 LogicSig with a non-multisig delegating account, we
162
+ // cannot derive the address of that account from only its signature, so
163
+ // assume the delegating account is the sender. If that's not the case,
164
+ // the signing will fail.
64
165
  lsigAddress = new Address(txn.sender.publicKey);
65
166
  } else if (lsig.lmsig) {
66
167
  const msigMetadata = {
@@ -1252,13 +1252,21 @@ export class Transaction implements encoding.Encodable {
1252
1252
  return encoding.encodeMsgpack(this);
1253
1253
  }
1254
1254
 
1255
- // returns the raw signature
1255
+ /**
1256
+ * @deprecated Use `(await signTransactionWithSigner(txn, signer)).stxn.sig` instead
1257
+ *
1258
+ * returns the raw signature
1259
+ *
1260
+ */
1256
1261
  rawSignTxn(sk: Uint8Array): Uint8Array {
1257
1262
  const toBeSigned = this.bytesToSign();
1258
1263
  const sig = nacl.sign(toBeSigned, sk);
1259
1264
  return sig;
1260
1265
  }
1261
1266
 
1267
+ /**
1268
+ * @deprecated Use `await signTransactionWithSigner(txn, signer)`
1269
+ */
1262
1270
  signTxn(sk: Uint8Array): Uint8Array {
1263
1271
  // TODO: deprecate in favor of SignedTransaction class
1264
1272
  const keypair = nacl.keyPairFromSecretKey(sk);
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  NamedMapSchema,
3
3
  FixedLengthByteArraySchema,
4
+ ByteArraySchema,
4
5
  Uint64Schema,
5
6
  ArraySchema,
6
7
  OptionalSchema,
7
8
  allOmitEmpty,
8
9
  } from '../../encoding/schema/index.js';
9
10
  import { ensureSafeUnsignedInteger } from '../../utils/utils.js';
11
+ import { PQ_SALT_MAX, PQ_SCHEME_SIZE } from '../../encoding/address.js';
10
12
 
11
13
  export interface EncodedSubsig {
12
14
  /**
@@ -116,3 +118,89 @@ export function encodedMultiSigToEncodingData(
116
118
  ['subsig', msig.subsig.map(encodedSubsigToEncodingData)],
117
119
  ]);
118
120
  }
121
+
122
+ /**
123
+ * A structure for the encoded post-quantum signature transaction authorization
124
+ * proof. Mirrors the `PQSig` struct from go-algorand.
125
+ */
126
+ export interface EncodedPQSig {
127
+ /**
128
+ * The 2-byte identifier of the post-quantum signature scheme (e.g. "f1"
129
+ * for Falcon-1024).
130
+ */
131
+ sch: Uint8Array;
132
+
133
+ /**
134
+ * The 1-byte salt used when deriving the post-quantum account address from the
135
+ * public key.
136
+ */
137
+ slt: number;
138
+
139
+ /**
140
+ * The post-quantum public key.
141
+ */
142
+ pk: Uint8Array;
143
+
144
+ /**
145
+ * The post-quantum signature over the transaction.
146
+ */
147
+ sig: Uint8Array;
148
+ }
149
+
150
+ export const ENCODED_PQSIG_SCHEMA = new NamedMapSchema(
151
+ allOmitEmpty([
152
+ {
153
+ key: 'sch',
154
+ valueSchema: new ByteArraySchema(),
155
+ },
156
+ {
157
+ key: 'slt',
158
+ valueSchema: new Uint64Schema(),
159
+ },
160
+ {
161
+ key: 'pk',
162
+ valueSchema: new ByteArraySchema(),
163
+ },
164
+ {
165
+ key: 'sig',
166
+ valueSchema: new ByteArraySchema(),
167
+ },
168
+ ])
169
+ );
170
+
171
+ export function encodedPQSigFromEncodingData(data: unknown): EncodedPQSig {
172
+ if (!(data instanceof Map)) {
173
+ throw new Error(`Invalid decoded EncodedPQSig: ${data}`);
174
+ }
175
+ const sch = data.get('sch') as Uint8Array;
176
+ if (sch.length !== PQ_SCHEME_SIZE) {
177
+ throw new Error(
178
+ `Invalid decoded EncodedPQSig: expected a ${PQ_SCHEME_SIZE}-byte scheme, got ${sch.length} bytes`
179
+ );
180
+ }
181
+ // The salt occupies a single byte of the address preimage, so anything wider
182
+ // could never have produced a valid address.
183
+ const slt = ensureSafeUnsignedInteger(data.get('slt'));
184
+ if (slt > PQ_SALT_MAX) {
185
+ throw new Error(
186
+ `Invalid decoded EncodedPQSig: salt ${slt} exceeds the maximum of ${PQ_SALT_MAX}`
187
+ );
188
+ }
189
+ return {
190
+ sch,
191
+ slt,
192
+ pk: data.get('pk'),
193
+ sig: data.get('sig'),
194
+ };
195
+ }
196
+
197
+ export function encodedPQSigToEncodingData(
198
+ pqsig: EncodedPQSig
199
+ ): Map<string, unknown> {
200
+ return new Map<string, unknown>([
201
+ ['sch', pqsig.sch],
202
+ ['slt', pqsig.slt],
203
+ ['pk', pqsig.pk],
204
+ ['sig', pqsig.sig],
205
+ ]);
206
+ }