Skip to content

Commit 89bd285

Browse files
authored
Merge pull request #410 from proto-kit/fix/bridge-contract-proof
BridgeContract: Remove context usage and add unit test
2 parents c69576a + d4ce180 commit 89bd285

7 files changed

Lines changed: 277 additions & 53 deletions

File tree

packages/library/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from "./math/UInt64";
44
export * from "./math/UInt112";
55
export * from "./math/UInt224";
66
export * from "./protocol/VanillaProtocolModules";
7+
export * from "./protocol/WithdrawalMessageProcessor";
78
export * from "./runtime/Balances";
89
export * from "./runtime/VanillaRuntimeModules";
910
export * from "./runtime/Withdrawals";

packages/module/src/messages/OutgoingMessages.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
Field,
33
FlexibleProvablePure,
44
InferProvable,
5+
Poseidon,
56
Struct,
67
TokenId,
78
} from "o1js";
@@ -112,7 +113,8 @@ export class OutgoingMessages<
112113
const counter = counterOption.orElse(Field(0));
113114

114115
const messageKey = { index: counter, tokenId };
115-
const messageType = prefixToField(key);
116+
// TODO Salt/prefix
117+
const messageType = Poseidon.hash([prefixToField(key)]);
116118

117119
await counterState.set(tokenId, counter.add(1));
118120
await stateMap.set(messageKey, { messageType, value });

packages/protocol/src/settlement/contracts/BridgeContract.ts

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
AccountUpdate,
33
Bool,
4-
Experimental,
54
Field,
65
method,
76
Permissions,
@@ -14,10 +13,11 @@ import {
1413
Struct,
1514
TokenContract,
1615
TokenId,
16+
Unconstrained,
1717
VerificationKey,
1818
} from "o1js";
1919
import { noop, range, TypedClass } from "@proto-kit/common";
20-
import { container, injectable, singleton } from "tsyringe";
20+
import { container } from "tsyringe";
2121

2222
import {
2323
OUTGOING_MESSAGE_BATCH_SIZE,
@@ -61,16 +61,10 @@ export class OutgoingMessageKey extends Struct({
6161
tokenId: Field,
6262
}) {}
6363

64-
@injectable()
65-
@singleton()
66-
export class BridgeContractContext {
67-
public data: {
68-
messageInputs: any[][];
69-
} = { messageInputs: [] };
70-
}
71-
7264
export interface BridgeContractArgs {
73-
SettlementContract: TypedClass<BridgingSettlementContractType> &
65+
SettlementContract: TypedClass<
66+
Pick<BridgingSettlementContractType, "assertStateRoot">
67+
> &
7468
typeof SmartContract;
7569
messageProcessors: OutgoingMessageProcessor<unknown>[];
7670
batchSize?: number;
@@ -171,40 +165,44 @@ export abstract class BridgeContractBase
171165
);
172166
}
173167

174-
private executeProcessors(batchIndex: number, args: OutgoingMessageArgument) {
168+
private executeProcessors(args: OutgoingMessageArgument) {
175169
const { messageProcessors } = this.getInitializationArgs();
176170
return messageProcessors.map((processor, j) => {
177-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
178-
const value = Experimental.memoizeWitness(processor.type, () => {
179-
return container.resolve(BridgeContractContext).data.messageInputs[
180-
batchIndex
181-
][j];
171+
const messageType = processor.getMessageType();
172+
173+
// Create the message struct from unconstrained message argument Field[]
174+
const value = Provable.witness(processor.type, () => {
175+
if (args.messageType.toString() === messageType.toString()) {
176+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
177+
const fieldData = (args.data as Unconstrained<Field[]>).get();
178+
return processor.type.fromFields(fieldData);
179+
} else {
180+
return processor.dummy();
181+
}
182182
});
183183

184184
const MessageType = createMessageStruct(processor.type);
185185
const message = new MessageType({
186-
messageType: args.messageType,
186+
messageType,
187187
value,
188188
});
189+
const result = processor.processMessage(value, {
190+
bridgeContract: {
191+
publicKey: this.address,
192+
tokenId: this.tokenId,
193+
},
194+
});
189195
return {
190-
messageType: args.messageType,
191-
result: processor.processMessage(value, {
192-
bridgeContract: {
193-
publicKey: this.address,
194-
tokenId: this.tokenId,
195-
},
196-
}),
196+
// messageType is authenticated via the hash, which is checked again
197+
messageType,
198+
result,
197199
hash: Poseidon.hash(MessageType.toFields(message)),
198200
};
199201
});
200202
}
201203

202-
public processMessage(
203-
batchIndex: number,
204-
args: OutgoingMessageArgument,
205-
isDummy: Bool
206-
) {
207-
const results = this.executeProcessors(batchIndex, args);
204+
public processMessage(args: OutgoingMessageArgument, isDummy: Bool) {
205+
const results = this.executeProcessors(args);
208206

209207
const maxAccountUpdates = Math.max(
210208
0,
@@ -290,7 +288,7 @@ export abstract class BridgeContractBase
290288

291289
const isDummy = batch.isDummys[i];
292290

293-
const message = this.processMessage(i, args, isDummy);
291+
const message = this.processMessage(args, isDummy);
294292

295293
// Check witness
296294
const path = Path.fromKey(
@@ -302,6 +300,10 @@ export abstract class BridgeContractBase
302300
}
303301
);
304302

303+
Provable.log(message.hash);
304+
Provable.log(path);
305+
Provable.log(stateRoot);
306+
305307
args.witness
306308
.checkMembership(stateRoot, path, message.hash)
307309
.or(isDummy)

packages/protocol/src/settlement/messages/OutgoingMessageArgument.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { Bool, Field, FlexibleProvablePure, Provable, Struct } from "o1js";
1+
import {
2+
Bool,
3+
Field,
4+
FlexibleProvablePure,
5+
Provable,
6+
Struct,
7+
Unconstrained,
8+
} from "o1js";
29
import {
310
LinkedMerkleTree,
411
LinkedMerkleTreeReadWitness,
@@ -19,11 +26,13 @@ export function createMessageStruct<T>(type: FlexibleProvablePure<T>) {
1926
export class OutgoingMessageArgument extends Struct({
2027
witness: LinkedMerkleTreeReadWitness,
2128
messageType: Field,
29+
data: Unconstrained<Field[]>,
2230
}) {
2331
public static dummy(): OutgoingMessageArgument {
2432
return new OutgoingMessageArgument({
2533
witness: LinkedMerkleTree.dummyReadWitness(),
2634
messageType: Field(0),
35+
data: Unconstrained.from([]),
2736
});
2837
}
2938
}

packages/protocol/src/settlement/modularity/OutgoingMessageProcessor.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import {
33
Bool,
44
Field,
55
FlexibleProvablePure,
6+
Poseidon,
67
PublicKey,
78
} from "o1js";
8-
import { implement, NoConfig } from "@proto-kit/common";
9+
import { implement, NoConfig, prefixToField } from "@proto-kit/common";
910

1011
import { ProtocolModule } from "../../protocol/ProtocolModule";
1112

@@ -55,7 +56,19 @@ export abstract class OutgoingMessageProcessor<
5556
};
5657
}
5758

58-
abstract type: FlexibleProvablePure<T>;
59+
public getMessageType(): Field {
60+
// TODO static salt/prefix
61+
// This executes the bigint poseidon behind the scenes, therefore creates a constant
62+
const messageType = Poseidon.hash([prefixToField(this.messageType)]);
63+
if (!messageType.isConstant()) {
64+
throw new Error(
65+
"Underlying poseidon implementation has changed and doesn't create a constant anymore"
66+
);
67+
}
68+
return messageType;
69+
}
70+
71+
abstract type: FlexibleProvablePure<T> & { name: string };
5972

6073
abstract messageType: string;
6174

0 commit comments

Comments
 (0)