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

feat(db): add support for configurable BTC data source when find best rollback tx #3210

Merged
merged 2 commits into from
Jan 20, 2025
Merged
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
81 changes: 72 additions & 9 deletions crates/rooch/src/commands/db/commands/best_rollback.rs
Original file line number Diff line number Diff line change
@@ -5,16 +5,22 @@ use bitcoin::hashes::Hash;
use clap::Parser;
use rooch_types::da::chunk::chunk_from_segments;
use rooch_types::da::segment::{segment_from_bytes, Segment};
use rooch_types::error::RoochResult;
use rooch_types::error::{RoochError, RoochResult};
use rooch_types::transaction::{LedgerTransaction, LedgerTxData};
use std::collections::HashSet;

#[derive(clap::ValueEnum, Clone, Debug)]
pub enum DataSource {
Mempool,
Blockstream,
}

/// try to find the best tx_order for rollback caused by chain reorg
#[derive(Debug, Parser)]
pub struct BestRollbackCommand {
#[clap(
long = "da-url",
help = "open-da rpc url. e.g., https://storage.googleapis.com/rooch-openda-testnet/7a6b74d2"
help = "open-da rpc url. e.g., https://storage.googleapis.com/rooch-openda-testnet/31e8fe04 for testnet, https://storage.googleapis.com/rooch-openda-main/527d69c3 for mainnet"
)]
pub da_url: String,

@@ -29,6 +35,13 @@ pub struct BestRollbackCommand {
default_value = "16"
)]
pub search_depth: Option<u64>,
#[clap(
long = "data-source",
help = "data source for btc block hash",
default_value = "mempool",
value_enum
)]
pub data_source: DataSource,
#[clap(long = "main", help = "Bitcoin Mainnet or not. default is false")]
pub main: bool,
}
@@ -57,17 +70,35 @@ impl BestRollbackCommand {
let mut best_tx_order = 0;
let mut found_matched_block_hash = false;
let mut found_best_tx_order = false;

let mut last_matched_block_height: u64 = 0;
let mut last_matched_block_hash = String::new();

let mut first_mismatched_block_height = 0;
let mut first_mismatched_block_hash_exp = String::new();
let mut first_mismatched_block_hash_act = String::new();

let data_source = self.data_source.clone();

for da_block_hash in da_hashes.iter() {
let block_height = da_block_hash.block_height;
let rpc_block_hash = get_block_hash_from_btc_rpc(block_height, self.main).await?;
let rpc_block_hash =
get_block_hash_from_btc_rpc(data_source.clone(), block_height, self.main).await?;
if rpc_block_hash != da_block_hash.block_hash {
if found_matched_block_hash {
best_tx_order = da_block_hash.previous_tx_order;
found_best_tx_order = true;
first_mismatched_block_height = block_height;
first_mismatched_block_hash_exp = rpc_block_hash;
first_mismatched_block_hash_act = da_block_hash.block_hash.clone();
break;
}
} else {
found_matched_block_hash = true;
if block_height > last_matched_block_height {
last_matched_block_height = block_height;
last_matched_block_hash = rpc_block_hash;
}
}
}

@@ -80,7 +111,24 @@ impl BestRollbackCommand {
println!("all block hash matched, no need to rollback");
return Ok(());
} else {
if first_mismatched_block_height != last_matched_block_height + 1 {
return Err(RoochError::from(anyhow::anyhow!(
"first mismatched block height is not next to last matched block height"
)));
}

println!("best rollback tx_order: {}", best_tx_order);
println!("--------------- details ------------------");
println!(
"last matched block height: {}, block hash: {}",
last_matched_block_height, last_matched_block_hash
);
println!(
"first mismatched block height: {}, exp block hash: {}, act block hash: {}",
first_mismatched_block_height,
first_mismatched_block_hash_exp,
first_mismatched_block_hash_act
);
}

Ok(())
@@ -166,14 +214,29 @@ async fn get_segment(
segment_from_bytes(&res.bytes().await?)
}

async fn get_block_hash_from_btc_rpc(block_height: u64, main: bool) -> anyhow::Result<String> {
let url = if main {
"https://blockstream.info/api/block-height/"
} else {
"https://blockstream.info/testnet/api/block-height/"
async fn get_block_hash_from_btc_rpc(
data_source: DataSource,
block_height: u64,
main: bool,
) -> anyhow::Result<String> {
let base_url = match data_source {
DataSource::Mempool => {
if main {
"https://mempool.space/api/block-height/"
} else {
"https://mempool.space/testnet/api/block-height/"
}
}
DataSource::Blockstream => {
if main {
"https://blockstream.info/api/block-height/"
} else {
"https://blockstream.info/testnet/api/block-height/"
}
}
};

let url = format!("{}{}", url, block_height);
let url = format!("{}{}", base_url, block_height);
let block_hash = reqwest::get(url).await.unwrap().text().await?;
Ok(block_hash)
}

Unchanged files with check annotations Beta

import { LoadingButton } from "@mui/lab";
import { Button, Chip, Drawer, Stack, Typography } from "@mui/material";
import { styled } from "@mui/material/styles";
import { Args, Transaction } from "@roochnetwork/rooch-sdk";

Check failure on line 10 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'Args' is defined but never used

Check failure on line 10 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'Transaction' is defined but never used
import {
UseSignAndExecuteTransaction,
useConnectWallet,
} from "@roochnetwork/rooch-sdk-kit";
import { enqueueSnackbar } from "notistack";
import { useState } from "react";
import CountUp from "react-countup";

Check failure on line 24 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'CountUp' is defined but never used
import "./App.css";
import { useRccOwner } from "./hooks/useRccOwner";
import { fNumber, shortAddress } from "./utils";
import { DebugScene } from './scenes/debug_scene'
import { PondScene } from './scenes/pond_scene';
import { useGameState } from './hooks/useGameState';

Check failure on line 30 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'useGameState' is defined but never used
import { useLatestTransaction } from "./hooks/useLatestTransaction";

Check failure on line 31 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'useLatestTransaction' is defined but never used
function getNextRewardClick(currentClicks: number): number {

Check failure on line 33 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'getNextRewardClick' is defined but never used
const remainder = currentClicks % 21;
if (remainder === 0) {
return currentClicks + 21;
}));
// Publish address of the counter contract
const counterAddress =

Check failure on line 64 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'counterAddress' is assigned a value but never used
"0x872502737008ac71c4c008bb3846a688bfd9fa54c6724089ea51b72f813dc71e";
const roochCounterObject =

Check failure on line 67 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'roochCounterObject' is assigned a value but never used
"0x24e093a6fa4698d1b6efd27ae9f1c21057b91bb9a2ef3c0fce2c94b44601764b";
const treasuryObject =

Check failure on line 70 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'treasuryObject' is assigned a value but never used
"0xe7beeda989fa0b2201c945310d533c82027c3270a39e2bcbaa65c4563210db82";
const treasuryOwnerAddress =
const { mutateAsync: createSessionKey } = useCreateSessionKey();
const { mutateAsync: removeSessionKey } = useRemoveSession();
const { mutateAsync: signAndExecuteTransaction } =

Check failure on line 107 in examples/rooch_fish/web/src/App.tsx

GitHub Actions / Check-Build-Test

'signAndExecuteTransaction' is assigned a value but never used
UseSignAndExecuteTransaction();
const { rccOwnerList } = useRccOwner();
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
};
}, [pondID, fishID, boundaries, speed]);

Check warning on line 160 in examples/rooch_fish/web/src/hooks/useFishController.ts

GitHub Actions / Check-Build-Test

React Hook useEffect has a missing dependency: 'handleFishMove'. Either include it or remove the dependency array
return fishState;
};
setFields(newFields);
}
}, [fieldStats]);

Check warning on line 52 in examples/rooch_fish/web/src/hooks/useRoochFieldStates.ts

GitHub Actions / Check-Build-Test

React Hook useEffect has a missing dependency: 'deserializeFieldState'. Either include it or remove the dependency array
return {
fields: fields,
});
}
previousStateRootRef.current = stateRoot;
}, [stateRoot]);

Check warning on line 94 in examples/rooch_fish/web/src/hooks/useRoochWSFieldStates.ts

GitHub Actions / Check-Build-Test

React Hook useEffect has a missing dependency: 'fetchFullData'. Either include it or remove the dependency array
const fetchFullData = async () => {
if (!objectID || !stateRoot || !client) return;
return () => {
clearInterval(intervalId);
};
}, [stateRoot, txOrder, objectID, fieldBcsType, client, opts.diffInterval]);

Check warning on line 190 in examples/rooch_fish/web/src/hooks/useRoochWSFieldStates.ts

GitHub Actions / Check-Build-Test

React Hook useEffect has missing dependencies: 'fetchDiffData' and 'fetchFullData'. Either include them or remove the dependency array
return {
fields: fields.size > 0 ? fields : lastValidFieldsRef.current,
const queryClient = useQueryClient();
const timerRef = useRef<any>();
const queryKey = ["rooch_seq_number", address];

Check warning on line 13 in examples/rooch_fish/web/src/hooks/useSeqNumber.ts

GitHub Actions / Check-Build-Test

The 'queryKey' array makes the dependencies of useCallback Hook (at line 27) change on every render. To fix this, wrap the initialization of 'queryKey' in its own useMemo() Hook

Check warning on line 13 in examples/rooch_fish/web/src/hooks/useSeqNumber.ts

GitHub Actions / Check-Build-Test

The 'queryKey' array makes the dependencies of useCallback Hook (at line 44) change on every render. To fix this, wrap the initialization of 'queryKey' in its own useMemo() Hook
const { data: seqNumber } = useQuery({
queryKey,
// REVIEW: can this throw?
let parsed = Path.parse(entry.name);
if (parsed.ext == ".bin") {

Check warning on line 230 in third_party/move/language/evm/hardhat-move/src/index.ts

GitHub Actions / Check-Build-Test

Expected '===' and instead saw '=='
info.push(parsed.name);
}
}
.setAction(async ({ quiet }: { quiet: boolean }, { artifacts, config, run }) => {
let packagePaths: String[] = await listMovePackages(Path.join(config.paths.root, "contracts"));
if (packagePaths.length == 0) {

Check warning on line 342 in third_party/move/language/evm/hardhat-move/src/index.ts

GitHub Actions / Check-Build-Test

Expected '===' and instead saw '=='
console.log("No Move contracts to compile");
return;
}
let plural = packagePaths.length == 1 ? "" : "s";

Check warning on line 347 in third_party/move/language/evm/hardhat-move/src/index.ts

GitHub Actions / Check-Build-Test

Expected '===' and instead saw '=='
console.log("Building %d Move package%s...", packagePaths.length, plural);
let locateRes = await locateMoveExecutablePath();
let buildResults = await Promise.all(packagePaths.map(path => buildPackageAndGenerateArtifacts(movePath, config.paths.root, path.toString())));
let failedToBuildAll = false;
console.assert(packagePaths.length == buildResults.length);

Check warning on line 361 in third_party/move/language/evm/hardhat-move/src/index.ts

GitHub Actions / Check-Build-Test

Expected '===' and instead saw '=='
for (let idx in packagePaths) {
let packagePathRel = Path.relative(config.paths.root, packagePaths[idx].toString());