-
Notifications
You must be signed in to change notification settings - Fork 0
/
HumanMessage.ts
119 lines (103 loc) · 2.98 KB
/
HumanMessage.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import {
AccountUpdate,
arrayProp,
CircuitValue,
Encoding,
Field,
isReady,
method,
Mina,
PrivateKey,
PublicKey,
SmartContract,
State,
state,
} from 'snarkyjs';
import {
MerkleWitness,
SemaphorePrivateKey,
SignedMerkleRoot,
WorldId,
} from './WorldId.js';
export {
HumanMessage,
StringOf7Fields,
deployHumanMessage,
zkappAddress,
getMessage,
feePayer,
};
class StringOf7Fields extends CircuitValue {
@arrayProp(Field, 7) fields: Field[];
static from(message: string) {
let fields = Encoding.Bijective.Fp.fromString(message);
let n = fields.length;
if (n > 7) throw Error('string too long');
fields = fields.concat(Array(7 - n).fill(Field.zero));
return new StringOf7Fields(fields);
}
toString() {
return Encoding.Bijective.Fp.toString(this.fields);
}
}
class HumanMessage extends SmartContract {
@state(StringOf7Fields) currentMessage = State<StringOf7Fields>();
@state(Field) currentNullifier = State<Field>();
events = { message: StringOf7Fields };
/**
* publish a message, provided you're a unique human, and you didn't do the last message already
*/
@method publishMessage(
message: StringOf7Fields,
privateKey: SemaphorePrivateKey,
// TODO: when we support async circuits, we can fetch the merkle proof inside this method
merklePath: MerkleWitness,
signedRoot: SignedMerkleRoot
) {
// check that whoever wants to publish is a human
let externalNullifier = Encoding.stringToFields('human message')[0];
let worldId = new WorldId(PublicKey.empty());
let nullifier = worldId.provePersonhoodBase(
privateKey,
merklePath,
signedRoot,
externalNullifier
);
// we use the nullifier to ensure that nobody can publish 2 messages in a row!
let currentNullifier = this.currentNullifier.get();
this.currentNullifier.assertEquals(currentNullifier); // precondition which is checked by on chain verifier
currentNullifier.equals(nullifier).assertFalse();
this.currentMessage.set(message);
this.emitEvent('message', message);
}
}
await isReady;
// local ledger for mocking blockchain interactions
// TODO: deploy to testnet and use deployed version instead
let LocalBlockchain = Mina.LocalBlockchain();
Mina.setActiveInstance(LocalBlockchain);
let feePayer = LocalBlockchain.testAccounts[0].privateKey;
let zkappKey = PrivateKey.random();
let zkappAddress = zkappKey.toPublicKey();
let isDeployed = false;
// helper to "deploy" the contract to the local ledger, if it isn't yet
async function deployHumanMessage() {
if (isDeployed) return;
await HumanMessage.compile();
let tx = await Mina.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer);
let zkapp = new HumanMessage(zkappAddress);
zkapp.deploy();
});
tx.sign([zkappKey]);
await tx.send().wait();
isDeployed = true;
}
function getMessage() {
let zkapp = new HumanMessage(zkappAddress);
try {
return zkapp.currentMessage.get();
} catch {
return '';
}
}