Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DB issues #370

Merged
merged 3 commits into from
Dec 13, 2023
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
4 changes: 3 additions & 1 deletion bindings_ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ There is no support for using breakpoints or a debugger with FFI currently. Meth
1. Examine the database in your app
1. Use logs to find the location of the Sqlite database and the database encryption key
1. Find the database (for example, on Android emulator use Device File Explorer in Android Studio)
1. Copy the database to your local machine and open it on the command line using `sqlite3`
1. `brew install sqlcipher`
1. Copy the database to your local machine and open it on the command line using `sqlcipher <file>.db3`
1. Decrypt the database if needed [as follows](https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_sql_pragmas/#pragma-key) (or disable database encryption before running the app)
1. Note that decryption is buggy if using `sqlite3` - `sqlcipher` is necessary here

# Releasing new version

Expand Down
6 changes: 3 additions & 3 deletions bindings_ffi/examples/ExampleInstrumentedTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class ExampleInstrumentedTest {
val credentials: Credentials = Credentials.create(ECKeyPair.create(privateKey))
val inboxOwner = Web3jInboxOwner(credentials)
runBlocking {
val client = uniffi.xmtpv3.createClient(AndroidFfiLogger(), inboxOwner, EMULATOR_LOCALHOST_ADDRESS, false)
assertNotNull("Should be able to construct client", client.walletAddress())
val client = uniffi.xmtpv3.createClient(AndroidFfiLogger(), inboxOwner, EMULATOR_LOCALHOST_ADDRESS, false, null, null)
assertNotNull("Should be able to construct client", client.accountAddress())
client.close()
}
}
Expand All @@ -38,7 +38,7 @@ class ExampleInstrumentedTest {
runBlocking {
var didThrow = false;
try {
val client = uniffi.xmtpv3.createClient(AndroidFfiLogger(), inboxOwner, "http://incorrect:5556", false)
val client = uniffi.xmtpv3.createClient(AndroidFfiLogger(), inboxOwner, "http://incorrect:5556", false, null, null)
} catch (e: Exception) {
didThrow = true
}
Expand Down
3 changes: 2 additions & 1 deletion bindings_ffi/examples/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import java.io.File
import java.nio.charset.StandardCharsets
import java.security.SecureRandom

const val EMULATOR_LOCALHOST_ADDRESS = "http://10.0.2.2:5556"
const val DEV_NETWORK_ADDRESS = "https://dev.xmtp.network:5556"

class Web3jInboxOwner(private val credentials: Credentials) : FfiInboxOwner {
Expand Down Expand Up @@ -58,7 +59,7 @@ class MainActivity : AppCompatActivity() {
val client = uniffi.xmtpv3.createClient(
AndroidFfiLogger(),
inboxOwner,
DEV_NETWORK_ADDRESS,
EMULATOR_LOCALHOST_ADDRESS,
true,
dbPath,
dbEncryptionKey
Expand Down
Binary file modified bindings_ffi/jniLibs/arm64-v8a/libuniffi_xmtpv3.so
Binary file not shown.
40 changes: 20 additions & 20 deletions bindings_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ fn stringify_error_chain<T: Error>(error: &T) -> String {
result
}

fn static_enc_key() -> EncryptionKey {
[2u8; 32]
}

#[uniffi::export(async_runtime = "tokio")]
pub async fn create_client(
logger: Box<dyn FfiLogger>,
Expand All @@ -81,19 +77,21 @@ pub async fn create_client(
db,
encryption_key.is_some()
);
let key: EncryptionKey = match encryption_key {
Some(key) => key.try_into().map_err(|_err| GenericError::Generic {
err: "Malformed 32 byte encryption key".to_string(),
})?,
None => static_enc_key(),
};

let store = match db {
// TODO support encryption
Some(path) => EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(path)),
let storage_option = match db {
Some(path) => StorageOption::Persistent(path),
None => StorageOption::Ephemeral,
};

None => EncryptedMessageStore::new(StorageOption::Ephemeral, key),
}?;
let store = match encryption_key {
Some(key) => {
let key: EncryptionKey = key.try_into().map_err(|_err| GenericError::Generic {
err: "Malformed 32 byte encryption key".to_string(),
})?;
EncryptedMessageStore::new(storage_option, key)?
}
None => EncryptedMessageStore::new_unencrypted(storage_option)?,
};

log::info!("Creating XMTP client");
let xmtp_client: RustXmtpClient = ClientBuilder::new(inbox_owner.into())
Expand Down Expand Up @@ -277,15 +275,14 @@ mod tests {
use std::{env, sync::Arc};

use crate::{
create_client, inbox_owner::SigningError, logger::FfiLogger, static_enc_key, FfiInboxOwner,
FfiXmtpClient,
create_client, inbox_owner::SigningError, logger::FfiLogger, FfiInboxOwner, FfiXmtpClient,
};
use ethers_core::rand::{
self,
distributions::{Alphanumeric, DistString},
};
use xmtp_cryptography::{signature::RecoverableSignature, utils::rng};
use xmtp_mls::InboxOwner;
use xmtp_mls::{storage::EncryptionKey, InboxOwner};

#[derive(Clone)]
pub struct LocalWalletInboxOwner {
Expand Down Expand Up @@ -329,6 +326,10 @@ mod tests {
format!("{}/{}.db3", env::temp_dir().to_str().unwrap(), db_name)
}

fn static_enc_key() -> EncryptionKey {
[2u8; 32]
}

async fn new_test_client() -> Arc<FfiXmtpClient> {
let ffi_inbox_owner = LocalWalletInboxOwner::new();

Expand Down Expand Up @@ -391,7 +392,6 @@ mod tests {
)
}

#[ignore]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_create_client_with_key() {
let ffi_inbox_owner = LocalWalletInboxOwner::new();
Expand All @@ -414,7 +414,7 @@ mod tests {
drop(client_a);

let mut other_key = static_enc_key();
other_key[0] = 1;
other_key[31] = 1;

let result_errored = create_client(
Box::new(MockLogger {}),
Expand Down
11 changes: 5 additions & 6 deletions xmtp_mls/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ where
{
fn initialize_identity(
self,
store: &EncryptedMessageStore,
provider: &'a XmtpOpenMlsProvider,
) -> Result<Identity, ClientBuilderError> {
let identity_option: Option<Identity> =
store.conn()?.fetch(&())?.map(|i: StoredIdentity| i.into());
let identity_option: Option<Identity> = provider
.conn()
.fetch(&())?
.map(|i: StoredIdentity| i.into());
debug!("Existing identity in store: {:?}", identity_option);
match self {
IdentityStrategy::CachedOnly => {
Expand Down Expand Up @@ -144,9 +145,7 @@ where
let conn = store.conn()?;
let provider = XmtpOpenMlsProvider::new(&conn);
debug!("Initializing identity");
let identity = self
.identity_strategy
.initialize_identity(&store, &provider)?;
let identity = self.identity_strategy.initialize_identity(&provider)?;
Ok(Client::new(api_client, network, identity, store))
}
}
Expand Down
47 changes: 27 additions & 20 deletions xmtp_mls/src/storage/encrypted_store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub fn ignore_unique_violation<T>(
pub struct EncryptedMessageStore {
connect_opt: StorageOption,
pool: Pool<ConnectionManager<SqliteConnection>>,
enc_key: Option<EncryptionKey>,
}

impl<'a> From<&'a EncryptedMessageStore> for Cow<'a, EncryptedMessageStore> {
Expand Down Expand Up @@ -101,18 +102,13 @@ impl EncryptedMessageStore {
.map_err(|e| StorageError::DbInit(e.to_string()))?,
};

// // Setup SqlCipherKey
if let Some(key) = enc_key {
log::info!("Setting SqlCipher key");
Self::set_sqlcipher_key(pool.clone(), &key)?;
}

// TODO: Validate that sqlite is correctly configured. Bad EncKey is not detected until the
// migrations run which returns an unhelpful error.

let mut obj = Self {
connect_opt: opts,
pool,
enc_key,
};

obj.init_db()?;
Expand All @@ -133,11 +129,15 @@ impl EncryptedMessageStore {
fn raw_conn(
&self,
) -> Result<PooledConnection<ConnectionManager<SqliteConnection>>, StorageError> {
let conn = self
let mut conn = self
.pool
.get()
.map_err(|e| StorageError::Pool(e.to_string()))?;

if let Some(key) = self.enc_key {
conn.batch_execute(&format!("PRAGMA key = \"x'{}'\";", hex::encode(key)))?;
}

Ok(conn)
}

Expand Down Expand Up @@ -172,19 +172,6 @@ impl EncryptedMessageStore {
})
}

fn set_sqlcipher_key(
pool: Pool<ConnectionManager<SqliteConnection>>,
encryption_key: &[u8; 32],
) -> Result<(), StorageError> {
let conn = &mut pool.get().map_err(|e| StorageError::Pool(e.to_string()))?;

conn.batch_execute(&format!(
"PRAGMA key = \"x'{}'\";",
hex::encode(encryption_key)
))?;
Ok(())
}

pub fn generate_enc_key() -> EncryptionKey {
// TODO: Handle Key Better/ Zeroize
let mut key = [0u8; 32];
Expand Down Expand Up @@ -370,6 +357,26 @@ mod tests {
fs::remove_file(db_path).unwrap();
}

#[tokio::test]
async fn encrypted_db_with_multiple_connections() {
let db_path = tmp_path();
let store = EncryptedMessageStore::new(
StorageOption::Persistent(db_path.clone()),
EncryptedMessageStore::generate_enc_key(),
)
.unwrap();

let conn1 = &store.conn().unwrap();
let account_address = "address";
StoredIdentity::new(account_address.to_string(), rand_vec(), rand_vec())
.store(conn1)
.unwrap();

let conn2 = &store.conn().unwrap();
let fetched_identity: StoredIdentity = conn2.fetch(&()).unwrap().unwrap();
assert_eq!(fetched_identity.account_address, account_address);
}

#[test]
fn it_returns_ok_when_given_ok_result() {
let result: Result<(), diesel::result::Error> = Ok(());
Expand Down
Loading