kawasekit 0.4.0 → 0.5.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/dist/{chunk-G3UC6SME.js → chunk-P5563RGP.js} +3 -3
- package/dist/{chunk-G3UC6SME.js.map → chunk-P5563RGP.js.map} +1 -1
- package/dist/{chunk-FD6Q4NB2.js → chunk-S2ZSX2VS.js} +136 -31
- package/dist/chunk-S2ZSX2VS.js.map +1 -0
- package/dist/{chunk-5TTOAVHE.js → chunk-VJUXTVSW.js} +11 -3
- package/dist/chunk-VJUXTVSW.js.map +1 -0
- package/dist/cli/index.cjs +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/index.cjs +140 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/signer/index.cjs +141 -27
- package/dist/signer/index.cjs.map +1 -1
- package/dist/signer/index.d.cts +57 -1
- package/dist/signer/index.d.ts +57 -1
- package/dist/signer/index.js +2 -2
- package/dist/x402/index.cjs.map +1 -1
- package/dist/x402/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-5TTOAVHE.js.map +0 -1
- package/dist/chunk-FD6Q4NB2.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -533,9 +533,17 @@ var PolicyGatedSignerConfigError = class extends Error {
|
|
|
533
533
|
}
|
|
534
534
|
};
|
|
535
535
|
var CoSignUnavailableError = class extends Error {
|
|
536
|
+
/**
|
|
537
|
+
* `true` when the failure is the **transient transport class** (connect failed /
|
|
538
|
+
* connection dropped) — the only class the adapter's bounded retry replays
|
|
539
|
+
* (RFC m6-3a §4.7: never a delivered rejection, never a ban/identifiable-abort,
|
|
540
|
+
* never a protocol anomaly or timeout). Defaults to `false`.
|
|
541
|
+
*/
|
|
542
|
+
transient;
|
|
536
543
|
constructor(message, options) {
|
|
537
|
-
super(message, options);
|
|
544
|
+
super(message, options?.cause === void 0 ? void 0 : { cause: options.cause });
|
|
538
545
|
this.name = "CoSignUnavailableError";
|
|
546
|
+
this.transient = options?.transient ?? false;
|
|
539
547
|
}
|
|
540
548
|
};
|
|
541
549
|
|
|
@@ -837,6 +845,7 @@ function createLocalPolicyGatedSigner(params) {
|
|
|
837
845
|
|
|
838
846
|
// src/signer/mpc-2p-wire.ts
|
|
839
847
|
var WIRE_VERSION = 2;
|
|
848
|
+
var MAX_FRAME_BYTES = 8 * 1024 * 1024;
|
|
840
849
|
function toWireIntent(intent) {
|
|
841
850
|
return {
|
|
842
851
|
token: intent.token.toLowerCase(),
|
|
@@ -891,6 +900,12 @@ function createMpc2pPolicyGatedSigner(params) {
|
|
|
891
900
|
const { agent, transport, authenticator, session } = params;
|
|
892
901
|
const pinned = resolveAssetParam(params.asset);
|
|
893
902
|
const from = viem.getAddress(params.from);
|
|
903
|
+
const wire = params.wire ?? {};
|
|
904
|
+
const maxAttempts = Math.max(1, wire.maxAttempts ?? 2);
|
|
905
|
+
const ceremonyTimeoutMs = wire.ceremonyTimeoutMs ?? 3e4;
|
|
906
|
+
const minWindowSecs = wire.minWindowSecs ?? 30;
|
|
907
|
+
const clockSkewBudgetSecs = wire.clockSkewBudgetSecs ?? 30;
|
|
908
|
+
const maxFrameBytes = wire.maxFrameBytes ?? MAX_FRAME_BYTES;
|
|
894
909
|
const agentEoa = viem.getAddress(agent.groupEoa());
|
|
895
910
|
if (agentEoa !== from) {
|
|
896
911
|
throw new PolicyGatedSignerConfigError(
|
|
@@ -927,23 +942,59 @@ function createMpc2pPolicyGatedSigner(params) {
|
|
|
927
942
|
nonce: intent.nonce
|
|
928
943
|
}
|
|
929
944
|
});
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
945
|
+
const validBeforeSecs = Number(intent.validBefore);
|
|
946
|
+
const nowSecs = Math.floor(Date.now() / 1e3);
|
|
947
|
+
if (validBeforeSecs - nowSecs < minWindowSecs + clockSkewBudgetSecs) {
|
|
948
|
+
throw new CoSignUnavailableError(
|
|
949
|
+
`intent.validBefore (${validBeforeSecs}) leaves under the minimum ceremony window (${minWindowSecs}s + ${clockSkewBudgetSecs}s clock-skew budget) \u2014 refusing a co-sign that risks being born expired (W11)`
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
const deadlineMs = Math.min(
|
|
953
|
+
Date.now() + ceremonyTimeoutMs,
|
|
954
|
+
(validBeforeSecs - clockSkewBudgetSecs) * 1e3
|
|
955
|
+
);
|
|
956
|
+
let lastTransient;
|
|
957
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
958
|
+
if (Date.now() >= deadlineMs) break;
|
|
959
|
+
const env = {
|
|
960
|
+
ceremonyId: globalThis.crypto.randomUUID(),
|
|
961
|
+
ssid: globalThis.crypto.randomUUID(),
|
|
962
|
+
intent,
|
|
963
|
+
freshnessTs: Math.floor(Date.now() / 1e3),
|
|
964
|
+
freshnessNonce: viem.toHex(globalThis.crypto.getRandomValues(new Uint8Array(16)))
|
|
965
|
+
};
|
|
966
|
+
const authTag = await authenticator.tag(canonicalRequestBytes(env));
|
|
942
967
|
try {
|
|
943
|
-
await
|
|
944
|
-
|
|
968
|
+
const conn = await openConnection(transport);
|
|
969
|
+
try {
|
|
970
|
+
return await runCeremony({
|
|
971
|
+
conn,
|
|
972
|
+
agent,
|
|
973
|
+
sessionId: session.id,
|
|
974
|
+
env,
|
|
975
|
+
digest,
|
|
976
|
+
authTag,
|
|
977
|
+
from,
|
|
978
|
+
deadlineMs,
|
|
979
|
+
maxFrameBytes
|
|
980
|
+
});
|
|
981
|
+
} finally {
|
|
982
|
+
try {
|
|
983
|
+
await conn.close();
|
|
984
|
+
} catch {
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
} catch (error) {
|
|
988
|
+
if (error instanceof CoSignUnavailableError && error.transient && attempt < maxAttempts) {
|
|
989
|
+
lastTransient = error;
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
throw error;
|
|
945
993
|
}
|
|
946
994
|
}
|
|
995
|
+
throw lastTransient ?? new CoSignUnavailableError(
|
|
996
|
+
"co-sign ceremony budget exhausted before validBefore (W11) \u2014 no attempt could start"
|
|
997
|
+
);
|
|
947
998
|
},
|
|
948
999
|
describe() {
|
|
949
1000
|
return {
|
|
@@ -962,23 +1013,32 @@ async function openConnection(transport) {
|
|
|
962
1013
|
try {
|
|
963
1014
|
return await transport.connect();
|
|
964
1015
|
} catch (cause) {
|
|
965
|
-
throw new CoSignUnavailableError("co-signer connection failed", { cause });
|
|
1016
|
+
throw new CoSignUnavailableError("co-signer connection failed", { cause, transient: true });
|
|
966
1017
|
}
|
|
967
1018
|
}
|
|
968
|
-
async function runCeremony(
|
|
1019
|
+
async function runCeremony(ctx) {
|
|
1020
|
+
const { conn, agent, env, digest, from } = ctx;
|
|
969
1021
|
const send = async (frame) => {
|
|
970
1022
|
try {
|
|
971
|
-
await conn.send(frame);
|
|
1023
|
+
await withDeadline(conn.send(frame), ctx.deadlineMs);
|
|
972
1024
|
} catch (cause) {
|
|
973
|
-
|
|
1025
|
+
if (cause instanceof CoSignUnavailableError) throw cause;
|
|
1026
|
+
throw new CoSignUnavailableError("co-sign connection dropped while sending", {
|
|
1027
|
+
cause,
|
|
1028
|
+
transient: true
|
|
1029
|
+
});
|
|
974
1030
|
}
|
|
975
1031
|
};
|
|
976
1032
|
const recv = async () => {
|
|
977
1033
|
let frame;
|
|
978
1034
|
try {
|
|
979
|
-
frame = await conn.recv();
|
|
1035
|
+
frame = await withDeadline(conn.recv(), ctx.deadlineMs);
|
|
980
1036
|
} catch (cause) {
|
|
981
|
-
|
|
1037
|
+
if (cause instanceof CoSignUnavailableError) throw cause;
|
|
1038
|
+
throw new CoSignUnavailableError("co-sign connection dropped mid-ceremony", {
|
|
1039
|
+
cause,
|
|
1040
|
+
transient: true
|
|
1041
|
+
});
|
|
982
1042
|
}
|
|
983
1043
|
if (frame.wire_version !== WIRE_VERSION) {
|
|
984
1044
|
throw new CoSignUnavailableError(
|
|
@@ -990,13 +1050,13 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
|
|
|
990
1050
|
await send({
|
|
991
1051
|
wire_version: WIRE_VERSION,
|
|
992
1052
|
kind: "request",
|
|
993
|
-
session_id: sessionId,
|
|
1053
|
+
session_id: ctx.sessionId,
|
|
994
1054
|
ceremony_id: env.ceremonyId,
|
|
995
1055
|
ssid: env.ssid,
|
|
996
1056
|
intent: toWireIntent(env.intent),
|
|
997
1057
|
freshness_ts: env.freshnessTs,
|
|
998
1058
|
freshness_nonce: env.freshnessNonce,
|
|
999
|
-
auth_tag: authTag
|
|
1059
|
+
auth_tag: ctx.authTag
|
|
1000
1060
|
});
|
|
1001
1061
|
let first;
|
|
1002
1062
|
try {
|
|
@@ -1010,6 +1070,12 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
|
|
|
1010
1070
|
const frame = await recv();
|
|
1011
1071
|
switch (frame.kind) {
|
|
1012
1072
|
case "round": {
|
|
1073
|
+
const payloadBytes = (frame.payload.length - 2) / 2;
|
|
1074
|
+
if (payloadBytes > ctx.maxFrameBytes) {
|
|
1075
|
+
throw new CoSignUnavailableError(
|
|
1076
|
+
`co-signer sent a ${payloadBytes}-byte round frame (bound ${ctx.maxFrameBytes}) \u2014 refused pre-decode`
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1013
1079
|
let step;
|
|
1014
1080
|
try {
|
|
1015
1081
|
step = agent.step(frame.payload);
|
|
@@ -1024,15 +1090,16 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
|
|
|
1024
1090
|
break;
|
|
1025
1091
|
}
|
|
1026
1092
|
case "result": {
|
|
1093
|
+
const signature = assembleSignature({ r: frame.r, s: frame.s, v: frame.v });
|
|
1027
1094
|
if (agentSig === null) {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
);
|
|
1095
|
+
await verifyRecovers(digest, signature, frame.s, from, "idempotent-replay");
|
|
1096
|
+
return { ok: true, signature, intent: env.intent };
|
|
1031
1097
|
}
|
|
1032
1098
|
if (frame.r.toLowerCase() !== agentSig.r.toLowerCase() || frame.s.toLowerCase() !== agentSig.s.toLowerCase() || frame.v !== agentSig.v) {
|
|
1033
1099
|
throw new CoSignUnavailableError("the backend and agent derived different signatures");
|
|
1034
1100
|
}
|
|
1035
|
-
|
|
1101
|
+
await verifyRecovers(digest, signature, frame.s, from, "co-signed");
|
|
1102
|
+
return { ok: true, signature, intent: env.intent };
|
|
1036
1103
|
}
|
|
1037
1104
|
case "rejection": {
|
|
1038
1105
|
if (POLICY_REASONS.has(frame.reason)) {
|
|
@@ -1051,6 +1118,52 @@ async function runCeremony(conn, agent, sessionId, env, digest, authTag) {
|
|
|
1051
1118
|
}
|
|
1052
1119
|
}
|
|
1053
1120
|
}
|
|
1121
|
+
function withDeadline(promise, deadlineMs) {
|
|
1122
|
+
const remaining = deadlineMs - Date.now();
|
|
1123
|
+
if (remaining <= 0) {
|
|
1124
|
+
return Promise.reject(
|
|
1125
|
+
new CoSignUnavailableError(
|
|
1126
|
+
"co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
|
|
1127
|
+
)
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
return new Promise((resolve, reject) => {
|
|
1131
|
+
const timer = setTimeout(() => {
|
|
1132
|
+
reject(
|
|
1133
|
+
new CoSignUnavailableError(
|
|
1134
|
+
"co-sign ceremony timed out (the deadline fires before validBefore \u2014 W11)"
|
|
1135
|
+
)
|
|
1136
|
+
);
|
|
1137
|
+
}, remaining);
|
|
1138
|
+
promise.then(
|
|
1139
|
+
(value) => {
|
|
1140
|
+
clearTimeout(timer);
|
|
1141
|
+
resolve(value);
|
|
1142
|
+
},
|
|
1143
|
+
(cause) => {
|
|
1144
|
+
clearTimeout(timer);
|
|
1145
|
+
reject(cause);
|
|
1146
|
+
}
|
|
1147
|
+
);
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
var SECP256K1_HALF_N = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n;
|
|
1151
|
+
async function verifyRecovers(digest, signature, s, from, what) {
|
|
1152
|
+
if (BigInt(s) > SECP256K1_HALF_N) {
|
|
1153
|
+
throw new CoSignUnavailableError(`the ${what} signature is not low-S (EIP-2)`);
|
|
1154
|
+
}
|
|
1155
|
+
let recovered;
|
|
1156
|
+
try {
|
|
1157
|
+
recovered = await viem.recoverAddress({ hash: digest, signature });
|
|
1158
|
+
} catch (cause) {
|
|
1159
|
+
throw new CoSignUnavailableError(`the ${what} signature failed to recover`, { cause });
|
|
1160
|
+
}
|
|
1161
|
+
if (viem.getAddress(recovered) !== from) {
|
|
1162
|
+
throw new CoSignUnavailableError(
|
|
1163
|
+
`the ${what} signature recovers to ${viem.getAddress(recovered)}, not the group EOA ${from}`
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1054
1167
|
function assembleSignature(sig) {
|
|
1055
1168
|
const yParity = sig.v - 27;
|
|
1056
1169
|
if (yParity !== 0 && yParity !== 1) {
|