Skip to content

[WIP] Diesel ORM #5

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ Cargo.lock

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Database file
*.sqlite3
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ rusqlite_migration = { version = "1.0" }
rand = { version="0.8.5" }
serde = {version="1.0", features = ["derive"]}
serde_json = { version = "1.0", features = ["raw_value"] }
diesel = { version = "2.1.4", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] }
diesel_migrations = "2.1.0"

[dev-dependencies]
uuid = { version = "1.6.1", features = ["serde", "v4"] }
Expand Down
9 changes: 9 additions & 0 deletions diesel.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/store/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]

[migrations_directory]
dir = "src/store/migrations"
19 changes: 4 additions & 15 deletions src/cli/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl AccountCmd {
// LIST ACCOUNTS
// ================================================================================================

fn list_accounts(client: Client) -> Result<(), String> {
fn list_accounts(mut client: Client) -> Result<(), String> {
println!("{}", "-".repeat(240));
println!(
"{0: <18} | {1: <66} | {2: <66} | {3: <66} | {4: <15}",
Expand Down Expand Up @@ -100,7 +100,7 @@ fn list_accounts(client: Client) -> Result<(), String> {
// ================================================================================================

fn new_account(
client: Client,
mut client: Client,
template: &Option<AccountTemplate>,
deploy: bool,
) -> Result<(), String> {
Expand Down Expand Up @@ -151,20 +151,9 @@ fn new_account(
}
.map_err(|err| err.to_string())?;

// TODO: Make these inserts atomic through a single transaction
client
.store()
.insert_account_code(account.code())
.and_then(|_| client.store().insert_account_storage(account.storage()))
.and_then(|_| client.store().insert_account_vault(account.vault()))
.and_then(|_| client.store().insert_account(&account))
.map(|_| {
println!(
"Succesfully created and stored Account ID: {}",
account.id()
)
})
.map_err(|x| x.to_string())?;
.insert_account_with_metadata(&account)
.map_err(|err| err.to_string())?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub enum StoreError {
ConnectionError(rusqlite::Error),
MigrationError(rusqlite_migration::Error),
ColumnParsingError(rusqlite::Error),
QueryError(rusqlite::Error),
QueryError(diesel::result::Error),
InputSerializationError(serde_json::Error),
DataDeserializationError(serde_json::Error),
InputNoteNotFound(Digest),
Expand Down
32 changes: 31 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ use store::Store;
pub mod errors;
use errors::ClientError;

// from diesel guide, eval keeping this here
// pub mod models; // can we move this to /store ?
// pub mod schema; // can we move this to /store ?

// MIDEN CLIENT
// ================================================================================================

Expand Down Expand Up @@ -51,13 +55,23 @@ impl Client {
&self.store
}

// ACCOUNT INSERTION
// --------------------------------------------------------------------------------------------

/// Inserts a new account into the client's store.
pub fn insert_account_with_metadata(&mut self, account: &Account) -> Result<(), ClientError> {
self.store
.insert_account_with_metadata(account)
.map_err(ClientError::StoreError)
}

// ACCOUNT DATA RETRIEVAL
// --------------------------------------------------------------------------------------------

/// Returns summary info about the accounts managed by this client.
///
/// TODO: replace `AccountStub` with a more relevant structure.
pub fn get_accounts(&self) -> Result<Vec<AccountStub>, ClientError> {
pub fn get_accounts(&mut self) -> Result<Vec<AccountStub>, ClientError> {
self.store.get_accounts().map_err(|err| err.into())
}

Expand Down Expand Up @@ -180,6 +194,22 @@ mod tests {
account::MockAccountType, notes::AssetPreservationStatus, transaction::mock_inputs,
};

#[test]
/// This test is only to ensure that the database is created correctly.
fn test_get_accounts() {
// generate test store path
let store_path = create_test_store_path();

// generate test client
let mut client = super::Client::new(super::ClientConfig::new(
store_path.into_os_string().into_string().unwrap(),
super::Endpoint::default(),
))
.unwrap();

client.get_accounts().unwrap();
}

#[test]
fn test_input_notes_round_trip() {
// generate test store path
Expand Down
104 changes: 104 additions & 0 deletions src/store/diesel_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Diesel setup Instructions
[source](https://diesel.rs/guides/getting-started.html)

**Note:** this guide only needs to be followed in case `diesel_migrations` crate is not used, which is **not** the case for this project. This guide is only for reference on how to setup diesel with static sql files.

## Install libsqlite3:
- Ubuntu: `sudo apt intsall libsqlite3-dev`
- MacOS: preinstalled in modern versions.

## Install Diesel CLI:
`cargo install diesel_cli --no-default-features --features sqlite`

## Database creation:
`diesel setup --database-url=store.sqlite3`

This will create a `store.sqlite3` file in the current root of the proyect.

## Modify `diesel.toml`:
```toml
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/store/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]

[migrations_directory]
dir = "src/store/migrations"
```

## Generate migration file:
`diesel migration generate miden_client_store --database-url=store.sqlite3`

## Modify `src/store/migrations/TIMESTAMP_miden_client_store` file:

```sql
-- Create account_code table
CREATE TABLE account_code (
root BLOB NOT NULL, -- root of the Merkle tree for all exported procedures in account module.
procedures BLOB NOT NULL, -- serialized procedure digests for the account code.
module BLOB NOT NULL, -- serialized ModuleAst for the account code.
PRIMARY KEY (root)
);

-- Create account_storage table
CREATE TABLE account_storage (
root BLOB NOT NULL, -- root of the account storage Merkle tree.
slots BLOB NOT NULL, -- serialized key-value pair of non-empty account slots.
PRIMARY KEY (root)
);

-- Create account_vaults table
CREATE TABLE account_vaults (
root BLOB NOT NULL, -- root of the Merkle tree for the account vault.
assets BLOB NOT NULL, -- serialized account vault assets.
PRIMARY KEY (root)
);

-- Create account_keys table
CREATE TABLE account_keys (
account_id UNSIGNED BIG INT NOT NULL, -- ID of the account
key_pair BLOB NOT NULL, -- key pair
PRIMARY KEY (account_id),
FOREIGN KEY (account_id) REFERENCES accounts(id)
);

-- Create accounts table
CREATE TABLE accounts (
id UNSIGNED BIG INT NOT NULL, -- account ID.
code_root BLOB NOT NULL, -- root of the account_code Merkle tree.
storage_root BLOB NOT NULL, -- root of the account_storage Merkle tree.
vault_root BLOB NOT NULL, -- root of the account_vault Merkle tree.
nonce BIGINT NOT NULL, -- account nonce.
committed BOOLEAN NOT NULL, -- true if recorded, false if not.
PRIMARY KEY (id),
FOREIGN KEY (code_root) REFERENCES account_code(root),
FOREIGN KEY (storage_root) REFERENCES account_storage(root),
FOREIGN KEY (vault_root) REFERENCES account_vaults(root)
);

-- Create input notes table
CREATE TABLE input_notes (
hash BLOB NOT NULL, -- the note hash
nullifier BLOB NOT NULL, -- the nullifier of the note
script BLOB NOT NULL, -- the serialized NoteScript, including script hash and ProgramAst
vault BLOB NOT NULL, -- the serialized NoteVault, including vault hash and list of assets
inputs BLOB NOT NULL, -- the serialized NoteInputs, including inputs hash and list of inputs
serial_num BLOB NOT NULL, -- the note serial number
sender_id UNSIGNED BIG INT NOT NULL, -- the account ID of the sender
tag UNSIGNED BIG INT NOT NULL, -- the note tag
num_assets UNSIGNED BIG INT NOT NULL, -- the number of assets in the note
inclusion_proof BLOB NOT NULL, -- the inclusion proof of the note against a block number
recipients BLOB NOT NULL, -- a list of account IDs of accounts which can consume this note
status TEXT CHECK( status IN ('pending', 'committed')), -- the status of the note - either pending or committed
commit_height UNSIGNED BIG INT NOT NULL, -- the block number at which the note was included into the chain
PRIMARY KEY (hash)
);
```

## Apply migration:
`diesel migration run --database-url=store.sqlite3`

## Notice
If you followed this guide, some parts of the code will need to be modified to mark the default database url as `store.sqlite`.
21 changes: 0 additions & 21 deletions src/store/migrations.rs

This file was deleted.

1 change: 1 addition & 0 deletions src/store/migrations/miden_client_store/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- This file should undo anything in `up.sql`
File renamed without changes.
Loading