algosdk 3.6.0 → 3.7.0
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.
- package/README.md +4 -4
- package/dist/browser/algosdk.min.js +1 -1
- package/dist/browser/algosdk.min.js.map +1 -1
- package/dist/cjs/appAccess.js +4 -0
- package/dist/cjs/appAccess.js.map +1 -1
- package/dist/cjs/client/v2/algod/algod.js +0 -18
- package/dist/cjs/client/v2/algod/algod.js.map +1 -1
- package/dist/cjs/client/v2/algod/models/types.js +153 -437
- package/dist/cjs/client/v2/algod/models/types.js.map +1 -1
- package/dist/cjs/client/v2/indexer/models/types.js +163 -12
- package/dist/cjs/client/v2/indexer/models/types.js.map +1 -1
- package/dist/cjs/composer.js +4 -6
- package/dist/cjs/composer.js.map +1 -1
- package/dist/cjs/ed25519-signer.js +76 -0
- package/dist/cjs/ed25519-signer.js.map +1 -0
- package/dist/cjs/encoding/address.js +61 -1
- package/dist/cjs/encoding/address.js.map +1 -1
- package/dist/cjs/falcon-signer.js +17 -0
- package/dist/cjs/falcon-signer.js.map +1 -0
- package/dist/cjs/heartbeat.js +7 -0
- package/dist/cjs/heartbeat.js.map +1 -1
- package/dist/cjs/logicsig.js +201 -16
- package/dist/cjs/logicsig.js.map +1 -1
- package/dist/cjs/main.js +32 -63
- package/dist/cjs/main.js.map +1 -1
- package/dist/cjs/makeTxn.js +11 -2
- package/dist/cjs/makeTxn.js.map +1 -1
- package/dist/cjs/mnemonic/mnemonic.js +26 -1
- package/dist/cjs/mnemonic/mnemonic.js.map +1 -1
- package/dist/cjs/multisigSigning.js +78 -1
- package/dist/cjs/multisigSigning.js.map +1 -1
- package/dist/cjs/pq-signer.js +80 -0
- package/dist/cjs/pq-signer.js.map +1 -0
- package/dist/cjs/signedTransaction.js +15 -1
- package/dist/cjs/signedTransaction.js.map +1 -1
- package/dist/cjs/signer.js.map +1 -1
- package/dist/cjs/signing.js +94 -7
- package/dist/cjs/signing.js.map +1 -1
- package/dist/cjs/transaction.js +17 -1
- package/dist/cjs/transaction.js.map +1 -1
- package/dist/cjs/types/statedelta.js +17 -0
- package/dist/cjs/types/statedelta.js.map +1 -1
- package/dist/cjs/types/transactions/encoded.js +51 -1
- package/dist/cjs/types/transactions/encoded.js.map +1 -1
- package/dist/cjs/utils/ed25519-check.js +69 -0
- package/dist/cjs/utils/ed25519-check.js.map +1 -0
- package/dist/cjs/utils/utils.js +14 -1
- package/dist/cjs/utils/utils.js.map +1 -1
- package/dist/esm/appAccess.js +4 -0
- package/dist/esm/appAccess.js.map +1 -1
- package/dist/esm/client/v2/algod/algod.js +0 -18
- package/dist/esm/client/v2/algod/algod.js.map +1 -1
- package/dist/esm/client/v2/algod/models/types.js +149 -430
- package/dist/esm/client/v2/algod/models/types.js.map +1 -1
- package/dist/esm/client/v2/indexer/models/types.js +161 -11
- package/dist/esm/client/v2/indexer/models/types.js.map +1 -1
- package/dist/esm/composer.js +4 -6
- package/dist/esm/composer.js.map +1 -1
- package/dist/esm/ed25519-signer.js +72 -0
- package/dist/esm/ed25519-signer.js.map +1 -0
- package/dist/esm/encoding/address.js +58 -0
- package/dist/esm/encoding/address.js.map +1 -1
- package/dist/esm/falcon-signer.js +13 -0
- package/dist/esm/falcon-signer.js.map +1 -0
- package/dist/esm/heartbeat.js +8 -1
- package/dist/esm/heartbeat.js.map +1 -1
- package/dist/esm/logicsig.js +200 -15
- package/dist/esm/logicsig.js.map +1 -1
- package/dist/esm/main.js +8 -56
- package/dist/esm/main.js.map +1 -1
- package/dist/esm/makeTxn.js +11 -2
- package/dist/esm/makeTxn.js.map +1 -1
- package/dist/esm/mnemonic/mnemonic.js +25 -1
- package/dist/esm/mnemonic/mnemonic.js.map +1 -1
- package/dist/esm/multisigSigning.js +75 -0
- package/dist/esm/multisigSigning.js.map +1 -1
- package/dist/esm/pq-signer.js +76 -0
- package/dist/esm/pq-signer.js.map +1 -0
- package/dist/esm/signedTransaction.js +16 -2
- package/dist/esm/signedTransaction.js.map +1 -1
- package/dist/esm/signer.js.map +1 -1
- package/dist/esm/signing.js +91 -8
- package/dist/esm/signing.js.map +1 -1
- package/dist/esm/transaction.js +17 -1
- package/dist/esm/transaction.js.map +1 -1
- package/dist/esm/types/statedelta.js +17 -0
- package/dist/esm/types/statedelta.js.map +1 -1
- package/dist/esm/types/transactions/encoded.js +49 -1
- package/dist/esm/types/transactions/encoded.js.map +1 -1
- package/dist/esm/utils/ed25519-check.js +65 -0
- package/dist/esm/utils/ed25519-check.js.map +1 -0
- package/dist/esm/utils/utils.js +12 -0
- package/dist/esm/utils/utils.js.map +1 -1
- package/dist/types/client/v2/algod/algod.d.ts +0 -16
- package/dist/types/client/v2/algod/models/types.d.ts +90 -220
- package/dist/types/client/v2/indexer/models/types.d.ts +114 -7
- package/dist/types/composer.d.ts +3 -3
- package/dist/types/ed25519-signer.d.ts +7 -0
- package/dist/types/encoding/address.d.ts +37 -0
- package/dist/types/falcon-signer.d.ts +11 -0
- package/dist/types/heartbeat.d.ts +2 -0
- package/dist/types/logicsig.d.ts +72 -1
- package/dist/types/main.d.ts +8 -44
- package/dist/types/makeTxn.d.ts +8 -2
- package/dist/types/mnemonic/mnemonic.d.ts +13 -0
- package/dist/types/multisigSigning.d.ts +49 -0
- package/dist/types/pq-signer.d.ts +8 -0
- package/dist/types/signedTransaction.d.ts +7 -2
- package/dist/types/signer.d.ts +46 -0
- package/dist/types/signing.d.ts +45 -0
- package/dist/types/transaction.d.ts +10 -0
- package/dist/types/types/statedelta.d.ts +18 -0
- package/dist/types/types/transactions/base.d.ts +10 -0
- package/dist/types/types/transactions/encoded.d.ts +27 -0
- package/dist/types/utils/ed25519-check.d.ts +10 -0
- package/dist/types/utils/utils.d.ts +1 -0
- package/package.json +2 -1
- package/src/appAccess.ts +5 -0
- package/src/client/v2/algod/algod.ts +0 -19
- package/src/client/v2/algod/models/types.ts +324 -790
- package/src/client/v2/indexer/models/types.ts +280 -8
- package/src/composer.ts +7 -11
- package/src/ed25519-signer.ts +125 -0
- package/src/encoding/address.ts +80 -0
- package/src/falcon-signer.ts +33 -0
- package/src/heartbeat.ts +11 -0
- package/src/logicsig.ts +258 -16
- package/src/main.ts +30 -60
- package/src/makeTxn.ts +13 -4
- package/src/mnemonic/mnemonic.ts +34 -1
- package/src/multisigSigning.ts +111 -0
- package/src/pq-signer.ts +123 -0
- package/src/signedTransaction.ts +24 -0
- package/src/signer.ts +62 -0
- package/src/signing.ts +109 -8
- package/src/transaction.ts +19 -1
- package/src/types/statedelta.ts +34 -0
- package/src/types/transactions/base.ts +11 -0
- package/src/types/transactions/encoded.ts +88 -0
- package/src/utils/ed25519-check.ts +67 -0
- package/src/utils/utils.ts +13 -0
- package/dist/cjs/client/v2/algod/dryrun.js +0 -34
- package/dist/cjs/client/v2/algod/dryrun.js.map +0 -1
- package/dist/cjs/dryrun.js +0 -214
- package/dist/cjs/dryrun.js.map +0 -1
- package/dist/esm/client/v2/algod/dryrun.js +0 -28
- package/dist/esm/client/v2/algod/dryrun.js.map +0 -1
- package/dist/esm/dryrun.js +0 -208
- package/dist/esm/dryrun.js.map +0 -1
- package/dist/types/client/v2/algod/dryrun.d.ts +0 -11
- package/dist/types/dryrun.d.ts +0 -28
- package/src/client/v2/algod/dryrun.ts +0 -38
- package/src/dryrun.ts +0 -292
package/src/multisigSigning.ts
CHANGED
|
@@ -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.
|
package/src/pq-signer.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
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
|
+
AddressWithEmptyTransactionSigner,
|
|
9
|
+
AddressWithTransactionSigner,
|
|
10
|
+
DelegatedLsigSigner,
|
|
11
|
+
TransactionSigner,
|
|
12
|
+
} from './signer.js';
|
|
13
|
+
import { Transaction } from './transaction.js';
|
|
14
|
+
import { EncodedPQSig } from './types/transactions/encoded.js';
|
|
15
|
+
import { concatArrays } from './utils/utils.js';
|
|
16
|
+
|
|
17
|
+
export interface PQSigningKey {
|
|
18
|
+
pqScheme: Uint8Array;
|
|
19
|
+
pqPublicKey: Uint8Array;
|
|
20
|
+
pqSigner: (bytesToSign: Uint8Array) => Promise<Uint8Array>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function addressWithSignersFromRawPQSigner(
|
|
24
|
+
signingKey: PQSigningKey,
|
|
25
|
+
sendingAddress?: Address
|
|
26
|
+
): AddressWithTransactionSigner &
|
|
27
|
+
AddressWithDelegatedLsigSigner &
|
|
28
|
+
AddressWithEmptyTransactionSigner {
|
|
29
|
+
const { pqPublicKey, pqSigner: rawSigner, pqScheme } = signingKey;
|
|
30
|
+
const { address: authAddress, salt } = addressFromPQKey(
|
|
31
|
+
pqScheme,
|
|
32
|
+
pqPublicKey
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const txnSigner: TransactionSigner = async (
|
|
36
|
+
txnGroup: Transaction[],
|
|
37
|
+
indexesToSign: number[]
|
|
38
|
+
) => {
|
|
39
|
+
const stxns: SignedTransaction[] = [];
|
|
40
|
+
for (const index of indexesToSign) {
|
|
41
|
+
const txn = txnGroup[index];
|
|
42
|
+
// eslint-disable-next-line no-await-in-loop
|
|
43
|
+
const sig = await rawSigner(txn.bytesToSign());
|
|
44
|
+
const pqsig: EncodedPQSig = {
|
|
45
|
+
sch: pqScheme,
|
|
46
|
+
slt: salt,
|
|
47
|
+
pk: pqPublicKey,
|
|
48
|
+
sig,
|
|
49
|
+
};
|
|
50
|
+
const stxn = new SignedTransaction({
|
|
51
|
+
txn,
|
|
52
|
+
pqsig,
|
|
53
|
+
sgnr: txn.sender.equals(authAddress) ? undefined : authAddress,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
stxns.push(stxn);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return stxns.map((stxn) => encodeMsgpack(stxn));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// A TransactionSigner that attaches a placeholder PQ envelope rather than a
|
|
63
|
+
// real signature. The scheme, salt, and public key are populated but the
|
|
64
|
+
// signature bytes are left empty. When simulated with `allowEmptySignatures`,
|
|
65
|
+
// algod derives the authorizer from this envelope and charges the post-quantum
|
|
66
|
+
// fee surcharge, so the reported fee usage matches a genuinely signed group -
|
|
67
|
+
// all without paying the cost of producing a (large, slow) Falcon signature.
|
|
68
|
+
const emptyTxnSigner: TransactionSigner = (
|
|
69
|
+
txnGroup: Transaction[],
|
|
70
|
+
indexesToSign: number[]
|
|
71
|
+
) => {
|
|
72
|
+
const stxns: Uint8Array[] = [];
|
|
73
|
+
for (const index of indexesToSign) {
|
|
74
|
+
const txn = txnGroup[index];
|
|
75
|
+
const pqsig: EncodedPQSig = {
|
|
76
|
+
sch: pqScheme,
|
|
77
|
+
slt: salt,
|
|
78
|
+
pk: pqPublicKey,
|
|
79
|
+
sig: new Uint8Array(),
|
|
80
|
+
};
|
|
81
|
+
const stxn = new SignedTransaction({
|
|
82
|
+
txn,
|
|
83
|
+
pqsig,
|
|
84
|
+
sgnr: txn.sender.equals(authAddress) ? undefined : authAddress,
|
|
85
|
+
});
|
|
86
|
+
stxns.push(encodeMsgpack(stxn));
|
|
87
|
+
}
|
|
88
|
+
return Promise.resolve(stxns);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const delegatedLsigSigner: DelegatedLsigSigner = async (
|
|
92
|
+
lsig: LogicSig,
|
|
93
|
+
msig?: MultisigMetadata
|
|
94
|
+
) => {
|
|
95
|
+
if (msig) {
|
|
96
|
+
throw Error(
|
|
97
|
+
`post-quantum scheme ${new TextDecoder().decode(pqScheme)} does not support multisig signing`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const toBeSigned = new Uint8Array(
|
|
102
|
+
concatArrays(PQ_PROGRAM_TAG, authAddress.publicKey, lsig.logic)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const sig = await rawSigner(toBeSigned);
|
|
106
|
+
|
|
107
|
+
const pqsig: EncodedPQSig = {
|
|
108
|
+
sch: pqScheme,
|
|
109
|
+
slt: salt,
|
|
110
|
+
pk: pqPublicKey,
|
|
111
|
+
sig,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return { address: authAddress, pqsig };
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
address: sendingAddress ?? authAddress,
|
|
119
|
+
txnSigner,
|
|
120
|
+
emptyTxnSigner,
|
|
121
|
+
delegatedLsigSigner,
|
|
122
|
+
};
|
|
123
|
+
}
|
package/src/signedTransaction.ts
CHANGED
|
@@ -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,54 @@ 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
|
+
export interface AddressWithEmptyTransactionSigner extends Addressable {
|
|
44
|
+
/**
|
|
45
|
+
* A TransactionSigner that produces unsigned (or placeholder-signed)
|
|
46
|
+
* transactions. This must only be used to simulate transactions with the
|
|
47
|
+
* `allowEmptySignatures` option enabled.
|
|
48
|
+
*/
|
|
49
|
+
emptyTxnSigner: TransactionSigner;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Function for signing logic signatures for delegation
|
|
53
|
+
* @param lsig - The logic signature that is being signed for delegation
|
|
54
|
+
* @param msig - Optional multisig account that should be set when a public key is signing as a subsigner of a multisig
|
|
55
|
+
* @returns The address of the delegator
|
|
56
|
+
* */
|
|
57
|
+
export type DelegatedLsigSigner = (
|
|
58
|
+
lsig: LogicSig,
|
|
59
|
+
msig?: MultisigMetadata
|
|
60
|
+
) => Promise<
|
|
61
|
+
{ address: Address } & (
|
|
62
|
+
| { sig: Uint8Array }
|
|
63
|
+
| { lmsig: EncodedMultisig }
|
|
64
|
+
| { pqsig: EncodedPQSig }
|
|
65
|
+
)
|
|
66
|
+
>;
|
|
67
|
+
|
|
68
|
+
export interface AddressWithDelegatedLsigSigner extends Addressable {
|
|
69
|
+
delegatedLsigSigner: DelegatedLsigSigner;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type MxBytesSigner = (bytesToSign: Uint8Array) => Promise<Uint8Array>;
|
|
73
|
+
|
|
74
|
+
export interface AddressWithMxBytesSigner extends Addressable {
|
|
75
|
+
mxBytesSigner: MxBytesSigner;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type ProgramDataSigner = (
|
|
79
|
+
data: Uint8Array,
|
|
80
|
+
lsig: LogicSig
|
|
81
|
+
) => Promise<Uint8Array>;
|
|
82
|
+
|
|
83
|
+
export interface AddressWithProgramDataSigner extends Addressable {
|
|
84
|
+
programDataSigner: ProgramDataSigner;
|
|
85
|
+
}
|
|
86
|
+
|
|
25
87
|
/**
|
|
26
88
|
* Create a TransactionSigner that can sign transactions for the provided basic Account.
|
|
27
89
|
*/
|
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
|
-
|
|
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.
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
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 = {
|
package/src/transaction.ts
CHANGED
|
@@ -247,6 +247,12 @@ function ensureResourceReference(input: unknown): TransactionResourceReference {
|
|
|
247
247
|
if (box !== undefined) {
|
|
248
248
|
return { box: ensureBoxReference(box) };
|
|
249
249
|
}
|
|
250
|
+
|
|
251
|
+
// An empty resource reference is meaningful: it asks for a box I/O quota
|
|
252
|
+
// bump without naming a resource.
|
|
253
|
+
if (utils.isEmptyObject(input)) {
|
|
254
|
+
return {};
|
|
255
|
+
}
|
|
250
256
|
}
|
|
251
257
|
throw new Error(`Not a resource reference: ${input}`);
|
|
252
258
|
}
|
|
@@ -329,6 +335,7 @@ export interface HeartbeatTransactionFields {
|
|
|
329
335
|
readonly seed: Uint8Array;
|
|
330
336
|
readonly voteID: Uint8Array;
|
|
331
337
|
readonly keyDilution: bigint;
|
|
338
|
+
readonly challengeDiscount: boolean;
|
|
332
339
|
}
|
|
333
340
|
|
|
334
341
|
/**
|
|
@@ -876,6 +883,7 @@ export class Transaction implements encoding.Encodable {
|
|
|
876
883
|
seed: params.heartbeatParams.seed,
|
|
877
884
|
voteID: params.heartbeatParams.voteID,
|
|
878
885
|
keyDilution: params.heartbeatParams.keyDilution,
|
|
886
|
+
challengeDiscount: params.heartbeatParams.challengeDiscount,
|
|
879
887
|
});
|
|
880
888
|
}
|
|
881
889
|
|
|
@@ -1035,6 +1043,7 @@ export class Transaction implements encoding.Encodable {
|
|
|
1035
1043
|
seed: this.heartbeat.seed,
|
|
1036
1044
|
voteID: this.heartbeat.voteID,
|
|
1037
1045
|
keyDilution: this.heartbeat.keyDilution,
|
|
1046
|
+
challengeDiscount: this.heartbeat.challengeDiscount,
|
|
1038
1047
|
});
|
|
1039
1048
|
data.set('hb', heartbeat.toEncodingData());
|
|
1040
1049
|
return data;
|
|
@@ -1219,6 +1228,7 @@ export class Transaction implements encoding.Encodable {
|
|
|
1219
1228
|
seed: heartbeat.seed,
|
|
1220
1229
|
voteID: heartbeat.voteID,
|
|
1221
1230
|
keyDilution: heartbeat.keyDilution,
|
|
1231
|
+
challengeDiscount: heartbeat.challengeDiscount,
|
|
1222
1232
|
};
|
|
1223
1233
|
params.heartbeatParams = heartbeatParams;
|
|
1224
1234
|
} else {
|
|
@@ -1252,13 +1262,21 @@ export class Transaction implements encoding.Encodable {
|
|
|
1252
1262
|
return encoding.encodeMsgpack(this);
|
|
1253
1263
|
}
|
|
1254
1264
|
|
|
1255
|
-
|
|
1265
|
+
/**
|
|
1266
|
+
* @deprecated Use `(await signTransactionWithSigner(txn, signer)).stxn.sig` instead
|
|
1267
|
+
*
|
|
1268
|
+
* returns the raw signature
|
|
1269
|
+
*
|
|
1270
|
+
*/
|
|
1256
1271
|
rawSignTxn(sk: Uint8Array): Uint8Array {
|
|
1257
1272
|
const toBeSigned = this.bytesToSign();
|
|
1258
1273
|
const sig = nacl.sign(toBeSigned, sk);
|
|
1259
1274
|
return sig;
|
|
1260
1275
|
}
|
|
1261
1276
|
|
|
1277
|
+
/**
|
|
1278
|
+
* @deprecated Use `await signTransactionWithSigner(txn, signer)`
|
|
1279
|
+
*/
|
|
1262
1280
|
signTxn(sk: Uint8Array): Uint8Array {
|
|
1263
1281
|
// TODO: deprecate in favor of SignedTransaction class
|
|
1264
1282
|
const keypair = nacl.keyPairFromSecretKey(sk);
|