Skip to content

Commit

Permalink
Merge pull request #67 from proto-kit/feature/tx-validation
Browse files Browse the repository at this point in the history
Implement basic version of tx validation
  • Loading branch information
maht0rz authored Nov 2, 2023
2 parents 8d51b8b + cf3dc89 commit 877946d
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 11 deletions.
9 changes: 8 additions & 1 deletion packages/module/src/method/MethodParameterDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ const errors = {

export class MethodParameterDecoder {
public static fromMethod(target: RuntimeModule<unknown>, methodName: string) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const paramtypes = Reflect.getMetadata(
"design:paramtypes",
target,
methodName
);
) as FromFieldClass[] | undefined;

if (paramtypes === undefined) {
throw new Error(
`Method with name ${methodName} doesn't exist on this module`
);
}

return new MethodParameterDecoder(paramtypes);
}
Expand Down
19 changes: 9 additions & 10 deletions packages/sequencer/src/mempool/private/PrivateMempool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,31 @@ import { noop } from "@proto-kit/common";
import type { Mempool, MempoolCommitment } from "../Mempool.js";
import type { PendingTransaction } from "../PendingTransaction.js";
import { SequencerModule } from "../../sequencer/builder/SequencerModule";
import { TransactionValidator } from "../verification/TransactionValidator";

export class PrivateMempool extends SequencerModule<object> implements Mempool {
public commitment: Field;

public constructor(private queue: PendingTransaction[] = []) {
private queue: PendingTransaction[] = [];

public constructor(
private readonly transactionValidator: TransactionValidator
) {
super();
this.commitment = Field(0);
}

public validateTx(tx: PendingTransaction): boolean {
const valid = tx.signature.verify(tx.sender, tx.getSignatureData());

return valid.toBoolean();
}

public add(tx: PendingTransaction): MempoolCommitment {
if (this.validateTx(tx)) {
const [txValid, error] = this.transactionValidator.validateTx(tx);
if (txValid) {
this.queue.push(tx);

// Figure out how to generalize this
this.commitment = Poseidon.hash([this.commitment, tx.hash()]);

return { transactionsHash: this.commitment };
} else {
throw new Error("Validation of tx failed");
}
throw new Error(`Valdiation of tx failed: ${error ?? "unknown error"}`);
}

public getTxs(): {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { inject, injectable } from "tsyringe";
import {
MethodIdResolver,
MethodParameterDecoder,
Runtime,
RuntimeModulesRecord,
} from "@proto-kit/module";

import { PendingTransaction } from "../PendingTransaction";

@injectable()
export class TransactionValidator {
public constructor(
@inject("Runtime") private readonly runtime: Runtime<RuntimeModulesRecord>,
@inject("MethodIdResolver")
private readonly methodIdResolver: MethodIdResolver
) {}

private validateMethod(tx: PendingTransaction): string | undefined {
// Check if method exists

// We don't actually need to use runtime.getMethodById here, bcs the
// module name validation happens inside getMethodNameFromId
// and also in the next step
const methodPath = this.methodIdResolver.getMethodNameFromId(
tx.methodId.toBigInt()
);

if (methodPath === undefined) {
return `Method with id ${tx.methodId} does not exist`;
}

// Check if parameters are decodable
const runtimeModule = this.runtime.resolve(methodPath[0]);
const decoder = MethodParameterDecoder.fromMethod(
runtimeModule,
methodPath[1]
);

// We don't do additional checks for args yet - so the only thing we
// can check is if the Field[]'s length matches
if (tx.args.length !== decoder.fieldSize) {
return "Arguments field length doesn't match required length";
}
return undefined;
}

public validateTx(tx: PendingTransaction): [boolean, string | undefined] {
const methodError = this.validateMethod(tx);

if (methodError !== undefined) {
return [false, methodError];
}

const validSignature = tx.signature.verify(
tx.sender,
tx.getSignatureData()
);

if (!validSignature.toBoolean()) {
return [false, "Signature provided is not valid"];
}

return [true, undefined];
}
}

0 comments on commit 877946d

Please sign in to comment.