feat: 重构 Lighter 策略,更新签名桥接路径,移除不必要的文件,添加新的签名库支持

This commit is contained in:
discountry
2025-10-03 01:51:55 +08:00
parent abb56b917a
commit b7cd6eed99
12 changed files with 268 additions and 1345 deletions
+2 -1
View File
@@ -17,7 +17,8 @@ class StrOrErr(ctypes.Structure):
def _resolve_signer_path() -> str:
base = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
signers_dir = os.path.join(base, "docs", "lighter", "lighter-python-main", "lighter", "signers")
# Resolve to in-repo module location: src/exchanges/lighter/signers
signers_dir = os.path.join(base, "src", "exchanges", "lighter", "signers")
system = platform.system()
machine = platform.machine().lower()
-144
View File
@@ -1,144 +0,0 @@
import { Fp } from "./goldilocks";
import {
Fp5,
FP5_ZERO,
FP5_ONE,
FP5_TWO,
FP5_W,
fp5Add,
fp5Mul,
fp5ScalarMul,
fp5Double,
fp5InverseOrZero,
} from "./goldilocks-fp5";
import { Scalar } from "./scalar";
const B1 = 263n;
const B = Fp5.fromUint64Array([0n, B1, 0n, 0n, 0n]);
const B_MUL2 = Fp5.fromUint64Array([0n, 2n * B1, 0n, 0n, 0n]);
const B_MUL4 = Fp5.fromUint64Array([0n, 4n * B1, 0n, 0n, 0n]);
const B_MUL16 = Fp5.fromUint64Array([0n, 16n * B1, 0n, 0n, 0n]);
const A = Fp5.fromUint64Array([2n, 0n, 0n, 0n, 0n]);
const FOUR_CONST = Fp5.fromUint64Array([4n, 0n, 0n, 0n, 0n]);
export class ECPoint {
constructor(
readonly x: Fp5,
readonly z: Fp5,
readonly u: Fp5,
readonly t: Fp5,
) {}
clone(): ECPoint {
return new ECPoint(this.x.clone(), this.z.clone(), this.u.clone(), this.t.clone());
}
static neutral(): ECPoint {
return new ECPoint(FP5_ZERO, FP5_ONE, FP5_ZERO, FP5_ONE);
}
static generator(): ECPoint {
return new ECPoint(
Fp5.fromUint64Array([
12883135586176881569n,
4356519642755055268n,
5248930565894896907n,
2165973894480315022n,
2448410071095648785n,
]),
FP5_ONE,
FP5_ONE,
Fp5.fromUint64Array([4n, 0n, 0n, 0n, 0n]),
);
}
isNeutral(): boolean {
return this.u.equals(FP5_ZERO);
}
encode(): Fp5 {
return fp5Mul(this.t, fp5InverseOrZero(this.u));
}
add(other: ECPoint): ECPoint {
if (this.isNeutral()) return other.clone();
if (other.isNeutral()) return this.clone();
const x1 = this.x;
const z1 = this.z;
const u1 = this.u;
const t1 = this.t;
const x2 = other.x;
const z2 = other.z;
const u2 = other.u;
const t2 = other.t;
const t1_ = fp5Mul(x1, x2);
const t2_ = fp5Mul(z1, z2);
const t3 = fp5Mul(u1, u2);
const t4 = fp5Mul(t1, t2);
const t5 = fp5SubMul(fp5Add(x1, z1), fp5Add(x2, z2), fp5Add(t1_, t2_));
const t6 = fp5SubMul(fp5Add(u1, t1), fp5Add(u2, t2), fp5Add(t3, t4));
const t7 = fp5Add(t1_, fp5Mul(t2_, B));
const t8 = fp5Mul(t4, t7);
const t9 = fp5Mul(t3, fp5Add(fp5Mul(t5, B_MUL2), fp5Double(t7)));
const t10 = fp5Mul(fp5Add(t4, fp5Double(t3)), fp5Add(t5, t7));
const xNew = fp5Mul(fp5Sub(t10, t8), B);
const zNew = fp5Sub(t8, t9);
const uNew = fp5Mul(t6, fp5Sub(fp5Mul(t2_, B), t1_));
const tNew = fp5Add(t8, t9);
return new ECPoint(xNew, zNew, uNew, tNew);
}
double(): ECPoint {
if (this.isNeutral()) return this.clone();
const x = this.x;
const z = this.z;
const u = this.u;
const t = this.t;
const t1 = fp5Mul(z, t);
const t2 = fp5Mul(t1, t);
const x1 = fp5Mul(t2, t2);
const z1 = fp5Mul(t1, u);
const t3 = fp5Mul(u, u);
const w1 = fp5Sub(t2, fp5Mul(fp5Double(fp5Add(x, z)), t3));
const t4 = fp5Mul(z1, z1);
const xNew = fp5Mul(t4, B_MUL4);
const zNew = fp5Mul(w1, w1);
const uNew = fp5Sub(fp5Mul(fp5Add(w1, z1), fp5Add(w1, z1)), fp5Add(t4, zNew));
const tNew = fp5Sub(
fp5Double(x1),
fp5Add(fp5Mul(t4, FOUR_CONST), zNew),
);
return new ECPoint(xNew, zNew, uNew, tNew);
}
mul(scalar: Scalar): ECPoint {
let result = ECPoint.neutral();
let addend = this.clone();
let k = scalar.toBigInt();
while (k > 0n) {
if (k & 1n) {
result = result.add(addend);
}
addend = addend.double();
k >>= 1n;
}
return result;
}
}
function fp5Sub(a: Fp5, b: Fp5): Fp5 {
return a.sub(b);
}
function fp5SubMul(a: Fp5, b: Fp5, subtract: Fp5): Fp5 {
return fp5Mul(a, b).sub(subtract);
}
@@ -1,460 +0,0 @@
import { Fp, GOLDILOCKS_MODULUS, addMany, powers as fpPowers } from "./goldilocks";
export type Fp5Tuple = [Fp, Fp, Fp, Fp, Fp];
function expectLength(bytes: Uint8Array, expected: number): void {
if (bytes.length !== expected) {
throw new Error(`Expected ${expected} bytes, got ${bytes.length}`);
}
}
const BYTE_LENGTH = 5 * 8;
export const FP5_W = new Fp(3n);
export const FP5_DTH_ROOT = new Fp(1041288259238279555n);
const THREE = new Fp(3n);
export class Fp5 {
readonly c0: Fp;
readonly c1: Fp;
readonly c2: Fp;
readonly c3: Fp;
readonly c4: Fp;
constructor(coeffs: Fp5Tuple) {
[this.c0, this.c1, this.c2, this.c3, this.c4] = coeffs;
}
static ZERO = new Fp5([Fp.ZERO, Fp.ZERO, Fp.ZERO, Fp.ZERO, Fp.ZERO]);
static ONE = new Fp5([Fp.ONE, Fp.ZERO, Fp.ZERO, Fp.ZERO, Fp.ZERO]);
static fromFp(value: Fp): Fp5 {
return new Fp5([value.clone(), Fp.ZERO, Fp.ZERO, Fp.ZERO, Fp.ZERO]);
}
static fromUint64Array(values: ArrayLike<bigint | number>): Fp5 {
if (values.length !== 5) {
throw new Error("Fp5.fromUint64Array expects 5 values");
}
const get = (index: number): bigint | number => {
const value = values[index];
if (value === undefined) {
throw new Error("Fp5.fromUint64Array missing value");
}
return value;
};
const arr: [bigint | number, bigint | number, bigint | number, bigint | number, bigint | number] = [
get(0),
get(1),
get(2),
get(3),
get(4),
];
return new Fp5([
new Fp(arr[0]),
new Fp(arr[1]),
new Fp(arr[2]),
new Fp(arr[3]),
new Fp(arr[4]),
]);
}
toTuple(): Fp5Tuple {
return [this.c0, this.c1, this.c2, this.c3, this.c4];
}
toBytes(): Uint8Array {
const out = new Uint8Array(BYTE_LENGTH);
out.set(this.c0.toBytesLE(), 0);
out.set(this.c1.toBytesLE(), 8);
out.set(this.c2.toBytesLE(), 16);
out.set(this.c3.toBytesLE(), 24);
out.set(this.c4.toBytesLE(), 32);
return out;
}
static fromBytes(bytes: Uint8Array): Fp5 {
expectLength(bytes, BYTE_LENGTH);
return new Fp5([
Fp.fromBytesLE(bytes.subarray(0, 8)),
Fp.fromBytesLE(bytes.subarray(8, 16)),
Fp.fromBytesLE(bytes.subarray(16, 24)),
Fp.fromBytesLE(bytes.subarray(24, 32)),
Fp.fromBytesLE(bytes.subarray(32, 40)),
]);
}
equals(other: Fp5): boolean {
return (
this.c0.equals(other.c0) &&
this.c1.equals(other.c1) &&
this.c2.equals(other.c2) &&
this.c3.equals(other.c3) &&
this.c4.equals(other.c4)
);
}
isZero(): boolean {
return this.equals(Fp5.ZERO);
}
clone(): Fp5 {
return new Fp5([
this.c0.clone(),
this.c1.clone(),
this.c2.clone(),
this.c3.clone(),
this.c4.clone(),
]);
}
add(other: Fp5): Fp5 {
return new Fp5([
this.c0.add(other.c0),
this.c1.add(other.c1),
this.c2.add(other.c2),
this.c3.add(other.c3),
this.c4.add(other.c4),
]);
}
sub(other: Fp5): Fp5 {
return new Fp5([
this.c0.sub(other.c0),
this.c1.sub(other.c1),
this.c2.sub(other.c2),
this.c3.sub(other.c3),
this.c4.sub(other.c4),
]);
}
neg(): Fp5 {
return new Fp5([
this.c0.neg(),
this.c1.neg(),
this.c2.neg(),
this.c3.neg(),
this.c4.neg(),
]);
}
mul(other: Fp5): Fp5 {
const w = FP5_W;
const a0b0 = this.c0.mul(other.c0);
const a1b4 = this.c1.mul(other.c4);
const a2b3 = this.c2.mul(other.c3);
const a3b2 = this.c3.mul(other.c2);
const a4b1 = this.c4.mul(other.c1);
const added0 = addMany([a1b4, a2b3, a3b2, a4b1]);
const muld0 = added0.mul(w);
const c0 = a0b0.add(muld0);
const a0b1 = this.c0.mul(other.c1);
const a1b0 = this.c1.mul(other.c0);
const a2b4 = this.c2.mul(other.c4);
const a3b3 = this.c3.mul(other.c3);
const a4b2 = this.c4.mul(other.c2);
const added1 = addMany([a2b4, a3b3, a4b2]);
const muld1 = added1.mul(w);
const c1 = addMany([a0b1, a1b0, muld1]);
const a0b2 = this.c0.mul(other.c2);
const a1b1 = this.c1.mul(other.c1);
const a2b0 = this.c2.mul(other.c0);
const a3b4 = this.c3.mul(other.c4);
const a4b3 = this.c4.mul(other.c3);
const added2 = addMany([a3b4, a4b3]);
const muld2 = added2.mul(w);
const c2 = addMany([a0b2, a1b1, a2b0, muld2]);
const a0b3 = this.c0.mul(other.c3);
const a1b2 = this.c1.mul(other.c2);
const a2b1 = this.c2.mul(other.c1);
const a3b0 = this.c3.mul(other.c0);
const a4b4 = this.c4.mul(other.c4);
const muld3 = a4b4.mul(w);
const c3 = addMany([a0b3, a1b2, a2b1, a3b0, muld3]);
const a0b4 = this.c0.mul(other.c4);
const a1b3 = this.c1.mul(other.c3);
const a2b2 = this.c2.mul(other.c2);
const a3b1 = this.c3.mul(other.c1);
const a4b0 = this.c4.mul(other.c0);
const c4 = addMany([a0b4, a1b3, a2b2, a3b1, a4b0]);
return new Fp5([c0, c1, c2, c3, c4]);
}
square(): Fp5 {
const w = FP5_W;
const doubleW = FP5_W.add(FP5_W);
const a0s = this.c0.mul(this.c0);
const a1a4 = this.c1.mul(this.c4);
const a2a3 = this.c2.mul(this.c3);
const added0 = addMany([a1a4, a2a3]);
const muld0 = added0.mul(doubleW);
const c0 = a0s.add(muld0);
const a0Double = this.c0.add(this.c0);
const a0Doublea1 = a0Double.mul(this.c1);
const a2a4DoubleW = this.c2.mul(this.c4).mul(doubleW);
const a3a3w = this.c3.mul(this.c3).mul(w);
const c1 = addMany([a0Doublea1, a2a4DoubleW, a3a3w]);
const a0Doublea2 = a0Double.mul(this.c2);
const a1Square = this.c1.mul(this.c1);
const a4a3DoubleW = this.c4.mul(this.c3).mul(doubleW);
const c2 = addMany([a0Doublea2, a1Square, a4a3DoubleW]);
const a1Double = this.c1.add(this.c1);
const a0Doublea3 = a0Double.mul(this.c3);
const a1Doublea2 = a1Double.mul(this.c2);
const a4SquareW = this.c4.mul(this.c4).mul(w);
const c3 = addMany([a0Doublea3, a1Doublea2, a4SquareW]);
const a0Doublea4 = a0Double.mul(this.c4);
const a1Doublea3 = a1Double.mul(this.c3);
const a2Square = this.c2.mul(this.c2);
const c4 = addMany([a0Doublea4, a1Doublea3, a2Square]);
return new Fp5([c0, c1, c2, c3, c4]);
}
expPowerOf2(power: number): Fp5 {
let result = this.clone();
for (let i = 0; i < power; i++) {
result = result.square();
}
return result;
}
double(): Fp5 {
return this.add(this);
}
triple(): Fp5 {
return new Fp5([
this.c0.mul(THREE),
this.c1.mul(THREE),
this.c2.mul(THREE),
this.c3.mul(THREE),
this.c4.mul(THREE),
]);
}
scalarMul(scalar: Fp): Fp5 {
return new Fp5([
this.c0.mul(scalar),
this.c1.mul(scalar),
this.c2.mul(scalar),
this.c3.mul(scalar),
this.c4.mul(scalar),
]);
}
inverseOrZero(): Fp5 {
if (this.isZero()) return Fp5.ZERO;
const d = this.frobenius();
const e = this.mul(d.frobenius());
const f = e.mul(e.repeatedFrobenius(2));
const a0b0 = this.c0.mul(f.c0);
const a1b4 = this.c1.mul(f.c4);
const a2b3 = this.c2.mul(f.c3);
const a3b2 = this.c3.mul(f.c2);
const a4b1 = this.c4.mul(f.c1);
const added = addMany([a1b4, a2b3, a3b2, a4b1]);
const muld = added.mul(FP5_W);
const g = a0b0.add(muld);
const gInv = g.inverse();
return f.scalarMul(gInv);
}
div(other: Fp5): Fp5 {
const inv = other.inverseOrZero();
if (inv.isZero()) {
throw new Error("Division by zero in Fp5");
}
return this.mul(inv);
}
frobenius(): Fp5 {
return this.repeatedFrobenius(1);
}
repeatedFrobenius(count: number): Fp5 {
if (count === 0) return this;
const reduced = count % 5;
if (reduced === 0) return this;
let z0 = FP5_DTH_ROOT;
for (let i = 1; i < reduced; i++) {
z0 = z0.mul(FP5_DTH_ROOT);
}
const powerArray = fpPowers(z0, 5) as [Fp, Fp, Fp, Fp, Fp];
const [p0, p1, p2, p3, p4] = powerArray;
return new Fp5([
this.c0.mul(p0),
this.c1.mul(p1),
this.c2.mul(p2),
this.c3.mul(p3),
this.c4.mul(p4),
]);
}
legendre(): Fp {
const frob1 = this.frobenius();
const frob2 = frob1.frobenius();
const frob1TimesFrob2 = frob1.mul(frob2);
const frob2Frob1TimesFrob2 = frob1TimesFrob2.repeatedFrobenius(2);
const xrExt = this.mul(frob1TimesFrob2).mul(frob2Frob1TimesFrob2);
const xr = new Fp(xrExt.c0.toBigInt());
const xr31 = xr.pow(1n << 31n);
const xr31Inv = xr31.isZero() ? Fp.ZERO : xr31.inverse();
const xr63 = xr31.pow(1n << 32n);
return xr63.mul(xr31Inv);
}
sqrt(): { value: Fp5; exists: boolean } {
const v = this.expPowerOf2(31);
const d = this.mul(v.expPowerOf2(32)).mul(v.inverseOrZero());
const e = d.mul(d.repeatedFrobenius(2)).frobenius();
const f = e.square();
const x1f4 = this.c1.mul(f.c4);
const x2f3 = this.c2.mul(f.c3);
const x3f2 = this.c3.mul(f.c2);
const x4f1 = this.c4.mul(f.c1);
const added = addMany([x1f4, x2f3, x3f2, x4f1]);
const muld = added.mul(THREE);
const x0f0 = this.c0.mul(f.c0);
const g = x0f0.add(muld);
const s = sqrtFp(g);
if (!s) {
return { value: Fp5.ZERO, exists: false };
}
const eInv = e.inverseOrZero();
const sFp5 = Fp5.fromFp(s);
return { value: sFp5.mul(eInv), exists: true };
}
canonicalSqrt(): { value: Fp5; exists: boolean } {
const { value, exists } = this.sqrt();
if (!exists) return { value: Fp5.ZERO, exists: false };
return { value: sgn0(value) ? value.neg() : value, exists: true };
}
}
function sgn0(x: Fp5): boolean {
let sign = false;
let zero = true;
for (const limb of [x.c0, x.c1, x.c2, x.c3, x.c4]) {
const limbSign = (limb.toBigInt() & 1n) === 0n;
const limbZero = limb.isZero();
sign = sign || (zero && limbSign);
zero = zero && limbZero;
}
return sign;
}
function sqrtFp(value: Fp): Fp | null {
if (value.isZero()) return Fp.ZERO;
const p = GOLDILOCKS_MODULUS;
const leg = value.pow((p - 1n) / 2n);
if (leg.isZero()) return Fp.ZERO;
const legVal = leg.toBigInt();
if (legVal === p - 1n) return null;
let q = p - 1n;
let s = 0n;
while ((q & 1n) === 0n) {
q >>= 1n;
s += 1n;
}
let z = 2n;
while (true) {
const zLeg = new Fp(z).pow((p - 1n) / 2n).toBigInt();
if (zLeg === p - 1n) break;
z += 1n;
}
let c = new Fp(z).pow(q);
let x = value.pow((q + 1n) >> 1n);
let t = value.pow(q);
let m = s;
while (t.toBigInt() !== 1n) {
let i = 1n;
let t2i = t.mul(t);
while (t2i.toBigInt() !== 1n) {
t2i = t2i.mul(t2i);
i += 1n;
if (i === m) return null;
}
const b = c.pow(1n << (m - i - 1n));
x = x.mul(b);
c = b.mul(b);
t = t.mul(c);
m = i;
}
return x;
}
export const FP5_ZERO = Fp5.ZERO;
export const FP5_ONE = Fp5.ONE;
export const FP5_TWO = Fp5.fromFp(new Fp(2n));
export function fp5Add(a: Fp5, ...others: Fp5[]): Fp5 {
let acc = a;
for (const other of others) {
acc = acc.add(other);
}
return acc;
}
export function fp5Sub(a: Fp5, b: Fp5): Fp5 {
return a.sub(b);
}
export function fp5Mul(a: Fp5, b: Fp5): Fp5 {
return a.mul(b);
}
export function fp5Square(a: Fp5): Fp5 {
return a.square();
}
export function fp5Double(a: Fp5): Fp5 {
return a.double();
}
export function fp5ScalarMul(a: Fp5, scalar: Fp): Fp5 {
return a.scalarMul(scalar);
}
export function fp5InverseOrZero(a: Fp5): Fp5 {
return a.inverseOrZero();
}
export function fp5Frobenius(a: Fp5): Fp5 {
return a.frobenius();
}
export function fp5RepeatedFrobenius(a: Fp5, count: number): Fp5 {
return a.repeatedFrobenius(count);
}
export function fp5Legendre(a: Fp5): Fp {
return a.legendre();
}
export function fp5CanonicalSqrt(a: Fp5): { value: Fp5; exists: boolean } {
return a.canonicalSqrt();
}
-179
View File
@@ -1,179 +0,0 @@
import { randomBytes } from "crypto";
const TWO_POW_32 = 1n << 32n;
const TWO_POW_64 = 1n << 64n;
export const GOLDILOCKS_MODULUS = TWO_POW_64 - TWO_POW_32 + 1n;
const BYTE_LENGTH = 8;
function mod(value: bigint): bigint {
let v = value % GOLDILOCKS_MODULUS;
if (v < 0n) v += GOLDILOCKS_MODULUS;
return v;
}
export class Fp {
readonly value: bigint;
constructor(value: bigint | number) {
this.value = mod(typeof value === "number" ? BigInt(value) : value);
}
static readonly ZERO = new Fp(0n);
static readonly ONE = new Fp(1n);
static fromBytesLE(bytes: Uint8Array): Fp {
if (bytes.length !== BYTE_LENGTH) {
throw new Error(`Goldilocks element expects 8 bytes, got ${bytes.length}`);
}
let acc = 0n;
for (let i = 0; i < BYTE_LENGTH; i++) {
const byte = bytes[i];
if (byte === undefined) throw new Error("Unexpected undefined byte when reading Goldilocks element");
acc |= BigInt(byte) << BigInt(8 * i);
}
return new Fp(acc);
}
toBytesLE(): Uint8Array {
let v = this.value;
const out = new Uint8Array(BYTE_LENGTH);
for (let i = 0; i < BYTE_LENGTH; i++) {
out[i] = Number(v & 0xffn);
v >>= 8n;
}
return out;
}
toBigInt(): bigint {
return this.value;
}
toNumber(): number {
return Number(this.value);
}
add(...others: Fp[]): Fp {
let acc = this.value;
for (const other of others) acc += other.value;
return new Fp(acc);
}
sub(other: Fp): Fp {
return new Fp(this.value - other.value);
}
neg(): Fp {
return new Fp(this.value === 0n ? 0n : GOLDILOCKS_MODULUS - this.value);
}
mul(...others: Fp[]): Fp {
let acc = this.value;
for (const other of others) acc = mod(acc * other.value);
return new Fp(acc);
}
square(): Fp {
return new Fp(mod(this.value * this.value));
}
double(): Fp {
return new Fp(this.value << 1n);
}
inverse(): Fp {
if (this.isZero()) {
throw new Error("Cannot invert zero in Goldilocks field");
}
return this.pow(GOLDILOCKS_MODULUS - 2n);
}
pow(exponent: bigint): Fp {
let result = 1n;
let base = this.value;
let exp = exponent;
while (exp > 0n) {
if (exp & 1n) result = mod(result * base);
base = mod(base * base);
exp >>= 1n;
}
return new Fp(result);
}
isZero(): boolean {
return this.value === 0n;
}
isOne(): boolean {
return this.value === 1n;
}
equals(other: Fp): boolean {
return this.value === other.value;
}
clone(): Fp {
return new Fp(this.value);
}
static random(): Fp {
while (true) {
const buf = randomBytes(BYTE_LENGTH);
let acc = 0n;
for (let i = 0; i < BYTE_LENGTH; i++) {
const byte = buf[i];
if (byte === undefined) throw new Error("Unexpected undefined byte when sampling Goldilocks element");
acc |= BigInt(byte) << BigInt(8 * i);
}
if (acc < GOLDILOCKS_MODULUS) {
return new Fp(acc);
}
}
}
static fromUint32(value: number): Fp {
return new Fp(BigInt(value >>> 0));
}
static fromUint64(value: bigint | number): Fp {
return new Fp(value);
}
}
export function addMany(elements: readonly Fp[]): Fp {
let acc = 0n;
for (const el of elements) acc += el.value;
return new Fp(acc);
}
export function arrayToBytesLE(elements: readonly Fp[]): Uint8Array {
const out = new Uint8Array(elements.length * BYTE_LENGTH);
elements.forEach((elem, idx) => {
out.set(elem.toBytesLE(), idx * BYTE_LENGTH);
});
return out;
}
export function arrayFromBytesLE(bytes: Uint8Array): Fp[] {
if (bytes.length % BYTE_LENGTH !== 0) {
throw new Error("Goldilocks array bytes length must be multiple of 8");
}
const out: Fp[] = [];
for (let i = 0; i < bytes.length; i += BYTE_LENGTH) {
out.push(Fp.fromBytesLE(bytes.subarray(i, i + BYTE_LENGTH)));
}
return out;
}
export function powers(base: Fp, count: number): Fp[] {
if (count <= 0) return [];
const result = new Array<Fp>(count);
result[0] = Fp.ONE;
for (let i = 1; i < count; i++) {
const prev = result[i - 1];
if (!prev) throw new Error("unexpected undefined in goldilocks powers");
result[i] = prev.mul(base);
}
return result;
}
export const GOLDILOCKS_BYTE_LENGTH = BYTE_LENGTH;
-320
View File
@@ -1,320 +0,0 @@
import { Fp } from "./goldilocks";
import { Fp5 } from "./goldilocks-fp5";
const WIDTH = 12;
const RATE = 8;
const ROUNDS_F = 8;
const ROUNDS_F_HALF = 4;
const ROUNDS_P = 22;
const EXTERNAL_CONSTANTS: Fp[][] = [
[
new Fp(15492826721047263190n),
new Fp(11728330187201910315n),
new Fp(8836021247773420868n),
new Fp(16777404051263952451n),
new Fp(5510875212538051896n),
new Fp(6173089941271892285n),
new Fp(2927757366422211339n),
new Fp(10340958981325008808n),
new Fp(8541987352684552425n),
new Fp(9739599543776434497n),
new Fp(15073950188101532019n),
new Fp(12084856431752384512n),
],
[
new Fp(4584713381960671270n),
new Fp(8807052963476652830n),
new Fp(54136601502601741n),
new Fp(4872702333905478703n),
new Fp(5551030319979516287n),
new Fp(12889366755535460989n),
new Fp(16329242193178844328n),
new Fp(412018088475211848n),
new Fp(10505784623379650541n),
new Fp(9758812378619434837n),
new Fp(7421979329386275117n),
new Fp(375240370024755551n),
],
[
new Fp(3331431125640721931n),
new Fp(15684937309956309981n),
new Fp(578521833432107983n),
new Fp(14379242000670861838n),
new Fp(17922409828154900976n),
new Fp(8153494278429192257n),
new Fp(15904673920630731971n),
new Fp(11217863998460634216n),
new Fp(3301540195510742136n),
new Fp(9937973023749922003n),
new Fp(3059102938155026419n),
new Fp(1895288289490976132n),
],
[
new Fp(5580912693628927540n),
new Fp(10064804080494788323n),
new Fp(9582481583369602410n),
new Fp(10186259561546797986n),
new Fp(247426333829703916n),
new Fp(13193193905461376067n),
new Fp(6386232593701758044n),
new Fp(17954717245501896472n),
new Fp(1531720443376282699n),
new Fp(2455761864255501970n),
new Fp(11234429217864304495n),
new Fp(4746959618548874102n),
],
[
new Fp(13571697342473846203n),
new Fp(17477857865056504753n),
new Fp(15963032953523553760n),
new Fp(16033593225279635898n),
new Fp(14252634232868282405n),
new Fp(8219748254835277737n),
new Fp(7459165569491914711n),
new Fp(15855939513193752003n),
new Fp(16788866461340278896n),
new Fp(7102224659693946577n),
new Fp(3024718005636976471n),
new Fp(13695468978618890430n),
],
[
new Fp(8214202050877825436n),
new Fp(2670727992739346204n),
new Fp(16259532062589659211n),
new Fp(11869922396257088411n),
new Fp(3179482916972760137n),
new Fp(13525476046633427808n),
new Fp(3217337278042947412n),
new Fp(14494689598654046340n),
new Fp(15837379330312175383n),
new Fp(8029037639801151344n),
new Fp(2153456285263517937n),
new Fp(8301106462311849241n),
],
[
new Fp(13294194396455217955n),
new Fp(17394768489610594315n),
new Fp(12847609130464867455n),
new Fp(14015739446356528640n),
new Fp(5879251655839607853n),
new Fp(9747000124977436185n),
new Fp(8950393546890284269n),
new Fp(10765765936405694368n),
new Fp(14695323910334139959n),
new Fp(16366254691123000864n),
new Fp(15292774414889043182n),
new Fp(10910394433429313384n),
],
[
new Fp(17253424460214596184n),
new Fp(3442854447664030446n),
new Fp(3005570425335613727n),
new Fp(10859158614900201063n),
new Fp(9763230642109343539n),
new Fp(6647722546511515039n),
new Fp(909012944955815706n),
new Fp(18101204076790399111n),
new Fp(11588128829349125809n),
new Fp(15863878496612806566n),
new Fp(5201119062417750399n),
new Fp(176665553780565743n),
],
];
const INTERNAL_CONSTANTS: Fp[] = [
new Fp(11921381764981422944n),
new Fp(10318423381711320787n),
new Fp(8291411502347000766n),
new Fp(229948027109387563n),
new Fp(9152521390190983261n),
new Fp(7129306032690285515n),
new Fp(15395989607365232011n),
new Fp(8641397269074305925n),
new Fp(17256848792241043600n),
new Fp(6046475228902245682n),
new Fp(12041608676381094092n),
new Fp(12785542378683951657n),
new Fp(14546032085337914034n),
new Fp(3304199118235116851n),
new Fp(16499627707072547655n),
new Fp(10386478025625759321n),
new Fp(13475579315436919170n),
new Fp(16042710511297532028n),
new Fp(1411266850385657080n),
new Fp(9024840976168649958n),
new Fp(14047056970978379368n),
new Fp(838728605080212101n),
];
const MATRIX_DIAG: Fp[] = [
new Fp(0xc3b6c08e23ba9300n),
new Fp(0xd84b5de94a324fb6n),
new Fp(0x0d0c371c5b35b84fn),
new Fp(0x7964f570e7188037n),
new Fp(0x5daf18bbd996604bn),
new Fp(0x6743bc47b9595257n),
new Fp(0x5528b9362c59bb70n),
new Fp(0xac45e25b7127b68bn),
new Fp(0xa2077d7dfbb606b5n),
new Fp(0xf3faac6faee378aen),
new Fp(0x0c6388b51545e883n),
new Fp(0xd27dbb6944917b60n),
];
export function hashToFp5(values: Fp[]): Fp5 {
const result = hashNToMNoPad(values, 5);
const [c0, c1, c2, c3, c4] = result as [Fp, Fp, Fp, Fp, Fp];
return new Fp5([c0, c1, c2, c3, c4]);
}
export function hashTwoToOne(a: Fp[], b: Fp[]): Fp[] {
return hashNToMNoPad([...a, ...b], 4);
}
export function hashNToMNoPad(input: Fp[], outputCount: number): Fp[] {
const state: Fp[] = Array.from({ length: WIDTH }, () => Fp.ZERO);
for (let offset = 0; offset < input.length; offset += RATE) {
for (let j = 0; j < RATE && offset + j < input.length; j++) {
const current = state[j]!;
const value = input[offset + j];
if (value === undefined) throw new Error("poseidon input missing element");
state[j] = current.add(value);
}
permute(state);
}
const outputs: Fp[] = [];
while (outputs.length < outputCount) {
for (let i = 0; i < RATE && outputs.length < outputCount; i++) {
outputs.push(state[i]!);
}
if (outputs.length < outputCount) {
permute(state);
}
}
return outputs;
}
function permute(state: Fp[]): void {
externalLinearLayer(state);
fullRounds(state, 0);
partialRounds(state);
fullRounds(state, ROUNDS_F_HALF);
}
function fullRounds(state: Fp[], startRound: number): void {
for (let r = startRound; r < startRound + ROUNDS_F_HALF; r++) {
addRoundConstants(state, r);
sbox(state);
externalLinearLayer(state);
}
}
function partialRounds(state: Fp[]): void {
for (let r = 0; r < ROUNDS_P; r++) {
addInternalConstant(state, r);
sboxP(state, 0);
internalLinearLayer(state);
}
}
function addRoundConstants(state: Fp[], round: number): void {
const constants = EXTERNAL_CONSTANTS[round];
if (!constants) {
throw new Error(`poseidon round constant missing for round ${round}`);
}
for (let i = 0; i < WIDTH; i++) {
state[i] = state[i]!.add(constants[i]!);
}
}
function addInternalConstant(state: Fp[], round: number): void {
const constant = INTERNAL_CONSTANTS[round];
if (!constant) {
throw new Error(`poseidon internal constant missing for round ${round}`);
}
state[0] = state[0]!.add(constant);
}
function sbox(state: Fp[]): void {
for (let i = 0; i < WIDTH; i++) {
sboxP(state, i);
}
}
function sboxP(state: Fp[], index: number): void {
const x = state[index];
if (!x) throw new Error(`poseidon state missing at index ${index}`);
const x2 = x.square();
const x3 = x2.mul(x);
const x6 = x3.square();
state[index] = x6.mul(x);
}
function externalLinearLayer(state: Fp[]): void {
for (let block = 0; block < 3; block++) {
const base = block * 4;
const s0 = state[base]!;
const s1 = state[base + 1]!;
const s2 = state[base + 2]!;
const s3 = state[base + 3]!;
const t0 = s0.add(s1);
const t1 = s2.add(s3);
const t2 = t0.add(t1);
const t3 = t2.add(s1);
const t4 = t2.add(s3);
const t5 = s0.double();
const t6 = s2.double();
state[base] = t3.add(t0);
state[base + 1] = t6.add(t3);
state[base + 2] = t1.add(t4);
state[base + 3] = t5.add(t4);
}
const sums: Fp[] = [Fp.ZERO, Fp.ZERO, Fp.ZERO, Fp.ZERO];
for (let k = 0; k < 4; k++) {
for (let j = 0; j < WIDTH; j += 4) {
const currentSum = sums[k];
if (currentSum === undefined) throw new Error("poseidon sums missing value");
sums[k] = currentSum.add(state[j + k]!);
}
}
for (let i = 0; i < WIDTH; i++) {
state[i] = state[i]!.add(sums[i % 4]!);
}
}
function internalLinearLayer(state: Fp[]): void {
let sum = state[0]!;
for (let i = 1; i < WIDTH; i++) {
sum = sum.add(state[i]!);
}
for (let i = 0; i < WIDTH; i++) {
state[i] = state[i]!.mul(MATRIX_DIAG[i]!).add(sum);
}
}
export function hashToQuinticExtension(preimage: Fp[]): Fp5 {
return hashToFp5(preimage);
}
export function hashFp5Pair(a: Fp5, b: Fp5): Fp5 {
const inputs: Fp[] = [...a.toTuple(), ...b.toTuple()];
return hashToQuinticExtension(inputs);
}
export function mergeFp5WithHash(fp5Values: Fp5[]): Fp5 {
if (fp5Values.length === 0) return Fp5.ZERO;
const first = fp5Values[0];
if (!first) throw new Error("mergeFp5WithHash received empty array");
let acc = first;
for (let i = 1; i < fp5Values.length; i++) {
const next = fp5Values[i];
if (!next) throw new Error("mergeFp5WithHash encountered undefined value");
acc = hashFp5Pair(acc, next);
}
return acc;
}
-159
View File
@@ -1,159 +0,0 @@
import { randomBytes } from "crypto";
import { Fp } from "./goldilocks";
import { Fp5 } from "./goldilocks-fp5";
const ORDER = BigInt("1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241");
const BYTE_LENGTH = 40;
const FOUR_BIT_LIMBS = 80;
const BIT_LENGTH = 319;
function modOrder(value: bigint): bigint {
let v = value % ORDER;
if (v < 0n) v += ORDER;
return v;
}
export class Scalar {
readonly value: bigint;
constructor(value: bigint | number) {
this.value = modOrder(typeof value === "number" ? BigInt(value) : value);
}
static readonly ZERO = new Scalar(0n);
static readonly ONE = new Scalar(1n);
static fromBytesLE(bytes: Uint8Array): Scalar {
if (bytes.length !== BYTE_LENGTH) {
throw new Error(`Scalar expects ${BYTE_LENGTH} bytes, got ${bytes.length}`);
}
let acc = 0n;
for (let i = 0; i < BYTE_LENGTH; i++) {
const byte = bytes[i];
if (byte === undefined) throw new Error("Unexpected undefined byte when reading scalar");
acc |= BigInt(byte) << BigInt(8 * i);
}
return new Scalar(acc);
}
toBytesLE(): Uint8Array {
let v = this.value;
const out = new Uint8Array(BYTE_LENGTH);
for (let i = 0; i < BYTE_LENGTH; i++) {
out[i] = Number(v & 0xffn);
v >>= 8n;
}
return out;
}
toBigInt(): bigint {
return this.value;
}
add(other: Scalar): Scalar {
return new Scalar(this.value + other.value);
}
sub(other: Scalar): Scalar {
return new Scalar(this.value - other.value);
}
neg(): Scalar {
return new Scalar(this.value === 0n ? 0n : ORDER - this.value);
}
mul(other: Scalar): Scalar {
return new Scalar(this.value * other.value);
}
square(): Scalar {
return this.mul(this);
}
pow(exponent: bigint): Scalar {
let result = 1n;
let base = this.value;
let exp = exponent;
while (exp > 0n) {
if (exp & 1n) result = modOrder(result * base);
base = modOrder(base * base);
exp >>= 1n;
}
return new Scalar(result);
}
isZero(): boolean {
return this.value === 0n;
}
equals(other: Scalar): boolean {
return this.value === other.value;
}
clone(): Scalar {
return new Scalar(this.value);
}
splitTo4BitLimbs(): Uint8Array {
const limbs = new Uint8Array(FOUR_BIT_LIMBS);
let tmp = this.value;
for (let i = 0; i < FOUR_BIT_LIMBS; i++) {
limbs[i] = Number(tmp & 0xfn);
tmp >>= 4n;
}
return limbs;
}
recodeSigned(window: number): Int32Array {
const length = Math.ceil(BIT_LENGTH / window);
const digits = new Int32Array(length);
const twoPowW = 1n << BigInt(window);
const twoPowWMinus1 = 1n << BigInt(window - 1);
let k = this.value;
let i = 0;
while (k > 0n && i < length) {
if (k & 1n) {
let remainder = Number(k % twoPowW);
if (remainder >= Number(twoPowWMinus1)) {
remainder -= Number(twoPowW);
}
digits[i] = remainder;
k -= BigInt(remainder);
}
k >>= 1n;
i++;
}
// remaining digits already zero
return digits;
}
static random(): Scalar {
while (true) {
const buf = randomBytes(BYTE_LENGTH);
let acc = 0n;
for (let i = 0; i < BYTE_LENGTH; i++) {
const byte = buf[i];
if (byte === undefined) throw new Error("Unexpected undefined byte when sampling scalar");
acc |= BigInt(byte) << BigInt(8 * i);
}
if (acc < ORDER) {
return new Scalar(acc);
}
}
}
static fromFp5(element: Fp5): Scalar {
const coeffs = element.toTuple();
const entries = [coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4]];
let acc = 0n;
for (let i = entries.length - 1; i >= 0; i--) {
const limb = entries[i];
if (!limb) throw new Error("Fp5 tuple missing limb");
acc <<= 64n;
acc |= limb.toBigInt();
}
return new Scalar(acc);
}
}
export const SCALAR_ORDER = ORDER;
-64
View File
@@ -1,64 +0,0 @@
import { randomBytes } from "crypto";
import { Fp } from "./goldilocks";
import { Fp5 } from "./goldilocks-fp5";
import { hashToQuinticExtension } from "./poseidon2";
import { Scalar } from "./scalar";
import { ECPoint } from "./curve";
export interface SchnorrSignature {
s: Scalar;
e: Scalar;
}
export class LighterPrivateKey {
readonly scalar: Scalar;
constructor(scalar: Scalar) {
this.scalar = scalar;
}
static fromBytes(bytes: Uint8Array): LighterPrivateKey {
if (bytes.length !== 40) {
throw new Error("Lighter private key must be 40 bytes");
}
return new LighterPrivateKey(Scalar.fromBytesLE(bytes));
}
static fromHex(hex: string): LighterPrivateKey {
const normalized = hex.startsWith("0x") ? hex.slice(2) : hex;
if (normalized.length !== 80) {
throw new Error("Lighter private key hex must encode 40 bytes");
}
const bytes = Buffer.from(normalized, "hex");
return LighterPrivateKey.fromBytes(bytes);
}
toBytes(): Uint8Array {
return this.scalar.toBytesLE();
}
publicKey(): Fp5 {
const point = ECPoint.generator().mul(this.scalar);
return point.encode();
}
signHashedMessage(hashed: Fp5): SchnorrSignature {
const k = Scalar.random();
const rPoint = ECPoint.generator().mul(k);
const rEncoded = rPoint.encode();
const preimage: Fp[] = [...rEncoded.toTuple(), ...hashed.toTuple()];
const hash = hashToQuinticExtension(preimage);
const e = Scalar.fromFp5(hash);
const s = k.sub(e.mul(this.scalar));
return { s, e };
}
}
export function signatureToBytes(sig: SchnorrSignature): Uint8Array {
const sBytes = sig.s.toBytesLE();
const eBytes = sig.e.toBytesLE();
const out = new Uint8Array(80);
out.set(sBytes, 0);
out.set(eBytes, 40);
return out;
}
-17
View File
@@ -1,17 +0,0 @@
import { Fp, GOLDILOCKS_BYTE_LENGTH } from "./crypto/goldilocks";
const CHUNK_SIZE = GOLDILOCKS_BYTE_LENGTH;
export function arrayFromCanonicalLittleEndianBytes(bytes: Uint8Array): Fp[] {
if (!bytes.length) return [];
const remainder = bytes.length % CHUNK_SIZE;
const paddedLength = remainder === 0 ? bytes.length : bytes.length + (CHUNK_SIZE - remainder);
const padded = new Uint8Array(paddedLength);
padded.set(bytes, 0);
const result: Fp[] = [];
for (let offset = 0; offset < padded.length; offset += CHUNK_SIZE) {
const chunk = padded.subarray(offset, offset + CHUNK_SIZE);
result.push(Fp.fromBytesLE(chunk));
}
return result;
}
+265
View File
@@ -0,0 +1,265 @@
#!/usr/bin/env python3
"""Lightweight bridge to the Lighter signer shared library using stdin/stdout JSON RPC."""
import ctypes
import json
import os
import platform
import subprocess
import sys
from typing import Any, Dict
class StrOrErr(ctypes.Structure):
_fields_ = [("str", ctypes.c_char_p), ("err", ctypes.c_char_p)]
def _resolve_signer_path() -> str:
base = os.path.abspath(os.path.dirname(__file__))
signers_dir = os.path.join(base, "signers")
system = platform.system()
machine = platform.machine().lower()
if system == "Darwin":
path = os.path.join(signers_dir, "signer-arm64.dylib" if machine == "arm64" else "signer-amd64.dylib")
elif system == "Linux":
path = os.path.join(signers_dir, "signer-amd64.so")
else:
raise RuntimeError(f"Unsupported platform: {system} {machine}")
if not os.path.exists(path):
raise FileNotFoundError(f"Signer library missing: {path}")
return path
def _load_library(path: str) -> ctypes.CDLL:
try:
return ctypes.CDLL(path)
except OSError as exc: # pragma: no cover - runtime environment guard
message = str(exc)
if platform.system() == "Darwin" and "code signature" in message:
subprocess.run(["/usr/bin/xattr", "-d", "com.apple.quarantine", path], check=False, capture_output=True)
subprocess.run(["/usr/bin/codesign", "--force", "--sign", "-", path], check=False, capture_output=True)
return ctypes.CDLL(path)
raise
SIGNER_PATH = _resolve_signer_path()
try:
LIB = _load_library(SIGNER_PATH)
except OSError as exc: # pragma: no cover - runtime environment guard
print(json.dumps({"id": None, "error": f"failed_to_load_signer:{exc}"}), flush=True)
sys.exit(1)
LIB.CreateClient.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_longlong]
LIB.CreateClient.restype = ctypes.c_char_p
LIB.SwitchAPIKey.argtypes = [ctypes.c_int]
LIB.SwitchAPIKey.restype = ctypes.c_char_p
LIB.SignCreateOrder.argtypes = [
ctypes.c_int,
ctypes.c_longlong,
ctypes.c_longlong,
ctypes.c_int,
ctypes.c_int,
ctypes.c_int,
ctypes.c_int,
ctypes.c_int,
ctypes.c_int,
ctypes.c_longlong,
ctypes.c_longlong,
]
LIB.SignCreateOrder.restype = StrOrErr
LIB.SignCancelOrder.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong]
LIB.SignCancelOrder.restype = StrOrErr
LIB.SignCancelAllOrders.argtypes = [ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong]
LIB.SignCancelAllOrders.restype = StrOrErr
LIB.CreateAuthToken.argtypes = [ctypes.c_longlong]
LIB.CreateAuthToken.restype = StrOrErr
def _unwrap(result: StrOrErr) -> Dict[str, Any]:
if result.err:
return {"error": ctypes.string_at(result.err).decode("utf-8", errors="replace")}
if result.str:
return {"result": ctypes.string_at(result.str).decode("utf-8", errors="replace")}
return {"result": None}
def _maybe_error(ptr: ctypes.c_char_p) -> Dict[str, Any]:
if ptr:
return {"error": ctypes.string_at(ptr).decode("utf-8", errors="replace")}
return {"result": "ok"}
_INITIALISED_KEYS = set()
_CLIENT_CONFIG: Dict[int, Dict[str, Any]] = {}
def _ensure_client(params: Dict[str, Any]) -> Dict[str, Any]:
api_key_index = int(params["apiKeyIndex"])
config = _CLIENT_CONFIG.get(api_key_index)
if "baseUrl" in params and "privateKey" in params:
config = {
"baseUrl": params["baseUrl"],
"privateKey": params["privateKey"],
"chainId": int(params["chainId"]),
"accountIndex": int(params["accountIndex"]),
}
_CLIENT_CONFIG[api_key_index] = config
if config is None:
return {"error": "client_not_initialized"}
if api_key_index in _INITIALISED_KEYS:
return {"result": "ok"}
err_ptr = LIB.CreateClient(
config["baseUrl"].encode("utf-8"),
config["privateKey"].encode("utf-8"),
ctypes.c_int(int(config["chainId"])),
ctypes.c_int(api_key_index),
ctypes.c_longlong(int(config["accountIndex"])),
)
outcome = _maybe_error(err_ptr)
if "error" in outcome:
return outcome
_INITIALISED_KEYS.add(api_key_index)
return {"result": "ok"}
def _switch_api_key(api_key_index: int) -> Dict[str, Any]:
err_ptr = LIB.SwitchAPIKey(ctypes.c_int(api_key_index))
return _maybe_error(err_ptr)
def handle_create_client(params: Dict[str, Any]) -> Dict[str, Any]:
return _ensure_client(params)
def handle_sign_create_order(params: Dict[str, Any]) -> Dict[str, Any]:
ensure = _ensure_client(params)
if "error" in ensure:
return ensure
api_key_index = int(params["apiKeyIndex"])
switched = _switch_api_key(api_key_index)
if "error" in switched:
return switched
expiry = int(params["orderExpiry"])
result = LIB.SignCreateOrder(
ctypes.c_int(int(params["marketIndex"])),
ctypes.c_longlong(int(params["clientOrderIndex"])),
ctypes.c_longlong(int(params["baseAmount"])),
ctypes.c_int(int(params["price"])),
ctypes.c_int(int(params["isAsk"])),
ctypes.c_int(int(params["orderType"])),
ctypes.c_int(int(params["timeInForce"])),
ctypes.c_int(int(params["reduceOnly"])),
ctypes.c_int(int(params["triggerPrice"])),
ctypes.c_longlong(expiry),
ctypes.c_longlong(int(params["nonce"])),
)
return _unwrap(result)
def handle_sign_cancel_order(params: Dict[str, Any]) -> Dict[str, Any]:
ensure = _ensure_client(params)
if "error" in ensure:
return ensure
api_key_index = int(params["apiKeyIndex"])
switched = _switch_api_key(api_key_index)
if "error" in switched:
return switched
result = LIB.SignCancelOrder(
ctypes.c_int(int(params["marketIndex"])),
ctypes.c_longlong(int(params["orderIndex"])),
ctypes.c_longlong(int(params["nonce"])),
)
return _unwrap(result)
def handle_sign_cancel_all(params: Dict[str, Any]) -> Dict[str, Any]:
ensure = _ensure_client(params)
if "error" in ensure:
return ensure
api_key_index = int(params["apiKeyIndex"])
switched = _switch_api_key(api_key_index)
if "error" in switched:
return switched
result = LIB.SignCancelAllOrders(
ctypes.c_int(int(params["timeInForce"])),
ctypes.c_longlong(int(params["scheduledTime"])),
ctypes.c_longlong(int(params["nonce"])),
)
return _unwrap(result)
def handle_create_auth_token(params: Dict[str, Any]) -> Dict[str, Any]:
ensure = _ensure_client(params)
if "error" in ensure:
return ensure
api_key_index = int(params["apiKeyIndex"])
switched = _switch_api_key(api_key_index)
if "error" in switched:
return switched
result = LIB.CreateAuthToken(ctypes.c_longlong(int(params["deadlineMs"])))
return _unwrap(result)
HANDLERS = {
"create_client": handle_create_client,
"sign_create_order": handle_sign_create_order,
"sign_cancel_order": handle_sign_cancel_order,
"sign_cancel_all": handle_sign_cancel_all,
"create_auth_token": handle_create_auth_token,
}
def main() -> None:
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
except json.JSONDecodeError as exc: # pragma: no cover - defensive
print(json.dumps({"id": None, "error": f"invalid_json:{exc}"}), flush=True)
continue
req_id = request.get("id")
method = request.get("method")
params = request.get("params", {})
handler = HANDLERS.get(method)
if not handler:
response = {"id": req_id, "error": f"unknown_method:{method}"}
else:
try:
outcome = handler(params)
outcome.setdefault("id", req_id)
response = outcome
except Exception as exc: # pragma: no cover - safety net
response = {"id": req_id, "error": f"exception:{exc}"}
print(json.dumps(response), flush=True)
if __name__ == "__main__":
main()
+1 -1
View File
@@ -130,7 +130,7 @@ class PythonSignerBridge {
function resolveScriptPath(): string {
const current = path.dirname(fileURLToPath(import.meta.url));
return path.resolve(current, "../../..", "scripts", "lighter_signer_bridge.py");
return path.resolve(current, "./lighter_signer_bridge.py");
}
export class LighterSigner {
Binary file not shown.
Binary file not shown.