@neuraiproject/neurai-key 3.0.1 → 3.0.2

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 CHANGED
@@ -16,7 +16,7 @@ That is, use your 12 words to get addresses for Neurai mainnet and testnet.
16
16
  - ✅ Mainnet and Testnet support for Neurai (XNA)
17
17
  - ✅ Support for both XNA (BIP44: 1900) and XNA Legacy (BIP44: 0) networks
18
18
  - ✅ Convert raw public keys into Neurai mainnet or testnet addresses
19
- - ✅ PostQuantum addresses using ML-DSA-44 (FIPS 204) with Bech32m encoding
19
+ - ✅ PostQuantum AuthScript addresses using ML-DSA-44 (FIPS 204) with Bech32m encoding
20
20
 
21
21
  ## Network Types
22
22
 
@@ -24,7 +24,7 @@ This library supports three Neurai network configurations:
24
24
 
25
25
  - **`xna` / `xna-test`**: Current Neurai standard (BIP44 coin type: 1900)
26
26
  - **`xna-legacy` / `xna-legacy-test`**: Legacy Neurai addresses (BIP44 coin type: 0)
27
- - **`xna-pq` / `xna-pq-test`**: PostQuantum ML-DSA-44 addresses (Bech32m, witness v1)
27
+ - **`xna-pq` / `xna-pq-test`**: PostQuantum ML-DSA-44 AuthScript addresses (Bech32m, witness v1)
28
28
 
29
29
  The main difference is the derivation path and address encoding:
30
30
  - **XNA**: mainnet `m/44'/1900'/0'/0/0`, testnet `m/44'/1'/0'/0/0` — Base58Check (recommended for new wallets)
@@ -181,9 +181,9 @@ console.log(testAddress); // tPXGaMRNwZuV1UKSrD9gABPscrJWUmedQ9
181
181
 
182
182
  `publicKeyToAddress` throws if the key length is not 33 or 65 bytes so invalid inputs are surfaced immediately.
183
183
 
184
- ## PostQuantum (ML-DSA-44) Addresses
184
+ ## PostQuantum (ML-DSA-44) AuthScript Addresses
185
185
 
186
- Generate quantum-resistant addresses using the ML-DSA-44 signature scheme (FIPS 204). These addresses use Bech32m encoding with witness version 1, preparing for a future post-quantum fork.
186
+ Generate quantum-resistant AuthScript addresses using the ML-DSA-44 signature scheme (FIPS 204). The library now follows the migrated `witness v1 = AuthScript` layout, so the Bech32m program is a 32-byte commitment instead of the old 20-byte PQ keyhash.
187
187
 
188
188
  ### Generate a PQ address
189
189
 
@@ -205,11 +205,15 @@ Outputs
205
205
 
206
206
  ```
207
207
  {
208
- address: 'nq1...', // Bech32m address
208
+ address: 'nq1...', // Bech32m AuthScript address
209
+ authType: 1, // 0x01 = PQ single-key auth
210
+ authDescriptor: '01...', // 0x01 || HASH160(pq_pubkey)
211
+ commitment: '...', // tagged_hash("NeuraiAuthScript", ...)
209
212
  path: "m/100'/1900'/0'/0/0", // PQ derivation path
210
213
  publicKey: '...', // ML-DSA-44 public key (2624 hex chars = 1312 bytes)
211
214
  privateKey: '...', // ML-DSA-44 private key (5120 hex chars = 2560 bytes)
212
- seedKey: '...' // 32-byte BIP32 seed used for ML-DSA keygen (64 hex chars)
215
+ seedKey: '...', // 32-byte BIP32 seed used for ML-DSA keygen (64 hex chars)
216
+ witnessScript: '51' // default OP_TRUE script for simple PQ auth
213
217
  }
214
218
  ```
215
219
 
@@ -229,6 +233,19 @@ const reconstructed = NeuraiKey.pqPublicKeyToAddress("xna-pq", pqAddress.publicK
229
233
  // reconstructed === pqAddress.address
230
234
  ```
231
235
 
236
+ ### Custom AuthScript witnessScript
237
+
238
+ ```javascript
239
+ const pqAddress = NeuraiKey.getPQAddress("xna-pq", mnemonic, 0, 0, "", {
240
+ witnessScript: "5151"
241
+ });
242
+
243
+ console.log(pqAddress.witnessScript); // 5151
244
+ console.log(pqAddress.commitment); // new 32-byte commitment
245
+ ```
246
+
247
+ `authType` no es configurable en la API pública. Esta librería genera únicamente direcciones PQ simples con `authType = 0x01`.
248
+
232
249
  ### Advanced: derive by path with HD key reuse
233
250
 
234
251
  ```javascript
@@ -237,20 +254,22 @@ const addr0 = NeuraiKey.getPQAddressByPath("xna-pq", hdKey, "m/100'/1900'/0'/0/0
237
254
  const addr1 = NeuraiKey.getPQAddressByPath("xna-pq", hdKey, "m/100'/1900'/0'/0/1");
238
255
  ```
239
256
 
240
- ### PQ Address Details
257
+ ### PQ AuthScript Details
241
258
 
242
259
  | Property | Value |
243
260
  |----------|-------|
244
261
  | Signature algorithm | ML-DSA-44 (FIPS 204) |
245
- | Address encoding | Bech32m (witness version 1) |
262
+ | Address encoding | Bech32m (`witness v1` AuthScript) |
246
263
  | Mainnet HRP / prefix | `nq` / `nq1...` |
247
264
  | Testnet HRP / prefix | `tnq` / `tnq1...` |
248
265
  | Public key size | 1312 bytes |
249
266
  | Derivation path (mainnet) | `m/100'/1900'/0'/0/index` |
250
267
  | Derivation path (testnet default/external) | `m/100'/1'/account'/0/index` |
251
- | Address hash | HASH160(0x05 \|\| pubkey) |
268
+ | Auth descriptor | `0x01 \|\| HASH160(pq_pubkey)` |
269
+ | Commitment | `tagged_hash("NeuraiAuthScript", 0x01 \|\| auth_descriptor \|\| SHA256(witnessScript))` |
270
+ | Default witnessScript | `OP_TRUE` (`51` in hex) |
252
271
 
253
- **Note**: PQ addresses do not have a WIF (Wallet Import Format) field since WIF is specific to secp256k1 keys. The `seedKey` field contains the 32-byte BIP32-derived seed used for deterministic ML-DSA-44 key generation, useful for cross-implementation verification.
272
+ **Note**: PQ AuthScript addresses do not have a WIF (Wallet Import Format) field since WIF is specific to secp256k1 keys. The `seedKey` field contains the 32-byte BIP32-derived seed used for deterministic ML-DSA-44 key generation, useful for cross-implementation verification.
254
273
 
255
274
  ## Get public key from WIF
256
275
 
@@ -21503,6 +21503,13 @@ zurdo`.split('\n');
21503
21503
  function hash160(data) {
21504
21504
  return ripemd160(sha256(data));
21505
21505
  }
21506
+ function sha256Hash(data) {
21507
+ return sha256(data);
21508
+ }
21509
+ function taggedHash(tag, data) {
21510
+ const tagHash = sha256(utf8ToBytes(tag));
21511
+ return sha256(concatBytes(tagHash, tagHash, data));
21512
+ }
21506
21513
  function doubleSha256(data) {
21507
21514
  return sha256(sha256(data));
21508
21515
  }
@@ -21543,7 +21550,6 @@ zurdo`.split('\n');
21543
21550
  return Uint8Array.from(mnemonicToSeedSync(mnemonic, passphrase));
21544
21551
  }
21545
21552
  const BITCOIN_SEED_KEY = utf8ToBytes("Bitcoin seed");
21546
- const HASH160_PREFIX = Uint8Array.from([0x05]);
21547
21553
 
21548
21554
  /**
21549
21555
  * Utils for modular division and fields.
@@ -23708,6 +23714,10 @@ zurdo`.split('\n');
23708
23714
 
23709
23715
  var distExports = requireDist();
23710
23716
 
23717
+ const AUTHSCRIPT_TAG = "NeuraiAuthScript";
23718
+ const AUTHSCRIPT_VERSION = 0x01;
23719
+ const PQ_AUTH_TYPE = 0x01;
23720
+ const DEFAULT_WITNESS_SCRIPT = Uint8Array.from([0x51]);
23711
23721
  function encodeWIF(privateKey, version, compressed = true) {
23712
23722
  const payload = compressed
23713
23723
  ? concatBytes(Uint8Array.from([version]), privateKey, Uint8Array.from([0x01]))
@@ -23764,9 +23774,29 @@ zurdo`.split('\n');
23764
23774
  function bech32mEncode(hrp, witnessVersion, hash) {
23765
23775
  return distExports.bech32m.encode(hrp, [witnessVersion, ...distExports.bech32m.toWords(hash)]);
23766
23776
  }
23767
- function pqPublicKeyToAddressBytes(publicKey, network) {
23768
- const serialized = concatBytes(HASH160_PREFIX, publicKey);
23769
- return bech32mEncode(network.hrp, network.witnessVersion, hash160(serialized));
23777
+ function normalizeWitnessScript(input) {
23778
+ return input ? ensureBytes(input) : Uint8Array.from(DEFAULT_WITNESS_SCRIPT);
23779
+ }
23780
+ function pqPublicKeyToAuthDescriptor(publicKey) {
23781
+ return concatBytes(Uint8Array.from([PQ_AUTH_TYPE]), hash160(publicKey));
23782
+ }
23783
+ function pqPublicKeyToCommitment(publicKey, options = {}) {
23784
+ return pqPublicKeyToCommitmentParts(publicKey, options).commitment;
23785
+ }
23786
+ function pqPublicKeyToCommitmentParts(publicKey, options = {}) {
23787
+ const witnessScript = normalizeWitnessScript(options.witnessScript);
23788
+ const authDescriptor = pqPublicKeyToAuthDescriptor(publicKey);
23789
+ const witnessScriptHash = sha256Hash(witnessScript);
23790
+ const commitment = taggedHash(AUTHSCRIPT_TAG, concatBytes(Uint8Array.from([AUTHSCRIPT_VERSION]), authDescriptor, witnessScriptHash));
23791
+ return {
23792
+ authDescriptor,
23793
+ authType: PQ_AUTH_TYPE,
23794
+ commitment,
23795
+ witnessScript,
23796
+ };
23797
+ }
23798
+ function pqPublicKeyToAddressBytes(publicKey, network, options = {}) {
23799
+ return bech32mEncode(network.hrp, network.witnessVersion, pqPublicKeyToCommitment(publicKey, options));
23770
23800
  }
23771
23801
  function normalizePublicKey(input) {
23772
23802
  return ensureBytes(input);
@@ -24026,7 +24056,7 @@ zurdo`.split('\n');
24026
24056
  const seed = mnemonicToSeedBytes(mnemonicToSeedSync, mnemonic, passphrase);
24027
24057
  return HDKey.fromMasterSeed(seed, chain.bip32);
24028
24058
  }
24029
- function getPQAddressByPath(network, hdKey, path) {
24059
+ function getPQAddressByPath(network, hdKey, path, options = {}) {
24030
24060
  const chain = getPQNetwork(network);
24031
24061
  const derived = hdKey.derive(path);
24032
24062
  if (!derived.privateKey) {
@@ -24034,30 +24064,50 @@ zurdo`.split('\n');
24034
24064
  }
24035
24065
  const seed32 = Uint8Array.from(derived.privateKey);
24036
24066
  const { publicKey, secretKey } = ml_dsa44.keygen(seed32);
24067
+ const authScript = pqPublicKeyToCommitmentParts(publicKey, options);
24037
24068
  return {
24038
- address: pqPublicKeyToAddressBytes(publicKey, chain),
24069
+ address: pqPublicKeyToAddressBytes(publicKey, chain, options),
24070
+ authType: authScript.authType,
24071
+ authDescriptor: bytesToHex(authScript.authDescriptor),
24072
+ commitment: bytesToHex(authScript.commitment),
24039
24073
  path,
24040
24074
  publicKey: bytesToHex(publicKey),
24041
24075
  privateKey: bytesToHex(secretKey),
24042
24076
  seedKey: bytesToHex(seed32),
24077
+ witnessScript: bytesToHex(authScript.witnessScript),
24043
24078
  };
24044
24079
  }
24045
- function getPQAddress(network, mnemonic, account, index, passphrase = "") {
24080
+ function getPQAddress(network, mnemonic, account, index, passphrase = "", options = {}) {
24046
24081
  const chain = getPQNetwork(network);
24047
24082
  const hdKey = getPQHDKey(network, mnemonic, passphrase);
24048
24083
  const path = `m/${chain.purpose}'/${chain.coinType}'/${account}'/${chain.changeIndex}/${index}`;
24049
- return getPQAddressByPath(network, hdKey, path);
24084
+ return getPQAddressByPath(network, hdKey, path, options);
24085
+ }
24086
+ function pqPublicKeyToAddress(network, publicKey, options = {}) {
24087
+ const keyBytes = ensureBytes(publicKey);
24088
+ if (keyBytes.length !== 1312) {
24089
+ throw new Error("ML-DSA-44 public key must be 1312 bytes");
24090
+ }
24091
+ normalizeWitnessScript(options.witnessScript);
24092
+ return pqPublicKeyToAddressBytes(keyBytes, getPQNetwork(network), options);
24093
+ }
24094
+ function pqPublicKeyToCommitmentHex(publicKey, options = {}) {
24095
+ const keyBytes = ensureBytes(publicKey);
24096
+ if (keyBytes.length !== 1312) {
24097
+ throw new Error("ML-DSA-44 public key must be 1312 bytes");
24098
+ }
24099
+ return bytesToHex(pqPublicKeyToCommitment(keyBytes, options));
24050
24100
  }
24051
- function pqPublicKeyToAddress(network, publicKey) {
24101
+ function pqPublicKeyToAuthDescriptorHex(publicKey) {
24052
24102
  const keyBytes = ensureBytes(publicKey);
24053
24103
  if (keyBytes.length !== 1312) {
24054
24104
  throw new Error("ML-DSA-44 public key must be 1312 bytes");
24055
24105
  }
24056
- return pqPublicKeyToAddressBytes(keyBytes, getPQNetwork(network));
24106
+ return bytesToHex(pqPublicKeyToAuthDescriptor(keyBytes));
24057
24107
  }
24058
- function generatePQAddressObject(network = "xna-pq", passphrase = "") {
24108
+ function generatePQAddressObject(network = "xna-pq", passphrase = "", options = {}) {
24059
24109
  const mnemonic = generateMnemonic();
24060
- const addressObj = getPQAddress(network, mnemonic, 0, 0, passphrase);
24110
+ const addressObj = getPQAddress(network, mnemonic, 0, 0, passphrase, options);
24061
24111
  return {
24062
24112
  ...addressObj,
24063
24113
  mnemonic,
@@ -24080,6 +24130,8 @@ zurdo`.split('\n');
24080
24130
  getPQAddressByPath,
24081
24131
  getPQHDKey,
24082
24132
  pqPublicKeyToAddress,
24133
+ pqPublicKeyToAuthDescriptorHex,
24134
+ pqPublicKeyToCommitmentHex,
24083
24135
  generatePQAddressObject,
24084
24136
  };
24085
24137