global: fixes

This commit is contained in:
nym21
2026-04-27 11:19:05 +02:00
parent e543e4a5db
commit b24bfdc15c
15 changed files with 116 additions and 111 deletions

View File

@@ -110,8 +110,9 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec<String> {
for (name, schema) in schemas {
let mut type_deps = BTreeSet::new();
collect_schema_refs(schema, &mut type_deps);
// Only keep deps that are in our schemas
type_deps.retain(|d| schemas.contains_key(d));
// Only keep deps that are in our schemas, and drop self-references
// (handled at emit time by quoting via current_type)
type_deps.retain(|d| schemas.contains_key(d) && d != name);
deps.insert(name.clone(), type_deps);
}

View File

@@ -1,5 +1,5 @@
use std::{
fs,
fs, io,
path::{Path, PathBuf},
};
@@ -10,52 +10,53 @@ use brk_server::{
};
use brk_types::Port;
use owo_colors::OwoColorize;
use serde::{Deserialize, Deserializer, Serialize};
use serde::{Deserialize, Serialize};
use crate::{default_brk_path, dot_brk_path, fix_user_path};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
brkdir: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
brkport: Option<Port>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
website: Option<Website>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
cdn: Option<bool>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
maxweight: Option<usize>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
maxweightlocal: Option<usize>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
cachesize: Option<usize>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
bitcoindir: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
blocksdir: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
rpcconnect: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
rpcport: Option<u16>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
rpccookiefile: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
rpcuser: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
#[serde(default)]
rpcpassword: Option<String>,
}
@@ -319,10 +320,18 @@ Finally, you can run the program with '-h' for help."
}
fn read(path: &Path) -> Self {
fs::read_to_string(path).map_or_else(
|_| Config::default(),
|contents| toml::from_str(&contents).unwrap_or_default(),
)
let contents = match fs::read_to_string(path) {
Ok(contents) => contents,
Err(e) if e.kind() == io::ErrorKind::NotFound => return Config::default(),
Err(e) => {
eprintln!("Cannot read {}: {e}", path.display());
std::process::exit(1);
}
};
toml::from_str(&contents).unwrap_or_else(|e| {
eprintln!("Invalid {}:\n{e}", path.display());
std::process::exit(1);
})
}
pub fn rpc(&self) -> Result<Client> {
@@ -413,14 +422,3 @@ Finally, you can run the program with '-h' for help."
self.brkport
}
}
fn default_on_error<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Default,
{
match T::deserialize(deserializer) {
Ok(v) => Ok(v),
Err(_) => Ok(T::default()),
}
}

View File

@@ -8897,7 +8897,7 @@ pub struct BrkClient {
impl BrkClient {
/// Client version.
pub const VERSION: &'static str = "v0.3.0-beta.5";
pub const VERSION: &'static str = "v0.3.0-beta.6";
/// Create a new client with the given base URL.
pub fn new(base_url: impl Into<String>) -> Self {

View File

@@ -70,6 +70,7 @@ struct CostBasisCohortParam {
}
#[derive(Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
struct CostBasisQuery {
#[serde(default)]
bucket: UrpdAggregation,

View File

@@ -4,6 +4,7 @@ use serde::Deserialize;
use brk_types::Txid;
#[derive(Debug, Default, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AddrTxidsParam {
/// Txid to paginate from (return transactions before this one)
pub after_txid: Option<Txid>,

View File

@@ -11,6 +11,7 @@ pub struct TimestampParam {
/// Optional UNIX timestamp query parameter
#[derive(Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct OptionalTimestampParam {
pub timestamp: Option<Timestamp>,
}

View File

@@ -19,6 +19,7 @@ pub struct UrpdCohortParam {
/// Query parameters for URPD endpoints.
#[derive(Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct UrpdQuery {
/// Aggregation strategy. Default: raw (no aggregation). Accepts `bucket` as alias.
#[serde(default, rename = "agg", alias = "bucket")]

View File

@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
/// Pagination parameters for paginated API endpoints
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Pagination {
/// Pagination index
#[serde(default, alias = "p")]

View File

@@ -4,6 +4,7 @@ use serde::Deserialize;
use crate::{Limit, SeriesName};
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SearchQuery {
/// Search query string
pub q: SeriesName,

View File

@@ -7271,7 +7271,7 @@ function createTransferPattern(client, acc) {
* @extends BrkClientBase
*/
class BrkClient extends BrkClientBase {
VERSION = "v0.3.0-beta.5";
VERSION = "v0.3.0-beta.6";
INDEXES = /** @type {const} */ ([
"minute10",

View File

@@ -40,5 +40,5 @@
"url": "git+https://github.com/bitcoinresearchkit/brk.git"
},
"type": "module",
"version": "0.3.0-beta.5"
"version": "0.3.0-beta.6"
}

View File

@@ -21,10 +21,14 @@ T = TypeVar('T')
Addr = str
# US Dollar amount
Dollars = float
# Amount in satoshis (1 BTC = 100,000,000 sats)
Sats = int
# Index within its type (e.g., 0 for first P2WPKH address)
TypeIndex = int
# Type (P2PKH, P2WPKH, P2SH, P2TR, etc.)
OutputType = Literal["p2pk", "p2pk", "p2pkh", "multisig", "p2sh", "op_return", "v0_p2wpkh", "v0_p2wsh", "v1_p2tr", "p2a", "empty", "unknown"]
# Transaction ID (hash)
Txid = str
# Unified index for any address type (funded or empty)
AnyAddrIndex = TypeIndex
# Unsigned basis points stored as u16.
@@ -51,10 +55,14 @@ BasisPointsSigned32 = int
Bitcoin = float
# URL-friendly mining pool identifier
PoolSlug = Literal["unknown", "blockfills", "ultimuspool", "terrapool", "luxor", "onethash", "btccom", "bitfarms", "huobipool", "wayicn", "canoepool", "btctop", "bitcoincom", "pool175btc", "gbminers", "axbt", "asicminer", "bitminter", "bitcoinrussia", "btcserv", "simplecoinus", "btcguild", "eligius", "ozcoin", "eclipsemc", "maxbtc", "triplemining", "coinlab", "pool50btc", "ghashio", "stminingcorp", "bitparking", "mmpool", "polmine", "kncminer", "bitalo", "f2pool", "hhtt", "megabigpower", "mtred", "nmcbit", "yourbtcnet", "givemecoins", "braiinspool", "antpool", "multicoinco", "bcpoolio", "cointerra", "kanopool", "solock", "ckpool", "nicehash", "bitclub", "bitcoinaffiliatenetwork", "btcc", "bwpool", "exxbw", "bitsolo", "bitfury", "twentyoneinc", "digitalbtc", "eightbaochi", "mybtccoinpool", "tbdice", "hashpool", "nexious", "bravomining", "hotpool", "okexpool", "bcmonster", "onehash", "bixin", "tatmaspool", "viabtc", "connectbtc", "batpool", "waterhole", "dcexploration", "dcex", "btpool", "fiftyeightcoin", "bitcoinindia", "shawnp0wers", "phashio", "rigpool", "haozhuzhu", "sevenpool", "miningkings", "hashbx", "dpool", "rawpool", "haominer", "helix", "bitcoinukraine", "poolin", "secretsuperstar", "tigerpoolnet", "sigmapoolcom", "okpooltop", "hummerpool", "tangpool", "bytepool", "spiderpool", "novablock", "miningcity", "binancepool", "minerium", "lubiancom", "okkong", "aaopool", "emcdpool", "foundryusa", "sbicrypto", "arkpool", "purebtccom", "marapool", "kucoinpool", "entrustcharitypool", "okminer", "titan", "pegapool", "btcnuggets", "cloudhashing", "digitalxmintsy", "telco214", "btcpoolparty", "multipool", "transactioncoinmining", "btcdig", "trickysbtcpool", "btcmp", "eobot", "unomp", "patels", "gogreenlight", "bitcoinindiapool", "ekanembtc", "canoe", "tiger", "onem1x", "zulupool", "secpool", "ocean", "whitepool", "wiz", "wk057", "futurebitapollosolo", "carbonnegative", "portlandhodl", "phoenix", "neopool", "maxipool", "bitfufupool", "gdpool", "miningdutch", "publicpool", "miningsquared", "innopolistech", "btclab", "parasite", "redrockpool", "est3lar", "braiinssolo", "solopool"]
# Fee rate in sat/vB
FeeRate = float
# Weight in weight units (WU). Max block weight is 4,000,000 WU.
Weight = int
# Block height
Height = int
# UNIX timestamp in seconds
Timestamp = int
# Block hash
BlockHash = str
# Transaction index within a block (0 = coinbase)
@@ -90,6 +98,8 @@ CostBasisValue = Literal["supply", "realized", "unrealized"]
# Options: raw (no aggregation), lin200/lin500/lin1000 (linear $200/$500/$1000),
# log10/log50/log100/log200 (logarithmic with 10/50/100/200 buckets per decade).
UrpdAggregation = Literal["raw", "lin200", "lin500", "lin1000", "log10", "log50", "log100", "log200"]
# Virtual size in vbytes (weight / 4, rounded up). Max block vsize is ~1,000,000 vB.
VSize = int
# Date in YYYYMMDD format stored as u32
Date = int
# Output format for API responses
@@ -112,6 +122,9 @@ High = Dollars
Hour1 = int
Hour12 = int
Hour4 = int
# Aggregation dimension for querying series. Includes time-based (date, week, month, year),
# block-based (height, tx_index), and address/output type indexes.
Index = Literal["minute10", "minute30", "hour1", "hour4", "hour12", "day1", "day3", "week1", "month1", "month3", "month6", "year1", "year10", "halving", "epoch", "height", "tx_index", "txin_index", "txout_index", "empty_output_index", "op_return_index", "p2a_addr_index", "p2ms_output_index", "p2pk33_addr_index", "p2pk65_addr_index", "p2pkh_addr_index", "p2sh_addr_index", "p2tr_addr_index", "p2wpkh_addr_index", "p2wsh_addr_index", "unknown_output_index", "funded_addr_index", "empty_addr_index"]
# Series name
SeriesName = str
# Lowest price value for a time period
@@ -201,6 +214,8 @@ Witness = List[str]
# Unlike TxVersion (u8, indexed), this preserves non-standard values
# used in coinbase txs for miner signaling/branding.
TxVersionRaw = int
# Hierarchical tree node for organizing series into categories
TreeNode = Union[dict[str, "TreeNode"], "SeriesLeafWithSchema"]
TxInIndex = int
TxOutIndex = int
# Input index in the spending transaction
@@ -211,21 +226,6 @@ UnknownOutputIndex = TypeIndex
Week1 = int
Year1 = int
Year10 = int
# Fee rate in sat/vB
FeeRate = float
# Aggregation dimension for querying series. Includes time-based (date, week, month, year),
# block-based (height, tx_index), and address/output type indexes.
Index = Literal["minute10", "minute30", "hour1", "hour4", "hour12", "day1", "day3", "week1", "month1", "month3", "month6", "year1", "year10", "halving", "epoch", "height", "tx_index", "txin_index", "txout_index", "empty_output_index", "op_return_index", "p2a_addr_index", "p2ms_output_index", "p2pk33_addr_index", "p2pk65_addr_index", "p2pkh_addr_index", "p2sh_addr_index", "p2tr_addr_index", "p2wpkh_addr_index", "p2wsh_addr_index", "unknown_output_index", "funded_addr_index", "empty_addr_index"]
# Amount in satoshis (1 BTC = 100,000,000 sats)
Sats = int
# UNIX timestamp in seconds
Timestamp = int
# Hierarchical tree node for organizing series into categories
TreeNode = Union[dict[str, "TreeNode"], "SeriesLeafWithSchema"]
# Transaction ID (hash)
Txid = str
# Virtual size in vbytes (weight / 4, rounded up). Max block vsize is ~1,000,000 vB.
VSize = int
class AddrChainStats(TypedDict):
"""
Address statistics on the blockchain (confirmed transactions only)
@@ -1241,6 +1241,45 @@ class Prices(TypedDict):
time: Timestamp
USD: Dollars
class RbfTx(TypedDict):
"""
Transaction summary carried inside an RBF replacement node. Shape
matches mempool.space's `/api/v1/tx/:txid/rbf` and
`/api/v1/replacements` responses.
Attributes:
value: Sum of output amounts.
rbf: BIP-125 signaling: at least one input has sequence < 0xffffffff-1.
fullRbf: Only populated on the root `tx` of an RBF response. `true` iff
this tx displaced at least one non-signaling predecessor.
"""
txid: Txid
fee: Sats
vsize: VSize
value: Sats
rate: FeeRate
time: Timestamp
rbf: bool
fullRbf: Optional[bool]
class ReplacementNode(TypedDict):
"""
One node in an RBF replacement tree. The node's `tx` replaced each
entry in `replaces`, recursively.
Attributes:
time: First-seen timestamp, duplicated here to match mempool.space's
on-the-wire shape.
fullRbf: Any predecessor in this subtree was non-signaling.
interval: Seconds between this node's `time` and the successor that
replaced it. Omitted on the root of an RBF response.
"""
tx: RbfTx
time: Timestamp
fullRbf: bool
interval: Optional[int]
replaces: List["ReplacementNode"]
class RbfResponse(TypedDict):
"""
Response body for `GET /api/v1/tx/:txid/rbf`. Both fields are null
@@ -1304,6 +1343,21 @@ class SeriesInfo(TypedDict):
indexes: List[Index]
type: str
class SeriesLeafWithSchema(TypedDict):
"""
SeriesLeaf with JSON Schema for client generation
Attributes:
name: The series name/identifier
kind: The Rust type (e.g., "Sats", "StoredF64")
indexes: Available indexes for this series
type: JSON Schema type (e.g., "integer", "number", "string", "boolean", "array", "object")
"""
name: str
kind: str
indexes: List[Index]
type: str
class SeriesNameWithIndex(TypedDict):
"""
Attributes:
@@ -1595,60 +1649,6 @@ class ValidateAddrParam(TypedDict):
"""
address: str
class RbfTx(TypedDict):
"""
Transaction summary carried inside an RBF replacement node. Shape
matches mempool.space's `/api/v1/tx/:txid/rbf` and
`/api/v1/replacements` responses.
Attributes:
value: Sum of output amounts.
rbf: BIP-125 signaling: at least one input has sequence < 0xffffffff-1.
fullRbf: Only populated on the root `tx` of an RBF response. `true` iff
this tx displaced at least one non-signaling predecessor.
"""
txid: Txid
fee: Sats
vsize: VSize
value: Sats
rate: FeeRate
time: Timestamp
rbf: bool
fullRbf: Optional[bool]
class ReplacementNode(TypedDict):
"""
One node in an RBF replacement tree. The node's `tx` replaced each
entry in `replaces`, recursively.
Attributes:
time: First-seen timestamp, duplicated here to match mempool.space's
on-the-wire shape.
fullRbf: Any predecessor in this subtree was non-signaling.
interval: Seconds between this node's `time` and the successor that
replaced it. Omitted on the root of an RBF response.
"""
tx: RbfTx
time: Timestamp
fullRbf: bool
interval: Optional[int]
replaces: List["ReplacementNode"]
class SeriesLeafWithSchema(TypedDict):
"""
SeriesLeaf with JSON Schema for client generation
Attributes:
name: The series name/identifier
kind: The Rust type (e.g., "Sats", "StoredF64")
indexes: Available indexes for this series
type: JSON Schema type (e.g., "integer", "number", "string", "boolean", "array", "object")
"""
name: str
kind: str
indexes: List[Index]
type: str
class BrkError(Exception):
"""Custom error class for BRK client errors."""
@@ -6522,7 +6522,7 @@ class SeriesTree:
class BrkClient(BrkClientBase):
"""Main BRK client with series tree and API methods."""
VERSION = "v0.3.0-beta.5"
VERSION = "v0.3.0-beta.6"
INDEXES = [
"minute10",

View File

@@ -1,6 +1,6 @@
[project]
name = "brk-client"
version = "0.3.0-beta.5"
version = "0.3.0-beta.6"
description = "Bitcoin on-chain analytics client — thousands of metrics, block explorer, and address index"
readme = "README.md"
requires-python = ">=3.9"

View File

@@ -50,7 +50,7 @@ wheels = [
[[package]]
name = "brk-client"
version = "0.3.0b2"
version = "0.3.0b6"
source = { editable = "." }
[package.dev-dependencies]

View File

@@ -10,8 +10,8 @@ fi
echo "=== Cloudflare cache purge ==="
echo ""
if [ -z "$CF_API_TOKEN" ]; then
echo "CF_API_TOKEN not set. Add it to scripts/.tokens"
if [ -z "$CF_PURGE_API_TOKEN" ]; then
echo "CF_PURGE_API_TOKEN not set. Add it to scripts/.tokens"
exit 1
fi
if [ -z "$CF_ZONE_ID" ]; then
@@ -21,7 +21,7 @@ fi
RESPONSE=$(curl -sS -X POST \
"https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Authorization: Bearer $CF_PURGE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}')