Compare commits

...

3 Commits

Author SHA1 Message Date
nym21 66f1e92cb6 release: v0.0.111 2025-10-03 14:16:00 +02:00
nym21 d9c4653f82 global: fixes 2025-10-03 14:15:23 +02:00
nym21 cfdf8fdbca changelog: update 2025-10-02 18:09:39 +02:00
13 changed files with 198 additions and 129 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Changelog Generation for Claude Code
**TASK**: Update docs/CHANGELOG.md for ALL releases after and including v0.0.107.
**TASK**: Update docs/CHANGELOG.md for ALL latest releases missing from the file.
## ⚠️ CRITICAL FAILURE MODE TO AVOID ⚠️
**THE #1 FAILURE**: Ignoring most changes and only documenting a few
Generated
+15 -15
View File
@@ -496,7 +496,7 @@ dependencies = [
[[package]]
name = "brk"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"brk_binder",
"brk_bundler",
@@ -562,7 +562,7 @@ checksum = "91ff3e445e42475fba5e0cfaed51345f491e479b9f2069f29875f434a5327913"
[[package]]
name = "brk_binder"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"brk_interface",
"brk_structs",
@@ -570,7 +570,7 @@ dependencies = [
[[package]]
name = "brk_bundler"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"brk_rolldown",
"log",
@@ -581,7 +581,7 @@ dependencies = [
[[package]]
name = "brk_cli"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"bitcoincore-rpc",
"brk_binder",
@@ -607,7 +607,7 @@ dependencies = [
[[package]]
name = "brk_computer"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"allocative",
"bitcoin",
@@ -631,7 +631,7 @@ dependencies = [
[[package]]
name = "brk_error"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -645,7 +645,7 @@ dependencies = [
[[package]]
name = "brk_fetcher"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"brk_error",
"brk_logger",
@@ -657,7 +657,7 @@ dependencies = [
[[package]]
name = "brk_indexer"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -674,7 +674,7 @@ dependencies = [
[[package]]
name = "brk_interface"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"bitcoincore-rpc",
"brk_computer",
@@ -695,7 +695,7 @@ dependencies = [
[[package]]
name = "brk_logger"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"env_logger",
"jiff",
@@ -705,7 +705,7 @@ dependencies = [
[[package]]
name = "brk_mcp"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"axum",
"brk_interface",
@@ -718,7 +718,7 @@ dependencies = [
[[package]]
name = "brk_parser"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -1124,7 +1124,7 @@ dependencies = [
[[package]]
name = "brk_server"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"axum",
"bitcoin",
@@ -1151,7 +1151,7 @@ dependencies = [
[[package]]
name = "brk_store"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"brk_error",
"brk_structs",
@@ -1176,7 +1176,7 @@ dependencies = [
[[package]]
name = "brk_structs"
version = "0.0.110"
version = "0.0.111"
dependencies = [
"allocative",
"bitcoin",
+15 -15
View File
@@ -4,7 +4,7 @@ members = ["crates/*"]
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
package.license = "MIT"
package.edition = "2024"
package.version = "0.0.110"
package.version = "0.0.111"
package.homepage = "https://bitcoinresearchkit.org"
package.repository = "https://github.com/bitcoinresearchkit/brk"
package.readme = "README.md"
@@ -45,20 +45,20 @@ allocative = { version = "0.3.4", features = ["parking_lot"] }
axum = "0.8.6"
bitcoin = { version = "0.32.7", features = ["serde"] }
bitcoincore-rpc = "0.19.0"
brk_binder = { version = "0.0.110", path = "crates/brk_binder" }
brk_bundler = { version = "0.0.110", path = "crates/brk_bundler" }
brk_cli = { version = "0.0.110", path = "crates/brk_cli" }
brk_computer = { version = "0.0.110", path = "crates/brk_computer" }
brk_error = { version = "0.0.110", path = "crates/brk_error" }
brk_fetcher = { version = "0.0.110", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.0.110", path = "crates/brk_indexer" }
brk_interface = { version = "0.0.110", path = "crates/brk_interface" }
brk_logger = { version = "0.0.110", path = "crates/brk_logger" }
brk_mcp = { version = "0.0.110", path = "crates/brk_mcp" }
brk_parser = { version = "0.0.110", path = "crates/brk_parser" }
brk_server = { version = "0.0.110", path = "crates/brk_server" }
brk_store = { version = "0.0.110", path = "crates/brk_store" }
brk_structs = { version = "0.0.110", path = "crates/brk_structs" }
brk_binder = { version = "0.0.111", path = "crates/brk_binder" }
brk_bundler = { version = "0.0.111", path = "crates/brk_bundler" }
brk_cli = { version = "0.0.111", path = "crates/brk_cli" }
brk_computer = { version = "0.0.111", path = "crates/brk_computer" }
brk_error = { version = "0.0.111", path = "crates/brk_error" }
brk_fetcher = { version = "0.0.111", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.0.111", path = "crates/brk_indexer" }
brk_interface = { version = "0.0.111", path = "crates/brk_interface" }
brk_logger = { version = "0.0.111", path = "crates/brk_logger" }
brk_mcp = { version = "0.0.111", path = "crates/brk_mcp" }
brk_parser = { version = "0.0.111", path = "crates/brk_parser" }
brk_server = { version = "0.0.111", path = "crates/brk_server" }
brk_store = { version = "0.0.111", path = "crates/brk_store" }
brk_structs = { version = "0.0.111", path = "crates/brk_structs" }
byteview = "=0.6.1"
derive_deref = "1.1.1"
fjall = "2.11.2"
+1 -1
View File
@@ -96,7 +96,7 @@ impl BRK {
dateindex + CHUNK_SIZE
);
let body: Value = sonic_rs::from_str(minreq::get(url).send()?.json()?)?;
let body: Value = sonic_rs::from_str(minreq::get(url).send()?.as_str()?)?;
body.as_array()
.ok_or(Error::Str("Expect to be an array"))?
+3 -11
View File
@@ -228,25 +228,17 @@ impl<'a> Interface<'a> {
}
pub fn distinct_metric_count(&self) -> usize {
self.vecs.metric_count
self.vecs.distinct_metric_count
}
pub fn total_metric_count(&self) -> usize {
self.vecs.vec_count
self.vecs.total_metric_count
}
pub fn index_count(&self) -> usize {
self.vecs.index_count
}
pub fn get_indexes(&self) -> &[&'static str] {
pub fn get_indexes(&self) -> &BTreeMap<&'static str, &'static [&'static str]> {
&self.vecs.indexes
}
pub fn get_accepted_indexes(&self) -> &BTreeMap<&'static str, &'static [&'static str]> {
&self.vecs.accepted_indexes
}
pub fn get_metrics(&self, pagination: PaginationParam) -> &[&str] {
self.vecs.metrics(pagination)
}
+2 -2
View File
@@ -3,7 +3,7 @@ use std::fmt;
use derive_deref::Deref;
use schemars::JsonSchema;
use serde::Deserialize;
use sonic_rs::{JsonContainerTrait, JsonValueTrait, Value};
use serde_json::Value;
#[derive(Debug, Deref, JsonSchema)]
pub struct MaybeMetrics(Vec<String>);
@@ -46,7 +46,7 @@ impl<'de> Deserialize<'de> for MaybeMetrics {
} else if let Some(vec) = value.as_array() {
if vec.len() <= MAX_VECS {
Ok(MaybeMetrics(sanitize_metrics(
vec.into_iter().map(|s| s.as_str().unwrap().to_string()),
vec.iter().map(|s| s.as_str().unwrap().to_string()),
)))
} else {
Err(serde::de::Error::custom("Given parameter is too long"))
+5 -13
View File
@@ -14,11 +14,9 @@ pub struct Vecs<'a> {
pub metric_to_index_to_vec: BTreeMap<&'a str, IndexToVec<'a>>,
pub index_to_metric_to_vec: BTreeMap<Index, MetricToVec<'a>>,
pub metrics: Vec<&'a str>,
pub indexes: Vec<&'static str>,
pub accepted_indexes: BTreeMap<&'static str, &'static [&'static str]>,
pub index_count: usize,
pub metric_count: usize,
pub vec_count: usize,
pub indexes: BTreeMap<&'static str, &'static [&'static str]>,
pub distinct_metric_count: usize,
pub total_metric_count: usize,
metric_to_indexes: BTreeMap<&'a str, Vec<&'static str>>,
index_to_metrics: BTreeMap<Index, Vec<&'a str>>,
}
@@ -53,19 +51,13 @@ impl<'a> Vecs<'a> {
sort_ids(&mut ids);
this.metrics = ids;
this.metric_count = this.metric_to_index_to_vec.keys().count();
this.index_count = this.index_to_metric_to_vec.keys().count();
this.vec_count = this
this.distinct_metric_count = this.metric_to_index_to_vec.keys().count();
this.total_metric_count = this
.index_to_metric_to_vec
.values()
.map(|tree| tree.len())
.sum::<usize>();
this.indexes = this
.index_to_metric_to_vec
.keys()
.map(|i| i.serialize_long())
.collect::<Vec<_>>();
this.accepted_indexes = this
.index_to_metric_to_vec
.keys()
.map(|i| (i.serialize_long(), i.possible_values()))
+5 -26
View File
@@ -32,38 +32,27 @@ impl MCP {
}
#[tool(description = "
Get the count of all existing indexes.
")]
async fn get_index_count(&self) -> Result<CallToolResult, McpError> {
info!("mcp: get_index_count");
Ok(CallToolResult::success(vec![
Content::json(self.interface.index_count()).unwrap(),
]))
}
#[tool(description = "
Get the count of all existing metrics.
Get the count of unique metrics.
")]
async fn get_metric_count(&self) -> Result<CallToolResult, McpError> {
info!("mcp: get_metric_count");
info!("mcp: distinct_metric_count");
Ok(CallToolResult::success(vec![
Content::json(self.interface.distinct_metric_count()).unwrap(),
]))
}
#[tool(description = "
Get the count of all existing vecs.
Equals to the sum of supported Indexes of each vec id.
Get the count of all metrics. (distinct metrics multiplied by the number of indexes supported by each one)
")]
async fn get_vec_count(&self) -> Result<CallToolResult, McpError> {
info!("mcp: get_vec_count");
info!("mcp: total_metric_count");
Ok(CallToolResult::success(vec![
Content::json(self.interface.total_metric_count()).unwrap(),
]))
}
#[tool(description = "
Get the list of all existing indexes.
Get the list of all existing indexes and their accepted variants.
")]
async fn get_indexes(&self) -> Result<CallToolResult, McpError> {
info!("mcp: get_indexes");
@@ -72,16 +61,6 @@ Get the list of all existing indexes.
]))
}
#[tool(description = "
Get an object which has all existing indexes as keys and a list of their accepted variants as values.
")]
async fn get_accepted_indexes(&self) -> Result<CallToolResult, McpError> {
info!("mcp: get_accepted_indexes");
Ok(CallToolResult::success(vec![
Content::json(self.interface.get_accepted_indexes()).unwrap(),
]))
}
#[tool(description = "
Get a paginated list of all existing vec ids.
There are up to 1,000 values per page.
+1 -1
View File
@@ -32,7 +32,7 @@ impl ApiMetricsRoutes for Router<AppState> {
.route(
"/api/metrics/indexes",
get(async |State(app_state): State<AppState>| -> Response {
Json(app_state.interface.get_accepted_indexes()).into_response()
Json(app_state.interface.get_indexes()).into_response()
}),
)
// .route(
+61
View File
@@ -4,6 +4,67 @@
All notable changes to the Bitcoin Research Kit (BRK) project will be documented in this file.
## [v0.0.110](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.110) - 2025-10-02
### New Features
#### `brk_binder`
- Created new crate for generating language binding files to facilitate integration with other programming languages ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_binder/src/lib.rs))
- Implemented JavaScript binding generator with compressed metric-to-index mappings using base62 encoding for optimized frontend bundle sizes ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_binder/src/js.rs))
- Added automatic generation of TypeScript-compatible pool ID mappings with sorted pool names for improved developer experience
- Implemented word frequency analysis to create compressed metric names, reducing JavaScript bundle size and improving load times
- Added version file generation to track binding file compatibility with backend versions
#### `brk_server`
- Implemented comprehensive address lookup API endpoint supporting all Bitcoin address types including P2PK, P2PKH, P2SH, P2WPKH, P2WSH, P2TR, and P2A ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_server/src/api/explorer/mod.rs))
- Added transaction lookup endpoint with raw transaction decoding from blk files including position-based seeking and XOR decryption
- Created address balance and statistics API providing UTXO counts, sent/received amounts, realized value, and average cost basis
- Implemented real-time USD balance calculation using latest price data for address endpoints
- Reorganized API structure into dedicated `metrics/` and `explorer/` modules for better code organization and maintainability ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_server/src/api/metrics/mod.rs))
- Added metrics count endpoint exposing both distinct and total metric counts for API discovery
- Created bulk metrics query endpoint for efficient batch data retrieval
#### `brk_interface`
- Implemented `MaybeMetrics` type for handling multiple metric queries with automatic sanitization and validation ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_interface/src/metrics.rs))
- Added support for both comma-separated string and array formats for metric parameters with configurable size limits (max 32 metrics, 2KB total)
- Implemented automatic metric name normalization converting hyphens to underscores and enforcing lowercase for consistent API usage
- Added metric-to-indexes mapping functionality for discovering available data dimensions per metric
#### `brk_bundler`
- Restructured bundler to use centralized `modules/` directory instead of website-specific packages for better code reuse ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_bundler/src/lib.rs))
- Implemented automatic module copying from `modules/` to website source directories before bundling
- Added inline constant optimization configuration for improved runtime performance through compile-time constant evaluation
- Enhanced HTML generation with dynamic script path injection based on bundled entry point hashes
#### Frontend Modules
- Created `brk-client` module providing type-safe BRK API client with metrics catalog, pool mappings, and idle callback utilities
- Implemented `brk-resources` module for managing shared resources and dependencies across frontend applications
- Added `brk-signals` module for reactive state management with SolidJS signals compatibility
- Centralized third-party dependencies including lightweight-charts 5.0.9, lean-qr 2.6.0, ufuzzy 1.0.19, and solidjs-signals 0.6.3
### Internal Changes
#### `brk_cli`
- Removed legacy bridge generation code in favor of new `brk_binder` crate for cleaner separation of concerns ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.0.110/crates/brk_cli/src/lib.rs))
- Integrated `brk_binder` for automated JavaScript binding file generation during CLI operations
#### `brk_computer`
- Removed unused profit/loss metrics from stateful computations to streamline calculation pipelines
- Cleaned up cohort analysis code removing obsolete breakeven metrics for better performance
#### `brk_interface`
- Removed legacy `MaybeIds` type in favor of `MaybeMetrics` for more accurate API parameter naming
- Enhanced deserialization logic with better error messages for invalid metric formats
- Restructured interface to expose metric catalog and index mappings for API documentation
#### `brk_mcp`
- Updated Model Context Protocol integration to use new metrics infrastructure with improved parameter handling
#### Workspace
- Reorganized project structure moving website packages to centralized `modules/` directory for better maintainability
- Removed historical asset files reducing repository size and focusing on active development
- Updated solidjs-signals from 0.4.1 to 0.6.3 with enhanced TypeScript definitions and reactive primitives
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.109...v0.0.110)
## [v0.0.109](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.109) - 2025-09-20
### New Features
+3
View File
@@ -11,6 +11,7 @@
- _COMPUTER_
- BUG: **add rollback of states (in stateful)**
- FEAT: add support for per index computation
- FEAT: Add percentiles of cost basis weighted by amount invested compared to total invested
- BUG: fix min fee_rate which is always ZERO due to coinbase transaction
- BUG: before computing multiple sources check their length, panic if not equal
- DX: create usd versions of vecs structs instead of having options everywhere
@@ -136,9 +137,11 @@
- FEAT: Add ?
- NAV
- UX: move share button to footer ?
- FEAT: add hide sidebar button
- BUG: when clicking on already selected option, pushes to history, bad !
- GLOBAL
- BUG: improve behavior when local storage is unavailable by having a global state, otherwise the website forgets/don't save user's settings
- FEAT: Add manual theme switcher, maybe in a smart way to avoid using real estate ?
- UI: font:
- https://fonts.google.com/specimen/Space+Mono
- PERF: keep as many files as possible [under 14kb](https://endtimes.dev/why-your-website-should-be-under-14kb-in-size/)
@@ -2551,37 +2551,85 @@ export function createPartialOptions({ colors, brk }) {
],
},
]),
{
name: "Coins Destroyed",
title: `Coins Destroyed ${title}`,
bottom: list.flatMap(({ color, name, id: _id }) => {
const id = fixId(_id);
return /** @type {const} */ ([
createBaseSeries({
metric: `${id}coinblocks_destroyed`,
name: useGroupName ? name : "sum",
color,
}),
createBaseSeries({
metric: `${id}coinblocks_destroyed_cumulative`,
name: useGroupName ? name : "cumulative",
color,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}coindays_destroyed`,
name: useGroupName ? name : "sum",
color,
}),
createBaseSeries({
metric: `${id}coindays_destroyed_cumulative`,
name: useGroupName ? name : "cumulative",
color,
defaultActive: false,
}),
]);
}),
},
...("list" in args
? [
{
name: "Coins Destroyed",
tree: [
{
name: "Sum",
title: `Sum of Coins Destroyed ${title}`,
bottom: list.flatMap(({ color, name, id: _id }) => {
const id = fixId(_id);
return /** @type {const} */ ([
createBaseSeries({
metric: `${id}coinblocks_destroyed`,
name,
color,
}),
createBaseSeries({
metric: `${id}coindays_destroyed`,
name,
color,
}),
]);
}),
},
{
name: "Cumulative",
title: `Cumulative Coins Destroyed ${title}`,
bottom: list.flatMap(({ color, name, id: _id }) => {
const id = fixId(_id);
return /** @type {const} */ ([
createBaseSeries({
metric: `${id}coinblocks_destroyed_cumulative`,
name,
color,
}),
createBaseSeries({
metric: `${id}coindays_destroyed_cumulative`,
name,
color,
}),
]);
}),
},
],
},
]
: [
{
name: "Coins Destroyed",
title: `Coins Destroyed ${title}`,
bottom: list.flatMap(({ color, name, id: _id }) => {
const id = fixId(_id);
return /** @type {const} */ ([
createBaseSeries({
metric: `${id}coinblocks_destroyed`,
name: "sum",
color,
}),
createBaseSeries({
metric: `${id}coinblocks_destroyed_cumulative`,
name: "cumulative",
color,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}coindays_destroyed`,
name: "sum",
color,
}),
createBaseSeries({
metric: `${id}coindays_destroyed_cumulative`,
name: "cumulative",
color,
defaultActive: false,
}),
]);
}),
},
]),
],
});
}
@@ -2713,24 +2761,22 @@ export function createPartialOptions({ colors, brk }) {
bottom: [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
metric: `${id}_price_returns`,
title: "Returns",
title: "total",
type: "Baseline",
}),
createPriceLine({
unit: "percentage",
}),
...(brk.hasMetric(cagr)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
metric: cagr,
title: "cagr",
type: "Baseline",
}),
createPriceLine({
unit: "percentage",
colors: [colors.lime, colors.pink],
}),
]
: []),
createPriceLine({
unit: "percentage",
}),
],
};
}),
@@ -234,10 +234,6 @@ export function init({
iseries.update(last);
break;
}
case "dateindex": {
iseries.update(last);
break;
}
default: {
if (index === "weekindex") {
date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7));
@@ -257,7 +253,7 @@ export function init({
0,
1,
);
} else {
} else if (index !== "dateindex") {
throw Error("Unsupported");
}
@@ -270,7 +266,7 @@ export function init({
}
iseries.update(last);
} else {
latest.time = time;
last.time = time;
iseries.update(last);
}
}