mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-14 00:33:36 -07:00
@@ -19,6 +19,9 @@ _*
|
||||
# Logs
|
||||
.log
|
||||
|
||||
# Environment variables/configs
|
||||
.env
|
||||
|
||||
# Profiling
|
||||
profile.json.gz
|
||||
flamegraph.svg
|
||||
|
||||
Generated
+1
@@ -1046,6 +1046,7 @@ dependencies = [
|
||||
"log",
|
||||
"minreq",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
|
||||
@@ -7,6 +7,7 @@ package.edition = "2024"
|
||||
package.version = "0.0.80"
|
||||
package.homepage = "https://bitcoinresearchkit.org"
|
||||
package.repository = "https://github.com/bitcoinresearchkit/brk"
|
||||
package.readme = "README.md"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
|
||||
@@ -13,7 +13,6 @@ use clap_derive::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::services::Services;
|
||||
|
||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
#[command(version, about)]
|
||||
@@ -33,10 +32,6 @@ pub struct Config {
|
||||
#[arg(long, value_name = "PATH")]
|
||||
brkdir: Option<String>,
|
||||
|
||||
/// Activated services, default: all, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long)]
|
||||
services: Option<Services>,
|
||||
|
||||
/// Computation of computed datasets, `lazy` computes data whenever requested without saving it, `eager` computes the data once and saves it to disk, default: `lazy`, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
@@ -129,9 +124,6 @@ impl Config {
|
||||
config_saved.brkdir = Some(brkdir);
|
||||
}
|
||||
|
||||
if let Some(services) = config_args.services.take() {
|
||||
config_saved.services = Some(services);
|
||||
}
|
||||
|
||||
if let Some(computation) = config_args.computation.take() {
|
||||
config_saved.computation = Some(computation);
|
||||
@@ -306,15 +298,6 @@ impl Config {
|
||||
self.outputsdir().join("hars")
|
||||
}
|
||||
|
||||
pub fn process(&self) -> bool {
|
||||
self.services
|
||||
.is_none_or(|m| m == Services::All || m == Services::Processor)
|
||||
}
|
||||
|
||||
pub fn serve(&self) -> bool {
|
||||
self.services
|
||||
.is_none_or(|m| m == Services::All || m == Services::Server)
|
||||
}
|
||||
|
||||
fn path_cookiefile(&self) -> PathBuf {
|
||||
self.rpccookiefile.as_ref().map_or_else(
|
||||
|
||||
@@ -4,7 +4,6 @@ use brk_core::{dot_brk_log_path, dot_brk_path};
|
||||
|
||||
mod config;
|
||||
mod run;
|
||||
mod services;
|
||||
|
||||
use run::*;
|
||||
|
||||
|
||||
+29
-41
@@ -13,18 +13,20 @@ pub fn run() -> color_eyre::Result<()> {
|
||||
let config = Config::import()?;
|
||||
|
||||
let rpc = config.rpc()?;
|
||||
|
||||
let exit = Exit::new();
|
||||
|
||||
let parser = brk_parser::Parser::new(config.blocksdir(), rpc);
|
||||
let parser = brk_parser::Parser::new(
|
||||
config.blocksdir(),
|
||||
config.brkdir(),
|
||||
rpc,
|
||||
);
|
||||
|
||||
let format = config.format();
|
||||
|
||||
let mut indexer = Indexer::forced_import(&config.outputsdir())?;
|
||||
|
||||
let wait_for_synced_node = || -> color_eyre::Result<()> {
|
||||
let wait_for_synced_node = |rpc_client: &bitcoincore_rpc::Client| -> color_eyre::Result<()> {
|
||||
let is_synced = || -> color_eyre::Result<bool> {
|
||||
let info = rpc.get_blockchain_info()?;
|
||||
let info = rpc_client.get_blockchain_info()?;
|
||||
Ok(info.headers == info.blocks)
|
||||
};
|
||||
|
||||
@@ -50,54 +52,40 @@ pub fn run() -> color_eyre::Result<()> {
|
||||
.enable_all()
|
||||
.build()?
|
||||
.block_on(async {
|
||||
let server = if config.serve() {
|
||||
let served_indexer = indexer.clone();
|
||||
let served_computer = computer.clone();
|
||||
let served_indexer = indexer.clone();
|
||||
let served_computer = computer.clone();
|
||||
|
||||
let server = Server::new(served_indexer, served_computer, config.website())?;
|
||||
let server = Server::new(served_indexer, served_computer, config.website())?;
|
||||
|
||||
let watch = config.watch();
|
||||
let mcp = config.mcp();
|
||||
let opt = Some(tokio::spawn(async move {
|
||||
server.serve(watch, mcp).await.unwrap();
|
||||
}));
|
||||
let watch = config.watch();
|
||||
let mcp = config.mcp();
|
||||
let server_handle = tokio::spawn(async move {
|
||||
server.serve(watch, mcp).await.unwrap();
|
||||
});
|
||||
|
||||
sleep(Duration::from_secs(1));
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
opt
|
||||
} else {
|
||||
None
|
||||
};
|
||||
loop {
|
||||
wait_for_synced_node(rpc)?;
|
||||
|
||||
if config.process() {
|
||||
loop {
|
||||
wait_for_synced_node()?;
|
||||
let block_count = rpc.get_block_count()?;
|
||||
|
||||
let block_count = rpc.get_block_count()?;
|
||||
info!("{} blocks found.", block_count + 1);
|
||||
|
||||
info!("{} blocks found.", block_count + 1);
|
||||
let starting_indexes =
|
||||
indexer.index(&parser, rpc, &exit, config.check_collisions())?;
|
||||
|
||||
let starting_indexes =
|
||||
indexer.index(&parser, rpc, &exit, config.check_collisions())?;
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
|
||||
computer.compute(&mut indexer, starting_indexes, &exit)?;
|
||||
if let Some(delay) = config.delay() {
|
||||
sleep(Duration::from_secs(delay))
|
||||
}
|
||||
|
||||
if let Some(delay) = config.delay() {
|
||||
sleep(Duration::from_secs(delay))
|
||||
}
|
||||
info!("Waiting for new blocks...");
|
||||
|
||||
info!("Waiting for new blocks...");
|
||||
|
||||
while block_count == rpc.get_block_count()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
while block_count == rpc.get_block_count()? {
|
||||
sleep(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(handle) = server {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use clap_derive::{Parser, ValueEnum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Parser,
|
||||
ValueEnum,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
)]
|
||||
pub enum Services {
|
||||
#[default]
|
||||
All,
|
||||
Processor,
|
||||
Server,
|
||||
}
|
||||
@@ -26,7 +26,7 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
thread::Builder::new()
|
||||
.stack_size(256 * 1024 * 1024)
|
||||
.spawn(move || -> color_eyre::Result<()> {
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), default_brk_path(), rpc);
|
||||
|
||||
let _outputs_dir = Path::new("/Volumes/WD_BLACK/brk").join("outputs");
|
||||
let outputs_dir = _outputs_dir.as_path();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{path::Path, time::Instant};
|
||||
|
||||
use brk_core::default_bitcoin_path;
|
||||
use brk_core::{default_bitcoin_path, default_brk_path};
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::Parser;
|
||||
@@ -13,6 +13,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
brk_logger::init(Some(Path::new(".log")));
|
||||
|
||||
let bitcoin_dir = default_bitcoin_path();
|
||||
let brk_dir = default_brk_path();
|
||||
|
||||
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
|
||||
"http://localhost:8332",
|
||||
@@ -20,7 +21,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
)?));
|
||||
let exit = Exit::new();
|
||||
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), brk_dir, rpc);
|
||||
|
||||
let outputs = Path::new("../../_outputs");
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use bitcoincore_rpc::{Auth, Client};
|
||||
use brk_core::{Height, default_bitcoin_path};
|
||||
use brk_core::{Height, default_bitcoin_path, default_brk_path};
|
||||
use brk_parser::Parser;
|
||||
|
||||
fn main() {
|
||||
let i = std::time::Instant::now();
|
||||
|
||||
let bitcoin_dir = default_bitcoin_path();
|
||||
let brk_dir = default_brk_path();
|
||||
|
||||
let rpc = Box::leak(Box::new(
|
||||
Client::new(
|
||||
@@ -18,7 +19,7 @@ fn main() {
|
||||
let start = None;
|
||||
let end = None;
|
||||
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), brk_dir, rpc);
|
||||
|
||||
parser
|
||||
.parse(start, end)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use bitcoincore_rpc::{Auth, Client};
|
||||
use brk_core::{Height, OutputType, default_bitcoin_path};
|
||||
use brk_core::{Height, OutputType, default_bitcoin_path, default_brk_path};
|
||||
use brk_parser::Parser;
|
||||
|
||||
fn main() {
|
||||
let i = std::time::Instant::now();
|
||||
|
||||
let bitcoin_dir = default_bitcoin_path();
|
||||
let brk_dir = default_brk_path();
|
||||
|
||||
let rpc = Box::leak(Box::new(
|
||||
Client::new(
|
||||
@@ -18,7 +19,7 @@ fn main() {
|
||||
// let start = None;
|
||||
// let end = None;
|
||||
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), brk_dir, rpc);
|
||||
|
||||
// parser
|
||||
// .parse(start, end)
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{BlkIndexToBlkPath, Height, blk_recap::BlkRecap};
|
||||
use crate::{blk_recap::BlkRecap, BlkIndexToBlkPath, Height};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlkIndexToBlkRecap {
|
||||
@@ -15,11 +15,11 @@ pub struct BlkIndexToBlkRecap {
|
||||
|
||||
impl BlkIndexToBlkRecap {
|
||||
pub fn import(
|
||||
bitcoin_dir: &Path,
|
||||
outputs_dir: &Path,
|
||||
blk_index_to_blk_path: &BlkIndexToBlkPath,
|
||||
start: Option<Height>,
|
||||
) -> (Self, u16) {
|
||||
let path = bitcoin_dir.join("blk_index_to_blk_recap.json");
|
||||
let path = outputs_dir.join("blk_index_to_blk_recap.json");
|
||||
|
||||
let tree = {
|
||||
if let Ok(file) = File::open(&path) {
|
||||
|
||||
@@ -38,12 +38,17 @@ const BOUND_CAP: usize = 50;
|
||||
|
||||
pub struct Parser {
|
||||
blocks_dir: PathBuf,
|
||||
outputs_dir: PathBuf,
|
||||
rpc: &'static bitcoincore_rpc::Client,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(blocks_dir: PathBuf, rpc: &'static bitcoincore_rpc::Client) -> Self {
|
||||
Self { blocks_dir, rpc }
|
||||
pub fn new(blocks_dir: PathBuf, outputs_dir: PathBuf, rpc: &'static bitcoincore_rpc::Client) -> Self {
|
||||
Self {
|
||||
blocks_dir,
|
||||
outputs_dir: outputs_dir,
|
||||
rpc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, height: Height) -> Block {
|
||||
@@ -74,7 +79,7 @@ impl Parser {
|
||||
let blk_index_to_blk_path = BlkIndexToBlkPath::scan(blocks_dir);
|
||||
|
||||
let (mut blk_index_to_blk_recap, blk_index) =
|
||||
BlkIndexToBlkRecap::import(blocks_dir, &blk_index_to_blk_path, start);
|
||||
BlkIndexToBlkRecap::import(&self.outputs_dir, &blk_index_to_blk_path, start);
|
||||
|
||||
let xor_bytes = XORBytes::from(blocks_dir);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ jiff = { workspace = true }
|
||||
log = { workspace = true }
|
||||
minreq = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
||||
tracing = "0.1.41"
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{path::Path, thread::sleep, time::Duration};
|
||||
|
||||
use bitcoincore_rpc::RpcApi;
|
||||
use brk_computer::Computer;
|
||||
use brk_core::default_bitcoin_path;
|
||||
use brk_core::{default_bitcoin_path, default_brk_path};
|
||||
use brk_exit::Exit;
|
||||
use brk_fetcher::Fetcher;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -18,6 +18,7 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
let process = true;
|
||||
|
||||
let bitcoin_dir = default_bitcoin_path();
|
||||
let brk_dir = default_brk_path();
|
||||
|
||||
let rpc = Box::leak(Box::new(bitcoincore_rpc::Client::new(
|
||||
"http://localhost:8332",
|
||||
@@ -25,7 +26,7 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
)?));
|
||||
let exit = Exit::new();
|
||||
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), rpc);
|
||||
let parser = Parser::new(bitcoin_dir.join("blocks"), brk_dir, rpc);
|
||||
|
||||
let outputs_dir = Path::new("../../_outputs");
|
||||
|
||||
|
||||
@@ -107,6 +107,16 @@ impl ApiRoutes for Router<AppState> {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/health",
|
||||
get(|| async {
|
||||
Json(serde_json::json!({
|
||||
"status": "healthy",
|
||||
"service": "brk-server",
|
||||
"timestamp": jiff::Timestamp::now().to_string()
|
||||
}))
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api",
|
||||
get(|| async {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Build artifacts
|
||||
target/
|
||||
|
||||
# Development files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Docker files
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
.dockerignore
|
||||
docker-build.sh
|
||||
|
||||
# Documentation
|
||||
docs/
|
||||
LICENSE
|
||||
# Keep README.md for build process
|
||||
!README.md
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
|
||||
# Logs and temporary files
|
||||
*.log
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# BRK runtime data (should be in volumes)
|
||||
.brk/
|
||||
|
||||
# Example and test data
|
||||
examples/
|
||||
tests/
|
||||
*.test
|
||||
*.example
|
||||
|
||||
# Node modules (if any frontend deps)
|
||||
node_modules/
|
||||
|
||||
# Python cache (if any)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# Rust workspace cache
|
||||
**/*.rs.bk
|
||||
|
||||
# macOS
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Windows
|
||||
Desktop.ini
|
||||
ehthumbs.db
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
@@ -0,0 +1,42 @@
|
||||
# Bitcoin Core data directory
|
||||
# This should point to your Bitcoin Core data directory
|
||||
BITCOIN_DATA_DIR=/path/to/bitcoin
|
||||
|
||||
# Bitcoin Core RPC configuration
|
||||
# If running Bitcoin Core on the same host (not in Docker), use host.docker.internal on macOS/Windows
|
||||
# or the host's IP address on Linux
|
||||
BTC_RPC_HOST=localhost
|
||||
BTC_RPC_PORT=8332
|
||||
|
||||
# Use either cookie file authentication (recommended) or username/password
|
||||
# Cookie file is automatically created by Bitcoin Core
|
||||
# If using username/password, comment out RPCCOOKIEFILE in docker-compose.yml
|
||||
# BTC_RPC_USER=your_rpc_username
|
||||
# BTC_RPC_PASSWORD=your_rpc_password
|
||||
|
||||
# BRK configuration
|
||||
# Services to run: all, processor, or server
|
||||
BRK_SERVICES=all
|
||||
|
||||
# Computation mode: lazy (compute on demand) or eager (precompute and save)
|
||||
BRK_COMPUTATION=lazy
|
||||
|
||||
# Data format: raw (faster) or compressed (saves disk space)
|
||||
BRK_FORMAT=raw
|
||||
|
||||
# Enable price fetching from exchanges
|
||||
BRK_FETCH=true
|
||||
|
||||
# Enable Model Context Protocol (MCP) for AI/LLM integration
|
||||
BRK_MCP=true
|
||||
|
||||
# BRK data storage options
|
||||
# Option 1: Use a Docker named volume (default, recommended)
|
||||
# This is the default configuration - no changes needed.
|
||||
# Leave this commented to use the default named volume
|
||||
# BRK_DATA_VOLUME=brk-data
|
||||
|
||||
# Option 2: Use a bind mount to a local directory
|
||||
# Uncomment and set this to use a specific directory on your host
|
||||
# Also uncomment the corresponding line in docker-compose.yml
|
||||
# BRK_DATA_DIR=/path/to/brk/data
|
||||
@@ -0,0 +1,263 @@
|
||||
# Docker Setup for BRK
|
||||
|
||||
This guide explains how to run BRK using Docker and Docker Compose.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker Engine (with buildx support)
|
||||
- Docker Compose v2
|
||||
- A running Bitcoin Core node with RPC enabled
|
||||
- Access to Bitcoin Core's blocks directory
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Create environment file**
|
||||
```bash
|
||||
cp docker/.env.example docker/.env
|
||||
```
|
||||
Edit `docker/.env` and set `BITCOIN_DATA_DIR` to your Bitcoin Core data directory.
|
||||
|
||||
2. **Run with Docker Compose**
|
||||
```bash
|
||||
docker compose -f docker/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Or from the docker directory:
|
||||
```bash
|
||||
cd docker && docker compose up -d
|
||||
```
|
||||
|
||||
3. **Access BRK**
|
||||
- Web interface: http://localhost:7070
|
||||
- API: http://localhost:7070/api
|
||||
- Health check: http://localhost:7070/health
|
||||
|
||||
## Architecture
|
||||
|
||||
BRK runs as a single container that includes both the blockchain processor and API server. This simplified architecture:
|
||||
- Ensures processor and server are always in sync
|
||||
- Simplifies deployment and monitoring
|
||||
- Uses a single shared data directory
|
||||
|
||||
```bash
|
||||
# Start BRK
|
||||
docker compose -f docker/docker-compose.yml up
|
||||
|
||||
# Or run in background
|
||||
docker compose -f docker/docker-compose.yml up -d
|
||||
|
||||
# Alternative: from docker directory
|
||||
cd docker && docker compose up -d
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `BITCOIN_DATA_DIR` | Path to Bitcoin Core data directory | - |
|
||||
| `BTC_RPC_HOST` | Bitcoin Core RPC host | `localhost` |
|
||||
| `BTC_RPC_PORT` | Bitcoin Core RPC port | `8332` |
|
||||
| `BTC_RPC_USER` | Bitcoin RPC username | - |
|
||||
| `BTC_RPC_PASSWORD` | Bitcoin RPC password | - |
|
||||
| `BRK_DATA_VOLUME` | Docker volume name for BRK data | `brk-data` |
|
||||
| `BRK_COMPUTATION` | Computation mode (`lazy`, `eager`) | `lazy` |
|
||||
| `BRK_FORMAT` | Data format (`raw`, `compressed`) | `raw` |
|
||||
| `BRK_FETCH` | Enable price fetching | `true` |
|
||||
| `BRK_MCP` | Enable MCP for AI/LLM | `true` |
|
||||
|
||||
### Example .env File
|
||||
|
||||
```env
|
||||
# Bitcoin Core paths
|
||||
BITCOIN_DATA_DIR=/path/to/bitcoin/data
|
||||
BRK_DATA_VOLUME=brk-data
|
||||
|
||||
# Bitcoin RPC configuration
|
||||
BTC_RPC_HOST=localhost
|
||||
BTC_RPC_PORT=8332
|
||||
BTC_RPC_USER=your_username
|
||||
BTC_RPC_PASSWORD=your_password
|
||||
|
||||
# BRK settings
|
||||
BRK_COMPUTATION=lazy
|
||||
BRK_FORMAT=raw
|
||||
BRK_FETCH=true
|
||||
BRK_MCP=true
|
||||
```
|
||||
|
||||
### Connecting to Bitcoin Core
|
||||
|
||||
#### Option 1: Cookie File Authentication (Recommended)
|
||||
BRK will automatically use the `.cookie` file from your Bitcoin Core directory.
|
||||
|
||||
#### Option 2: Username/Password
|
||||
Set `BTC_RPC_USER` and `BTC_RPC_PASSWORD` in your `docker/.env` file.
|
||||
|
||||
#### Network Connectivity
|
||||
- **Same host**:
|
||||
- If Bitcoin Core is running natively (not in Docker): Use `host.docker.internal` on macOS/Windows or `172.17.0.1` on Linux
|
||||
- If Bitcoin Core is also in Docker: Use the service name or container IP
|
||||
- **Remote host**: Use the actual IP address or hostname
|
||||
|
||||
## Building the Image
|
||||
|
||||
### Using Docker Compose
|
||||
```bash
|
||||
docker compose -f docker/docker-compose.yml build
|
||||
```
|
||||
|
||||
### or ... Using Docker Build Script
|
||||
```bash
|
||||
# Build with default settings
|
||||
./docker/docker-build.sh
|
||||
|
||||
# Build with custom tag
|
||||
./docker/docker-build.sh --tag v1.0.0
|
||||
```
|
||||
|
||||
## Volumes and Data Storage
|
||||
|
||||
BRK supports two options for storing its data:
|
||||
|
||||
### Option 1: Docker Named Volume (Default)
|
||||
Uses a Docker-managed named volume called `brk-data`. This is the recommended approach for most users.
|
||||
|
||||
### Option 2: Bind Mount
|
||||
Maps a specific directory on your host to the container's data directory.
|
||||
This may be desirable if you want to use a specific storage location for BRK data (e.g. a different disk).
|
||||
|
||||
1. Set `BRK_DATA_DIR` in your `docker/.env` file to your desired host directory
|
||||
2. In `docker/docker-compose.yml`, comment out the named volume line and uncomment the bind mount line
|
||||
|
||||
```bash
|
||||
# In docker/.env file
|
||||
BRK_DATA_DIR=/home/user/brk-data
|
||||
```
|
||||
|
||||
```bash
|
||||
# In docker/docker-compose.yml
|
||||
# Comment out:
|
||||
- ${BRK_DATA_VOLUME:-brk-data}:/home/brk/.brk
|
||||
|
||||
# Uncomment:
|
||||
# - ${BRK_DATA_DIR:-./brk-data}:/home/brk/.brk
|
||||
```
|
||||
|
||||
Can also remove or comment out the `volumes` section from the docker/docker-compose.yml file (right at the bottom):
|
||||
```bash
|
||||
# Comment out:
|
||||
volumes:
|
||||
brk-data:
|
||||
driver: local
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
The container includes a combined health check that verifies:
|
||||
- The BRK process is running
|
||||
- The API server is responding on port 3110
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Check Container Status
|
||||
```bash
|
||||
# View running container
|
||||
docker compose -f docker/docker-compose.yml ps
|
||||
|
||||
# Check health status
|
||||
docker compose -f docker/docker-compose.yml ps --format \"table {{.Service}}\\t{{.Status}}\\t{{.Health}}\"
|
||||
```
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
# View logs
|
||||
docker compose -f docker/docker-compose.yml logs
|
||||
|
||||
# Follow logs in real-time
|
||||
docker compose -f docker/docker-compose.yml logs -f
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Server Issues
|
||||
|
||||
#### Server returns empty data
|
||||
- This is normal if the processor hasn't indexed any blocks yet
|
||||
- The server component will serve data as the processor indexes blocks
|
||||
|
||||
#### Server won't start
|
||||
- Check Docker Compose logs: `docker compose -f docker/docker-compose.yml logs`
|
||||
- Verify health endpoint: `curl http://localhost:7070/health`
|
||||
- Ensure no port conflicts on 7070
|
||||
|
||||
### Processor Issues
|
||||
|
||||
#### Cannot connect to Bitcoin Core
|
||||
1. Ensure Bitcoin Core is running with `-server=1`
|
||||
2. Check RPC credentials are correct
|
||||
3. Verify network connectivity from container
|
||||
4. Test RPC connection: `docker compose -f docker/docker-compose.yml exec brk brk --help`
|
||||
|
||||
#### Processor fails to start
|
||||
- Verify Bitcoin RPC credentials in `docker/.env`
|
||||
- Ensure Bitcoin Core is running and accessible
|
||||
- Check Bitcoin data directory permissions (should be readable by UID 1000)
|
||||
|
||||
### Performance Issues
|
||||
|
||||
#### Slow indexing
|
||||
- Ensure adequate disk space for indexed data - a minimum of 3GB/s is recommended
|
||||
- Monitor memory usage during initial indexing
|
||||
- Use `BRK_COMPUTATION=lazy` to reduce memory usage
|
||||
|
||||
#### Out of memory
|
||||
- Increase Docker's memory limit
|
||||
- Use `BRK_COMPUTATION=lazy` mode
|
||||
- Monitor container resource usage: `docker stats`
|
||||
|
||||
### Permission Issues
|
||||
|
||||
#### Permission denied errors
|
||||
- Ensure the Bitcoin data directory is readable by the container user (UID 1000)
|
||||
- Check that volumes are properly mounted
|
||||
- Verify file ownership: `ls -la $BITCOIN_DATA_DIR`
|
||||
|
||||
### Network Issues
|
||||
|
||||
#### Cannot access web interface
|
||||
- Verify port mapping: `docker compose -f docker/docker-compose.yml ps`
|
||||
- Check firewall settings
|
||||
- Ensure no other services are using port 7070
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Bitcoin data is mounted read-only for safety
|
||||
- BRK runs as non-root user inside container
|
||||
- Only necessary ports are exposed
|
||||
|
||||
## Backup and Recovery
|
||||
|
||||
### Backing Up BRK Data
|
||||
|
||||
```bash
|
||||
# Create backup of named volume
|
||||
docker run --rm -v brk_brk-data:/source -v \"$(pwd)\":/backup alpine tar czf /backup/brk-backup.tar.gz -C /source .
|
||||
|
||||
# Or if using bind mount
|
||||
tar czf brk-backup.tar.gz -C \"$BRK_DATA_DIR\" .
|
||||
```
|
||||
|
||||
### Restoring BRK Data
|
||||
|
||||
```bash
|
||||
# Stop container
|
||||
docker compose -f docker/docker-compose.yml down
|
||||
|
||||
# Restore from backup (named volume)
|
||||
docker run --rm -v brk_brk-data:/target -v \"$(pwd)\":/backup alpine tar xzf /backup/brk-backup.tar.gz -C /target
|
||||
|
||||
# Start container
|
||||
docker compose -f docker/docker-compose.yml up -d
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
# *************
|
||||
# Builder
|
||||
# *************
|
||||
FROM rustlang/rust:nightly AS builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy all source files
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN cargo build --release --locked
|
||||
|
||||
# *************
|
||||
# Runtime
|
||||
# *************
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
openssl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -s /bin/bash brk
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /app/target/release/brk /usr/local/bin/brk
|
||||
|
||||
# Copy websites directory
|
||||
COPY --from=builder /app/websites /app/websites
|
||||
|
||||
# Set ownership
|
||||
RUN chown -R brk:brk /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER brk
|
||||
|
||||
# Create directories for BRK data
|
||||
RUN mkdir -p /home/brk/.brk
|
||||
|
||||
# Expose API port
|
||||
EXPOSE 3110
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /home/brk
|
||||
|
||||
# Default entrypoint
|
||||
ENTRYPOINT ["brk"]
|
||||
|
||||
# Default command (can be overridden)
|
||||
CMD ["--services", "all"]
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
IMAGE_NAME="brk"
|
||||
TAG="latest"
|
||||
|
||||
# Function to print colored output
|
||||
print_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-t|--tag)
|
||||
TAG="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -t, --tag TAG Tag for the image (default: latest)"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Build the image
|
||||
print_info "Building BRK Docker image..."
|
||||
print_info "Image: ${IMAGE_NAME}:${TAG}"
|
||||
|
||||
# Detect script location and set paths accordingly
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Determine if we're running from project root or docker directory
|
||||
if [[ "$(basename "$PWD")" == "docker" ]]; then
|
||||
# Running from docker directory
|
||||
DOCKERFILE_PATH="./Dockerfile"
|
||||
BUILD_CONTEXT=".."
|
||||
print_info "Running from docker directory"
|
||||
else
|
||||
# Running from project root or elsewhere
|
||||
DOCKERFILE_PATH="docker/Dockerfile"
|
||||
BUILD_CONTEXT="."
|
||||
print_info "Running from project root"
|
||||
fi
|
||||
|
||||
# Execute the build
|
||||
if docker build -f "$DOCKERFILE_PATH" -t "${IMAGE_NAME}:${TAG}" "$BUILD_CONTEXT"; then
|
||||
print_info "Build completed successfully!"
|
||||
print_info "Image built as ${IMAGE_NAME}:${TAG}"
|
||||
else
|
||||
print_error "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,64 @@
|
||||
# BRK single-container Docker Compose configuration
|
||||
|
||||
name: brk
|
||||
|
||||
services:
|
||||
brk:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
image: brk:latest
|
||||
container_name: brk
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 7070:3110 # Map host port 7070 to container port 3110
|
||||
volumes:
|
||||
# Bitcoin Core data directory (read-only)
|
||||
# For access to raw block data
|
||||
- ${BITCOIN_DATA_DIR:-/path/to/bitcoin}:/bitcoin:ro
|
||||
# BRK data directory for outputs and state
|
||||
# Option 1: Use a named volume (default)
|
||||
- ${BRK_DATA_VOLUME:-brk-data}:/home/brk/.brk
|
||||
# Option 2: Use a bind mount (uncomment and set BRK_DATA_DIR in .env)
|
||||
# - ${BRK_DATA_DIR:-./brk-data}:/home/brk/.brk
|
||||
environment:
|
||||
# Bitcoin Core configuration
|
||||
- BITCOINDIR=/bitcoin
|
||||
- BLOCKSDIR=/bitcoin/blocks
|
||||
|
||||
# RPC configuration (required for processor)
|
||||
- RPCCONNECT=${BTC_RPC_HOST:-localhost}
|
||||
- RPCPORT=${BTC_RPC_PORT:-8332}
|
||||
# - RPCCOOKIEFILE=/bitcoin/.cookie
|
||||
|
||||
# Username/password authentication
|
||||
- RPCUSER=${BTC_RPC_USER}
|
||||
- RPCPASSWORD=${BTC_RPC_PASSWORD}
|
||||
|
||||
# BRK configuration
|
||||
- BRKDIR=/home/brk/.brk
|
||||
- COMPUTATION=${BRK_COMPUTATION:-lazy}
|
||||
- FORMAT=${BRK_FORMAT:-raw}
|
||||
- FETCH=${BRK_FETCH:-true}
|
||||
- MCP=${BRK_MCP:-true}
|
||||
command:
|
||||
- --bitcoindir
|
||||
- /bitcoin
|
||||
- --brkdir
|
||||
- /home/brk/.brk
|
||||
- --rpcconnect
|
||||
- "${BTC_RPC_HOST:-localhost}"
|
||||
- --rpcuser
|
||||
- "${BTC_RPC_USER:-bitcoin}"
|
||||
- --rpcpassword
|
||||
- "${BTC_RPC_PASSWORD:-bitcoin}"
|
||||
healthcheck:
|
||||
test: ["CMD", "sh", "-c", "pgrep -f brk && nc -z localhost 3110"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
volumes:
|
||||
brk-data:
|
||||
driver: local
|
||||
Reference in New Issue
Block a user