@ocap/tx-pipeline 1.30.3 → 1.30.5

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.
@@ -0,0 +1,10 @@
1
+ //#region src/helpers/encode-tx-by-context.d.ts
2
+ /**
3
+ * Pick the encoder matching `context.txEncoding`. Centralizes the ternary
4
+ * that lived in three verify pipes so future encoding additions touch one
5
+ * place. Chain-side only — client bundles that want tree-shake keep their
6
+ * local selection.
7
+ */
8
+ declare function encodeTxByEncoding(encoding: string | undefined, tx: Record<string, unknown>): Uint8Array;
9
+ //#endregion
10
+ export { encodeTxByEncoding };
@@ -0,0 +1,16 @@
1
+ import { encodeTx } from "@ocap/message/cbor";
2
+ import { encodeTx as encodeTx$1 } from "@ocap/message/protobuf";
3
+
4
+ //#region src/helpers/encode-tx-by-context.ts
5
+ /**
6
+ * Pick the encoder matching `context.txEncoding`. Centralizes the ternary
7
+ * that lived in three verify pipes so future encoding additions touch one
8
+ * place. Chain-side only — client bundles that want tree-shake keep their
9
+ * local selection.
10
+ */
11
+ function encodeTxByEncoding(encoding, tx) {
12
+ return encoding === "cbor" ? encodeTx(tx) : encodeTx$1(tx);
13
+ }
14
+
15
+ //#endregion
16
+ export { encodeTxByEncoding };
@@ -0,0 +1,17 @@
1
+ //#region src/helpers/unwrap-opaque-any.d.ts
2
+ interface DecodedAny {
3
+ type?: string;
4
+ value?: unknown;
5
+ }
6
+ /**
7
+ * Undo the duplicate base64+JSON serialization that `decodeAny` leaves on
8
+ * `json` / `vc` typed Any payloads. Mutates `decoded.value` in place when
9
+ * applicable and is a no-op otherwise (including on malformed JSON — we
10
+ * swallow the parse error to keep legacy behavior).
11
+ *
12
+ * HACK carried forward from pre-Phase-43 multisig verifiers; kept in one
13
+ * place so the two verify pipes share the exact same fallback.
14
+ */
15
+ declare function unwrapOpaqueAnyData(decoded: DecodedAny | undefined): DecodedAny | undefined;
16
+ //#endregion
17
+ export { unwrapOpaqueAnyData };
@@ -0,0 +1,19 @@
1
+ //#region src/helpers/unwrap-opaque-any.ts
2
+ /**
3
+ * Undo the duplicate base64+JSON serialization that `decodeAny` leaves on
4
+ * `json` / `vc` typed Any payloads. Mutates `decoded.value` in place when
5
+ * applicable and is a no-op otherwise (including on malformed JSON — we
6
+ * swallow the parse error to keep legacy behavior).
7
+ *
8
+ * HACK carried forward from pre-Phase-43 multisig verifiers; kept in one
9
+ * place so the two verify pipes share the exact same fallback.
10
+ */
11
+ function unwrapOpaqueAnyData(decoded) {
12
+ if (decoded?.type && ["json", "vc"].includes(decoded.type)) try {
13
+ decoded.value = JSON.parse(Buffer.from(decoded.value, "base64").toString());
14
+ } catch (_e) {}
15
+ return decoded;
16
+ }
17
+
18
+ //#endregion
19
+ export { unwrapOpaqueAnyData };
@@ -1,17 +1,64 @@
1
1
  import { CustomError } from "@ocap/util/lib/error";
2
- import { decodeAny } from "@ocap/message";
2
+ import { createMessage, decodeAny, fromTypeUrl, toTypeUrl } from "@ocap/message";
3
3
 
4
4
  //#region src/pipes/decode-itx.ts
5
+ /**
6
+ * Normalize `tx.itx` to a `{ typeUrl, value }` pair across the three shapes
7
+ * that reach this pipe:
8
+ *
9
+ * protobuf wire → jspb Any.toObject() → `{ typeUrl, value: <base64-bytes> }`
10
+ * cbor wire → canonical-cbor flat Any → `{ typeUrl, ...inner }`
11
+ * client raw → pre-encoding object → `{ type, value: { ...inner } }`
12
+ *
13
+ * Downstream protocol schemas and pipes expect the jspb-shaped inner object
14
+ * (repeated fields carry the `xxxList` alias, bytes fields are base64 strings,
15
+ * BigUint wrappers are `{ value: Uint8Array }`). We round-trip the cbor /
16
+ * client-raw cases through `createMessage().toObject()` so downstream code
17
+ * sees a single, stable shape regardless of wire format.
18
+ */
19
+ function toJspbObject(typeUrl, inner) {
20
+ const msg = createMessage(fromTypeUrl(typeUrl), inner);
21
+ return msg?.toObject ? msg.toObject() : inner;
22
+ }
23
+ function normalizeItx(raw) {
24
+ if (!raw || typeof raw !== "object") return null;
25
+ if (raw.typeUrl) {
26
+ if (typeof raw.value === "string") {
27
+ const decoded = decodeAny(raw);
28
+ return {
29
+ typeUrl: raw.typeUrl,
30
+ value: decoded.value
31
+ };
32
+ }
33
+ const inner = {};
34
+ for (const [key, val] of Object.entries(raw)) {
35
+ if (key === "typeUrl" || key === "type_url") continue;
36
+ inner[key] = val;
37
+ }
38
+ return {
39
+ typeUrl: raw.typeUrl,
40
+ value: toJspbObject(raw.typeUrl, inner)
41
+ };
42
+ }
43
+ if (raw.type) {
44
+ const typeUrl = toTypeUrl(raw.type);
45
+ return {
46
+ typeUrl,
47
+ value: toJspbObject(typeUrl, raw.value || {})
48
+ };
49
+ }
50
+ return null;
51
+ }
5
52
  function DecodeItx(context, next) {
6
53
  try {
7
- const decoded = decodeAny(context.tx.itx);
8
- const txType = context.tx.itx.typeUrl;
54
+ const normalized = normalizeItx(context.tx.itx);
55
+ if (!normalized) return next(new CustomError("INVALID_TX", "Failed to decode itx"));
9
56
  Object.defineProperty(context, "itx", {
10
- value: decoded.value,
57
+ value: normalized.value,
11
58
  enumerable: true
12
59
  });
13
60
  Object.defineProperty(context, "txType", {
14
- value: txType,
61
+ value: normalized.typeUrl,
15
62
  enumerable: true
16
63
  });
17
64
  context.logger?.info("Tx decoded", {
@@ -19,7 +66,7 @@ function DecodeItx(context, next) {
19
66
  txHash: context.txHash,
20
67
  txTime: context.txTime,
21
68
  txSize: context.txSize,
22
- txType,
69
+ txType: normalized.typeUrl,
23
70
  request: context.extra?.request
24
71
  });
25
72
  } catch (_err) {
@@ -1,18 +1,34 @@
1
1
  import { name } from "../package.mjs";
2
2
  import { fromBase64, toUint8Array } from "@ocap/util";
3
3
  import { CustomError } from "@ocap/util/lib/error";
4
- import { getMessageType } from "@ocap/message";
5
4
  import { toTxHash } from "@ocap/mcrypto";
5
+ import { CANONICAL_SELF_DESCRIBE_PREFIX, decodeTx } from "@ocap/message/cbor";
6
+ import { decodeTx as decodeTx$1 } from "@ocap/message/protobuf";
6
7
  import Debug from "debug";
7
8
 
8
9
  //#region src/pipes/decode-tx.ts
9
10
  const debug = Debug(`${name}:pipe:decode-tx`);
10
- const Transaction = getMessageType("Transaction").fn;
11
+ /**
12
+ * Detect the CBOR self-describe tag 55799 that our canonical encoder always
13
+ * prepends to a Transaction. The first three bytes are unambiguously distinct
14
+ * from any protobuf encoding of Transaction because `D9` = `(27 << 3) | 1`,
15
+ * and `Transaction` has no top-level field number ≥ 27 (enforced by
16
+ * `scripts/assert-proto-field-bound.ts`).
17
+ */
18
+ function hasCborSelfDescribeTag(buf) {
19
+ return buf.length >= CANONICAL_SELF_DESCRIBE_PREFIX.length && buf[0] === CANONICAL_SELF_DESCRIBE_PREFIX[0] && buf[1] === CANONICAL_SELF_DESCRIBE_PREFIX[1] && buf[2] === CANONICAL_SELF_DESCRIBE_PREFIX[2];
20
+ }
11
21
  function DecodeTx(context, next) {
12
22
  try {
13
23
  const txBuffer = toUint8Array(fromBase64(context.txBase64));
24
+ const isCbor = hasCborSelfDescribeTag(txBuffer);
25
+ const tx = isCbor ? decodeTx(txBuffer) : decodeTx$1(txBuffer);
14
26
  Object.defineProperty(context, "tx", {
15
- value: Transaction.deserializeBinary(txBuffer).toObject(),
27
+ value: tx,
28
+ enumerable: true
29
+ });
30
+ Object.defineProperty(context, "txEncoding", {
31
+ value: isCbor ? "cbor" : "protobuf",
16
32
  enumerable: true
17
33
  });
18
34
  Object.defineProperty(context, "txSize", {
@@ -1,7 +1,9 @@
1
+ import { encodeTxByEncoding } from "../helpers/encode-tx-by-context.mjs";
2
+ import { unwrapOpaqueAnyData } from "../helpers/unwrap-opaque-any.mjs";
1
3
  import { CustomError } from "@ocap/util/lib/error";
2
4
  import get from "lodash/get.js";
3
- import { createMessage, decodeAny } from "@ocap/message";
4
- import { getListField } from "@ocap/util/lib/get-list-field";
5
+ import { decodeAny } from "@ocap/message";
6
+ import { getListField, setListField } from "@ocap/util/lib/get-list-field";
5
7
  import { toTypeInfo } from "@arcblock/did";
6
8
  import cloneDeep from "lodash/cloneDeep.js";
7
9
  import { fromPublicKey } from "@ocap/wallet";
@@ -17,22 +19,17 @@ function CreateVerifyMultiSigV2Pipe({ signersKey }) {
17
19
  if (signatures.length !== signers.length) return next(new CustomError("INVALID_SIGNATURE", `Expect ${signers.length} signatures but found ${signatures.length}`));
18
20
  for (const signer of signers) if (!signatures.find((x) => [x.delegator, x.signer].includes(signer))) return next(new CustomError("INVALID_SIGNATURE", `Signature for ${signer} not found in tx.signatures`));
19
21
  signatures.forEach((sig) => {
20
- const decoded = sig.data ? decodeAny(sig.data) : void 0;
21
- if (decoded?.type && ["json", "vc"].includes(decoded.type)) try {
22
- decoded.value = JSON.parse(Buffer.from(decoded.value, "base64").toString());
23
- } catch (_e) {}
24
- sig.data = decoded;
22
+ sig.data = unwrapOpaqueAnyData(sig.data ? decodeAny(sig.data) : void 0);
25
23
  });
26
24
  tx.signature = void 0;
27
- tx.signaturesList = signatures.map((x) => omit(x, "signature"));
25
+ setListField(tx, "signatures", signatures.map((x) => omit(x, "signature")));
28
26
  for (let i = 0; i < signatures.length; i++) {
29
27
  const { signer, pk, delegator, signature, extra = "" } = signatures[i];
30
28
  const address = delegator || signer;
31
29
  const wallet = fromPublicKey(pk, toTypeInfo(address));
32
- const message = createMessage("Transaction", tx);
33
- if (!message) return next(new CustomError("INVALID_SIGNATURE", `Failed to create message for ${address}`));
30
+ const txBytes = encodeTxByEncoding(context.txEncoding, tx);
34
31
  try {
35
- if (await wallet.verify(message.serializeBinary(), signature, true, extra) === false) return next(new CustomError("INVALID_SIGNATURE", `Signature for ${address} is not valid`));
32
+ if (await wallet.verify(txBytes, signature, true, extra) === false) return next(new CustomError("INVALID_SIGNATURE", `Signature for ${address} is not valid`));
36
33
  } catch (err) {
37
34
  console.error(err);
38
35
  return next(new CustomError("INVALID_SIGNATURE", `Signature for ${address} verify failed: ${err.message}`));
@@ -1,6 +1,8 @@
1
+ import { encodeTxByEncoding } from "../helpers/encode-tx-by-context.mjs";
2
+ import { unwrapOpaqueAnyData } from "../helpers/unwrap-opaque-any.mjs";
1
3
  import { CustomError } from "@ocap/util/lib/error";
2
- import { createMessage, decodeAny } from "@ocap/message";
3
- import { getListField } from "@ocap/util/lib/get-list-field";
4
+ import { decodeAny } from "@ocap/message";
5
+ import { deleteListField, getListField, setListField } from "@ocap/util/lib/get-list-field";
4
6
  import { toTypeInfo } from "@arcblock/did";
5
7
  import cloneDeep from "lodash/cloneDeep.js";
6
8
  import { fromPublicKey } from "@ocap/wallet";
@@ -12,24 +14,19 @@ function CreateVerifyMultiSigPipe(numSigs = 0) {
12
14
  const signatures = getListField(tx, "signatures");
13
15
  if (signatures.length !== numSigs) return next(new CustomError("INVALID_SIGNATURE", `Expect ${numSigs} multi signatures but found ${signatures.length}`));
14
16
  if (numSigs === 0) return next();
15
- delete tx.signatures;
16
- delete tx.signaturesList;
17
+ deleteListField(tx, "signatures");
17
18
  while (signatures.length > 0) {
18
19
  const { signer, pk, delegator, signature, data } = signatures.shift();
19
20
  const address = delegator || signer;
20
21
  const wallet = fromPublicKey(pk, toTypeInfo(address));
21
- const decoded = data ? decodeAny(data) : void 0;
22
- if (decoded?.type && ["json", "vc"].includes(decoded.type)) try {
23
- decoded.value = JSON.parse(Buffer.from(decoded.value, "base64").toString());
24
- } catch (_e) {}
25
- tx.signatures = [{
22
+ setListField(tx, "signatures", [{
26
23
  signer,
27
24
  pk,
28
25
  delegator,
29
- data: decoded
30
- }].concat(signatures);
31
- const message = createMessage("Transaction", tx);
32
- if (!message || await wallet.verify(message.serializeBinary(), signature) === false) return next(new CustomError("INVALID_SIGNATURE", `Multi signature for ${address} is not valid`));
26
+ data: unwrapOpaqueAnyData(data ? decodeAny(data) : void 0)
27
+ }].concat(signatures));
28
+ const txBytes = encodeTxByEncoding(context.txEncoding, tx);
29
+ if (await wallet.verify(txBytes, signature) === false) return next(new CustomError("INVALID_SIGNATURE", `Multi signature for ${address} is not valid`));
33
30
  }
34
31
  next();
35
32
  };
@@ -1,7 +1,8 @@
1
+ import { encodeTxByEncoding } from "../helpers/encode-tx-by-context.mjs";
1
2
  import { CustomError } from "@ocap/util/lib/error";
2
3
  import get from "lodash/get.js";
3
- import { createMessage } from "@ocap/message";
4
4
  import { types } from "@ocap/mcrypto";
5
+ import { deleteListField } from "@ocap/util/lib/get-list-field";
5
6
  import { isFromPublicKey, toTypeInfo } from "@arcblock/did";
6
7
  import cloneDeep from "lodash/cloneDeep.js";
7
8
  import { fromPublicKey } from "@ocap/wallet";
@@ -13,20 +14,16 @@ async function VerifySignature(context, next) {
13
14
  tx.signature = void 0;
14
15
  const multiSignV2Txs = get(context, "config.transaction.multiSignV2Txs", []);
15
16
  const txType = context.txType;
16
- if (!txType || multiSignV2Txs.includes(txType) === false) {
17
- delete tx.signatures;
18
- delete tx.signaturesList;
19
- }
17
+ if (!txType || multiSignV2Txs.includes(txType) === false) deleteListField(tx, "signatures");
20
18
  const { pk, delegator, from } = tx;
21
19
  const signer = delegator || from;
22
20
  if (isFromPublicKey(signer, pk) === false) return next(new CustomError("INVALID_SIGNATURE", "signer and pk does not match"));
23
- const message = createMessage("Transaction", tx);
24
- if (!message) return next(new CustomError("INVALID_SIGNATURE", "Failed to create transaction message"));
21
+ const txBytes = encodeTxByEncoding(context.txEncoding, tx);
25
22
  const type = toTypeInfo(signer);
26
23
  const wallet = fromPublicKey(pk, type);
27
24
  const extra = type.role === types.RoleType.ROLE_PASSKEY ? get(context, "extra.txExtra") : "";
28
25
  try {
29
- if (!signature || await wallet.verify(message.serializeBinary(), signature, true, extra || "") === false) return next(new CustomError("INVALID_SIGNATURE", "tx.signature is invalid"));
26
+ if (!signature || await wallet.verify(txBytes, signature, true, extra || "") === false) return next(new CustomError("INVALID_SIGNATURE", "tx.signature is invalid"));
30
27
  } catch (err) {
31
28
  console.error("tx.signature verify failed", err);
32
29
  return next(new CustomError("INVALID_SIGNATURE", "tx.signature verify failed"));
@@ -1,11 +1,13 @@
1
1
  import { CustomError } from "@ocap/util/lib/error";
2
2
  import get from "lodash/get.js";
3
+ import { toTypeUrl } from "@ocap/message";
3
4
 
4
5
  //#region src/pipes/verify-tx-size.ts
5
6
  function VerifyTxSize(context, next) {
6
7
  const { txSize, txType, tx, config } = context;
7
8
  const defaultSizeLimit = get(config, "transaction.maxTxSize.default");
8
- const sizeLimit = get(config, `transaction.maxTxSize.${tx.itx.typeUrl}`, defaultSizeLimit);
9
+ const rawItx = tx.itx;
10
+ const sizeLimit = get(config, `transaction.maxTxSize.${txType || rawItx?.typeUrl || (rawItx?.type ? toTypeUrl(rawItx.type) : void 0)}`, defaultSizeLimit);
9
11
  if (txSize > sizeLimit) return next(new CustomError("INVALID_TX_SIZE", `Transaction size too large for ${txType}: expect ${sizeLimit}, got ${txSize}`));
10
12
  next();
11
13
  }
@@ -1,5 +1,6 @@
1
1
  import { CustomError } from "@ocap/util/lib/error";
2
2
  import get from "lodash/get.js";
3
+ import { toTypeUrl } from "@ocap/message";
3
4
  import { getListField } from "@ocap/util/lib/get-list-field";
4
5
  import { isFromPublicKey } from "@arcblock/did";
5
6
  import isEmpty from "lodash/isEmpty.js";
@@ -19,7 +20,9 @@ function VerifyTx(context, next) {
19
20
  const n = Number(nonce);
20
21
  if (Number.isNaN(n) || n < 0) return next(new CustomError("INVALID_NONCE", "tx.nonce should be a positive integer"));
21
22
  const { supportedTxs, maxMultisig } = get(context, "config.transaction");
22
- if (supportedTxs.includes(itx.typeUrl) === false) return next(new CustomError("UNSUPPORTED_TX", `Transaction ${itx.typeUrl} is not supported`));
23
+ const rawItx = itx;
24
+ const itxTypeUrl = rawItx.typeUrl || (rawItx.type ? toTypeUrl(rawItx.type) : void 0);
25
+ if (!itxTypeUrl || supportedTxs.includes(itxTypeUrl) === false) return next(new CustomError("UNSUPPORTED_TX", `Transaction ${itxTypeUrl} is not supported`));
23
26
  if (getListField(context, "tx.signatures").length > maxMultisig) return next(new CustomError("INVALID_TX", "Too many multisig signatures"));
24
27
  if (isFromPublicKey(delegator || from, pk) === false) return next(new CustomError("INVALID_TX", "Sender or delegator address does not match pk"));
25
28
  next();
@@ -0,0 +1,17 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let _ocap_message_cbor = require("@ocap/message/cbor");
3
+ let _ocap_message_protobuf = require("@ocap/message/protobuf");
4
+
5
+ //#region src/helpers/encode-tx-by-context.ts
6
+ /**
7
+ * Pick the encoder matching `context.txEncoding`. Centralizes the ternary
8
+ * that lived in three verify pipes so future encoding additions touch one
9
+ * place. Chain-side only — client bundles that want tree-shake keep their
10
+ * local selection.
11
+ */
12
+ function encodeTxByEncoding(encoding, tx) {
13
+ return encoding === "cbor" ? (0, _ocap_message_cbor.encodeTx)(tx) : (0, _ocap_message_protobuf.encodeTx)(tx);
14
+ }
15
+
16
+ //#endregion
17
+ exports.encodeTxByEncoding = encodeTxByEncoding;
@@ -0,0 +1,10 @@
1
+ //#region src/helpers/encode-tx-by-context.d.ts
2
+ /**
3
+ * Pick the encoder matching `context.txEncoding`. Centralizes the ternary
4
+ * that lived in three verify pipes so future encoding additions touch one
5
+ * place. Chain-side only — client bundles that want tree-shake keep their
6
+ * local selection.
7
+ */
8
+ declare function encodeTxByEncoding(encoding: string | undefined, tx: Record<string, unknown>): Uint8Array;
9
+ //#endregion
10
+ export { encodeTxByEncoding };
@@ -0,0 +1,20 @@
1
+
2
+ //#region src/helpers/unwrap-opaque-any.ts
3
+ /**
4
+ * Undo the duplicate base64+JSON serialization that `decodeAny` leaves on
5
+ * `json` / `vc` typed Any payloads. Mutates `decoded.value` in place when
6
+ * applicable and is a no-op otherwise (including on malformed JSON — we
7
+ * swallow the parse error to keep legacy behavior).
8
+ *
9
+ * HACK carried forward from pre-Phase-43 multisig verifiers; kept in one
10
+ * place so the two verify pipes share the exact same fallback.
11
+ */
12
+ function unwrapOpaqueAnyData(decoded) {
13
+ if (decoded?.type && ["json", "vc"].includes(decoded.type)) try {
14
+ decoded.value = JSON.parse(Buffer.from(decoded.value, "base64").toString());
15
+ } catch (_e) {}
16
+ return decoded;
17
+ }
18
+
19
+ //#endregion
20
+ exports.unwrapOpaqueAnyData = unwrapOpaqueAnyData;
@@ -0,0 +1,17 @@
1
+ //#region src/helpers/unwrap-opaque-any.d.ts
2
+ interface DecodedAny {
3
+ type?: string;
4
+ value?: unknown;
5
+ }
6
+ /**
7
+ * Undo the duplicate base64+JSON serialization that `decodeAny` leaves on
8
+ * `json` / `vc` typed Any payloads. Mutates `decoded.value` in place when
9
+ * applicable and is a no-op otherwise (including on malformed JSON — we
10
+ * swallow the parse error to keep legacy behavior).
11
+ *
12
+ * HACK carried forward from pre-Phase-43 multisig verifiers; kept in one
13
+ * place so the two verify pipes share the exact same fallback.
14
+ */
15
+ declare function unwrapOpaqueAnyData(decoded: DecodedAny | undefined): DecodedAny | undefined;
16
+ //#endregion
17
+ export { unwrapOpaqueAnyData };
@@ -4,16 +4,63 @@ let _ocap_util_lib_error = require("@ocap/util/lib/error");
4
4
  let _ocap_message = require("@ocap/message");
5
5
 
6
6
  //#region src/pipes/decode-itx.ts
7
+ /**
8
+ * Normalize `tx.itx` to a `{ typeUrl, value }` pair across the three shapes
9
+ * that reach this pipe:
10
+ *
11
+ * protobuf wire → jspb Any.toObject() → `{ typeUrl, value: <base64-bytes> }`
12
+ * cbor wire → canonical-cbor flat Any → `{ typeUrl, ...inner }`
13
+ * client raw → pre-encoding object → `{ type, value: { ...inner } }`
14
+ *
15
+ * Downstream protocol schemas and pipes expect the jspb-shaped inner object
16
+ * (repeated fields carry the `xxxList` alias, bytes fields are base64 strings,
17
+ * BigUint wrappers are `{ value: Uint8Array }`). We round-trip the cbor /
18
+ * client-raw cases through `createMessage().toObject()` so downstream code
19
+ * sees a single, stable shape regardless of wire format.
20
+ */
21
+ function toJspbObject(typeUrl, inner) {
22
+ const msg = (0, _ocap_message.createMessage)((0, _ocap_message.fromTypeUrl)(typeUrl), inner);
23
+ return msg?.toObject ? msg.toObject() : inner;
24
+ }
25
+ function normalizeItx(raw) {
26
+ if (!raw || typeof raw !== "object") return null;
27
+ if (raw.typeUrl) {
28
+ if (typeof raw.value === "string") {
29
+ const decoded = (0, _ocap_message.decodeAny)(raw);
30
+ return {
31
+ typeUrl: raw.typeUrl,
32
+ value: decoded.value
33
+ };
34
+ }
35
+ const inner = {};
36
+ for (const [key, val] of Object.entries(raw)) {
37
+ if (key === "typeUrl" || key === "type_url") continue;
38
+ inner[key] = val;
39
+ }
40
+ return {
41
+ typeUrl: raw.typeUrl,
42
+ value: toJspbObject(raw.typeUrl, inner)
43
+ };
44
+ }
45
+ if (raw.type) {
46
+ const typeUrl = (0, _ocap_message.toTypeUrl)(raw.type);
47
+ return {
48
+ typeUrl,
49
+ value: toJspbObject(typeUrl, raw.value || {})
50
+ };
51
+ }
52
+ return null;
53
+ }
7
54
  function DecodeItx(context, next) {
8
55
  try {
9
- const decoded = (0, _ocap_message.decodeAny)(context.tx.itx);
10
- const txType = context.tx.itx.typeUrl;
56
+ const normalized = normalizeItx(context.tx.itx);
57
+ if (!normalized) return next(new _ocap_util_lib_error.CustomError("INVALID_TX", "Failed to decode itx"));
11
58
  Object.defineProperty(context, "itx", {
12
- value: decoded.value,
59
+ value: normalized.value,
13
60
  enumerable: true
14
61
  });
15
62
  Object.defineProperty(context, "txType", {
16
- value: txType,
63
+ value: normalized.typeUrl,
17
64
  enumerable: true
18
65
  });
19
66
  context.logger?.info("Tx decoded", {
@@ -21,7 +68,7 @@ function DecodeItx(context, next) {
21
68
  txHash: context.txHash,
22
69
  txTime: context.txTime,
23
70
  txSize: context.txSize,
24
- txType,
71
+ txType: normalized.typeUrl,
25
72
  request: context.extra?.request
26
73
  });
27
74
  } catch (_err) {
@@ -3,19 +3,35 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
3
  const require_package = require('../package.cjs');
4
4
  let _ocap_util = require("@ocap/util");
5
5
  let _ocap_util_lib_error = require("@ocap/util/lib/error");
6
- let _ocap_message = require("@ocap/message");
7
6
  let _ocap_mcrypto = require("@ocap/mcrypto");
7
+ let _ocap_message_cbor = require("@ocap/message/cbor");
8
+ let _ocap_message_protobuf = require("@ocap/message/protobuf");
8
9
  let debug = require("debug");
9
10
  debug = require_rolldown_runtime.__toESM(debug);
10
11
 
11
12
  //#region src/pipes/decode-tx.ts
12
13
  const debug$1 = (0, debug.default)(`${require_package.name}:pipe:decode-tx`);
13
- const Transaction = (0, _ocap_message.getMessageType)("Transaction").fn;
14
+ /**
15
+ * Detect the CBOR self-describe tag 55799 that our canonical encoder always
16
+ * prepends to a Transaction. The first three bytes are unambiguously distinct
17
+ * from any protobuf encoding of Transaction because `D9` = `(27 << 3) | 1`,
18
+ * and `Transaction` has no top-level field number ≥ 27 (enforced by
19
+ * `scripts/assert-proto-field-bound.ts`).
20
+ */
21
+ function hasCborSelfDescribeTag(buf) {
22
+ return buf.length >= _ocap_message_cbor.CANONICAL_SELF_DESCRIBE_PREFIX.length && buf[0] === _ocap_message_cbor.CANONICAL_SELF_DESCRIBE_PREFIX[0] && buf[1] === _ocap_message_cbor.CANONICAL_SELF_DESCRIBE_PREFIX[1] && buf[2] === _ocap_message_cbor.CANONICAL_SELF_DESCRIBE_PREFIX[2];
23
+ }
14
24
  function DecodeTx(context, next) {
15
25
  try {
16
26
  const txBuffer = (0, _ocap_util.toUint8Array)((0, _ocap_util.fromBase64)(context.txBase64));
27
+ const isCbor = hasCborSelfDescribeTag(txBuffer);
28
+ const tx = isCbor ? (0, _ocap_message_cbor.decodeTx)(txBuffer) : (0, _ocap_message_protobuf.decodeTx)(txBuffer);
17
29
  Object.defineProperty(context, "tx", {
18
- value: Transaction.deserializeBinary(txBuffer).toObject(),
30
+ value: tx,
31
+ enumerable: true
32
+ });
33
+ Object.defineProperty(context, "txEncoding", {
34
+ value: isCbor ? "cbor" : "protobuf",
19
35
  enumerable: true
20
36
  });
21
37
  Object.defineProperty(context, "txSize", {
@@ -1,5 +1,7 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
  const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_helpers_encode_tx_by_context = require('../helpers/encode-tx-by-context.cjs');
4
+ const require_helpers_unwrap_opaque_any = require('../helpers/unwrap-opaque-any.cjs');
3
5
  let _ocap_util_lib_error = require("@ocap/util/lib/error");
4
6
  let lodash_get = require("lodash/get");
5
7
  lodash_get = require_rolldown_runtime.__toESM(lodash_get);
@@ -22,22 +24,17 @@ function CreateVerifyMultiSigV2Pipe({ signersKey }) {
22
24
  if (signatures.length !== signers.length) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Expect ${signers.length} signatures but found ${signatures.length}`));
23
25
  for (const signer of signers) if (!signatures.find((x) => [x.delegator, x.signer].includes(signer))) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Signature for ${signer} not found in tx.signatures`));
24
26
  signatures.forEach((sig) => {
25
- const decoded = sig.data ? (0, _ocap_message.decodeAny)(sig.data) : void 0;
26
- if (decoded?.type && ["json", "vc"].includes(decoded.type)) try {
27
- decoded.value = JSON.parse(Buffer.from(decoded.value, "base64").toString());
28
- } catch (_e) {}
29
- sig.data = decoded;
27
+ sig.data = require_helpers_unwrap_opaque_any.unwrapOpaqueAnyData(sig.data ? (0, _ocap_message.decodeAny)(sig.data) : void 0);
30
28
  });
31
29
  tx.signature = void 0;
32
- tx.signaturesList = signatures.map((x) => (0, lodash_omit.default)(x, "signature"));
30
+ (0, _ocap_util_lib_get_list_field.setListField)(tx, "signatures", signatures.map((x) => (0, lodash_omit.default)(x, "signature")));
33
31
  for (let i = 0; i < signatures.length; i++) {
34
32
  const { signer, pk, delegator, signature, extra = "" } = signatures[i];
35
33
  const address = delegator || signer;
36
34
  const wallet = (0, _ocap_wallet.fromPublicKey)(pk, (0, _arcblock_did.toTypeInfo)(address));
37
- const message = (0, _ocap_message.createMessage)("Transaction", tx);
38
- if (!message) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Failed to create message for ${address}`));
35
+ const txBytes = require_helpers_encode_tx_by_context.encodeTxByEncoding(context.txEncoding, tx);
39
36
  try {
40
- if (await wallet.verify(message.serializeBinary(), signature, true, extra) === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Signature for ${address} is not valid`));
37
+ if (await wallet.verify(txBytes, signature, true, extra) === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Signature for ${address} is not valid`));
41
38
  } catch (err) {
42
39
  console.error(err);
43
40
  return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Signature for ${address} verify failed: ${err.message}`));
@@ -1,5 +1,7 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
  const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_helpers_encode_tx_by_context = require('../helpers/encode-tx-by-context.cjs');
4
+ const require_helpers_unwrap_opaque_any = require('../helpers/unwrap-opaque-any.cjs');
3
5
  let _ocap_util_lib_error = require("@ocap/util/lib/error");
4
6
  let _ocap_message = require("@ocap/message");
5
7
  let _ocap_util_lib_get_list_field = require("@ocap/util/lib/get-list-field");
@@ -15,24 +17,19 @@ function CreateVerifyMultiSigPipe(numSigs = 0) {
15
17
  const signatures = (0, _ocap_util_lib_get_list_field.getListField)(tx, "signatures");
16
18
  if (signatures.length !== numSigs) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Expect ${numSigs} multi signatures but found ${signatures.length}`));
17
19
  if (numSigs === 0) return next();
18
- delete tx.signatures;
19
- delete tx.signaturesList;
20
+ (0, _ocap_util_lib_get_list_field.deleteListField)(tx, "signatures");
20
21
  while (signatures.length > 0) {
21
22
  const { signer, pk, delegator, signature, data } = signatures.shift();
22
23
  const address = delegator || signer;
23
24
  const wallet = (0, _ocap_wallet.fromPublicKey)(pk, (0, _arcblock_did.toTypeInfo)(address));
24
- const decoded = data ? (0, _ocap_message.decodeAny)(data) : void 0;
25
- if (decoded?.type && ["json", "vc"].includes(decoded.type)) try {
26
- decoded.value = JSON.parse(Buffer.from(decoded.value, "base64").toString());
27
- } catch (_e) {}
28
- tx.signatures = [{
25
+ (0, _ocap_util_lib_get_list_field.setListField)(tx, "signatures", [{
29
26
  signer,
30
27
  pk,
31
28
  delegator,
32
- data: decoded
33
- }].concat(signatures);
34
- const message = (0, _ocap_message.createMessage)("Transaction", tx);
35
- if (!message || await wallet.verify(message.serializeBinary(), signature) === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Multi signature for ${address} is not valid`));
29
+ data: require_helpers_unwrap_opaque_any.unwrapOpaqueAnyData(data ? (0, _ocap_message.decodeAny)(data) : void 0)
30
+ }].concat(signatures));
31
+ const txBytes = require_helpers_encode_tx_by_context.encodeTxByEncoding(context.txEncoding, tx);
32
+ if (await wallet.verify(txBytes, signature) === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", `Multi signature for ${address} is not valid`));
36
33
  }
37
34
  next();
38
35
  };
@@ -1,10 +1,11 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
  const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_helpers_encode_tx_by_context = require('../helpers/encode-tx-by-context.cjs');
3
4
  let _ocap_util_lib_error = require("@ocap/util/lib/error");
4
5
  let lodash_get = require("lodash/get");
5
6
  lodash_get = require_rolldown_runtime.__toESM(lodash_get);
6
- let _ocap_message = require("@ocap/message");
7
7
  let _ocap_mcrypto = require("@ocap/mcrypto");
8
+ let _ocap_util_lib_get_list_field = require("@ocap/util/lib/get-list-field");
8
9
  let _arcblock_did = require("@arcblock/did");
9
10
  let lodash_cloneDeep = require("lodash/cloneDeep");
10
11
  lodash_cloneDeep = require_rolldown_runtime.__toESM(lodash_cloneDeep);
@@ -17,20 +18,16 @@ async function VerifySignature(context, next) {
17
18
  tx.signature = void 0;
18
19
  const multiSignV2Txs = (0, lodash_get.default)(context, "config.transaction.multiSignV2Txs", []);
19
20
  const txType = context.txType;
20
- if (!txType || multiSignV2Txs.includes(txType) === false) {
21
- delete tx.signatures;
22
- delete tx.signaturesList;
23
- }
21
+ if (!txType || multiSignV2Txs.includes(txType) === false) (0, _ocap_util_lib_get_list_field.deleteListField)(tx, "signatures");
24
22
  const { pk, delegator, from } = tx;
25
23
  const signer = delegator || from;
26
24
  if ((0, _arcblock_did.isFromPublicKey)(signer, pk) === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", "signer and pk does not match"));
27
- const message = (0, _ocap_message.createMessage)("Transaction", tx);
28
- if (!message) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", "Failed to create transaction message"));
25
+ const txBytes = require_helpers_encode_tx_by_context.encodeTxByEncoding(context.txEncoding, tx);
29
26
  const type = (0, _arcblock_did.toTypeInfo)(signer);
30
27
  const wallet = (0, _ocap_wallet.fromPublicKey)(pk, type);
31
28
  const extra = type.role === _ocap_mcrypto.types.RoleType.ROLE_PASSKEY ? (0, lodash_get.default)(context, "extra.txExtra") : "";
32
29
  try {
33
- if (!signature || await wallet.verify(message.serializeBinary(), signature, true, extra || "") === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", "tx.signature is invalid"));
30
+ if (!signature || await wallet.verify(txBytes, signature, true, extra || "") === false) return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", "tx.signature is invalid"));
34
31
  } catch (err) {
35
32
  console.error("tx.signature verify failed", err);
36
33
  return next(new _ocap_util_lib_error.CustomError("INVALID_SIGNATURE", "tx.signature verify failed"));
@@ -3,12 +3,14 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
3
  let _ocap_util_lib_error = require("@ocap/util/lib/error");
4
4
  let lodash_get = require("lodash/get");
5
5
  lodash_get = require_rolldown_runtime.__toESM(lodash_get);
6
+ let _ocap_message = require("@ocap/message");
6
7
 
7
8
  //#region src/pipes/verify-tx-size.ts
8
9
  function VerifyTxSize(context, next) {
9
10
  const { txSize, txType, tx, config } = context;
10
11
  const defaultSizeLimit = (0, lodash_get.default)(config, "transaction.maxTxSize.default");
11
- const sizeLimit = (0, lodash_get.default)(config, `transaction.maxTxSize.${tx.itx.typeUrl}`, defaultSizeLimit);
12
+ const rawItx = tx.itx;
13
+ const sizeLimit = (0, lodash_get.default)(config, `transaction.maxTxSize.${txType || rawItx?.typeUrl || (rawItx?.type ? (0, _ocap_message.toTypeUrl)(rawItx.type) : void 0)}`, defaultSizeLimit);
12
14
  if (txSize > sizeLimit) return next(new _ocap_util_lib_error.CustomError("INVALID_TX_SIZE", `Transaction size too large for ${txType}: expect ${sizeLimit}, got ${txSize}`));
13
15
  next();
14
16
  }
@@ -3,6 +3,7 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
3
  let _ocap_util_lib_error = require("@ocap/util/lib/error");
4
4
  let lodash_get = require("lodash/get");
5
5
  lodash_get = require_rolldown_runtime.__toESM(lodash_get);
6
+ let _ocap_message = require("@ocap/message");
6
7
  let _ocap_util_lib_get_list_field = require("@ocap/util/lib/get-list-field");
7
8
  let _arcblock_did = require("@arcblock/did");
8
9
  let lodash_isEmpty = require("lodash/isEmpty");
@@ -23,7 +24,9 @@ function VerifyTx(context, next) {
23
24
  const n = Number(nonce);
24
25
  if (Number.isNaN(n) || n < 0) return next(new _ocap_util_lib_error.CustomError("INVALID_NONCE", "tx.nonce should be a positive integer"));
25
26
  const { supportedTxs, maxMultisig } = (0, lodash_get.default)(context, "config.transaction");
26
- if (supportedTxs.includes(itx.typeUrl) === false) return next(new _ocap_util_lib_error.CustomError("UNSUPPORTED_TX", `Transaction ${itx.typeUrl} is not supported`));
27
+ const rawItx = itx;
28
+ const itxTypeUrl = rawItx.typeUrl || (rawItx.type ? (0, _ocap_message.toTypeUrl)(rawItx.type) : void 0);
29
+ if (!itxTypeUrl || supportedTxs.includes(itxTypeUrl) === false) return next(new _ocap_util_lib_error.CustomError("UNSUPPORTED_TX", `Transaction ${itxTypeUrl} is not supported`));
27
30
  if ((0, _ocap_util_lib_get_list_field.getListField)(context, "tx.signatures").length > maxMultisig) return next(new _ocap_util_lib_error.CustomError("INVALID_TX", "Too many multisig signatures"));
28
31
  if ((0, _arcblock_did.isFromPublicKey)(delegator || from, pk) === false) return next(new _ocap_util_lib_error.CustomError("INVALID_TX", "Sender or delegator address does not match pk"));
29
32
  next();
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.30.3",
6
+ "version": "1.30.5",
7
7
  "description": "Pipeline runner and common pipelines to process transactions",
8
8
  "type": "module",
9
9
  "main": "./lib/index.cjs",
@@ -49,15 +49,15 @@
49
49
  "elliptic": "6.5.3"
50
50
  },
51
51
  "dependencies": {
52
- "@arcblock/did": "1.30.3",
53
- "@arcblock/did-util": "1.30.3",
54
- "@ocap/client": "1.30.3",
55
- "@ocap/mcrypto": "1.30.3",
56
- "@ocap/message": "1.30.3",
57
- "@ocap/state": "1.30.3",
58
- "@ocap/types": "1.30.3",
59
- "@ocap/util": "1.30.3",
60
- "@ocap/wallet": "1.30.3",
52
+ "@arcblock/did": "1.30.5",
53
+ "@arcblock/did-util": "1.30.5",
54
+ "@ocap/client": "1.30.5",
55
+ "@ocap/mcrypto": "1.30.5",
56
+ "@ocap/message": "1.30.5",
57
+ "@ocap/state": "1.30.5",
58
+ "@ocap/types": "1.30.5",
59
+ "@ocap/util": "1.30.5",
60
+ "@ocap/wallet": "1.30.5",
61
61
  "debug": "^4.4.3",
62
62
  "lodash": "^4.17.23"
63
63
  }