Skip to content

Commit

Permalink
test: add spec with attestation, remove inline tests, create test-utils
Browse files Browse the repository at this point in the history
  • Loading branch information
martonmoro committed Sep 30, 2024
1 parent becd019 commit dbf3e6e
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 124 deletions.
56 changes: 0 additions & 56 deletions src/program-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,62 +357,6 @@ function and(left: Node<Bool>, right: Node<Bool>): Node<Bool> {
return { type: 'and', left, right };
}

// TODO remove
// small inline test

const isMain = import.meta.filename === process.argv[1];
if (isMain) {
const Bytes32 = Bytes(32);
const InputData = { age: Field, name: Bytes32 };

const spec = Spec(
{
signedData: Attestation.signature(InputData),
targetAge: Input.public(Field),
targetName: Input.constant(Bytes32, Bytes32.fromString('Alice')),
},
({ signedData, targetAge, targetName }) => ({
assert: Operation.and(
Operation.equals(Operation.property(signedData, 'age'), targetAge),
Operation.equals(Operation.property(signedData, 'name'), targetName)
),
data: Operation.property(signedData, 'age'),
})
);
console.log(spec.logic);

// create user inputs
let data = { age: Field(18), name: Bytes32.fromString('Alice') };
let signedData = createAttestation(InputData, data);

let userInputs: UserInputs<typeof spec.inputs> = {
signedData,
targetAge: Field(18),
};

// evaluate the logic at input
let { privateInput, publicInput } = splitUserInputs(spec, userInputs);
let root = recombineDataInputs(spec, publicInput, privateInput);
let assert = Node.eval(root, spec.logic.assert);
let output = Node.eval(root, spec.logic.data);
Provable.log({ publicInput, privateInput, root, assert, output });

// public inputs, extracted at the type level
type specPublicInputs = PublicInputs<typeof spec.inputs>;

// private inputs, extracted at the type level
type specPrivateInputs = PrivateInputs<typeof spec.inputs>;

function createAttestation<Data>(type: NestedProvableFor<Data>, data: Data) {
let issuer = PrivateKey.randomKeypair();
let signature = Signature.create(
issuer.privateKey,
NestedProvable.get(type).toFields(data)
);
return { public: issuer.publicKey, private: signature, data };
}
}

function publicInputTypes<S extends Spec>({
inputs,
}: S): Record<string, NestedProvablePure> {
Expand Down
54 changes: 0 additions & 54 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,60 +74,6 @@ function createProgram<S extends Spec>(
};
}

// inline test

const isMain = import.meta.filename === process.argv[1];
if (isMain) {
let { Bytes } = await import('o1js');

const Bytes32 = Bytes(32);
const InputData = { age: Field, name: Bytes32 };

// TODO always include owner pk and verify signature on it
const spec = Spec(
{
signedData: Attestation.signature(InputData),
targetAge: Input.public(Field),
targetName: Input.constant(Bytes32, Bytes32.fromString('Alice')),
},
({ signedData, targetAge, targetName }) => ({
assert: Operation.and(
Operation.equals(Operation.property(signedData, 'age'), targetAge),
Operation.equals(Operation.property(signedData, 'name'), targetName)
),
data: Operation.property(signedData, 'age'),
})
);

function createAttestation<Data>(type: NestedProvableFor<Data>, data: Data) {
let issuer = PrivateKey.randomKeypair();
let signature = Signature.create(
issuer.privateKey,
NestedProvable.get(type).toFields(data)
);
return { public: issuer.publicKey, private: signature, data };
}

let data = { age: Field(18), name: Bytes32.fromString('Alice') };
let signedData = createAttestation(InputData, data);

let program = createProgram(spec);
await program.compile();

// input types are inferred from spec
// TODO leverage `From<>` type to pass in inputs directly as numbers / strings etc
let proof = await program.run({ signedData, targetAge: Field(18) });

Provable.log({
publicInput: proof.publicInput,
publicOutput: proof.publicOutput,
});

// proof types are inferred from spec
proof.publicInput satisfies { signedData: PublicKey; targetAge: Field };
proof.publicOutput satisfies Field;
}

// helper

type GetSpecData<S extends Spec> = S extends Spec<infer Data, any>
Expand Down
51 changes: 47 additions & 4 deletions tests/program-config.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { test } from 'node:test';
import assert from 'node:assert';
import { Bytes, Field } from 'o1js';
import { Spec, Input, Operation, Node } from '../src/program-config.ts';
import {
Spec,
Input,
Operation,
Node,
Attestation,
type UserInputs,
splitUserInputs,
recombineDataInputs,
} from '../src/program-config.ts';
import { createAttestation } from './test-utils.ts';

test(' Spec and Node operations', async (t) => {
const Bytes32 = Bytes(32);
Expand All @@ -24,9 +34,6 @@ test(' Spec and Node operations', async (t) => {
targetAge: Field(25),
};

console.log('spec.logic.assert:', spec.logic.assert);
console.log('spec.logic.data:', spec.logic.data);

const assertResult = Node.eval(root, spec.logic.assert);
const dataResult = Node.eval(root, spec.logic.data);

Expand Down Expand Up @@ -126,4 +133,40 @@ test(' Spec and Node operations', async (t) => {
assert.strictEqual(assertResult.toString(), 'true');
assert.deepStrictEqual(dataResult, Bytes32.fromString('Charlie'));
});

await t.test('Spec with attestation', () => {
const InputData = { age: Field, name: Bytes32 };
const spec = Spec(
{
signedData: Attestation.signature(InputData),
targetAge: Input.public(Field),
targetName: Input.public(Bytes32),
},
({ signedData, targetAge, targetName }) => ({
assert: Operation.and(
Operation.equals(Operation.property(signedData, 'age'), targetAge),
Operation.equals(Operation.property(signedData, 'name'), targetName)
),
data: Operation.property(signedData, 'age'),
})
);

const data = { age: Field(30), name: Bytes32.fromString('David') };
const signedData = createAttestation(InputData, data);

let userInputs: UserInputs<typeof spec.inputs> = {
signedData,
targetAge: Field(30),
targetName: Bytes32.fromString('David'),
};

let { privateInput, publicInput } = splitUserInputs(spec, userInputs);
let root = recombineDataInputs(spec, publicInput, privateInput);

const assertResult = Node.eval(root, spec.logic.assert);
const dataResult = Node.eval(root, spec.logic.data);

assert.strictEqual(assertResult.toString(), 'true');
assert.deepStrictEqual(dataResult, Field(30));
});
});
11 changes: 1 addition & 10 deletions tests/program.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import assert from 'node:assert';
import { Field, Bytes, PrivateKey, Signature } from 'o1js';
import { createProgram } from '../src/program.ts';
import { Attestation, Input, Operation, Spec } from '../src/program-config.ts';
import { NestedProvable } from '../src/nested.ts';
import { createAttestation } from './test-utils.ts';

test('createProgram with simple spec', async (t) => {
const Bytes32 = Bytes(32);
Expand Down Expand Up @@ -95,12 +95,3 @@ test('createProgram with simple spec', async (t) => {
);
});
});

function createAttestation<Data>(type: NestedProvable, data: Data) {
const issuer = PrivateKey.random();
const signature = Signature.create(
issuer,
NestedProvable.get(type).toFields(data)
);
return { public: issuer.toPublicKey(), private: signature, data };
}
13 changes: 13 additions & 0 deletions tests/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { PrivateKey, Signature } from 'o1js';
import { NestedProvable } from '../src/nested.ts';

export { createAttestation };

function createAttestation<Data>(type: NestedProvable, data: Data) {
let issuer = PrivateKey.randomKeypair();
let signature = Signature.create(
issuer.privateKey,
NestedProvable.get(type).toFields(data)
);
return { public: issuer.publicKey, private: signature, data };
}

0 comments on commit dbf3e6e

Please sign in to comment.