Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ECDSA gadget #1240

Merged
merged 114 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 85 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
6c34c9f
double
mitschabaude Nov 15, 2023
d41f30f
tweak cs printing
mitschabaude Nov 15, 2023
4c3f6a5
initial aggregator for scaling
mitschabaude Nov 15, 2023
c99cee2
another helper method on Field3
mitschabaude Nov 15, 2023
95017b3
add constant ecdsa implementation and helper types
mitschabaude Nov 15, 2023
dd2ebd3
bindings
mitschabaude Nov 15, 2023
fc6f1f8
helper
mitschabaude Nov 15, 2023
00a11f7
stub out provable ecdsa, skipping the hard part
mitschabaude Nov 15, 2023
ce389ef
revert explosion of helper methods in favor of general interface
mitschabaude Nov 15, 2023
e458907
add constant cases and a bit more ecdsa logic
mitschabaude Nov 15, 2023
8db8d06
bindings
mitschabaude Nov 15, 2023
ab9584f
implement ecdsa
mitschabaude Nov 16, 2023
c39e0f8
signature from hex
mitschabaude Nov 16, 2023
b924cad
add ecdsa test script
mitschabaude Nov 16, 2023
9a2629f
move initial ec testing code
mitschabaude Nov 16, 2023
a1370e8
fix bit slicing logic
mitschabaude Nov 16, 2023
92318fe
bindings
mitschabaude Nov 16, 2023
92a627e
several fixes to the scaling algorithm, debugging
mitschabaude Nov 16, 2023
cc4f9b1
fix uneven window sizes by glueing together overlapping chunk between…
mitschabaude Nov 16, 2023
90d341f
remove debug logs
mitschabaude Nov 16, 2023
e7b8d23
fixup
mitschabaude Nov 16, 2023
f7c5176
fix
mitschabaude Nov 17, 2023
a4f5638
Merge branch 'feature/ffmul' into feature/ecdsa-new
mitschabaude Nov 17, 2023
968a5ae
adapt to ffmul changes
mitschabaude Nov 17, 2023
1129739
Merge branch 'feature/ffmul' into feature/ecdsa-new
mitschabaude Nov 17, 2023
9b29f40
bound y (needed for doubling)
mitschabaude Nov 17, 2023
38b9b7f
tweak initial aggregator
mitschabaude Nov 17, 2023
69a814d
bindings
mitschabaude Nov 17, 2023
be15bf1
remove unused function
mitschabaude Nov 17, 2023
b025198
Merge branch 'feature/ffmul' into feature/ecdsa-new
mitschabaude Nov 17, 2023
c0f297d
generalize doubleScalarMul to multiScalarMul to deduplicate logic
mitschabaude Nov 20, 2023
241aae1
save >3k constraints by optimizing array get gadget
mitschabaude Nov 20, 2023
f4801d0
clean up table logic
mitschabaude Nov 20, 2023
80dbef6
optimal window size changed
mitschabaude Nov 20, 2023
73d2b2d
replace mul input rcs with generic gates
mitschabaude Nov 20, 2023
21050b6
enable strict typing of variable fields
mitschabaude Nov 21, 2023
89e0cdd
optimized assertOneOf gadget
mitschabaude Nov 21, 2023
d0a3159
simplify low-level gadgets
mitschabaude Nov 21, 2023
64610e7
make new assertRank1 more efficient and enable it
mitschabaude Nov 21, 2023
eacc1e6
expose assertMul and Sum
mitschabaude Nov 21, 2023
003f29d
adapt elliptic curve gadget
mitschabaude Nov 21, 2023
347524a
add assertion to prevent invalid multiplication
mitschabaude Nov 21, 2023
24a0bf6
document assertMul
mitschabaude Nov 21, 2023
3969ccd
Merge branch 'feature/assert-mul' into feature/ecdsa-new
mitschabaude Nov 22, 2023
569e1c6
some API and comment tweaks
mitschabaude Nov 22, 2023
a4b1137
code moving
mitschabaude Nov 22, 2023
ea17473
bindings
mitschabaude Nov 22, 2023
2898db3
adapt to bindings
mitschabaude Nov 22, 2023
9fd2353
fields on curve
mitschabaude Nov 22, 2023
3e9ee6b
ecdsa sign
mitschabaude Nov 22, 2023
c501bad
adapt
mitschabaude Nov 22, 2023
321f963
bindings
mitschabaude Nov 22, 2023
c705ed5
fix test compilation
mitschabaude Nov 22, 2023
9d48098
map rng pf spec
mitschabaude Nov 22, 2023
5780bfe
wip quick ecdsa tests
mitschabaude Nov 22, 2023
4fee529
finish unit test
mitschabaude Nov 23, 2023
b35bf20
clean up constant ecdsa
mitschabaude Nov 23, 2023
3369ae3
comment
mitschabaude Nov 23, 2023
cf27df5
api cleanup
mitschabaude Nov 23, 2023
bf6d2e9
comments and make sure r,s are valid
mitschabaude Nov 23, 2023
4bb7b1a
fix
mitschabaude Nov 23, 2023
415f8ae
sketch public API
mitschabaude Nov 23, 2023
a9e75e8
bindings
mitschabaude Nov 23, 2023
b451ebe
add helper to apply all ff range checks
mitschabaude Nov 24, 2023
d891330
add more documentation
mitschabaude Nov 24, 2023
697ddb3
use range check helper in ec gadgets
mitschabaude Nov 24, 2023
86da890
add tests
mitschabaude Nov 24, 2023
5e4e0a4
Merge branch 'main' into feature/ecdsa-new
mitschabaude Nov 27, 2023
206440d
expose ecdsa types
mitschabaude Nov 27, 2023
8aff97a
start ecdsa example
mitschabaude Nov 27, 2023
05925b2
expose elliptic curve intf on new Crypto namespace
mitschabaude Nov 27, 2023
89f7d01
finish example
mitschabaude Nov 27, 2023
2094f68
make vk returned by zkprogram consistent with smart contract
mitschabaude Nov 27, 2023
9ad5794
changelog
mitschabaude Nov 27, 2023
e7efc5d
changelog
mitschabaude Nov 27, 2023
e0c43f7
fix: use a variable for public key when analyzing ecdsa constraints
mitschabaude Nov 27, 2023
a2fe70f
add vk regression test for ecdsa
mitschabaude Nov 27, 2023
665158b
expose cs printing
mitschabaude Nov 27, 2023
71b7dcf
minor
mitschabaude Nov 27, 2023
febd9c9
delete ec unit test
mitschabaude Nov 27, 2023
f27f2e1
changelog
mitschabaude Nov 27, 2023
191971d
finish doccomments
mitschabaude Nov 27, 2023
6a449b2
bindings
mitschabaude Nov 27, 2023
16f0c0a
Merge branch 'feature/assert-mul' into feature/ecdsa-new
mitschabaude Nov 27, 2023
d0dc254
fix unit test
mitschabaude Nov 27, 2023
e7ad795
move todo out of doccomment
mitschabaude Nov 30, 2023
660f1a4
return a bool from verify()
mitschabaude Nov 30, 2023
1a55a5d
update vk
mitschabaude Nov 30, 2023
57576fa
wip adapting ecdsa soundness to bool return
mitschabaude Nov 30, 2023
5f5cd31
cherry pick assertLessThan
mitschabaude Nov 30, 2023
af6de47
finish adapting ecdsa final step
mitschabaude Nov 30, 2023
c193bd7
update vk
mitschabaude Nov 30, 2023
66a9dc1
document `config`
mitschabaude Nov 30, 2023
2731b0c
Merge branch 'main' into feature/ecdsa-new
mitschabaude Dec 1, 2023
1ad2602
foreign field: split assert non zero into function
mitschabaude Dec 1, 2023
1c90b52
assertNotEquals -> equals
mitschabaude Dec 1, 2023
7886bef
fix final check in scalar mul
mitschabaude Dec 1, 2023
ba0c361
split out equals
mitschabaude Dec 1, 2023
dba58f3
vk update
mitschabaude Dec 1, 2023
9c3c653
Merge branch 'feature/assert-mul' into feature/ecdsa-new
mitschabaude Dec 4, 2023
d0b09b9
apply small review suggestions
mitschabaude Dec 4, 2023
f68c148
support for a!=0 in double gadget
mitschabaude Dec 4, 2023
124d918
move ff equals gadget and handle an edge case
mitschabaude Dec 4, 2023
2495901
rename assertAlmostFieldElements
mitschabaude Dec 4, 2023
4999fe8
Merge branch 'main' into feature/ecdsa-new
mitschabaude Dec 4, 2023
6fc4e86
fixup vk test
mitschabaude Dec 4, 2023
f5ab9bc
Merge branch 'main' into feature/ecdsa-new
mitschabaude Dec 4, 2023
621d2ef
fixups
mitschabaude Dec 4, 2023
e2b0dd6
Merge branch 'feature/assert-mul' into feature/ecdsa-new
mitschabaude Dec 4, 2023
450203e
Merge branch 'main' into feature/ecdsa-new
mitschabaude Dec 6, 2023
f51e5c0
remove gadgets.ecdsa which ended up in a different final form
mitschabaude Dec 6, 2023
bbcfb17
fix build:examples while we're here
mitschabaude Dec 6, 2023
7ec8090
remove ecdsa from vk test for now
mitschabaude Dec 6, 2023
507a04b
fixup
mitschabaude Dec 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 9 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased](https://github.com/o1-labs/o1js/compare/1ad7333e9e...HEAD)

# Breaking changes

- `ZkProgram.compile()` now returns the verification key and its hash, to be consistent with `SmartContract.compile()` https://github.com/o1-labs/o1js/pull/1240
mitschabaude marked this conversation as resolved.
Show resolved Hide resolved

# Added

- **ECDSA signature verification**: new provable method `Gadgets.Ecdsa.verify()` and helpers on `Gadgets.Ecdsa.Signature` https://github.com/o1-labs/o1js/pull/1240
- For an example, see `./src/examples/zkprogram/ecdsa`
- `Crypto` namespace which exposes elliptic curve and finite field arithmetic on bigints, as well as example curve parameters https://github.com/o1-labs/o1js/pull/1240
- `Gadgets.ForeignField.assertMul()` for efficiently constraining products of sums in non-native arithmetic https://github.com/o1-labs/o1js/pull/1262
- `Unconstrained` for safely maintaining unconstrained values in provable code https://github.com/o1-labs/o1js/pull/1262

Expand All @@ -29,12 +36,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Change precondition APIs to use "require" instead of "assert" as the verb, to distinguish them from provable assertions.
- `this.x.getAndAssertEquals()` is now `this.x.getAndRequireEquals()` https://github.com/o1-labs/o1js/pull/1263
- `this.x.assertEquals(x)` is now `this.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1263
- `this.x.assertNothing()` is now `this.x.requireNothing()` https://github.com/o1-labs/o1js/pull/1263
- `this.account.x.getAndAssertEquals(x)` is now `this.account.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1265
- `this.account.x.assertBetween()` is now `this.account.x.requireBetween()` https://github.com/o1-labs/o1js/pull/1265
- `this.account.x.assertEquals(x)` is now `this.account.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1265
- `this.account.x.assertNothing()` is now `this.account.x.requireNothing()` https://github.com/o1-labs/o1js/pull/1265
- `this.currentSlot.assertBetween(x,y)` is now `this.currentSlot.requireBetween(x,y)` https://github.com/o1-labs/o1js/pull/1265
- `this.network.x.getAndAssertEquals()` is now `this.network.x.getAndRequireEquals()` https://github.com/o1-labs/o1js/pull/1265
- `Provable.constraintSystem()` and `{ZkProgram,SmartContract}.analyzeMethods()` return a `print()` method for pretty-printing the constraint system https://github.com/o1-labs/o1js/pull/1240
jspada marked this conversation as resolved.
Show resolved Hide resolved

## [0.14.2](https://github.com/o1-labs/o1js/compare/26363465d...1ad7333e9e)

Expand Down
2 changes: 1 addition & 1 deletion src/bindings
4 changes: 2 additions & 2 deletions src/examples/constraint_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Field, Poseidon, Provable } from 'o1js';

let hash = Poseidon.hash([Field(1), Field(-1)]);

let { rows, digest, gates, publicInputSize } = Provable.constraintSystem(() => {
let { rows, digest, publicInputSize, print } = Provable.constraintSystem(() => {
let x = Provable.witness(Field, () => Field(1));
let y = Provable.witness(Field, () => Field(-1));
x.add(y).assertEquals(Field(0));
let z = Poseidon.hash([x, y]);
z.assertEquals(hash);
});

console.log(JSON.stringify(gates));
print();
console.log({ rows, digest, publicInputSize });
41 changes: 41 additions & 0 deletions src/examples/zkprogram/ecdsa/ecdsa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Gadgets, ZkProgram, Struct, Crypto } from 'o1js';

export { ecdsaProgram, Point, Secp256k1 };

let { ForeignField, Field3, Ecdsa } = Gadgets;
mitschabaude marked this conversation as resolved.
Show resolved Hide resolved

// TODO expose this as part of Gadgets.Curve

class Point extends Struct({ x: Field3.provable, y: Field3.provable }) {
// point from bigints
static from({ x, y }: { x: bigint; y: bigint }) {
return new Point({ x: Field3.from(x), y: Field3.from(y) });
}
}

const Secp256k1 = Crypto.createCurve(Crypto.CurveParams.Secp256k1);
mitschabaude marked this conversation as resolved.
Show resolved Hide resolved

const ecdsaProgram = ZkProgram({
name: 'ecdsa',
publicInput: Point,

methods: {
verifyEcdsa: {
privateInputs: [Ecdsa.Signature.provable, Field3.provable],
mitschabaude marked this conversation as resolved.
Show resolved Hide resolved
method(
publicKey: Point,
signature: Gadgets.Ecdsa.Signature,
msgHash: Gadgets.Field3
) {
// assert that private inputs are valid
ForeignField.assertAlmostFieldElements(
[signature.r, signature.s, msgHash],
Secp256k1.order
);

// verify signature
Ecdsa.verify(Secp256k1, signature, msgHash, publicKey);
},
},
},
});
43 changes: 43 additions & 0 deletions src/examples/zkprogram/ecdsa/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Gadgets } from 'o1js';
import { Point, Secp256k1, ecdsaProgram } from './ecdsa.js';
import assert from 'assert';

// create an example ecdsa signature

let privateKey = Secp256k1.Scalar.random();
let publicKey = Secp256k1.scale(Secp256k1.one, privateKey);

// TODO use an actual keccak hash
let messageHash = Secp256k1.Scalar.random();

let signature = Gadgets.Ecdsa.sign(Secp256k1, messageHash, privateKey);

// investigate the constraint system generated by ECDSA verify

console.time('ecdsa verify (build constraint system)');
let cs = ecdsaProgram.analyzeMethods().verifyEcdsa;
console.timeEnd('ecdsa verify (build constraint system)');

let gateTypes: Record<string, number> = {};
gateTypes['Total rows'] = cs.rows;
for (let gate of cs.gates) {
gateTypes[gate.type] ??= 0;
gateTypes[gate.type]++;
}
console.log(gateTypes);

// compile and prove

console.time('ecdsa verify (compile)');
await ecdsaProgram.compile();
console.timeEnd('ecdsa verify (compile)');

console.time('ecdsa verify (prove)');
let proof = await ecdsaProgram.verifyEcdsa(
Point.from(publicKey),
Gadgets.Ecdsa.Signature.from(signature),
Gadgets.Field3.from(messageHash)
);
jspada marked this conversation as resolved.
Show resolved Hide resolved
console.timeEnd('ecdsa verify (prove)');

assert(await ecdsaProgram.verify(proof), 'proof verifies');
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export {
method,
declareMethods,
Account,
VerificationKey,
Reducer,
} from './lib/zkapp.js';
export { state, State, declareState } from './lib/state.js';
Expand All @@ -45,6 +44,7 @@ export {
Empty,
Undefined,
Void,
VerificationKey,
} from './lib/proof_system.js';
export { Cache, CacheHeader } from './lib/proof-system/cache.js';

Expand Down Expand Up @@ -81,6 +81,8 @@ export { Nullifier } from './lib/nullifier.js';
import { ExperimentalZkProgram, ZkProgram } from './lib/proof_system.js';
export { ZkProgram };

export { Crypto } from './lib/crypto.js';

// experimental APIs
import { Callback } from './lib/zkapp.js';
import { createChildAccountUpdate } from './lib/account_update.js';
Expand Down
31 changes: 31 additions & 0 deletions src/lib/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { CurveParams as CurveParams_ } from '../bindings/crypto/elliptic-curve-examples.js';
import {
CurveAffine,
createCurveAffine,
} from '../bindings/crypto/elliptic_curve.js';

// crypto namespace
const Crypto = {
/**
* Create elliptic curve arithmetic methods.
*/
createCurve(params: Crypto.CurveParams): Crypto.Curve {
return createCurveAffine(params);
},
/**
* Parameters defining an elliptic curve in short Weierstraß form
* y^2 = x^3 + ax + b
*/
CurveParams: CurveParams_,
};

namespace Crypto {
/**
* Parameters defining an elliptic curve in short Weierstraß form
* y^2 = x^3 + ax + b
*/
export type CurveParams = CurveParams_;

export type Curve = CurveAffine;
}
export { Crypto };
45 changes: 44 additions & 1 deletion src/lib/gadgets/basic.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,58 @@
/**
* Basic gadgets that only use generic gates
*/
import { Fp } from '../../bindings/crypto/finite_field.js';
import type { Field, VarField } from '../field.js';
import { existsOne, toVar } from './common.js';
import { Gates } from '../gates.js';
import { TupleN } from '../util/types.js';

export { assertOneOf };
export { arrayGet, assertOneOf };

// TODO: create constant versions of these and expose on Gadgets

/**
* Get value from array in O(n) rows.
*
* Assumes that index is in [0, n), returns an unconstrained result otherwise.
*
* Note: This saves 0.5*n constraints compared to equals() + switch()
*/
function arrayGet(array: Field[], index: Field) {
let i = toVar(index);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this making the index a const circuit variable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other way! it's making the index a Var if it's currently a Constant | Add | Scale


// witness result
let a = existsOne(() => array[Number(i.toBigInt())].toBigInt());

// we prove a === array[j] + zj*(i - j) for some zj, for all j.
mitschabaude marked this conversation as resolved.
Show resolved Hide resolved
// setting j = i, this implies a === array[i]
// thanks to our assumption that the index i is within bounds, we know that j = i for some j
let n = array.length;
for (let j = 0; j < n; j++) {
let zj = existsOne(() => {
let zj = Fp.div(
Fp.sub(a.toBigInt(), array[j].toBigInt()),
Fp.sub(i.toBigInt(), Fp.fromNumber(j))
);
return zj ?? 0n;
});
// prove that zj*(i - j) === a - array[j]
// TODO abstract this logic into a general-purpose assertMul() gadget,
// which is able to use the constant coefficient
// (snarky's assert_r1cs somehow leads to much more constraints than this)
if (array[j].isConstant()) {
// zj*i + (-j)*zj + 0*i + array[j] === a
assertBilinear(zj, i, [1n, -BigInt(j), 0n, array[j].toBigInt()], a);
} else {
let aMinusAj = toVar(a.sub(array[j]));
// zj*i + (-j)*zj + 0*i + 0 === (a - array[j])
assertBilinear(zj, i, [1n, -BigInt(j), 0n, 0n], aMinusAj);
}
}

return a;
}
mitschabaude marked this conversation as resolved.
Show resolved Hide resolved

/**
* Assert that a value equals one of a finite list of constants:
* `(x - c1)*(x - c2)*...*(x - cn) === 0`
Expand Down