feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,227 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! RPC middleware to collect prometheus metrics on RPC calls.
use std::time::Instant;
use jsonrpsee::{types::Request, MethodResponse};
use prometheus_endpoint::{
register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry,
U64,
};
/// Histogram time buckets in microseconds.
const HISTOGRAM_BUCKETS: [f64; 11] = [
5.0,
25.0,
100.0,
500.0,
1_000.0,
2_500.0,
10_000.0,
25_000.0,
100_000.0,
1_000_000.0,
10_000_000.0,
];
/// Metrics for RPC middleware storing information about the number of requests started/completed,
/// calls started/completed and their timings.
#[derive(Debug, Clone)]
pub struct RpcMetrics {
/// Histogram over RPC execution times.
calls_time: HistogramVec,
/// Number of calls started.
calls_started: CounterVec<U64>,
/// Number of calls completed.
calls_finished: CounterVec<U64>,
/// Number of Websocket sessions opened.
ws_sessions_opened: Option<Counter<U64>>,
/// Number of Websocket sessions closed.
ws_sessions_closed: Option<Counter<U64>>,
/// Histogram over RPC websocket sessions.
ws_sessions_time: HistogramVec,
}
impl RpcMetrics {
/// Create an instance of metrics
pub fn new(metrics_registry: Option<&Registry>) -> Result<Option<Self>, PrometheusError> {
if let Some(metrics_registry) = metrics_registry {
Ok(Some(Self {
calls_time: register(
HistogramVec::new(
HistogramOpts::new(
"bizinikiwi_rpc_calls_time",
"Total time [μs] of processed RPC calls",
)
.buckets(HISTOGRAM_BUCKETS.to_vec()),
&["protocol", "method", "is_rate_limited"],
)?,
metrics_registry,
)?,
calls_started: register(
CounterVec::new(
Opts::new(
"bizinikiwi_rpc_calls_started",
"Number of received RPC calls (unique un-batched requests)",
),
&["protocol", "method"],
)?,
metrics_registry,
)?,
calls_finished: register(
CounterVec::new(
Opts::new(
"bizinikiwi_rpc_calls_finished",
"Number of processed RPC calls (unique un-batched requests)",
),
&["protocol", "method", "is_error", "is_rate_limited"],
)?,
metrics_registry,
)?,
ws_sessions_opened: register(
Counter::new(
"bizinikiwi_rpc_sessions_opened",
"Number of persistent RPC sessions opened",
)?,
metrics_registry,
)?
.into(),
ws_sessions_closed: register(
Counter::new(
"bizinikiwi_rpc_sessions_closed",
"Number of persistent RPC sessions closed",
)?,
metrics_registry,
)?
.into(),
ws_sessions_time: register(
HistogramVec::new(
HistogramOpts::new(
"bizinikiwi_rpc_sessions_time",
"Total time [s] for each websocket session",
)
.buckets(HISTOGRAM_BUCKETS.to_vec()),
&["protocol"],
)?,
metrics_registry,
)?,
}))
} else {
Ok(None)
}
}
pub(crate) fn ws_connect(&self) {
self.ws_sessions_opened.as_ref().map(|counter| counter.inc());
}
pub(crate) fn ws_disconnect(&self, now: Instant) {
let micros = now.elapsed().as_secs();
self.ws_sessions_closed.as_ref().map(|counter| counter.inc());
self.ws_sessions_time.with_label_values(&["ws"]).observe(micros as _);
}
pub(crate) fn on_call(&self, req: &Request, transport_label: &'static str) {
log::trace!(
target: "rpc_metrics",
"[{transport_label}] on_call name={} params={:?}",
req.method_name(),
req.params(),
);
self.calls_started
.with_label_values(&[transport_label, req.method_name()])
.inc();
}
pub(crate) fn on_response(
&self,
req: &Request,
rp: &MethodResponse,
is_rate_limited: bool,
transport_label: &'static str,
now: Instant,
) {
log::trace!(target: "rpc_metrics", "[{transport_label}] on_response started_at={:?}", now);
log::trace!(target: "rpc_metrics::extra", "[{transport_label}] result={}", rp.as_result());
let micros = now.elapsed().as_micros();
log::debug!(
target: "rpc_metrics",
"[{transport_label}] {} call took {} μs",
req.method_name(),
micros,
);
self.calls_time
.with_label_values(&[
transport_label,
req.method_name(),
if is_rate_limited { "true" } else { "false" },
])
.observe(micros as _);
self.calls_finished
.with_label_values(&[
transport_label,
req.method_name(),
// the label "is_error", so `success` should be regarded as false
// and vice-versa to be registered correctly.
if rp.is_success() { "false" } else { "true" },
if is_rate_limited { "true" } else { "false" },
])
.inc();
}
}
/// Metrics with transport label.
#[derive(Clone, Debug)]
pub struct Metrics {
pub(crate) inner: RpcMetrics,
pub(crate) transport_label: &'static str,
}
impl Metrics {
/// Create a new [`Metrics`].
pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self {
Self { inner: metrics, transport_label }
}
pub(crate) fn ws_connect(&self) {
self.inner.ws_connect();
}
pub(crate) fn ws_disconnect(&self, now: Instant) {
self.inner.ws_disconnect(now)
}
pub(crate) fn on_call(&self, req: &Request) {
self.inner.on_call(req, self.transport_label)
}
pub(crate) fn on_response(
&self,
req: &Request,
rp: &MethodResponse,
is_rate_limited: bool,
now: Instant,
) {
self.inner.on_response(req, rp, is_rate_limited, self.transport_label, now)
}
}
@@ -0,0 +1,150 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! JSON-RPC specific middleware.
use std::{
num::NonZeroU32,
time::{Duration, Instant},
};
use futures::future::{BoxFuture, FutureExt};
use governor::{clock::Clock, Jitter};
use jsonrpsee::{
server::middleware::rpc::RpcServiceT,
types::{ErrorObject, Id, Request},
MethodResponse,
};
mod metrics;
mod node_health;
mod rate_limit;
pub use metrics::*;
pub use node_health::*;
pub use rate_limit::*;
const MAX_JITTER: Duration = Duration::from_millis(50);
const MAX_RETRIES: usize = 10;
/// JSON-RPC middleware layer.
#[derive(Debug, Clone, Default)]
pub struct MiddlewareLayer {
rate_limit: Option<RateLimit>,
metrics: Option<Metrics>,
}
impl MiddlewareLayer {
/// Create an empty MiddlewareLayer.
pub fn new() -> Self {
Self::default()
}
/// Enable new rate limit middleware enforced per minute.
pub fn with_rate_limit_per_minute(self, n: NonZeroU32) -> Self {
Self { rate_limit: Some(RateLimit::per_minute(n)), metrics: self.metrics }
}
/// Enable metrics middleware.
pub fn with_metrics(self, metrics: Metrics) -> Self {
Self { rate_limit: self.rate_limit, metrics: Some(metrics) }
}
/// Register a new websocket connection.
pub fn ws_connect(&self) {
self.metrics.as_ref().map(|m| m.ws_connect());
}
/// Register that a websocket connection was closed.
pub fn ws_disconnect(&self, now: Instant) {
self.metrics.as_ref().map(|m| m.ws_disconnect(now));
}
}
impl<S> tower::Layer<S> for MiddlewareLayer {
type Service = Middleware<S>;
fn layer(&self, service: S) -> Self::Service {
Middleware { service, rate_limit: self.rate_limit.clone(), metrics: self.metrics.clone() }
}
}
/// JSON-RPC middleware that handles metrics
/// and rate-limiting.
///
/// These are part of the same middleware
/// because the metrics needs to know whether
/// a call was rate-limited or not because
/// it will impact the roundtrip for a call.
pub struct Middleware<S> {
service: S,
rate_limit: Option<RateLimit>,
metrics: Option<Metrics>,
}
impl<'a, S> RpcServiceT<'a> for Middleware<S>
where
S: Send + Sync + RpcServiceT<'a> + Clone + 'static,
{
type Future = BoxFuture<'a, MethodResponse>;
fn call(&self, req: Request<'a>) -> Self::Future {
let now = Instant::now();
self.metrics.as_ref().map(|m| m.on_call(&req));
let service = self.service.clone();
let rate_limit = self.rate_limit.clone();
let metrics = self.metrics.clone();
async move {
let mut is_rate_limited = false;
if let Some(limit) = rate_limit.as_ref() {
let mut attempts = 0;
let jitter = Jitter::up_to(MAX_JITTER);
loop {
if attempts >= MAX_RETRIES {
return reject_too_many_calls(req.id);
}
if let Err(rejected) = limit.inner.check() {
tokio::time::sleep(jitter + rejected.wait_time_from(limit.clock.now()))
.await;
} else {
break;
}
is_rate_limited = true;
attempts += 1;
}
}
let rp = service.call(req.clone()).await;
metrics.as_ref().map(|m| m.on_response(&req, &rp, is_rate_limited, now));
rp
}
.boxed()
}
}
fn reject_too_many_calls(id: Id) -> MethodResponse {
MethodResponse::error(id, ErrorObject::owned(-32999, "RPC rate limit exceeded", None::<()>))
}
@@ -0,0 +1,207 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Middleware for handling `/health` and `/health/readiness` endpoints.
use std::{
error::Error,
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures::future::FutureExt;
use http::{HeaderValue, Method, StatusCode, Uri};
use jsonrpsee::{
server::{HttpBody, HttpRequest, HttpResponse},
types::{Response as RpcResponse, ResponseSuccess as RpcResponseSuccess},
};
use tower::Service;
const RPC_SYSTEM_HEALTH_CALL: &str = r#"{"jsonrpc":"2.0","method":"system_health","id":0}"#;
const HEADER_VALUE_JSON: HeaderValue = HeaderValue::from_static("application/json; charset=utf-8");
/// Layer that applies [`NodeHealthProxy`] which
/// proxies `/health` and `/health/readiness` endpoints.
#[derive(Debug, Clone, Default)]
pub struct NodeHealthProxyLayer;
impl<S> tower::Layer<S> for NodeHealthProxyLayer {
type Service = NodeHealthProxy<S>;
fn layer(&self, service: S) -> Self::Service {
NodeHealthProxy::new(service)
}
}
/// Middleware that proxies `/health` and `/health/readiness` endpoints.
pub struct NodeHealthProxy<S>(S);
impl<S> NodeHealthProxy<S> {
/// Creates a new [`NodeHealthProxy`].
pub fn new(service: S) -> Self {
Self(service)
}
}
impl<S> tower::Service<http::Request<hyper::body::Incoming>> for NodeHealthProxy<S>
where
S: Service<HttpRequest, Response = HttpResponse>,
S::Response: 'static,
S::Error: Into<Box<dyn Error + Send + Sync>> + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = Box<dyn Error + Send + Sync + 'static>;
type Future =
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx).map_err(Into::into)
}
fn call(&mut self, req: http::Request<hyper::body::Incoming>) -> Self::Future {
let mut req = req.map(|body| HttpBody::new(body));
let maybe_intercept = InterceptRequest::from_http(&req);
// Modify the request and proxy it to `system_health`
if let InterceptRequest::Health | InterceptRequest::Readiness = maybe_intercept {
// RPC methods are accessed with `POST`.
*req.method_mut() = Method::POST;
// Precautionary remove the URI.
*req.uri_mut() = Uri::from_static("/");
// Requests must have the following headers:
req.headers_mut().insert(http::header::CONTENT_TYPE, HEADER_VALUE_JSON);
req.headers_mut().insert(http::header::ACCEPT, HEADER_VALUE_JSON);
// Adjust the body to reflect the method call.
req = req.map(|_| HttpBody::from(RPC_SYSTEM_HEALTH_CALL));
}
// Call the inner service and get a future that resolves to the response.
let fut = self.0.call(req);
async move {
Ok(match maybe_intercept {
InterceptRequest::Deny =>
http_response(StatusCode::METHOD_NOT_ALLOWED, HttpBody::empty()),
InterceptRequest::No => fut.await.map_err(|err| err.into())?,
InterceptRequest::Health => {
let res = fut.await.map_err(|err| err.into())?;
if let Ok(health) = parse_rpc_response(res.into_body()).await {
http_ok_response(serde_json::to_string(&health)?)
} else {
http_internal_error()
}
},
InterceptRequest::Readiness => {
let res = fut.await.map_err(|err| err.into())?;
match parse_rpc_response(res.into_body()).await {
Ok(health)
if (!health.is_syncing && health.peers > 0) ||
!health.should_have_peers =>
http_ok_response(HttpBody::empty()),
_ => http_internal_error(),
}
},
})
}
.boxed()
}
}
// NOTE: This is duplicated here to avoid dependency to the `RPC API`.
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Health {
/// Number of connected peers
pub peers: usize,
/// Is the node syncing
pub is_syncing: bool,
/// Should this node have any peers
///
/// Might be false for local chains or when running without discovery.
pub should_have_peers: bool,
}
fn http_ok_response<S: Into<HttpBody>>(body: S) -> HttpResponse {
http_response(StatusCode::OK, body)
}
fn http_response<S: Into<HttpBody>>(status_code: StatusCode, body: S) -> HttpResponse {
HttpResponse::builder()
.status(status_code)
.header(http::header::CONTENT_TYPE, HEADER_VALUE_JSON)
.body(body.into())
.expect("Header is valid; qed")
}
fn http_internal_error() -> HttpResponse {
http_response(hyper::StatusCode::INTERNAL_SERVER_ERROR, HttpBody::empty())
}
async fn parse_rpc_response(
body: HttpBody,
) -> Result<Health, Box<dyn Error + Send + Sync + 'static>> {
use http_body_util::BodyExt;
let bytes = body.collect().await?.to_bytes();
let raw_rp = serde_json::from_slice::<RpcResponse<Health>>(&bytes)?;
let rp = RpcResponseSuccess::<Health>::try_from(raw_rp)?;
Ok(rp.result)
}
/// Whether the request should be treated as ordinary RPC call or be modified.
enum InterceptRequest {
/// Proxy `/health` to `system_health`.
Health,
/// Checks if node has at least one peer and is not doing major syncing.
///
/// Returns HTTP status code 200 on success otherwise HTTP status code 500 is returned.
Readiness,
/// Treat as a ordinary RPC call and don't modify the request or response.
No,
/// Deny health or readiness calls that is not HTTP GET request.
///
/// Returns HTTP status code 405.
Deny,
}
impl InterceptRequest {
fn from_http(req: &HttpRequest) -> InterceptRequest {
match req.uri().path() {
"/health" =>
if req.method() == http::Method::GET {
InterceptRequest::Health
} else {
InterceptRequest::Deny
},
"/health/readiness" =>
if req.method() == http::Method::GET {
InterceptRequest::Readiness
} else {
InterceptRequest::Deny
},
// Forward all other requests to the RPC server.
_ => InterceptRequest::No,
}
}
}
@@ -0,0 +1,47 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! RPC rate limit.
use governor::{
clock::{DefaultClock, QuantaClock},
middleware::NoOpMiddleware,
state::{InMemoryState, NotKeyed},
Quota,
};
use std::{num::NonZeroU32, sync::Arc};
type RateLimitInner = governor::RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>;
/// Rate limit.
#[derive(Debug, Clone)]
pub struct RateLimit {
pub(crate) inner: Arc<RateLimitInner>,
pub(crate) clock: QuantaClock,
}
impl RateLimit {
/// Create a new `RateLimit` per minute.
pub fn per_minute(n: NonZeroU32) -> Self {
let clock = QuantaClock::default();
Self {
inner: Arc::new(RateLimitInner::direct_with_clock(Quota::per_minute(n), &clock)),
clock,
}
}
}