@proto-kit/protocol 0.1.1-develop.457 → 0.1.1-develop.600
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blockmodules/AccountStateModule.d.ts.map +1 -1
- package/dist/blockmodules/AccountStateModule.js +10 -3
- package/dist/blockmodules/BlockHeightHook.d.ts +3 -3
- package/dist/blockmodules/BlockHeightHook.d.ts.map +1 -1
- package/dist/blockmodules/BlockHeightHook.js +5 -4
- package/dist/blockmodules/LastStateRootBlockHook.d.ts +8 -0
- package/dist/blockmodules/LastStateRootBlockHook.d.ts.map +1 -0
- package/dist/blockmodules/LastStateRootBlockHook.js +15 -0
- package/dist/blockmodules/NoopBlockHook.d.ts +6 -4
- package/dist/blockmodules/NoopBlockHook.d.ts.map +1 -1
- package/dist/blockmodules/NoopBlockHook.js +4 -4
- package/dist/blockmodules/NoopSettlementHook.d.ts +6 -0
- package/dist/blockmodules/NoopSettlementHook.d.ts.map +1 -0
- package/dist/blockmodules/NoopSettlementHook.js +18 -0
- package/dist/hooks/AccountStateHook.d.ts.map +1 -1
- package/dist/hooks/AccountStateHook.js +7 -2
- package/dist/hooks/BlockHeightHook.d.ts.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/model/MethodPublicOutput.d.ts +8 -0
- package/dist/model/MethodPublicOutput.d.ts.map +1 -1
- package/dist/model/MethodPublicOutput.js +1 -0
- package/dist/model/network/NetworkState.d.ts +40 -0
- package/dist/model/network/NetworkState.d.ts.map +1 -1
- package/dist/model/network/NetworkState.js +14 -2
- package/dist/model/transaction/RuntimeTransaction.d.ts +45 -20
- package/dist/model/transaction/RuntimeTransaction.d.ts.map +1 -1
- package/dist/model/transaction/RuntimeTransaction.js +68 -11
- package/dist/model/transaction/SignedTransaction.d.ts +71 -0
- package/dist/model/transaction/SignedTransaction.d.ts.map +1 -0
- package/dist/model/transaction/SignedTransaction.js +33 -0
- package/dist/model/transaction/ValueOption.d.ts +119 -0
- package/dist/model/transaction/ValueOption.d.ts.map +1 -0
- package/dist/model/transaction/ValueOption.js +24 -0
- package/dist/protocol/Protocol.d.ts +7 -3
- package/dist/protocol/Protocol.d.ts.map +1 -1
- package/dist/protocol/Protocol.js +26 -16
- package/dist/protocol/ProvableBlockHook.d.ts +2 -10
- package/dist/protocol/ProvableBlockHook.d.ts.map +1 -1
- package/dist/protocol/ProvableBlockHook.js +1 -1
- package/dist/protocol/ProvableTransactionHook.d.ts +1 -1
- package/dist/protocol/ProvableTransactionHook.d.ts.map +1 -1
- package/dist/protocol/TransitioningProtocolModule.d.ts +3 -2
- package/dist/protocol/TransitioningProtocolModule.d.ts.map +1 -1
- package/dist/protocol/TransitioningProtocolModule.js +3 -2
- package/dist/prover/block/BlockProvable.d.ts +106 -28
- package/dist/prover/block/BlockProvable.d.ts.map +1 -1
- package/dist/prover/block/BlockProvable.js +23 -5
- package/dist/prover/block/BlockProver.d.ts +29 -8
- package/dist/prover/block/BlockProver.d.ts.map +1 -1
- package/dist/prover/block/BlockProver.js +241 -77
- package/dist/prover/block/accummulators/BlockHashMerkleTree.d.ts +45 -0
- package/dist/prover/block/accummulators/BlockHashMerkleTree.d.ts.map +1 -0
- package/dist/prover/block/accummulators/BlockHashMerkleTree.js +16 -0
- package/dist/settlement/ProvableSettlementHook.d.ts +26 -0
- package/dist/settlement/ProvableSettlementHook.d.ts.map +1 -0
- package/dist/settlement/ProvableSettlementHook.js +3 -0
- package/dist/settlement/SettlementContract.d.ts +230 -0
- package/dist/settlement/SettlementContract.d.ts.map +1 -0
- package/dist/settlement/SettlementContract.js +346 -0
- package/dist/settlement/modules/NetworkStateSettlementModule.d.ts +11 -0
- package/dist/settlement/modules/NetworkStateSettlementModule.d.ts.map +1 -0
- package/dist/settlement/modules/NetworkStateSettlementModule.js +12 -0
- package/dist/state/context/RuntimeMethodExecutionContext.d.ts +75 -1
- package/dist/state/context/RuntimeMethodExecutionContext.d.ts.map +1 -1
- package/dist/state/context/RuntimeMethodExecutionContext.js +19 -1
- package/dist/utils/MinaPrefixedProvableHashList.d.ts +24 -0
- package/dist/utils/MinaPrefixedProvableHashList.d.ts.map +1 -0
- package/dist/utils/MinaPrefixedProvableHashList.js +51 -0
- package/package.json +2 -2
- package/src/blockmodules/AccountStateModule.ts +17 -6
- package/src/blockmodules/BlockHeightHook.ts +5 -8
- package/src/blockmodules/LastStateRootBlockHook.ts +26 -0
- package/src/blockmodules/NoopBlockHook.ts +15 -11
- package/src/blockmodules/NoopSettlementHook.ts +21 -0
- package/src/blockmodules/SequenceStateTransactionModule.ts +25 -0
- package/src/index.ts +9 -3
- package/src/model/MethodPublicOutput.ts +3 -2
- package/src/model/network/NetworkState.ts +15 -3
- package/src/model/transaction/RuntimeTransaction.ts +90 -16
- package/src/model/transaction/SignedTransaction.ts +54 -0
- package/src/model/transaction/ValueOption.ts +28 -0
- package/src/protocol/Protocol.ts +49 -31
- package/src/protocol/ProvableBlockHook.ts +10 -13
- package/src/protocol/ProvableTransactionHook.ts +2 -1
- package/src/protocol/TransitioningProtocolModule.ts +3 -2
- package/src/prover/block/BlockProvable.ts +39 -6
- package/src/prover/block/BlockProver.ts +473 -143
- package/src/prover/block/accummulators/BlockHashMerkleTree.ts +16 -0
- package/src/settlement/ProvableSettlementHook.ts +37 -0
- package/src/settlement/SettlementContract.ts +444 -0
- package/src/settlement/modules/NetworkStateSettlementModule.ts +39 -0
- package/src/state/context/RuntimeMethodExecutionContext.ts +18 -1
- package/src/utils/MinaPrefixedProvableHashList.ts +72 -0
- package/test/BlockProver.test.ts +2 -3
- package/src/model/transaction/ProtocolTransaction.ts +0 -25
- package/src/prover/block/BlockTransactionPosition.ts +0 -34
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createMerkleTree } from "@proto-kit/common";
|
|
2
|
+
import { Bool, Field, Poseidon, Provable, Struct } from "o1js";
|
|
3
|
+
|
|
4
|
+
export class BlockHashMerkleTree extends createMerkleTree(16) {}
|
|
5
|
+
export class BlockHashMerkleTreeWitness extends BlockHashMerkleTree.WITNESS {}
|
|
6
|
+
|
|
7
|
+
export class BlockHashTreeEntry extends Struct({
|
|
8
|
+
blockHash: Field,
|
|
9
|
+
closed: Bool,
|
|
10
|
+
// TODO We could add startingEternalTransactionsHash here to offer
|
|
11
|
+
// a more trivial connection to the sequence state
|
|
12
|
+
}) {
|
|
13
|
+
public hash(): Field {
|
|
14
|
+
return Poseidon.hash([this.blockHash, ...this.closed.toFields()]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Field, UInt32 } from "o1js";
|
|
2
|
+
|
|
3
|
+
import type { BlockProof } from "../prover/block/BlockProver";
|
|
4
|
+
import { ProtocolModule } from "../protocol/ProtocolModule";
|
|
5
|
+
import { NetworkState } from "../model/network/NetworkState";
|
|
6
|
+
|
|
7
|
+
import type { SettlementContract } from "./SettlementContract";
|
|
8
|
+
|
|
9
|
+
export type SettlementStateRecord = {
|
|
10
|
+
sequencerKey: Field;
|
|
11
|
+
lastSettlementL1Block: UInt32;
|
|
12
|
+
|
|
13
|
+
stateRoot: Field;
|
|
14
|
+
networkStateHash: Field;
|
|
15
|
+
blockHashRoot: Field;
|
|
16
|
+
|
|
17
|
+
promisedMessagesHash: Field;
|
|
18
|
+
honoredMessagesHash: Field;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type SettlementHookInputs = {
|
|
22
|
+
blockProof: BlockProof;
|
|
23
|
+
fromNetworkState: NetworkState;
|
|
24
|
+
toNetworkState: NetworkState;
|
|
25
|
+
newPromisedMessagesHash: Field;
|
|
26
|
+
contractState: SettlementStateRecord;
|
|
27
|
+
currentL1Block: UInt32;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export abstract class ProvableSettlementHook<
|
|
31
|
+
Config
|
|
32
|
+
> extends ProtocolModule<Config> {
|
|
33
|
+
public abstract beforeSettlement(
|
|
34
|
+
smartContract: SettlementContract,
|
|
35
|
+
inputs: SettlementHookInputs
|
|
36
|
+
): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccountUpdate,
|
|
3
|
+
Bool,
|
|
4
|
+
Field,
|
|
5
|
+
method,
|
|
6
|
+
Mina,
|
|
7
|
+
Poseidon,
|
|
8
|
+
Proof,
|
|
9
|
+
Provable,
|
|
10
|
+
ProvableExtended,
|
|
11
|
+
PublicKey,
|
|
12
|
+
Reducer,
|
|
13
|
+
Signature,
|
|
14
|
+
SmartContract,
|
|
15
|
+
State,
|
|
16
|
+
state,
|
|
17
|
+
Struct,
|
|
18
|
+
TokenId,
|
|
19
|
+
UInt32,
|
|
20
|
+
UInt64,
|
|
21
|
+
} from "o1js";
|
|
22
|
+
import {
|
|
23
|
+
EMPTY_PUBLICKEY,
|
|
24
|
+
prefixToField,
|
|
25
|
+
RollupMerkleTree,
|
|
26
|
+
RollupMerkleTreeWitness,
|
|
27
|
+
} from "@proto-kit/common";
|
|
28
|
+
import { inject, injectable, injectAll } from "tsyringe";
|
|
29
|
+
|
|
30
|
+
import { ProtocolModule } from "../protocol/ProtocolModule";
|
|
31
|
+
import { BlockProver } from "../prover/block/BlockProver";
|
|
32
|
+
import {
|
|
33
|
+
BlockProvable,
|
|
34
|
+
BlockProverPublicInput,
|
|
35
|
+
BlockProverPublicOutput,
|
|
36
|
+
} from "../prover/block/BlockProvable";
|
|
37
|
+
import { NetworkState } from "../model/network/NetworkState";
|
|
38
|
+
import { BlockHashMerkleTree } from "../prover/block/accummulators/BlockHashMerkleTree";
|
|
39
|
+
import { RuntimeTransaction } from "../model/transaction/RuntimeTransaction";
|
|
40
|
+
import { Path } from "../model/Path";
|
|
41
|
+
import { MinaActions, MinaEvents } from "../utils/MinaPrefixedProvableHashList";
|
|
42
|
+
|
|
43
|
+
import {
|
|
44
|
+
ProvableSettlementHook,
|
|
45
|
+
SettlementHookInputs,
|
|
46
|
+
SettlementStateRecord,
|
|
47
|
+
} from "./ProvableSettlementHook";
|
|
48
|
+
|
|
49
|
+
class LazyBlockProof extends Proof<
|
|
50
|
+
BlockProverPublicInput,
|
|
51
|
+
BlockProverPublicOutput
|
|
52
|
+
> {
|
|
53
|
+
public static publicInputType = BlockProverPublicInput;
|
|
54
|
+
|
|
55
|
+
public static publicOutputType = BlockProverPublicOutput;
|
|
56
|
+
|
|
57
|
+
public static tag: () => { name: string } = () => {
|
|
58
|
+
throw new Error("Tag not initialized yet");
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type SettlementMethodIdMapping = Record<`${string}.${string}`, bigint>;
|
|
63
|
+
|
|
64
|
+
export class Deposit extends Struct({
|
|
65
|
+
address: PublicKey,
|
|
66
|
+
amount: UInt64,
|
|
67
|
+
}) {}
|
|
68
|
+
|
|
69
|
+
export class Withdrawal extends Struct({
|
|
70
|
+
address: PublicKey,
|
|
71
|
+
amount: UInt64,
|
|
72
|
+
}) {
|
|
73
|
+
static dummy() {
|
|
74
|
+
return new Withdrawal({
|
|
75
|
+
address: EMPTY_PUBLICKEY,
|
|
76
|
+
amount: UInt64.from(0),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const OUTGOING_MESSAGE_BATCH_SIZE = 1;
|
|
82
|
+
|
|
83
|
+
export class OutgoingMessageArgument extends Struct({
|
|
84
|
+
witness: RollupMerkleTreeWitness,
|
|
85
|
+
value: Withdrawal,
|
|
86
|
+
}) {
|
|
87
|
+
public static dummy(): OutgoingMessageArgument {
|
|
88
|
+
return new OutgoingMessageArgument({
|
|
89
|
+
witness: RollupMerkleTreeWitness.dummy(),
|
|
90
|
+
value: Withdrawal.dummy(),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class OutgoingMessageArgumentBatch extends Struct({
|
|
96
|
+
arguments: Provable.Array(
|
|
97
|
+
OutgoingMessageArgument,
|
|
98
|
+
OUTGOING_MESSAGE_BATCH_SIZE
|
|
99
|
+
),
|
|
100
|
+
|
|
101
|
+
isDummys: Provable.Array(Bool, OUTGOING_MESSAGE_BATCH_SIZE),
|
|
102
|
+
}) {
|
|
103
|
+
public static fromMessages(providedArguments: OutgoingMessageArgument[]) {
|
|
104
|
+
const batch = providedArguments.slice();
|
|
105
|
+
const isDummys = batch.map(() => Bool(false));
|
|
106
|
+
|
|
107
|
+
while (batch.length < OUTGOING_MESSAGE_BATCH_SIZE) {
|
|
108
|
+
batch.push(OutgoingMessageArgument.dummy());
|
|
109
|
+
isDummys.push(Bool(true));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return new OutgoingMessageArgumentBatch({
|
|
113
|
+
arguments: batch,
|
|
114
|
+
isDummys,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Some random prefix for the sequencer signature
|
|
120
|
+
export const BATCH_SIGNATURE_PREFIX = prefixToField("pk-batchSignature");
|
|
121
|
+
|
|
122
|
+
export const ACTIONS_EMPTY_HASH = Reducer.initialActionState;
|
|
123
|
+
|
|
124
|
+
export class SettlementContract extends SmartContract {
|
|
125
|
+
@state(Field) public sequencerKey = State<Field>();
|
|
126
|
+
@state(UInt32) public lastSettlementL1Block = State<UInt32>();
|
|
127
|
+
|
|
128
|
+
@state(Field) public stateRoot = State<Field>();
|
|
129
|
+
@state(Field) public networkStateHash = State<Field>();
|
|
130
|
+
@state(Field) public blockHashRoot = State<Field>();
|
|
131
|
+
|
|
132
|
+
@state(Field) public promisedMessagesHash = State<Field>();
|
|
133
|
+
@state(Field) public honoredMessagesHash = State<Field>();
|
|
134
|
+
|
|
135
|
+
@state(Field) public outgoingMessageCursor = State<Field>();
|
|
136
|
+
|
|
137
|
+
public constructor(
|
|
138
|
+
address: PublicKey,
|
|
139
|
+
private readonly methodIdMappings: Record<string, bigint>,
|
|
140
|
+
private readonly hooks: ProvableSettlementHook<unknown>[],
|
|
141
|
+
private readonly withdrawalStatePath: [string, string],
|
|
142
|
+
private readonly incomingMessagesPaths: Record<
|
|
143
|
+
string,
|
|
144
|
+
`${string}.${string}`
|
|
145
|
+
>,
|
|
146
|
+
// 24 hours
|
|
147
|
+
private readonly escapeHatchSlotsInterval = (60 / 3) * 24
|
|
148
|
+
) {
|
|
149
|
+
super(address);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@method
|
|
153
|
+
public initialize(sequencer: PublicKey) {
|
|
154
|
+
this.sequencerKey.getAndAssertEquals().assertEquals(Field(0));
|
|
155
|
+
this.stateRoot.getAndAssertEquals().assertEquals(Field(0));
|
|
156
|
+
this.blockHashRoot.getAndAssertEquals().assertEquals(Field(0));
|
|
157
|
+
this.networkStateHash.getAndAssertEquals().assertEquals(Field(0));
|
|
158
|
+
this.promisedMessagesHash.getAndAssertEquals().assertEquals(Field(0));
|
|
159
|
+
this.honoredMessagesHash.getAndAssertEquals().assertEquals(Field(0));
|
|
160
|
+
|
|
161
|
+
this.sequencerKey.set(sequencer.x);
|
|
162
|
+
this.stateRoot.set(Field(RollupMerkleTree.EMPTY_ROOT));
|
|
163
|
+
this.blockHashRoot.set(Field(BlockHashMerkleTree.EMPTY_ROOT));
|
|
164
|
+
this.networkStateHash.set(NetworkState.empty().hash());
|
|
165
|
+
this.promisedMessagesHash.set(ACTIONS_EMPTY_HASH);
|
|
166
|
+
this.honoredMessagesHash.set(ACTIONS_EMPTY_HASH);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@method
|
|
170
|
+
public settle(
|
|
171
|
+
blockProof: LazyBlockProof,
|
|
172
|
+
signature: Signature,
|
|
173
|
+
publicKey: PublicKey,
|
|
174
|
+
inputNetworkState: NetworkState,
|
|
175
|
+
outputNetworkState: NetworkState,
|
|
176
|
+
newPromisedMessagesHash: Field
|
|
177
|
+
) {
|
|
178
|
+
// Verify the blockproof
|
|
179
|
+
blockProof.verify();
|
|
180
|
+
|
|
181
|
+
// Get and assert on-chain values
|
|
182
|
+
const stateRoot = this.stateRoot.getAndAssertEquals();
|
|
183
|
+
const networkStateHash = this.networkStateHash.getAndAssertEquals();
|
|
184
|
+
const blockHashRoot = this.blockHashRoot.getAndAssertEquals();
|
|
185
|
+
const sequencerKey = this.sequencerKey.getAndAssertEquals();
|
|
186
|
+
const promisedMessagesHash = this.promisedMessagesHash.getAndAssertEquals();
|
|
187
|
+
const honoredMessagesHash = this.honoredMessagesHash.getAndAssertEquals();
|
|
188
|
+
const lastSettlementL1Block =
|
|
189
|
+
this.lastSettlementL1Block.getAndAssertEquals();
|
|
190
|
+
|
|
191
|
+
// Get block height and use the lower bound for all ops
|
|
192
|
+
const minBlockIncluded = this.network.globalSlotSinceGenesis.get();
|
|
193
|
+
this.network.globalSlotSinceGenesis.assertBetween(
|
|
194
|
+
minBlockIncluded,
|
|
195
|
+
minBlockIncluded.add(20)
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Check signature/escape catch
|
|
199
|
+
publicKey.x.assertEquals(
|
|
200
|
+
sequencerKey,
|
|
201
|
+
"Sequencer public key witness not matching"
|
|
202
|
+
);
|
|
203
|
+
const signatureValid = signature.verify(publicKey, [
|
|
204
|
+
BATCH_SIGNATURE_PREFIX,
|
|
205
|
+
lastSettlementL1Block.value,
|
|
206
|
+
]);
|
|
207
|
+
const escapeHatchActivated = lastSettlementL1Block
|
|
208
|
+
.add(UInt32.from(this.escapeHatchSlotsInterval))
|
|
209
|
+
.lessThan(minBlockIncluded);
|
|
210
|
+
signatureValid
|
|
211
|
+
.or(escapeHatchActivated)
|
|
212
|
+
.assertTrue(
|
|
213
|
+
"Sequencer signature not valid and escape hatch not activated"
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Assert correctness of networkState witness
|
|
217
|
+
inputNetworkState
|
|
218
|
+
.hash()
|
|
219
|
+
.assertEquals(networkStateHash, "InputNetworkState witness not valid");
|
|
220
|
+
outputNetworkState
|
|
221
|
+
.hash()
|
|
222
|
+
.assertEquals(
|
|
223
|
+
blockProof.publicOutput.networkStateHash,
|
|
224
|
+
"OutputNetworkState witness not valid"
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
blockProof.publicOutput.closed.assertEquals(
|
|
228
|
+
Bool(true),
|
|
229
|
+
"Supplied proof is not a closed BlockProof"
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Execute onSettlementHooks for additional checks
|
|
233
|
+
const stateRecord: SettlementStateRecord = {
|
|
234
|
+
blockHashRoot,
|
|
235
|
+
stateRoot,
|
|
236
|
+
networkStateHash,
|
|
237
|
+
honoredMessagesHash,
|
|
238
|
+
lastSettlementL1Block,
|
|
239
|
+
promisedMessagesHash,
|
|
240
|
+
sequencerKey,
|
|
241
|
+
};
|
|
242
|
+
const inputs: SettlementHookInputs = {
|
|
243
|
+
blockProof,
|
|
244
|
+
contractState: stateRecord,
|
|
245
|
+
newPromisedMessagesHash,
|
|
246
|
+
fromNetworkState: inputNetworkState,
|
|
247
|
+
toNetworkState: outputNetworkState,
|
|
248
|
+
currentL1Block: minBlockIncluded,
|
|
249
|
+
};
|
|
250
|
+
this.hooks.forEach((hook) => {
|
|
251
|
+
hook.beforeSettlement(this, inputs);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Apply blockProof
|
|
255
|
+
stateRoot.assertEquals(
|
|
256
|
+
blockProof.publicInput.stateRoot,
|
|
257
|
+
"Input state root not matching"
|
|
258
|
+
);
|
|
259
|
+
networkStateHash.assertEquals(
|
|
260
|
+
blockProof.publicInput.networkStateHash,
|
|
261
|
+
"Input networkStateHash not matching"
|
|
262
|
+
);
|
|
263
|
+
blockHashRoot.assertEquals(
|
|
264
|
+
blockProof.publicInput.blockHashRoot,
|
|
265
|
+
"Input blockHashRoot not matching"
|
|
266
|
+
);
|
|
267
|
+
this.stateRoot.set(blockProof.publicOutput.stateRoot);
|
|
268
|
+
this.networkStateHash.set(blockProof.publicOutput.networkStateHash);
|
|
269
|
+
this.blockHashRoot.set(blockProof.publicOutput.blockHashRoot);
|
|
270
|
+
|
|
271
|
+
// Assert and apply deposit commitments
|
|
272
|
+
promisedMessagesHash.assertEquals(
|
|
273
|
+
blockProof.publicOutput.incomingMessagesHash,
|
|
274
|
+
"Promised messages not honored"
|
|
275
|
+
);
|
|
276
|
+
this.honoredMessagesHash.set(promisedMessagesHash);
|
|
277
|
+
|
|
278
|
+
// Assert and apply new promisedMessagesHash
|
|
279
|
+
this.self.account.actionState.assertEquals(newPromisedMessagesHash);
|
|
280
|
+
this.promisedMessagesHash.set(newPromisedMessagesHash);
|
|
281
|
+
|
|
282
|
+
this.lastSettlementL1Block.set(minBlockIncluded);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private dispatchMessage<Type>(
|
|
286
|
+
methodId: Field,
|
|
287
|
+
value: Type,
|
|
288
|
+
valueType: ProvableExtended<Type>
|
|
289
|
+
) {
|
|
290
|
+
const args = valueType.toFields(value);
|
|
291
|
+
// Should be the same as RuntimeTransaction.hash
|
|
292
|
+
const argsHash = Poseidon.hash(args);
|
|
293
|
+
const runtimeTransaction = RuntimeTransaction.fromMessage({
|
|
294
|
+
methodId,
|
|
295
|
+
argsHash,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Append tx to incomingMessagesHash
|
|
299
|
+
const actionData = runtimeTransaction.hashData();
|
|
300
|
+
const actionHash = MinaActions.actionHash(actionData);
|
|
301
|
+
|
|
302
|
+
this.self.body.actions = {
|
|
303
|
+
hash: actionHash,
|
|
304
|
+
data: [actionData],
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const eventHash = MinaEvents.eventHash(args);
|
|
308
|
+
this.self.body.events = {
|
|
309
|
+
hash: eventHash,
|
|
310
|
+
data: [args],
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@method
|
|
315
|
+
public deposit(amount: UInt64) {
|
|
316
|
+
// Save this, since otherwise it would be a second witness later,
|
|
317
|
+
// which could be a different values than the first
|
|
318
|
+
const sender = this.sender;
|
|
319
|
+
|
|
320
|
+
// Credit the amount to the bridge contract
|
|
321
|
+
this.self.balance.addInPlace(amount);
|
|
322
|
+
|
|
323
|
+
const action = new Deposit({
|
|
324
|
+
address: sender,
|
|
325
|
+
amount,
|
|
326
|
+
});
|
|
327
|
+
const methodId = Field(
|
|
328
|
+
this.methodIdMappings[this.incomingMessagesPaths["deposit"]]
|
|
329
|
+
);
|
|
330
|
+
this.dispatchMessage(methodId.toConstant(), action, Deposit);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@method
|
|
334
|
+
public rollupOutgoingMessages(batch: OutgoingMessageArgumentBatch) {
|
|
335
|
+
let counter = this.outgoingMessageCursor.getAndAssertEquals();
|
|
336
|
+
const stateRoot = this.stateRoot.getAndAssertEquals();
|
|
337
|
+
|
|
338
|
+
const [withdrawalModule, withdrawalStateName] = this.withdrawalStatePath;
|
|
339
|
+
const mapPath = Path.fromProperty(withdrawalModule, withdrawalStateName);
|
|
340
|
+
|
|
341
|
+
let accountCreationFeePaid = Field(0);
|
|
342
|
+
|
|
343
|
+
for (let i = 0; i < OUTGOING_MESSAGE_BATCH_SIZE; i++) {
|
|
344
|
+
const args = batch.arguments[i];
|
|
345
|
+
|
|
346
|
+
// Check witness
|
|
347
|
+
const path = Path.fromKey(mapPath, Field, counter);
|
|
348
|
+
|
|
349
|
+
args.witness
|
|
350
|
+
.checkMembership(
|
|
351
|
+
stateRoot,
|
|
352
|
+
path,
|
|
353
|
+
Poseidon.hash(Withdrawal.toFields(args.value))
|
|
354
|
+
)
|
|
355
|
+
.assertTrue("Provided Withdrawal witness not valid");
|
|
356
|
+
|
|
357
|
+
// Process message
|
|
358
|
+
const { address, amount } = args.value;
|
|
359
|
+
const isDummy = address.equals(this.address);
|
|
360
|
+
|
|
361
|
+
const tokenAu = this.token.mint({ address, amount });
|
|
362
|
+
const isNewAccount = tokenAu.account.isNew.getAndAssertEquals();
|
|
363
|
+
tokenAu.body.balanceChange.magnitude =
|
|
364
|
+
tokenAu.body.balanceChange.magnitude.sub(
|
|
365
|
+
Provable.if(
|
|
366
|
+
isNewAccount,
|
|
367
|
+
Mina.accountCreationFee().toConstant(),
|
|
368
|
+
UInt64.zero
|
|
369
|
+
)
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
accountCreationFeePaid = accountCreationFeePaid.add(
|
|
373
|
+
Provable.if(isNewAccount, Field(1e9), Field(0))
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
counter = counter.add(Provable.if(isDummy, Field(0), Field(1)));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.balance.subInPlace(UInt64.from(accountCreationFeePaid));
|
|
380
|
+
|
|
381
|
+
this.outgoingMessageCursor.set(counter);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
@method
|
|
385
|
+
public redeem(additionUpdate: AccountUpdate) {
|
|
386
|
+
additionUpdate.body.tokenId.assertEquals(
|
|
387
|
+
TokenId.default,
|
|
388
|
+
"Tokenid not default token"
|
|
389
|
+
);
|
|
390
|
+
additionUpdate.body.balanceChange.sgn
|
|
391
|
+
.isPositive()
|
|
392
|
+
.assertTrue("Sign not correct");
|
|
393
|
+
const amount = additionUpdate.body.balanceChange.magnitude;
|
|
394
|
+
|
|
395
|
+
// Burn tokens
|
|
396
|
+
this.token.burn({
|
|
397
|
+
address: additionUpdate.publicKey,
|
|
398
|
+
amount,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Send mina
|
|
402
|
+
this.approve(additionUpdate);
|
|
403
|
+
this.balance.subInPlace(amount);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export interface SettlementContractModuleConfig {
|
|
408
|
+
withdrawalStatePath: `${string}.${string}`;
|
|
409
|
+
withdrawalMethodPath: `${string}.${string}`;
|
|
410
|
+
incomingMessagesMethods: Record<string, `${string}.${string}`>;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@injectable()
|
|
414
|
+
export class SettlementContractModule extends ProtocolModule<SettlementContractModuleConfig> {
|
|
415
|
+
public constructor(
|
|
416
|
+
@injectAll("ProvableSettlementHook")
|
|
417
|
+
private readonly hooks: ProvableSettlementHook<unknown>[],
|
|
418
|
+
@inject("BlockProver")
|
|
419
|
+
private readonly blockProver: BlockProvable
|
|
420
|
+
) {
|
|
421
|
+
super();
|
|
422
|
+
LazyBlockProof.tag = blockProver.zkProgrammable.zkProgram.Proof.tag;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
public getContractClass(): typeof SettlementContract {
|
|
426
|
+
return SettlementContract;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
public createContract(
|
|
430
|
+
address: PublicKey,
|
|
431
|
+
methodIdMappings: SettlementMethodIdMapping
|
|
432
|
+
): SettlementContract {
|
|
433
|
+
// We know that this returns [string, string], but TS can't infer that
|
|
434
|
+
const withdrawalPath = this.config.withdrawalStatePath.split(".");
|
|
435
|
+
|
|
436
|
+
return new SettlementContract(
|
|
437
|
+
address,
|
|
438
|
+
methodIdMappings,
|
|
439
|
+
this.hooks,
|
|
440
|
+
[withdrawalPath[0], withdrawalPath[1]],
|
|
441
|
+
this.config.incomingMessagesMethods
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { UInt64 } from "o1js";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ProvableSettlementHook,
|
|
5
|
+
SettlementHookInputs,
|
|
6
|
+
} from "../ProvableSettlementHook";
|
|
7
|
+
import { SettlementContract } from "../SettlementContract";
|
|
8
|
+
|
|
9
|
+
type NetworkStateSettlementModuleConfig = {
|
|
10
|
+
blocksPerL1Block: UInt64;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export class NetworkStateSettlementModule extends ProvableSettlementHook<NetworkStateSettlementModuleConfig> {
|
|
14
|
+
public beforeSettlement(
|
|
15
|
+
smartContract: SettlementContract,
|
|
16
|
+
{
|
|
17
|
+
blockProof,
|
|
18
|
+
fromNetworkState,
|
|
19
|
+
toNetworkState,
|
|
20
|
+
contractState,
|
|
21
|
+
currentL1Block,
|
|
22
|
+
}: SettlementHookInputs
|
|
23
|
+
): void {
|
|
24
|
+
const { lastSettlementL1Block } = contractState;
|
|
25
|
+
|
|
26
|
+
const blocksPerL1Block = this.config.blocksPerL1Block.toConstant();
|
|
27
|
+
|
|
28
|
+
const numL1Blocks = currentL1Block.sub(lastSettlementL1Block);
|
|
29
|
+
const expectedHeightDiff = numL1Blocks.toUInt64().mul(blocksPerL1Block);
|
|
30
|
+
|
|
31
|
+
const actualHeightDiff = toNetworkState.block.height.sub(
|
|
32
|
+
fromNetworkState.block.height
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const acceptableDerivation = numL1Blocks.mul(1).div(10);
|
|
36
|
+
|
|
37
|
+
// TODO Check within bounds efficiently
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bool } from "o1js";
|
|
1
|
+
import { Bool, Provable, Struct } from "o1js";
|
|
2
2
|
import { singleton } from "tsyringe";
|
|
3
3
|
import {
|
|
4
4
|
ProvableMethodExecutionContext,
|
|
@@ -29,6 +29,11 @@ export interface RuntimeMethodExecutionData {
|
|
|
29
29
|
networkState: NetworkState;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export class RuntimeMethodExecutionDataStruct extends Struct({
|
|
33
|
+
transaction: RuntimeTransaction,
|
|
34
|
+
networkState: NetworkState,
|
|
35
|
+
}) {}
|
|
36
|
+
|
|
32
37
|
/**
|
|
33
38
|
* Execution context used to wrap runtime module methods,
|
|
34
39
|
* allowing them to post relevant information (such as execution status)
|
|
@@ -93,6 +98,18 @@ export class RuntimeMethodExecutionContext extends ProvableMethodExecutionContex
|
|
|
93
98
|
this.input = input;
|
|
94
99
|
}
|
|
95
100
|
|
|
101
|
+
public witnessInput(): RuntimeMethodExecutionDataStruct {
|
|
102
|
+
this.assertSetupCalled();
|
|
103
|
+
return Provable.witness(RuntimeMethodExecutionDataStruct, () => {
|
|
104
|
+
// TODO Is that right? Or this.current().input
|
|
105
|
+
const { transaction, networkState } = this.input!;
|
|
106
|
+
return new RuntimeMethodExecutionDataStruct({
|
|
107
|
+
networkState,
|
|
108
|
+
transaction,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
public setSimulated(simulated: boolean) {
|
|
97
114
|
this.isSimulated = simulated;
|
|
98
115
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Field, FlexibleProvablePure, Poseidon } from "o1js";
|
|
2
|
+
import { hashWithPrefix, prefixToField } from "@proto-kit/common";
|
|
3
|
+
|
|
4
|
+
import { ProvableHashList } from "./ProvableHashList";
|
|
5
|
+
|
|
6
|
+
function salt(prefix: string) {
|
|
7
|
+
return Poseidon.update(
|
|
8
|
+
[Field(0), Field(0), Field(0)],
|
|
9
|
+
[prefixToField(prefix)]
|
|
10
|
+
) as [Field, Field, Field];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const MINA_EVENT_PREFIXES = {
|
|
14
|
+
event: "MinaZkappEvent******",
|
|
15
|
+
events: "MinaZkappEvents*****",
|
|
16
|
+
sequenceEvents: "MinaZkappSeqEvents**",
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
export function emptyActions(): Field {
|
|
20
|
+
return salt("MinaZkappActionsEmpty")[0];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function emptyEvents(): Field {
|
|
24
|
+
return salt("MinaZkappEventsEmpty")[0];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class MinaActions {
|
|
28
|
+
static actionHash(
|
|
29
|
+
action: Field[],
|
|
30
|
+
previousHash: Field = emptyActions()
|
|
31
|
+
): Field {
|
|
32
|
+
const actionDataHash = hashWithPrefix(MINA_EVENT_PREFIXES.event, action);
|
|
33
|
+
return hashWithPrefix(MINA_EVENT_PREFIXES.sequenceEvents, [
|
|
34
|
+
previousHash,
|
|
35
|
+
actionDataHash,
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class MinaEvents {
|
|
41
|
+
static eventHash(event: Field[], previousHash: Field = emptyEvents()): Field {
|
|
42
|
+
const actionDataHash = hashWithPrefix(MINA_EVENT_PREFIXES.event, event);
|
|
43
|
+
return hashWithPrefix(MINA_EVENT_PREFIXES.events, [
|
|
44
|
+
previousHash,
|
|
45
|
+
actionDataHash,
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class MinaPrefixedProvableHashList<
|
|
51
|
+
Value
|
|
52
|
+
> extends ProvableHashList<Value> {
|
|
53
|
+
public constructor(
|
|
54
|
+
valueType: FlexibleProvablePure<Value>,
|
|
55
|
+
public readonly prefix: string,
|
|
56
|
+
internalCommitment: Field = Field(0)
|
|
57
|
+
) {
|
|
58
|
+
super(valueType, internalCommitment);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected hash(elements: Field[]): Field {
|
|
62
|
+
const init = salt(this.prefix);
|
|
63
|
+
const digest = Poseidon.update(init, elements);
|
|
64
|
+
return digest[0];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class MinaActionsHashList extends MinaPrefixedProvableHashList<Field> {
|
|
69
|
+
public constructor(internalCommitment: Field = Field(0)) {
|
|
70
|
+
super(Field, MINA_EVENT_PREFIXES.sequenceEvents, internalCommitment);
|
|
71
|
+
}
|
|
72
|
+
}
|
package/test/BlockProver.test.ts
CHANGED
|
@@ -22,13 +22,12 @@ import { UnsignedTransaction } from "@proto-kit/sequencer";
|
|
|
22
22
|
import { AccountStateModule } from "../src/blockmodules/AccountStateModule";
|
|
23
23
|
import { container } from "tsyringe";
|
|
24
24
|
import {
|
|
25
|
-
BlockModule,
|
|
26
25
|
DefaultProvableHashList,
|
|
27
26
|
MethodPublicOutput,
|
|
28
27
|
NetworkState,
|
|
29
28
|
Protocol,
|
|
30
29
|
ProtocolMethodExecutionContext,
|
|
31
|
-
|
|
30
|
+
SignedTransaction,
|
|
32
31
|
ProvableStateTransition,
|
|
33
32
|
RuntimeTransaction,
|
|
34
33
|
StateTransitionProver,
|
|
@@ -101,7 +100,7 @@ describe("blockProver", () => {
|
|
|
101
100
|
fromStateRoot: Field,
|
|
102
101
|
toStateRoot: Field,
|
|
103
102
|
protocolHash: Field,
|
|
104
|
-
tx:
|
|
103
|
+
tx: SignedTransaction,
|
|
105
104
|
networkState: NetworkState
|
|
106
105
|
): BlockProverProofPair {
|
|
107
106
|
const transactionHash =
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Bool, Field, PublicKey, Signature, Struct, UInt64 } from "o1js";
|
|
2
|
-
|
|
3
|
-
export class ProtocolTransaction extends Struct({
|
|
4
|
-
methodId: Field,
|
|
5
|
-
nonce: UInt64,
|
|
6
|
-
sender: PublicKey,
|
|
7
|
-
argsHash: Field,
|
|
8
|
-
signature: Signature,
|
|
9
|
-
}) {
|
|
10
|
-
public static getSignatureData(args: {
|
|
11
|
-
methodId: Field;
|
|
12
|
-
nonce: UInt64;
|
|
13
|
-
argsHash: Field;
|
|
14
|
-
}): Field[] {
|
|
15
|
-
return [args.methodId, ...args.nonce.toFields(), args.argsHash];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public getSignatureData(): Field[] {
|
|
19
|
-
return ProtocolTransaction.getSignatureData(this);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public validateSignature(): Bool {
|
|
23
|
-
return this.signature.verify(this.sender, this.getSignatureData());
|
|
24
|
-
}
|
|
25
|
-
}
|