Skip to content

Commit 1716b0f

Browse files
authored
Ⓜ️ Add HumanID authentication process (#17) (#18)
Ⓜ️ Add HumanID authentication process (fixes #17)
1 parent 019ed55 commit 1716b0f

File tree

3 files changed

+90
-28
lines changed

3 files changed

+90
-28
lines changed

mina/examples/Airdrop.test.ts

+43-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import {
22
AccountUpdate,
3+
Bool,
34
Field,
45
MerkleTree,
56
Mina,
67
PrivateKey,
78
Signature,
89
UInt64
910
} from "o1js";
10-
import { HumanIDWitness, Signatures } from "../humanIDv1";
11+
import { HumanIDWitness, Signatures, authenticate } from "../humanIDv1";
1112
import { Airdrop } from "./Airdrop";
1213

1314
describe('Example Airdrop zkApp', () => {
@@ -17,10 +18,25 @@ describe('Example Airdrop zkApp', () => {
1718
const sender = senderKey.toPublicKey();
1819
const appKey = PrivateKey.random();
1920
const appAddr = appKey.toPublicKey();
21+
const id1 = Field(1);
22+
const id2 = Field(2);
23+
const privKey1 = PrivateKey.fromBigInt(1n);
24+
const privKey2 = PrivateKey.fromBigInt(2n);
25+
const privKey3 = PrivateKey.fromBigInt(3n);
2026
const sigs = new Signatures({
21-
sig1: Signature.create(senderKey, [Field(1)]),
22-
sig2: Signature.create(senderKey, [Field(2)]),
23-
sig3: Signature.create(senderKey, [Field(3)])
27+
sig1: Signature.create(privKey1, [Field(100), sender.x.add(sender.isOdd.toField())]),
28+
sig2: Signature.create(privKey2, [Field(100), sender.x.add(sender.isOdd.toField())]),
29+
sig3: Signature.create(privKey3, [Field(100), sender.x.add(sender.isOdd.toField())])
30+
});
31+
const sigs1 = new Signatures({
32+
sig1: Signature.create(privKey1, [id1, sender.x.add(sender.isOdd.toField())]),
33+
sig2: Signature.create(privKey2, [id1, sender.x.add(sender.isOdd.toField())]),
34+
sig3: Signature.create(privKey3, [id1, sender.x.add(sender.isOdd.toField())])
35+
});
36+
const sigs2 = new Signatures({
37+
sig1: Signature.create(privKey1, [id2, sender.x.add(sender.isOdd.toField())]),
38+
sig2: Signature.create(privKey2, [id2, sender.x.add(sender.isOdd.toField())]),
39+
sig3: Signature.create(privKey3, [id2, sender.x.add(sender.isOdd.toField())])
2440
});
2541
let tree: MerkleTree;
2642
let app: Airdrop;
@@ -49,6 +65,11 @@ describe('Example Airdrop zkApp', () => {
4965
}).then((txn) => txn.prove())
5066
.then((txn) => txn.sign([deployerKey, appKey]).send())
5167

68+
it('should verify signatures', () => {
69+
authenticate(id1, sigs1, sender);
70+
authenticate(id2, sigs2, sender);
71+
})
72+
5273
it('should deploy the app and fund it', async () => {
5374
await deploy();
5475
await fundZkApp();
@@ -71,9 +92,14 @@ describe('Example Airdrop zkApp', () => {
7192

7293
const id1 = 123123123123123123123123123123n;
7394
const truncatedId1 = id1 & 0xFFFFFFFFn;
95+
const sigsTest1 = new Signatures({
96+
sig1: Signature.create(privKey1, [Field(id1), sender.x.add(sender.isOdd.toField())]),
97+
sig2: Signature.create(privKey2, [Field(id1), sender.x.add(sender.isOdd.toField())]),
98+
sig3: Signature.create(privKey3, [Field(id1), sender.x.add(sender.isOdd.toField())])
99+
});
74100
await Mina.transaction(
75101
sender,
76-
() => app.claimReward(Field(id1), sigs, new HumanIDWitness(tree.getWitness(truncatedId1)))
102+
() => app.claimReward(Field(id1), sigsTest1, new HumanIDWitness(tree.getWitness(truncatedId1)))
77103
)
78104
.then((txn) => txn.prove())
79105
.then((txn) => txn.sign([senderKey]).send());
@@ -82,9 +108,14 @@ describe('Example Airdrop zkApp', () => {
82108

83109
const id2 = 123123123123123123123123123124n;
84110
const truncatedId2 = id2 & 0xFFFFFFFFn;
111+
const sigsTest2 = new Signatures({
112+
sig1: Signature.create(privKey1, [Field(id2), sender.x.add(sender.isOdd.toField())]),
113+
sig2: Signature.create(privKey2, [Field(id2), sender.x.add(sender.isOdd.toField())]),
114+
sig3: Signature.create(privKey3, [Field(id2), sender.x.add(sender.isOdd.toField())])
115+
});
85116
await Mina.transaction(
86117
sender,
87-
() => app.claimReward(Field(id2), sigs, new HumanIDWitness(tree.getWitness(truncatedId2)))
118+
() => app.claimReward(Field(id2), sigsTest2, new HumanIDWitness(tree.getWitness(truncatedId2)))
88119
)
89120
.then((txn) => txn.prove())
90121
.then((txn) => txn.sign([senderKey]).send());
@@ -94,9 +125,14 @@ describe('Example Airdrop zkApp', () => {
94125
await deploy();
95126
await fundZkApp();
96127

128+
const sigsTest3 = new Signatures({
129+
sig1: Signature.create(privKey1, [Field(123123123123123n), sender.x.add(sender.isOdd.toField())]),
130+
sig2: Signature.create(privKey2, [Field(123123123123123n), sender.x.add(sender.isOdd.toField())]),
131+
sig3: Signature.create(privKey3, [Field(123123123123123n), sender.x.add(sender.isOdd.toField())])
132+
});
97133
await expect(() => Mina.transaction(
98134
sender,
99-
() => app.claimReward(Field(123123123123123n), sigs, new HumanIDWitness(tree.getWitness(100n)))
135+
() => app.claimReward(Field(123123123123123n), sigsTest3, new HumanIDWitness(tree.getWitness(100n)))
100136
)
101137
.then((txn) => txn.prove())
102138
.then((txn) => txn.sign([senderKey]).send())).rejects.toThrow(/does not match the witness/);

mina/examples/Airdrop.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Airdrop extends SmartContract {
3131
sigs: Signatures,
3232
witness: HumanIDWitness,
3333
) {
34-
acceptHumanIDv1(humanIDv1, sigs, this.treeRoot, witness);
34+
acceptHumanIDv1(humanIDv1, sigs, this.treeRoot, witness, this.sender.getAndRequireSignature());
3535
this.send({ to: this.sender.getUnconstrained(), amount: 10 * MINA });
3636
}
3737
}

mina/humanIDv1.ts

+46-20
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1-
import { Field, MerkleWitness, Signature, State, Struct } from "o1js";
1+
import {
2+
Field,
3+
MerkleWitness,
4+
PrivateKey,
5+
PublicKey,
6+
Signature,
7+
State,
8+
Struct,
9+
} from "o1js";
210

311
class Signatures extends Struct({
412
sig1: Signature,
513
sig2: Signature,
6-
sig3: Signature
7-
}) { }
14+
sig3: Signature,
15+
}) {}
816

9-
class HumanIDWitness extends MerkleWitness(33) { }
17+
const node1PrivKey = PrivateKey.fromBigInt(1n);
18+
const node1PublicKey = node1PrivKey.toPublicKey();
19+
const node2PrivKey = PrivateKey.fromBigInt(2n);
20+
const node2PublicKey = node2PrivKey.toPublicKey();
21+
const node3PrivKey = PrivateKey.fromBigInt(3n);
22+
const node3PublicKey = node3PrivKey.toPublicKey();
23+
24+
class HumanIDWitness extends MerkleWitness(33) {}
1025

1126
const addToMerkleTree = (treeRoot: State<Field>, witness: HumanIDWitness) => {
1227
const currentTreeRoot = treeRoot.getAndRequireEquals();
@@ -15,34 +30,45 @@ const addToMerkleTree = (treeRoot: State<Field>, witness: HumanIDWitness) => {
1530
"HumanID already exists in the set"
1631
);
1732
treeRoot.set(witness.calculateRoot(Field(1)));
18-
}
33+
};
1934

20-
const authenticate = (humanIDv1: Field, sigs: Signatures) => {
21-
// TODO(KimlikDAO-bot)
22-
return true;
23-
}
35+
const authenticate = (
36+
humanIDv1: Field,
37+
sigs: Signatures,
38+
claimant: PublicKey
39+
) => {
40+
sigs.sig1.verify(node1PublicKey, [humanIDv1, claimant.x.add(claimant.isOdd.toField())]).assertTrue();
41+
sigs.sig2.verify(node2PublicKey, [humanIDv1, claimant.x.add(claimant.isOdd.toField())]).assertTrue();
42+
sigs.sig3.verify(node3PublicKey, [humanIDv1, claimant.x.add(claimant.isOdd.toField())]).assertTrue();
43+
};
2444

25-
const EmptyRoot = Field(0x21afce36daa1a2d67391072035f4555a85aea7197e5830b128f121aa382770cdn);
45+
const EmptyRoot =
46+
Field(0x21afce36daa1a2d67391072035f4555a85aea7197e5830b128f121aa382770cdn);
2647

27-
const Inverse2Exp32 = Field(0x3fffffffc00000000000000000000000224698fbe706601f8fe037d166d2cf14n);
48+
const Inverse2Exp32 =
49+
Field(0x3fffffffc00000000000000000000000224698fbe706601f8fe037d166d2cf14n);
2850

2951
const requireConsistent = (humanIDv1: Field, truncatedHumanIDv1: Field) => {
30-
humanIDv1.sub(truncatedHumanIDv1).mul(Inverse2Exp32).assertLessThan(
31-
(1n << 222n) + 0x224698fc094cf91b992d30edn,
32-
"HumanID does not match the witness"
33-
);
34-
}
52+
humanIDv1
53+
.sub(truncatedHumanIDv1)
54+
.mul(Inverse2Exp32)
55+
.assertLessThan(
56+
(1n << 222n) + 0x224698fc094cf91b992d30edn,
57+
"HumanID does not match the witness"
58+
);
59+
};
3560

3661
const acceptHumanIDv1 = (
3762
humanIDv1: Field,
3863
sigs: Signatures,
3964
treeRoot: State<Field>,
40-
witness: HumanIDWitness
65+
witness: HumanIDWitness,
66+
claimant: PublicKey
4167
) => {
42-
authenticate(humanIDv1, sigs);
68+
authenticate(humanIDv1, sigs, claimant);
4369
requireConsistent(humanIDv1, witness.calculateIndex());
4470
addToMerkleTree(treeRoot, witness);
45-
}
71+
};
4672

4773
export {
4874
EmptyRoot,
@@ -51,5 +77,5 @@ export {
5177
acceptHumanIDv1,
5278
addToMerkleTree,
5379
authenticate,
54-
requireConsistent
80+
requireConsistent,
5581
};

0 commit comments

Comments
 (0)