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 @@ -18,7 +18,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", branch = "refactor/replaceBLSLibrary" }
dash-sdk = { git = "https://github.com/dashpay/platform", branch = "refactor/replaceBLSLibrary", features = ["tokio-sleep"] }
thiserror = "1"
serde = "1.0.197"
serde_json = "1.0.120"
Expand Down
7 changes: 6 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ impl App for AppState {
// Handle the result on the main thread
match task_result {
TaskResult::Success(message) => match message {
BackendTaskSuccessResult::None => {}
BackendTaskSuccessResult::None => {
self.visible_screen_mut().pop_on_success();
}
BackendTaskSuccessResult::Message(message) => {
self.visible_screen_mut()
.display_message(&message, MessageType::Info);
Expand All @@ -267,6 +269,9 @@ impl App for AppState {
BackendTaskSuccessResult::CoreItem(_) => {
self.visible_screen_mut().display_task_result(message);
}
BackendTaskSuccessResult::SuccessfulVotes(_) => {
self.visible_screen_mut().refresh();
}
},
TaskResult::Error(message) => {
self.visible_screen_mut()
Expand Down
8 changes: 6 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,12 @@ impl AppContext {
self.db.get_local_qualified_identities(self)
}

pub fn load_contested_names(&self) -> Result<Vec<ContestedName>> {
self.db.get_contested_names(self)
pub fn all_contested_names(&self) -> Result<Vec<ContestedName>> {
self.db.get_all_contested_names(self)
}

pub fn ongoing_contested_names(&self) -> Result<Vec<ContestedName>> {
self.db.get_ongoing_contested_names(self)
}

/// Updates the `start_root_screen` in the settings table
Expand Down
345 changes: 300 additions & 45 deletions src/database/contested_names.rs

Large diffs are not rendered by default.

46 changes: 44 additions & 2 deletions src/database/identities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,52 @@ impl Database {

let conn = self.conn.lock().unwrap();
let mut stmt = conn.prepare(
"SELECT id, data, alias, identity_type FROM identity WHERE is_local = 1 AND network = ? AND data IS NOT NULL",
"SELECT data FROM identity WHERE is_local = 1 AND network = ? AND data IS NOT NULL",
)?;
let identity_iter = stmt.query_map(params![network], |row| {
let data: Vec<u8> = row.get(1)?;
let data: Vec<u8> = row.get(0)?;
let identity: QualifiedIdentity = QualifiedIdentity::from_bytes(&data);

Ok(identity)
})?;

let identities: rusqlite::Result<Vec<QualifiedIdentity>> = identity_iter.collect();
identities
}

pub fn get_local_voting_identities(
&self,
app_context: &AppContext,
) -> rusqlite::Result<Vec<QualifiedIdentity>> {
let network = app_context.network_string();

let conn = self.conn.lock().unwrap();
let mut stmt = conn.prepare(
"SELECT data FROM identity WHERE is_local = 1 AND network = ? AND identity_type != 'User' AND data IS NOT NULL",
)?;
let identity_iter = stmt.query_map(params![network], |row| {
let data: Vec<u8> = row.get(0)?;
let identity: QualifiedIdentity = QualifiedIdentity::from_bytes(&data);

Ok(identity)
})?;

let identities: rusqlite::Result<Vec<QualifiedIdentity>> = identity_iter.collect();
identities
}

pub fn get_local_user_identities(
&self,
app_context: &AppContext,
) -> rusqlite::Result<Vec<QualifiedIdentity>> {
let network = app_context.network_string();

let conn = self.conn.lock().unwrap();
let mut stmt = conn.prepare(
"SELECT data FROM identity WHERE is_local = 1 AND network = ? AND identity_type = 'User' AND data IS NOT NULL",
)?;
let identity_iter = stmt.query_map(params![network], |row| {
let data: Vec<u8> = row.get(0)?;
let identity: QualifiedIdentity = QualifiedIdentity::from_bytes(&data);

Ok(identity)
Expand Down
22 changes: 14 additions & 8 deletions src/database/initialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,22 @@ impl Database {
[],
)?;

// Create the composite index for faster querying
self.execute(
"CREATE INDEX IF NOT EXISTS idx_identity_local_network_type
ON identity (is_local, network, identity_type)",
[],
)?;

// Create the contested names table
self.execute(
"CREATE TABLE IF NOT EXISTS contested_name (
normalized_contested_name TEXT,
normalized_contested_name TEXT NOT NULL,
locked_votes INTEGER,
abstain_votes INTEGER,
winner_type INTEGER NOT NULL,
awarded_to BLOB,
ending_time INTEGER,
end_time INTEGER,
locked INTEGER NOT NULL DEFAULT 0,
last_updated INTEGER,
network TEXT NOT NULL,
PRIMARY KEY (normalized_contested_name, network)
Expand All @@ -57,18 +64,17 @@ impl Database {
// Create the contestants table
self.execute(
"CREATE TABLE IF NOT EXISTS contestant (
contest_id TEXT,
identity_id BLOB,
normalized_contested_name TEXT NOT NULL,
identity_id BLOB NOT NULL,
name TEXT,
votes INTEGER,
created_at INTEGER,
created_at_block_height INTEGER,
created_at_core_block_height INTEGER,
document_id BLOB,
network TEXT NOT NULL,
PRIMARY KEY (contest_id, identity_id, network),
FOREIGN KEY (contest_id) REFERENCES contested_names(contest_id),
FOREIGN KEY (identity_id) REFERENCES identities(id)
PRIMARY KEY (normalized_contested_name, identity_id, network),
FOREIGN KEY (normalized_contested_name, network) REFERENCES contested_name(normalized_contested_name, network) ON DELETE CASCADE
)",
[],
)?;
Expand Down
2 changes: 1 addition & 1 deletion src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn initialize_logger() {
};

let filter = EnvFilter::try_new(
"debug,dash_sdk=trace,tenderdash_abci=trace,drive=trace,drive_proof_verifier=trace,rs_dapi_client=debug",
"error,dash_sdk=debug,tenderdash_abci=debug,drive=debug,drive_proof_verifier=debug,rs_dapi_client=debug",
)
.unwrap_or_else(|e| panic!("Failed to create EnvFilter: {:?}", e));

Expand Down
21 changes: 20 additions & 1 deletion src/model/contested_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,33 @@ use dash_sdk::dpp::prelude::{BlockHeight, CoreBlockHeight, Identifier};
use dash_sdk::dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
use std::collections::BTreeMap;

#[derive(Debug, Encode, Decode, Clone)]
pub enum ContestState {
Unknown,
Joinable,
Ongoing,
WonBy(Identifier),
Locked,
}

impl ContestState {
pub fn state_is_votable(&self) -> bool {
match self {
ContestState::Joinable | ContestState::Ongoing => true,
_ => false,
}
}
}

#[derive(Debug, Encode, Decode, Clone)]
pub struct ContestedName {
pub normalized_contested_name: String,
pub contestants: Option<Vec<Contestant>>,
pub locked_votes: Option<u32>,
pub abstain_votes: Option<u32>,
pub awarded_to: Option<Identifier>,
pub ending_time: Option<TimestampMillis>,
pub end_time: Option<TimestampMillis>,
pub state: ContestState,
pub last_updated: Option<TimestampMillis>,
pub my_votes: BTreeMap<(Identifier, EncryptedPrivateKeyTarget, KeyID), ResourceVoteChoice>,
}
Expand Down
24 changes: 24 additions & 0 deletions src/model/qualified_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use dash_sdk::dpp::identity::identity_public_key::accessors::v0::IdentityPublicK
use dash_sdk::dpp::identity::signer::Signer;
use dash_sdk::dpp::identity::KeyType::{BIP13_SCRIPT_HASH, ECDSA_HASH160};
use dash_sdk::dpp::identity::{Identity, KeyID, KeyType, Purpose, SecurityLevel};
use dash_sdk::dpp::platform_value::string_encoding::Encoding;
use dash_sdk::dpp::platform_value::BinaryData;
use dash_sdk::dpp::state_transition::errors::InvalidIdentityPublicKeyTypeError;
use dash_sdk::dpp::{bls_signatures, ed25519_dalek, ProtocolError};
Expand All @@ -27,6 +28,16 @@ pub enum IdentityType {
Evonode,
}

impl IdentityType {
pub fn vote_strength(&self) -> u64 {
match self {
IdentityType::User => 1,
IdentityType::Masternode => 1,
IdentityType::Evonode => 4,
}
}
}

impl Display for IdentityType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -141,6 +152,19 @@ impl QualifiedIdentity {
.0
}

pub fn display_string(&self) -> String {
self.alias
.clone()
.unwrap_or(self.identity.id().to_string(Encoding::Base58))
}

pub fn display_short_string(&self) -> String {
self.alias.clone().unwrap_or_else(|| {
let id_str = self.identity.id().to_string(Encoding::Base58);
id_str.chars().take(5).collect()
})
}

pub fn masternode_payout_address(&self, network: Network) -> Option<Address> {
self.identity
.get_first_public_key_matching(
Expand Down
24 changes: 14 additions & 10 deletions src/platform/contested_names/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod vote_on_dpns_name;

use crate::app::TaskResult;
use crate::context::AppContext;
use crate::model::qualified_identity::QualifiedIdentity;
use crate::platform::BackendTaskSuccessResult;
use dash_sdk::dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
use dash_sdk::Sdk;
use std::sync::Arc;
Expand All @@ -14,7 +16,7 @@ use tokio::sync::mpsc;
pub(crate) enum ContestedResourceTask {
QueryDPNSContestedResources,
QueryDPNSVoteContenders(String),
VoteOnDPNSName(String, ResourceVoteChoice),
VoteOnDPNSName(String, ResourceVoteChoice, Vec<QualifiedIdentity>),
}

impl AppContext {
Expand All @@ -23,17 +25,19 @@ impl AppContext {
task: ContestedResourceTask,
sdk: &Sdk,
sender: mpsc::Sender<TaskResult>,
) -> Result<(), String> {
) -> Result<BackendTaskSuccessResult, String> {
let sdk = sdk.clone();
match &task {
ContestedResourceTask::QueryDPNSContestedResources => {
self.query_dpns_contested_resources(sdk, sender).await
}
ContestedResourceTask::QueryDPNSVoteContenders(name) => {
self.query_dpns_vote_contenders(name, sdk, sender).await
}
ContestedResourceTask::VoteOnDPNSName(name, vote_choice) => {
self.vote_on_dpns_name(name, *vote_choice, sdk, sender)
ContestedResourceTask::QueryDPNSContestedResources => self
.query_dpns_contested_resources(sdk, sender)
.await
.map(|_| BackendTaskSuccessResult::None),
ContestedResourceTask::QueryDPNSVoteContenders(name) => self
.query_dpns_vote_contenders(name, sdk, sender)
.await
.map(|_| BackendTaskSuccessResult::None),
ContestedResourceTask::VoteOnDPNSName(name, vote_choice, voters) => {
self.vote_on_dpns_name(name, *vote_choice, voters, sdk, sender)
.await
} // ContestedResourceTask::VoteOnContestedResource(vote_poll, vote_choice) => {
// let mut vote = Vote::default();
Expand Down
64 changes: 48 additions & 16 deletions src/platform/contested_names/vote_on_dpns_name.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::app::TaskResult;
use crate::context::AppContext;
use crate::model::qualified_identity::QualifiedIdentity;
use crate::platform::BackendTaskSuccessResult;
use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters;
use dash_sdk::dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
use dash_sdk::dpp::identity::accessors::IdentityGettersV0;
Expand All @@ -19,45 +21,75 @@ impl AppContext {
self: &Arc<Self>,
name: &String,
vote_choice: ResourceVoteChoice,
voters: &Vec<QualifiedIdentity>,
sdk: Sdk,
sender: mpsc::Sender<TaskResult>,
) -> Result<(), String> {
let qualified_identities = self.load_local_qualified_identities().unwrap_or_default();

_sender: mpsc::Sender<TaskResult>,
) -> Result<BackendTaskSuccessResult, String> {
// Fetch DPNS contract and document type information
let data_contract = self.dpns_contract.as_ref();
let document_type = data_contract
.document_type_for_name("domain")
.expect("expected document type");

let Some(contested_index) = document_type.find_contested_index() else {
return Err("No contested index on dpns domains".to_string());
};
let index_values = [Value::from("dash"), Value::Text(name.clone())]; // hardcoded for dpns

// Hardcoded values for DPNS
let index_values = [Value::from("dash"), Value::Text(name.clone())];

// Create the vote poll to use in the vote
let vote_poll = ContestedDocumentResourceVotePoll {
index_name: contested_index.name.clone(),
index_values: index_values.to_vec(),
document_type_name: document_type.name().to_string(),
contract_id: data_contract.id(),
};

for qualified_identity in qualified_identities.iter().take(1) {
let mut vote_results = vec![];
let mut strength = 0;

// Iterate over the provided voters (QualifiedIdentity)
for qualified_identity in voters.iter() {
if let Some((_, public_key)) = &qualified_identity.associated_voter_identity {
// Create the resource vote
let resource_vote = ResourceVoteV0 {
vote_poll: vote_poll.clone().into(),
resource_vote_choice: vote_choice,
};
let vote = Vote::ResourceVote(ResourceVote::V0(resource_vote));
vote.put_to_platform_and_wait_for_response(
qualified_identity.identity.id(),
public_key,
&sdk,
qualified_identity,
None,
)
.await
.map_err(|e| format!("Error voting: {}", e))?;

// Submit the vote to the platform and await a response
let result = vote
.put_to_platform_and_wait_for_response(
qualified_identity.identity.id(),
public_key,
&sdk,
qualified_identity,
None,
)
.await
.map_err(|e| format!("Error voting: {}", e))?;

strength += qualified_identity.identity_type.vote_strength();
vote_results.push(result);
} else {
return Err(format!(
"No associated voter identity for qualified identity: {:?}",
qualified_identity.identity.id()
));
}
}
Ok(())

self.db
.update_vote_count(
name,
self.network.to_string().as_str(),
strength,
vote_choice,
)
.map_err(|e| format!("error updating ending time: {}", e))?;

Ok(BackendTaskSuccessResult::SuccessfulVotes(vote_results))
}
}
Loading