Skip to content

Commit

Permalink
fix: resolve checks need in lh propose
Browse files Browse the repository at this point in the history
  • Loading branch information
preethamr committed Oct 25, 2023
1 parent 65bcfd0 commit 537214c
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 17 deletions.
20 changes: 19 additions & 1 deletion packages/adapters/database/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,17 @@ export const getCurrentProposedSnapshot = async (
return snapshot ? convertFromDbSnapshot(snapshot) : undefined;
};

export const getCurrentPropagatedSnapshot = async (
_pool?: Pool | db.TxnClientForRepeatableRead,
): Promise<Snapshot | undefined> => {
const poolToUse = _pool ?? pool;

const snapshot = await db
.selectOne("snapshots", { status: "Propagated" }, { limit: 1, order: { by: "id", direction: "DESC" } })
.run(poolToUse);
return snapshot ? convertFromDbSnapshot(snapshot) : undefined;
};

export const getCurrentProposedOptimisticRoot = async (
domain: string,
_pool?: Pool | db.TxnClientForRepeatableRead,
Expand Down Expand Up @@ -1106,10 +1117,17 @@ export const saveFinalizedRoots = async (

await Promise.all(
roots.map(async (root) => {
await db.update("snapshots", { status: "Finalized" }, { aggregate_root: root.aggregateRoot }).run(poolToUse);
await db
.update(
"snapshots",
{ status: "Finalized" },
{ aggregate_root: root.aggregateRoot, propagate_timestamp: root.timestamp },
)
.run(poolToUse);
}),
);
};

export const saveProposedSpokeRoots = async (
_roots: SpokeOptimisticRoot[],
_pool?: Pool | db.TxnClientForRepeatableRead,
Expand Down
4 changes: 4 additions & 0 deletions packages/adapters/database/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
getLatestAggregateRoots,
getPendingAggregateRoot,
getCurrentProposedSnapshot,
getCurrentPropagatedSnapshot,
getLatestPendingSnapshotRootByDomain,
getMessageRootAggregatedFromIndex,
getMessageRootsFromIndex,
Expand Down Expand Up @@ -216,6 +217,7 @@ export type Database = {
getAggregateRoots: (count: number, _pool?: Pool | TxnClientForRepeatableRead) => Promise<string[]>;
getBaseAggregateRoot: (_pool?: Pool | TxnClientForRepeatableRead) => Promise<string | undefined>;
getCurrentProposedSnapshot: (_pool?: Pool | TxnClientForRepeatableRead) => Promise<Snapshot | undefined>;
getCurrentPropagatedSnapshot: (_pool?: Pool | TxnClientForRepeatableRead) => Promise<Snapshot | undefined>;
getMessageRootIndex: (
domain: string,
messageRoot: string,
Expand Down Expand Up @@ -409,6 +411,7 @@ export const getDatabase = async (databaseUrl: string, logger: Logger): Promise<
getLatestAggregateRoots,
getPendingAggregateRoot,
getCurrentProposedSnapshot,
getCurrentPropagatedSnapshot,
getLatestPendingSnapshotRootByDomain,
getMessageRootAggregatedFromIndex,
getMessageRootsFromIndex,
Expand Down Expand Up @@ -501,6 +504,7 @@ export const getDatabaseAndPool = async (
getLatestAggregateRoots,
getPendingAggregateRoot,
getCurrentProposedSnapshot,
getCurrentPropagatedSnapshot,
getLatestPendingSnapshotRootByDomain,
getMessageRootAggregatedFromIndex,
getMessageRootsFromIndex,
Expand Down
1 change: 1 addition & 0 deletions packages/adapters/database/test/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const mockDatabase = (): Database => {
savePropagatedOptimisticRoots: stub().resolves(),
saveSnapshotRoots: stub().resolves(),
getCurrentProposedSnapshot: stub().resolves(),
getCurrentPropagatedSnapshot: stub().resolves(),
getBaseAggregateRoot: stub().resolves(),
getAggregateRoots: stub().resolves(),
getPendingAggregateRoot: stub().resolves(),
Expand Down
18 changes: 18 additions & 0 deletions packages/adapters/txservice/src/shared/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,17 @@ export const getDeployedMerkleRootManagerContract = (
return contract ? { address: contract.address, abi: contract.abi } : undefined;
};

export const getDeployedMerkleTreeManagerRootContract = (
chainId: number,
postfix: ContractPostfix = "",
): { address: string; abi: any } | undefined => {
const record = _getContractDeployments()[chainId.toString()] ?? {};
const contract = record[0]?.contracts
? record[0]?.contracts[`MerkleTreeManagerRootUpgradeBeaconProxy${postfix}`]
: undefined;
return contract ? { address: contract.address, abi: contract.abi } : undefined;
};

export const getDeployedHubConnecterContract = (
chainId: number,
prefix: string,
Expand Down Expand Up @@ -212,6 +223,11 @@ export type MerkleTreeManagerDeploymentGetter = (
postfix?: ContractPostfix,
) => { address: string; abi: any } | undefined;

export type RootMerkleTreeManagerDeploymentGetter = (
chainId: number,
postfix?: ContractPostfix,
) => { address: string; abi: any } | undefined;

export type AmbDeploymentGetter = (
chainId: number,
prefix: string,
Expand Down Expand Up @@ -239,6 +255,7 @@ export type ConnextContractDeployments = {
stableSwap: ConnextContractDeploymentGetter;
spokeConnector: SpokeConnectorDeploymentGetter;
spokeMerkleTreeManager: MerkleTreeManagerDeploymentGetter;
rootMerkleTreeManager: RootMerkleTreeManagerDeploymentGetter;
hubConnector: HubConnectorDeploymentGetter;
multisend: MultisendContractDeploymentGetter;
unwrapper: UnwrapperContractDeploymentGetter;
Expand All @@ -252,6 +269,7 @@ export const contractDeployments: ConnextContractDeployments = {
stableSwap: getDeployedStableSwapContract,
spokeConnector: getDeployedSpokeConnecterContract,
spokeMerkleTreeManager: getDeployedMerkleRootManagerContract,
rootMerkleTreeManager: getDeployedMerkleTreeManagerRootContract,
hubConnector: getDeployedHubConnecterContract,
multisend: getDeployedMultisendContract,
unwrapper: getDeployedUnwrapperContract,
Expand Down
11 changes: 11 additions & 0 deletions packages/agents/lighthouse/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const TChainConfig = Type.Object({
providers: Type.Array(Type.String()),
deployments: Type.Object({
spokeMerkleTree: TAddress,
hubMerkleTree: TAddress,
spokeConnector: TAddress,
relayerProxy: TAddress,
rootManager: TAddress,
Expand Down Expand Up @@ -289,6 +290,16 @@ export const getEnvConfig = (
return res.address;
})(),

hubMerkleTree:
chainConfig.deployments?.hubMerkleTree ??
(() => {
const res = chainDataForChain ? deployments.rootMerkleTreeManager(hubChain, contractPostfix) : undefined;

if (!res) {
throw new Error(`No hub MerkleTreeManager contract address for domain ${hubChain}`);
}
return res.address;
})(),
relayerProxy:
chainConfig.deployments?.relayerProxy ??
(() => {
Expand Down
33 changes: 30 additions & 3 deletions packages/agents/lighthouse/src/tasks/propose/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ export class NoSpokeConnector extends NxtpError {
});
}
}
export class NoMerkleTreeAddress extends NxtpError {
constructor(
public readonly hubDomain: string,
public readonly requestContext: RequestContext,
public readonly methodContext: MethodContext,
public readonly context: any = {},
) {
super(`No spokeconnector for domain ${hubDomain}`, {
...context,
requestContext,
methodContext,
});
}
}

export class MissingRequiredDomain extends NxtpError {
constructor(
Expand Down Expand Up @@ -60,14 +74,27 @@ export class NoSnapshotRoot extends NxtpError {
}
}

export class NoSpokeOptimisticRoot extends NxtpError {
export class LatestPropagatedSnapshot extends NxtpError {
constructor(
public readonly domain: string,
public readonly requestContext: RequestContext,
public readonly methodContext: MethodContext,
public readonly context: any = {},
) {
super(`SpokeOptimisticRoot not available for domain ${domain}`, {
super(`Latest propagated snapshot not available for hub domain`, {
...context,
requestContext,
methodContext,
});
}
}
export class NoRootTimestamp extends NxtpError {
constructor(
public readonly aggregateRoot: string,
public readonly requestContext: RequestContext,
public readonly methodContext: MethodContext,
public readonly context: any = {},
) {
super(`No propagated_timestamp available for snapshot with aggregate root ${aggregateRoot}`, {
...context,
requestContext,
methodContext,
Expand Down
61 changes: 57 additions & 4 deletions packages/agents/lighthouse/src/tasks/propose/operations/propose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import { BigNumber } from "ethers";

import { NoBaseAggregateRootCount, NoBaseAggregateRoot } from "../../../errors";
import { sendWithRelayerWithBackup } from "../../../mockable";
import { NoChainIdForDomain, MissingRequiredDomain, NoSnapshotRoot, NoSpokeConnector } from "../errors";
import {
NoChainIdForDomain,
MissingRequiredDomain,
NoSnapshotRoot,
NoSpokeConnector,
NoMerkleTreeAddress,
} from "../errors";
import { getContext } from "../propose";
import { OptimisticHubDBHelper } from "../adapters";

Expand All @@ -20,6 +26,7 @@ export type ExtraPropagateParam = {
_fee: string;
_encodedData: string;
};
const EMPTY_ROOT = "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757";

export const proposeHub = async () => {
const {
Expand Down Expand Up @@ -127,14 +134,60 @@ export const proposeSnapshot = async (
const hubChainId = domainToChainId(+config.hubDomain);
// const _totalFee = constants.Zero;

const baseAggregateRoot = await database.getBaseAggregateRoot();
let baseAggregateRoot = await database.getBaseAggregateRoot();

// If this is the first snapshot, there will be no base aggregate root.
let baseAggregateRootCount: number | undefined;
if (!baseAggregateRoot) {
const rootMerkleTreeAddress = config.chains[config.hubDomain]?.deployments.hubMerkleTree;
if (!rootMerkleTreeAddress) {
throw new NoMerkleTreeAddress(config.hubDomain, requestContext, methodContext);
}

let root: string;
let count: BigNumber;
const encodedData = contracts.merkleTreeManager.encodeFunctionData("rootAndCount");
try {
const idResultData = await chainreader.readTx({
domain: +config.hubDomain,
to: rootMerkleTreeAddress,
data: encodedData,
});

[root, count] = contracts.merkleTreeManager.decodeFunctionResult("rootAndCount", idResultData);
} catch (err: unknown) {
logger.error(
"Failed to read the latest aggregate root and count from onchain",
requestContext,
methodContext,
jsonifyError(err as NxtpError),
);
// Cannot proceed without the latest snapshot ID.
return;
}
logger.info("Got the latest aggregate root and count from onchain", requestContext, methodContext, {
root,
count,
});

if (root === EMPTY_ROOT && count.isZero()) {
baseAggregateRoot = root;
baseAggregateRootCount = 0;
logger.info("Found EMPTY_ROOT from onchain", requestContext, methodContext, {
baseAggregateRoot,
baseAggregateRootCount,
});
}
}

if (baseAggregateRoot === undefined) {
throw new NoBaseAggregateRoot();
}

const baseAggregateRootCount = await database.getBaseAggregateRootCount(baseAggregateRoot);
if (!baseAggregateRootCount) {
if (baseAggregateRootCount === undefined) {
baseAggregateRootCount = await database.getBaseAggregateRootCount(baseAggregateRoot);
}
if (baseAggregateRootCount === undefined) {
throw new NoBaseAggregateRootCount(baseAggregateRoot);
}
const baseAggregateRoots: string[] = await database.getAggregateRoots(baseAggregateRootCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createLoggingContext, NxtpError, RequestContext, jsonifyError, domainTo
import { BigNumber } from "ethers";

import { sendWithRelayerWithBackup } from "../../../mockable";
import { NoChainIdForDomain, NoSpokeOptimisticRoot, NoSpokeConnector } from "../errors";
import { NoChainIdForDomain, LatestPropagatedSnapshot, NoSpokeConnector, NoRootTimestamp } from "../errors";
import { getContext } from "../propose";

export type ExtraPropagateParam = {
Expand All @@ -21,11 +21,17 @@ export const proposeSpoke = async (spokeDomain: string) => {
adapters: { database, contracts, chainreader },
} = getContext();
const { requestContext, methodContext } = createLoggingContext(proposeSpoke.name);
const latestPropagatedSnapshot = await database.getCurrentPropagatedSnapshot();
if (!latestPropagatedSnapshot) {
throw new LatestPropagatedSnapshot(requestContext, methodContext);
}
if (!latestPropagatedSnapshot.propagateTimestamp) {
throw new NoRootTimestamp(latestPropagatedSnapshot.aggregateRoot, requestContext, methodContext);
}

//TODO: V1.1 special handling for when spoke domain is hub domain
if (spokeDomain === config.hubDomain) {
logger.info("Starting propose operation for hub", requestContext, methodContext, spokeDomain);
//TODO: V1.1 check if proposed aggregate root is alraadly saved
await sendRootToHubSpoke(requestContext);
return;
}
Expand Down Expand Up @@ -72,14 +78,9 @@ export const proposeSpoke = async (spokeDomain: string) => {
});

try {
const latestOptimisticRoot = await database.getLatestPendingSpokeOptimisticRootByDomain(spokeDomain);
if (!latestOptimisticRoot) {
throw new NoSpokeOptimisticRoot(spokeDomain, requestContext, methodContext);
}

await proposeOptimisticRoot(
latestOptimisticRoot.aggregateRoot,
latestOptimisticRoot.rootTimestamp,
latestPropagatedSnapshot.aggregateRoot,
latestPropagatedSnapshot.propagateTimestamp,
spokeDomain,
requestContext,
);
Expand Down

0 comments on commit 537214c

Please sign in to comment.