Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ strum = { version = "0.26.1", features = ["derive"] }
bs58 = "0.5.0"
base64 = "0.22.1"
copypasta = "0.10.1"
dash-sdk = { git = "https://github.com/dashpay/platform", rev = "a4f906acd127bc9856d72452eba002da16935209" }
dash-sdk = { git = "https://github.com/dashpay/platform", rev = "e2ed81f0a5af5ef74fc703097e2657349021094b" }
thiserror = "1"
serde = "1.0.197"
serde_json = "1.0.120"
Expand Down
11 changes: 10 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::components::core_zmq_listener::{CoreZMQListener, ZMQMessage};
use crate::context::AppContext;
use crate::database::Database;
use crate::logging::initialize_logger;
use crate::ui::document_query_screen::DocumentQueryScreen;
use crate::ui::contracts_documents::document_query_screen::DocumentQueryScreen;
use crate::ui::dpns::dpns_contested_names_screen::{
DPNSContestedNamesScreen, DPNSSubscreen, IndividualVoteCastingStatus,
};
Expand Down Expand Up @@ -460,6 +460,15 @@ impl App for AppState {
BackendTaskSuccessResult::ToppedUpIdentity(_) => {
self.visible_screen_mut().display_task_result(message);
}
BackendTaskSuccessResult::FetchedContract(_) => {
self.visible_screen_mut().display_task_result(message);
}
BackendTaskSuccessResult::FetchedContracts(_) => {
self.visible_screen_mut().display_task_result(message);
}
BackendTaskSuccessResult::PageDocuments(_, _) => {
self.visible_screen_mut().display_task_result(message);
}
},
TaskResult::Error(message) => {
self.visible_screen_mut()
Expand Down
57 changes: 51 additions & 6 deletions src/backend_task/contract.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,63 @@
use crate::context::AppContext;
use dash_sdk::dpp::system_data_contracts::dpns_contract;
use dash_sdk::platform::{DataContract, Fetch, Identifier};
use dash_sdk::platform::{DataContract, Fetch, FetchMany, Identifier};
use dash_sdk::Sdk;

use super::BackendTaskSuccessResult;

#[derive(Debug, Clone, PartialEq)]
pub(crate) enum ContractTask {
FetchDPNSContract,
FetchContract(Identifier, Option<String>),
FetchContracts(Vec<Identifier>),
RemoveContract(Identifier),
}

impl AppContext {
pub async fn run_contract_task(&self, task: ContractTask, sdk: &Sdk) -> Result<(), String> {
pub async fn run_contract_task(
&self,
task: ContractTask,
sdk: &Sdk,
) -> Result<BackendTaskSuccessResult, String> {
match task {
ContractTask::FetchContract(identifier, name) => {
match DataContract::fetch(sdk, identifier).await {
Ok(Some(data_contract)) => self
.db
.insert_contract_if_not_exists(&data_contract, name.as_deref(), self)
.map_err(|e| e.to_string()),
Ok(None) => Ok(()),
Err(e) => Err(e.to_string()),
.map(|_| BackendTaskSuccessResult::FetchedContract(data_contract))
.map_err(|e| {
format!(
"Error inserting contract into the database: {}",
e.to_string()
)
}),
Ok(None) => Err("Contract not found".to_string()),
Err(e) => Err(format!("Error fetching contract: {}", e.to_string())),
}
}
ContractTask::FetchContracts(identifiers) => {
match DataContract::fetch_many(sdk, identifiers).await {
Ok(data_contracts) => {
let mut results = vec![];
for data_contract in data_contracts {
if let Some(contract) = &data_contract.1 {
self.db
.insert_contract_if_not_exists(contract, None, self)
.map_err(|e| {
format!(
"Error inserting contract into the database: {}",
e.to_string()
)
})?;
results.push(Some(contract.clone()));
} else {
results.push(None);
}
}
Ok(BackendTaskSuccessResult::FetchedContracts(results))
}
Err(e) => Err(format!("Error fetching contracts: {}", e.to_string())),
}
}
ContractTask::FetchDPNSContract => {
Expand All @@ -29,11 +67,18 @@ impl AppContext {
Ok(Some(data_contract)) => self
.db
.insert_contract_if_not_exists(&data_contract, Some("dpns"), self)
.map(|_| BackendTaskSuccessResult::FetchedContract(data_contract))
.map_err(|e| e.to_string()),
Ok(None) => Err("No DPNS contract found".to_string()),
Err(e) => Err(e.to_string()),
Err(e) => Err(format!("Error fetching DPNS contract: {}", e.to_string())),
}
}
ContractTask::RemoveContract(identifier) => self
.remove_contract(&identifier)
.map(|_| {
BackendTaskSuccessResult::Message("Successfully removed contract".to_string())
})
.map_err(|e| format!("Error removing contract: {}", e.to_string())),
}
}
}
53 changes: 47 additions & 6 deletions src/backend_task/document.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::backend_task::BackendTaskSuccessResult;
use crate::context::AppContext;
use dash_sdk::platform::{Document, DocumentQuery, FetchMany};
use dash_sdk::platform::proto::get_documents_request::get_documents_request_v0::Start;
use dash_sdk::platform::{Document, DocumentQuery, FetchMany, Identifier};
use dash_sdk::query_types::IndexMap;
use dash_sdk::Sdk;

pub type DocumentTypeName = String;
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum DocumentTask {
FetchDocuments(DocumentQuery),
FetchDocumentsPage(DocumentQuery),
}

impl AppContext {
Expand All @@ -16,10 +18,49 @@ impl AppContext {
sdk: &Sdk,
) -> Result<BackendTaskSuccessResult, String> {
match task {
DocumentTask::FetchDocuments(drive_query) => Document::fetch_many(sdk, drive_query)
.await
.map(BackendTaskSuccessResult::Documents)
.map_err(|e| e.to_string()),
DocumentTask::FetchDocuments(document_query) => {
Document::fetch_many(sdk, document_query)
.await
.map(BackendTaskSuccessResult::Documents)
.map_err(|e| format!("Error fetching documents: {}", e.to_string()))
}
DocumentTask::FetchDocumentsPage(mut document_query) => {
// Set the limit for each page
document_query.limit = 100;

// Initialize an empty IndexMap to accumulate documents for this page
let mut page_docs: IndexMap<Identifier, Option<Document>> = IndexMap::new();

// Fetch a single page
let docs_batch_result = Document::fetch_many(sdk, document_query.clone())
.await
.map_err(|e| format!("Error fetching documents: {}", e))?;

let batch_len = docs_batch_result.len();

// Insert the batch into the page map
for (id, doc_opt) in docs_batch_result {
page_docs.insert(id, doc_opt);
}

// Determine if there's a next page
let has_next_page = batch_len == 100;

// If there's a next page, set the 'start' parameter for the next cursor
let next_cursor = if has_next_page {
page_docs.keys().last().cloned().map(|last_doc_id| {
let id_bytes = last_doc_id.to_buffer();
Start::StartAfter(id_bytes.to_vec())
})
} else {
None
};

Ok(BackendTaskSuccessResult::PageDocuments(
page_docs,
next_cursor,
))
}
}
}
}
17 changes: 11 additions & 6 deletions src/backend_task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ use crate::backend_task::withdrawal_statuses::{WithdrawStatusPartialData, Withdr
use crate::context::AppContext;
use crate::model::qualified_identity::QualifiedIdentity;
use contested_names::ScheduledDPNSVote;
use dash_sdk::dpp::prelude::DataContract;
use dash_sdk::dpp::voting::votes::Vote;
use dash_sdk::query_types::Documents;
use dash_sdk::platform::proto::get_documents_request::get_documents_request_v0::Start;
use dash_sdk::platform::{Document, Identifier};
use dash_sdk::query_types::{Documents, IndexMap};
use std::sync::Arc;
use tokio::sync::mpsc;

pub mod contested_names;
pub mod contract;
pub mod core;
mod document;
pub mod document;
pub mod identity;
pub mod withdrawal_statuses;

Expand All @@ -42,6 +45,9 @@ pub(crate) enum BackendTaskSuccessResult {
SuccessfulVotes(Vec<Vote>),
CastScheduledVote(ScheduledDPNSVote),
WithdrawalStatus(WithdrawStatusPartialData),
FetchedContract(DataContract),
FetchedContracts(Vec<Option<DataContract>>),
PageDocuments(IndexMap<Identifier, Option<Document>>, Option<Start>),
}

impl BackendTaskSuccessResult {}
Expand All @@ -65,10 +71,9 @@ impl AppContext {
) -> Result<BackendTaskSuccessResult, String> {
let sdk = self.sdk.clone();
match task {
BackendTask::ContractTask(contract_task) => self
.run_contract_task(contract_task, &sdk)
.await
.map(|_| BackendTaskSuccessResult::None),
BackendTask::ContractTask(contract_task) => {
self.run_contract_task(contract_task, &sdk).await
}
BackendTask::ContestedResourceTask(contested_resource_task) => {
self.run_contested_resource_task(contested_resource_task, &sdk, sender)
.await
Expand Down
20 changes: 19 additions & 1 deletion src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ impl AppContext {
.insert_local_qualified_identity(&identity.clone().into(), None, self)
}

/// Inserts a local qualified identity into the database
pub fn insert_local_qualified_identity(
&self,
qualified_identity: &QualifiedIdentity,
Expand All @@ -155,6 +156,7 @@ impl AppContext {
)
}

/// Updates a local qualified identity in the database
pub fn update_local_qualified_identity(
&self,
qualified_identity: &QualifiedIdentity,
Expand All @@ -163,6 +165,7 @@ impl AppContext {
.update_local_qualified_identity(qualified_identity, self)
}

/// Sets the alias for an identity
pub fn set_alias(&self, identifier: &Identifier, new_alias: Option<&str>) -> Result<()> {
self.db.set_alias(identifier, new_alias)
}
Expand All @@ -182,44 +185,54 @@ impl AppContext {
)
}

/// Fetches all local qualified identities from the database
pub fn load_local_qualified_identities(&self) -> Result<Vec<QualifiedIdentity>> {
let wallets = self.wallets.read().unwrap();
self.db.get_local_qualified_identities(self, &wallets)
}

/// Fetches all voting identities from the database
pub fn load_local_voting_identities(&self) -> Result<Vec<QualifiedIdentity>> {
self.db.get_local_voting_identities(self)
}

/// Fetches all contested names from the database including past and active ones
pub fn all_contested_names(&self) -> Result<Vec<ContestedName>> {
self.db.get_all_contested_names(self)
}

/// Fetches all ongoing contested names from the database
pub fn ongoing_contested_names(&self) -> Result<Vec<ContestedName>> {
self.db.get_ongoing_contested_names(self)
}

/// Inserts scheduled votes into the database
pub fn insert_scheduled_votes(&self, scheduled_votes: &Vec<ScheduledDPNSVote>) -> Result<()> {
self.db.insert_scheduled_votes(self, &scheduled_votes)
}
Comment on lines +209 to 212

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

insert_scheduled_votes method.

When inserting multiple votes, ensure no partial insert states remain if an error arises midway. A transaction-based approach or an up-front check might be needed (depending on the DB schema).


/// Fetches all scheduled votes from the database
pub fn get_scheduled_votes(&self) -> Result<Vec<ScheduledDPNSVote>> {
self.db.get_scheduled_votes(&self)
}

/// Clears all scheduled votes from the database
pub fn clear_all_scheduled_votes(&self) -> Result<()> {
self.db.clear_all_scheduled_votes(self)
}

/// Clears all executed scheduled votes from the database
pub fn clear_executed_scheduled_votes(&self) -> Result<()> {
self.db.clear_executed_scheduled_votes(self)
}

/// Deletes a scheduled vote from the database
pub fn delete_scheduled_vote(&self, identity_id: &[u8], contested_name: &String) -> Result<()> {
self.db
.delete_scheduled_vote(self, identity_id, &contested_name)
}

/// Marks a scheduled vote as executed in the database
pub fn mark_vote_executed(&self, identity_id: &[u8], contested_name: String) -> Result<()> {
self.db
.mark_vote_executed(self, identity_id, contested_name)
Expand Down Expand Up @@ -270,7 +283,7 @@ impl AppContext {
self.db.get_settings()
}

/// Retrieves the DPNS contract along with other contracts from the database.
/// Retrieves all contracts from the database plus the DPNS contract from app context.
pub fn get_contracts(
&self,
limit: Option<u32>,
Expand All @@ -291,6 +304,11 @@ impl AppContext {
Ok(contracts)
}

// Remove contract from the database by ID
pub fn remove_contract(&self, contract_id: &Identifier) -> Result<()> {
self.db.remove_contract(contract_id.as_bytes(), &self)
}

pub(crate) fn received_transaction_finality(
&self,
tx: &Transaction,
Expand Down
14 changes: 14 additions & 0 deletions src/database/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,18 @@ impl Database {

Ok(contracts)
}

pub fn remove_contract(
&self,
contract_id: &[u8],
app_context: &AppContext,
) -> rusqlite::Result<()> {
let network = app_context.network_string();
let conn = self.conn.lock().unwrap();
conn.execute(
"DELETE FROM contract WHERE contract_id = ? AND network = ?",
rusqlite::params![contract_id, network],
)?;
Ok(())
}
Comment on lines +187 to +200

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Check for potential foreign key constraints or references when removing contracts.

The newly added remove_contract method will delete the contract unconditionally. If other records reference the contract, removing it can cause orphaned references unless you have implemented cascading or manual cleanup. Double-check constraints to maintain referential integrity.

}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod logging;
mod model;
mod sdk_wrapper;
mod ui;
mod utils;

include!(concat!(env!("OUT_DIR"), "/version.rs"));

Expand Down
Loading