Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./math/UInt64";
export * from "./math/UInt112";
export * from "./math/UInt224";
export * from "./protocol/VanillaProtocolModules";
export * from "./protocol/WithdrawalMessageProcessor";
export * from "./runtime/Balances";
export * from "./runtime/VanillaRuntimeModules";
export * from "./runtime/Withdrawals";
Expand Down
1 change: 1 addition & 0 deletions packages/module/src/messages/OutgoingMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export class OutgoingMessages<
const counter = counterOption.orElse(Field(0));

const messageKey = { index: counter, tokenId };
// TODO Salt/prefix
const messageType = prefixToField(key);

await counterState.set(tokenId, counter.add(1));
Expand Down
72 changes: 41 additions & 31 deletions packages/protocol/src/settlement/contracts/BridgeContract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
AccountUpdate,
Bool,
Experimental,
Field,
method,
Permissions,
Expand All @@ -14,10 +13,11 @@ import {
Struct,
TokenContract,
TokenId,
Unconstrained,
VerificationKey,
} from "o1js";
import { noop, range, TypedClass } from "@proto-kit/common";
import { container, injectable, singleton } from "tsyringe";
import { container } from "tsyringe";

import {
OUTGOING_MESSAGE_BATCH_SIZE,
Expand Down Expand Up @@ -61,16 +61,18 @@ export class OutgoingMessageKey extends Struct({
tokenId: Field,
}) {}

@injectable()
@singleton()
export class BridgeContractContext {
public data: {
messageInputs: any[][];
} = { messageInputs: [] };
}
// @injectable()
// @singleton()
// export class BridgeContractContext {
// public data: {
// messageInputs: any[][];
// } = { messageInputs: [] };
// }

export interface BridgeContractArgs {
SettlementContract: TypedClass<BridgingSettlementContractType> &
SettlementContract: TypedClass<
Pick<BridgingSettlementContractType, "assertStateRoot">
> &
typeof SmartContract;
messageProcessors: OutgoingMessageProcessor<unknown>[];
batchSize?: number;
Expand Down Expand Up @@ -171,40 +173,44 @@ export abstract class BridgeContractBase
);
}

private executeProcessors(batchIndex: number, args: OutgoingMessageArgument) {
private executeProcessors(args: OutgoingMessageArgument) {
const { messageProcessors } = this.getInitializationArgs();
return messageProcessors.map((processor, j) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const value = Experimental.memoizeWitness(processor.type, () => {
return container.resolve(BridgeContractContext).data.messageInputs[
batchIndex
][j];
const messageType = processor.getMessageType();

// Create the message struct from unconstrained message argument Field[]
const value = Provable.witness(processor.type, () => {
if (args.messageType.toString() === messageType.toString()) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const fieldData = (args.data as Unconstrained<Field[]>).get();
return processor.type.fromFields(fieldData);
} else {
return processor.dummy();
}
});

const MessageType = createMessageStruct(processor.type);
const message = new MessageType({
messageType: args.messageType,
messageType,
value,
});
const result = processor.processMessage(value, {
bridgeContract: {
publicKey: this.address,
tokenId: this.tokenId,
},
});
return {
messageType: args.messageType,
result: processor.processMessage(value, {
bridgeContract: {
publicKey: this.address,
tokenId: this.tokenId,
},
}),
// messageType is authenticated via the hash, which is checked again
messageType,
result,
hash: Poseidon.hash(MessageType.toFields(message)),
};
});
}

public processMessage(
batchIndex: number,
args: OutgoingMessageArgument,
isDummy: Bool
) {
const results = this.executeProcessors(batchIndex, args);
public processMessage(args: OutgoingMessageArgument, isDummy: Bool) {
const results = this.executeProcessors(args);

const maxAccountUpdates = Math.max(
0,
Expand Down Expand Up @@ -290,7 +296,7 @@ export abstract class BridgeContractBase

const isDummy = batch.isDummys[i];

const message = this.processMessage(i, args, isDummy);
const message = this.processMessage(args, isDummy);

// Check witness
const path = Path.fromKey(
Expand All @@ -302,6 +308,10 @@ export abstract class BridgeContractBase
}
);

Provable.log(message.hash);
Provable.log(path);
Provable.log(stateRoot);

args.witness
.checkMembership(stateRoot, path, message.hash)
.or(isDummy)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Bool, Field, FlexibleProvablePure, Provable, Struct } from "o1js";
import {
Bool,
Field,
FlexibleProvablePure,
Provable,
Struct,
Unconstrained,
} from "o1js";
import {
LinkedMerkleTree,
LinkedMerkleTreeReadWitness,
Expand All @@ -19,11 +26,13 @@ export function createMessageStruct<T>(type: FlexibleProvablePure<T>) {
export class OutgoingMessageArgument extends Struct({
witness: LinkedMerkleTreeReadWitness,
messageType: Field,
data: Unconstrained<Field[]>,
}) {
public static dummy(): OutgoingMessageArgument {
return new OutgoingMessageArgument({
witness: LinkedMerkleTree.dummyReadWitness(),
messageType: Field(0),
data: Unconstrained.from([]),
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {
Bool,
Field,
FlexibleProvablePure,
Poseidon,
PublicKey,
} from "o1js";
import { implement, NoConfig } from "@proto-kit/common";
import { implement, NoConfig, prefixToField } from "@proto-kit/common";

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

Expand Down Expand Up @@ -55,7 +56,19 @@ export abstract class OutgoingMessageProcessor<
};
}

abstract type: FlexibleProvablePure<T>;
public getMessageType(): Field {
// TODO static salt/prefix
// This executes the bigint poseidon behind the scenes, therefore creates a constant
const messageType = Poseidon.hash([prefixToField(this.messageType)]);
if (!messageType.isConstant()) {
throw new Error(
"Underlying poseidon implementation has changed and doesn't create a constant anymore"
);
}
return messageType;
}

abstract type: FlexibleProvablePure<T> & { name: string };

abstract messageType: string;

Expand Down
Loading
Loading