Skip to content

Commit f64ee9b

Browse files
committed
Retrieve utxo datums in chain observer
1 parent 8d65b08 commit f64ee9b

File tree

4 files changed

+141
-1
lines changed

4 files changed

+141
-1
lines changed

mithril-common/src/beacon_provider.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl BeaconProvider for BeaconProviderImpl {
7979

8080
#[cfg(test)]
8181
mod tests {
82-
use crate::chain_observer::{ChainObserver, ChainObserverError};
82+
use crate::chain_observer::{ChainAddress, ChainObserver, ChainObserverError, TxDatum};
8383
use crate::digesters::DumbImmutableFileObserver;
8484
use crate::entities::{Epoch, StakeDistribution};
8585

@@ -89,6 +89,13 @@ mod tests {
8989

9090
#[async_trait]
9191
impl ChainObserver for DumbChainObserver {
92+
async fn get_current_datums(
93+
&self,
94+
_address: &ChainAddress,
95+
) -> Result<Vec<TxDatum>, ChainObserverError> {
96+
Ok(Vec::new())
97+
}
98+
9299
async fn get_current_epoch(&self) -> Result<Option<Epoch>, ChainObserverError> {
93100
Ok(Some(Epoch(42)))
94101
}

mithril-common/src/chain_observer/cli_observer.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
use async_trait::async_trait;
22
use nom::IResult;
3+
use rand_core::RngCore;
34
use serde_json::Value;
5+
use std::collections::HashMap;
46
use std::error::Error;
57
use std::fs;
68
use std::path::PathBuf;
79
use tokio::process::Command;
810

911
use crate::chain_observer::interface::*;
12+
use crate::chain_observer::{ChainAddress, TxDatum};
1013
use crate::crypto_helper::{KESPeriod, OpCert, SerDeShelleyFileFormat};
1114
use crate::entities::{Epoch, StakeDistribution};
1215
use crate::CardanoNetwork;
1316

1417
#[async_trait]
1518
pub trait CliRunner {
19+
async fn launch_utxo(&self, address: &str) -> Result<String, Box<dyn Error + Sync + Send>>;
1620
async fn launch_stake_distribution(&self) -> Result<String, Box<dyn Error + Sync + Send>>;
1721
async fn launch_stake_snapshot(
1822
&self,
@@ -43,6 +47,27 @@ impl CardanoCliRunner {
4347
}
4448
}
4549

50+
fn random_out_file() -> PathBuf {
51+
let mut rng = rand_core::OsRng;
52+
std::env::temp_dir()
53+
.join("cardano-cli-runner")
54+
.join(format!("{}", rng.next_u64()))
55+
}
56+
57+
fn command_for_utxo(&self, address: &str, out_file: PathBuf) -> Command {
58+
let mut command = self.get_command();
59+
command
60+
.arg("query")
61+
.arg("utxo")
62+
.arg("--address")
63+
.arg(address)
64+
.arg("--out-file")
65+
.arg(out_file);
66+
self.post_config_command(&mut command);
67+
68+
command
69+
}
70+
4671
fn command_for_stake_distribution(&self) -> Command {
4772
let mut command = self.get_command();
4873
command.arg("query").arg("stake-distribution");
@@ -110,6 +135,27 @@ impl CardanoCliRunner {
110135

111136
#[async_trait]
112137
impl CliRunner for CardanoCliRunner {
138+
async fn launch_utxo(&self, address: &str) -> Result<String, Box<dyn Error + Sync + Send>> {
139+
let out_file = Self::random_out_file();
140+
let output = self
141+
.command_for_utxo(address, out_file.clone())
142+
.output()
143+
.await?;
144+
145+
if output.status.success() {
146+
Ok(std::str::from_utf8(&output.stdout)?.trim().to_string())
147+
} else {
148+
let message = String::from_utf8_lossy(&output.stderr);
149+
150+
Err(format!(
151+
"Error launching command {:?}, error = '{}'",
152+
self.command_for_utxo(address, out_file),
153+
message
154+
)
155+
.into())
156+
}
157+
}
158+
113159
async fn launch_stake_distribution(&self) -> Result<String, Box<dyn Error + Sync + Send>> {
114160
let output = self.command_for_stake_distribution().output().await?;
115161

@@ -255,6 +301,30 @@ impl ChainObserver for CardanoCliChainObserver {
255301
}
256302
}
257303

304+
async fn get_current_datums(
305+
&self,
306+
address: &ChainAddress,
307+
) -> Result<Vec<TxDatum>, ChainObserverError> {
308+
let output = self
309+
.cli_runner
310+
.launch_utxo(address)
311+
.await
312+
.map_err(ChainObserverError::General)?;
313+
let v: HashMap<String, Value> = serde_json::from_str(&output).map_err(|e| {
314+
ChainObserverError::InvalidContent(
315+
format!("Error: {e:?}, output was = '{output}'").into(),
316+
)
317+
})?;
318+
319+
Ok(v.values()
320+
.filter_map(|v| {
321+
v.get("inlineDatum")
322+
.filter(|datum| !datum.is_null())
323+
.map(|datum| TxDatum(datum.to_string()))
324+
})
325+
.collect())
326+
}
327+
258328
async fn get_current_stake_distribution(
259329
&self,
260330
) -> Result<Option<StakeDistribution>, ChainObserverError> {
@@ -337,6 +407,45 @@ mod tests {
337407

338408
#[async_trait]
339409
impl CliRunner for TestCliRunner {
410+
async fn launch_utxo(
411+
&self,
412+
_address: &str,
413+
) -> Result<String, Box<dyn Error + Sync + Send>> {
414+
let output = r#"
415+
{
416+
"1fd4d3e131afe3c8b212772a3f3083d2fbc6b2a7b20e54e4ff08e001598818d8#0": {
417+
"address": "addr_test1vpcr3he05gemue6eyy0c9clajqnnww8aa2l3jszjdlszjhq093qrn",
418+
"datum": null,
419+
"inlineDatum": {
420+
"constructor": 0,
421+
"fields": [
422+
{
423+
"bytes": "5b0a20207b0a20202020226e616d65223a20227468616c6573222c0a202020202265706f6368223a203132330a20207d2c0a20207b0a20202020226e616d65223a20227079746861676f726173222c0a202020202265706f6368223a206e756c6c0a20207d0a5d0a"
424+
}
425+
]
426+
},
427+
"inlineDatumhash": "b97cbaa0dc5b41864c83c2f625d9bc2a5f3e6b5cd5071c14a2090e630e188c80",
428+
"referenceScript": null,
429+
"value": {
430+
"lovelace": 10000000
431+
}
432+
},
433+
"1fd4d3e131afe3c8b212772a3f3083d2fbc6b2a7b20e54e4ff08e001598818d8#1": {
434+
"address": "addr_test1vpcr3he05gemue6eyy0c9clajqnnww8aa2l3jszjdlszjhq093qrn",
435+
"datum": null,
436+
"datumhash": null,
437+
"inlineDatum": null,
438+
"referenceScript": null,
439+
"value": {
440+
"lovelace": 9989656678
441+
}
442+
}
443+
}
444+
"#;
445+
446+
Ok(output.to_string())
447+
}
448+
340449
async fn launch_stake_distribution(&self) -> Result<String, Box<dyn Error + Sync + Send>> {
341450
let output = r#"
342451
PoolId Stake frac
@@ -471,6 +580,14 @@ pool1qz2vzszautc2c8mljnqre2857dpmheq7kgt6vav0s38tvvhxm6w 1.051e-6
471580
);
472581
}
473582

583+
#[tokio::test]
584+
async fn test_get_current_datums() {
585+
let observer = CardanoCliChainObserver::new(Box::new(TestCliRunner {}));
586+
let address = "addrtest_123456".to_string();
587+
let datums = observer.get_current_datums(&address).await.unwrap();
588+
assert_eq!(vec![TxDatum("{\"constructor\":0,\"fields\":[{\"bytes\":\"5b0a20207b0a20202020226e616d65223a20227468616c6573222c0a202020202265706f6368223a203132330a20207d2c0a20207b0a20202020226e616d65223a20227079746861676f726173222c0a202020202265706f6368223a206e756c6c0a20207d0a5d0a\"}]}".to_string())], datums);
589+
}
590+
474591
#[tokio::test]
475592
async fn test_get_current_stake_value() {
476593
let observer = CardanoCliChainObserver::new(Box::new(TestCliRunner {}));

mithril-common/src/chain_observer/fake_observer.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use async_trait::async_trait;
22
use tokio::sync::RwLock;
33

44
use crate::chain_observer::interface::*;
5+
use crate::chain_observer::{ChainAddress, TxDatum};
56
use crate::crypto_helper::{KESPeriod, OpCert};
67
use crate::{entities::*, test_utils::fake_data};
78

@@ -57,6 +58,13 @@ impl Default for FakeObserver {
5758

5859
#[async_trait]
5960
impl ChainObserver for FakeObserver {
61+
async fn get_current_datums(
62+
&self,
63+
_address: &ChainAddress,
64+
) -> Result<Vec<TxDatum>, ChainObserverError> {
65+
Ok(Vec::new())
66+
}
67+
6068
async fn get_current_epoch(&self) -> Result<Option<Epoch>, ChainObserverError> {
6169
Ok(self
6270
.current_beacon

mithril-common/src/chain_observer/interface.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use mockall::automock;
77
use std::error::Error as StdError;
88
use thiserror::Error;
99

10+
use super::{ChainAddress, TxDatum};
11+
1012
/// [ChainObserver] related errors.
1113
#[derive(Debug, Error)]
1214
pub enum ChainObserverError {
@@ -23,6 +25,12 @@ pub enum ChainObserverError {
2325
#[automock]
2426
#[async_trait]
2527
pub trait ChainObserver: Sync + Send {
28+
/// Retrive the datums associated to and address
29+
async fn get_current_datums(
30+
&self,
31+
address: &ChainAddress,
32+
) -> Result<Vec<TxDatum>, ChainObserverError>;
33+
2634
/// Retrieve the current epoch of the Cardano network
2735
async fn get_current_epoch(&self) -> Result<Option<Epoch>, ChainObserverError>;
2836

0 commit comments

Comments
 (0)