Skip to content

Commit d493f98

Browse files
committed
chore: rewrite platform/generic/proteus in terms of the new traits
1 parent e956e1d commit d493f98

File tree

2 files changed

+142
-196
lines changed

2 files changed

+142
-196
lines changed
Lines changed: 77 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,120 @@
11
use rusqlite::OptionalExtension;
22

33
use crate::{
4-
CryptoKeystoreError, MissingKeyErrorKind,
4+
CryptoKeystoreError, CryptoKeystoreResult, Entity, EntityBase, EntityTransactionExt, UniqueEntity,
55
connection::{KeystoreDatabaseConnection, TransactionWrapper},
6-
entities::{Entity, EntityBase, EntityFindParams, EntityTransactionExt, ProteusIdentity, StringEntityId},
6+
entities::ProteusIdentity,
77
};
88

9-
#[async_trait::async_trait]
10-
impl Entity for ProteusIdentity {
11-
fn id_raw(&self) -> &[u8] {
12-
Self::ID
13-
}
14-
15-
async fn find_all(
16-
conn: &mut Self::ConnectionType,
17-
_params: EntityFindParams,
18-
) -> crate::CryptoKeystoreResult<Vec<Self>> {
19-
let mut res = vec![];
20-
if let Some(identity) = Self::find_one(conn, &StringEntityId::default()).await? {
21-
res.push(identity);
22-
}
9+
impl EntityBase for ProteusIdentity {
10+
type ConnectionType = KeystoreDatabaseConnection;
11+
type AutoGeneratedFields = ();
12+
const COLLECTION_NAME: &'static str = "proteus_identities";
2313

24-
Ok(res)
14+
fn to_transaction_entity(self) -> crate::transaction::dynamic_dispatch::Entity {
15+
crate::transaction::dynamic_dispatch::Entity::ProteusIdentity(self)
2516
}
17+
}
2618

27-
async fn find_one(
28-
conn: &mut Self::ConnectionType,
29-
_id: &StringEntityId,
30-
) -> crate::CryptoKeystoreResult<Option<Self>> {
31-
let mut conn = conn.conn().await;
32-
let transaction = conn.transaction()?;
19+
impl UniqueEntity for ProteusIdentity {
20+
const KEY: () = ();
21+
}
3322

34-
let mut row_id: Option<i64> = transaction
35-
.query_row(
36-
"SELECT rowid FROM proteus_identities ORDER BY rowid ASC LIMIT 1",
37-
[],
38-
|r| r.get(0),
39-
)
40-
.optional()?;
23+
#[async_trait::async_trait]
24+
impl Entity for ProteusIdentity {
25+
/// Each distinct [`PrimaryKey`] uniquely identifies either 0 or 1 instance.
26+
///
27+
/// This constraint should be enforced at the DB level.
28+
type PrimaryKey = ();
29+
30+
/// Get this entity's primary key.
31+
fn primary_key(&self) -> Self::PrimaryKey {
32+
()
33+
}
4134

42-
let row_id = if let Some(rowid) = row_id.take() {
43-
rowid
44-
} else {
35+
/// Get an entity by its primary key.
36+
///
37+
/// The type signature here is somewhat complicated, but it breaks down simply: if our primary key is something
38+
/// like `Vec<u8>`, we want to be able to use this method even if what we have on hand is `&[u8]`.
39+
///
40+
/// Actually ignoring the key here is unusual, but acceptable for a unique entity.
41+
async fn get(conn: &mut Self::ConnectionType, _key: &()) -> CryptoKeystoreResult<Option<Self>> {
42+
let conn = conn.conn().await;
43+
// this gets the oldest identity, and is retained behavior from a previous implementation:
44+
// it used to save additional bonus identities without error
45+
let mut statement = conn.prepare_cached("SELECT pk, sk FROM proteus_identities ORDER BY rowid ASC LIMIT 1")?;
46+
let Some(identity) = statement
47+
.query_row([], |row| {
48+
let identity = ProteusIdentity {
49+
pk: row.get("pk")?,
50+
sk: row.get("sk")?,
51+
};
52+
Ok(identity)
53+
})
54+
.optional()?
55+
else {
4556
return Ok(None);
4657
};
4758

48-
use std::io::Read as _;
49-
let mut blob = transaction.blob_open(rusqlite::MAIN_DB, "proteus_identities", "pk", row_id, true)?;
50-
if blob.len() != Self::PK_KEY_SIZE {
59+
if identity.pk.len() != Self::PK_KEY_SIZE {
5160
return Err(CryptoKeystoreError::InvalidKeySize {
5261
expected: Self::PK_KEY_SIZE,
53-
actual: blob.len(),
62+
actual: identity.pk.len(),
5463
key: "pk",
5564
});
5665
}
57-
let mut pk = Vec::with_capacity(blob.len());
58-
blob.read_to_end(&mut pk)?;
59-
blob.close()?;
60-
61-
let mut blob = transaction.blob_open(rusqlite::MAIN_DB, "proteus_identities", "sk", row_id, true)?;
62-
if blob.len() != Self::SK_KEY_SIZE {
66+
if identity.sk.len() != Self::SK_KEY_SIZE {
6367
return Err(CryptoKeystoreError::InvalidKeySize {
6468
expected: Self::SK_KEY_SIZE,
65-
actual: blob.len(),
69+
actual: identity.sk.len(),
6670
key: "sk",
6771
});
6872
}
69-
let mut sk = Vec::with_capacity(blob.len());
70-
blob.read_to_end(&mut sk)?;
71-
blob.close()?;
7273

73-
Ok(Some(Self { pk, sk }))
74+
Ok(Some(identity))
7475
}
7576

76-
async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<usize> {
77+
/// Count the number of entities of this type in the database.
78+
async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<u32> {
7779
let conn = conn.conn().await;
78-
let count = conn.query_row("SELECT COUNT(*) FROM proteus_identities", [], |r| r.get(0))?;
80+
let mut statement = conn.prepare_cached("SELECT COUNT(*) FROM proteus_identities")?;
81+
let count = statement.query_one([], |row| row.get(0))?;
7982
// This should always be less or equal 1
8083
debug_assert!(count <= 1);
8184
Ok(count)
8285
}
83-
}
8486

85-
#[async_trait::async_trait]
86-
impl EntityBase for ProteusIdentity {
87-
type ConnectionType = KeystoreDatabaseConnection;
88-
type AutoGeneratedFields = ();
89-
const COLLECTION_NAME: &'static str = "proteus_identities";
90-
91-
fn to_missing_key_err_kind() -> MissingKeyErrorKind {
92-
MissingKeyErrorKind::ProteusIdentity
93-
}
94-
95-
fn to_transaction_entity(self) -> crate::transaction::dynamic_dispatch::Entity {
96-
crate::transaction::dynamic_dispatch::Entity::ProteusIdentity(self)
87+
/// Retrieve all entities of this type from the database.
88+
async fn load_all(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<Vec<Self>> {
89+
Self::get(conn, &())
90+
.await
91+
.map(|optional| optional.into_iter().collect())
9792
}
9893
}
9994

10095
#[async_trait::async_trait]
101-
impl EntityTransactionExt for ProteusIdentity {
102-
async fn save(&self, transaction: &TransactionWrapper<'_>) -> crate::CryptoKeystoreResult<()> {
103-
use rusqlite::ToSql as _;
104-
transaction.execute(
105-
"INSERT INTO proteus_identities (sk, pk) VALUES (?, ?)",
106-
[
107-
rusqlite::blob::ZeroBlob(self.sk.len() as i32).to_sql()?,
108-
rusqlite::blob::ZeroBlob(self.pk.len() as i32).to_sql()?,
109-
],
110-
)?;
111-
112-
let row_id = transaction.last_insert_rowid();
113-
114-
use std::io::Write as _;
115-
let mut blob = transaction.blob_open(rusqlite::MAIN_DB, "proteus_identities", "sk", row_id, false)?;
116-
blob.write_all(&self.sk)?;
117-
blob.close()?;
118-
119-
let mut blob = transaction.blob_open(rusqlite::MAIN_DB, "proteus_identities", "pk", row_id, false)?;
120-
blob.write_all(&self.pk)?;
121-
blob.close()?;
96+
impl<'a> EntityTransactionExt<'a> for ProteusIdentity {
97+
type Transaction = TransactionWrapper<'a>;
98+
99+
/// Use the transaction's interface to save this entity to the database
100+
async fn save(&self, tx: &Self::Transaction) -> CryptoKeystoreResult<()> {
101+
let mut statement = tx.prepare_cached("SELECT COUNT(*) FROM proteus_identities")?;
102+
let count = statement.query_one([], |row| row.get::<_, u32>(0))?;
103+
if count > 0 {
104+
return Err(CryptoKeystoreError::AlreadyExists("ProteusEntity"));
105+
}
122106

107+
let mut statement = tx.prepare_cached("INSERT INTO proteus_identities (sk, pk) values (?, ?)")?;
108+
statement.execute([&self.sk, &self.pk])?;
123109
Ok(())
124110
}
125111

126-
async fn delete_fail_on_missing_id(
127-
transaction: &TransactionWrapper<'_>,
128-
_id: StringEntityId<'_>,
129-
) -> crate::CryptoKeystoreResult<()> {
130-
let row_id = transaction.query_row(
131-
"SELECT rowid FROM proteus_identities ORDER BY rowid ASC LIMIT 1",
132-
[],
133-
|r| r.get::<_, i64>(0),
134-
)?;
135-
use rusqlite::ToSql as _;
136-
transaction.execute("DELETE FROM proteus_identities WHERE rowid = ?", [row_id.to_sql()?])?;
137-
138-
Ok(())
112+
/// Use the transaction's inteface to delete this entity from the database.
113+
///
114+
/// Returns `true` if at least one entity was deleted, or `false` if the id was not found in the database.
115+
async fn delete(tx: &Self::Transaction, _id: &()) -> CryptoKeystoreResult<bool> {
116+
let mut statement = tx.prepare_cached("DELETE FROM proteus_identities")?;
117+
let affected = statement.execute([])?;
118+
Ok(affected > 0)
139119
}
140120
}

0 commit comments

Comments
 (0)