Skip to content

Commit e5814dd

Browse files
committed
feat: implement queries needed for utxos, totals, and assets endpoints
Signed-off-by: William Hankins <[email protected]>
1 parent f735cb7 commit e5814dd

File tree

6 files changed

+260
-9
lines changed

6 files changed

+260
-9
lines changed

common/src/queries/addresses.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::{Address, AddressTotals, TxIdentifier, UTxOIdentifier};
1+
use crate::{
2+
Address, AddressTotals, NativeAssets, ShelleyAddress, TxIdentifier, UTxOIdentifier, ValueDelta,
3+
};
24

35
pub const DEFAULT_ADDRESS_QUERY_TOPIC: (&str, &str) =
46
("address-state-query-topic", "cardano.query.address");
@@ -8,13 +10,23 @@ pub enum AddressStateQuery {
810
GetAddressTotals { address: Address },
911
GetAddressUTxOs { address: Address },
1012
GetAddressTransactions { address: Address },
13+
14+
// Accounts related queries
15+
GetAddressesAssets { addresses: Vec<ShelleyAddress> },
16+
GetAddressesTotals { addresses: Vec<ShelleyAddress> },
17+
GetAddressesUTxOs { addresses: Vec<ShelleyAddress> },
1118
}
1219

1320
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1421
pub enum AddressStateQueryResponse {
1522
AddressTotals(AddressTotals),
1623
AddressUTxOs(Vec<UTxOIdentifier>),
1724
AddressTransactions(Vec<TxIdentifier>),
25+
26+
// Accounts related queries
27+
AddressesAssets(NativeAssets),
28+
AddressesTotals(ValueDelta),
29+
AddressesUTxOs(Vec<UTxOIdentifier>),
1830
NotFound,
1931
Error(String),
2032
}

common/src/queries/utxos.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ pub enum UTxOStateQuery {
88
GetUTxOsSum {
99
utxo_identifiers: Vec<UTxOIdentifier>,
1010
},
11+
GetUTxOsMap {
12+
utxo_identifiers: Vec<UTxOIdentifier>,
13+
},
1114
}
1215

1316
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1417
pub enum UTxOStateQueryResponse {
1518
UTxOsSum(Value),
19+
UTxOsMap(Vec<Value>),
1620
NotFound,
1721
Error(String),
1822
}

modules/address_state/src/address_state.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,24 @@ impl AddressState {
228228
Err(e) => AddressStateQueryResponse::Error(e.to_string()),
229229
}
230230
}
231+
AddressStateQuery::GetAddressesAssets { addresses } => {
232+
match state.get_addresses_assets(addresses).await {
233+
Ok(assets) => AddressStateQueryResponse::AddressesAssets(assets),
234+
Err(e) => AddressStateQueryResponse::Error(e.to_string()),
235+
}
236+
}
237+
AddressStateQuery::GetAddressesTotals { addresses } => {
238+
match state.get_addresses_totals(addresses).await {
239+
Ok(totals) => AddressStateQueryResponse::AddressesTotals(totals),
240+
Err(e) => AddressStateQueryResponse::Error(e.to_string()),
241+
}
242+
}
243+
AddressStateQuery::GetAddressesUTxOs { addresses } => {
244+
match state.get_addresses_utxos(addresses).await {
245+
Ok(utxos) => AddressStateQueryResponse::AddressesUTxOs(utxos),
246+
Err(e) => AddressStateQueryResponse::Error(e.to_string()),
247+
}
248+
}
231249
};
232250
Arc::new(Message::StateQueryResponse(StateQueryResponse::Addresses(
233251
response,

modules/address_state/src/state.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::{
55
};
66

77
use acropolis_common::{
8-
Address, AddressDelta, AddressTotals, BlockInfo, TxIdentifier, UTxOIdentifier, ValueDelta,
8+
Address, AddressDelta, AddressTotals, BlockInfo, NativeAssets, ShelleyAddress, TxIdentifier,
9+
UTxOIdentifier, ValueDelta,
910
};
1011
use anyhow::Result;
1112

@@ -199,6 +200,31 @@ impl State {
199200

200201
Ok(())
201202
}
203+
204+
pub async fn get_addresses_assets(
205+
&self,
206+
_addresses: &[ShelleyAddress],
207+
) -> Result<NativeAssets> {
208+
Ok(NativeAssets::default())
209+
}
210+
211+
pub async fn get_addresses_totals(&self, _addresses: &[ShelleyAddress]) -> Result<ValueDelta> {
212+
Ok(ValueDelta::default())
213+
}
214+
215+
pub async fn get_addresses_utxos(
216+
&self,
217+
addresses: &[ShelleyAddress],
218+
) -> Result<Vec<UTxOIdentifier>> {
219+
let mut utxos = Vec::new();
220+
221+
for addr in addresses {
222+
if let Some(list) = self.get_address_utxos(&Address::Shelley(addr.clone())).await? {
223+
utxos.extend(list);
224+
}
225+
}
226+
Ok(utxos)
227+
}
202228
}
203229

204230
#[cfg(test)]

modules/rest_blockfrost/src/handlers/accounts.rs

Lines changed: 192 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use std::sync::Arc;
33

44
use acropolis_common::messages::{Message, RESTResponse, StateQuery, StateQueryResponse};
55
use acropolis_common::queries::accounts::{AccountsStateQuery, AccountsStateQueryResponse};
6+
use acropolis_common::queries::addresses::{AddressStateQuery, AddressStateQueryResponse};
67
use acropolis_common::queries::blocks::{
78
BlocksStateQuery, BlocksStateQueryResponse, TransactionHashes,
89
};
910
use acropolis_common::queries::utils::query_state;
11+
use acropolis_common::queries::utxos::UTxOStateQuery;
1012
use acropolis_common::serialization::{Bech32Conversion, Bech32WithHrp};
11-
use acropolis_common::{DRepChoice, ShelleyAddress, StakeAddress};
13+
use acropolis_common::{DRepChoice, StakeAddress};
1214
use anyhow::{anyhow, Result};
1315
use caryatid_sdk::Context;
1416

@@ -637,10 +639,75 @@ pub async fn handle_account_addresses_blockfrost(
637639

638640
/// Handle `/accounts/{stake_address}/addresses/assets` Blockfrost-compatible endpoint
639641
pub async fn handle_account_assets_blockfrost(
640-
_context: Arc<Context<Message>>,
641-
_params: Vec<String>,
642-
_handlers_config: Arc<HandlersConfig>,
642+
context: Arc<Context<Message>>,
643+
params: Vec<String>,
644+
handlers_config: Arc<HandlersConfig>,
643645
) -> Result<RESTResponse> {
646+
let account = match parse_stake_address(&params) {
647+
Ok(addr) => addr,
648+
Err(resp) => return Ok(resp),
649+
};
650+
651+
// Prepare the message
652+
let msg = Arc::new(Message::StateQuery(StateQuery::Accounts(
653+
AccountsStateQuery::GetAccountAssociatedAddresses { account },
654+
)));
655+
656+
// Get addresses from historical accounts state
657+
let addresses = query_state(
658+
&context,
659+
&handlers_config.historical_accounts_query_topic,
660+
msg,
661+
|message| match message {
662+
Message::StateQueryResponse(StateQueryResponse::Accounts(
663+
AccountsStateQueryResponse::AccountAssociatedAddresses(addresses),
664+
)) => Ok(Some(addresses)),
665+
Message::StateQueryResponse(StateQueryResponse::Accounts(
666+
AccountsStateQueryResponse::NotFound,
667+
)) => Ok(None),
668+
Message::StateQueryResponse(StateQueryResponse::Accounts(
669+
AccountsStateQueryResponse::Error(e),
670+
)) => Err(anyhow::anyhow!(
671+
"Internal server error while retrieving account addresses: {e}"
672+
)),
673+
_ => Err(anyhow::anyhow!(
674+
"Unexpected message type while retrieving account addresses"
675+
)),
676+
},
677+
)
678+
.await?;
679+
680+
let Some(addresses) = addresses else {
681+
return Ok(RESTResponse::with_text(404, "Account not found"));
682+
};
683+
684+
let msg = Arc::new(Message::StateQuery(StateQuery::Addresses(
685+
AddressStateQuery::GetAddressesAssets { addresses },
686+
)));
687+
688+
// Get assets from address state
689+
let _assets = query_state(
690+
&context,
691+
&handlers_config.historical_accounts_query_topic,
692+
msg,
693+
|message| match message {
694+
Message::StateQueryResponse(StateQueryResponse::Addresses(
695+
AddressStateQueryResponse::AddressesAssets(assets),
696+
)) => Ok(Some(assets)),
697+
Message::StateQueryResponse(StateQueryResponse::Addresses(
698+
AddressStateQueryResponse::NotFound,
699+
)) => Ok(None),
700+
Message::StateQueryResponse(StateQueryResponse::Addresses(
701+
AddressStateQueryResponse::Error(e),
702+
)) => Err(anyhow::anyhow!(
703+
"Internal server error while retrieving account assets: {e}"
704+
)),
705+
_ => Err(anyhow::anyhow!(
706+
"Unexpected message type while retrieving account assets"
707+
)),
708+
},
709+
)
710+
.await?;
644711
Ok(RESTResponse::with_text(501, "Not implemented"))
645712
}
646713

@@ -688,15 +755,133 @@ pub async fn handle_account_totals_blockfrost(
688755
return Ok(RESTResponse::with_text(404, "Account not found"));
689756
};
690757

758+
let msg = Arc::new(Message::StateQuery(StateQuery::Addresses(
759+
AddressStateQuery::GetAddressesTotals { addresses },
760+
)));
761+
762+
// Get totals from address state
763+
let _totals = query_state(
764+
&context,
765+
&handlers_config.historical_accounts_query_topic,
766+
msg,
767+
|message| match message {
768+
Message::StateQueryResponse(StateQueryResponse::Addresses(
769+
AddressStateQueryResponse::AddressesTotals(totals),
770+
)) => Ok(Some(totals)),
771+
Message::StateQueryResponse(StateQueryResponse::Addresses(
772+
AddressStateQueryResponse::NotFound,
773+
)) => Ok(None),
774+
Message::StateQueryResponse(StateQueryResponse::Addresses(
775+
AddressStateQueryResponse::Error(e),
776+
)) => Err(anyhow::anyhow!(
777+
"Internal server error while retrieving account totals: {e}"
778+
)),
779+
_ => Err(anyhow::anyhow!(
780+
"Unexpected message type while retrieving account totals"
781+
)),
782+
},
783+
)
784+
.await?;
691785
Ok(RESTResponse::with_text(501, "Not implemented"))
692786
}
693787

694788
/// Handle `/accounts/{stake_address}/utxos` Blockfrost-compatible endpoint
695789
pub async fn handle_account_utxos_blockfrost(
696-
_context: Arc<Context<Message>>,
697-
_params: Vec<String>,
698-
_handlers_config: Arc<HandlersConfig>,
790+
context: Arc<Context<Message>>,
791+
params: Vec<String>,
792+
handlers_config: Arc<HandlersConfig>,
699793
) -> Result<RESTResponse> {
794+
let account = match parse_stake_address(&params) {
795+
Ok(addr) => addr,
796+
Err(resp) => return Ok(resp),
797+
};
798+
799+
// Get addresses from historical accounts state
800+
let msg = Arc::new(Message::StateQuery(StateQuery::Accounts(
801+
AccountsStateQuery::GetAccountAssociatedAddresses { account },
802+
)));
803+
let addresses = query_state(
804+
&context,
805+
&handlers_config.historical_accounts_query_topic,
806+
msg,
807+
|message| match message {
808+
Message::StateQueryResponse(StateQueryResponse::Accounts(
809+
AccountsStateQueryResponse::AccountAssociatedAddresses(addresses),
810+
)) => Ok(Some(addresses)),
811+
Message::StateQueryResponse(StateQueryResponse::Accounts(
812+
AccountsStateQueryResponse::NotFound,
813+
)) => Ok(None),
814+
Message::StateQueryResponse(StateQueryResponse::Accounts(
815+
AccountsStateQueryResponse::Error(e),
816+
)) => Err(anyhow::anyhow!(
817+
"Internal server error while retrieving account addresses: {e}"
818+
)),
819+
_ => Err(anyhow::anyhow!(
820+
"Unexpected message type while retrieving account addresses"
821+
)),
822+
},
823+
)
824+
.await?;
825+
826+
let Some(addresses) = addresses else {
827+
return Ok(RESTResponse::with_text(404, "Account not found"));
828+
};
829+
830+
// Get utxos from address state
831+
let msg = Arc::new(Message::StateQuery(StateQuery::Addresses(
832+
AddressStateQuery::GetAddressesUTxOs { addresses },
833+
)));
834+
let utxo_identifiers = query_state(
835+
&context,
836+
&handlers_config.historical_accounts_query_topic,
837+
msg,
838+
|message| match message {
839+
Message::StateQueryResponse(StateQueryResponse::Addresses(
840+
AddressStateQueryResponse::AddressesUTxOs(utxos),
841+
)) => Ok(utxos),
842+
Message::StateQueryResponse(StateQueryResponse::Addresses(
843+
AddressStateQueryResponse::NotFound,
844+
)) => Err(anyhow::anyhow!(
845+
"Internal server error while retrieving account UTxOs: No UTxOs found"
846+
)),
847+
Message::StateQueryResponse(StateQueryResponse::Addresses(
848+
AddressStateQueryResponse::Error(e),
849+
)) => Err(anyhow::anyhow!(
850+
"Internal server error while retrieving account UTxOs: {e}"
851+
)),
852+
_ => Err(anyhow::anyhow!(
853+
"Unexpected message type while retrieving account UTxOs"
854+
)),
855+
},
856+
)
857+
.await?;
858+
859+
// Get UTxO balances from utxo state
860+
let msg = Arc::new(Message::StateQuery(StateQuery::UTxOs(
861+
UTxOStateQuery::GetUTxOsMap { utxo_identifiers },
862+
)));
863+
let balances = query_state(
864+
&context,
865+
&handlers_config.historical_accounts_query_topic,
866+
msg,
867+
|message| match message {
868+
Message::StateQueryResponse(StateQueryResponse::Addresses(
869+
AddressStateQueryResponse::AddressesUTxOs(utxos),
870+
)) => Ok(Some(utxos)),
871+
Message::StateQueryResponse(StateQueryResponse::Addresses(
872+
AddressStateQueryResponse::NotFound,
873+
)) => Ok(None),
874+
Message::StateQueryResponse(StateQueryResponse::Addresses(
875+
AddressStateQueryResponse::Error(e),
876+
)) => Err(anyhow::anyhow!(
877+
"Internal server error while retrieving account UTxOs: {e}"
878+
)),
879+
_ => Err(anyhow::anyhow!(
880+
"Unexpected message type while retrieving account UTxOs"
881+
)),
882+
},
883+
)
884+
.await?;
700885
Ok(RESTResponse::with_text(501, "Not implemented"))
701886
}
702887

modules/utxo_state/src/utxo_state.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ impl UTXOState {
124124
Err(e) => UTxOStateQueryResponse::Error(e.to_string()),
125125
}
126126
}
127+
UTxOStateQuery::GetUTxOsMap { utxo_identifiers } => {
128+
match state.get_utxos_sum(utxo_identifiers).await {
129+
Ok(balance) => UTxOStateQueryResponse::UTxOsSum(balance),
130+
Err(e) => UTxOStateQueryResponse::Error(e.to_string()),
131+
}
132+
}
127133
};
128134
Arc::new(Message::StateQueryResponse(StateQueryResponse::UTxOs(
129135
response,

0 commit comments

Comments
 (0)