@monolythium/core-sdk 0.4.22 → 0.4.24
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/dist/crypto/index.cjs +6 -529
- package/dist/crypto/index.cjs.map +1 -1
- package/dist/crypto/index.d.cts +75 -3
- package/dist/crypto/index.d.ts +75 -3
- package/dist/crypto/index.js +6 -494
- package/dist/crypto/index.js.map +1 -1
- package/dist/index.cjs +638 -1031
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7014 -392
- package/dist/index.d.ts +7014 -392
- package/dist/index.js +633 -1025
- package/dist/index.js.map +1 -1
- package/dist/ml-dsa-Drcmrw5h.d.cts +90 -0
- package/dist/ml-dsa-Drcmrw5h.d.ts +90 -0
- package/package.json +1 -1
- package/dist/submission-B4FmDnm_.d.cts +0 -7079
- package/dist/submission-B4FmDnm_.d.ts +0 -7079
package/dist/crypto/index.js
CHANGED
|
@@ -3,9 +3,6 @@ import { blake3 } from '@noble/hashes/blake3.js';
|
|
|
3
3
|
import { keccak_256, shake256 } from '@noble/hashes/sha3.js';
|
|
4
4
|
import { entropyToMnemonic, mnemonicToEntropy } from '@scure/bip39';
|
|
5
5
|
import { wordlist } from '@scure/bip39/wordlists/english.js';
|
|
6
|
-
import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';
|
|
7
|
-
import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
8
|
-
import { randomBytes } from '@noble/hashes/utils.js';
|
|
9
6
|
|
|
10
7
|
// src/crypto/bincode.ts
|
|
11
8
|
var BincodeWriter = class {
|
|
@@ -446,12 +443,8 @@ function generatePqm1Mnemonic(rng = defaultRandomFill) {
|
|
|
446
443
|
rng(entropy);
|
|
447
444
|
return pqm1PayloadToMnemonic(assemblePqm1Payload(entropy));
|
|
448
445
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
var ML_KEM_768_ENCAPSULATION_KEY_LEN = 1184;
|
|
452
|
-
var ML_KEM_768_SHARED_SECRET_LEN = 32;
|
|
453
|
-
var DKG_NONCE_LEN = 12;
|
|
454
|
-
var DKG_AEAD_TAG_LEN = 16;
|
|
446
|
+
|
|
447
|
+
// src/crypto/envelope.ts
|
|
455
448
|
var MempoolClass = {
|
|
456
449
|
Transfer: 0,
|
|
457
450
|
ContractCall: 1,
|
|
@@ -463,430 +456,8 @@ var MempoolClass = {
|
|
|
463
456
|
GovernanceOp: 5,
|
|
464
457
|
RWAOp: 6
|
|
465
458
|
};
|
|
466
|
-
function bincodeNonceAad(aad) {
|
|
467
|
-
const w = new BincodeWriter();
|
|
468
|
-
w.bytes(expectBytes(aad.sender, 20, "NonceAad.sender"));
|
|
469
|
-
w.u64(aad.nonce);
|
|
470
|
-
w.u64(aad.chainId);
|
|
471
|
-
w.enumVariant(aad.class);
|
|
472
|
-
w.u128(aad.maxFeePerGas);
|
|
473
|
-
w.u128(aad.maxPriorityFeePerGas);
|
|
474
|
-
w.u64(aad.gasLimit);
|
|
475
|
-
return w.toBytes();
|
|
476
|
-
}
|
|
477
|
-
function bincodeDecryptHint(hint) {
|
|
478
|
-
const w = new BincodeWriter();
|
|
479
|
-
w.u64(hint.epoch);
|
|
480
|
-
w.u16(hint.scheme);
|
|
481
|
-
return w.toBytes();
|
|
482
|
-
}
|
|
483
|
-
function bincodeEncryptedEnvelope(env) {
|
|
484
|
-
const w = new BincodeWriter();
|
|
485
|
-
w.rawBytes(bincodeNonceAad(env.nonceAad));
|
|
486
|
-
w.bytes(env.ciphertext);
|
|
487
|
-
w.rawBytes(bincodeDecryptHint(env.decryptionHint));
|
|
488
|
-
bincodeMlDsa65OpaqueInto2(w, expectBytes(env.senderPubkey, ML_DSA_65_PUBLIC_KEY_LEN, "senderPubkey"));
|
|
489
|
-
bincodeMlDsa65OpaqueInto2(w, expectBytes(env.outerSignature, ML_DSA_65_SIGNATURE_LEN, "outerSignature"));
|
|
490
|
-
w.bytes(expectBytes(env.sender, 20, "sender"));
|
|
491
|
-
return w.toBytes();
|
|
492
|
-
}
|
|
493
|
-
function encryptInnerTx(signedInnerTxBincode, nonceAad, kemEncapsulationKey) {
|
|
494
|
-
expectBytes(kemEncapsulationKey, ML_KEM_768_ENCAPSULATION_KEY_LEN, "kemEncapsulationKey");
|
|
495
|
-
const { cipherText: kemCt, sharedSecret } = ml_kem768.encapsulate(kemEncapsulationKey);
|
|
496
|
-
const nonce = randomBytes(DKG_NONCE_LEN);
|
|
497
|
-
const cipher = chacha20poly1305(sharedSecret, nonce, aadFor(nonceAad));
|
|
498
|
-
const aeadCt = cipher.encrypt(signedInnerTxBincode);
|
|
499
|
-
sharedSecret.fill(0);
|
|
500
|
-
return concatBytes(kemCt, nonce, aeadCt);
|
|
501
|
-
}
|
|
502
|
-
function outerSigDigest(nonceAad, ciphertext, decryptionHint, senderPubkey) {
|
|
503
|
-
const aad = bincodeNonceAad(nonceAad);
|
|
504
|
-
const hint = bincodeDecryptHint(decryptionHint);
|
|
505
|
-
return keccak_256(concatBytes(aad, ciphertext, hint, expectBytes(senderPubkey, ML_DSA_65_PUBLIC_KEY_LEN, "senderPubkey")));
|
|
506
|
-
}
|
|
507
|
-
async function buildEncryptedEnvelope(args) {
|
|
508
|
-
const ciphertext = encryptInnerTx(args.signedInnerTxBincode, args.nonceAad, args.kemEncapsulationKey);
|
|
509
|
-
const digest = outerSigDigest(args.nonceAad, ciphertext, args.decryptionHint, args.senderPubkey);
|
|
510
|
-
const outerSignature = await args.signOuterDigest(digest);
|
|
511
|
-
const envelope = {
|
|
512
|
-
nonceAad: args.nonceAad,
|
|
513
|
-
ciphertext,
|
|
514
|
-
decryptionHint: args.decryptionHint,
|
|
515
|
-
senderPubkey: expectBytes(args.senderPubkey, ML_DSA_65_PUBLIC_KEY_LEN, "senderPubkey"),
|
|
516
|
-
outerSignature: expectBytes(outerSignature, ML_DSA_65_SIGNATURE_LEN, "outerSignature"),
|
|
517
|
-
sender: expectBytes(args.senderAddress, 20, "senderAddress")
|
|
518
|
-
};
|
|
519
|
-
const wireBytes = bincodeEncryptedEnvelope(envelope);
|
|
520
|
-
return { envelope, wireBytes, wireHex: bytesToHex(wireBytes) };
|
|
521
|
-
}
|
|
522
|
-
function aadFor(aad) {
|
|
523
|
-
return concatBytes(DKG_AEAD_DOMAIN_TAG, bincodeNonceAad(aad));
|
|
524
|
-
}
|
|
525
|
-
function bincodeMlDsa65OpaqueInto2(w, raw) {
|
|
526
|
-
w.enumVariant(ENUM_VARIANT_INDEX_ML_DSA_65);
|
|
527
|
-
w.u16(STANDARD_ALGO_NUMBER_ML_DSA_65);
|
|
528
|
-
w.bytes(raw);
|
|
529
|
-
}
|
|
530
|
-
var SEAL_EK_LEN = 1184;
|
|
531
|
-
var SEAL_DK_LEN = 2400;
|
|
532
|
-
var SEAL_KEM_CT_LEN = 1088;
|
|
533
|
-
var SEAL_KEM_SEED_LEN = 64;
|
|
534
|
-
var SEAL_KEY_LEN = 32;
|
|
535
|
-
var SEAL_NONCE_LEN = 12;
|
|
536
|
-
var SEAL_TAG_LEN = 16;
|
|
537
|
-
var SEAL_COMMIT_LEN = 32;
|
|
538
|
-
var SEAL_SECRET_LEN = 32;
|
|
539
|
-
var SEAL_SHARE_LEN = 1 + SEAL_SECRET_LEN;
|
|
540
|
-
var CLUSTER_MLKEM_SHAMIR = 3;
|
|
541
|
-
var COMMIT_DOMAIN = new TextEncoder().encode("lythiumseal/commit/v1");
|
|
542
|
-
var KEK_DOMAIN = new TextEncoder().encode("lythiumseal/kek/v1");
|
|
543
|
-
var NONCE_DOMAIN = new TextEncoder().encode("lythiumseal/nonce/v1");
|
|
544
|
-
var BODY_AAD_DOMAIN = new TextEncoder().encode("lythiumseal/body/v1");
|
|
545
|
-
var SHARE_AAD_DOMAIN = new TextEncoder().encode("lythiumseal/share/v1");
|
|
546
|
-
var ROSTER_DOMAIN = new TextEncoder().encode("lythiumseal/roster/v1");
|
|
547
|
-
function cryptoRandomSource() {
|
|
548
|
-
return {
|
|
549
|
-
fillBytes(dest) {
|
|
550
|
-
crypto.getRandomValues(dest);
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
function generateOperatorSealKeypair() {
|
|
555
|
-
const { publicKey, secretKey } = ml_kem768.keygen();
|
|
556
|
-
return {
|
|
557
|
-
encapsulationKey: expectBytes(publicKey, SEAL_EK_LEN, "encapsulationKey").slice(),
|
|
558
|
-
decapsulationKey: expectBytes(secretKey, SEAL_DK_LEN, "decapsulationKey").slice()
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
function u32le(n) {
|
|
562
|
-
const out = new Uint8Array(4);
|
|
563
|
-
out[0] = n & 255;
|
|
564
|
-
out[1] = n >>> 8 & 255;
|
|
565
|
-
out[2] = n >>> 16 & 255;
|
|
566
|
-
out[3] = n >>> 24 & 255;
|
|
567
|
-
return out;
|
|
568
|
-
}
|
|
569
|
-
function u64le(n) {
|
|
570
|
-
const out = new Uint8Array(8);
|
|
571
|
-
let v = n;
|
|
572
|
-
for (let i = 0; i < 8; i++) {
|
|
573
|
-
out[i] = Number(v & 0xffn);
|
|
574
|
-
v >>= 8n;
|
|
575
|
-
}
|
|
576
|
-
return out;
|
|
577
|
-
}
|
|
578
|
-
function framed(field) {
|
|
579
|
-
return concatBytes(u32le(field.length), field);
|
|
580
|
-
}
|
|
581
|
-
function keyCommitment(key) {
|
|
582
|
-
return shake256(concatBytes(framed(COMMIT_DOMAIN), key), { dkLen: SEAL_COMMIT_LEN });
|
|
583
|
-
}
|
|
584
|
-
function deriveKek(sharedSecret, domain, clusterId, epoch, opIndex) {
|
|
585
|
-
const input = concatBytes(
|
|
586
|
-
framed(KEK_DOMAIN),
|
|
587
|
-
framed(sharedSecret),
|
|
588
|
-
framed(domain),
|
|
589
|
-
u32le(clusterId),
|
|
590
|
-
u64le(epoch),
|
|
591
|
-
Uint8Array.of(opIndex)
|
|
592
|
-
);
|
|
593
|
-
return shake256(input, { dkLen: SEAL_KEY_LEN });
|
|
594
|
-
}
|
|
595
|
-
function deriveNonce(domain, context) {
|
|
596
|
-
const input = concatBytes(framed(NONCE_DOMAIN), framed(domain), framed(context));
|
|
597
|
-
return shake256(input, { dkLen: SEAL_NONCE_LEN });
|
|
598
|
-
}
|
|
599
|
-
function bodyAad(ctx, k, n) {
|
|
600
|
-
return concatBytes(
|
|
601
|
-
BODY_AAD_DOMAIN,
|
|
602
|
-
u32le(ctx.clusterId),
|
|
603
|
-
u64le(ctx.epoch),
|
|
604
|
-
Uint8Array.of(k),
|
|
605
|
-
Uint8Array.of(n),
|
|
606
|
-
ctx.rosterHash
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
function shareAad(ctx, opIndex) {
|
|
610
|
-
return concatBytes(
|
|
611
|
-
SHARE_AAD_DOMAIN,
|
|
612
|
-
u32le(ctx.clusterId),
|
|
613
|
-
u64le(ctx.epoch),
|
|
614
|
-
Uint8Array.of(opIndex),
|
|
615
|
-
ctx.rosterHash
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
function aeadSeal(key, nonce, plaintext, aad) {
|
|
619
|
-
const cipher = chacha20poly1305(key, nonce, aad);
|
|
620
|
-
const ct = cipher.encrypt(plaintext);
|
|
621
|
-
return { nonce, ct, commitment: keyCommitment(key) };
|
|
622
|
-
}
|
|
623
|
-
function gfMul(a, b) {
|
|
624
|
-
let product = 0;
|
|
625
|
-
let x = a & 255;
|
|
626
|
-
let y = b & 255;
|
|
627
|
-
for (let i = 0; i < 8; i++) {
|
|
628
|
-
const mask = -(y & 1) & 255;
|
|
629
|
-
product ^= x & mask;
|
|
630
|
-
const high = -(x >> 7 & 1) & 255;
|
|
631
|
-
x = x << 1 & 255;
|
|
632
|
-
x ^= 27 & high;
|
|
633
|
-
y >>= 1;
|
|
634
|
-
}
|
|
635
|
-
return product & 255;
|
|
636
|
-
}
|
|
637
|
-
function polyEval(coeffs, x) {
|
|
638
|
-
let acc = 0;
|
|
639
|
-
for (let i = coeffs.length - 1; i >= 0; i--) {
|
|
640
|
-
acc = gfMul(acc, x) ^ coeffs[i];
|
|
641
|
-
}
|
|
642
|
-
return acc & 255;
|
|
643
|
-
}
|
|
644
|
-
function shamirSplit(secret, t, n, rng) {
|
|
645
|
-
const byteCoeffs = [];
|
|
646
|
-
for (let j = 0; j < SEAL_SECRET_LEN; j++) {
|
|
647
|
-
const c = new Uint8Array(t);
|
|
648
|
-
c[0] = secret[j];
|
|
649
|
-
if (t > 1) {
|
|
650
|
-
const tail = new Uint8Array(t - 1);
|
|
651
|
-
rng.fillBytes(tail);
|
|
652
|
-
c.set(tail, 1);
|
|
653
|
-
}
|
|
654
|
-
byteCoeffs.push(c);
|
|
655
|
-
}
|
|
656
|
-
const shares = [];
|
|
657
|
-
for (let k = 0; k < n; k++) {
|
|
658
|
-
const x = k + 1 & 255;
|
|
659
|
-
const value = new Uint8Array(SEAL_SECRET_LEN);
|
|
660
|
-
for (let j = 0; j < SEAL_SECRET_LEN; j++) {
|
|
661
|
-
value[j] = polyEval(byteCoeffs[j], x);
|
|
662
|
-
}
|
|
663
|
-
shares.push({ index: x, value });
|
|
664
|
-
}
|
|
665
|
-
return shares;
|
|
666
|
-
}
|
|
667
|
-
function shareToBytes(s) {
|
|
668
|
-
const out = new Uint8Array(SEAL_SHARE_LEN);
|
|
669
|
-
out[0] = s.index;
|
|
670
|
-
out.set(s.value, 1);
|
|
671
|
-
return out;
|
|
672
|
-
}
|
|
673
|
-
function sealRosterHash(keccak2562, clusterId, t, n, roster) {
|
|
674
|
-
const chunks = [ROSTER_DOMAIN, u32le(clusterId), Uint8Array.of(t), Uint8Array.of(n)];
|
|
675
|
-
for (const { operatorIndex, ek } of roster) {
|
|
676
|
-
chunks.push(Uint8Array.of(operatorIndex), ek);
|
|
677
|
-
}
|
|
678
|
-
return keccak2562(concatBytes(...chunks));
|
|
679
|
-
}
|
|
680
|
-
function encodeSealEnvelope(env) {
|
|
681
|
-
const chunks = [];
|
|
682
|
-
chunks.push(u32le(env.clusterId));
|
|
683
|
-
chunks.push(u64le(env.epoch));
|
|
684
|
-
chunks.push(expectBytes(env.rosterHash, 32, "rosterHash"));
|
|
685
|
-
chunks.push(Uint8Array.of(env.t));
|
|
686
|
-
chunks.push(Uint8Array.of(env.n));
|
|
687
|
-
pushAeadBody(chunks, env.aeadBody);
|
|
688
|
-
chunks.push(u64le(BigInt(env.recipients.length)));
|
|
689
|
-
for (const r of env.recipients) {
|
|
690
|
-
chunks.push(Uint8Array.of(r.operatorIndex));
|
|
691
|
-
chunks.push(u64le(BigInt(r.kemCt.length)));
|
|
692
|
-
chunks.push(r.kemCt);
|
|
693
|
-
pushAeadBody(chunks, r.wrapped);
|
|
694
|
-
}
|
|
695
|
-
return concatBytes(...chunks);
|
|
696
|
-
}
|
|
697
|
-
function pushAeadBody(chunks, body) {
|
|
698
|
-
chunks.push(expectBytes(body.nonce, SEAL_NONCE_LEN, "aead nonce"));
|
|
699
|
-
chunks.push(u64le(BigInt(body.ct.length)));
|
|
700
|
-
chunks.push(body.ct);
|
|
701
|
-
chunks.push(expectBytes(body.commitment, SEAL_COMMIT_LEN, "aead commitment"));
|
|
702
|
-
}
|
|
703
|
-
function sealToCluster(args) {
|
|
704
|
-
const { plaintext, recipientEks, t, clusterId } = args;
|
|
705
|
-
const epoch = args.epoch;
|
|
706
|
-
const rosterHash = expectBytes(args.rosterHash, 32, "rosterHash");
|
|
707
|
-
const rng = args.rng ?? cryptoRandomSource();
|
|
708
|
-
const n = recipientEks.length;
|
|
709
|
-
if (!Number.isInteger(t) || t < 1 || t > n || n < 1 || n > 255) {
|
|
710
|
-
throw new Error(`invalid threshold/recipient count: t=${t} n=${n}`);
|
|
711
|
-
}
|
|
712
|
-
for (let i = 0; i < n; i++) {
|
|
713
|
-
expectBytes(recipientEks[i], SEAL_EK_LEN, `recipientEks[${i}]`);
|
|
714
|
-
}
|
|
715
|
-
const ctx = { clusterId, epoch, rosterHash };
|
|
716
|
-
const bodyKey = new Uint8Array(SEAL_KEY_LEN);
|
|
717
|
-
rng.fillBytes(bodyKey);
|
|
718
|
-
const aad = bodyAad(ctx, t, n);
|
|
719
|
-
const bodyNonce = deriveNonce(new TextEncoder().encode("body"), aad);
|
|
720
|
-
const aeadBody = aeadSeal(bodyKey, bodyNonce, plaintext, aad);
|
|
721
|
-
const shares = shamirSplit(bodyKey, t, n, rng);
|
|
722
|
-
const recipients = [];
|
|
723
|
-
for (let i = 0; i < n; i++) {
|
|
724
|
-
const opIndex = i + 1 & 255;
|
|
725
|
-
const m = new Uint8Array(32);
|
|
726
|
-
rng.fillBytes(m);
|
|
727
|
-
const { cipherText: kemCt, sharedSecret } = ml_kem768.encapsulate(recipientEks[i], m);
|
|
728
|
-
const kek = deriveKek(sharedSecret, rosterHash, clusterId, epoch, opIndex);
|
|
729
|
-
const sAad = shareAad(ctx, opIndex);
|
|
730
|
-
const wrapNonce = deriveNonce(new TextEncoder().encode("share"), sAad);
|
|
731
|
-
const wrapped = aeadSeal(kek, wrapNonce, shareToBytes(shares[i]), sAad);
|
|
732
|
-
recipients.push({ operatorIndex: opIndex, kemCt, wrapped });
|
|
733
|
-
sharedSecret.fill(0);
|
|
734
|
-
kek.fill(0);
|
|
735
|
-
}
|
|
736
|
-
bodyKey.fill(0);
|
|
737
|
-
return {
|
|
738
|
-
clusterId,
|
|
739
|
-
epoch,
|
|
740
|
-
rosterHash,
|
|
741
|
-
t,
|
|
742
|
-
n,
|
|
743
|
-
aeadBody,
|
|
744
|
-
recipients
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// src/crypto/seal.ts
|
|
749
|
-
var CLUSTER_MLKEM_SHAMIR_ALGO = "cluster-mlkem768-shamir";
|
|
750
|
-
function parseClusterSealKeys(source) {
|
|
751
|
-
const n = source.roster.length;
|
|
752
|
-
if (n === 0) {
|
|
753
|
-
throw new Error("cluster seal roster is empty");
|
|
754
|
-
}
|
|
755
|
-
if (source.n !== n) {
|
|
756
|
-
throw new Error(`cluster seal roster n=${source.n} disagrees with ${n} entries`);
|
|
757
|
-
}
|
|
758
|
-
if (!Number.isInteger(source.t) || source.t < 2 || source.t > n) {
|
|
759
|
-
throw new Error(`cluster seal threshold t=${source.t} out of range 2..=${n}`);
|
|
760
|
-
}
|
|
761
|
-
const sorted = [...source.roster].sort((a, b) => a.operatorIndex - b.operatorIndex);
|
|
762
|
-
const recipientEks = [];
|
|
763
|
-
const hashInput = [];
|
|
764
|
-
for (let i = 0; i < n; i++) {
|
|
765
|
-
const entry = sorted[i];
|
|
766
|
-
if (entry.operatorIndex !== i + 1) {
|
|
767
|
-
throw new Error(
|
|
768
|
-
`cluster seal roster operator indices must be 1..=${n}; got ${entry.operatorIndex} at slot ${i + 1}`
|
|
769
|
-
);
|
|
770
|
-
}
|
|
771
|
-
const ek = expectBytes(hexToBytes(entry.mlKemEk, `operator ${entry.operatorIndex} mlKemEk`), SEAL_EK_LEN, `operator ${entry.operatorIndex} ek`);
|
|
772
|
-
recipientEks.push(ek);
|
|
773
|
-
hashInput.push({ operatorIndex: entry.operatorIndex, ek });
|
|
774
|
-
}
|
|
775
|
-
const recomputed = sealRosterHash(keccak256, source.clusterId, source.t, n, hashInput);
|
|
776
|
-
if (source.rosterHash !== void 0) {
|
|
777
|
-
const supplied = expectBytes(hexToBytes(source.rosterHash, "rosterHash"), 32, "rosterHash");
|
|
778
|
-
if (!bytesEqual(supplied, recomputed)) {
|
|
779
|
-
throw new Error(
|
|
780
|
-
`cluster seal roster hash mismatch: source ${bytesToHex(supplied)} != recomputed ${bytesToHex(recomputed)} (the roster hash does not commit to this ek set)`
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
return {
|
|
785
|
-
algo: source.algo ?? CLUSTER_MLKEM_SHAMIR_ALGO,
|
|
786
|
-
clusterId: source.clusterId,
|
|
787
|
-
epoch: toBigInt(source.epoch),
|
|
788
|
-
rosterHash: recomputed,
|
|
789
|
-
t: source.t,
|
|
790
|
-
n,
|
|
791
|
-
recipientEks
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
async function getClusterSealKeys(client, clusterId = 0) {
|
|
795
|
-
const result = await client.call(
|
|
796
|
-
"lyth_getClusterSealKeys",
|
|
797
|
-
[clusterId]
|
|
798
|
-
);
|
|
799
|
-
return parseClusterSealKeys({ ...result, clusterId: result.clusterId ?? clusterId });
|
|
800
|
-
}
|
|
801
|
-
async function sealTransaction(args) {
|
|
802
|
-
const keys = args.clusterSealKeys;
|
|
803
|
-
const senderPubkey = expectBytes(args.senderPubkey, ML_DSA_65_PUBLIC_KEY_LEN, "senderPubkey");
|
|
804
|
-
const senderAddress = expectBytes(args.senderAddress, 20, "senderAddress");
|
|
805
|
-
const env = sealToCluster({
|
|
806
|
-
plaintext: args.signedTxBincode,
|
|
807
|
-
recipientEks: keys.recipientEks,
|
|
808
|
-
t: keys.t,
|
|
809
|
-
clusterId: keys.clusterId,
|
|
810
|
-
epoch: keys.epoch,
|
|
811
|
-
rosterHash: keys.rosterHash,
|
|
812
|
-
rng: args.rng
|
|
813
|
-
});
|
|
814
|
-
const ciphertext = encodeSealEnvelope(env);
|
|
815
|
-
const decryptionHint = { epoch: keys.epoch, scheme: CLUSTER_MLKEM_SHAMIR };
|
|
816
|
-
const digest = outerSigDigest(args.aad, ciphertext, decryptionHint, senderPubkey);
|
|
817
|
-
const outerSignature = expectBytes(
|
|
818
|
-
await args.signOuterDigest(digest),
|
|
819
|
-
ML_DSA_65_SIGNATURE_LEN,
|
|
820
|
-
"outerSignature"
|
|
821
|
-
);
|
|
822
|
-
const envelope = {
|
|
823
|
-
nonceAad: args.aad,
|
|
824
|
-
ciphertext,
|
|
825
|
-
decryptionHint,
|
|
826
|
-
senderPubkey,
|
|
827
|
-
outerSignature,
|
|
828
|
-
sender: senderAddress
|
|
829
|
-
};
|
|
830
|
-
const envelopeWireBytes = bincodeEncryptedEnvelope(envelope);
|
|
831
|
-
return {
|
|
832
|
-
envelopeWireHex: `0x${bytesToHex(envelopeWireBytes).slice(2)}`,
|
|
833
|
-
envelopeWireBytes,
|
|
834
|
-
ciphertextBytes: ciphertext.length
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
async function submitSealedTransaction(client, submission) {
|
|
838
|
-
return client.call("lyth_submitEncrypted", [submission.envelopeWireHex]);
|
|
839
|
-
}
|
|
840
|
-
function keccak256(input) {
|
|
841
|
-
return keccak_256(input);
|
|
842
|
-
}
|
|
843
|
-
function toBigInt(value) {
|
|
844
|
-
if (typeof value === "bigint") return value;
|
|
845
|
-
return BigInt(value);
|
|
846
|
-
}
|
|
847
|
-
function bytesEqual(a, b) {
|
|
848
|
-
if (a.length !== b.length) return false;
|
|
849
|
-
for (let i = 0; i < a.length; i++) {
|
|
850
|
-
if (a[i] !== b[i]) return false;
|
|
851
|
-
}
|
|
852
|
-
return true;
|
|
853
|
-
}
|
|
854
459
|
|
|
855
460
|
// src/crypto/submission.ts
|
|
856
|
-
async function fetchEncryptionKey(client) {
|
|
857
|
-
const result = await client.call(
|
|
858
|
-
"lyth_getEncryptionKey",
|
|
859
|
-
[]
|
|
860
|
-
);
|
|
861
|
-
return {
|
|
862
|
-
algo: result.algo ?? "ml-kem-768",
|
|
863
|
-
epoch: typeof result.epoch === "string" ? BigInt(result.epoch) : BigInt(result.epoch),
|
|
864
|
-
encapsulationKey: hexToBytes(result.encapsulationKey, "encapsulationKey")
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
var ENCRYPTED_SUBMISSION_UNAVAILABLE_MESSAGE = "private submission requires cluster seal keys; pass clusterSealKeysSource or enable lyth_getClusterSealKeys";
|
|
868
|
-
async function buildEncryptedSubmission(args) {
|
|
869
|
-
const signed = args.backend.signEvmTx(args.tx);
|
|
870
|
-
const clusterSealKeys = await resolveClusterSealKeys(args);
|
|
871
|
-
const aad = nonceAadForTx(args.tx, args.backend.addressBytes(), args.class);
|
|
872
|
-
const sealed = await sealTransaction({
|
|
873
|
-
signedTxBincode: signed.wireBytes,
|
|
874
|
-
clusterSealKeys,
|
|
875
|
-
aad,
|
|
876
|
-
senderAddress: args.backend.addressBytes(),
|
|
877
|
-
senderPubkey: args.backend.publicKey(),
|
|
878
|
-
signOuterDigest: (digest) => args.backend.signPrehash(digest)
|
|
879
|
-
});
|
|
880
|
-
return {
|
|
881
|
-
envelopeWireHex: sealed.envelopeWireHex,
|
|
882
|
-
innerSighashHex: bytesToHex(signed.sighash),
|
|
883
|
-
innerTxHashHex: bytesToHex(signed.txHash),
|
|
884
|
-
innerWireBytes: signed.wireBytes.length
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
async function submitEncryptedEnvelope(client, envelopeWireHex) {
|
|
888
|
-
return client.call("lyth_submitEncrypted", [envelopeWireHex]);
|
|
889
|
-
}
|
|
890
461
|
function buildPlaintextSubmission(args) {
|
|
891
462
|
const signed = args.backend.signEvmTx(args.tx);
|
|
892
463
|
return {
|
|
@@ -905,29 +476,14 @@ async function submitPlaintextTransaction(client, signedTxWireHex, expectedTxHas
|
|
|
905
476
|
);
|
|
906
477
|
}
|
|
907
478
|
const expectedBytes = hexToBytes(expectedTxHashHex, "expected tx hash");
|
|
908
|
-
if (!
|
|
479
|
+
if (!bytesEqual(returnedBytes, expectedBytes)) {
|
|
909
480
|
throw new Error(
|
|
910
481
|
`mesh_submitTx returned tx hash ${bytesToHex(returnedBytes)} but the locally computed canonical hash is ${bytesToHex(expectedBytes)}`
|
|
911
482
|
);
|
|
912
483
|
}
|
|
913
484
|
return bytesToHex(returnedBytes);
|
|
914
485
|
}
|
|
915
|
-
async function
|
|
916
|
-
if (args.private) {
|
|
917
|
-
const built = await buildEncryptedSubmission({
|
|
918
|
-
client: args.client,
|
|
919
|
-
backend: args.backend,
|
|
920
|
-
tx: args.tx,
|
|
921
|
-
encryptionKey: args.encryptionKey,
|
|
922
|
-
clusterId: args.clusterId,
|
|
923
|
-
clusterSealKeys: args.clusterSealKeys,
|
|
924
|
-
clusterSealKeysSource: args.clusterSealKeysSource,
|
|
925
|
-
class: args.class
|
|
926
|
-
});
|
|
927
|
-
const returned = await submitEncryptedEnvelope(args.client, built.envelopeWireHex);
|
|
928
|
-
assertRpcHash(returned, "lyth_submitEncrypted tx hash");
|
|
929
|
-
return built.innerTxHashHex;
|
|
930
|
-
}
|
|
486
|
+
async function submitTransaction(args) {
|
|
931
487
|
const plaintext = buildPlaintextSubmission({ backend: args.backend, tx: args.tx });
|
|
932
488
|
return submitPlaintextTransaction(
|
|
933
489
|
args.client,
|
|
@@ -935,58 +491,14 @@ async function submitTransactionWithPrivacy(args) {
|
|
|
935
491
|
plaintext.innerTxHashHex
|
|
936
492
|
);
|
|
937
493
|
}
|
|
938
|
-
function
|
|
494
|
+
function bytesEqual(a, b) {
|
|
939
495
|
if (a.length !== b.length) return false;
|
|
940
496
|
for (let i = 0; i < a.length; i++) {
|
|
941
497
|
if (a[i] !== b[i]) return false;
|
|
942
498
|
}
|
|
943
499
|
return true;
|
|
944
500
|
}
|
|
945
|
-
async function resolveClusterSealKeys(args) {
|
|
946
|
-
if (args.clusterSealKeys !== void 0) return args.clusterSealKeys;
|
|
947
|
-
if (args.clusterSealKeysSource !== void 0) {
|
|
948
|
-
return parseClusterSealKeys(args.clusterSealKeysSource);
|
|
949
|
-
}
|
|
950
|
-
if (args.client === void 0) {
|
|
951
|
-
throw new Error(ENCRYPTED_SUBMISSION_UNAVAILABLE_MESSAGE);
|
|
952
|
-
}
|
|
953
|
-
const clusterId = args.clusterId ?? 0;
|
|
954
|
-
const result = await args.client.call(
|
|
955
|
-
"lyth_getClusterSealKeys",
|
|
956
|
-
[clusterId]
|
|
957
|
-
);
|
|
958
|
-
return parseClusterSealKeys({ ...result, clusterId: result.clusterId ?? clusterId });
|
|
959
|
-
}
|
|
960
|
-
function nonceAadForTx(tx, sender, mempoolClass) {
|
|
961
|
-
return {
|
|
962
|
-
sender,
|
|
963
|
-
nonce: parseBigint(tx.nonce, "nonce"),
|
|
964
|
-
chainId: parseBigint(tx.chainId, "chainId"),
|
|
965
|
-
class: mempoolClass ?? inferMempoolClass(tx),
|
|
966
|
-
maxFeePerGas: parseBigint(tx.maxFeePerGas, "maxFeePerGas"),
|
|
967
|
-
maxPriorityFeePerGas: parseBigint(tx.maxPriorityFeePerGas, "maxPriorityFeePerGas"),
|
|
968
|
-
gasLimit: parseBigint(tx.gasLimit, "gasLimit")
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
function inferMempoolClass(tx) {
|
|
972
|
-
if (tx.to === null || hasInput(tx.input)) return MempoolClass.ContractCall;
|
|
973
|
-
return MempoolClass.Transfer;
|
|
974
|
-
}
|
|
975
|
-
function hasInput(input) {
|
|
976
|
-
if (input === void 0) return false;
|
|
977
|
-
if (typeof input === "string") {
|
|
978
|
-
const stripped = input.startsWith("0x") || input.startsWith("0X") ? input.slice(2) : input;
|
|
979
|
-
return stripped.length > 0;
|
|
980
|
-
}
|
|
981
|
-
return input.length > 0;
|
|
982
|
-
}
|
|
983
|
-
function assertRpcHash(value, label) {
|
|
984
|
-
const bytes = hexToBytes(value, label);
|
|
985
|
-
if (bytes.length !== 32) {
|
|
986
|
-
throw new Error(`${label} must be 32 bytes, got ${bytes.length}`);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
501
|
|
|
990
|
-
export { ADDRESS_DERIVATION_DOMAIN, BincodeWriter,
|
|
502
|
+
export { ADDRESS_DERIVATION_DOMAIN, BincodeWriter, ENUM_VARIANT_INDEX_ML_DSA_65, ML_DSA_65_PUBLIC_KEY_LEN, ML_DSA_65_SEED_LEN, ML_DSA_65_SIGNATURE_LEN, ML_DSA_65_SIGNING_KEY_LEN, MempoolClass, MlDsa65Backend, PQM1_ALGO_TAG_FALCON512_RESERVED, PQM1_ALGO_TAG_MLDSA65, PQM1_ALGO_TAG_MLDSA87_RESERVED, PQM1_ALGO_TAG_SLHDSA128S_RESERVED, PQM1_ENTROPY_LEN, PQM1_PAYLOAD_LEN, PQM1_V1_MLDSA65_DOMAIN_TAG, PQM1_V1_MNEMONIC_WORDS, PQM1_VERSION_V1, Pqm1Error, STANDARD_ALGO_NUMBER_ML_DSA_65, assemblePqm1Payload, bincodeSignedTransaction, buildPlaintextSubmission, bytesToHex, concatBytes, derivePqm1MlDsa65SeedFromPayload, encodeMlDsa65Opaque, encodeTransactionForHash, expectBytes, generatePqm1Mnemonic, hexToBytes, mlDsa65AddressBytes, mlDsa65AddressFromPublicKey, parsePqm1Payload, pqm1MnemonicToAddress, pqm1MnemonicToMlDsa65Backend, pqm1MnemonicToMlDsa65Seed, pqm1MnemonicToPayload, pqm1PayloadToMnemonic, submitPlaintextTransaction, submitTransaction };
|
|
991
503
|
//# sourceMappingURL=index.js.map
|
|
992
504
|
//# sourceMappingURL=index.js.map
|