@callcium/sdk 0.11.1 → 0.12.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/index.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { bytesToHex as bytesToHex$1, concatBytes, hexToBytes as hexToBytes$1, utf8ToBytes } from "@noble/hashes/utils.js";
1
2
  import { keccak_256 } from "@noble/hashes/sha3.js";
2
- import { utf8ToBytes } from "@noble/hashes/utils.js";
3
3
  //#region src/errors.ts
4
4
  var CallciumError = class extends Error {
5
5
  code;
@@ -36,29 +36,17 @@ function formatDiagnostic(violation) {
36
36
  //#endregion
37
37
  //#region src/bytes.ts
38
38
  const hexBodyPattern = /^[0-9a-fA-F]*$/;
39
- const HEX_DIGITS = "0123456789abcdef";
40
- const nibble = new Int8Array(128).fill(-1);
41
- for (let value = 0; value < 16; value++) {
42
- nibble[HEX_DIGITS.charCodeAt(value)] = value;
43
- nibble[HEX_DIGITS.toUpperCase().charCodeAt(value)] = value;
44
- }
45
39
  function hexToBytes(hex) {
46
40
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
47
41
  if (clean.length % 2 !== 0) throw new CallciumError("INVALID_HEX", "Odd-length hex string");
48
- const bytes = new Uint8Array(clean.length / 2);
49
- for (let i = 0; i < bytes.length; i++) {
50
- const high = nibble[clean.charCodeAt(i * 2)] ?? -1;
51
- const low = nibble[clean.charCodeAt(i * 2 + 1)] ?? -1;
52
- if (high < 0 || low < 0) throw new CallciumError("INVALID_HEX", "Invalid hex characters");
53
- bytes[i] = high << 4 | low;
42
+ try {
43
+ return hexToBytes$1(clean);
44
+ } catch {
45
+ throw new CallciumError("INVALID_HEX", "Invalid hex characters");
54
46
  }
55
- return bytes;
56
47
  }
57
- const hexPairs = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
58
48
  function bytesToHex(bytes) {
59
- let body = "";
60
- for (let i = 0; i < bytes.length; i++) body += hexPairs[bytes[i]];
61
- return `0x${body}`;
49
+ return `0x${bytesToHex$1(bytes)}`;
62
50
  }
63
51
  function toHex(data, start, end) {
64
52
  return bytesToHex(data.subarray(start, end));
@@ -335,6 +323,30 @@ const OP_TABLE = [
335
323
  label: "== ctx",
336
324
  operands: "single"
337
325
  },
326
+ {
327
+ key: "GT_CTX",
328
+ code: 9,
329
+ label: "> ctx",
330
+ operands: "single"
331
+ },
332
+ {
333
+ key: "LT_CTX",
334
+ code: 10,
335
+ label: "< ctx",
336
+ operands: "single"
337
+ },
338
+ {
339
+ key: "GTE_CTX",
340
+ code: 11,
341
+ label: ">= ctx",
342
+ operands: "single"
343
+ },
344
+ {
345
+ key: "LTE_CTX",
346
+ code: 12,
347
+ label: "<= ctx",
348
+ operands: "single"
349
+ },
338
350
  {
339
351
  key: "BITMASK_ALL",
340
352
  code: 16,
@@ -544,6 +556,18 @@ var ConstraintBuilder = class {
544
556
  neqCtx(contextPropertyId) {
545
557
  return this.push(singleOp(Op.EQ_CTX | Op.NOT, contextOperand(contextPropertyId)));
546
558
  }
559
+ gtCtx(contextPropertyId) {
560
+ return this.push(singleOp(Op.GT_CTX, contextOperand(contextPropertyId)));
561
+ }
562
+ ltCtx(contextPropertyId) {
563
+ return this.push(singleOp(Op.LT_CTX, contextOperand(contextPropertyId)));
564
+ }
565
+ gteCtx(contextPropertyId) {
566
+ return this.push(singleOp(Op.GTE_CTX, contextOperand(contextPropertyId)));
567
+ }
568
+ lteCtx(contextPropertyId) {
569
+ return this.push(singleOp(Op.LTE_CTX, contextOperand(contextPropertyId)));
570
+ }
547
571
  gt(bound) {
548
572
  return this.push(singleOp(Op.GT, bound), [bound]);
549
573
  }
@@ -656,13 +680,9 @@ function isLengthOp(opCode) {
656
680
  const base = opCode & ~Op.NOT;
657
681
  return base >= Op.LENGTH_EQ && base <= Op.LENGTH_BETWEEN;
658
682
  }
659
- function lengthToValueOp(base) {
660
- if (base === Op.LENGTH_EQ) return Op.EQ;
661
- if (base === Op.LENGTH_GT) return Op.GT;
662
- if (base === Op.LENGTH_LT) return Op.LT;
663
- if (base === Op.LENGTH_GTE) return Op.GTE;
664
- if (base === Op.LENGTH_LTE) return Op.LTE;
665
- return Op.BETWEEN;
683
+ function isContextOp(opCode) {
684
+ const base = opCode & ~Op.NOT;
685
+ return base >= Op.EQ_CTX && base <= Op.LTE_CTX;
666
686
  }
667
687
  function isLengthValidType(typeCode) {
668
688
  return typeCode === TypeCode.BYTES || typeCode === TypeCode.STRING || typeCode === TypeCode.DYNAMIC_ARRAY;
@@ -689,12 +709,7 @@ function isIn(value, data) {
689
709
  return false;
690
710
  }
691
711
  function applyOperator(opCode, value, valueLength, operandData, typeCode) {
692
- let base = opCode & ~Op.NOT;
693
- if (isLengthOp(base)) {
694
- value = valueLength;
695
- typeCode = TypeCode.UINT_MAX;
696
- base = lengthToValueOp(base);
697
- }
712
+ const base = opCode & ~Op.NOT;
698
713
  let result;
699
714
  switch (base) {
700
715
  case Op.EQ:
@@ -726,16 +741,48 @@ function applyOperator(opCode, value, valueLength, operandData, typeCode) {
726
741
  result = (value & mask) === mask;
727
742
  break;
728
743
  }
729
- case Op.BITMASK_ANY: {
730
- const mask = toBigInt(operandData, 0);
731
- result = (value & mask) !== 0n;
744
+ case Op.BITMASK_ANY:
745
+ result = (value & toBigInt(operandData, 0)) !== 0n;
732
746
  break;
733
- }
734
- case Op.BITMASK_NONE: {
735
- const mask = toBigInt(operandData, 0);
736
- result = (value & mask) === 0n;
747
+ case Op.BITMASK_NONE:
748
+ result = (value & toBigInt(operandData, 0)) === 0n;
749
+ break;
750
+ case Op.LENGTH_EQ:
751
+ result = valueLength === toBigInt(operandData, 0);
752
+ break;
753
+ case Op.LENGTH_GT:
754
+ result = valueLength > toBigInt(operandData, 0);
755
+ break;
756
+ case Op.LENGTH_LT:
757
+ result = valueLength < toBigInt(operandData, 0);
758
+ break;
759
+ case Op.LENGTH_GTE:
760
+ result = valueLength >= toBigInt(operandData, 0);
761
+ break;
762
+ case Op.LENGTH_LTE:
763
+ result = valueLength <= toBigInt(operandData, 0);
764
+ break;
765
+ case Op.LENGTH_BETWEEN: {
766
+ const lower = toBigInt(operandData, 0);
767
+ const upper = toBigInt(operandData, 32);
768
+ result = valueLength >= lower && valueLength <= upper;
737
769
  break;
738
770
  }
771
+ case Op.EQ_CTX:
772
+ result = value === toBigInt(operandData, 0);
773
+ break;
774
+ case Op.GT_CTX:
775
+ result = value > toBigInt(operandData, 0);
776
+ break;
777
+ case Op.LT_CTX:
778
+ result = value < toBigInt(operandData, 0);
779
+ break;
780
+ case Op.GTE_CTX:
781
+ result = value >= toBigInt(operandData, 0);
782
+ break;
783
+ case Op.LTE_CTX:
784
+ result = value <= toBigInt(operandData, 0);
785
+ break;
739
786
  default: throw new CallciumError("UNKNOWN_OPERATOR", `Unknown operator code ${formatCode(base, PolicyFormat.RULE_OPCODE_SIZE)}`);
740
787
  }
741
788
  return (opCode & Op.NOT) !== 0 ? !result : result;
@@ -1238,15 +1285,7 @@ function fromTypes(typesCsv) {
1238
1285
  segments.push([segStart, typesCsv.length]);
1239
1286
  const paramDescs = segments.map(([start, end]) => parseType(typesCsv, start, end));
1240
1287
  if (paramDescs.length > DescriptorFormat.MAX_PARAMS) throw new CallciumError("TOO_MANY_PARAMS", `Parameter count ${paramDescs.length} exceeds maximum ${DescriptorFormat.MAX_PARAMS}`);
1241
- const totalBytes = paramDescs.reduce((sum, d) => sum + d.length, 0);
1242
- const result = new Uint8Array(DescriptorFormat.HEADER_SIZE + totalBytes);
1243
- result[0] = DescriptorFormat.VERSION;
1244
- result[1] = paramDescs.length;
1245
- let offset = DescriptorFormat.HEADER_SIZE;
1246
- for (const desc of paramDescs) {
1247
- result.set(desc, offset);
1248
- offset += desc.length;
1249
- }
1288
+ const result = concatBytes(Uint8Array.of(DescriptorFormat.VERSION, paramDescs.length), ...paramDescs);
1250
1289
  decodeDescriptor(result);
1251
1290
  return result;
1252
1291
  }
@@ -1532,8 +1571,8 @@ function decodePolicy(blob) {
1532
1571
  prev = cur;
1533
1572
  }
1534
1573
  }
1535
- if (opBase === Op.EQ_CTX) {
1536
- if (toBigInt(data, dataStart) > BigInt(MAX_CONTEXT_PROPERTY_ID)) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", "EQ_CTX operand references an undefined context property", ruleOffset);
1574
+ if (isContextOp(opBase)) {
1575
+ if (toBigInt(data, dataStart) > BigInt(MAX_CONTEXT_PROPERTY_ID)) throw new CallciumError("UNKNOWN_CONTEXT_PROPERTY", "Context reference operand references an undefined context property", ruleOffset);
1537
1576
  }
1538
1577
  if (depthValue === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step", ruleOffset);
1539
1578
  if (depthValue > PolicyFormat.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depthValue} exceeds maximum ${PolicyFormat.MAX_PATH_DEPTH}`, ruleOffset);
@@ -1669,15 +1708,7 @@ function encodeRule(rule) {
1669
1708
  return buf;
1670
1709
  }
1671
1710
  function encodeGroupRules(rules) {
1672
- const parts = rules.map(encodeRule);
1673
- const totalSize = parts.reduce((sum, p) => sum + p.length, 0);
1674
- const buf = new Uint8Array(totalSize);
1675
- let offset = 0;
1676
- for (const part of parts) {
1677
- buf.set(part, offset);
1678
- offset += part.length;
1679
- }
1680
- return buf;
1711
+ return concatBytes(...rules.map(encodeRule));
1681
1712
  }
1682
1713
  function buildOperatorHex(rule) {
1683
1714
  return `0x${rule.opCode.value.toString(16).padStart(2, "0")}${rule.data.value.slice(2)}`;
@@ -2259,7 +2290,7 @@ function isComparisonOp(opBase) {
2259
2290
  function isBitmaskOp(opBase) {
2260
2291
  return opBase >= Op.BITMASK_ALL && opBase <= Op.BITMASK_NONE;
2261
2292
  }
2262
- function getIncompat(opBase, typeInfo) {
2293
+ function checkCompatibility(opBase, typeInfo) {
2263
2294
  const { typeCode, isDynamic, staticSize } = typeInfo;
2264
2295
  if (isValueOp(opBase)) {
2265
2296
  if (isDynamic || staticSize !== 32) return {
@@ -2282,7 +2313,7 @@ function getIncompat(opBase, typeInfo) {
2282
2313
  code: "IN_ON_BOOL",
2283
2314
  message: "IN operator used on boolean type"
2284
2315
  };
2285
- if (opBase === Op.EQ_CTX && typeCode !== TypeCode.ADDRESS && !(typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX)) return {
2316
+ if (isContextOp(opBase) && !(typeCode >= TypeCode.UINT_MIN && typeCode <= TypeCode.UINT_MAX) && !(opBase === Op.EQ_CTX && typeCode === TypeCode.ADDRESS)) return {
2286
2317
  code: "CONTEXT_TYPE_MISMATCH",
2287
2318
  message: "Context reference operator used on incompatible type"
2288
2319
  };
@@ -2301,7 +2332,8 @@ function getIncompat(opBase, typeInfo) {
2301
2332
  };
2302
2333
  }
2303
2334
  function isOpAllowed(opCode, typeInfo) {
2304
- return getIncompat(opCode & ~Op.NOT, typeInfo) === null;
2335
+ const opBase = opCode & ~Op.NOT;
2336
+ return operandsOf(opBase) !== void 0 && checkCompatibility(opBase, typeInfo) === null;
2305
2337
  }
2306
2338
  function readValue(opHex) {
2307
2339
  return BigInt(`0x${opHex.slice(4)}`);
@@ -2571,9 +2603,9 @@ function checkDuplicates(issues, operators, groupIndex, constraintIndex) {
2571
2603
  }
2572
2604
  function checkFusibleRange(ctx, operators, groupIndex, constraintIndex, issues) {
2573
2605
  const numericPair = findLonePair(operators, Op.GTE, Op.LTE);
2574
- if (numericPair && getIncompat(Op.GTE, ctx.typeInfo) === null && signedCompare(numericPair.low, numericPair.high, ctx.numeric.isSigned) <= 0n) issues.push(fusibleRange(false, groupIndex, constraintIndex, bigintToHex(numericPair.low), bigintToHex(numericPair.high)));
2606
+ if (numericPair && checkCompatibility(Op.GTE, ctx.typeInfo) === null && signedCompare(numericPair.low, numericPair.high, ctx.numeric.isSigned) <= 0n) issues.push(fusibleRange(false, groupIndex, constraintIndex, bigintToHex(numericPair.low), bigintToHex(numericPair.high)));
2575
2607
  const lengthPair = findLonePair(operators, Op.LENGTH_GTE, Op.LENGTH_LTE);
2576
- if (lengthPair && getIncompat(Op.LENGTH_GTE, ctx.typeInfo) === null && lengthPair.low <= lengthPair.high) issues.push(fusibleRange(true, groupIndex, constraintIndex, bigintToHex(lengthPair.low), bigintToHex(lengthPair.high)));
2608
+ if (lengthPair && checkCompatibility(Op.LENGTH_GTE, ctx.typeInfo) === null && lengthPair.low <= lengthPair.high) issues.push(fusibleRange(true, groupIndex, constraintIndex, bigintToHex(lengthPair.low), bigintToHex(lengthPair.high)));
2577
2609
  }
2578
2610
  function hasValidPayload(opBase, dataLength) {
2579
2611
  return dataLength <= fieldMax(PolicyFormat.RULE_DATALENGTH_SIZE) && isValidOperatorData(opBase, dataLength);
@@ -2613,12 +2645,12 @@ function validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues
2613
2645
  }
2614
2646
  const isNegated = (opCode & Op.NOT) !== 0;
2615
2647
  if (underAny && isNegated) issues.push(negationUnderAny(groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
2616
- const incompat = getIncompat(base, ctx.typeInfo);
2617
- if (incompat) {
2618
- issues.push(fromOpRule(incompat.code, incompat.message, groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
2648
+ const incompatibility = checkCompatibility(base, ctx.typeInfo);
2649
+ if (incompatibility) {
2650
+ issues.push(fromOpRule(incompatibility.code, incompatibility.message, groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
2619
2651
  continue;
2620
2652
  }
2621
- if (base === Op.EQ_CTX) {
2653
+ if (isContextOp(base)) {
2622
2654
  const ctxOperand = readValue(opHex);
2623
2655
  const property = findContextProperty(Number(ctxOperand));
2624
2656
  if (!property) issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(ctxOperand), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
@@ -3002,7 +3034,7 @@ function resolveContextOperand(operandData, context) {
3002
3034
  return contextValueToWord(value);
3003
3035
  }
3004
3036
  function referenceOperand(opCode, operandData, context) {
3005
- if ((opCode & ~Op.NOT) !== Op.EQ_CTX) return void 0;
3037
+ if (!isContextOp(opCode)) return void 0;
3006
3038
  return resolveContextOperand(operandData, context);
3007
3039
  }
3008
3040
  function readTargetBlock(hint, targetOffset) {
@@ -3071,7 +3103,7 @@ function evalTarget(callData, target, block, opCode, operandData, ctxOperand) {
3071
3103
  ctxTypeCode: ctxOperand.ctxTypeCode
3072
3104
  };
3073
3105
  return {
3074
- passed: value === ctxOperand !== ((opCode & Op.NOT) !== 0),
3106
+ passed: applyOperator(opCode, value, 32n, hexToBytes(bigintToHex(ctxOperand)), typeCode),
3075
3107
  value,
3076
3108
  ctxOperand
3077
3109
  };
@@ -3233,7 +3265,7 @@ function evaluateContextRule(pathBytes, opCode, operandData, groupIndex, ruleInd
3233
3265
  const value = contextValueToWord(contextValue);
3234
3266
  let result;
3235
3267
  let ctxOperand;
3236
- if ((opCode & ~Op.NOT) === Op.EQ_CTX) {
3268
+ if (isContextOp(opCode)) {
3237
3269
  const operand = resolveContextOperand(operandData, context);
3238
3270
  if (typeof operand !== "bigint") return {
3239
3271
  code: "MISSING_CONTEXT",
@@ -3242,7 +3274,7 @@ function evaluateContextRule(pathBytes, opCode, operandData, groupIndex, ruleInd
3242
3274
  operandData: bytesToHex(operandData),
3243
3275
  typeCode: operand.ctxTypeCode
3244
3276
  };
3245
- result = value === operand !== ((opCode & Op.NOT) !== 0);
3277
+ result = applyOperator(opCode, value, 32n, hexToBytes(bigintToHex(operand)), propertyInfo.typeCode);
3246
3278
  ctxOperand = operand;
3247
3279
  } else result = applyOperator(opCode, value, 32n, operandData, propertyInfo.typeCode);
3248
3280
  if (!result) return {
@@ -3261,6 +3293,6 @@ const PolicyEnforcer = {
3261
3293
  enforce
3262
3294
  };
3263
3295
  //#endregion
3264
- export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, MAX_CONTEXT_PROPERTY_ID, Op, PolicyBuilder, PolicyCoder, PolicyEnforcer, PolicyFormat, PolicyValidator, PolicyViolationError, Quantifier, Scope, TypeCode, ValidationError, arg, baseFee, blockNumber, blockTimestamp, bytesToHex, chainId, findContextProperty, gasPrice, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
3296
+ export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, Op, PolicyBuilder, PolicyCoder, PolicyEnforcer, PolicyFormat, PolicyValidator, PolicyViolationError, Quantifier, Scope, TypeCode, ValidationError, arg, baseFee, blockNumber, blockTimestamp, bytesToHex, chainId, findContextProperty, gasPrice, hexToBytes, isContextOp, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, txOrigin };
3265
3297
 
3266
3298
  //# sourceMappingURL=index.js.map