Skip to content

Commit

Permalink
replace Truffle (and Web3 in batcher) with Ethers (#304)
Browse files Browse the repository at this point in the history
* Add log statement

* Checkpoint

* Add block number cache

* checkpoint

* checkpoint

* fix batcher logic more

* Fix some typos and old comments

* more small fixes

* checkpoint

* Fix merge

* fix remaining todo

* Fix batcher middleware configuration

* deduplicate ethers tx send logic
  • Loading branch information
SebastienGllmt authored Mar 14, 2024
1 parent 614eb59 commit fb82de7
Show file tree
Hide file tree
Showing 40 changed files with 2,789 additions and 3,959 deletions.
5,698 changes: 2,338 additions & 3,360 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions packages/batcher/address-validator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type Web3 from 'web3';
import type { Pool } from 'pg';

import { addressTypeName, GenericRejectionCode, ENV } from '@paima/batcher-utils';
Expand All @@ -15,13 +14,14 @@ import { isSameDay, isSameMinute } from './date-utils.js';
import type { BatchedSubunit } from '@paima/concise';
import { CryptoManager } from '@paima/crypto';
import { createMessageForBatcher } from '@paima/concise';
import { initWeb3, AddressType, getReadNamespaces } from '@paima/utils';
import { AddressType, getReadNamespaces } from '@paima/utils';
import assertNever from 'assert-never';
import { query, getErrorResponse } from '@dcspark/carp-client/client/src/index';
import { query } from '@dcspark/carp-client/client/src/index';
import { Routes } from '@dcspark/carp-client/shared/routes';
import { ethers } from 'ethers';

class PaimaAddressValidator {
private web3: Web3 | undefined;
private web3: ethers.JsonRpcProvider | undefined;
private nodeUrl: string;
private pool: Pool;

Expand All @@ -32,7 +32,7 @@ class PaimaAddressValidator {
}

public initialize = async (): Promise<void> => {
this.web3 = await initWeb3(this.nodeUrl);
this.web3 = new ethers.JsonRpcProvider(this.nodeUrl);
};

public validateUserInput = async (input: BatchedSubunit, height: number): Promise<ErrorCode> => {
Expand Down Expand Up @@ -77,7 +77,7 @@ class PaimaAddressValidator {
case AddressType.EVM: {
if (this.web3 == null)
throw new Error(`[address-validator] web3 not initialized before address validations`);
return await CryptoManager.Evm(this.web3).verifyAddress(address);
return await CryptoManager.Evm().verifyAddress(address);
}
case AddressType.CARDANO:
return await CryptoManager.Cardano().verifyAddress(address);
Expand Down Expand Up @@ -107,7 +107,7 @@ class PaimaAddressValidator {
if (!this.web3) {
throw new Error('[PaimaAddressValidator::verifySignature] web3 not initialized!');
}
return await CryptoManager.Evm(this.web3).verifySignature(
return await CryptoManager.Evm().verifySignature(
input.userAddress,
message,
input.userSignature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fi
if [ ! -x $executable ]; then chmod +x $executable; fi

export ENV_FILE="../.env.${NETWORK:-localhost}";
echo \"ENV FILE: $ENV_FILE\";
echo ENV FILE: $ENV_FILE;
docker compose --env-file $ENV_FILE build

# running a localhost node on your machine isn't exposed to docker which runs in a container
Expand Down
62 changes: 35 additions & 27 deletions packages/batcher/batcher-transaction-poster/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,40 @@ import {
updateStateRejected,
} from '@paima/batcher-db';
import { keepRunning, ENV, gameInputValidatorClosed, webserverClosed } from '@paima/batcher-utils';
import type { TruffleEvmProvider } from '@paima/providers';
import type { EthersEvmProvider } from '@paima/providers';

import { estimateGasLimit } from './gas-limit.js';
import { hashBatchSubunit, buildBatchData } from '@paima/concise';
import { getPaimaL2Contract, wait } from '@paima/utils';
import type { PaimaL2Contract } from '@paima/utils';
import { contractAbis, wait } from '@paima/utils';
import { utf8ToHex } from 'web3-utils';
import { ethers } from 'ethers';

class BatchedTransactionPoster {
private truffleProvider: TruffleEvmProvider;
private provider: EthersEvmProvider;
private contractAddress: string;
private maxSize: number;
private pool: Pool;
private fee: string;
private storage: PaimaL2Contract;

constructor(
truffleProvider: TruffleEvmProvider,
contractAddress: string,
maxSize: number,
pool: Pool
) {
this.truffleProvider = truffleProvider;
private storage: ethers.Contract;

constructor(provider: EthersEvmProvider, contractAddress: string, maxSize: number, pool: Pool) {
this.provider = provider;
this.contractAddress = contractAddress;
this.maxSize = maxSize;
this.pool = pool;
this.fee = ENV.DEFAULT_FEE;
this.storage = getPaimaL2Contract(this.contractAddress, truffleProvider.web3);
// TODO: this isn't a typed version of the contract
// since paima-utils still uses web3 and we haven't migrated to something like viem
this.storage = new ethers.Contract(
contractAddress,
contractAbis.paimaL2ContractBuild.abi,
provider.getConnection().api
);
}

public initialize = async (): Promise<void> => {
try {
this.fee = await this.storage.methods.fee().call();
this.fee = await this.storage.fee();
} catch (err) {
console.log(
'[batcher-transaction-poster] Error while retrieving fee, reverting to default:',
Expand Down Expand Up @@ -75,22 +77,26 @@ class BatchedTransactionPoster {
}
};

public updateWeb3 = (newTruffleProvider: TruffleEvmProvider): void => {
this.truffleProvider = newTruffleProvider;
public updateWeb3 = (newProvider: EthersEvmProvider): void => {
this.provider = newProvider;
};

private postMessage = async (msg: string): Promise<[number, string]> => {
const hexMsg = this.truffleProvider.web3.utils.utf8ToHex(msg);
const tx = {
data: this.storage.methods.paimaSubmitGameInput(hexMsg).encodeABI(),
const hexMsg = utf8ToHex(msg);
// todo: unify with buildDirectTx
const iface = new ethers.Interface([
'function paimaSubmitGameInput(bytes calldata data) payable',
]);
const encodedData = iface.encodeFunctionData('paimaSubmitGameInput', [hexMsg]);
const transaction = await this.provider.sendTransaction({
data: encodedData,
to: this.contractAddress,
from: this.truffleProvider.getAddress().address,
value: this.truffleProvider.web3.utils.numberToHex(this.fee),
gas: estimateGasLimit(msg.length),
};
return await this.truffleProvider.web3.eth
.sendTransaction(tx)
.then(receipt => [receipt.blockNumber, receipt.transactionHash]);
from: this.provider.getAddress().address,
value: '0x' + Number(this.fee).toString(16),
gasLimit: estimateGasLimit(msg.length),
});
const receipt = (await transaction.extra.wait())!;
return [receipt.blockNumber, receipt.hash];
};

// Returns number of input successfully posted, or a negative number on failure.
Expand Down Expand Up @@ -118,6 +124,8 @@ class BatchedTransactionPoster {
blockHeight = postedMessage[0];
transactionHash = postedMessage[1];
} catch (postError) {
console.error('Transaction batch failed. Cleaning up...');
console.error(postError);
await this.rejectPostedStates(hashes);
await this.deletePostedInputs(ids);
return ids.length;
Expand Down
3 changes: 2 additions & 1 deletion packages/batcher/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"dependencies": {
"express": "^4.18.1",
"pg": "^8.11.3"
"pg": "^8.11.3",
"ethers": "6.11.1"
}
}
58 changes: 38 additions & 20 deletions packages/batcher/runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
import type { Pool } from 'pg';

import BatchedTransactionPoster from '@paima/batcher-transaction-poster';
import { server, startServer } from '@paima/batcher-webserver';
import GameInputValidator, {
DefaultInputValidatorCoreInitializator,
EmptyInputValidatorCoreInitializator,
getErrors,
} from '@paima/batcher-game-input-validator';

import type { TruffleEvmProvider } from '@paima/providers';
import {
ENV,
getWalletWeb3AndAddress,
Expand All @@ -20,14 +9,26 @@ import {
VERSION_STRING,
getAndConfirmWeb3,
getInvalidEnvVars,
} from '@paima/batcher-utils';
} from '@paima/batcher-utils'; // load first to load ENV variables
import type { Pool } from 'pg';

import BatchedTransactionPoster from '@paima/batcher-transaction-poster';
import { server, startServer } from '@paima/batcher-webserver';
import GameInputValidator, {
DefaultInputValidatorCoreInitializator,
EmptyInputValidatorCoreInitializator,
getErrors,
} from '@paima/batcher-game-input-validator';

import type { EthersEvmProvider } from '@paima/providers';
import type { ErrorCode, ErrorMessageFxn, GameInputValidatorCore } from '@paima/batcher-utils';

import { initializePool } from './pg/pgPool.js';
import type { BatcherRuntimeInitializer } from './types.js';
import { setLogger } from '@paima/utils';
import * as fs from 'fs';
import { parseSecurityYaml } from '@paima/utils-backend';
import { getRemoteBackendVersion, initMiddlewareCore } from '@paima/mw-core';

setLogger(s => {
try {
Expand Down Expand Up @@ -102,15 +103,28 @@ const BatcherRuntime: BatcherRuntimeInitializer = {
async run(
gameInputValidator: GameInputValidator,
batchedTransactionPoster: BatchedTransactionPoster,
truffleProivder: TruffleEvmProvider
provider: EthersEvmProvider
): Promise<void> {
// pass endpoints to web server and run

// do not await on these as they may run forever
void Promise.all([
startServer(pool, errorCodeToMessage, truffleProivder),
gameInputValidator.run(ENV.GAME_INPUT_VALIDATOR_PERIOD),
batchedTransactionPoster.run(ENV.BATCHED_TRANSACTION_POSTER_PERIOD),
startServer(pool, errorCodeToMessage, provider).catch(e => {
console.log('Uncaught error in startServer');
console.log(e);
throw e;
}),
gameInputValidator.run(ENV.GAME_INPUT_VALIDATOR_PERIOD).catch(e => {
console.log('Uncaught error in gameInputValidator');
console.log(e);
throw e;
}),
,
batchedTransactionPoster.run(ENV.BATCHED_TRANSACTION_POSTER_PERIOD).catch(e => {
console.log('Uncaught error in batchedTransactionPoster');
console.log(e);
throw e;
}),
]);
},
};
Expand Down Expand Up @@ -147,17 +161,21 @@ async function main(): Promise<void> {
}

const pool = initializePool();
const truffleProvider = await getWalletWeb3AndAddress(ENV.CHAIN_URI, privateKey);
await initMiddlewareCore(
'Game Batcher', // TODO: it doesn't matter now, but there is no way for the batcher to get the name of the game
await getRemoteBackendVersion()
);
const provider = await getWalletWeb3AndAddress(ENV.CHAIN_URI, privateKey);

console.log('Chain URI: ', ENV.CHAIN_URI);
console.log('Validation type: ', ENV.GAME_INPUT_VALIDATION_TYPE);
console.log('PaimaL2Contract address:', ENV.CONTRACT_ADDRESS);
console.log('Batcher account address:', truffleProvider.getAddress());
console.log('Batcher account address:', provider.getAddress());

const gameInputValidatorCore = await getValidatorCore(ENV.GAME_INPUT_VALIDATION_TYPE);
const gameInputValidator = new GameInputValidator(gameInputValidatorCore, pool);
const batchedTransactionPoster = new BatchedTransactionPoster(
truffleProvider,
provider,
ENV.CONTRACT_ADDRESS,
ENV.BATCHED_MESSAGE_SIZE_LIMIT,
pool
Expand All @@ -168,7 +186,7 @@ async function main(): Promise<void> {
const runtime = BatcherRuntime.initialize(pool);

requestStart();
await runtime.run(gameInputValidator, batchedTransactionPoster, truffleProvider);
await runtime.run(gameInputValidator, batchedTransactionPoster, provider);
}

void main();
4 changes: 2 additions & 2 deletions packages/batcher/runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { RequestHandler } from 'express';
import type BatchedTransactionPoster from '@paima/batcher-transaction-poster';
import type GameInputValidator from '@paima/batcher-game-input-validator';
import type { Pool } from 'pg';
import type { TruffleEvmProvider } from '@paima/providers';
import type { EthersEvmProvider } from '@paima/providers';

export interface BatcherRuntimeInitializer {
initialize: (pool: Pool) => BatcherRuntime;
Expand All @@ -14,6 +14,6 @@ export interface BatcherRuntime {
run: (
gameInputValidator: GameInputValidator,
BatchedTransactionPoster: BatchedTransactionPoster,
truffleProvider: TruffleEvmProvider
provider: EthersEvmProvider
) => Promise<void>;
}
1 change: 1 addition & 0 deletions packages/batcher/runtime/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
{ "path": "../../paima-sdk/paima-providers/tsconfig.build.json" },
{ "path": "../../paima-sdk/paima-utils/tsconfig.build.json" },
{ "path": "../../node-sdk/paima-utils-backend" },
{ "path": "../../paima-sdk/paima-mw-core/tsconfig.json" },
]
}
2 changes: 0 additions & 2 deletions packages/batcher/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
"build": "tsc"
},
"dependencies": {
"@truffle/hdwallet-provider": "^2.1.15",
"web3": "1.10.0",
"assert-never": "^1.2.1"
}
}
Loading

0 comments on commit fb82de7

Please sign in to comment.