Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: arbitrum slowpath processing #6344

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
10 changes: 10 additions & 0 deletions packages/agents/lighthouse/example.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@lighthouseUrl = https://lighthouse-prover-subscriber.mainnet.connext.ninja

@adminToken = foo

POST {{lighthouseUrl}}/clear-cache
Content-Type: application/json

{
"adminToken": "{{adminToken}}"
}
2 changes: 1 addition & 1 deletion packages/agents/lighthouse/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"author": "Connext",
"license": "ISC",
"dependencies": {
"@arbitrum/sdk": "3.1.11",
"@arbitrum/sdk": "4.0.1",
"@connext/nxtp-adapters-database": "workspace:*",
"@connext/nxtp-adapters-relayer": "workspace:*",
"@connext/nxtp-adapters-subgraph": "workspace:*",
Expand Down
12 changes: 7 additions & 5 deletions packages/agents/lighthouse/src/mockable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { getDeployedRootManagerContract as _getDeployedRootManagerContract } fro
import { CrossChainMessenger as _OptimismCrossChainMessenger } from "@eth-optimism/sdk";
import { CrossChainMessenger as _MantleCrossChainMessenger } from "@mantleio/sdk";
import { sendWithRelayerWithBackup as _sendWithRelayerWithBackup } from "@connext/nxtp-adapters-relayer";
import { EventFetcher as _EventFetcher, L2TransactionReceipt as _L2TransactionReceipt } from "@arbitrum/sdk";
import { L1ToL2MessageGasEstimator } from "@arbitrum/sdk/dist/lib/message/L1ToL2MessageGasEstimator";
import { EventFetcher as _EventFetcher, ChildTransactionReceipt as _ChildTransactionReceipt } from "@arbitrum/sdk";
import { ParentToChildMessageGasEstimator } from "@arbitrum/sdk/dist/lib/message/ParentToChildMessageGasEstimator";
import { getBaseFee as _getBaseFee } from "@arbitrum/sdk/dist/lib/utils/lib";
import { LineaSDK as _LineaSDK } from "@consensys/linea-sdk";
import {
Expand Down Expand Up @@ -42,7 +42,7 @@ export const sendWithRelayerWithBackup = _sendWithRelayerWithBackup;

export const EventFetcher = _EventFetcher;

export const L2TransactionReceipt = _L2TransactionReceipt;
export const ChildTransactionReceipt = _ChildTransactionReceipt;

export const RollupUserLogic__factory = _RollupUserLogic__factory;

Expand Down Expand Up @@ -72,8 +72,10 @@ export const getZkSyncWeb3Provider = (url: string): zk.Provider => {
return new zk.Provider(url);
};

export const getL1ToL2MessageGasEstimator = (l2Provider: providers.JsonRpcProvider): L1ToL2MessageGasEstimator => {
return new L1ToL2MessageGasEstimator(l2Provider);
export const getParentToChildMessageGasEstimator = (
l2Provider: providers.JsonRpcProvider,
): ParentToChildMessageGasEstimator => {
return new ParentToChildMessageGasEstimator(l2Provider);
};

export const getContract = (address: string, abi: ContractInterface, provider?: providers.JsonRpcProvider) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createLoggingContext, jsonifyError } from "@connext/nxtp-utils";
import { BigNumber, BigNumberish, utils } from "ethers";
import { l2Networks } from "@arbitrum/sdk/dist/lib/dataEntities/networks";
import { getChildrenForNetwork } from "@arbitrum/sdk";
import { NodeInterface__factory } from "@arbitrum/sdk/dist/lib/abi/factories/NodeInterface__factory";

import { getContext } from "../processFromRoot";
import { ConfirmDataDoesNotMatch, NoRootAvailable, RollUpNodeStaked } from "../errors";
import { EventFetcher, JsonRpcProvider, L2TransactionReceipt, RollupUserLogic__factory } from "../../../mockable";
import { EventFetcher, JsonRpcProvider, ChildTransactionReceipt, RollupUserLogic__factory } from "../../../mockable";
import { ArbitrumNodeCreatedEventsNotFound } from "../../../errors";

import { GetProcessArgsParams } from ".";
Expand Down Expand Up @@ -37,15 +37,15 @@ export const getProcessFromArbitrumRootArgs = async ({
// // get the tx
const spokeJsonProvider = new JsonRpcProvider(spokeProvider);
const tx = await spokeJsonProvider.getTransactionReceipt(sendHash);
const l2TxnReceipt = new L2TransactionReceipt(tx);
const l2TxnReceipt = new ChildTransactionReceipt(tx);
// @ts-ignore
const dataIsOnL1 = await l2TxnReceipt.isDataAvailable(spokeJsonProvider);
if (!dataIsOnL1) {
throw new NoRootAvailable(spokeChainId, hubChainId, requestContext, methodContext);
}
// get the proof
const hubJsonProvider = new JsonRpcProvider(hubProvider);
const [reader] = await l2TxnReceipt.getL2ToL1Messages(hubJsonProvider);
const [reader] = await l2TxnReceipt.getChildToParentMessages(hubJsonProvider);
const msg = (reader as any).nitroReader;
if (!msg?.event) {
throw new Error(`Could not find event for message in ${sendHash}`);
Expand Down Expand Up @@ -78,7 +78,7 @@ export const getProcessFromArbitrumRootArgs = async ({
// 2. (not used) Calculate the send root and the item hash using the `Outbox.sol` interface, then
// find the event emitted after the `ethBlockNum` of the message containing a matching
// sendRoot. Find the nodeNum from this event, and submit to chain (seen below)
const arbNetwork = l2Networks[spokeChainId];
const arbNetwork = getChildrenForNetwork(hubChainId).find((n) => n.chainId === spokeChainId)!;
const latest = await hubJsonProvider.getBlockNumber();
const fetcher = new EventFetcher(hubJsonProvider);
logger.info("Fetching events", requestContext, methodContext, {
Expand All @@ -105,7 +105,7 @@ export const getProcessFromArbitrumRootArgs = async ({
const log = logs[mid];
let sendCount = BigNumber.from(msg.event.position as BigNumberish);
try {
const block = await msg.getBlockFromNodeLog(spokeJsonProvider, log);
const block = await msg.getBlockFromAssertionLog(spokeJsonProvider, log);
sendCount = BigNumber.from(block.sendCount);
} catch (e: unknown) {
logger.warn("Failed to get block from node log", requestContext, methodContext, {
Expand All @@ -116,14 +116,15 @@ export const getProcessFromArbitrumRootArgs = async ({
if (sendCount.gt(msg.event.position as BigNumberish)) {
foundLog = log;
right = mid - 1;
left = right + 1;
} else {
left = mid + 1;
}
}

const earliestNodeWithExit = foundLog.event.nodeNum;
const rollup = RollupUserLogic__factory.getContract(arbNetwork.ethBridge.rollup, RollupUserLogic__factory.abi);
const foundBlock = await msg.getBlockFromNodeNum(
const foundBlock = await msg.getBlockFromAssertionId(
rollup.connect(hubJsonProvider),
earliestNodeWithExit,
spokeJsonProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NoSpokeConnector, NoHubConnector, NoProviderForDomain } from "../errors
import { ExtraPropagateParam } from "../operations/propagate";
import {
getJsonRpcProvider,
getL1ToL2MessageGasEstimator,
getParentToChildMessageGasEstimator,
getBaseFee,
getInterface,
getBestProvider,
Expand Down Expand Up @@ -62,7 +62,7 @@ export const getPropagateParams = async (

try {
const l2Provider = getJsonRpcProvider(l2RpcUrl);
const l1ToL2MessageGasEstimate = getL1ToL2MessageGasEstimator(l2Provider);
const l1ToL2MessageGasEstimate = getParentToChildMessageGasEstimator(l2Provider);

// example encoded payload: 0x4ff746f6000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000207465737400000000000000000000000000000000000000000000000000000000
// length = 200 not including 0x = 100 bytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ import * as Mockable from "../../../../src/mockable";
import { getPropagateParams } from "../../../../src/tasks/propagate/helpers/arbitrum";
import { getInterfaceMock, propagateCtxMock, getBestProviderMock } from "../../../globalTestHook";
import { mock } from "../../../mock";
import { L1ToL2MessageGasEstimator } from "@arbitrum/sdk";
import { ParentToChildMessageGasEstimator } from "@arbitrum/sdk";

const requestContext = createRequestContext("test");

const estimateSubmissionFee = Promise.resolve(constants.One);
const estimateRetryableTicketGasLimit = Promise.resolve(constants.Two);
let l1ToL2: SinonStubbedInstance<L1ToL2MessageGasEstimator>;
let l1ToL2: SinonStubbedInstance<ParentToChildMessageGasEstimator>;

describe("Helpers: Arbitrum ", () => {
beforeEach(() => {
l1ToL2 = createStubInstance(L1ToL2MessageGasEstimator, { estimateSubmissionFee, estimateRetryableTicketGasLimit });
l1ToL2 = createStubInstance(ParentToChildMessageGasEstimator, {
estimateSubmissionFee,
estimateRetryableTicketGasLimit,
});
stub(Mockable, "getJsonRpcProvider").returns(createStubInstance(providers.JsonRpcProvider));
stub(Mockable, "getL1ToL2MessageGasEstimator").returns(l1ToL2);
stub(Mockable, "getParentToChildMessageGasEstimator").returns(l1ToL2);
stub(Mockable, "getBaseFee").resolves(BigNumber.from(1));
getInterfaceMock.returns({ encodeFunctionData: stub().resolves(mkBytes32("0xcalldadta")) });
});
Expand Down
2 changes: 1 addition & 1 deletion packages/deployments/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@eth-optimism/sdk": "3.2.0"
},
"devDependencies": {
"@arbitrum/sdk": "3.1.11",
"@arbitrum/sdk": "4.0.1",
"@certusone/wormhole-sdk": "0.9.21",
"@connext/nxtp-utils": "workspace:*",
"@consensys/linea-sdk": "0.1.6",
Expand Down
13 changes: 12 additions & 1 deletion packages/deployments/contracts/src/cli/op-mode/switchMode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import commandLineArgs from "command-line-args";

import { Wallet } from "ethers";
import { Contract, Wallet } from "ethers";
import { Env, getProviderFromHardhatConfig } from "../..";
import { SUPPORTED_DOMAINS, HUBS, updateIfNeeded } from "../helpers";
import {
Expand Down Expand Up @@ -107,6 +107,17 @@ export const switchMode = async () => {

// Update SpokeConnector
console.log(`\tUpdating SpokeConnector (${deployments.SpokeConnector.address})...`);
const owner = await deployments.SpokeConnector.contract.owner();
const contract = new Contract(
owner,
["function getThreshold() view returns (uint256)"],
deployments.SpokeConnector.contract.provider,
);
let threshold = "N/A";
try {
threshold = await contract.getThreshold();
} catch (e) {}
console.log(`\t\tOwner: ${owner}, threshold: ${threshold}`);
await updateIfNeeded(
optimistic
? {
Expand Down
29 changes: 17 additions & 12 deletions packages/deployments/contracts/tasks/connector/processFromRoot.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { task } from "hardhat/config";
import { EventFetcher, L2TransactionReceipt } from "@arbitrum/sdk";
import { EventFetcher, ChildTransactionReceipt, getChildrenForNetwork } from "@arbitrum/sdk";

import { BigNumber, BigNumberish, constants, Contract, providers, Wallet } from "ethers";
import { defaultAbiCoder, keccak256 } from "ethers/lib/utils";
import { l2Networks } from "@arbitrum/sdk/dist/lib/dataEntities/networks";
import {
DEFAULT_L2_CONTRACT_ADDRESSES,
CrossChainMessenger as OptimismCrossChainMessenger,
Expand Down Expand Up @@ -53,7 +53,7 @@ const processFromPolygonRoot = async (spoke: number, sendHash: string, hubProvid
SEND_MESSAGE_EVENT_SIG,
providerMapping,
);
if (!hash || !payload) {
if (!payload) {
throw new Error(`no hash or payload. already or not yet ready to be processed`);
}
console.log("hash: ", hash);
Expand All @@ -75,16 +75,16 @@ const processFromArbitrumRoot = async (
// // uint256 _index, x
// // L2Message calldata _message x
// // get the tx
const l2TxnReceipt = new L2TransactionReceipt(await spokeProvider.getTransactionReceipt(sendHash));
const l2TxnReceipt = new ChildTransactionReceipt(await spokeProvider.getTransactionReceipt(sendHash));
// @ts-ignore
const dataIsOnL1 = await l2TxnReceipt.isDataAvailable(spokeProvider);
if (!dataIsOnL1) {
throw new Error(`tx data not yet posted to l1`);
}
// get the proof
const [reader] = await l2TxnReceipt.getL2ToL1Messages(hubProvider);
const [reader] = await l2TxnReceipt.getChildToParentMessages(hubProvider);
console.log("msg:", (reader as any).nitroReader.event);
const msg = (reader as any).nitroReader;
const msg = (reader as any).nitroReader; //as nitro.ChildToParentMessageReaderNitro;
if (!msg?.event) {
throw new Error(`Could not find event for message in ${sendHash}`);
}
Expand Down Expand Up @@ -115,13 +115,17 @@ const processFromArbitrumRoot = async (
// 2. Calculate the send root and the item hash using the `Outbox.sol` interface, then
// find the event emitted after the `ethBlockNum` of the message containing a matching
// sendRoot. Find the nodeNum from this event, and submit to chain. Not used.
const arbNetwork = l2Networks[spoke];
const arbNetwork = getChildrenForNetwork(1).find((n) => n.chainId === spoke);
if (!arbNetwork) {
throw new Error(`could not find arbitrum child network for spoke ${spoke}`);
}
const fetcher = new EventFetcher(hubProvider);
console.log("searching for node created events at", arbNetwork.ethBridge.rollup);
const logs = await fetcher.getEvents(RollupUserLogic__factory, (t) => t.filters.NodeCreated(), {
fromBlock: msg.event.ethBlockNum.toNumber(),
toBlock: "latest",
});
console.log("found", logs.length, "logs to sort through. searching for send count >", msg.event.position.toString());
// use binary search to find the first node with sendCount > this.event.position
// default to the last node since we already checked above
let foundLog: FetchedEvent<NodeCreatedEvent> = logs[logs.length - 1];
Expand All @@ -130,11 +134,11 @@ const processFromArbitrumRoot = async (
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const log = logs[mid];
const block = await msg.getBlockFromNodeLog(spokeProvider, log);
const sendCount = BigNumber.from(block.sendCount);
if (sendCount.gt(msg.event.position as BigNumberish)) {
const block = await msg.getBlockFromAssertionLog(spokeProvider, log);
if (BigNumber.from(block.sendCount).gt(msg.event.position as BigNumberish)) {
foundLog = log;
right = mid - 1;
left = right + 1;
} else {
left = mid + 1;
}
Expand All @@ -146,7 +150,7 @@ const processFromArbitrumRoot = async (
RollupUserLogic__factory.abi,
deployer.connect(hubProvider),
);
const foundBlock = await msg.getBlockFromNodeNum(rollup, earliestNodeWithExit, spokeProvider);
const foundBlock = await msg.getBlockFromAssertionId(rollup, earliestNodeWithExit, spokeProvider);
console.log("node:", earliestNodeWithExit.toString());
console.log("msg.position", msg.event.position.toString());
console.log("foundLog:", foundLog);
Expand Down Expand Up @@ -324,7 +328,7 @@ export default task("process-from-root", "Call `Connector.processFromRoot()` to
.addOptionalParam("networkType", "Type of network of contracts")
.setAction(
async ({ env: _env, tx: sendHash, spoke: _spoke, networkType: _networkType }: TaskArgs, { deployments }) => {
const deployer = Wallet.fromMnemonic(process.env.MAINNET_MNEMONIC!);
const deployer = Wallet.fromMnemonic(process.env.MAINNET_MNEMONIC ?? process.env.MNEMONIC!);

const env = mustGetEnv(_env);
const spoke = +_spoke;
Expand Down Expand Up @@ -354,6 +358,7 @@ export default task("process-from-root", "Call `Connector.processFromRoot()` to
args = await processFromOptimismRoot(spoke, sendHash, protocolConfig, l2Provider, l1Provider);
break;
case "Arbitrum":
method = "processMessageFromRoot";
args = await processFromArbitrumRoot(spoke, sendHash, l2Provider, l1Provider, deployer);
break;
case "Polygon":
Expand Down
8 changes: 4 additions & 4 deletions packages/deployments/contracts/tasks/connector/redeem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { L1ToL2MessageStatus, L1TransactionReceipt } from "@arbitrum/sdk";
import { ParentToChildMessageStatus, ParentTransactionReceipt } from "@arbitrum/sdk";
import { providers, Wallet } from "ethers";
import { task } from "hardhat/config";

Expand Down Expand Up @@ -26,16 +26,16 @@ const redeemFromArbitrum = async (
spokeProvider: providers.JsonRpcProvider,
) => {
// Get the message receipt from arbitrum sdk
const receipt = new L1TransactionReceipt(await hubProvider.getTransactionReceipt(hash));
const receipt = new ParentTransactionReceipt(await hubProvider.getTransactionReceipt(hash));
console.log("got l1 -> l2 message receipt on l1", receipt.transactionHash);

// get the mesesage (assume only one arb message in receipt)
const [message] = await receipt.getL1ToL2Messages(signer.connect(spokeProvider));
const [message] = await receipt.getParentToChildMessages(signer.connect(spokeProvider));
const { status } = await message.waitForStatus();
console.log("got l1 -> l2 message", message);

// check to see if it needs redemption
if (status != L1ToL2MessageStatus.FUNDS_DEPOSITED_ON_L2) {
if (status != ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD) {
throw new Error(`Not ready to redeem, or was auto-redeemed. Status: ${status}`);
}

Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/maticjs/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum MaticJsErrorType {
BlockNotIncluded = "no_block_found",
IncorrectTx = "incorrect_transaction",
TxNotCheckpointed = "transaction_not_checkpointed",
NoExitHash = "no_exit_hash",
}

// custom errors for better error handling
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/maticjs/proof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const generateExitPayload = async (
try {
exitHash = await maticClient.exitUtil.getExitHash(burnTxHash, 0, eventSignature);
} catch (error: any) {
throw new InfoError(MaticJsErrorType.TxNotCheckpointed, "Burn transaction has not been checkpointed yet");
throw new InfoError(MaticJsErrorType.NoExitHash, "Could not retrieve the exit hash");
}

// build payload for exit
Expand Down
22 changes: 16 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,16 @@ __metadata:
languageName: node
linkType: hard

"@arbitrum/sdk@npm:3.1.11":
version: 3.1.11
resolution: "@arbitrum/sdk@npm:3.1.11"
"@arbitrum/sdk@npm:4.0.1":
version: 4.0.1
resolution: "@arbitrum/sdk@npm:4.0.1"
dependencies:
"@ethersproject/address": ^5.0.8
"@ethersproject/bignumber": ^5.1.1
"@ethersproject/bytes": ^5.0.8
async-mutex: ^0.4.0
ethers: ^5.1.0
checksum: 79066b07d685755c53f4d7a2862f84c189ab07207d7c4cf8909b540cfbeb07007fec5d7cb9057c2c01df4ee196653cdbe17674bf80eb45bf2fb64f07b8d5f6a2
checksum: 7404c019563538c078f24b9f965d99ca5080435cd681628ac0f7aef2f7777806ec10ffcb3b31ccb9b44d951572ed219cfc1bde8f5a3f0878b7cbd9af4b4b334d
languageName: node
linkType: hard

Expand Down Expand Up @@ -2428,7 +2429,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@connext/lighthouse@workspace:packages/agents/lighthouse"
dependencies:
"@arbitrum/sdk": 3.1.11
"@arbitrum/sdk": 4.0.1
"@connext/nxtp-adapters-database": "workspace:*"
"@connext/nxtp-adapters-relayer": "workspace:*"
"@connext/nxtp-adapters-subgraph": "workspace:*"
Expand Down Expand Up @@ -2946,7 +2947,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@connext/smart-contracts@workspace:packages/deployments/contracts"
dependencies:
"@arbitrum/sdk": 3.1.11
"@arbitrum/sdk": 4.0.1
"@certusone/wormhole-sdk": 0.9.21
"@connext/nxtp-utils": "workspace:*"
"@consensys/linea-sdk": 0.1.6
Expand Down Expand Up @@ -10739,6 +10740,15 @@ __metadata:
languageName: node
linkType: hard

"async-mutex@npm:^0.4.0":
version: 0.4.1
resolution: "async-mutex@npm:0.4.1"
dependencies:
tslib: ^2.4.0
checksum: cb529c0d257c8ff8e5b26c81b36127a255e545edee2c42c76247d533b89a7664037d95f33130f964de49b111ad3324ead8dcccd419c47acdfeb7413ed93063a1
languageName: node
linkType: hard

"async-retry@npm:^1.3.1":
version: 1.3.3
resolution: "async-retry@npm:1.3.3"
Expand Down
Loading