Skip to content

Commit 66ccb9a

Browse files
authored
Merge pull request #285 from ergoplatform/i53-prom-monitoring
Export Prometheus metrics
2 parents ab90edc + 098d391 commit 66ccb9a

File tree

13 files changed

+1608
-246
lines changed

13 files changed

+1608
-246
lines changed

core/Cargo.toml

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
[package]
22
name = "oracle-core"
33
version = "2.0.0-beta9"
4-
authors = ["Robert Kornacki <[email protected]>", "@greenhat", "@kettlebell", "@SethDusek"]
4+
authors = [
5+
"Robert Kornacki <[email protected]>",
6+
"@greenhat",
7+
"@kettlebell",
8+
"@SethDusek",
9+
]
510
edition = "2021"
611

712
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -29,19 +34,20 @@ ergo-lib = { workspace = true }
2934
ergo-node-interface = { git = "https://github.com/ergoplatform/ergo-node-interface-rust", rev = "143c2a3dc8fb772d1af37f1f1e1924067c6aad14" }
3035
# ergo-node-interface = { version = "0.4" }
3136
derive_more = "0.99"
32-
clap = {version = "4.2.4", features = ["derive"]}
37+
clap = { version = "4.2.4", features = ["derive"] }
3338
exitcode = "1.1.2"
3439
lazy_static = "1.4.0"
3540
once_cell = "1.15.0"
3641
futures = "0.3"
42+
prometheus = "0.13"
3743

3844
[dev-dependencies]
39-
ergo-lib = { workspace = true, features = ["arbitrary"]}
40-
proptest = {version = "1.0.0"}
41-
proptest-derive = {version = "0.3.0"}
42-
sigma-test-util = {version = "0.3.0"}
43-
ergo-chain-sim = {version = "0.1.0", path="../ergo-chain-sim"}
44-
env_logger = {version = "0.10.0"}
45-
tokio-test = {version = "0.4"}
46-
pretty_assertions = {workspace = true}
45+
ergo-lib = { workspace = true, features = ["arbitrary"] }
46+
proptest = { version = "1.0.0" }
47+
proptest-derive = { version = "0.3.0" }
48+
sigma-test-util = { version = "0.3.0" }
49+
ergo-chain-sim = { version = "0.1.0", path = "../ergo-chain-sim" }
50+
env_logger = { version = "0.10.0" }
51+
tokio-test = { version = "0.4" }
52+
pretty_assertions = { workspace = true }
4753
expect-test = "1.0.1"

core/src/address_util.rs

Lines changed: 14 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use ergo_lib::ergotree_ir::{
2-
chain::address::{Address, AddressEncoder, AddressEncoderError},
3-
mir::constant::{Constant, Literal},
4-
serialization::{SigmaParsingError, SigmaSerializable, SigmaSerializationError},
5-
sigma_protocol::sigma_boolean::ProveDlog,
6-
};
1+
use ergo_lib::ergo_chain_types::EcPoint;
2+
use ergo_lib::ergotree_ir::chain::address::Address;
3+
use ergo_lib::ergotree_ir::chain::address::AddressEncoderError;
4+
use ergo_lib::ergotree_ir::chain::address::NetworkAddress;
5+
use ergo_lib::ergotree_ir::chain::address::NetworkPrefix;
6+
use ergo_lib::ergotree_ir::serialization::SigmaParsingError;
7+
use ergo_lib::ergotree_ir::serialization::SigmaSerializationError;
78
use thiserror::Error;
89

910
#[derive(Error, Debug)]
@@ -22,126 +23,11 @@ pub enum AddressUtilError {
2223
Base16DecodeError(#[from] base16::DecodeError),
2324
}
2425

25-
/// Given a P2S Ergo address, extract the hex-encoded serialized ErgoTree (script)
26-
pub fn address_to_tree(address: &str) -> Result<String, AddressUtilError> {
27-
let address_parsed = AddressEncoder::unchecked_parse_network_address_from_str(address)?;
28-
let script = address_parsed.address().script()?;
29-
Ok(base16::encode_lower(&script.sigma_serialize_bytes()?))
30-
}
31-
32-
/// Given a P2S Ergo address, convert it to a hex-encoded Sigma byte array constant
33-
pub fn address_to_bytes(address: &str) -> Result<String, AddressUtilError> {
34-
let address_parsed = AddressEncoder::unchecked_parse_network_address_from_str(address)?;
35-
let script = address_parsed.address().script()?;
36-
Ok(base16::encode_lower(
37-
&Constant::from(script.sigma_serialize_bytes()?).sigma_serialize_bytes()?,
38-
))
39-
}
40-
41-
/// Given an Ergo P2PK Address, convert it to a raw hex-encoded EC point
42-
/// and prepend the type bytes so it is encoded and ready
43-
/// to be used in a register.
44-
pub fn address_to_raw_for_register(address: &str) -> Result<String, AddressUtilError> {
45-
let address_parsed = AddressEncoder::unchecked_parse_network_address_from_str(address)?;
46-
match address_parsed.address() {
47-
Address::P2Pk(ProveDlog { h }) => Ok(base16::encode_lower(
48-
&Constant::from(*h).sigma_serialize_bytes()?,
49-
)),
50-
Address::P2SH(_) | Address::P2S(_) => Err(AddressUtilError::ExpectedP2PK),
51-
}
52-
}
53-
54-
/// Given an Ergo P2PK Address, convert it to a raw hex-encoded EC point
55-
pub fn address_to_raw(address: &str) -> Result<String, AddressUtilError> {
56-
let address_parsed = AddressEncoder::unchecked_parse_network_address_from_str(address)?;
57-
match address_parsed.address() {
58-
Address::P2Pk(_) => Ok(base16::encode_lower(
59-
&address_parsed.address().content_bytes(),
60-
)),
61-
Address::P2SH(_) | Address::P2S(_) => Err(AddressUtilError::ExpectedP2PK),
62-
}
63-
}
64-
65-
/// Given a raw hex-encoded EC point, convert it to a P2PK address
66-
pub fn raw_to_address(raw: &str) -> Result<Address, AddressUtilError> {
67-
let bytes = base16::decode(raw)?;
68-
Address::p2pk_from_pk_bytes(&bytes).map_err(Into::into)
69-
}
70-
71-
/// Given a raw hex-encoded EC point from a register (thus with type encoded characters in front),
72-
/// convert it to a P2PK address
73-
pub fn raw_from_register_to_address(raw: &str) -> Result<Address, AddressUtilError> {
74-
let bytes = base16::decode(raw)?;
75-
let constant = Constant::sigma_parse_bytes(&bytes)?;
76-
if let Literal::GroupElement(h) = constant.v {
77-
Ok(Address::P2Pk(ProveDlog { h }))
78-
} else {
79-
Err(AddressUtilError::ExpectedP2PK)
80-
}
81-
}
82-
83-
#[cfg(test)]
84-
mod test {
85-
use ergo_lib::ergotree_ir::chain::address::{AddressEncoder, NetworkPrefix};
86-
87-
use crate::address_util::{
88-
address_to_bytes, address_to_raw, address_to_raw_for_register, address_to_tree,
89-
raw_from_register_to_address, raw_to_address,
90-
};
91-
92-
// Test serialization for default address argument of /utils/addressToRaw
93-
#[test]
94-
fn test_address_to_raw_for_register() {
95-
assert_eq!(
96-
"07028333f9f7454f8d5ff73dbac9833767ed6fc3a86cf0a73df946b32ea9927d9197",
97-
address_to_raw_for_register("3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt")
98-
.unwrap()
99-
);
100-
assert_eq!(
101-
"028333f9f7454f8d5ff73dbac9833767ed6fc3a86cf0a73df946b32ea9927d9197",
102-
address_to_raw("3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt").unwrap()
103-
);
104-
}
105-
#[test]
106-
fn test_address_raw_roundtrip() {
107-
let address = AddressEncoder::new(NetworkPrefix::Testnet)
108-
.parse_address_from_str("3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt")
109-
.unwrap();
110-
assert_eq!(
111-
address,
112-
raw_to_address(
113-
&address_to_raw("3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt").unwrap()
114-
)
115-
.unwrap()
116-
);
117-
}
118-
#[test]
119-
fn test_address_raw_register_roundtrip() {
120-
let address = AddressEncoder::new(NetworkPrefix::Testnet)
121-
.parse_address_from_str("3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt")
122-
.unwrap();
123-
assert_eq!(
124-
address,
125-
raw_from_register_to_address(
126-
&address_to_raw_for_register(
127-
"3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt"
128-
)
129-
.unwrap()
130-
)
131-
.unwrap()
132-
);
133-
}
134-
135-
// test serialization of "sigmaProp(true)" script
136-
#[test]
137-
fn test_address_to_tree() {
138-
assert_eq!(
139-
"10010101d17300",
140-
address_to_tree("Ms7smJwLGbUAjuWQ").unwrap()
141-
);
142-
assert_eq!(
143-
"0e0710010101d17300",
144-
address_to_bytes("Ms7smJwLGbUAjuWQ").unwrap()
145-
);
146-
}
26+
pub fn pks_to_network_addresses(
27+
pks: Vec<EcPoint>,
28+
network_prefix: NetworkPrefix,
29+
) -> Vec<NetworkAddress> {
30+
pks.into_iter()
31+
.map(|pk| NetworkAddress::new(network_prefix, &Address::P2Pk(pk.into())))
32+
.collect()
14733
}

core/src/api.rs

Lines changed: 33 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use std::convert::From;
22
use std::net::SocketAddr;
33
use std::sync::Arc;
44

5-
use crate::box_kind::{OracleBoxWrapper, PoolBox};
6-
use crate::node_interface::node_api::NodeApi;
7-
use crate::oracle_config::{get_core_api_port, ORACLE_CONFIG};
5+
use crate::box_kind::PoolBox;
6+
use crate::monitor::{check_oracle_health, check_pool_health, PoolHealth};
7+
use crate::node_interface::node_api::{NodeApi, NodeApiError};
8+
use crate::oracle_config::ORACLE_CONFIG;
89
use crate::oracle_state::{DataSourceError, LocalDatapointState, OraclePool};
910
use crate::pool_config::POOL_CONFIG;
1011
use axum::http::StatusCode;
@@ -135,26 +136,8 @@ fn pool_status_sync(oracle_pool: Arc<OraclePool>) -> Result<Json<serde_json::Val
135136
.epoch_length();
136137
let pool_box_height = pool_box.get_box().creation_height;
137138
let epoch_end_height = pool_box_height + epoch_length.0 as u32;
138-
139-
let posted_boxes = oracle_pool
140-
.get_posted_datapoint_boxes_source()
141-
.get_posted_datapoint_boxes()?;
142-
let posted_count_current_epoch = posted_boxes
143-
.into_iter()
144-
.filter(|b| b.get_box().creation_height >= pool_box_height)
145-
.count();
146-
147-
let collected_boxes = oracle_pool
148-
.get_collected_datapoint_boxes_source()
149-
.get_collected_datapoint_boxes()?;
150-
let collected_count_previous_epoch = collected_boxes
151-
.into_iter()
152-
.filter(|b| b.get_box().creation_height == pool_box_height)
153-
.count();
154-
155-
let active_oracle_count = collected_count_previous_epoch + posted_count_current_epoch;
156139
let pool_health = pool_health_sync(oracle_pool)?;
157-
140+
let active_oracle_count = pool_health.details.active_oracles.len();
158141
let json = Json(json!({
159142
"latest_pool_datapoint": pool_box.rate(),
160143
"latest_pool_box_height": pool_box_height,
@@ -202,72 +185,43 @@ fn oracle_health_sync(oracle_pool: Arc<OraclePool>) -> Result<serde_json::Value,
202185
.get_pool_box_source()
203186
.get_pool_box()?
204187
.get_box()
205-
.creation_height;
206-
let mut check_details = json!({
207-
"pool_box_height": pool_box_height,
208-
});
209-
let is_healthy = match oracle_pool
210-
.get_local_datapoint_box_source()
211-
.get_local_oracle_datapoint_box()?
212-
{
213-
Some(b) => match b {
214-
OracleBoxWrapper::Posted(posted_box) => {
215-
let creation_height = posted_box.get_box().creation_height;
216-
check_details["posted_box_height"] = json!(creation_height);
217-
creation_height > pool_box_height
218-
}
219-
OracleBoxWrapper::Collected(collected_box) => {
220-
let creation_height = collected_box.get_box().creation_height;
221-
check_details["collected_box_height"] = json!(creation_height);
222-
creation_height == pool_box_height
223-
}
224-
},
225-
None => false,
226-
};
227-
let json = json!({
228-
"status": if is_healthy { "OK" } else { "DOWN" },
229-
"details": check_details,
230-
});
231-
Ok(json)
188+
.creation_height
189+
.into();
190+
let oracle_health = check_oracle_health(oracle_pool, pool_box_height)?;
191+
Ok(serde_json::to_value(oracle_health).unwrap())
232192
}
233193

234194
async fn pool_health(oracle_pool: Arc<OraclePool>) -> Result<Json<serde_json::Value>, ApiError> {
235-
let json = task::spawn_blocking(|| pool_health_sync(oracle_pool))
195+
let json = task::spawn_blocking(|| pool_health_sync_json(oracle_pool))
236196
.await
237197
.unwrap()?;
238198
Ok(Json(json))
239199
}
240-
fn pool_health_sync(oracle_pool: Arc<OraclePool>) -> Result<serde_json::Value, ApiError> {
241-
let pool_conf = &POOL_CONFIG;
200+
201+
fn pool_health_sync(oracle_pool: Arc<OraclePool>) -> Result<PoolHealth, ApiError> {
242202
let node_api = NodeApi::new(ORACLE_CONFIG.node_api_key.clone(), &ORACLE_CONFIG.node_url);
243-
let current_height = node_api.node.current_block_height()? as u32;
203+
let current_height = (node_api.node.current_block_height()? as u32).into();
244204
let pool_box_height = oracle_pool
245205
.get_pool_box_source()
246206
.get_pool_box()?
247207
.get_box()
248-
.creation_height;
249-
let epoch_length = pool_conf
250-
.refresh_box_wrapper_inputs
251-
.contract_inputs
252-
.contract_parameters()
253-
.epoch_length()
254-
.0 as u32;
255-
let check_details = json!({
256-
"pool_box_height": pool_box_height,
257-
"current_block_height": current_height,
258-
"epoch_length": epoch_length,
259-
});
260-
let is_healthy = pool_box_height >= current_height - epoch_length;
261-
let json = json!({
262-
"status": if is_healthy { "OK" } else { "DOWN" },
263-
"details": check_details,
264-
});
265-
Ok(json)
208+
.creation_height
209+
.into();
210+
let network_prefix = node_api.get_change_address()?.network();
211+
let pool_health =
212+
check_pool_health(current_height, pool_box_height, oracle_pool, network_prefix)?;
213+
Ok(pool_health)
214+
}
215+
216+
fn pool_health_sync_json(oracle_pool: Arc<OraclePool>) -> Result<serde_json::Value, ApiError> {
217+
let pool_health = pool_health_sync(oracle_pool)?;
218+
Ok(serde_json::to_value(pool_health).unwrap())
266219
}
267220

268221
pub async fn start_rest_server(
269222
repost_receiver: Receiver<bool>,
270223
oracle_pool: Arc<OraclePool>,
224+
api_port: u16,
271225
) -> Result<(), anyhow::Error> {
272226
let op_clone = oracle_pool.clone();
273227
let op_clone2 = oracle_pool.clone();
@@ -290,7 +244,8 @@ pub async fn start_rest_server(
290244
.allow_origin(tower_http::cors::Any)
291245
.allow_methods([axum::http::Method::GET]),
292246
);
293-
let addr = SocketAddr::from(([0, 0, 0, 0], get_core_api_port().parse().unwrap()));
247+
let addr = SocketAddr::from(([0, 0, 0, 0], api_port));
248+
log::info!("Starting REST server on {}", addr);
294249
axum::Server::try_bind(&addr)?
295250
.serve(app.into_make_service())
296251
.await?;
@@ -322,3 +277,9 @@ impl From<anyhow::Error> for ApiError {
322277
ApiError(format!("Error: {:?}", err))
323278
}
324279
}
280+
281+
impl From<NodeApiError> for ApiError {
282+
fn from(err: NodeApiError) -> Self {
283+
ApiError(format!("NodeApiError: {:?}", err))
284+
}
285+
}

core/src/box_kind/oracle_box.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,10 @@ impl OracleBox for OracleBoxWrapper {
175175
}
176176

177177
fn public_key(&self) -> EcPoint {
178-
self.get_box()
179-
.get_register(NonMandatoryRegisterId::R4.into())
180-
.unwrap()
181-
.try_extract_into::<EcPoint>()
182-
.unwrap()
178+
match self {
179+
OracleBoxWrapper::Posted(p) => p.public_key().clone(),
180+
OracleBoxWrapper::Collected(c) => c.public_key().clone(),
181+
}
183182
}
184183

185184
fn get_box(&self) -> &ErgoBox {
@@ -276,6 +275,14 @@ impl CollectedOracleBox {
276275
pub fn get_box(&self) -> &ErgoBox {
277276
&self.ergo_box
278277
}
278+
279+
pub fn public_key(&self) -> EcPoint {
280+
self.ergo_box
281+
.get_register(NonMandatoryRegisterId::R4.into())
282+
.unwrap()
283+
.try_extract_into::<EcPoint>()
284+
.unwrap()
285+
}
279286
}
280287

281288
#[derive(Clone, Debug)]

0 commit comments

Comments
 (0)