From 4ee988f45917d64498fc8dc7b2e79cb52f20a531 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 3 Nov 2021 13:26:59 +0200 Subject: [PATCH 01/14] add sqlite store --- Cargo.lock | 21 ++++-- Cargo.toml | 2 +- applications/tari_validator_node/Cargo.toml | 1 + .../tari_validator_node/src/dan_node.rs | 10 +-- applications/tari_validator_node/src/main.rs | 3 +- dan_layer/core/src/storage/error.rs | 6 ++ dan_layer/core/src/storage/mod.rs | 54 ++++++-------- dan_layer/core/src/storage/sqlite/mod.rs | 21 ------ dan_layer/core/src/workers/states/prepare.rs | 2 +- dan_layer/core/src/workers/states/starting.rs | 2 +- .../Cargo.toml | 8 +- .../diesel.toml | 0 .../migrations/.gitkeep | 0 .../2021-11-02-185150_create_nodes/down.sql | 0 .../2021-11-02-185150_create_nodes/up.sql | 0 dan_layer/storage_sqlite/src/error.rs | 59 +++++++++++++++ .../src/lib.rs | 10 +++ dan_layer/storage_sqlite/src/models/mod.rs | 23 ++++++ dan_layer/storage_sqlite/src/models/node.rs | 36 +++++++++ .../src/schema.rs | 0 .../src/sqlite_backend_adapter.rs | 73 +++++++++++++++++++ .../storage_sqlite/src/sqlite_db_factory.rs | 60 +++++++++++++++ .../storage_sqlite/src/sqlite_transaction.rs | 37 ++++++++++ 23 files changed, 354 insertions(+), 74 deletions(-) rename dan_layer/{validator_node_sqlite => storage_sqlite}/Cargo.toml (55%) rename dan_layer/{validator_node_sqlite => storage_sqlite}/diesel.toml (100%) rename dan_layer/{validator_node_sqlite => storage_sqlite}/migrations/.gitkeep (100%) rename dan_layer/{validator_node_sqlite => storage_sqlite}/migrations/2021-11-02-185150_create_nodes/down.sql (100%) rename dan_layer/{validator_node_sqlite => storage_sqlite}/migrations/2021-11-02-185150_create_nodes/up.sql (100%) create mode 100644 dan_layer/storage_sqlite/src/error.rs rename dan_layer/{validator_node_sqlite => storage_sqlite}/src/lib.rs (84%) create mode 100644 dan_layer/storage_sqlite/src/models/mod.rs create mode 100644 dan_layer/storage_sqlite/src/models/node.rs rename dan_layer/{validator_node_sqlite => storage_sqlite}/src/schema.rs (100%) create mode 100644 dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs create mode 100644 dan_layer/storage_sqlite/src/sqlite_db_factory.rs create mode 100644 dan_layer/storage_sqlite/src/sqlite_transaction.rs diff --git a/Cargo.lock b/Cargo.lock index 28a2449411..6c8c38836f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6395,6 +6395,18 @@ dependencies = [ "tonic", ] +[[package]] +name = "tari_dan_storage_sqlite" +version = "0.1.0" +dependencies = [ + "diesel", + "diesel_migrations", + "dotenv", + "tari_common", + "tari_dan_core", + "thiserror", +] + [[package]] name = "tari_infra_derive" version = "0.12.0" @@ -6716,6 +6728,7 @@ dependencies = [ "tari_core", "tari_crypto", "tari_dan_core", + "tari_dan_storage_sqlite", "tari_mmr", "tari_p2p", "tari_service_framework", @@ -7891,14 +7904,6 @@ dependencies = [ "getrandom 0.2.3", ] -[[package]] -name = "validator_node_sqlite" -version = "0.1.0" -dependencies = [ - "diesel", - "dotenv", -] - [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index d066db85dc..166b4d0533 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ members = [ "comms/dht", "comms/rpc_macros", "dan_layer/core", - "dan_layer/validator_node_sqlite", + "dan_layer/storage_sqlite", "infrastructure/shutdown", "infrastructure/storage", "infrastructure/test_utils", diff --git a/applications/tari_validator_node/Cargo.toml b/applications/tari_validator_node/Cargo.toml index e4cc848be8..a58f6b66c5 100644 --- a/applications/tari_validator_node/Cargo.toml +++ b/applications/tari_validator_node/Cargo.toml @@ -22,6 +22,7 @@ tari_shutdown = { path = "../../infrastructure/shutdown" } tari_storage = { path = "../../infrastructure/storage" } tari_core = {path = "../../base_layer/core"} tari_dan_core = {path = "../../dan_layer/core"} +tari_dan_storage_sqlite = {path = "../../dan_layer/storage_sqlite"} anyhow = "1.0.32" async-trait = "0.1.50" diff --git a/applications/tari_validator_node/src/dan_node.rs b/applications/tari_validator_node/src/dan_node.rs index e54e4405ea..d40b64c931 100644 --- a/applications/tari_validator_node/src/dan_node.rs +++ b/applications/tari_validator_node/src/dan_node.rs @@ -66,16 +66,10 @@ use tari_dan_core::{ TariDanPayloadProcessor, TariDanPayloadProvider, }, - storage::{ - sqlite::SqliteStorageService, - AssetDataStore, - BackendAdapter, - DbFactory, - LmdbAssetStore, - SqliteDbFactory, - }, + storage::{sqlite::SqliteStorageService, AssetDataStore, BackendAdapter, DbFactory, LmdbAssetStore}, workers::ConsensusWorker, }; +use tari_dan_storage_sqlite::SqliteDbFactory; use tari_p2p::{ comms_connector::{pubsub_connector, SubscriptionFactory}, initialization::{spawn_comms_using_transport, P2pConfig, P2pInitializer}, diff --git a/applications/tari_validator_node/src/main.rs b/applications/tari_validator_node/src/main.rs index b7c07b8b92..e57dc1c769 100644 --- a/applications/tari_validator_node/src/main.rs +++ b/applications/tari_validator_node/src/main.rs @@ -42,8 +42,9 @@ use tari_app_utilities::initialization::init_configuration; use tari_common::{configuration::bootstrap::ApplicationType, exit_codes::ExitCodes, GlobalConfig}; use tari_dan_core::{ services::{MempoolService, MempoolServiceHandle}, - storage::{BackendAdapter, ChainStorageService, DbFactory, SqliteDbFactory}, + storage::{BackendAdapter, ChainStorageService, DbFactory}, }; +use tari_dan_storage_sqlite::SqliteDbFactory; use tari_shutdown::{Shutdown, ShutdownSignal}; use tokio::{runtime, runtime::Runtime, task}; use tonic::transport::Server; diff --git a/dan_layer/core/src/storage/error.rs b/dan_layer/core/src/storage/error.rs index e2227ec43c..eec7162efb 100644 --- a/dan_layer/core/src/storage/error.rs +++ b/dan_layer/core/src/storage/error.rs @@ -26,6 +26,8 @@ use tari_storage::lmdb_store::LMDBError; #[derive(Debug, thiserror::Error)] pub enum StorageError { + #[error("Could not connect to storage:{reason}")] + ConnectionError { reason: String }, #[error("IO Error: {0}")] Io(#[from] io::Error), #[error("LMDB: {0}")] @@ -34,4 +36,8 @@ pub enum StorageError { LMDBError(#[from] LMDBError), #[error("Decode Error: {0}")] DecodeError(#[from] bytecodec::Error), + #[error("Query error:{reason}")] + QueryError { reason: String }, + #[error("Migration error: {reason}")] + MigrationError { reason: String }, } diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index 2845c83efc..053e03dc96 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -20,10 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - models::{ChainHeight, HotStuffTreeNode, Instruction, SidechainMetadata, TreeNodeHash}, - storage::sqlite::SqliteBackendAdapter, -}; +use crate::models::{ChainHeight, HotStuffTreeNode, Instruction, SidechainMetadata, TreeNodeHash}; pub use chain_storage_service::ChainStorageService; pub use error::StorageError; pub use lmdb::{LmdbAssetBackend, LmdbAssetStore}; @@ -40,34 +37,19 @@ mod store; pub mod sqlite; pub trait DbFactory { - fn create(&self) -> ChainDb; + fn create(&self) -> Result, StorageError>; } -#[derive(Clone)] -pub struct SqliteDbFactory {} - -impl SqliteDbFactory { - pub fn new(config: &GlobalConfig) -> Self { - Self {} - } - - fn create_adapter(&self) -> SqliteBackendAdapter { - SqliteBackendAdapter {} - } +pub struct ChainDb { + adapter: TBackendAdapter, } -impl DbFactory for SqliteDbFactory { - fn create(&self) -> ChainDb { - ChainDb { - adapter: self.create_adapter(), - } +impl ChainDb { + pub fn new(adapter: TBackendAdapter) -> ChainDb { + ChainDb { adapter } } } -pub struct ChainDb { - adapter: TBackendAdapter, -} - impl ChainDb { pub fn new_unit_of_work(&self) -> ChainDbUnitOfWork { ChainDbUnitOfWork { @@ -98,9 +80,10 @@ pub enum NewUnitOfWorkTracker { pub trait BackendAdapter: Send + Sync + Clone { type BackendTransaction; - fn create_transaction(&self) -> Self::BackendTransaction; - fn insert(&self, item: &NewUnitOfWorkTracker, transaction: &Self::BackendTransaction) -> Result<(), StorageError>; - fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), StorageError>; + type Error: Into; + fn create_transaction(&self) -> Result; + fn insert(&self, item: &NewUnitOfWorkTracker, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; + fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; } pub struct ChainDbUnitOfWorkInner { @@ -147,12 +130,21 @@ impl UnitOfWork for ChainDbUnitOfWork Result<(), StorageError> { let mut inner = self.inner.write().unwrap(); - let tx = inner.backend_adapter.create_transaction(); + let tx = inner + .backend_adapter + .create_transaction() + .map_err(TBackendAdapter::Error::into)?; for item in inner.new_items.iter() { - inner.backend_adapter.insert(item, &tx)?; + inner + .backend_adapter + .insert(item, &tx) + .map_err(TBackendAdapter::Error::into)?; } - inner.backend_adapter.commit(&tx)?; + inner + .backend_adapter + .commit(&tx) + .map_err(TBackendAdapter::Error::into)?; Ok(()) } diff --git a/dan_layer/core/src/storage/sqlite/mod.rs b/dan_layer/core/src/storage/sqlite/mod.rs index 068646b5f1..059c515c4c 100644 --- a/dan_layer/core/src/storage/sqlite/mod.rs +++ b/dan_layer/core/src/storage/sqlite/mod.rs @@ -49,24 +49,3 @@ impl ChainStorageService for SqliteStorageService { Ok(()) } } - -#[derive(Clone)] -pub struct SqliteBackendAdapter {} - -pub struct SqliteTransaction {} - -impl BackendAdapter for SqliteBackendAdapter { - type BackendTransaction = SqliteTransaction; - - fn create_transaction(&self) -> Self::BackendTransaction { - SqliteTransaction {} - } - - fn insert(&self, item: &NewUnitOfWorkTracker, transaction: &Self::BackendTransaction) -> Result<(), StorageError> { - todo!() - } - - fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), StorageError> { - todo!() - } -} diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index d7ee3c4154..6cd2ae2581 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -268,7 +268,7 @@ where unimplemented!("Node is not safe") } - let db = self.db_factory.create(); + let db = self.db_factory.create()?; let unit_of_work = db.new_unit_of_work(); let res = payload_processor diff --git a/dan_layer/core/src/workers/states/starting.rs b/dan_layer/core/src/workers/states/starting.rs index a7fa4ce0d7..4d225a2a96 100644 --- a/dan_layer/core/src/workers/states/starting.rs +++ b/dan_layer/core/src/workers/states/starting.rs @@ -98,7 +98,7 @@ where TBaseNodeClient: BaseNodeClient } // read and create the genesis block - let chain_db = db_factory.create(); + let chain_db = db_factory.create()?; if chain_db.is_empty() { let mut tx = chain_db.new_unit_of_work(); diff --git a/dan_layer/validator_node_sqlite/Cargo.toml b/dan_layer/storage_sqlite/Cargo.toml similarity index 55% rename from dan_layer/validator_node_sqlite/Cargo.toml rename to dan_layer/storage_sqlite/Cargo.toml index 17b27cb3b5..6ac865c5d3 100644 --- a/dan_layer/validator_node_sqlite/Cargo.toml +++ b/dan_layer/storage_sqlite/Cargo.toml @@ -1,10 +1,14 @@ [package] -name = "validator_node_sqlite" +name = "tari_dan_storage_sqlite" version = "0.1.0" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +tari_dan_core = {path="../core"} +tari_common = { path = "../../common"} diesel = { version = "1.4.8", features = ["sqlite"] } -dotenv = "0.15.0" \ No newline at end of file +diesel_migrations = "1.4.0" +dotenv = "0.15.0" +thiserror = "1.0.30" \ No newline at end of file diff --git a/dan_layer/validator_node_sqlite/diesel.toml b/dan_layer/storage_sqlite/diesel.toml similarity index 100% rename from dan_layer/validator_node_sqlite/diesel.toml rename to dan_layer/storage_sqlite/diesel.toml diff --git a/dan_layer/validator_node_sqlite/migrations/.gitkeep b/dan_layer/storage_sqlite/migrations/.gitkeep similarity index 100% rename from dan_layer/validator_node_sqlite/migrations/.gitkeep rename to dan_layer/storage_sqlite/migrations/.gitkeep diff --git a/dan_layer/validator_node_sqlite/migrations/2021-11-02-185150_create_nodes/down.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/down.sql similarity index 100% rename from dan_layer/validator_node_sqlite/migrations/2021-11-02-185150_create_nodes/down.sql rename to dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/down.sql diff --git a/dan_layer/validator_node_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql similarity index 100% rename from dan_layer/validator_node_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql rename to dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql diff --git a/dan_layer/storage_sqlite/src/error.rs b/dan_layer/storage_sqlite/src/error.rs new file mode 100644 index 0000000000..574a17bf2d --- /dev/null +++ b/dan_layer/storage_sqlite/src/error.rs @@ -0,0 +1,59 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use diesel; +use tari_dan_core::storage::StorageError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum SqliteStorageError { + #[error("Could not connect to database: {source}")] + ConnectionError { + #[from] + source: diesel::ConnectionError, + }, + #[error("General diesel error: {source}")] + DieselError { + #[from] + source: diesel::result::Error, + }, + #[error("Could not migrate the database")] + MigrationError { + #[from] + source: diesel_migrations::RunMigrationsError, + }, +} + +impl From for StorageError { + fn from(source: SqliteStorageError) -> Self { + match source { + SqliteStorageError::ConnectionError { .. } => StorageError::ConnectionError { + reason: source.to_string(), + }, + SqliteStorageError::DieselError { .. } => StorageError::QueryError { + reason: source.to_string(), + }, + SqliteStorageError::MigrationError { .. } => StorageError::MigrationError { + reason: source.to_string(), + }, + } + } +} diff --git a/dan_layer/validator_node_sqlite/src/lib.rs b/dan_layer/storage_sqlite/src/lib.rs similarity index 84% rename from dan_layer/validator_node_sqlite/src/lib.rs rename to dan_layer/storage_sqlite/src/lib.rs index dfed793316..8415d50e58 100644 --- a/dan_layer/validator_node_sqlite/src/lib.rs +++ b/dan_layer/storage_sqlite/src/lib.rs @@ -22,5 +22,15 @@ #[macro_use] extern crate diesel; +#[macro_use] +extern crate diesel_migrations; +pub mod error; mod schema; +mod sqlite_backend_adapter; +pub use sqlite_backend_adapter::SqliteBackendAdapter; +mod sqlite_transaction; +pub use sqlite_transaction::SqliteTransaction; +mod sqlite_db_factory; +pub use sqlite_db_factory::SqliteDbFactory; +mod models; diff --git a/dan_layer/storage_sqlite/src/models/mod.rs b/dan_layer/storage_sqlite/src/models/mod.rs new file mode 100644 index 0000000000..6d2d0a8573 --- /dev/null +++ b/dan_layer/storage_sqlite/src/models/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod node; diff --git a/dan_layer/storage_sqlite/src/models/node.rs b/dan_layer/storage_sqlite/src/models/node.rs new file mode 100644 index 0000000000..484a7dddac --- /dev/null +++ b/dan_layer/storage_sqlite/src/models/node.rs @@ -0,0 +1,36 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::schema::*; + +#[derive(Queryable)] +pub struct Node { + hash: Vec, + parent: Vec, +} + +#[derive(Insertable)] +#[table_name = "nodes"] +pub struct NewNode { + pub hash: Vec, + pub parent: Vec, +} diff --git a/dan_layer/validator_node_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs similarity index 100% rename from dan_layer/validator_node_sqlite/src/schema.rs rename to dan_layer/storage_sqlite/src/schema.rs diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs new file mode 100644 index 0000000000..ba90e7d4ad --- /dev/null +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -0,0 +1,73 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{error::SqliteStorageError, models::node::NewNode, schema::*, SqliteTransaction}; +use diesel::{prelude::*, Connection, SqliteConnection}; +use diesel_migrations::embed_migrations; +use tari_dan_core::storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError}; + +#[derive(Clone)] +pub struct SqliteBackendAdapter { + database_url: String, +} + +impl SqliteBackendAdapter { + pub fn new(database_url: String) -> SqliteBackendAdapter { + Self { database_url } + } +} + +impl BackendAdapter for SqliteBackendAdapter { + type BackendTransaction = SqliteTransaction; + type Error = SqliteStorageError; + + fn create_transaction(&self) -> Result { + let connection = SqliteConnection::establish(self.database_url.as_str())?; + connection.execute("PRAGMA foreign_keys = ON;"); + connection.execute("BEGIN EXCLUSIVE TRANSACTION;"); + + Ok(SqliteTransaction::new(connection)) + } + + fn insert(&self, item: &NewUnitOfWorkTracker, transaction: &Self::BackendTransaction) -> Result<(), Self::Error> { + match item { + NewUnitOfWorkTracker::Node { hash, parent } => { + let new_node = NewNode { + hash: Vec::from(hash.as_bytes()), + parent: Vec::from(parent.as_bytes()), + }; + diesel::insert_into(nodes::table) + .values(&new_node) + .execute(transaction.connection())?; + }, + NewUnitOfWorkTracker::Instruction { .. } => { + todo!() + }, + } + Ok(()) + } + + fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error> { + transaction.connection().execute("COMMIT TRANSACTION;")?; + Ok(()) + } +} diff --git a/dan_layer/storage_sqlite/src/sqlite_db_factory.rs b/dan_layer/storage_sqlite/src/sqlite_db_factory.rs new file mode 100644 index 0000000000..84785f7206 --- /dev/null +++ b/dan_layer/storage_sqlite/src/sqlite_db_factory.rs @@ -0,0 +1,60 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{error::SqliteStorageError, SqliteBackendAdapter}; +use diesel::{prelude::*, Connection, SqliteConnection}; +use diesel_migrations::embed_migrations; +use tari_common::GlobalConfig; +use tari_dan_core::storage::{ChainDb, DbFactory, StorageError}; + +#[derive(Clone)] +pub struct SqliteDbFactory { + database_url: String, +} + +impl SqliteDbFactory { + pub fn new(config: &GlobalConfig) -> Self { + let database_url = config + .data_dir + .join("dan_storage.sqlite") + .into_os_string() + .into_string() + .unwrap(); + + Self { database_url } + } + + fn create_adapter(&self) -> SqliteBackendAdapter { + SqliteBackendAdapter::new(self.database_url.clone()) + } +} + +impl DbFactory for SqliteDbFactory { + fn create(&self) -> Result, StorageError> { + let connection = SqliteConnection::establish(self.database_url.as_str()).map_err(SqliteStorageError::from)?; + connection.execute("PRAGMA foreign_keys = ON;"); + // Create the db + embed_migrations!("./migrations"); + embedded_migrations::run(&connection).map_err(SqliteStorageError::from)?; + Ok(ChainDb::new(self.create_adapter())) + } +} diff --git a/dan_layer/storage_sqlite/src/sqlite_transaction.rs b/dan_layer/storage_sqlite/src/sqlite_transaction.rs new file mode 100644 index 0000000000..a29f0a1c4a --- /dev/null +++ b/dan_layer/storage_sqlite/src/sqlite_transaction.rs @@ -0,0 +1,37 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use diesel::SqliteConnection; + +pub struct SqliteTransaction { + connection: SqliteConnection, +} + +impl SqliteTransaction { + pub fn new(connection: SqliteConnection) -> Self { + Self { connection } + } + + pub fn connection(&self) -> &SqliteConnection { + &self.connection + } +} From 1564f096a1ee015470dca6ef75ef3469972fe17a Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 3 Nov 2021 19:35:20 +0200 Subject: [PATCH 02/14] add locked qc table --- Cargo.lock | 3 + .../tari_validator_node/src/dan_node.rs | 4 +- dan_layer/core/src/models/mod.rs | 4 + .../core/src/storage/chain_storage_service.rs | 6 ++ dan_layer/core/src/storage/mod.rs | 74 ++++++++++++++++--- dan_layer/core/src/workers/states/starting.rs | 3 +- dan_layer/storage_sqlite/Cargo.toml | 5 +- .../2021-11-02-185150_create_nodes/up.sql | 10 ++- dan_layer/storage_sqlite/src/lib.rs | 2 + .../storage_sqlite/src/models/locked_qc.rs | 30 ++++++++ dan_layer/storage_sqlite/src/models/mod.rs | 1 + dan_layer/storage_sqlite/src/schema.rs | 16 +++- .../src/sqlite_backend_adapter.rs | 70 +++++++++++++++++- .../src/sqlite_storage_service.rs} | 25 ++++++- 14 files changed, 232 insertions(+), 21 deletions(-) create mode 100644 dan_layer/storage_sqlite/src/models/locked_qc.rs rename dan_layer/{core/src/storage/sqlite/mod.rs => storage_sqlite/src/sqlite_storage_service.rs} (80%) diff --git a/Cargo.lock b/Cargo.lock index 6c8c38836f..c1ae6d28b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6399,12 +6399,15 @@ dependencies = [ name = "tari_dan_storage_sqlite" version = "0.1.0" dependencies = [ + "async-trait", "diesel", "diesel_migrations", "dotenv", "tari_common", "tari_dan_core", "thiserror", + "tokio 1.13.0", + "tokio-stream", ] [[package]] diff --git a/applications/tari_validator_node/src/dan_node.rs b/applications/tari_validator_node/src/dan_node.rs index d40b64c931..5857e6c3d2 100644 --- a/applications/tari_validator_node/src/dan_node.rs +++ b/applications/tari_validator_node/src/dan_node.rs @@ -66,10 +66,10 @@ use tari_dan_core::{ TariDanPayloadProcessor, TariDanPayloadProvider, }, - storage::{sqlite::SqliteStorageService, AssetDataStore, BackendAdapter, DbFactory, LmdbAssetStore}, + storage::{AssetDataStore, BackendAdapter, DbFactory, LmdbAssetStore}, workers::ConsensusWorker, }; -use tari_dan_storage_sqlite::SqliteDbFactory; +use tari_dan_storage_sqlite::{SqliteDbFactory, SqliteStorageService}; use tari_p2p::{ comms_connector::{pubsub_connector, SubscriptionFactory}, initialization::{spawn_comms_using_transport, P2pConfig, P2pInitializer}, diff --git a/dan_layer/core/src/models/mod.rs b/dan_layer/core/src/models/mod.rs index bead61a970..f055503abd 100644 --- a/dan_layer/core/src/models/mod.rs +++ b/dan_layer/core/src/models/mod.rs @@ -206,6 +206,10 @@ impl Signature { pub fn combine(&self, other: &Signature) -> Signature { other.clone() } + + pub fn to_bytes(&self) -> Vec { + vec![] + } } #[derive(Copy, Clone, Debug)] diff --git a/dan_layer/core/src/storage/chain_storage_service.rs b/dan_layer/core/src/storage/chain_storage_service.rs index b4467c945b..a5f94f8763 100644 --- a/dan_layer/core/src/storage/chain_storage_service.rs +++ b/dan_layer/core/src/storage/chain_storage_service.rs @@ -38,6 +38,12 @@ pub trait ChainStorageService { node: &HotStuffTreeNode, db: TUnitOfWork, ) -> Result<(), StorageError>; + + async fn set_locked_qc( + &self, + qc: QuorumCertificate, + db: TUnitOfWork, + ) -> Result<(), StorageError>; } // #[derive(Clone)] // pub struct ChainStorageServiceHandle { diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index 053e03dc96..ab914ddd5c 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -20,7 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::models::{ChainHeight, HotStuffTreeNode, Instruction, SidechainMetadata, TreeNodeHash}; +use crate::models::{ + ChainHeight, + HotStuffMessageType, + HotStuffTreeNode, + Instruction, + SidechainMetadata, + Signature, + TreeNodeHash, + ViewId, +}; pub use chain_storage_service::ChainStorageService; pub use error::StorageError; pub use lmdb::{LmdbAssetBackend, LmdbAssetStore}; @@ -34,7 +43,6 @@ pub mod lmdb; mod store; // feature sql -pub mod sqlite; pub trait DbFactory { fn create(&self) -> Result, StorageError>; @@ -50,21 +58,27 @@ impl ChainDb { } } -impl ChainDb { - pub fn new_unit_of_work(&self) -> ChainDbUnitOfWork { +impl ChainDb { + pub fn new_unit_of_work(&self) -> ChainDbUnitOfWork { ChainDbUnitOfWork { inner: Arc::new(RwLock::new(ChainDbUnitOfWorkInner::new(self.adapter.clone()))), } } } -impl ChainDb { - pub fn is_empty(&self) -> bool { - return true; +impl ChainDb { + pub fn is_empty(&self) -> Result { + self.adapter.is_empty().map_err(TBackendAdapter::Error::into) } } pub enum UnitOfWorkTracker { SidechainMetadata, + LockedQc { + message_type: HotStuffMessageType, + view_number: ViewId, + node_hash: TreeNodeHash, + signature: Option, + }, } pub enum NewUnitOfWorkTracker { @@ -81,15 +95,25 @@ pub enum NewUnitOfWorkTracker { pub trait BackendAdapter: Send + Sync + Clone { type BackendTransaction; type Error: Into; + type Id: Send + Sync; + + fn is_empty(&self) -> Result; fn create_transaction(&self) -> Result; fn insert(&self, item: &NewUnitOfWorkTracker, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; + fn update( + &self, + id: &Self::Id, + item: &UnitOfWorkTracker, + transaction: &Self::BackendTransaction, + ) -> Result<(), Self::Error>; fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; + fn locked_qc_id(&self) -> Self::Id; } pub struct ChainDbUnitOfWorkInner { backend_adapter: TBackendAdapter, - clean_items: Vec, - dirty_items: Vec, + clean_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, + dirty_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, new_items: Vec, } @@ -108,6 +132,13 @@ pub trait UnitOfWork: Clone + Send + Sync { fn commit(&mut self) -> Result<(), StorageError>; fn add_node(&mut self, hash: TreeNodeHash, parent: TreeNodeHash) -> Result<(), StorageError>; fn add_instruction(&mut self, node_hash: TreeNodeHash, instruction: Instruction) -> Result<(), StorageError>; + fn set_locked_qc( + &mut self, + message_type: HotStuffMessageType, + view_number: ViewId, + node_hash: TreeNodeHash, + signature: Option, + ) -> Result<(), StorageError>; } // Cloneable, Send, Sync wrapper @@ -141,6 +172,13 @@ impl UnitOfWork for ChainDbUnitOfWork UnitOfWork for ChainDbUnitOfWork, + ) -> Result<(), StorageError> { + let mut inner = self.inner.write().unwrap(); + let id = inner.backend_adapter.locked_qc_id(); + inner.dirty_items.push((id, UnitOfWorkTracker::LockedQc { + message_type, + view_number, + node_hash, + signature, + })); + Ok(()) + } } pub struct StateDb { diff --git a/dan_layer/core/src/workers/states/starting.rs b/dan_layer/core/src/workers/states/starting.rs index 4d225a2a96..d5190cffaa 100644 --- a/dan_layer/core/src/workers/states/starting.rs +++ b/dan_layer/core/src/workers/states/starting.rs @@ -99,7 +99,7 @@ where TBaseNodeClient: BaseNodeClient // read and create the genesis block let chain_db = db_factory.create()?; - if chain_db.is_empty() { + if chain_db.is_empty()? { let mut tx = chain_db.new_unit_of_work(); let tx2 = tx.clone(); @@ -109,6 +109,7 @@ where TBaseNodeClient: BaseNodeClient payload_processor.process_payload(&payload, tx2).await?; let genesis_qc = QuorumCertificate::genesis(payload); chain_storage_service.save_node(genesis_qc.node(), tx.clone()).await?; + chain_storage_service.set_locked_qc(genesis_qc, tx.clone()).await?; tx.commit()?; } diff --git a/dan_layer/storage_sqlite/Cargo.toml b/dan_layer/storage_sqlite/Cargo.toml index 6ac865c5d3..175e1fce1e 100644 --- a/dan_layer/storage_sqlite/Cargo.toml +++ b/dan_layer/storage_sqlite/Cargo.toml @@ -11,4 +11,7 @@ tari_common = { path = "../../common"} diesel = { version = "1.4.8", features = ["sqlite"] } diesel_migrations = "1.4.0" dotenv = "0.15.0" -thiserror = "1.0.30" \ No newline at end of file +thiserror = "1.0.30" +async-trait = "0.1.50" +tokio = { version="1.10", features = ["macros", "time"]} +tokio-stream = { version = "0.1.7", features = ["sync"] } \ No newline at end of file diff --git a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql index 87d2ce970a..2feeb3fba7 100644 --- a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql +++ b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql @@ -12,4 +12,12 @@ create table instructions ( template_id int not null, method text not null, args blob not null -); \ No newline at end of file +); + +create table locked_qc ( + id integer primary key not null, -- should always be 1 row + message_type integer not null, + view_number bigint not null, + node_hash blob not null, + signature blob null +) \ No newline at end of file diff --git a/dan_layer/storage_sqlite/src/lib.rs b/dan_layer/storage_sqlite/src/lib.rs index 8415d50e58..8decae6264 100644 --- a/dan_layer/storage_sqlite/src/lib.rs +++ b/dan_layer/storage_sqlite/src/lib.rs @@ -34,3 +34,5 @@ pub use sqlite_transaction::SqliteTransaction; mod sqlite_db_factory; pub use sqlite_db_factory::SqliteDbFactory; mod models; +mod sqlite_storage_service; +pub use sqlite_storage_service::SqliteStorageService; diff --git a/dan_layer/storage_sqlite/src/models/locked_qc.rs b/dan_layer/storage_sqlite/src/models/locked_qc.rs new file mode 100644 index 0000000000..460b9fe323 --- /dev/null +++ b/dan_layer/storage_sqlite/src/models/locked_qc.rs @@ -0,0 +1,30 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[derive(Queryable)] +pub struct LockedQc { + pub id: i32, + pub message_type: i32, + pub view_number: i64, + pub node_hash: Vec, + pub signature: Option>, +} diff --git a/dan_layer/storage_sqlite/src/models/mod.rs b/dan_layer/storage_sqlite/src/models/mod.rs index 6d2d0a8573..0a6bd53f27 100644 --- a/dan_layer/storage_sqlite/src/models/mod.rs +++ b/dan_layer/storage_sqlite/src/models/mod.rs @@ -20,4 +20,5 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod locked_qc; pub mod node; diff --git a/dan_layer/storage_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs index 7a413a2344..6b75e4fb34 100644 --- a/dan_layer/storage_sqlite/src/schema.rs +++ b/dan_layer/storage_sqlite/src/schema.rs @@ -10,6 +10,16 @@ table! { } } +table! { + locked_qc (id) { + id -> Integer, + message_type -> Integer, + view_number -> BigInt, + node_hash -> Binary, + signature -> Nullable, + } +} + table! { nodes (hash) { hash -> Binary, @@ -17,4 +27,8 @@ table! { } } -allow_tables_to_appear_in_same_query!(instructions, nodes,); +allow_tables_to_appear_in_same_query!( + instructions, + locked_qc, + nodes, +); diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs index ba90e7d4ad..9414aae627 100644 --- a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -20,10 +20,18 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{error::SqliteStorageError, models::node::NewNode, schema::*, SqliteTransaction}; +use crate::{ + error::SqliteStorageError, + models::{ + locked_qc::LockedQc, + node::{NewNode, Node}, + }, + schema::{locked_qc::dsl, *}, + SqliteTransaction, +}; use diesel::{prelude::*, Connection, SqliteConnection}; use diesel_migrations::embed_migrations; -use tari_dan_core::storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError}; +use tari_dan_core::storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError, UnitOfWorkTracker}; #[derive(Clone)] pub struct SqliteBackendAdapter { @@ -39,6 +47,13 @@ impl SqliteBackendAdapter { impl BackendAdapter for SqliteBackendAdapter { type BackendTransaction = SqliteTransaction; type Error = SqliteStorageError; + type Id = i32; + + fn is_empty(&self) -> Result { + let connection = SqliteConnection::establish(self.database_url.as_str())?; + let n: Option = nodes::table.first(&connection).optional()?; + Ok(n.is_none()) + } fn create_transaction(&self) -> Result { let connection = SqliteConnection::establish(self.database_url.as_str())?; @@ -66,8 +81,59 @@ impl BackendAdapter for SqliteBackendAdapter { Ok(()) } + fn update( + &self, + id: &Self::Id, + item: &UnitOfWorkTracker, + transaction: &Self::BackendTransaction, + ) -> Result<(), Self::Error> { + match item { + UnitOfWorkTracker::SidechainMetadata => { + todo!() + }, + UnitOfWorkTracker::LockedQc { + message_type, + view_number, + node_hash, + signature, + } => { + use crate::schema::locked_qc::dsl; + let message_type = message_type.as_u8() as i32; + let existing: Result = dsl::locked_qc.find(id).first(transaction.connection()); + match existing { + Ok(x) => { + diesel::update(dsl::locked_qc.find(id)) + .set(( + dsl::message_type.eq(message_type), + dsl::view_number.eq(view_number.0 as i64), + dsl::node_hash.eq(node_hash.as_bytes()), + dsl::signature.eq(signature.as_ref().map(|s| s.to_bytes())), + )) + .execute(transaction.connection())?; + }, + Err(_) => { + diesel::insert_into(locked_qc::table) + .values(( + dsl::id.eq(id), + dsl::message_type.eq(message_type), + dsl::view_number.eq(view_number.0 as i64), + dsl::node_hash.eq(node_hash.as_bytes()), + dsl::signature.eq(signature.as_ref().map(|s| s.to_bytes())), + )) + .execute(transaction.connection())?; + }, + } + }, + } + Ok(()) + } + fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error> { transaction.connection().execute("COMMIT TRANSACTION;")?; Ok(()) } + + fn locked_qc_id(&self) -> Self::Id { + 1 + } } diff --git a/dan_layer/core/src/storage/sqlite/mod.rs b/dan_layer/storage_sqlite/src/sqlite_storage_service.rs similarity index 80% rename from dan_layer/core/src/storage/sqlite/mod.rs rename to dan_layer/storage_sqlite/src/sqlite_storage_service.rs index 059c515c4c..6f0cb50036 100644 --- a/dan_layer/core/src/storage/sqlite/mod.rs +++ b/dan_layer/storage_sqlite/src/sqlite_storage_service.rs @@ -20,16 +20,18 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - models::{HotStuffTreeNode, QuorumCertificate, SidechainMetadata, TariDanPayload}, - storage::{BackendAdapter, ChainDbUnitOfWork, ChainStorageService, NewUnitOfWorkTracker, StorageError, UnitOfWork}, -}; use async_trait::async_trait; use std::sync::Arc; +use tari_dan_core::{ + models::{HotStuffTreeNode, QuorumCertificate, SidechainMetadata, TariDanPayload}, + storage::{ChainStorageService, StorageError, UnitOfWork}, +}; use tokio::sync::RwLock; pub struct SqliteStorageService {} +// TODO: this has no references to Sqlite, so may be worth moving to dan_layer.core + #[async_trait] impl ChainStorageService for SqliteStorageService { async fn get_metadata(&self) -> Result { @@ -48,4 +50,19 @@ impl ChainStorageService for SqliteStorageService { db.add_node(node.hash().clone(), node.parent().clone())?; Ok(()) } + + async fn set_locked_qc( + &self, + qc: QuorumCertificate, + db: TUnitOfWork, + ) -> Result<(), StorageError> { + let mut db = db; + db.set_locked_qc( + qc.message_type(), + qc.view_number(), + qc.node().hash().clone(), + qc.signature().map(|s| s.clone()), + )?; + Ok(()) + } } From d4f606d3a5d281afd767e22b9ee39e9bc2ee36ed Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 3 Nov 2021 21:25:57 +0200 Subject: [PATCH 03/14] pre-remove payload from qc --- base_layer/mmr/src/merkle_proof.rs | 12 +- dan_layer/core/src/models/mod.rs | 1 - .../core/src/models/quorum_certificate.rs | 14 +- dan_layer/core/src/models/view_id.rs | 20 ++- .../node_addressable.rs | 7 +- dan_layer/core/src/storage/chain_db.rs | 156 ++++++++++++++++++ dan_layer/core/src/storage/mod.rs | 130 +-------------- .../core/src/workers/consensus_worker.rs | 4 +- .../core/src/workers/states/next_view.rs | 7 +- dan_layer/core/src/workers/states/prepare.rs | 20 +++ .../2021-11-02-185150_create_nodes/up.sql | 10 +- dan_layer/storage_sqlite/src/models/mod.rs | 1 + .../storage_sqlite/src/models/prepare_qc.rs | 30 ++++ dan_layer/storage_sqlite/src/schema.rs | 11 ++ .../src/sqlite_backend_adapter.rs | 36 +++- 15 files changed, 308 insertions(+), 151 deletions(-) create mode 100644 dan_layer/core/src/storage/chain_db.rs create mode 100644 dan_layer/storage_sqlite/src/models/prepare_qc.rs diff --git a/base_layer/mmr/src/merkle_proof.rs b/base_layer/mmr/src/merkle_proof.rs index a5a458e4cc..60ab82bb37 100644 --- a/base_layer/mmr/src/merkle_proof.rs +++ b/base_layer/mmr/src/merkle_proof.rs @@ -54,7 +54,7 @@ pub enum MerkleProofError { } /// A Merkle proof that proves a particular element at a particular position exists in an MMR. -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, PartialOrd, Ord)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, PartialOrd, Ord, Default)] pub struct MerkleProof { /// The size of the MMR at the time the proof was created. mmr_size: usize, @@ -66,16 +66,6 @@ pub struct MerkleProof { peaks: Vec, } -impl Default for MerkleProof { - fn default() -> MerkleProof { - MerkleProof { - mmr_size: 0, - path: Vec::default(), - peaks: Vec::default(), - } - } -} - impl MerkleProof { /// Build a Merkle Proof the given MMR at the given *leaf* position. This is usually the version you'll want to /// call, since you'll know the leaf index more often than the MMR index. diff --git a/dan_layer/core/src/models/mod.rs b/dan_layer/core/src/models/mod.rs index f055503abd..2907783fc7 100644 --- a/dan_layer/core/src/models/mod.rs +++ b/dan_layer/core/src/models/mod.rs @@ -50,7 +50,6 @@ pub use asset_definition::AssetDefinition; pub use base_layer_metadata::BaseLayerMetadata; pub use base_layer_output::BaseLayerOutput; use blake2::Digest; -use digest::Update; pub use sidechain_metadata::SidechainMetadata; use tari_crypto::common::Blake256; pub use tari_dan_payload::{CheckpointData, TariDanPayload}; diff --git a/dan_layer/core/src/models/quorum_certificate.rs b/dan_layer/core/src/models/quorum_certificate.rs index a87d950379..443a7493eb 100644 --- a/dan_layer/core/src/models/quorum_certificate.rs +++ b/dan_layer/core/src/models/quorum_certificate.rs @@ -20,21 +20,21 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::models::{HotStuffMessageType, HotStuffTreeNode, Payload, Signature, ViewId}; +use crate::models::{HotStuffMessageType, HotStuffTreeNode, Payload, Signature, TreeNodeHash, ViewId}; #[derive(Debug, Clone)] -pub struct QuorumCertificate { +pub struct QuorumCertificate { message_type: HotStuffMessageType, - node: HotStuffTreeNode, + node: TreeNodeHash, view_number: ViewId, signature: Option, } -impl QuorumCertificate { +impl QuorumCertificate { pub fn new( message_type: HotStuffMessageType, view_number: ViewId, - node: HotStuffTreeNode, + node: TreeNodeHash, signature: Option, ) -> Self { Self { @@ -48,13 +48,13 @@ impl QuorumCertificate { pub fn genesis(payload: TPayload) -> Self { Self { message_type: HotStuffMessageType::Genesis, - node: HotStuffTreeNode::genesis(payload), + node: HotStuffTreeNode::genesis(payload).hash().clone(), view_number: 0.into(), signature: None, } } - pub fn node(&self) -> &HotStuffTreeNode { + pub fn node(&self) -> &TreeNodeHash { &self.node } diff --git a/dan_layer/core/src/models/view_id.rs b/dan_layer/core/src/models/view_id.rs index 7ed8297ef7..bc60c079ed 100644 --- a/dan_layer/core/src/models/view_id.rs +++ b/dan_layer/core/src/models/view_id.rs @@ -20,7 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cmp::Ordering; +use std::{ + cmp::Ordering, + fmt::{self, Display}, + ops::Sub, +}; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct ViewId(pub u64); @@ -50,3 +54,17 @@ impl From for ViewId { Self(v) } } + +impl Display for ViewId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "View({})", self.0) + } +} + +impl Sub for ViewId { + type Output = ViewId; + + fn sub(self, rhs: Self) -> Self::Output { + ViewId(self.0 - rhs.0) + } +} diff --git a/dan_layer/core/src/services/infrastructure_services/node_addressable.rs b/dan_layer/core/src/services/infrastructure_services/node_addressable.rs index 476609256c..546167608b 100644 --- a/dan_layer/core/src/services/infrastructure_services/node_addressable.rs +++ b/dan_layer/core/src/services/infrastructure_services/node_addressable.rs @@ -20,10 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{fmt::Debug, hash::Hash}; +use std::{ + fmt::{Debug, Display}, + hash::Hash, +}; use tari_comms::types::CommsPublicKey; -pub trait NodeAddressable: Eq + Hash + Clone + Debug + Send + Sync {} +pub trait NodeAddressable: Eq + Hash + Clone + Debug + Send + Sync + Display {} impl NodeAddressable for String {} diff --git a/dan_layer/core/src/storage/chain_db.rs b/dan_layer/core/src/storage/chain_db.rs new file mode 100644 index 0000000000..4aa87739c5 --- /dev/null +++ b/dan_layer/core/src/storage/chain_db.rs @@ -0,0 +1,156 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + models::{HotStuffMessageType, Instruction, Payload, QuorumCertificate, Signature, TreeNodeHash, ViewId}, + storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError, UnitOfWork, UnitOfWorkTracker}, +}; +use std::sync::{Arc, RwLock}; + +pub struct ChainDb { + adapter: TBackendAdapter, +} + +impl ChainDb { + pub fn new(adapter: TBackendAdapter) -> ChainDb { + ChainDb { adapter } + } + + pub fn find_highest_prepared_qc(&self) -> Result, StorageError> { + self.adapter + .find_highest_prepared_qc() + .map_err(TBackendAdapter::Error::into) + } +} + +impl ChainDb { + pub fn new_unit_of_work(&self) -> ChainDbUnitOfWork { + ChainDbUnitOfWork { + inner: Arc::new(RwLock::new(ChainDbUnitOfWorkInner::new(self.adapter.clone()))), + } + } +} +impl ChainDb { + pub fn is_empty(&self) -> Result { + self.adapter.is_empty().map_err(TBackendAdapter::Error::into) + } +} + +// Cloneable, Send, Sync wrapper +pub struct ChainDbUnitOfWork { + inner: Arc>>, +} + +pub struct ChainDbUnitOfWorkInner { + backend_adapter: TBackendAdapter, + clean_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, + dirty_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, + new_items: Vec, +} + +impl ChainDbUnitOfWorkInner { + pub fn new(backend_adapter: TBackendAdapter) -> Self { + Self { + backend_adapter, + clean_items: vec![], + dirty_items: vec![], + new_items: vec![], + } + } +} + +impl Clone for ChainDbUnitOfWork { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl UnitOfWork for ChainDbUnitOfWork { + // pub fn register_clean(&mut self, item: UnitOfWorkTracker) { + // self.clean.push(item); + // } + + fn commit(&mut self) -> Result<(), StorageError> { + let mut inner = self.inner.write().unwrap(); + let tx = inner + .backend_adapter + .create_transaction() + .map_err(TBackendAdapter::Error::into)?; + for item in inner.new_items.iter() { + inner + .backend_adapter + .insert(item, &tx) + .map_err(TBackendAdapter::Error::into)?; + } + + for (id, item) in inner.dirty_items.iter() { + inner + .backend_adapter + .update(id, item, &tx) + .map_err(TBackendAdapter::Error::into)?; + } + + inner + .backend_adapter + .commit(&tx) + .map_err(TBackendAdapter::Error::into)?; + Ok(()) + } + + fn add_node(&mut self, hash: TreeNodeHash, parent: TreeNodeHash) -> Result<(), StorageError> { + self.inner + .write() + .unwrap() + .new_items + .push(NewUnitOfWorkTracker::Node { hash, parent }); + Ok(()) + } + + fn add_instruction(&mut self, node_hash: TreeNodeHash, instruction: Instruction) -> Result<(), StorageError> { + self.inner + .write() + .unwrap() + .new_items + .push(NewUnitOfWorkTracker::Instruction { node_hash, instruction }); + Ok(()) + } + + fn set_locked_qc( + &mut self, + message_type: HotStuffMessageType, + view_number: ViewId, + node_hash: TreeNodeHash, + signature: Option, + ) -> Result<(), StorageError> { + let mut inner = self.inner.write().unwrap(); + let id = inner.backend_adapter.locked_qc_id(); + inner.dirty_items.push((id, UnitOfWorkTracker::LockedQc { + message_type, + view_number, + node_hash, + signature, + })); + Ok(()) + } +} diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index ab914ddd5c..64774dbd97 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -25,6 +25,8 @@ use crate::models::{ HotStuffMessageType, HotStuffTreeNode, Instruction, + Payload, + QuorumCertificate, SidechainMetadata, Signature, TreeNodeHash, @@ -35,8 +37,9 @@ pub use error::StorageError; pub use lmdb::{LmdbAssetBackend, LmdbAssetStore}; use std::sync::{Arc, RwLock}; pub use store::{AssetDataStore, AssetStore}; -use tari_common::GlobalConfig; +mod chain_db; +pub use chain_db::{ChainDb, ChainDbUnitOfWork}; mod chain_storage_service; mod error; pub mod lmdb; @@ -48,29 +51,6 @@ pub trait DbFactory { fn create(&self) -> Result, StorageError>; } -pub struct ChainDb { - adapter: TBackendAdapter, -} - -impl ChainDb { - pub fn new(adapter: TBackendAdapter) -> ChainDb { - ChainDb { adapter } - } -} - -impl ChainDb { - pub fn new_unit_of_work(&self) -> ChainDbUnitOfWork { - ChainDbUnitOfWork { - inner: Arc::new(RwLock::new(ChainDbUnitOfWorkInner::new(self.adapter.clone()))), - } - } -} -impl ChainDb { - pub fn is_empty(&self) -> Result { - self.adapter.is_empty().map_err(TBackendAdapter::Error::into) - } -} - pub enum UnitOfWorkTracker { SidechainMetadata, LockedQc { @@ -96,6 +76,7 @@ pub trait BackendAdapter: Send + Sync + Clone { type BackendTransaction; type Error: Into; type Id: Send + Sync; + type Payload: Payload; fn is_empty(&self) -> Result; fn create_transaction(&self) -> Result; @@ -108,24 +89,7 @@ pub trait BackendAdapter: Send + Sync + Clone { ) -> Result<(), Self::Error>; fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; fn locked_qc_id(&self) -> Self::Id; -} - -pub struct ChainDbUnitOfWorkInner { - backend_adapter: TBackendAdapter, - clean_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, - dirty_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, - new_items: Vec, -} - -impl ChainDbUnitOfWorkInner { - pub fn new(backend_adapter: TBackendAdapter) -> Self { - Self { - backend_adapter, - clean_items: vec![], - dirty_items: vec![], - new_items: vec![], - } - } + fn find_highest_prepared_qc(&self) -> Result, Self::Error>; } pub trait UnitOfWork: Clone + Send + Sync { @@ -141,88 +105,6 @@ pub trait UnitOfWork: Clone + Send + Sync { ) -> Result<(), StorageError>; } -// Cloneable, Send, Sync wrapper -pub struct ChainDbUnitOfWork { - inner: Arc>>, -} - -impl Clone for ChainDbUnitOfWork { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } - } -} - -impl UnitOfWork for ChainDbUnitOfWork { - // pub fn register_clean(&mut self, item: UnitOfWorkTracker) { - // self.clean.push(item); - // } - - fn commit(&mut self) -> Result<(), StorageError> { - let mut inner = self.inner.write().unwrap(); - let tx = inner - .backend_adapter - .create_transaction() - .map_err(TBackendAdapter::Error::into)?; - for item in inner.new_items.iter() { - inner - .backend_adapter - .insert(item, &tx) - .map_err(TBackendAdapter::Error::into)?; - } - - for (id, item) in inner.dirty_items.iter() { - inner - .backend_adapter - .update(id, item, &tx) - .map_err(TBackendAdapter::Error::into)?; - } - - inner - .backend_adapter - .commit(&tx) - .map_err(TBackendAdapter::Error::into)?; - Ok(()) - } - - fn add_node(&mut self, hash: TreeNodeHash, parent: TreeNodeHash) -> Result<(), StorageError> { - self.inner - .write() - .unwrap() - .new_items - .push(NewUnitOfWorkTracker::Node { hash, parent }); - Ok(()) - } - - fn add_instruction(&mut self, node_hash: TreeNodeHash, instruction: Instruction) -> Result<(), StorageError> { - self.inner - .write() - .unwrap() - .new_items - .push(NewUnitOfWorkTracker::Instruction { node_hash, instruction }); - Ok(()) - } - - fn set_locked_qc( - &mut self, - message_type: HotStuffMessageType, - view_number: ViewId, - node_hash: TreeNodeHash, - signature: Option, - ) -> Result<(), StorageError> { - let mut inner = self.inner.write().unwrap(); - let id = inner.backend_adapter.locked_qc_id(); - inner.dirty_items.push((id, UnitOfWorkTracker::LockedQc { - message_type, - view_number, - node_hash, - signature, - })); - Ok(()) - } -} - pub struct StateDb { unit_of_work: Option, } diff --git a/dan_layer/core/src/workers/consensus_worker.rs b/dan_layer/core/src/workers/consensus_worker.rs index 3ae93f4110..83fb516b50 100644 --- a/dan_layer/core/src/workers/consensus_worker.rs +++ b/dan_layer/core/src/workers/consensus_worker.rs @@ -328,7 +328,7 @@ where state .next_event( &self.get_current_view()?, - self.prepare_qc.as_ref().clone(), + &self.db_factory, &mut self.outbound_service, self.committee_manager.current_committee()?, self.node_id.clone(), @@ -352,7 +352,7 @@ where use ConsensusWorkerStateEvent::*; let from = self.state; self.state = match (&self.state, event) { - (Starting, Initialized) => Prepare, + (Starting, Initialized) => NextView, (_, NotPartOfCommittee) => Idle, (Idle, TimedOut) => Starting, (_, TimedOut) => NextView, diff --git a/dan_layer/core/src/workers/states/next_view.rs b/dan_layer/core/src/workers/states/next_view.rs index 9ee5cc17aa..ceeac21d7a 100644 --- a/dan_layer/core/src/workers/states/next_view.rs +++ b/dan_layer/core/src/workers/states/next_view.rs @@ -24,6 +24,7 @@ use crate::{ digital_assets_error::DigitalAssetError, models::{Committee, HotStuffMessage, Payload, QuorumCertificate, View}, services::infrastructure_services::{NodeAddressable, OutboundService}, + storage::{BackendAdapter, DbFactory}, workers::states::ConsensusWorkerStateEvent, }; use log::*; @@ -42,15 +43,19 @@ impl NextViewState { TPayload: Payload, TOutboundService: OutboundService, TAddr: NodeAddressable + Clone + Send, + TBackendAdapter: BackendAdapter, + TDbFactory: DbFactory, >( &mut self, current_view: &View, - prepare_qc: QuorumCertificate, + db_factory: &TDbFactory, broadcast: &mut TOutboundService, committee: &Committee, node_id: TAddr, _shutdown: &ShutdownSignal, ) -> Result { + let db = db_factory.create()?; + let prepare_qc = db.find_highest_prepared_qc()?; let message = HotStuffMessage::new_view(prepare_qc, current_view.view_id); let next_view = current_view.view_id.next(); let leader = committee.leader_for_view(next_view); diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index 6cd2ae2581..3c8c27d3b3 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -202,6 +202,23 @@ where outbound: &mut TOutboundService, ) -> Result, DigitalAssetError> { if message.message_type() != &HotStuffMessageType::NewView { + warn!( + target: LOG_TARGET, + "{} sent wrong message of type {:?}. Expecting NEW_VIEW", + sender, + message.message_type() + ); + return Ok(None); + } + + if message.view_number() != current_view.view_id - 1.into() { + warn!( + target: LOG_TARGET, + "{} sent wrong view number for NEW_VIEW message. Expecting {}, got {}", + sender, + current_view.view_id - 1.into(), + message.view_number() + ); return Ok(None); } @@ -220,6 +237,7 @@ where committee.len() ); let high_qc = self.find_highest_qc(); + dbg!(&high_qc); let proposal = self.create_proposal(high_qc.node(), payload_provider).await?; self.broadcast_proposal(outbound, committee, proposal, high_qc, current_view.view_id) .await?; @@ -311,6 +329,8 @@ where parent: &HotStuffTreeNode, payload_provider: &TPayloadProvider, ) -> Result, DigitalAssetError> { + info!(target: LOG_TARGET, "Creating new proposal"); + // TODO: Artificial delay here to set the block time sleep(Duration::from_secs(3)).await; diff --git a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql index 2feeb3fba7..dcb2f448ec 100644 --- a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql +++ b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql @@ -20,4 +20,12 @@ create table locked_qc ( view_number bigint not null, node_hash blob not null, signature blob null -) \ No newline at end of file +); + +create table prepare_qcs ( + id integer primary key autoincrement not null, + message_type integer not null, + view_number bigint not null, + node_hash blob not null, + signature blob null +) diff --git a/dan_layer/storage_sqlite/src/models/mod.rs b/dan_layer/storage_sqlite/src/models/mod.rs index 0a6bd53f27..05f07df566 100644 --- a/dan_layer/storage_sqlite/src/models/mod.rs +++ b/dan_layer/storage_sqlite/src/models/mod.rs @@ -22,3 +22,4 @@ pub mod locked_qc; pub mod node; +pub mod prepare_qc; diff --git a/dan_layer/storage_sqlite/src/models/prepare_qc.rs b/dan_layer/storage_sqlite/src/models/prepare_qc.rs new file mode 100644 index 0000000000..99a39f4b2b --- /dev/null +++ b/dan_layer/storage_sqlite/src/models/prepare_qc.rs @@ -0,0 +1,30 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[derive(Queryable)] +pub struct PrepareQc { + pub id: i32, + pub message_type: i32, + pub view_number: i64, + pub node_hash: Vec, + pub signature: Option>, +} diff --git a/dan_layer/storage_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs index 6b75e4fb34..0b784cb6bc 100644 --- a/dan_layer/storage_sqlite/src/schema.rs +++ b/dan_layer/storage_sqlite/src/schema.rs @@ -27,8 +27,19 @@ table! { } } +table! { + prepare_qcs (id) { + id -> Integer, + message_type -> Integer, + view_number -> BigInt, + node_hash -> Binary, + signature -> Nullable, + } +} + allow_tables_to_appear_in_same_query!( instructions, locked_qc, nodes, + prepare_qcs, ); diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs index 9414aae627..34d238c2ae 100644 --- a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -25,13 +25,17 @@ use crate::{ models::{ locked_qc::LockedQc, node::{NewNode, Node}, + prepare_qc::PrepareQc, }, schema::{locked_qc::dsl, *}, SqliteTransaction, }; use diesel::{prelude::*, Connection, SqliteConnection}; use diesel_migrations::embed_migrations; -use tari_dan_core::storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError, UnitOfWorkTracker}; +use tari_dan_core::{ + models::{Payload, QuorumCertificate, TariDanPayload}, + storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError, UnitOfWorkTracker}, +}; #[derive(Clone)] pub struct SqliteBackendAdapter { @@ -48,6 +52,7 @@ impl BackendAdapter for SqliteBackendAdapter { type BackendTransaction = SqliteTransaction; type Error = SqliteStorageError; type Id = i32; + type Payload = TariDanPayload; fn is_empty(&self) -> Result { let connection = SqliteConnection::establish(self.database_url.as_str())?; @@ -136,4 +141,33 @@ impl BackendAdapter for SqliteBackendAdapter { fn locked_qc_id(&self) -> Self::Id { 1 } + + fn find_highest_prepared_qc(&self) -> Result, Self::Error> { + use crate::schema::*; + let connection = SqliteConnection::establish(self.database_url.as_str())?; + let result: Option = prepare_qcs::table + .order_by(prepare_qcs::view_number.desc()) + .first(connection) + .optional()?; + let qc = match result { + Some(r) => r, + None => { + let l: LockedQc = dsl::locked_qc + .find(self.locked_qc_id()) + .first(transaction.connection())?; + PrepareQc { + id: 1, + message_type: l.message_type, + view_number: l.view_number, + node_hash: l.node_hash.clone(), + signature: l.signature.clone(), + } + }, + }; + + Ok(QuorumCertificate::new( + MessageType::from(qc.message_type as u8), + ViewId::from(qc.view_number as u64), + )) + } } From 1a2c258bb3b3b45b74a5fa78508aebc10cdb6734 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 3 Nov 2021 22:23:17 +0200 Subject: [PATCH 04/14] remove node from qc --- .../proto/p2p/dan_consensus.proto | 2 +- .../tari_validator_node/src/dan_node.rs | 4 +- .../src/p2p/proto/conversions/dan.rs | 14 +++--- .../core/src/models/hot_stuff_message.rs | 16 +++---- .../core/src/models/hot_stuff_tree_node.rs | 4 +- dan_layer/core/src/models/mod.rs | 4 ++ .../core/src/models/quorum_certificate.rs | 14 +++--- dan_layer/core/src/storage/chain_db.rs | 2 +- .../core/src/storage/chain_storage_service.rs | 2 +- dan_layer/core/src/storage/mod.rs | 2 +- .../core/src/workers/consensus_worker.rs | 18 ++++---- .../core/src/workers/states/commit_state.rs | 43 ++++++++++--------- .../core/src/workers/states/decide_state.rs | 21 ++++----- .../src/workers/states/pre_commit_state.rs | 40 +++++++++-------- dan_layer/core/src/workers/states/prepare.rs | 35 ++++++++------- dan_layer/core/src/workers/states/starting.rs | 7 +-- .../src/sqlite_backend_adapter.rs | 15 ++++--- .../src/sqlite_storage_service.rs | 4 +- 18 files changed, 127 insertions(+), 120 deletions(-) diff --git a/applications/tari_validator_node/proto/p2p/dan_consensus.proto b/applications/tari_validator_node/proto/p2p/dan_consensus.proto index 3657ec34b6..82239ae2d1 100644 --- a/applications/tari_validator_node/proto/p2p/dan_consensus.proto +++ b/applications/tari_validator_node/proto/p2p/dan_consensus.proto @@ -22,7 +22,7 @@ message HotStuffMessage { message QuorumCertificate { HotStuffMessageType message_type = 1; - HotStuffTreeNode node = 2; + bytes node_hash = 2; uint64 view_number = 3; Signature signature = 4; } diff --git a/applications/tari_validator_node/src/dan_node.rs b/applications/tari_validator_node/src/dan_node.rs index 5857e6c3d2..ce04adddd6 100644 --- a/applications/tari_validator_node/src/dan_node.rs +++ b/applications/tari_validator_node/src/dan_node.rs @@ -54,7 +54,7 @@ use tari_comms::{ use tari_comms_dht::{store_forward::SafConfig, DbConnectionUrl, Dht, DhtConfig}; use tari_crypto::tari_utilities::hex::Hex; use tari_dan_core::{ - models::{AssetDefinition, Committee}, + models::{AssetDefinition, Committee, TariDanPayload}, services::{ ConcreteAssetProcessor, ConcreteCommitteeManager, @@ -165,7 +165,7 @@ impl DanNode { async fn start_asset_worker< TMempoolService: MempoolService + Clone, - TBackendAdapter: BackendAdapter + Send + Sync, + TBackendAdapter: BackendAdapter + Send + Sync, TDbFactory: DbFactory + Clone + Send + Sync, >( &self, diff --git a/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs b/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs index d0930de423..bc897f7bc6 100644 --- a/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs +++ b/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs @@ -62,11 +62,11 @@ impl From> for dan_proto::HotStuffTreeNode { } } -impl From> for dan_proto::QuorumCertificate { - fn from(source: QuorumCertificate) -> Self { +impl From for dan_proto::QuorumCertificate { + fn from(source: QuorumCertificate) -> Self { Self { message_type: source.message_type().as_u8() as i32, - node: Some(source.node().clone().into()), + node_hash: Vec::from(source.node_hash().as_bytes()), view_number: source.view_number().as_u64(), signature: source.signature().map(|s| s.clone().into()), } @@ -127,18 +127,14 @@ impl TryFrom for HotStuffMessage { } } -impl TryFrom for QuorumCertificate { +impl TryFrom for QuorumCertificate { type Error = String; fn try_from(value: dan_proto::QuorumCertificate) -> Result { Ok(Self::new( HotStuffMessageType::try_from(value.message_type as u8)?, ViewId(value.view_number), - value - .node - .map(|n| n.try_into()) - .transpose()? - .ok_or_else(|| "node not provided on Quorum Certificate".to_string())?, + TreeNodeHash(value.node_hash), value.signature.map(|s| s.try_into()).transpose()?, )) } diff --git a/dan_layer/core/src/models/hot_stuff_message.rs b/dan_layer/core/src/models/hot_stuff_message.rs index d5757ad918..5fe116a08b 100644 --- a/dan_layer/core/src/models/hot_stuff_message.rs +++ b/dan_layer/core/src/models/hot_stuff_message.rs @@ -29,7 +29,7 @@ use tari_crypto::common::Blake256; pub struct HotStuffMessage { view_number: ViewId, message_type: HotStuffMessageType, - justify: Option>, + justify: Option, node: Option>, partial_sig: Option, } @@ -38,7 +38,7 @@ impl HotStuffMessage { pub fn new( view_number: ViewId, message_type: HotStuffMessageType, - justify: Option>, + justify: Option, node: Option>, partial_sig: Option, ) -> Self { @@ -51,7 +51,7 @@ impl HotStuffMessage { } } - pub fn new_view(prepare_qc: QuorumCertificate, view_number: ViewId) -> Self { + pub fn new_view(prepare_qc: QuorumCertificate, view_number: ViewId) -> Self { Self { message_type: HotStuffMessageType::NewView, view_number, @@ -63,7 +63,7 @@ impl HotStuffMessage { pub fn prepare( proposal: HotStuffTreeNode, - high_qc: Option>, + high_qc: Option, view_number: ViewId, ) -> Self { Self { @@ -77,7 +77,7 @@ impl HotStuffMessage { pub fn pre_commit( node: Option>, - prepare_qc: Option>, + prepare_qc: Option, view_number: ViewId, ) -> Self { Self { @@ -91,7 +91,7 @@ impl HotStuffMessage { pub fn commit( node: Option>, - pre_commit_qc: Option>, + pre_commit_qc: Option, view_number: ViewId, ) -> Self { Self { @@ -105,7 +105,7 @@ impl HotStuffMessage { pub fn decide( node: Option>, - commit_qc: Option>, + commit_qc: Option, view_number: ViewId, ) -> Self { Self { @@ -139,7 +139,7 @@ impl HotStuffMessage { &self.message_type } - pub fn justify(&self) -> Option<&QuorumCertificate> { + pub fn justify(&self) -> Option<&QuorumCertificate> { self.justify.as_ref() } diff --git a/dan_layer/core/src/models/hot_stuff_tree_node.rs b/dan_layer/core/src/models/hot_stuff_tree_node.rs index 115c4fc99a..cf3ca03395 100644 --- a/dan_layer/core/src/models/hot_stuff_tree_node.rs +++ b/dan_layer/core/src/models/hot_stuff_tree_node.rs @@ -52,8 +52,8 @@ impl HotStuffTreeNode { s } - pub fn from_parent(parent: &HotStuffTreeNode, payload: TPayload) -> HotStuffTreeNode { - Self::new(parent.calculate_hash(), payload) + pub fn from_parent(parent: TreeNodeHash, payload: TPayload) -> HotStuffTreeNode { + Self::new(parent, payload) } pub fn calculate_hash(&self) -> TreeNodeHash { diff --git a/dan_layer/core/src/models/mod.rs b/dan_layer/core/src/models/mod.rs index 2907783fc7..ca45545952 100644 --- a/dan_layer/core/src/models/mod.rs +++ b/dan_layer/core/src/models/mod.rs @@ -202,6 +202,10 @@ pub enum ConsensusWorkerState { pub struct Signature {} impl Signature { + pub fn from_bytes(source: &[u8]) -> Self { + Self {} + } + pub fn combine(&self, other: &Signature) -> Signature { other.clone() } diff --git a/dan_layer/core/src/models/quorum_certificate.rs b/dan_layer/core/src/models/quorum_certificate.rs index 443a7493eb..dad2de467b 100644 --- a/dan_layer/core/src/models/quorum_certificate.rs +++ b/dan_layer/core/src/models/quorum_certificate.rs @@ -25,7 +25,7 @@ use crate::models::{HotStuffMessageType, HotStuffTreeNode, Payload, Signature, T #[derive(Debug, Clone)] pub struct QuorumCertificate { message_type: HotStuffMessageType, - node: TreeNodeHash, + node_hash: TreeNodeHash, view_number: ViewId, signature: Option, } @@ -34,28 +34,28 @@ impl QuorumCertificate { pub fn new( message_type: HotStuffMessageType, view_number: ViewId, - node: TreeNodeHash, + node_hash: TreeNodeHash, signature: Option, ) -> Self { Self { message_type, - node, + node_hash, view_number, signature, } } - pub fn genesis(payload: TPayload) -> Self { + pub fn genesis(node_hash: TreeNodeHash) -> Self { Self { message_type: HotStuffMessageType::Genesis, - node: HotStuffTreeNode::genesis(payload).hash().clone(), + node_hash, view_number: 0.into(), signature: None, } } - pub fn node(&self) -> &TreeNodeHash { - &self.node + pub fn node_hash(&self) -> &TreeNodeHash { + &self.node_hash } pub fn view_number(&self) -> ViewId { diff --git a/dan_layer/core/src/storage/chain_db.rs b/dan_layer/core/src/storage/chain_db.rs index 4aa87739c5..3a3eaadc29 100644 --- a/dan_layer/core/src/storage/chain_db.rs +++ b/dan_layer/core/src/storage/chain_db.rs @@ -35,7 +35,7 @@ impl ChainDb { ChainDb { adapter } } - pub fn find_highest_prepared_qc(&self) -> Result, StorageError> { + pub fn find_highest_prepared_qc(&self) -> Result { self.adapter .find_highest_prepared_qc() .map_err(TBackendAdapter::Error::into) diff --git a/dan_layer/core/src/storage/chain_storage_service.rs b/dan_layer/core/src/storage/chain_storage_service.rs index a5f94f8763..a94e6cccb7 100644 --- a/dan_layer/core/src/storage/chain_storage_service.rs +++ b/dan_layer/core/src/storage/chain_storage_service.rs @@ -41,7 +41,7 @@ pub trait ChainStorageService { async fn set_locked_qc( &self, - qc: QuorumCertificate, + qc: QuorumCertificate, db: TUnitOfWork, ) -> Result<(), StorageError>; } diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index 64774dbd97..06c43c4db5 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -89,7 +89,7 @@ pub trait BackendAdapter: Send + Sync + Clone { ) -> Result<(), Self::Error>; fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; fn locked_qc_id(&self) -> Self::Id; - fn find_highest_prepared_qc(&self) -> Result, Self::Error>; + fn find_highest_prepared_qc(&self) -> Result; } pub trait UnitOfWork: Clone + Send + Sync { diff --git a/dan_layer/core/src/workers/consensus_worker.rs b/dan_layer/core/src/workers/consensus_worker.rs index 83fb516b50..734bbd4fb8 100644 --- a/dan_layer/core/src/workers/consensus_worker.rs +++ b/dan_layer/core/src/workers/consensus_worker.rs @@ -87,9 +87,7 @@ pub struct ConsensusWorker< timeout: Duration, node_id: TAddr, payload_provider: TPayloadProvider, - prepare_qc: Arc>, events_publisher: TEventsPublisher, - locked_qc: Arc>, signing_service: TSigningService, payload_processor: TPayloadProcessor, asset_definition: AssetDefinition, @@ -97,6 +95,7 @@ pub struct ConsensusWorker< db_factory: TDbFactory, chain_storage_service: TChainStorageService, pd: PhantomData, + pd2: PhantomData, } impl< @@ -142,7 +141,7 @@ where TBaseNodeClient: BaseNodeClient, // TODO: REmove this Send - TBackendAdapter: BackendAdapter + Send + Sync, + TBackendAdapter: BackendAdapter + Send + Sync, TDbFactory: DbFactory + Clone, TChainStorageService: ChainStorageService, { @@ -162,7 +161,7 @@ where db_factory: TDbFactory, chain_storage_service: TChainStorageService, ) -> Self { - let prepare_qc = Arc::new(QuorumCertificate::genesis(payload_provider.create_genesis_payload())); + // let prepare_qc = Arc::new(QuorumCertificate::genesis(ash())); Self { inbound_connections, @@ -172,8 +171,6 @@ where outbound_service, committee_manager, node_id, - locked_qc: prepare_qc.clone(), - prepare_qc, payload_provider, events_publisher, signing_service, @@ -183,6 +180,7 @@ where db_factory, chain_storage_service, pd: PhantomData, + pd2: PhantomData, } } @@ -251,7 +249,7 @@ where .await }, Prepare => { - let mut p = states::Prepare::new(self.node_id.clone(), self.locked_qc.clone(), self.db_factory.clone()); + let mut p = states::Prepare::new(self.node_id.clone(), self.db_factory.clone()); p.next_event( &self.get_current_view()?, self.timeout, @@ -279,7 +277,8 @@ where ) .await?; if let Some(prepare_qc) = prepare_qc { - self.prepare_qc = Arc::new(prepare_qc); + todo!("Fix this? save to db?"); + // self.prepare_qc = Arc::new(prepare_qc); } Ok(res) }, @@ -299,7 +298,8 @@ where ) .await?; if let Some(locked_qc) = locked_qc { - self.locked_qc = Arc::new(locked_qc); + todo!("Save to db?"); + // self.locked_qc = Arc::new(locked_qc); } Ok(res) }, diff --git a/dan_layer/core/src/workers/states/commit_state.rs b/dan_layer/core/src/workers/states/commit_state.rs index 2c8ea178ce..5e216faed7 100644 --- a/dan_layer/core/src/workers/states/commit_state.rs +++ b/dan_layer/core/src/workers/states/commit_state.rs @@ -58,8 +58,8 @@ where p_p: PhantomData, p_s: PhantomData, received_new_view_messages: HashMap>, - pre_commit_qc: Option>, - locked_qc: Option>, + pre_commit_qc: Option, + locked_qc: Option, } impl @@ -93,7 +93,7 @@ where inbound_services: &mut TInboundConnectionService, outbound_service: &mut TOutboundService, signing_service: &TSigningService, - ) -> Result<(ConsensusWorkerStateEvent, Option>), DigitalAssetError> { + ) -> Result<(ConsensusWorkerStateEvent, Option), DigitalAssetError> { let mut next_event_result = ConsensusWorkerStateEvent::Errored { reason: "loop ended without setting this event".to_string(), }; @@ -190,7 +190,7 @@ where async fn broadcast( &self, outbound: &mut TOutboundService, - pre_commit_qc: QuorumCertificate, + pre_commit_qc: QuorumCertificate, view_number: ViewId, ) -> Result<(), DigitalAssetError> { let message = HotStuffMessage::commit(None, Some(pre_commit_qc), view_number); @@ -199,7 +199,7 @@ where .await } - fn create_qc(&self, current_view: &View) -> Option> { + fn create_qc(&self, current_view: &View) -> Option { // TODO: This can be done in one loop instead of two let mut node = None; for message in self.received_new_view_messages.values() { @@ -218,12 +218,13 @@ where }; } - let node = node.unwrap(); - let mut qc = QuorumCertificate::new(HotStuffMessageType::PreCommit, current_view.view_id, node, None); - for message in self.received_new_view_messages.values() { - qc.combine_sig(message.partial_sig().unwrap()) - } - Some(qc) + todo!("Fix this"); + // let node = node.unwrap(); + // let mut qc = QuorumCertificate::new(HotStuffMessageType::PreCommit, current_view.view_id, node, None); + // for message in self.received_new_view_messages.values() { + // qc.combine_sig(message.partial_sig().unwrap()) + // } + // Some(qc) } async fn process_replica_message( @@ -254,15 +255,17 @@ where return Ok(None); } - self.locked_qc = Some(justify.clone()); - self.send_vote_to_leader( - justify.node(), - outbound, - view_leader, - current_view.view_id, - signing_service, - ) - .await?; + todo!("Fix this"); + + // self.locked_qc = Some(justify.clone()); + // self.send_vote_to_leader( + // justify.node(), + // outbound, + // view_leader, + // current_view.view_id, + // signing_service, + // ) + // .await?; Ok(Some(ConsensusWorkerStateEvent::Committed)) } else { dbg!("received non justify message"); diff --git a/dan_layer/core/src/workers/states/decide_state.rs b/dan_layer/core/src/workers/states/decide_state.rs index 0e514169a4..f9b0df1976 100644 --- a/dan_layer/core/src/workers/states/decide_state.rs +++ b/dan_layer/core/src/workers/states/decide_state.rs @@ -58,8 +58,8 @@ where p_p: PhantomData, p_s: PhantomData, received_new_view_messages: HashMap>, - commit_qc: Option>, - _locked_qc: Option>, + commit_qc: Option, + _locked_qc: Option, } impl @@ -180,7 +180,7 @@ where async fn broadcast( &self, outbound: &mut TOutboundService, - commit_qc: QuorumCertificate, + commit_qc: QuorumCertificate, view_number: ViewId, ) -> Result<(), DigitalAssetError> { let message = HotStuffMessage::decide(None, Some(commit_qc), view_number); @@ -189,7 +189,7 @@ where .await } - fn create_qc(&self, current_view: &View) -> Option> { + fn create_qc(&self, current_view: &View) -> Option { let mut node = None; for message in self.received_new_view_messages.values() { node = match node { @@ -207,12 +207,13 @@ where }; } - let node = node.unwrap(); - let mut qc = QuorumCertificate::new(HotStuffMessageType::Commit, current_view.view_id, node, None); - for message in self.received_new_view_messages.values() { - qc.combine_sig(message.partial_sig().unwrap()) - } - Some(qc) + todo!("Fix this"); + // let node = node.unwrap(); + // let mut qc = QuorumCertificate::new(HotStuffMessageType::Commit, current_view.view_id, node, None); + // for message in self.received_new_view_messages.values() { + // qc.combine_sig(message.partial_sig().unwrap()) + // } + // Some(qc) } async fn process_replica_message( diff --git a/dan_layer/core/src/workers/states/pre_commit_state.rs b/dan_layer/core/src/workers/states/pre_commit_state.rs index dbbfb4c068..bf14fd87c3 100644 --- a/dan_layer/core/src/workers/states/pre_commit_state.rs +++ b/dan_layer/core/src/workers/states/pre_commit_state.rs @@ -57,7 +57,7 @@ where p_p: PhantomData, p_s: PhantomData, received_new_view_messages: HashMap>, - prepare_qc: Option>, + prepare_qc: Option, } impl @@ -90,7 +90,7 @@ where inbound_services: &mut TInboundConnectionService, outbound_service: &mut TOutboundService, signing_service: &TSigningService, - ) -> Result<(ConsensusWorkerStateEvent, Option>), DigitalAssetError> { + ) -> Result<(ConsensusWorkerStateEvent, Option), DigitalAssetError> { let mut next_event_result = ConsensusWorkerStateEvent::Errored { reason: "loop ended without setting this event".to_string(), }; @@ -186,7 +186,7 @@ where &self, outbound: &mut TOutboundService, committee: &Committee, - prepare_qc: QuorumCertificate, + prepare_qc: QuorumCertificate, view_number: ViewId, ) -> Result<(), DigitalAssetError> { let message = HotStuffMessage::pre_commit(None, Some(prepare_qc), view_number); @@ -195,7 +195,7 @@ where .await } - fn create_qc(&self, current_view: &View) -> Option> { + fn create_qc(&self, current_view: &View) -> Option { let mut node = None; for message in self.received_new_view_messages.values() { node = match node { @@ -213,12 +213,13 @@ where }; } - let node = node.unwrap(); - let mut qc = QuorumCertificate::new(HotStuffMessageType::Prepare, current_view.view_id, node, None); - for message in self.received_new_view_messages.values() { - qc.combine_sig(message.partial_sig().unwrap()) - } - Some(qc) + todo!("Fix this") + // let node = node.unwrap(); + // let mut qc = QuorumCertificate::new(HotStuffMessageType::Prepare, current_view.view_id, node, None); + // for message in self.received_new_view_messages.values() { + // qc.combine_sig(message.partial_sig().unwrap()) + // } + // Some(qc) } async fn process_replica_message( @@ -249,15 +250,16 @@ where return Ok(None); } - self.prepare_qc = Some(justify.clone()); - self.send_vote_to_leader( - justify.node(), - outbound, - view_leader, - current_view.view_id, - signing_service, - ) - .await?; + todo!("Fix this"); + // self.prepare_qc = Some(justify.clone()); + // self.send_vote_to_leader( + // justify.node(), + // outbound, + // view_leader, + // current_view.view_id, + // signing_service, + // ) + // .await?; Ok(Some(ConsensusWorkerStateEvent::PreCommitted)) } else { // dbg!("received non justify message"); diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index 3c8c27d3b3..a5dfd6a8e3 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -43,6 +43,7 @@ use log::*; use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Instant}; use crate::{ + models::TreeNodeHash, services::PayloadProcessor, storage::{BackendAdapter, DbFactory}, }; @@ -72,7 +73,7 @@ pub struct Prepare< TDbFactory: DbFactory, { node_id: TAddr, - locked_qc: Arc>, + locked_qc: Arc, // bft_service: Box, // TODO remove this hack phantom: PhantomData, @@ -118,7 +119,8 @@ where TBackendAdapter: BackendAdapter + Send + Sync, TDbFactory: DbFactory + Clone, { - pub fn new(node_id: TAddr, locked_qc: Arc>, db_factory: TDbFactory) -> Self { + pub fn new(node_id: TAddr, db_factory: TDbFactory) -> Self { + let locked_qc = todo!(); Self { node_id, locked_qc, @@ -238,7 +240,9 @@ where ); let high_qc = self.find_highest_qc(); dbg!(&high_qc); - let proposal = self.create_proposal(high_qc.node(), payload_provider).await?; + let proposal = self + .create_proposal(high_qc.node_hash().clone(), payload_provider) + .await?; self.broadcast_proposal(outbound, committee, proposal, high_qc, current_view.view_id) .await?; // Ok(Some(ConsensusWorkerStateEvent::Prepared)) @@ -281,7 +285,7 @@ where } let node = message.node().unwrap(); if let Some(justify) = message.justify() { - if self.does_extend(node, justify.node()) { + if self.does_extend(node, justify.node_hash()) { if !self.is_safe_node(node, justify) { unimplemented!("Node is not safe") } @@ -289,9 +293,7 @@ where let db = self.db_factory.create()?; let unit_of_work = db.new_unit_of_work(); - let res = payload_processor - .process_payload(justify.node().payload(), unit_of_work) - .await?; + let res = payload_processor.process_payload(node.payload(), unit_of_work).await?; // TODO: Check result equals qc result @@ -306,7 +308,7 @@ where } } - fn find_highest_qc(&self) -> QuorumCertificate { + fn find_highest_qc(&self) -> QuorumCertificate { let mut max_qc = None; for message in self.received_new_view_messages.values() { match &max_qc { @@ -326,7 +328,7 @@ where async fn create_proposal( &self, - parent: &HotStuffTreeNode, + parent: TreeNodeHash, payload_provider: &TPayloadProvider, ) -> Result, DigitalAssetError> { info!(target: LOG_TARGET, "Creating new proposal"); @@ -343,7 +345,7 @@ where outbound: &mut TOutboundService, committee: &Committee, proposal: HotStuffTreeNode, - high_qc: QuorumCertificate, + high_qc: QuorumCertificate, view_number: ViewId, ) -> Result<(), DigitalAssetError> { let message = HotStuffMessage::prepare(proposal, Some(high_qc), view_number); @@ -352,16 +354,13 @@ where .await } - fn does_extend(&self, node: &HotStuffTreeNode, from: &HotStuffTreeNode) -> bool { - &from.calculate_hash() == node.parent() + fn does_extend(&self, node: &HotStuffTreeNode, from: &TreeNodeHash) -> bool { + &from == &node.parent() } - fn is_safe_node( - &self, - node: &HotStuffTreeNode, - quorum_certificate: &QuorumCertificate, - ) -> bool { - self.does_extend(node, self.locked_qc.node()) || quorum_certificate.view_number() > self.locked_qc.view_number() + fn is_safe_node(&self, node: &HotStuffTreeNode, quorum_certificate: &QuorumCertificate) -> bool { + self.does_extend(node, self.locked_qc.node_hash()) || + quorum_certificate.view_number() > self.locked_qc.view_number() } async fn send_vote_to_leader( diff --git a/dan_layer/core/src/workers/states/starting.rs b/dan_layer/core/src/workers/states/starting.rs index d5190cffaa..16528187d6 100644 --- a/dan_layer/core/src/workers/states/starting.rs +++ b/dan_layer/core/src/workers/states/starting.rs @@ -22,7 +22,7 @@ use crate::{ digital_assets_error::DigitalAssetError, - models::{AssetDefinition, Payload, QuorumCertificate, TariDanPayload}, + models::{AssetDefinition, HotStuffTreeNode, Payload, QuorumCertificate, TariDanPayload}, services::{ infrastructure_services::NodeAddressable, BaseNodeClient, @@ -107,8 +107,9 @@ where TBaseNodeClient: BaseNodeClient let payload = payload_provider.create_genesis_payload(); payload_processor.process_payload(&payload, tx2).await?; - let genesis_qc = QuorumCertificate::genesis(payload); - chain_storage_service.save_node(genesis_qc.node(), tx.clone()).await?; + let node = HotStuffTreeNode::genesis(payload); + let genesis_qc = QuorumCertificate::genesis(node.hash().clone()); + chain_storage_service.save_node(&node, tx.clone()).await?; chain_storage_service.set_locked_qc(genesis_qc, tx.clone()).await?; tx.commit()?; } diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs index 34d238c2ae..3328a5b8dc 100644 --- a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -32,8 +32,9 @@ use crate::{ }; use diesel::{prelude::*, Connection, SqliteConnection}; use diesel_migrations::embed_migrations; +use std::convert::TryFrom; use tari_dan_core::{ - models::{Payload, QuorumCertificate, TariDanPayload}, + models::{HotStuffMessageType, Payload, QuorumCertificate, Signature, TariDanPayload, TreeNodeHash, ViewId}, storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError, UnitOfWorkTracker}, }; @@ -142,19 +143,17 @@ impl BackendAdapter for SqliteBackendAdapter { 1 } - fn find_highest_prepared_qc(&self) -> Result, Self::Error> { + fn find_highest_prepared_qc(&self) -> Result { use crate::schema::*; let connection = SqliteConnection::establish(self.database_url.as_str())?; let result: Option = prepare_qcs::table .order_by(prepare_qcs::view_number.desc()) - .first(connection) + .first(&connection) .optional()?; let qc = match result { Some(r) => r, None => { - let l: LockedQc = dsl::locked_qc - .find(self.locked_qc_id()) - .first(transaction.connection())?; + let l: LockedQc = dsl::locked_qc.find(self.locked_qc_id()).first(&connection)?; PrepareQc { id: 1, message_type: l.message_type, @@ -166,8 +165,10 @@ impl BackendAdapter for SqliteBackendAdapter { }; Ok(QuorumCertificate::new( - MessageType::from(qc.message_type as u8), + HotStuffMessageType::try_from(qc.message_type as u8).unwrap(), ViewId::from(qc.view_number as u64), + TreeNodeHash(qc.node_hash.clone()), + qc.signature.map(|s| Signature::from_bytes(s.as_slice())), )) } } diff --git a/dan_layer/storage_sqlite/src/sqlite_storage_service.rs b/dan_layer/storage_sqlite/src/sqlite_storage_service.rs index 6f0cb50036..b4af5933ab 100644 --- a/dan_layer/storage_sqlite/src/sqlite_storage_service.rs +++ b/dan_layer/storage_sqlite/src/sqlite_storage_service.rs @@ -53,14 +53,14 @@ impl ChainStorageService for SqliteStorageService { async fn set_locked_qc( &self, - qc: QuorumCertificate, + qc: QuorumCertificate, db: TUnitOfWork, ) -> Result<(), StorageError> { let mut db = db; db.set_locked_qc( qc.message_type(), qc.view_number(), - qc.node().hash().clone(), + qc.node_hash().clone(), qc.signature().map(|s| s.clone()), )?; Ok(()) From 50ac674cfad9e8a475509783a20343c441c10b10 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 3 Nov 2021 22:53:58 +0200 Subject: [PATCH 05/14] remove node from qc --- dan_layer/core/src/storage/chain_db.rs | 4 ++++ dan_layer/core/src/storage/mod.rs | 1 + dan_layer/core/src/workers/consensus_worker.rs | 5 ++++- dan_layer/core/src/workers/states/next_view.rs | 1 + dan_layer/core/src/workers/states/prepare.rs | 5 ++--- .../storage_sqlite/src/sqlite_backend_adapter.rs | 11 +++++++++++ 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/dan_layer/core/src/storage/chain_db.rs b/dan_layer/core/src/storage/chain_db.rs index 3a3eaadc29..d2f22cc99d 100644 --- a/dan_layer/core/src/storage/chain_db.rs +++ b/dan_layer/core/src/storage/chain_db.rs @@ -40,6 +40,10 @@ impl ChainDb { .find_highest_prepared_qc() .map_err(TBackendAdapter::Error::into) } + + pub fn get_locked_qc(&self) -> Result { + self.adapter.get_locked_qc().map_err(TBackendAdapter::Error::into) + } } impl ChainDb { diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index 06c43c4db5..41bc9ca1de 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -90,6 +90,7 @@ pub trait BackendAdapter: Send + Sync + Clone { fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; fn locked_qc_id(&self) -> Self::Id; fn find_highest_prepared_qc(&self) -> Result; + fn get_locked_qc(&self) -> Result; } pub trait UnitOfWork: Clone + Send + Sync { diff --git a/dan_layer/core/src/workers/consensus_worker.rs b/dan_layer/core/src/workers/consensus_worker.rs index 734bbd4fb8..730aba7bc4 100644 --- a/dan_layer/core/src/workers/consensus_worker.rs +++ b/dan_layer/core/src/workers/consensus_worker.rs @@ -249,7 +249,10 @@ where .await }, Prepare => { - let mut p = states::Prepare::new(self.node_id.clone(), self.db_factory.clone()); + let db = self.db_factory.create()?; + let locked_qc = db.get_locked_qc()?; + + let mut p = states::Prepare::new(self.node_id.clone(), self.db_factory.clone(), locked_qc); p.next_event( &self.get_current_view()?, self.timeout, diff --git a/dan_layer/core/src/workers/states/next_view.rs b/dan_layer/core/src/workers/states/next_view.rs index ceeac21d7a..45394a3f3c 100644 --- a/dan_layer/core/src/workers/states/next_view.rs +++ b/dan_layer/core/src/workers/states/next_view.rs @@ -56,6 +56,7 @@ impl NextViewState { ) -> Result { let db = db_factory.create()?; let prepare_qc = db.find_highest_prepared_qc()?; + dbg!(&prepare_qc); let message = HotStuffMessage::new_view(prepare_qc, current_view.view_id); let next_view = current_view.view_id.next(); let leader = committee.leader_for_view(next_view); diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index a5dfd6a8e3..12a0ddf47a 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -73,7 +73,7 @@ pub struct Prepare< TDbFactory: DbFactory, { node_id: TAddr, - locked_qc: Arc, + locked_qc: QuorumCertificate, // bft_service: Box, // TODO remove this hack phantom: PhantomData, @@ -119,8 +119,7 @@ where TBackendAdapter: BackendAdapter + Send + Sync, TDbFactory: DbFactory + Clone, { - pub fn new(node_id: TAddr, db_factory: TDbFactory) -> Self { - let locked_qc = todo!(); + pub fn new(node_id: TAddr, db_factory: TDbFactory, locked_qc: QuorumCertificate) -> Self { Self { node_id, locked_qc, diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs index 3328a5b8dc..ac6e4f3cfc 100644 --- a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -171,4 +171,15 @@ impl BackendAdapter for SqliteBackendAdapter { qc.signature.map(|s| Signature::from_bytes(s.as_slice())), )) } + + fn get_locked_qc(&self) -> Result { + let connection = SqliteConnection::establish(self.database_url.as_str())?; + let qc: LockedQc = dsl::locked_qc.find(self.locked_qc_id()).first(&connection)?; + Ok(QuorumCertificate::new( + HotStuffMessageType::try_from(qc.message_type as u8).unwrap(), + ViewId::from(qc.view_number as u64), + TreeNodeHash(qc.node_hash.clone()), + qc.signature.map(|s| Signature::from_bytes(s.as_slice())), + )) + } } From 3355eac21edcc2e5cf3118093e4c878457001e0f Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Sun, 7 Nov 2021 20:42:33 +0200 Subject: [PATCH 06/14] wip --- .../tari_collectibles/web-app/src/helpers.js | 27 ++++++++ .../proto/p2p/dan_consensus.proto | 1 + .../src/p2p/proto/conversions/dan.rs | 2 + base_layer/wallet/src/types.rs | 7 +- dan_layer/core/src/models/asset_definition.rs | 29 ++++++-- .../core/src/models/hot_stuff_message.rs | 48 +++++++++++++- .../core/src/services/asset_processor.rs | 6 +- .../core/src/services/payload_processor.rs | 6 +- dan_layer/core/src/storage/mod.rs | 66 ++++++++++--------- .../src/workers/states/pre_commit_state.rs | 41 ++++++------ dan_layer/core/src/workers/states/prepare.rs | 17 +++-- dan_layer/core/src/workers/states/starting.rs | 17 +++-- .../2021-11-02-185150_create_nodes/up.sql | 10 ++- dan_layer/storage_sqlite/src/models/mod.rs | 1 + .../src/models/state_key_value.rs | 39 +++++++++++ dan_layer/storage_sqlite/src/schema.rs | 10 +++ .../storage_sqlite/src/sqlite_db_factory.rs | 6 +- 17 files changed, 250 insertions(+), 83 deletions(-) create mode 100644 applications/tari_collectibles/web-app/src/helpers.js create mode 100644 dan_layer/storage_sqlite/src/models/state_key_value.rs diff --git a/applications/tari_collectibles/web-app/src/helpers.js b/applications/tari_collectibles/web-app/src/helpers.js new file mode 100644 index 0000000000..804b905e43 --- /dev/null +++ b/applications/tari_collectibles/web-app/src/helpers.js @@ -0,0 +1,27 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +export function toHexString(byteArray) { + return Array.from(byteArray, function(byte) { + return ('0' + (byte & 0xFF).toString(16)).slice(-2); + }).join('') +} diff --git a/applications/tari_validator_node/proto/p2p/dan_consensus.proto b/applications/tari_validator_node/proto/p2p/dan_consensus.proto index 82239ae2d1..0e1c58fd14 100644 --- a/applications/tari_validator_node/proto/p2p/dan_consensus.proto +++ b/applications/tari_validator_node/proto/p2p/dan_consensus.proto @@ -18,6 +18,7 @@ message HotStuffMessage { QuorumCertificate justify = 3; HotStuffTreeNode node= 4; Signature partial_sig = 5; + optional bytes node_hash = 6; } message QuorumCertificate { diff --git a/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs b/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs index bc897f7bc6..66e7f915a6 100644 --- a/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs +++ b/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs @@ -49,6 +49,7 @@ impl From> for dan_proto::HotStuffMessage { justify: source.justify().map(|j| j.clone().into()), partial_sig: source.partial_sig().map(|s| s.clone().into()), view_number: source.view_number().as_u64(), + node_hash: source.node_hash().map(|s| s.0.clone()), } } } @@ -122,6 +123,7 @@ impl TryFrom for HotStuffMessage { HotStuffMessageType::try_from(value.message_type as u8)?, value.justify.map(|j| j.try_into()).transpose()?, value.node.map(|n| n.try_into()).transpose()?, + value.node_hash.map(|v| TreeNodeHash(v.clone())), value.partial_sig.map(|p| p.try_into()).transpose()?, )) } diff --git a/base_layer/wallet/src/types.rs b/base_layer/wallet/src/types.rs index 2dcffdfa6e..4538d9ee96 100644 --- a/base_layer/wallet/src/types.rs +++ b/base_layer/wallet/src/types.rs @@ -42,10 +42,9 @@ pub(crate) struct MockPersistentKeyManager { impl MockPersistentKeyManager { pub fn new() -> Self { - todo!() - // Self { - // key_manager: KeyManager::new(&mut OsRng), - // } + Self { + key_manager: KeyManager::new(), + } } } diff --git a/dan_layer/core/src/models/asset_definition.rs b/dan_layer/core/src/models/asset_definition.rs index 4f6a6f3635..6d0b559759 100644 --- a/dan_layer/core/src/models/asset_definition.rs +++ b/dan_layer/core/src/models/asset_definition.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::types::PublicKey; -use serde::{self, de, Deserialize, Deserializer}; +use serde::{self, de, Deserialize, Deserializer, Serialize}; use std::{fmt, marker::PhantomData}; use tari_crypto::tari_utilities::hex::Hex; @@ -36,7 +36,7 @@ pub struct AssetDefinition { // TODO: Better name? lock time/peg time? (in number of blocks) pub base_layer_confirmation_time: u64, pub checkpoint_unique_id: Vec, - pub templates: Vec, + pub initial_state: InitialState, } impl Default for AssetDefinition { @@ -47,7 +47,7 @@ impl Default for AssetDefinition { public_key: Default::default(), initial_committee: vec![], phase_timeout: 10, - templates: vec![], + initial_state: Default::default(), } } } @@ -73,10 +73,25 @@ impl AssetDefinition { } des.deserialize_str(KeyStringVisitor { marker: PhantomData }) } + + pub fn initial_state(&self) -> &InitialState { + &self.initial_state + } } -#[derive(Deserialize, Clone)] -#[serde(tag = "id", content = "data")] -pub enum TemplateArgs { - Tmp721 { num_tokens: u64 }, +#[derive(Serialize, Deserialize, Default, Clone)] +pub struct InitialState { + pub schemas: Vec, +} + +#[derive(Serialize, Deserialize, Default, Clone)] +pub struct SchemaState { + pub name: String, + pub items: Vec, +} + +#[derive(Serialize, Deserialize, Default, Clone)] +pub struct KeyValue { + pub key: Vec, + pub value: Vec, } diff --git a/dan_layer/core/src/models/hot_stuff_message.rs b/dan_layer/core/src/models/hot_stuff_message.rs index 5fe116a08b..bfe7d25e3c 100644 --- a/dan_layer/core/src/models/hot_stuff_message.rs +++ b/dan_layer/core/src/models/hot_stuff_message.rs @@ -20,7 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::models::{HotStuffMessageType, HotStuffTreeNode, Payload, QuorumCertificate, Signature, ViewId}; +use crate::models::{ + HotStuffMessageType, + HotStuffTreeNode, + Payload, + QuorumCertificate, + Signature, + TreeNodeHash, + ViewId, +}; use digest::Digest; use tari_crypto::common::Blake256; @@ -31,6 +39,7 @@ pub struct HotStuffMessage { message_type: HotStuffMessageType, justify: Option, node: Option>, + node_hash: Option, partial_sig: Option, } @@ -40,6 +49,7 @@ impl HotStuffMessage { message_type: HotStuffMessageType, justify: Option, node: Option>, + node_hash: Option, partial_sig: Option, ) -> Self { HotStuffMessage { @@ -47,6 +57,7 @@ impl HotStuffMessage { message_type, justify, node, + node_hash, partial_sig, } } @@ -58,6 +69,7 @@ impl HotStuffMessage { justify: Some(prepare_qc), node: None, partial_sig: None, + node_hash: None, } } @@ -72,6 +84,18 @@ impl HotStuffMessage { justify: high_qc, view_number, partial_sig: None, + node_hash: None, + } + } + + pub fn vote_prepare(node_hash: TreeNodeHash, view_number: ViewId) -> Self { + Self { + message_type: HotStuffMessageType::Prepare, + node_hash: Some(node_hash), + view_number, + node: None, + partial_sig: None, + justify: None, } } @@ -85,7 +109,19 @@ impl HotStuffMessage { node, justify: prepare_qc, view_number, + node_hash: None, + partial_sig: None, + } + } + + pub fn vote_pre_commit(node_hash: TreeNodeHash, view_number: ViewId) -> Self { + Self { + message_type: HotStuffMessageType::PreCommit, + node_hash: Some(node_hash), + view_number, + node: None, partial_sig: None, + justify: None, } } @@ -100,6 +136,7 @@ impl HotStuffMessage { justify: pre_commit_qc, view_number, partial_sig: None, + node_hash: None, } } @@ -114,6 +151,7 @@ impl HotStuffMessage { justify: commit_qc, view_number, partial_sig: None, + node_hash: None, } } @@ -123,6 +161,10 @@ impl HotStuffMessage { .chain(self.view_number.as_u64().to_le_bytes()); if let Some(ref node) = self.node { b = b.chain(node.calculate_hash().as_bytes()); + } else { + if let Some(ref node_hash) = self.node_hash { + b = b.chain(node_hash.as_bytes()); + } } b.finalize().to_vec() } @@ -135,6 +177,10 @@ impl HotStuffMessage { self.node.as_ref() } + pub fn node_hash(&self) -> Option<&TreeNodeHash> { + self.node_hash.as_ref() + } + pub fn message_type(&self) -> &HotStuffMessageType { &self.message_type } diff --git a/dan_layer/core/src/services/asset_processor.rs b/dan_layer/core/src/services/asset_processor.rs index a0564a17fa..eddce31340 100644 --- a/dan_layer/core/src/services/asset_processor.rs +++ b/dan_layer/core/src/services/asset_processor.rs @@ -34,7 +34,7 @@ use tokio::sync::RwLock; pub trait AssetProcessor { // purposefully made sync, because instructions should be run in order, and complete before the // next one starts. There may be a better way to enforce this though... - fn execute_instruction( + fn execute_instruction( &self, instruction: &Instruction, db: TUnitOfWork, @@ -48,7 +48,7 @@ pub struct ConcreteAssetProcessor { } impl AssetProcessor for ConcreteAssetProcessor { - fn execute_instruction( + fn execute_instruction( &self, instruction: &Instruction, db: TUnitOfWork, @@ -75,7 +75,7 @@ impl ConcreteAssetProcessor { } } - pub fn execute( + pub fn execute( &self, template_id: TemplateId, method: String, diff --git a/dan_layer/core/src/services/payload_processor.rs b/dan_layer/core/src/services/payload_processor.rs index b376ec7b5b..ae9d3be7f6 100644 --- a/dan_layer/core/src/services/payload_processor.rs +++ b/dan_layer/core/src/services/payload_processor.rs @@ -24,7 +24,7 @@ use crate::{ digital_assets_error::DigitalAssetError, models::{Payload, TariDanPayload}, services::{AssetProcessor, MempoolService}, - storage::{ChainDb, ChainDbUnitOfWork, DbFactory, UnitOfWork}, + storage::{ChainDb, ChainDbUnitOfWork, DbFactory, StateDbUnitOfWork, UnitOfWork}, }; use async_trait::async_trait; use std::sync::Arc; @@ -32,7 +32,7 @@ use tokio::sync::RwLock; #[async_trait] pub trait PayloadProcessor { - async fn process_payload( + async fn process_payload( &self, payload: &TPayload, unit_of_work: TUnitOfWork, @@ -63,7 +63,7 @@ impl impl PayloadProcessor for TariDanPayloadProcessor { - async fn process_payload( + async fn process_payload( &self, payload: &TariDanPayload, unit_of_work: TUnitOfWork, diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index 41bc9ca1de..df6ff5b8eb 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -49,6 +49,7 @@ mod store; pub trait DbFactory { fn create(&self) -> Result, StorageError>; + fn create_state_db(&self) -> Result; } pub enum UnitOfWorkTracker { @@ -106,46 +107,49 @@ pub trait UnitOfWork: Clone + Send + Sync { ) -> Result<(), StorageError>; } -pub struct StateDb { - unit_of_work: Option, +pub trait StateDbUnitOfWork: Clone + Send + Sync { + fn set_value(&mut self, schema: String, key: Vec, value: Vec) -> Result<(), StorageError>; + fn commit(&mut self) -> Result; } -impl StateDb { - pub fn new_unit_of_work(&mut self) -> &mut StateDbUnitOfWork { - self.unit_of_work = Some(StateDbUnitOfWork { child: None }); - self.unit_of_work.as_mut().unwrap() - // let mut unit_of_work = self.current_unit_of_work_mut(); - // if unit_of_work.is_none() { - // self.unit_of_work = Some(StateDbUnitOfWork {}); - // unit_of_work = self.unit_of_work - // }; - // unit_of_work.as_mut().unwrap() +pub struct StateRoot { + root: Vec, +} + +#[derive(Clone)] +pub struct StateDbUnitOfWorkImpl { + // hashmap rather? + updates: Vec<(String, Vec, Vec)>, +} + +impl StateDbUnitOfWork for StateDbUnitOfWorkImpl { + fn set_value(&mut self, schema: String, key: Vec, value: Vec) -> Result<(), StorageError> { + self.updates.push((schema, key, value)); + Ok(()) } - fn current_unit_of_work_mut(&mut self) -> Option<&mut StateDbUnitOfWork> { - unimplemented!() - // let mut result = self.unit_of_work.as_mut(); - // let mut child = result; - // while let Some(c) = child { - // result = child; - // child = c.child.as_mut(); - // } - // - // return result; + fn commit(&mut self) -> Result { + // todo!("actually commit") + Ok(StateRoot { + root: Vec::from([8u8; 32]), + }) } } -pub struct StateDbUnitOfWork { - child: Option>>, -} +pub struct StateDb {} -impl StateDbUnitOfWork { - pub fn new_unit_of_work(&mut self) -> &mut StateDbUnitOfWork { - // TODO: better implementation - self +impl StateDb { + pub fn new() -> Self { + Self {} } - pub fn commit(&mut self) -> Result<(), StorageError> { - Ok(()) + pub fn new_unit_of_work(&self) -> StateDbUnitOfWorkImpl { + StateDbUnitOfWorkImpl { updates: vec![] } + // let mut unit_of_work = self.current_unit_of_work_mut(); + // if unit_of_work.is_none() { + // self.unit_of_work = Some(StateDbUnitOfWork {}); + // unit_of_work = self.unit_of_work + // }; + // unit_of_work.as_mut().unwrap() } } diff --git a/dan_layer/core/src/workers/states/pre_commit_state.rs b/dan_layer/core/src/workers/states/pre_commit_state.rs index bf14fd87c3..37d1953895 100644 --- a/dan_layer/core/src/workers/states/pre_commit_state.rs +++ b/dan_layer/core/src/workers/states/pre_commit_state.rs @@ -29,6 +29,7 @@ use crate::{ HotStuffTreeNode, Payload, QuorumCertificate, + TreeNodeHash, View, ViewId, }, @@ -199,9 +200,9 @@ where let mut node = None; for message in self.received_new_view_messages.values() { node = match node { - None => message.node().cloned(), + None => message.node_hash().cloned(), Some(n) => { - if let Some(m_node) = message.node() { + if let Some(m_node) = message.node_hash() { if &n != m_node { unimplemented!("Nodes did not match"); } @@ -213,13 +214,12 @@ where }; } - todo!("Fix this") - // let node = node.unwrap(); - // let mut qc = QuorumCertificate::new(HotStuffMessageType::Prepare, current_view.view_id, node, None); - // for message in self.received_new_view_messages.values() { - // qc.combine_sig(message.partial_sig().unwrap()) - // } - // Some(qc) + let node = node.unwrap(); + let mut qc = QuorumCertificate::new(HotStuffMessageType::Prepare, current_view.view_id, node.clone(), None); + for message in self.received_new_view_messages.values() { + qc.combine_sig(message.partial_sig().unwrap()) + } + Some(qc) } async fn process_replica_message( @@ -250,16 +250,15 @@ where return Ok(None); } - todo!("Fix this"); - // self.prepare_qc = Some(justify.clone()); - // self.send_vote_to_leader( - // justify.node(), - // outbound, - // view_leader, - // current_view.view_id, - // signing_service, - // ) - // .await?; + self.prepare_qc = Some(justify.clone()); + self.send_vote_to_leader( + justify.node_hash().clone(), + outbound, + view_leader, + current_view.view_id, + signing_service, + ) + .await?; Ok(Some(ConsensusWorkerStateEvent::PreCommitted)) } else { // dbg!("received non justify message"); @@ -269,13 +268,13 @@ where async fn send_vote_to_leader( &self, - node: &HotStuffTreeNode, + node: TreeNodeHash, outbound: &mut TOutboundService, view_leader: &TAddr, view_number: ViewId, signing_service: &TSigningService, ) -> Result<(), DigitalAssetError> { - let mut message = HotStuffMessage::pre_commit(Some(node.clone()), None, view_number); + let mut message = HotStuffMessage::vote_pre_commit(node.clone(), view_number); message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); outbound.send(self.node_id.clone(), view_leader.clone(), message).await } diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index 12a0ddf47a..febcb13f9d 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -289,15 +289,21 @@ where unimplemented!("Node is not safe") } - let db = self.db_factory.create()?; + let db = self.db_factory.create_state_db()?; let unit_of_work = db.new_unit_of_work(); let res = payload_processor.process_payload(node.payload(), unit_of_work).await?; // TODO: Check result equals qc result - self.send_vote_to_leader(node, outbound, view_leader, current_view.view_id, signing_service) - .await?; + self.send_vote_to_leader( + node.hash().clone(), + outbound, + view_leader, + current_view.view_id, + signing_service, + ) + .await?; Ok(Some(ConsensusWorkerStateEvent::Prepared)) } else { unimplemented!("Did not extend from qc.justify.node") @@ -364,13 +370,14 @@ where async fn send_vote_to_leader( &self, - node: &HotStuffTreeNode, + node: TreeNodeHash, outbound: &mut TOutboundService, view_leader: &TAddr, view_number: ViewId, signing_service: &TSigningService, ) -> Result<(), DigitalAssetError> { - let mut message = HotStuffMessage::prepare(node.clone(), None, view_number); + // TODO: Only send node hash, not the full node + let mut message = HotStuffMessage::vote_prepare(node, view_number); message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); outbound.send(self.node_id.clone(), view_leader.clone(), message).await } diff --git a/dan_layer/core/src/workers/states/starting.rs b/dan_layer/core/src/workers/states/starting.rs index 16528187d6..00b4952a91 100644 --- a/dan_layer/core/src/workers/states/starting.rs +++ b/dan_layer/core/src/workers/states/starting.rs @@ -30,7 +30,7 @@ use crate::{ PayloadProcessor, PayloadProvider, }, - storage::{BackendAdapter, ChainStorageService, DbFactory, UnitOfWork}, + storage::{BackendAdapter, ChainStorageService, DbFactory, StateDbUnitOfWork, UnitOfWork}, workers::states::ConsensusWorkerStateEvent, }; use log::*; @@ -102,15 +102,20 @@ where TBaseNodeClient: BaseNodeClient if chain_db.is_empty()? { let mut tx = chain_db.new_unit_of_work(); - let tx2 = tx.clone(); - // let metadata = chain_db.metadata.read(&mut tx); - let payload = payload_provider.create_genesis_payload(); + let state_db = db_factory.create_state_db()?; + let mut state_tx = state_db.new_unit_of_work(); - payload_processor.process_payload(&payload, tx2).await?; - let node = HotStuffTreeNode::genesis(payload); + let initial_state = asset_definition.initial_state(); + for schema in &initial_state.schemas { + for key_value in &schema.items { + state_tx.set_value(schema.name.clone(), key_value.key.clone(), key_value.value.clone()); + } + } + let node = HotStuffTreeNode::genesis(payload_provider.create_genesis_payload()); let genesis_qc = QuorumCertificate::genesis(node.hash().clone()); chain_storage_service.save_node(&node, tx.clone()).await?; chain_storage_service.set_locked_qc(genesis_qc, tx.clone()).await?; + state_tx.commit()?; tx.commit()?; } diff --git a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql index dcb2f448ec..57f98f6820 100644 --- a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql +++ b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql @@ -28,4 +28,12 @@ create table prepare_qcs ( view_number bigint not null, node_hash blob not null, signature blob null -) +); + + +create table state_key_values ( + id integer primary key autoincrement not null, + schema_name text not null, + key blob not null, + value blob not null +); \ No newline at end of file diff --git a/dan_layer/storage_sqlite/src/models/mod.rs b/dan_layer/storage_sqlite/src/models/mod.rs index 05f07df566..4402b7192c 100644 --- a/dan_layer/storage_sqlite/src/models/mod.rs +++ b/dan_layer/storage_sqlite/src/models/mod.rs @@ -23,3 +23,4 @@ pub mod locked_qc; pub mod node; pub mod prepare_qc; +pub mod state_key_value; diff --git a/dan_layer/storage_sqlite/src/models/state_key_value.rs b/dan_layer/storage_sqlite/src/models/state_key_value.rs new file mode 100644 index 0000000000..74bbfdbabc --- /dev/null +++ b/dan_layer/storage_sqlite/src/models/state_key_value.rs @@ -0,0 +1,39 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::schema::*; + +#[derive(Queryable)] +pub struct StateKeyValue { + pub id: i32, + pub schema_name: String, + pub key: Vec, + pub value: Vec, +} + +#[derive(Insertable)] +#[table_name = "state_key_values"] +pub struct NewStateKeyValue { + pub schema_name: String, + pub key: Vec, + pub value: Vec, +} diff --git a/dan_layer/storage_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs index 0b784cb6bc..c2f4da6884 100644 --- a/dan_layer/storage_sqlite/src/schema.rs +++ b/dan_layer/storage_sqlite/src/schema.rs @@ -37,9 +37,19 @@ table! { } } +table! { + state_key_values (id) { + id -> Integer, + schema_name -> Text, + key -> Binary, + value -> Binary, + } +} + allow_tables_to_appear_in_same_query!( instructions, locked_qc, nodes, prepare_qcs, + state_key_values, ); diff --git a/dan_layer/storage_sqlite/src/sqlite_db_factory.rs b/dan_layer/storage_sqlite/src/sqlite_db_factory.rs index 84785f7206..cc5f5c654f 100644 --- a/dan_layer/storage_sqlite/src/sqlite_db_factory.rs +++ b/dan_layer/storage_sqlite/src/sqlite_db_factory.rs @@ -24,7 +24,7 @@ use crate::{error::SqliteStorageError, SqliteBackendAdapter}; use diesel::{prelude::*, Connection, SqliteConnection}; use diesel_migrations::embed_migrations; use tari_common::GlobalConfig; -use tari_dan_core::storage::{ChainDb, DbFactory, StorageError}; +use tari_dan_core::storage::{ChainDb, DbFactory, StateDb, StorageError}; #[derive(Clone)] pub struct SqliteDbFactory { @@ -57,4 +57,8 @@ impl DbFactory for SqliteDbFactory { embedded_migrations::run(&connection).map_err(SqliteStorageError::from)?; Ok(ChainDb::new(self.create_adapter())) } + + fn create_state_db(&self) -> Result { + Ok(StateDb::new()) + } } From 7f44b7e1f72a3e0cc8df4a1b272c8d810f05a2ca Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Mon, 8 Nov 2021 12:00:43 +0200 Subject: [PATCH 07/14] wip --- dan_layer/core/src/storage/chain_db.rs | 12 ++++++ dan_layer/core/src/storage/mod.rs | 23 +++++++--- .../core/src/workers/consensus_worker.rs | 16 +++---- .../src/workers/states/pre_commit_state.rs | 19 +++++---- .../2021-11-02-185150_create_nodes/up.sql | 4 +- dan_layer/storage_sqlite/src/schema.rs | 4 +- .../src/sqlite_backend_adapter.rs | 42 ++++++++++++++++++- .../storage_sqlite/src/sqlite_db_factory.rs | 4 +- 8 files changed, 95 insertions(+), 29 deletions(-) diff --git a/dan_layer/core/src/storage/chain_db.rs b/dan_layer/core/src/storage/chain_db.rs index d2f22cc99d..edcb001896 100644 --- a/dan_layer/core/src/storage/chain_db.rs +++ b/dan_layer/core/src/storage/chain_db.rs @@ -157,4 +157,16 @@ impl UnitOfWork for ChainDbUnitOfWork Result<(), StorageError> { + let mut inner = self.inner.write().unwrap(); + let id = inner.backend_adapter.prepare_qc_id(); + inner.dirty_items.push((id, UnitOfWorkTracker::PrepareQc { + message_type: qc.message_type(), + view_number: qc.view_number(), + node_hash: qc.node_hash().clone(), + signature: qc.signature().cloned(), + })); + Ok(()) + } } diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index df6ff5b8eb..861c5bfbf7 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -40,6 +40,8 @@ pub use store::{AssetDataStore, AssetStore}; mod chain_db; pub use chain_db::{ChainDb, ChainDbUnitOfWork}; +use std::marker::PhantomData; + mod chain_storage_service; mod error; pub mod lmdb; @@ -49,7 +51,7 @@ mod store; pub trait DbFactory { fn create(&self) -> Result, StorageError>; - fn create_state_db(&self) -> Result; + fn create_state_db(&self) -> Result, StorageError>; } pub enum UnitOfWorkTracker { @@ -60,6 +62,12 @@ pub enum UnitOfWorkTracker { node_hash: TreeNodeHash, signature: Option, }, + PrepareQc { + message_type: HotStuffMessageType, + view_number: ViewId, + node_hash: TreeNodeHash, + signature: Option, + }, } pub enum NewUnitOfWorkTracker { @@ -90,6 +98,7 @@ pub trait BackendAdapter: Send + Sync + Clone { ) -> Result<(), Self::Error>; fn commit(&self, transaction: &Self::BackendTransaction) -> Result<(), Self::Error>; fn locked_qc_id(&self) -> Self::Id; + fn prepare_qc_id(&self) -> Self::Id; fn find_highest_prepared_qc(&self) -> Result; fn get_locked_qc(&self) -> Result; } @@ -105,9 +114,11 @@ pub trait UnitOfWork: Clone + Send + Sync { node_hash: TreeNodeHash, signature: Option, ) -> Result<(), StorageError>; + + fn set_prepare_qc(&mut self, qc: &QuorumCertificate) -> Result<(), StorageError>; } -pub trait StateDbUnitOfWork: Clone + Send + Sync { +pub trait StateDbUnitOfWork: Clone + Sized + Send + Sync { fn set_value(&mut self, schema: String, key: Vec, value: Vec) -> Result<(), StorageError>; fn commit(&mut self) -> Result; } @@ -136,11 +147,13 @@ impl StateDbUnitOfWork for StateDbUnitOfWorkImpl { } } -pub struct StateDb {} +pub struct StateDb { + pd: PhantomData, +} -impl StateDb { +impl StateDb { pub fn new() -> Self { - Self {} + Self { pd: Default::default() } } pub fn new_unit_of_work(&self) -> StateDbUnitOfWorkImpl { diff --git a/dan_layer/core/src/workers/consensus_worker.rs b/dan_layer/core/src/workers/consensus_worker.rs index 730aba7bc4..aa69307a3d 100644 --- a/dan_layer/core/src/workers/consensus_worker.rs +++ b/dan_layer/core/src/workers/consensus_worker.rs @@ -40,7 +40,7 @@ use crate::{ PayloadProvider, SigningService, }, - storage::{BackendAdapter, ChainStorageService, DbFactory}, + storage::{BackendAdapter, ChainStorageService, DbFactory, StateDbUnitOfWork, StateDbUnitOfWorkImpl, UnitOfWork}, workers::{states, states::ConsensusWorkerStateEvent}, }; use log::*; @@ -96,6 +96,8 @@ pub struct ConsensusWorker< chain_storage_service: TChainStorageService, pd: PhantomData, pd2: PhantomData, + // TODO: Make generic + state_db_unit_of_work: Option, } impl< @@ -138,7 +140,6 @@ where TSigningService: SigningService, TPayloadProcessor: PayloadProcessor, TCommitteeManager: CommitteeManager, - TBaseNodeClient: BaseNodeClient, // TODO: REmove this Send TBackendAdapter: BackendAdapter + Send + Sync, @@ -181,6 +182,7 @@ where chain_storage_service, pd: PhantomData, pd2: PhantomData, + state_db_unit_of_work: None, } } @@ -266,23 +268,23 @@ where .await }, PreCommit => { + let db = self.db_factory.create()?; + let mut unit_of_work = db.new_unit_of_work(); let mut state = states::PreCommitState::new( self.node_id.clone(), self.committee_manager.current_committee()?.clone(), ); - let (res, prepare_qc) = state + let res = state .next_event( self.timeout, &self.get_current_view()?, &mut self.inbound_connections, &mut self.outbound_service, &self.signing_service, + unit_of_work.clone(), ) .await?; - if let Some(prepare_qc) = prepare_qc { - todo!("Fix this? save to db?"); - // self.prepare_qc = Arc::new(prepare_qc); - } + unit_of_work.commit()?; Ok(res) }, diff --git a/dan_layer/core/src/workers/states/pre_commit_state.rs b/dan_layer/core/src/workers/states/pre_commit_state.rs index 37d1953895..4c9bccbe1d 100644 --- a/dan_layer/core/src/workers/states/pre_commit_state.rs +++ b/dan_layer/core/src/workers/states/pre_commit_state.rs @@ -37,6 +37,7 @@ use crate::{ infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, SigningService, }, + storage::UnitOfWork, workers::states::ConsensusWorkerStateEvent, }; use std::{collections::HashMap, marker::PhantomData, time::Instant}; @@ -58,7 +59,6 @@ where p_p: PhantomData, p_s: PhantomData, received_new_view_messages: HashMap>, - prepare_qc: Option, } impl @@ -79,19 +79,19 @@ where ta: PhantomData, p_p: PhantomData, received_new_view_messages: HashMap::new(), - prepare_qc: None, p_s: PhantomData, } } - pub async fn next_event( + pub async fn next_event( &mut self, timeout: Duration, current_view: &View, inbound_services: &mut TInboundConnectionService, outbound_service: &mut TOutboundService, signing_service: &TSigningService, - ) -> Result<(ConsensusWorkerStateEvent, Option), DigitalAssetError> { + unit_of_work: TUnitOfWork, + ) -> Result { let mut next_event_result = ConsensusWorkerStateEvent::Errored { reason: "loop ended without setting this event".to_string(), }; @@ -99,6 +99,7 @@ where self.received_new_view_messages.clear(); let started = Instant::now(); + let mut unit_of_work = unit_of_work; loop { tokio::select! { (from, message) = self.wait_for_message(inbound_services) => { @@ -111,7 +112,7 @@ where } let leader= self.committee.leader_for_view(current_view.view_id).clone(); - if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader, outbound_service, signing_service).await? { + if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader, outbound_service, signing_service, &mut unit_of_work).await? { next_event_result = result; break; } @@ -123,7 +124,7 @@ where } } } - Ok((next_event_result, self.prepare_qc.clone())) + Ok(next_event_result) } async fn wait_for_message( @@ -159,7 +160,6 @@ where ); if let Some(qc) = self.create_qc(current_view) { - self.prepare_qc = Some(qc.clone()); self.broadcast(outbound, &self.committee, qc, current_view.view_id) .await?; // return Ok(Some(ConsensusWorkerStateEvent::PreCommitted)); @@ -222,7 +222,7 @@ where Some(qc) } - async fn process_replica_message( + async fn process_replica_message( &mut self, message: &HotStuffMessage, current_view: &View, @@ -230,6 +230,7 @@ where view_leader: &TAddr, outbound: &mut TOutboundService, signing_service: &TSigningService, + unit_of_work: &mut TUnitOfWork, ) -> Result, DigitalAssetError> { if let Some(justify) = message.justify() { if !justify.matches(HotStuffMessageType::Prepare, current_view.view_id) { @@ -250,7 +251,7 @@ where return Ok(None); } - self.prepare_qc = Some(justify.clone()); + unit_of_work.set_prepare_qc(justify); self.send_vote_to_leader( justify.node_hash().clone(), outbound, diff --git a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql index 57f98f6820..ebb738756b 100644 --- a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql +++ b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql @@ -22,8 +22,8 @@ create table locked_qc ( signature blob null ); -create table prepare_qcs ( - id integer primary key autoincrement not null, +create table prepare_qc ( + id integer primary key not null, message_type integer not null, view_number bigint not null, node_hash blob not null, diff --git a/dan_layer/storage_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs index c2f4da6884..455469e525 100644 --- a/dan_layer/storage_sqlite/src/schema.rs +++ b/dan_layer/storage_sqlite/src/schema.rs @@ -28,7 +28,7 @@ table! { } table! { - prepare_qcs (id) { + prepare_qc (id) { id -> Integer, message_type -> Integer, view_number -> BigInt, @@ -50,6 +50,6 @@ allow_tables_to_appear_in_same_query!( instructions, locked_qc, nodes, - prepare_qcs, + prepare_qc, state_key_values, ); diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs index ac6e4f3cfc..8a0dc7f84e 100644 --- a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -130,6 +130,39 @@ impl BackendAdapter for SqliteBackendAdapter { }, } }, + UnitOfWorkTracker::PrepareQc { + message_type, + view_number, + node_hash, + signature, + } => { + use crate::schema::prepare_qc::dsl; + let message_type = message_type.as_u8() as i32; + let existing: Result = dsl::prepare_qc.find(id).first(transaction.connection()); + match existing { + Ok(x) => { + diesel::update(dsl::prepare_qc.find(id)) + .set(( + dsl::message_type.eq(message_type), + dsl::view_number.eq(view_number.0 as i64), + dsl::node_hash.eq(node_hash.as_bytes()), + dsl::signature.eq(signature.as_ref().map(|s| s.to_bytes())), + )) + .execute(transaction.connection())?; + }, + Err(_) => { + diesel::insert_into(prepare_qc::table) + .values(( + dsl::id.eq(id), + dsl::message_type.eq(message_type), + dsl::view_number.eq(view_number.0 as i64), + dsl::node_hash.eq(node_hash.as_bytes()), + dsl::signature.eq(signature.as_ref().map(|s| s.to_bytes())), + )) + .execute(transaction.connection())?; + }, + } + }, } Ok(()) } @@ -143,11 +176,16 @@ impl BackendAdapter for SqliteBackendAdapter { 1 } + fn prepare_qc_id(&self) -> Self::Id { + 1 + } + fn find_highest_prepared_qc(&self) -> Result { use crate::schema::*; let connection = SqliteConnection::establish(self.database_url.as_str())?; - let result: Option = prepare_qcs::table - .order_by(prepare_qcs::view_number.desc()) + // TODO: this should be a single row + let result: Option = prepare_qc::table + .order_by(prepare_qc::view_number.desc()) .first(&connection) .optional()?; let qc = match result { diff --git a/dan_layer/storage_sqlite/src/sqlite_db_factory.rs b/dan_layer/storage_sqlite/src/sqlite_db_factory.rs index cc5f5c654f..c72d44e785 100644 --- a/dan_layer/storage_sqlite/src/sqlite_db_factory.rs +++ b/dan_layer/storage_sqlite/src/sqlite_db_factory.rs @@ -24,7 +24,7 @@ use crate::{error::SqliteStorageError, SqliteBackendAdapter}; use diesel::{prelude::*, Connection, SqliteConnection}; use diesel_migrations::embed_migrations; use tari_common::GlobalConfig; -use tari_dan_core::storage::{ChainDb, DbFactory, StateDb, StorageError}; +use tari_dan_core::storage::{ChainDb, DbFactory, StateDb, StateDbUnitOfWorkImpl, StorageError}; #[derive(Clone)] pub struct SqliteDbFactory { @@ -58,7 +58,7 @@ impl DbFactory for SqliteDbFactory { Ok(ChainDb::new(self.create_adapter())) } - fn create_state_db(&self) -> Result { + fn create_state_db(&self) -> Result, StorageError> { Ok(StateDb::new()) } } From 12e47a119219cc7a1e458a24af0590d65006942a Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Mon, 8 Nov 2021 17:35:47 +0200 Subject: [PATCH 08/14] wip --- .../proto/p2p/dan_consensus.proto | 1 + .../src/p2p/proto/conversions/dan.rs | 2 + dan_layer/core/src/digital_assets_error.rs | 2 + .../core/src/models/hot_stuff_message.rs | 11 ++ .../core/src/models/hot_stuff_tree_node.rs | 14 +- dan_layer/core/src/models/mod.rs | 35 +++-- dan_layer/core/src/models/state_root.rs | 28 ++++ dan_layer/core/src/models/tari_dan_payload.rs | 8 +- .../core/src/services/payload_processor.rs | 18 +-- dan_layer/core/src/storage/chain_db.rs | 133 ++++++++++++++++-- .../core/src/storage/chain_storage_service.rs | 2 +- dan_layer/core/src/storage/error.rs | 4 + dan_layer/core/src/storage/mod.rs | 41 +++--- .../core/src/workers/consensus_worker.rs | 69 ++++++--- .../core/src/workers/states/commit_state.rs | 72 ++++------ .../core/src/workers/states/decide_state.rs | 71 +++------- .../core/src/workers/states/next_view.rs | 1 - dan_layer/core/src/workers/states/prepare.rs | 82 +++++++---- dan_layer/core/src/workers/states/starting.rs | 3 +- .../2021-11-02-185150_create_nodes/up.sql | 14 +- .../storage_sqlite/src/models/instruction.rs | 42 ++++++ dan_layer/storage_sqlite/src/models/mod.rs | 1 + dan_layer/storage_sqlite/src/models/node.rs | 8 +- dan_layer/storage_sqlite/src/schema.rs | 10 +- .../src/sqlite_backend_adapter.rs | 31 +++- .../src/sqlite_storage_service.rs | 11 +- 26 files changed, 487 insertions(+), 227 deletions(-) create mode 100644 dan_layer/core/src/models/state_root.rs create mode 100644 dan_layer/storage_sqlite/src/models/instruction.rs diff --git a/applications/tari_validator_node/proto/p2p/dan_consensus.proto b/applications/tari_validator_node/proto/p2p/dan_consensus.proto index 0e1c58fd14..2eeca051a1 100644 --- a/applications/tari_validator_node/proto/p2p/dan_consensus.proto +++ b/applications/tari_validator_node/proto/p2p/dan_consensus.proto @@ -31,6 +31,7 @@ message QuorumCertificate { message HotStuffTreeNode { bytes parent = 1; TariDanPayload payload = 2; + uint32 height = 3; } message Signature{ diff --git a/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs b/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs index 66e7f915a6..9b137040a4 100644 --- a/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs +++ b/applications/tari_validator_node/src/p2p/proto/conversions/dan.rs @@ -59,6 +59,7 @@ impl From> for dan_proto::HotStuffTreeNode { Self { parent: Vec::from(source.parent().as_bytes()), payload: Some(source.payload().clone().into()), + height: source.height(), } } } @@ -156,6 +157,7 @@ impl TryFrom for HotStuffTreeNode { .map(|p| p.try_into()) .transpose()? .ok_or_else(|| "payload not provided".to_string())?, + value.height, )) } } diff --git a/dan_layer/core/src/digital_assets_error.rs b/dan_layer/core/src/digital_assets_error.rs index 816a90d436..079fe2f759 100644 --- a/dan_layer/core/src/digital_assets_error.rs +++ b/dan_layer/core/src/digital_assets_error.rs @@ -38,6 +38,8 @@ pub enum DigitalAssetError { MalformedMetadata(String), #[error("Could not convert between types:{0}")] ConversionError(String), + #[error("Branched to an unexpected logic path, this is most likely due to a bug:{reason}")] + InvalidLogicPath { reason: String }, } impl From for DigitalAssetError { diff --git a/dan_layer/core/src/models/hot_stuff_message.rs b/dan_layer/core/src/models/hot_stuff_message.rs index bfe7d25e3c..eab335b624 100644 --- a/dan_layer/core/src/models/hot_stuff_message.rs +++ b/dan_layer/core/src/models/hot_stuff_message.rs @@ -140,6 +140,17 @@ impl HotStuffMessage { } } + pub fn vote_commit(node_hash: TreeNodeHash, view_number: ViewId) -> Self { + Self { + message_type: HotStuffMessageType::Commit, + node_hash: Some(node_hash), + view_number, + node: None, + partial_sig: None, + justify: None, + } + } + pub fn decide( node: Option>, commit_qc: Option, diff --git a/dan_layer/core/src/models/hot_stuff_tree_node.rs b/dan_layer/core/src/models/hot_stuff_tree_node.rs index cf3ca03395..24180d00a9 100644 --- a/dan_layer/core/src/models/hot_stuff_tree_node.rs +++ b/dan_layer/core/src/models/hot_stuff_tree_node.rs @@ -29,14 +29,16 @@ pub struct HotStuffTreeNode { parent: TreeNodeHash, payload: TPayload, hash: TreeNodeHash, + height: u32, } impl HotStuffTreeNode { - pub fn new(parent: TreeNodeHash, payload: TPayload) -> Self { + pub fn new(parent: TreeNodeHash, payload: TPayload, height: u32) -> Self { let mut s = HotStuffTreeNode { parent, payload, hash: TreeNodeHash(vec![]), + height, }; s.hash = s.calculate_hash(); s @@ -47,19 +49,21 @@ impl HotStuffTreeNode { parent: TreeNodeHash(vec![0u8; 32]), payload, hash: TreeNodeHash(vec![]), + height: 0, }; s.hash = s.calculate_hash(); s } - pub fn from_parent(parent: TreeNodeHash, payload: TPayload) -> HotStuffTreeNode { - Self::new(parent, payload) + pub fn from_parent(parent: TreeNodeHash, payload: TPayload, height: u32) -> HotStuffTreeNode { + Self::new(parent, payload, height) } pub fn calculate_hash(&self) -> TreeNodeHash { let result = Blake256::new() .chain(self.parent.0.as_slice()) .chain(self.payload.consensus_hash()) + .chain(self.height.to_le_bytes()) .finalize(); TreeNodeHash(result.to_vec()) } @@ -75,6 +79,10 @@ impl HotStuffTreeNode { pub fn payload(&self) -> &TPayload { &self.payload } + + pub fn height(&self) -> u32 { + self.height + } } impl PartialEq for HotStuffTreeNode { diff --git a/dan_layer/core/src/models/mod.rs b/dan_layer/core/src/models/mod.rs index ca45545952..63187a2467 100644 --- a/dan_layer/core/src/models/mod.rs +++ b/dan_layer/core/src/models/mod.rs @@ -21,8 +21,9 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::{convert::TryFrom, fmt::Debug, hash::Hash}; -// mod block; mod asset_definition; +mod base_layer_metadata; +mod base_layer_output; mod committee; pub mod domain_events; mod hot_stuff_message; @@ -30,27 +31,23 @@ mod hot_stuff_tree_node; mod instruction; mod instruction_set; mod quorum_certificate; -// mod replica_info; -mod base_layer_metadata; -mod base_layer_output; mod sidechain_metadata; +mod state_root; mod tari_dan_payload; mod view; mod view_id; - -// pub use block::Block; +pub use asset_definition::AssetDefinition; +pub use base_layer_metadata::BaseLayerMetadata; +pub use base_layer_output::BaseLayerOutput; +use blake2::Digest; pub use committee::Committee; pub use hot_stuff_message::HotStuffMessage; pub use hot_stuff_tree_node::HotStuffTreeNode; pub use instruction::Instruction; pub use instruction_set::InstructionSet; pub use quorum_certificate::QuorumCertificate; -// pub use replica_info::ReplicaInfo; -pub use asset_definition::AssetDefinition; -pub use base_layer_metadata::BaseLayerMetadata; -pub use base_layer_output::BaseLayerOutput; -use blake2::Digest; pub use sidechain_metadata::SidechainMetadata; +pub use state_root::StateRoot; use tari_crypto::common::Blake256; pub use tari_dan_payload::{CheckpointData, TariDanPayload}; pub use view::View; @@ -180,10 +177,20 @@ impl ConsensusHash for String { } // TODO: Perhaps should be CoW instead of Clone -pub trait Payload: Debug + Clone + Send + Sync + ConsensusHash {} +pub trait Payload: Debug + Clone + Send + Sync + ConsensusHash { + fn state_root(&self) -> StateRoot; +} -impl Payload for &str {} -impl Payload for String {} +impl Payload for &str { + fn state_root(&self) -> StateRoot { + StateRoot::default() + } +} +impl Payload for String { + fn state_root(&self) -> StateRoot { + StateRoot::default() + } +} pub trait Event: Clone + Send + Sync {} diff --git a/dan_layer/core/src/models/state_root.rs b/dan_layer/core/src/models/state_root.rs new file mode 100644 index 0000000000..e28b744f94 --- /dev/null +++ b/dan_layer/core/src/models/state_root.rs @@ -0,0 +1,28 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[derive(Default, PartialEq)] +pub struct StateRoot { + root: Vec, +} + +impl StateRoot {} diff --git a/dan_layer/core/src/models/tari_dan_payload.rs b/dan_layer/core/src/models/tari_dan_payload.rs index 7914b1a8c8..f40c330ab8 100644 --- a/dan_layer/core/src/models/tari_dan_payload.rs +++ b/dan_layer/core/src/models/tari_dan_payload.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::models::{ConsensusHash, Instruction, InstructionSet, Payload}; +use crate::models::{ConsensusHash, Instruction, InstructionSet, Payload, StateRoot}; use digest::Digest; use std::{ fmt::{Debug, Formatter}, @@ -70,7 +70,11 @@ impl ConsensusHash for TariDanPayload { } } -impl Payload for TariDanPayload {} +impl Payload for TariDanPayload { + fn state_root(&self) -> StateRoot { + StateRoot::default() + } +} #[derive(Debug, Clone)] pub struct CheckpointData { diff --git a/dan_layer/core/src/services/payload_processor.rs b/dan_layer/core/src/services/payload_processor.rs index ae9d3be7f6..55de06a64e 100644 --- a/dan_layer/core/src/services/payload_processor.rs +++ b/dan_layer/core/src/services/payload_processor.rs @@ -22,7 +22,7 @@ use crate::{ digital_assets_error::DigitalAssetError, - models::{Payload, TariDanPayload}, + models::{Payload, StateRoot, TariDanPayload}, services::{AssetProcessor, MempoolService}, storage::{ChainDb, ChainDbUnitOfWork, DbFactory, StateDbUnitOfWork, UnitOfWork}, }; @@ -36,7 +36,7 @@ pub trait PayloadProcessor { &self, payload: &TPayload, unit_of_work: TUnitOfWork, - ) -> Result<(), DigitalAssetError>; + ) -> Result; } pub struct TariDanPayloadProcessor @@ -66,22 +66,16 @@ impl( &self, payload: &TariDanPayload, - unit_of_work: TUnitOfWork, - ) -> Result<(), DigitalAssetError> { - // let mut unit_of_work = db.new_unit_of_work(); + state_tx: TUnitOfWork, + ) -> Result { for instruction in payload.instructions() { dbg!("Executing instruction"); dbg!(&instruction); // TODO: Should we swallow + log the error instead of propagating it? self.asset_processor - .execute_instruction(instruction, unit_of_work.clone())?; + .execute_instruction(instruction, state_tx.clone())?; } - // self.mempool_service.remove_instructions(payload.instructions()).await?; - - // TODO: Remove this....Unit of work should actually be committed only at COMMIT stage - // unit_of_work.commit()?; - - Ok(()) + Ok(state_tx.calculate_root()?) } } diff --git a/dan_layer/core/src/storage/chain_db.rs b/dan_layer/core/src/storage/chain_db.rs index edcb001896..08ea76f6bd 100644 --- a/dan_layer/core/src/storage/chain_db.rs +++ b/dan_layer/core/src/storage/chain_db.rs @@ -21,7 +21,16 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - models::{HotStuffMessageType, Instruction, Payload, QuorumCertificate, Signature, TreeNodeHash, ViewId}, + models::{ + HotStuffMessageType, + HotStuffTreeNode, + Instruction, + Payload, + QuorumCertificate, + Signature, + TreeNodeHash, + ViewId, + }, storage::{BackendAdapter, NewUnitOfWorkTracker, StorageError, UnitOfWork, UnitOfWorkTracker}, }; use std::sync::{Arc, RwLock}; @@ -64,6 +73,42 @@ pub struct ChainDbUnitOfWork { inner: Arc>>, } +impl ChainDbUnitOfWork { + fn find_proposed_node( + &mut self, + node_hash: &TreeNodeHash, + ) -> Result<(TBackendAdapter::Id, UnitOfWorkTracker), StorageError> { + let mut inner = self.inner.write().unwrap(); + for (clean_id, clean_item) in &inner.clean_items { + match clean_item { + UnitOfWorkTracker::Node { hash, .. } => { + if hash == node_hash { + return Ok((*clean_id, clean_item.clone())); + } + }, + _ => (), + }; + } + for (dirty_id, dirty_item) in &inner.dirty_items { + match dirty_item { + UnitOfWorkTracker::Node { hash, .. } => { + if hash == node_hash { + return Ok((*dirty_id, dirty_item.clone())); + } + }, + _ => (), + }; + } + // finally hit the db + let (id, item) = inner + .backend_adapter + .find_node_by_hash(node_hash) + .map_err(TBackendAdapter::Error::into)?; + inner.clean_items.push((id, item.clone())); + Ok((id, item)) + } +} + pub struct ChainDbUnitOfWorkInner { backend_adapter: TBackendAdapter, clean_items: Vec<(TBackendAdapter::Id, UnitOfWorkTracker)>, @@ -122,12 +167,12 @@ impl UnitOfWork for ChainDbUnitOfWork Result<(), StorageError> { + fn add_node(&mut self, hash: TreeNodeHash, parent: TreeNodeHash, height: u32) -> Result<(), StorageError> { self.inner .write() .unwrap() .new_items - .push(NewUnitOfWorkTracker::Node { hash, parent }); + .push(NewUnitOfWorkTracker::Node { hash, parent, height }); Ok(()) } @@ -140,20 +185,69 @@ impl UnitOfWork for ChainDbUnitOfWork, - ) -> Result<(), StorageError> { + fn get_locked_qc(&mut self) -> Result { + let mut inner = self.inner.write().unwrap(); + let id = inner.backend_adapter.locked_qc_id(); + + for (_, clean_item) in &inner.clean_items { + match clean_item { + UnitOfWorkTracker::LockedQc { + message_type, + view_number, + node_hash, + signature, + } => { + return Ok(QuorumCertificate::new( + message_type.clone(), + view_number.clone(), + node_hash.clone(), + signature.clone(), + )); + }, + _ => (), + }; + } + for (_, dirty_item) in &inner.dirty_items { + match dirty_item { + UnitOfWorkTracker::LockedQc { + message_type, + view_number, + node_hash, + signature, + } => { + return Ok(QuorumCertificate::new( + message_type.clone(), + view_number.clone(), + node_hash.clone(), + signature.clone(), + )); + }, + _ => (), + }; + } + // finally hit the db + let qc = inner + .backend_adapter + .get_locked_qc() + .map_err(TBackendAdapter::Error::into)?; + let id = inner.backend_adapter.locked_qc_id(); + inner.clean_items.push((id, UnitOfWorkTracker::LockedQc { + message_type: qc.message_type().clone(), + view_number: qc.view_number().clone(), + node_hash: qc.node_hash().clone(), + signature: qc.signature().cloned(), + })); + Ok(qc) + } + + fn set_locked_qc(&mut self, qc: &QuorumCertificate) -> Result<(), StorageError> { let mut inner = self.inner.write().unwrap(); let id = inner.backend_adapter.locked_qc_id(); inner.dirty_items.push((id, UnitOfWorkTracker::LockedQc { - message_type, - view_number, - node_hash, - signature, + message_type: qc.message_type(), + view_number: qc.view_number(), + node_hash: qc.node_hash().clone(), + signature: qc.signature().cloned(), })); Ok(()) } @@ -169,4 +263,15 @@ impl UnitOfWork for ChainDbUnitOfWork Result<(), StorageError> { + let (id, mut item) = self.find_proposed_node(node_hash)?; + let mut inner = self.inner.write().unwrap(); + match &mut item { + UnitOfWorkTracker::Node { is_committed, .. } => *is_committed = true, + _ => return Err(StorageError::InvalidUnitOfWorkTrackerType), + } + inner.dirty_items.push((id, item)); + Ok(()) + } } diff --git a/dan_layer/core/src/storage/chain_storage_service.rs b/dan_layer/core/src/storage/chain_storage_service.rs index a94e6cccb7..f4f7f9bdb2 100644 --- a/dan_layer/core/src/storage/chain_storage_service.rs +++ b/dan_layer/core/src/storage/chain_storage_service.rs @@ -33,7 +33,7 @@ use tokio::sync::RwLock; #[async_trait] pub trait ChainStorageService { async fn get_metadata(&self) -> Result; - async fn save_node( + async fn add_node( &self, node: &HotStuffTreeNode, db: TUnitOfWork, diff --git a/dan_layer/core/src/storage/error.rs b/dan_layer/core/src/storage/error.rs index eec7162efb..a235fad0ba 100644 --- a/dan_layer/core/src/storage/error.rs +++ b/dan_layer/core/src/storage/error.rs @@ -40,4 +40,8 @@ pub enum StorageError { QueryError { reason: String }, #[error("Migration error: {reason}")] MigrationError { reason: String }, + #[error("Invalid unit of work tracker type")] + InvalidUnitOfWorkTrackerType, + #[error("Item does not exist")] + NotFound, } diff --git a/dan_layer/core/src/storage/mod.rs b/dan_layer/core/src/storage/mod.rs index 861c5bfbf7..95962fe419 100644 --- a/dan_layer/core/src/storage/mod.rs +++ b/dan_layer/core/src/storage/mod.rs @@ -29,6 +29,7 @@ use crate::models::{ QuorumCertificate, SidechainMetadata, Signature, + StateRoot, TreeNodeHash, ViewId, }; @@ -54,6 +55,9 @@ pub trait DbFactory { fn create_state_db(&self) -> Result, StorageError>; } +// TODO: I don't really like the matches on this struct, so it would be better to have individual types, e.g. +// NodeDataTracker, QcDataTracker +#[derive(Clone)] pub enum UnitOfWorkTracker { SidechainMetadata, LockedQc { @@ -68,12 +72,19 @@ pub enum UnitOfWorkTracker { node_hash: TreeNodeHash, signature: Option, }, + Node { + hash: TreeNodeHash, + parent: TreeNodeHash, + height: u32, + is_committed: bool, + }, } pub enum NewUnitOfWorkTracker { Node { hash: TreeNodeHash, parent: TreeNodeHash, + height: u32, }, Instruction { instruction: Instruction, @@ -84,7 +95,7 @@ pub enum NewUnitOfWorkTracker { pub trait BackendAdapter: Send + Sync + Clone { type BackendTransaction; type Error: Into; - type Id: Send + Sync; + type Id: Copy + Send + Sync; type Payload: Payload; fn is_empty(&self) -> Result; @@ -101,30 +112,24 @@ pub trait BackendAdapter: Send + Sync + Clone { fn prepare_qc_id(&self) -> Self::Id; fn find_highest_prepared_qc(&self) -> Result; fn get_locked_qc(&self) -> Result; + fn find_node_by_hash(&self, node_hash: &TreeNodeHash) -> Result<(Self::Id, UnitOfWorkTracker), Self::Error>; } pub trait UnitOfWork: Clone + Send + Sync { fn commit(&mut self) -> Result<(), StorageError>; - fn add_node(&mut self, hash: TreeNodeHash, parent: TreeNodeHash) -> Result<(), StorageError>; + fn add_node(&mut self, hash: TreeNodeHash, parent: TreeNodeHash, height: u32) -> Result<(), StorageError>; fn add_instruction(&mut self, node_hash: TreeNodeHash, instruction: Instruction) -> Result<(), StorageError>; - fn set_locked_qc( - &mut self, - message_type: HotStuffMessageType, - view_number: ViewId, - node_hash: TreeNodeHash, - signature: Option, - ) -> Result<(), StorageError>; - + fn get_locked_qc(&mut self) -> Result; + fn set_locked_qc(&mut self, qc: &QuorumCertificate) -> Result<(), StorageError>; fn set_prepare_qc(&mut self, qc: &QuorumCertificate) -> Result<(), StorageError>; + fn commit_node(&mut self, node_hash: &TreeNodeHash) -> Result<(), StorageError>; + // fn find_proposed_node(&mut self, node_hash: TreeNodeHash) -> Result<(Self::Id, UnitOfWorkTracker), StorageError>; } pub trait StateDbUnitOfWork: Clone + Sized + Send + Sync { fn set_value(&mut self, schema: String, key: Vec, value: Vec) -> Result<(), StorageError>; fn commit(&mut self) -> Result; -} - -pub struct StateRoot { - root: Vec, + fn calculate_root(&self) -> Result; } #[derive(Clone)] @@ -141,9 +146,11 @@ impl StateDbUnitOfWork for StateDbUnitOfWorkImpl { fn commit(&mut self) -> Result { // todo!("actually commit") - Ok(StateRoot { - root: Vec::from([8u8; 32]), - }) + Ok(StateRoot::default()) + } + + fn calculate_root(&self) -> Result { + Ok(StateRoot::default()) } } diff --git a/dan_layer/core/src/workers/consensus_worker.rs b/dan_layer/core/src/workers/consensus_worker.rs index aa69307a3d..9ce1c176d3 100644 --- a/dan_layer/core/src/workers/consensus_worker.rs +++ b/dan_layer/core/src/workers/consensus_worker.rs @@ -252,20 +252,28 @@ where }, Prepare => { let db = self.db_factory.create()?; - let locked_qc = db.get_locked_qc()?; - - let mut p = states::Prepare::new(self.node_id.clone(), self.db_factory.clone(), locked_qc); - p.next_event( - &self.get_current_view()?, - self.timeout, - self.committee_manager.current_committee()?, - &mut self.inbound_connections, - &mut self.outbound_service, - &self.payload_provider, - &self.signing_service, - &mut self.payload_processor, - ) - .await + let mut unit_of_work = db.new_unit_of_work(); + let mut state_tx = self.db_factory.create_state_db()?.new_unit_of_work(); + let mut p = states::Prepare::new(self.node_id.clone(), self.db_factory.clone()); + let res = p + .next_event( + &self.get_current_view()?, + self.timeout, + self.committee_manager.current_committee()?, + &mut self.inbound_connections, + &mut self.outbound_service, + &self.payload_provider, + &self.signing_service, + &mut self.payload_processor, + &self.chain_storage_service, + unit_of_work.clone(), + &mut state_tx, + ) + .await?; + // Will only be committed in DECIDE + self.state_db_unit_of_work = Some(state_tx); + unit_of_work.commit()?; + Ok(res) }, PreCommit => { let db = self.db_factory.create()?; @@ -289,39 +297,55 @@ where }, Commit => { + let db = self.db_factory.create()?; + let mut unit_of_work = db.new_unit_of_work(); let mut state = states::CommitState::new( self.node_id.clone(), self.committee_manager.current_committee()?.clone(), ); - let (res, locked_qc) = state + let res = state .next_event( self.timeout, &self.get_current_view()?, &mut self.inbound_connections, &mut self.outbound_service, &self.signing_service, + unit_of_work.clone(), ) .await?; - if let Some(locked_qc) = locked_qc { - todo!("Save to db?"); - // self.locked_qc = Arc::new(locked_qc); - } + + unit_of_work.commit()?; + Ok(res) }, Decide => { + let db = self.db_factory.create()?; + let mut unit_of_work = db.new_unit_of_work(); let mut state = states::DecideState::new( self.node_id.clone(), self.committee_manager.current_committee()?.clone(), ); - state + let res = state .next_event( self.timeout, &self.get_current_view()?, &mut self.inbound_connections, &mut self.outbound_service, - &self.signing_service, + unit_of_work.clone(), ) - .await + .await?; + unit_of_work.commit()?; + if let Some(mut state_tx) = self.state_db_unit_of_work.take() { + state_tx.commit()?; + } else { + // technically impossible + error!(target: LOG_TARGET, "No state unit of work was present"); + return Err(DigitalAssetError::InvalidLogicPath { + reason: "Tried to commit state after DECIDE, but no state tx was present".to_string(), + }); + } + + Ok(res) }, NextView => { info!( @@ -329,6 +353,7 @@ where "Status: {} in mempool ", self.payload_provider.get_payload_queue().await, ); + self.state_db_unit_of_work = None; let mut state = states::NextViewState::new(); state .next_event( diff --git a/dan_layer/core/src/workers/states/commit_state.rs b/dan_layer/core/src/workers/states/commit_state.rs index 5e216faed7..5ef0ef9322 100644 --- a/dan_layer/core/src/workers/states/commit_state.rs +++ b/dan_layer/core/src/workers/states/commit_state.rs @@ -29,6 +29,7 @@ use crate::{ HotStuffTreeNode, Payload, QuorumCertificate, + TreeNodeHash, View, ViewId, }, @@ -36,6 +37,7 @@ use crate::{ infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, SigningService, }, + storage::UnitOfWork, workers::states::ConsensusWorkerStateEvent, }; use std::{collections::HashMap, marker::PhantomData, time::Instant}; @@ -58,8 +60,6 @@ where p_p: PhantomData, p_s: PhantomData, received_new_view_messages: HashMap>, - pre_commit_qc: Option, - locked_qc: Option, } impl @@ -80,20 +80,19 @@ where ta: PhantomData, p_p: PhantomData, received_new_view_messages: HashMap::new(), - pre_commit_qc: None, - locked_qc: None, p_s: PhantomData, } } - pub async fn next_event( + pub async fn next_event( &mut self, timeout: Duration, current_view: &View, inbound_services: &mut TInboundConnectionService, outbound_service: &mut TOutboundService, signing_service: &TSigningService, - ) -> Result<(ConsensusWorkerStateEvent, Option), DigitalAssetError> { + unit_of_work: TUnitOfWork, + ) -> Result { let mut next_event_result = ConsensusWorkerStateEvent::Errored { reason: "loop ended without setting this event".to_string(), }; @@ -102,6 +101,7 @@ where self.received_new_view_messages.clear(); // TODO: rather change the loop below to inside the wait for message let started = Instant::now(); + let mut unit_of_work = unit_of_work; loop { tokio::select! { (from, message) = self.wait_for_message(inbound_services) => { @@ -114,7 +114,7 @@ where } let leader= self.committee.leader_for_view(current_view.view_id).clone(); - if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader, outbound_service, signing_service).await? { + if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader, outbound_service, signing_service, &mut unit_of_work).await? { next_event_result = result; break; } @@ -127,7 +127,7 @@ where } } } - Ok((next_event_result, self.locked_qc.clone())) + Ok(next_event_result) } async fn wait_for_message( @@ -164,19 +164,11 @@ where ); if let Some(qc) = self.create_qc(current_view) { - self.pre_commit_qc = Some(qc.clone()); self.broadcast(outbound, qc, current_view.view_id).await?; - // return Ok(Some(ConsensusWorkerStateEvent::PreCommitted)); return Ok(None); // Replica will move this on } dbg!("committee did not agree on node"); Ok(None) - - // let high_qc = self.find_highest_qc(); - // let proposal = self.create_proposal(high_qc.node(), payload_provider); - // self.broadcast_proposal(outbound, proposal, high_qc, current_view.view_id) - // .await?; - // Ok(Some(ConsensusWorkerStateEvent::Prepared)) } else { println!( "[COMMIT] Consensus has NOT YET been reached with {:?} out of {} votes", @@ -201,12 +193,12 @@ where fn create_qc(&self, current_view: &View) -> Option { // TODO: This can be done in one loop instead of two - let mut node = None; + let mut node_hash = None; for message in self.received_new_view_messages.values() { - node = match node { - None => message.node().cloned(), + node_hash = match node_hash { + None => message.node_hash().cloned(), Some(n) => { - if let Some(m_node) = message.node() { + if let Some(m_node) = message.node_hash() { if &n != m_node { unimplemented!("Nodes did not match"); } @@ -218,16 +210,15 @@ where }; } - todo!("Fix this"); - // let node = node.unwrap(); - // let mut qc = QuorumCertificate::new(HotStuffMessageType::PreCommit, current_view.view_id, node, None); - // for message in self.received_new_view_messages.values() { - // qc.combine_sig(message.partial_sig().unwrap()) - // } - // Some(qc) + let node_hash = node_hash.unwrap(); + let mut qc = QuorumCertificate::new(HotStuffMessageType::PreCommit, current_view.view_id, node_hash, None); + for message in self.received_new_view_messages.values() { + qc.combine_sig(message.partial_sig().unwrap()) + } + Some(qc) } - async fn process_replica_message( + async fn process_replica_message( &mut self, message: &HotStuffMessage, current_view: &View, @@ -235,6 +226,7 @@ where view_leader: &TAddr, outbound: &mut TOutboundService, signing_service: &TSigningService, + unit_of_work: &mut TUnitOfWork, ) -> Result, DigitalAssetError> { if let Some(justify) = message.justify() { if !justify.matches(HotStuffMessageType::PreCommit, current_view.view_id) { @@ -255,17 +247,15 @@ where return Ok(None); } - todo!("Fix this"); - - // self.locked_qc = Some(justify.clone()); - // self.send_vote_to_leader( - // justify.node(), - // outbound, - // view_leader, - // current_view.view_id, - // signing_service, - // ) - // .await?; + unit_of_work.set_locked_qc(justify); + self.send_vote_to_leader( + justify.node_hash().clone(), + outbound, + view_leader, + current_view.view_id, + signing_service, + ) + .await?; Ok(Some(ConsensusWorkerStateEvent::Committed)) } else { dbg!("received non justify message"); @@ -275,13 +265,13 @@ where async fn send_vote_to_leader( &self, - node: &HotStuffTreeNode, + node: TreeNodeHash, outbound: &mut TOutboundService, view_leader: &TAddr, view_number: ViewId, signing_service: &TSigningService, ) -> Result<(), DigitalAssetError> { - let mut message = HotStuffMessage::commit(Some(node.clone()), None, view_number); + let mut message = HotStuffMessage::vote_commit(node, view_number); message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); outbound.send(self.node_id.clone(), view_leader.clone(), message).await } diff --git a/dan_layer/core/src/workers/states/decide_state.rs b/dan_layer/core/src/workers/states/decide_state.rs index f9b0df1976..353d198b2e 100644 --- a/dan_layer/core/src/workers/states/decide_state.rs +++ b/dan_layer/core/src/workers/states/decide_state.rs @@ -36,19 +36,19 @@ use crate::{ infrastructure_services::{InboundConnectionService, NodeAddressable, OutboundService}, SigningService, }, + storage::UnitOfWork, workers::states::ConsensusWorkerStateEvent, }; use std::{collections::HashMap, marker::PhantomData, time::Instant}; use tokio::time::{sleep, Duration}; // TODO: This is very similar to pre-commit, and commit state -pub struct DecideState +pub struct DecideState where TInboundConnectionService: InboundConnectionService, TAddr: NodeAddressable, TPayload: Payload, TOutboundService: OutboundService, - TSigningService: SigningService, { node_id: TAddr, committee: Committee, @@ -56,20 +56,16 @@ where phantom_outbound: PhantomData, ta: PhantomData, p_p: PhantomData, - p_s: PhantomData, received_new_view_messages: HashMap>, - commit_qc: Option, - _locked_qc: Option, } -impl - DecideState +impl + DecideState where TInboundConnectionService: InboundConnectionService, TOutboundService: OutboundService, TAddr: NodeAddressable, TPayload: Payload, - TSigningService: SigningService, { pub fn new(node_id: TAddr, committee: Committee) -> Self { Self { @@ -80,19 +76,16 @@ where ta: PhantomData, p_p: PhantomData, received_new_view_messages: HashMap::new(), - commit_qc: None, - _locked_qc: None, - p_s: PhantomData, } } - pub async fn next_event( + pub async fn next_event( &mut self, timeout: Duration, current_view: &View, inbound_services: &mut TInboundConnectionService, outbound_service: &mut TOutboundService, - _signing_service: &TSigningService, + unit_of_work: TUnitOfWork, ) -> Result { let mut next_event_result = ConsensusWorkerStateEvent::Errored { reason: "loop ended without setting this event".to_string(), @@ -101,6 +94,7 @@ where self.received_new_view_messages.clear(); let started = Instant::now(); + let mut unit_of_work = unit_of_work; loop { tokio::select! { (from, message) = self.wait_for_message(inbound_services) => { @@ -113,7 +107,7 @@ where } let leader= self.committee.leader_for_view(current_view.view_id).clone(); - if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader).await? { + if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader, &mut unit_of_work).await? { next_event_result = result; break; } @@ -161,7 +155,6 @@ where ); if let Some(qc) = self.create_qc(current_view) { - self.commit_qc = Some(qc.clone()); self.broadcast(outbound, qc, current_view.view_id).await?; return Ok(None); // Replica will move this on } @@ -190,12 +183,12 @@ where } fn create_qc(&self, current_view: &View) -> Option { - let mut node = None; + let mut node_hash = None; for message in self.received_new_view_messages.values() { - node = match node { - None => message.node().cloned(), + node_hash = match node_hash { + None => message.node_hash().cloned(), Some(n) => { - if let Some(m_node) = message.node() { + if let Some(m_node) = message.node_hash() { if &n != m_node { unimplemented!("Nodes did not match"); } @@ -207,21 +200,21 @@ where }; } - todo!("Fix this"); - // let node = node.unwrap(); - // let mut qc = QuorumCertificate::new(HotStuffMessageType::Commit, current_view.view_id, node, None); - // for message in self.received_new_view_messages.values() { - // qc.combine_sig(message.partial_sig().unwrap()) - // } - // Some(qc) + let node_hash = node_hash.unwrap(); + let mut qc = QuorumCertificate::new(HotStuffMessageType::Commit, current_view.view_id, node_hash, None); + for message in self.received_new_view_messages.values() { + qc.combine_sig(message.partial_sig().unwrap()) + } + Some(qc) } - async fn process_replica_message( + async fn process_replica_message( &mut self, message: &HotStuffMessage, current_view: &View, from: &TAddr, view_leader: &TAddr, + unit_of_work: &mut TUnitOfWork, ) -> Result, DigitalAssetError> { if let Some(justify) = message.justify() { if !justify.matches(HotStuffMessageType::Commit, current_view.view_id) { @@ -242,33 +235,11 @@ where return Ok(None); } - // self.locked_qc = Some(justify.clone()); - // self.send_vote_to_leader( - // justify.node(), - // outbound, - // view_leader, - // current_view.view_id, - // &signing_service, - // ) - // .await?; - + unit_of_work.commit_node(justify.node_hash()); Ok(Some(ConsensusWorkerStateEvent::Decided)) } else { dbg!("received non justify message"); Ok(None) } } - - async fn _send_vote_to_leader( - &self, - node: &HotStuffTreeNode, - outbound: &mut TOutboundService, - view_leader: &TAddr, - view_number: ViewId, - signing_service: &TSigningService, - ) -> Result<(), DigitalAssetError> { - let mut message = HotStuffMessage::commit(Some(node.clone()), None, view_number); - message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); - outbound.send(self.node_id.clone(), view_leader.clone(), message).await - } } diff --git a/dan_layer/core/src/workers/states/next_view.rs b/dan_layer/core/src/workers/states/next_view.rs index 45394a3f3c..ceeac21d7a 100644 --- a/dan_layer/core/src/workers/states/next_view.rs +++ b/dan_layer/core/src/workers/states/next_view.rs @@ -56,7 +56,6 @@ impl NextViewState { ) -> Result { let db = db_factory.create()?; let prepare_qc = db.find_highest_prepared_qc()?; - dbg!(&prepare_qc); let message = HotStuffMessage::new_view(prepare_qc, current_view.view_id); let next_view = current_view.view_id.next(); let leader = committee.leader_for_view(next_view); diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index febcb13f9d..5985f17a04 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -45,7 +45,7 @@ use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Instant}; use crate::{ models::TreeNodeHash, services::PayloadProcessor, - storage::{BackendAdapter, DbFactory}, + storage::{BackendAdapter, ChainStorageService, DbFactory, StateDbUnitOfWork, StorageError, UnitOfWork}, }; use tokio::time::{sleep, Duration}; @@ -73,7 +73,6 @@ pub struct Prepare< TDbFactory: DbFactory, { node_id: TAddr, - locked_qc: QuorumCertificate, // bft_service: Box, // TODO remove this hack phantom: PhantomData, @@ -119,10 +118,9 @@ where TBackendAdapter: BackendAdapter + Send + Sync, TDbFactory: DbFactory + Clone, { - pub fn new(node_id: TAddr, db_factory: TDbFactory, locked_qc: QuorumCertificate) -> Self { + pub fn new(node_id: TAddr, db_factory: TDbFactory) -> Self { Self { node_id, - locked_qc, phantom: PhantomData, phantom_payload_provider: PhantomData, phantom_outbound: PhantomData, @@ -135,7 +133,11 @@ where } #[allow(clippy::too_many_arguments)] - pub async fn next_event( + pub async fn next_event< + TChainStorageService: ChainStorageService, + TUnitOfWork: UnitOfWork, + TStateDbUnitOfWork: StateDbUnitOfWork, + >( &mut self, current_view: &View, timeout: Duration, @@ -145,6 +147,9 @@ where payload_provider: &TPayloadProvider, signing_service: &TSigningService, payload_processor: &mut TPayloadProcessor, + chain_storage_service: &TChainStorageService, + chain_tx: TUnitOfWork, + state_tx: &mut TStateDbUnitOfWork, ) -> Result { self.received_new_view_messages.clear(); @@ -154,6 +159,7 @@ where trace!(target: LOG_TARGET, "next_event_result: {:?}", next_event_result); let started = Instant::now(); + let mut chain_tx = chain_tx; loop { tokio::select! { @@ -168,7 +174,7 @@ where } if let Some(result) = self.process_replica_message(&message, current_view, &from, committee.leader_for_view(current_view.view_id), outbound_service, signing_service, - payload_processor).await? { + payload_processor, &mut chain_tx, chain_storage_service, state_tx).await? { next_event_result = result; break; } @@ -238,13 +244,13 @@ where committee.len() ); let high_qc = self.find_highest_qc(); - dbg!(&high_qc); + + // TODO: get actual height let proposal = self - .create_proposal(high_qc.node_hash().clone(), payload_provider) + .create_proposal(high_qc.node_hash().clone(), payload_provider, 0) .await?; self.broadcast_proposal(outbound, committee, proposal, high_qc, current_view.view_id) .await?; - // Ok(Some(ConsensusWorkerStateEvent::Prepared)) Ok(None) // Will move to pre-commit when it receives the message as a replica } else { println!( @@ -256,7 +262,11 @@ where } } - async fn process_replica_message( + async fn process_replica_message< + TUnitOfWork: UnitOfWork, + TChainStorageService: ChainStorageService, + TStateDbUnitOfWork: StateDbUnitOfWork, + >( &self, message: &HotStuffMessage, current_view: &View, @@ -265,6 +275,9 @@ where outbound: &mut TOutboundService, signing_service: &TSigningService, payload_processor: &mut TPayloadProcessor, + chain_tx: &mut TUnitOfWork, + chain_storage_service: &TChainStorageService, + state_tx: &mut TStateDbUnitOfWork, ) -> Result, DigitalAssetError> { if !message.matches(HotStuffMessageType::Prepare, current_view.view_id) { // println!( @@ -285,25 +298,28 @@ where let node = message.node().unwrap(); if let Some(justify) = message.justify() { if self.does_extend(node, justify.node_hash()) { - if !self.is_safe_node(node, justify) { + if !self.is_safe_node(node, justify, chain_tx)? { unimplemented!("Node is not safe") } - let db = self.db_factory.create_state_db()?; - let unit_of_work = db.new_unit_of_work(); - - let res = payload_processor.process_payload(node.payload(), unit_of_work).await?; - - // TODO: Check result equals qc result - - self.send_vote_to_leader( - node.hash().clone(), - outbound, - view_leader, - current_view.view_id, - signing_service, - ) - .await?; + dbg!(&node); + let res = payload_processor + .process_payload(node.payload(), state_tx.clone()) + .await?; + if res == node.payload().state_root() { + chain_storage_service + .add_node::(node, chain_tx.clone()) + .await?; + + self.send_vote_to_leader( + node.hash().clone(), + outbound, + view_leader, + current_view.view_id, + signing_service, + ) + .await?; + } Ok(Some(ConsensusWorkerStateEvent::Prepared)) } else { unimplemented!("Did not extend from qc.justify.node") @@ -335,6 +351,7 @@ where &self, parent: TreeNodeHash, payload_provider: &TPayloadProvider, + height: u32, ) -> Result, DigitalAssetError> { info!(target: LOG_TARGET, "Creating new proposal"); @@ -342,7 +359,7 @@ where sleep(Duration::from_secs(3)).await; let payload = payload_provider.create_payload().await?; - Ok(HotStuffTreeNode::from_parent(parent, payload)) + Ok(HotStuffTreeNode::from_parent(parent, payload, height)) } async fn broadcast_proposal( @@ -363,9 +380,14 @@ where &from == &node.parent() } - fn is_safe_node(&self, node: &HotStuffTreeNode, quorum_certificate: &QuorumCertificate) -> bool { - self.does_extend(node, self.locked_qc.node_hash()) || - quorum_certificate.view_number() > self.locked_qc.view_number() + fn is_safe_node( + &self, + node: &HotStuffTreeNode, + quorum_certificate: &QuorumCertificate, + chain_tx: &mut TUnitOfWork, + ) -> Result { + let locked_qc = chain_tx.get_locked_qc()?; + Ok(self.does_extend(node, locked_qc.node_hash()) || quorum_certificate.view_number() > locked_qc.view_number()) } async fn send_vote_to_leader( diff --git a/dan_layer/core/src/workers/states/starting.rs b/dan_layer/core/src/workers/states/starting.rs index 00b4952a91..d00cd36080 100644 --- a/dan_layer/core/src/workers/states/starting.rs +++ b/dan_layer/core/src/workers/states/starting.rs @@ -113,7 +113,8 @@ where TBaseNodeClient: BaseNodeClient } let node = HotStuffTreeNode::genesis(payload_provider.create_genesis_payload()); let genesis_qc = QuorumCertificate::genesis(node.hash().clone()); - chain_storage_service.save_node(&node, tx.clone()).await?; + chain_storage_service.add_node(&node, tx.clone()).await?; + tx.commit_node(node.hash())?; chain_storage_service.set_locked_qc(genesis_qc, tx.clone()).await?; state_tx.commit()?; tx.commit()?; diff --git a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql index ebb738756b..61407d524e 100644 --- a/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql +++ b/dan_layer/storage_sqlite/migrations/2021-11-02-185150_create_nodes/up.sql @@ -1,19 +1,23 @@ -- Your SQL goes here create table nodes ( - hash blob not null primary key, - parent blob not null + id integer primary key autoincrement not null, + hash blob not null unique, + parent blob not null, + height integer not null, + is_committed boolean not null DEFAULT FALSE ); create table instructions ( id integer primary key autoincrement not null, hash blob not null, - node_hash blob not null, - asset_id blob not null, + node_id integer not null , template_id int not null, method text not null, - args blob not null + args blob not null, + foreign key (node_id) references nodes(id) ); + create table locked_qc ( id integer primary key not null, -- should always be 1 row message_type integer not null, diff --git a/dan_layer/storage_sqlite/src/models/instruction.rs b/dan_layer/storage_sqlite/src/models/instruction.rs new file mode 100644 index 0000000000..683c4a39d6 --- /dev/null +++ b/dan_layer/storage_sqlite/src/models/instruction.rs @@ -0,0 +1,42 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::schema::*; + +#[derive(Queryable)] +pub struct Instruction { + pub id: i32, + pub hash: Vec, + pub node_hash: Vec, + pub template_id: i32, + pub method: String, + pub args: Vec, +} + +#[derive(Insertable)] +#[table_name = "instructions"] +pub struct NewInstruction { + pub hash: Vec, + pub node_id: i32, + pub template_id: i32, + pub method: String, + pub args: Vec, +} diff --git a/dan_layer/storage_sqlite/src/models/mod.rs b/dan_layer/storage_sqlite/src/models/mod.rs index 4402b7192c..c29a9c4202 100644 --- a/dan_layer/storage_sqlite/src/models/mod.rs +++ b/dan_layer/storage_sqlite/src/models/mod.rs @@ -20,6 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod instruction; pub mod locked_qc; pub mod node; pub mod prepare_qc; diff --git a/dan_layer/storage_sqlite/src/models/node.rs b/dan_layer/storage_sqlite/src/models/node.rs index 484a7dddac..571d78c072 100644 --- a/dan_layer/storage_sqlite/src/models/node.rs +++ b/dan_layer/storage_sqlite/src/models/node.rs @@ -24,8 +24,11 @@ use crate::schema::*; #[derive(Queryable)] pub struct Node { - hash: Vec, - parent: Vec, + pub id: i32, + pub hash: Vec, + pub parent: Vec, + pub height: i32, + pub is_committed: bool, } #[derive(Insertable)] @@ -33,4 +36,5 @@ pub struct Node { pub struct NewNode { pub hash: Vec, pub parent: Vec, + pub height: i32, } diff --git a/dan_layer/storage_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs index 455469e525..e9dc76631a 100644 --- a/dan_layer/storage_sqlite/src/schema.rs +++ b/dan_layer/storage_sqlite/src/schema.rs @@ -2,8 +2,7 @@ table! { instructions (id) { id -> Integer, hash -> Binary, - node_hash -> Binary, - asset_id -> Binary, + node_id -> Integer, template_id -> Integer, method -> Text, args -> Binary, @@ -21,9 +20,12 @@ table! { } table! { - nodes (hash) { + nodes (id) { + id -> Integer, hash -> Binary, parent -> Binary, + height -> Integer, + is_committed -> Bool, } } @@ -46,6 +48,8 @@ table! { } } +joinable!(instructions -> nodes (node_id)); + allow_tables_to_appear_in_same_query!( instructions, locked_qc, diff --git a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs index 8a0dc7f84e..ef82e57638 100644 --- a/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs +++ b/dan_layer/storage_sqlite/src/sqlite_backend_adapter.rs @@ -71,10 +71,11 @@ impl BackendAdapter for SqliteBackendAdapter { fn insert(&self, item: &NewUnitOfWorkTracker, transaction: &Self::BackendTransaction) -> Result<(), Self::Error> { match item { - NewUnitOfWorkTracker::Node { hash, parent } => { + NewUnitOfWorkTracker::Node { hash, parent, height } => { let new_node = NewNode { hash: Vec::from(hash.as_bytes()), parent: Vec::from(parent.as_bytes()), + height: *height as i32, }; diesel::insert_into(nodes::table) .values(&new_node) @@ -163,6 +164,22 @@ impl BackendAdapter for SqliteBackendAdapter { }, } }, + UnitOfWorkTracker::Node { + hash, + parent, + height, + is_committed, + } => { + use crate::schema::nodes::dsl; + diesel::update(dsl::nodes.find(id)) + .set(( + dsl::hash.eq(&hash.0), + dsl::parent.eq(&parent.0), + dsl::height.eq(*height as i32), + dsl::is_committed.eq(is_committed), + )) + .execute(transaction.connection())?; + }, } Ok(()) } @@ -220,4 +237,16 @@ impl BackendAdapter for SqliteBackendAdapter { qc.signature.map(|s| Signature::from_bytes(s.as_slice())), )) } + + fn find_node_by_hash(&self, node_hash: &TreeNodeHash) -> Result<(Self::Id, UnitOfWorkTracker), Self::Error> { + use crate::schema::nodes::dsl; + let connection = SqliteConnection::establish(self.database_url.as_str())?; + let node: Node = dsl::nodes.filter(nodes::hash.eq(&node_hash.0)).first(&connection)?; + Ok((node.id, UnitOfWorkTracker::Node { + hash: TreeNodeHash(node.hash), + parent: TreeNodeHash(node.parent), + height: node.height as u32, + is_committed: node.is_committed, + })) + } } diff --git a/dan_layer/storage_sqlite/src/sqlite_storage_service.rs b/dan_layer/storage_sqlite/src/sqlite_storage_service.rs index b4af5933ab..678effef50 100644 --- a/dan_layer/storage_sqlite/src/sqlite_storage_service.rs +++ b/dan_layer/storage_sqlite/src/sqlite_storage_service.rs @@ -38,7 +38,7 @@ impl ChainStorageService for SqliteStorageService { todo!() } - async fn save_node( + async fn add_node( &self, node: &HotStuffTreeNode, db: TUnitOfWork, @@ -47,7 +47,7 @@ impl ChainStorageService for SqliteStorageService { for instruction in node.payload().instructions() { db.add_instruction(node.hash().clone(), instruction.clone())?; } - db.add_node(node.hash().clone(), node.parent().clone())?; + db.add_node(node.hash().clone(), node.parent().clone(), node.height())?; Ok(()) } @@ -57,12 +57,7 @@ impl ChainStorageService for SqliteStorageService { db: TUnitOfWork, ) -> Result<(), StorageError> { let mut db = db; - db.set_locked_qc( - qc.message_type(), - qc.view_number(), - qc.node_hash().clone(), - qc.signature().map(|s| s.clone()), - )?; + db.set_locked_qc(&qc)?; Ok(()) } } From 0388da5ab99cd7251373ab03cde653b796b17cd5 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Tue, 9 Nov 2021 12:54:10 +0200 Subject: [PATCH 09/14] wip --- dan_layer/storage_sqlite/src/schema.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dan_layer/storage_sqlite/src/schema.rs b/dan_layer/storage_sqlite/src/schema.rs index e9dc76631a..c5246a1b44 100644 --- a/dan_layer/storage_sqlite/src/schema.rs +++ b/dan_layer/storage_sqlite/src/schema.rs @@ -50,10 +50,4 @@ table! { joinable!(instructions -> nodes (node_id)); -allow_tables_to_appear_in_same_query!( - instructions, - locked_qc, - nodes, - prepare_qc, - state_key_values, -); +allow_tables_to_appear_in_same_query!(instructions, locked_qc, nodes, prepare_qc, state_key_values,); From c6596799bd95c67d64fd8787e2c4a1875c6c4299 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Tue, 9 Nov 2021 22:57:02 +0200 Subject: [PATCH 10/14] add watched assets stub --- .../src-tauri/src/commands/assets/mod.rs | 1 + .../src-tauri/src/commands/assets/watched.rs | 33 +++++++ .../tari_collectibles/src-tauri/src/main.rs | 3 +- .../tari_collectibles/web-app/src/App.js | 30 ++++++- .../web-app/src/NewWatchedAsset.js | 89 +++++++++++++++++++ .../web-app/src/WatchedAssetDetails.js | 51 +++++++++++ .../tari_collectibles/web-app/src/binding.js | 4 + 7 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs create mode 100644 applications/tari_collectibles/web-app/src/NewWatchedAsset.js create mode 100644 applications/tari_collectibles/web-app/src/WatchedAssetDetails.js diff --git a/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs index c9179c21aa..813673e939 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs @@ -29,6 +29,7 @@ use tari_common_types::types::Commitment; use tari_crypto::{hash::blake2::Blake256, keys::PublicKey, ristretto::RistrettoPublicKey}; use tari_mmr::{MemBackendVec, MerkleMountainRange}; use tari_utilities::{hex::Hex, ByteArray, Hashable}; +pub mod watched; #[tauri::command] pub(crate) async fn assets_create( diff --git a/applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs b/applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs new file mode 100644 index 0000000000..3fcfbde24d --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs @@ -0,0 +1,33 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::app_state::ConcurrentAppState; + +#[tauri::command] +pub(crate) async fn assets_watched_create( + asset_pub_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + // Connect to storage + // save to storage + Ok("No problem".to_string()) +} diff --git a/applications/tari_collectibles/src-tauri/src/main.rs b/applications/tari_collectibles/src-tauri/src/main.rs index c624efa0e1..c3d8634b8f 100644 --- a/applications/tari_collectibles/src-tauri/src/main.rs +++ b/applications/tari_collectibles/src-tauri/src/main.rs @@ -21,7 +21,8 @@ fn main() { commands::assets::assets_create, commands::assets::assets_list_owned, commands::assets::assets_list_registered_assets, - commands::assets::assets_issue_simple_tokens + commands::assets::assets_issue_simple_tokens, + commands::assets::watched::assets_watched_create ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/applications/tari_collectibles/web-app/src/App.js b/applications/tari_collectibles/web-app/src/App.js index 893bceb7b7..f757ee5d87 100644 --- a/applications/tari_collectibles/web-app/src/App.js +++ b/applications/tari_collectibles/web-app/src/App.js @@ -32,7 +32,7 @@ import { AppBar, Box, CssBaseline, Divider, - Drawer, + Drawer, IconButton, List, ListItem, ListItemIcon, @@ -42,6 +42,7 @@ import { } from "@mui/material"; import DashboardIcon from "@mui/icons-material/Dashboard"; import CreateIcon from "@mui/icons-material/Create"; +import AddIcon from "@mui/icons-material/Add"; import AppsIcon from "@mui/icons-material/Apps"; import { ThemeProvider } from "@emotion/react"; import Create from "./Create"; @@ -51,6 +52,8 @@ import * as React from "react"; import PropTypes from "prop-types"; import Manage from "./Manage"; import AssetManager from "./AssetManager"; +import NewWatchedAsset from "./NewWatchedAsset"; +import WatchedAssetDetails from "./WatchedAssetDetails"; const mdTheme = createTheme({ palette: { @@ -60,6 +63,20 @@ const mdTheme = createTheme({ }, }); +function IconButtonLink(props) { + const { icon, to} = props; + const renderLink = React.useMemo( + () => React.forwardRef(function Link(itemProps, ref) { + return ; + }), + [to] + ) + + return ( + {icon} + ); +} + function ListItemLink(props) { const { icon, primary, to } = props; @@ -108,6 +125,10 @@ function App() { icon={} /> + } to="/assets/watched/new"> + + }>My Assets Issued Assets + + + + + + + diff --git a/applications/tari_collectibles/web-app/src/NewWatchedAsset.js b/applications/tari_collectibles/web-app/src/NewWatchedAsset.js new file mode 100644 index 0000000000..677db31ed7 --- /dev/null +++ b/applications/tari_collectibles/web-app/src/NewWatchedAsset.js @@ -0,0 +1,89 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import React, { useState, useMemo } from "react"; +import {withRouter} from "react-router-dom"; +import {Alert, Button, Container, Stack, TextField, Typography} from "@mui/material"; +import binding from "./binding"; + +class NewWatchedAsset extends React.Component { + constructor(props) { + super(props); + + this.state = { + error: null, + isSaving: false, + assetPublicKey: "" + }; + } + + onAssetPublicKeyChanged = (e) => { + this.setState({ assetPublicKey: e.target.value }); + }; + + onSave = async (e) => { + e.preventDefault(); + console.log(this.state.assetPublicKey); + this.setState({isSaving: true, error: null}); + try{ + await binding.command_assets_watched_create(this.state.assetPublicKey); + let history = this.props.history; +let path =`/assets/watched/details/${this.state.assetPublicKey}`; +console.log(path); + history.push(path); + return; + }catch(e) { + this.setState({error: e}); + console.error(e); + } + this.setState({isSaving: false}); + } + + render() { + return ( + + New asset account + + + {this.state.error ? ( + {this.state.error} + ) : ( + + )} + + + + ); + } +} + +export default withRouter(NewWatchedAsset); diff --git a/applications/tari_collectibles/web-app/src/WatchedAssetDetails.js b/applications/tari_collectibles/web-app/src/WatchedAssetDetails.js new file mode 100644 index 0000000000..e275914eaf --- /dev/null +++ b/applications/tari_collectibles/web-app/src/WatchedAssetDetails.js @@ -0,0 +1,51 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import React from "react"; +import {withRouter} from "react-router-dom"; +import {Container, Stack, Typography} from "@mui/material"; + +class WatchedAssetDetails extends React.Component { + constructor(props) { + super(props); + + this.state = { + error: null, + isSaving: false, + assetPublicKey: "" + }; + } + + render() { + return ( + + Asset Details + + + Templates Implemented + + + ); + } +} + +export default withRouter(WatchedAssetDetails); diff --git a/applications/tari_collectibles/web-app/src/binding.js b/applications/tari_collectibles/web-app/src/binding.js index a5d89a7141..8c4f9a2fe7 100644 --- a/applications/tari_collectibles/web-app/src/binding.js +++ b/applications/tari_collectibles/web-app/src/binding.js @@ -45,11 +45,15 @@ async function command_asset_issue_simple_tokens( }); } +async function command_assets_watched_create(assetPubKey) { + return await invoke("assets_watched_create", {assetPubKey}); +} const commands = { command_assets_create, command_assets_list_owned, command_assets_list_registered_assets, command_asset_issue_simple_tokens, + command_assets_watched_create }; export default commands; From 07c4667d356ee0507662d4f078a213fc7e957744 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 10 Nov 2021 14:27:19 +0200 Subject: [PATCH 11/14] add storage to collectibles --- .gitignore | 1 + Cargo.lock | 5 + .../tari_collectibles/src-tauri/Cargo.toml | 4 + .../tari_collectibles/src-tauri/diesel.toml | 5 + .../src-tauri/migrations/.gitkeep | 0 .../2021-11-10-113031_init_schema/down.sql | 1 + .../2021-11-10-113031_init_schema/up.sql | 4 + .../src-tauri/src/app_state.rs | 20 +++- .../src-tauri/src/commands/accounts/mod.rs | 49 ++++++++ .../src-tauri/src/commands/assets/mod.rs | 1 - .../src-tauri/src/commands/mod.rs | 1 + .../tari_collectibles/src-tauri/src/main.rs | 11 +- .../assets/watched.rs => models/account.rs} | 20 ++-- .../src-tauri/src/models/mod.rs | 3 +- .../tari_collectibles/src-tauri/src/schema.rs | 6 + .../src-tauri/src/settings.rs | 7 +- .../src-tauri/src/storage/mod.rs | 36 ++++++ .../src-tauri/src/storage/sqlite/mod.rs | 105 ++++++++++++++++++ .../src/storage/sqlite/models/account.rs | 30 +++++ .../src/storage/sqlite/models/mod.rs | 24 ++++ .../src-tauri/src/storage/storage_error.rs | 43 +++++++ .../tari_collectibles/src-tauri/temp.sqlite | Bin 0 -> 24576 bytes ...hedAssetDetails.js => AccountDashboard.js} | 11 +- .../tari_collectibles/web-app/src/App.js | 15 +-- .../src/{NewWatchedAsset.js => NewAccount.js} | 6 +- .../tari_collectibles/web-app/src/binding.js | 6 +- dan_layer/core/proto/tips/tip001.proto | 12 ++ dan_layer/core/proto/tips/tip002.proto | 22 ++++ 28 files changed, 415 insertions(+), 33 deletions(-) create mode 100644 applications/tari_collectibles/src-tauri/diesel.toml create mode 100644 applications/tari_collectibles/src-tauri/migrations/.gitkeep create mode 100644 applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/down.sql create mode 100644 applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql create mode 100644 applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs rename applications/tari_collectibles/src-tauri/src/{commands/assets/watched.rs => models/account.rs} (84%) create mode 100644 applications/tari_collectibles/src-tauri/src/schema.rs create mode 100644 applications/tari_collectibles/src-tauri/src/storage/mod.rs create mode 100644 applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs create mode 100644 applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs create mode 100644 applications/tari_collectibles/src-tauri/src/storage/sqlite/models/mod.rs create mode 100644 applications/tari_collectibles/src-tauri/src/storage/storage_error.rs create mode 100644 applications/tari_collectibles/src-tauri/temp.sqlite rename applications/tari_collectibles/web-app/src/{WatchedAssetDetails.js => AccountDashboard.js} (89%) rename applications/tari_collectibles/web-app/src/{NewWatchedAsset.js => NewAccount.js} (95%) create mode 100644 dan_layer/core/proto/tips/tip001.proto create mode 100644 dan_layer/core/proto/tips/tip002.proto diff --git a/.gitignore b/.gitignore index e94d175872..f3128c3fcc 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ node_modules # ignore output files from windows ISS buildtools/Output/ +/applications/tari_collectibles/src-tauri/data \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 1c746937f9..7e10b717e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6087,6 +6087,8 @@ name = "tari_collectibles" version = "0.1.0" dependencies = [ "blake2", + "diesel", + "diesel_migrations", "futures 0.3.17", "rand 0.8.4", "serde 1.0.130", @@ -6098,7 +6100,9 @@ dependencies = [ "tari_utilities", "tauri", "tauri-build", + "thiserror", "tonic", + "uuid", ] [[package]] @@ -7939,6 +7943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ "getrandom 0.2.3", + "serde 1.0.130", ] [[package]] diff --git a/applications/tari_collectibles/src-tauri/Cargo.toml b/applications/tari_collectibles/src-tauri/Cargo.toml index 7c8db7d842..eebfd923f5 100644 --- a/applications/tari_collectibles/src-tauri/Cargo.toml +++ b/applications/tari_collectibles/src-tauri/Cargo.toml @@ -28,6 +28,10 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tonic = "0.5.2" tauri = { version = "1.0.0-beta.8", features = ["api-all"] } +diesel = { version = "1.4.8", features = ["sqlite"] } +diesel_migrations = "1.4.0" +thiserror = "1.0.30" +uuid = { version = "0.8.2", features = ["serde"] } [features] default = [ "custom-protocol" ] diff --git a/applications/tari_collectibles/src-tauri/diesel.toml b/applications/tari_collectibles/src-tauri/diesel.toml new file mode 100644 index 0000000000..92267c829f --- /dev/null +++ b/applications/tari_collectibles/src-tauri/diesel.toml @@ -0,0 +1,5 @@ +# For documentation on how to configure this file, +# see diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" diff --git a/applications/tari_collectibles/src-tauri/migrations/.gitkeep b/applications/tari_collectibles/src-tauri/migrations/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/down.sql b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/down.sql new file mode 100644 index 0000000000..3af2f77426 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/down.sql @@ -0,0 +1 @@ +-- lol, no \ No newline at end of file diff --git a/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql new file mode 100644 index 0000000000..7b7785c561 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql @@ -0,0 +1,4 @@ +create table accounts ( + id blob not null primary key, + asset_public_key blob not null unique +); \ No newline at end of file diff --git a/applications/tari_collectibles/src-tauri/src/app_state.rs b/applications/tari_collectibles/src-tauri/src/app_state.rs index 6e13a62617..024895d383 100644 --- a/applications/tari_collectibles/src-tauri/src/app_state.rs +++ b/applications/tari_collectibles/src-tauri/src/app_state.rs @@ -20,12 +20,22 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{base_node_client::BaseNodeClient, settings::Settings, wallet_client::WalletClient}; +use crate::{ + base_node_client::BaseNodeClient, + settings::Settings, + storage::{ + sqlite::{SqliteCollectiblesStorage, SqliteDbFactory}, + StorageError, + }, + wallet_client::WalletClient, +}; +use diesel::SqliteConnection; use std::sync::Arc; use tauri::async_runtime::RwLock; pub struct AppState { config: Settings, + db_factory: SqliteDbFactory, } #[derive(Clone)] @@ -35,9 +45,11 @@ pub struct ConcurrentAppState { impl ConcurrentAppState { pub fn new() -> Self { + let settings = Settings::new(); Self { inner: Arc::new(RwLock::new(AppState { - config: Settings::new(), + db_factory: SqliteDbFactory::new(settings.data_dir.as_path()), + config: settings, })), } } @@ -52,4 +64,8 @@ impl ConcurrentAppState { BaseNodeClient::connect(format!("http://{}", lock.config.base_node_grpc_address)).await?; Ok(client) } + + pub async fn create_db(&self) -> Result { + self.inner.read().await.db_factory.create_db() + } } diff --git a/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs new file mode 100644 index 0000000000..d89a6c8946 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs @@ -0,0 +1,49 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::{ + app_state::ConcurrentAppState, + models::{Account, NewAccount}, + storage::{AccountsTableGateway, CollectiblesStorage}, +}; +use tari_common_types::types::PublicKey; +use tari_utilities::hex::Hex; + +#[tauri::command] +pub(crate) async fn accounts_create( + asset_pub_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + // Connect to storage + // save to storage + let new_account = NewAccount { + asset_public_key: PublicKey::from_hex(asset_pub_key.as_str()) + .map_err(|e| format!("Invalid public key:{}", e))?, + }; + let result = state + .create_db() + .await + .map_err(|e| format!("Could not connect to DB:{}", e))? + .accounts() + .insert(new_account) + .map_err(|e| format!("Could not save account: {}", e))?; + Ok(result) +} diff --git a/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs index 813673e939..c9179c21aa 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs @@ -29,7 +29,6 @@ use tari_common_types::types::Commitment; use tari_crypto::{hash::blake2::Blake256, keys::PublicKey, ristretto::RistrettoPublicKey}; use tari_mmr::{MemBackendVec, MerkleMountainRange}; use tari_utilities::{hex::Hex, ByteArray, Hashable}; -pub mod watched; #[tauri::command] pub(crate) async fn assets_create( diff --git a/applications/tari_collectibles/src-tauri/src/commands/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/mod.rs index 546e37fb99..b3c417f12d 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/mod.rs @@ -20,4 +20,5 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod accounts; pub mod assets; diff --git a/applications/tari_collectibles/src-tauri/src/main.rs b/applications/tari_collectibles/src-tauri/src/main.rs index c3d8634b8f..4fd51374e4 100644 --- a/applications/tari_collectibles/src-tauri/src/main.rs +++ b/applications/tari_collectibles/src-tauri/src/main.rs @@ -1,3 +1,4 @@ +#![feature(array_methods)] #![cfg_attr( all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" @@ -5,11 +6,19 @@ use crate::app_state::ConcurrentAppState; +#[macro_use] +extern crate diesel; + +#[macro_use] +extern crate diesel_migrations; + mod app_state; mod base_node_client; mod commands; mod models; +mod schema; mod settings; +mod storage; mod wallet_client; fn main() { @@ -22,7 +31,7 @@ fn main() { commands::assets::assets_list_owned, commands::assets::assets_list_registered_assets, commands::assets::assets_issue_simple_tokens, - commands::assets::watched::assets_watched_create + commands::accounts::accounts_create ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs b/applications/tari_collectibles/src-tauri/src/models/account.rs similarity index 84% rename from applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs rename to applications/tari_collectibles/src-tauri/src/models/account.rs index 3fcfbde24d..72b4bfd7ad 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/assets/watched.rs +++ b/applications/tari_collectibles/src-tauri/src/models/account.rs @@ -20,14 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::app_state::ConcurrentAppState; +use serde::{Deserialize, Serialize}; +use tari_common_types::types::PublicKey; +use uuid::Uuid; -#[tauri::command] -pub(crate) async fn assets_watched_create( - asset_pub_key: String, - state: tauri::State<'_, ConcurrentAppState>, -) -> Result { - // Connect to storage - // save to storage - Ok("No problem".to_string()) +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Account { + pub id: Uuid, + pub asset_public_key: PublicKey, +} + +pub struct NewAccount { + pub asset_public_key: PublicKey, } diff --git a/applications/tari_collectibles/src-tauri/src/models/mod.rs b/applications/tari_collectibles/src-tauri/src/models/mod.rs index 8659239d1e..e60acb6e12 100644 --- a/applications/tari_collectibles/src-tauri/src/models/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/models/mod.rs @@ -22,6 +22,7 @@ mod asset_info; pub use asset_info::AssetInfo; - mod registered_asset_info; pub use registered_asset_info::RegisteredAssetInfo; +mod account; +pub use account::{Account, NewAccount}; diff --git a/applications/tari_collectibles/src-tauri/src/schema.rs b/applications/tari_collectibles/src-tauri/src/schema.rs new file mode 100644 index 0000000000..29ff0ec25d --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/schema.rs @@ -0,0 +1,6 @@ +table! { + accounts (id) { + id -> Binary, + asset_public_key -> Binary, + } +} diff --git a/applications/tari_collectibles/src-tauri/src/settings.rs b/applications/tari_collectibles/src-tauri/src/settings.rs index 99f42de261..014dd54dc3 100644 --- a/applications/tari_collectibles/src-tauri/src/settings.rs +++ b/applications/tari_collectibles/src-tauri/src/settings.rs @@ -20,12 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::env; +use std::{env, path::PathBuf}; pub struct Settings { pub(crate) wallet_grpc_address: String, pub(crate) base_node_grpc_address: String, pub(crate) _favourite_assets: Vec, + pub(crate) data_dir: PathBuf, } impl Settings { @@ -35,7 +36,8 @@ impl Settings { // base_node_grpc_address: "localhost:18142".to_string(), // _favourite_assets: vec!["1234".to_string()], // } - + let data_dir = env::var("DATA_DIR").unwrap_or_else(|_| "data".to_string()); + let data_dir = PathBuf::from(data_dir); // TODO: remove this, just for convenience Self { wallet_grpc_address: env::var("WALLET_GRPC_ADDRESS") @@ -43,6 +45,7 @@ impl Settings { base_node_grpc_address: env::var("BASE_NODE_GRPC_ADDRESS") .unwrap_or_else(|_| "localhost:18142".to_string()), _favourite_assets: vec!["1234".to_string()], + data_dir, } } } diff --git a/applications/tari_collectibles/src-tauri/src/storage/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/mod.rs new file mode 100644 index 0000000000..699cbbef70 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/storage/mod.rs @@ -0,0 +1,36 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::models::{Account, NewAccount}; +pub mod sqlite; +mod storage_error; +pub use storage_error::StorageError; + +pub trait CollectiblesStorage { + type Accounts: AccountsTableGateway; + fn accounts(&self) -> Self::Accounts; +} + +pub trait AccountsTableGateway { + fn list(&self) -> Result, StorageError>; + fn insert(&self, account: NewAccount) -> Result; +} diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs new file mode 100644 index 0000000000..d913a405b5 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs @@ -0,0 +1,105 @@ +use crate::{ + models::{Account, NewAccount}, + storage::{AccountsTableGateway, CollectiblesStorage, StorageError}, +}; +use diesel::{Connection, SqliteConnection}; +use std::path::Path; +use tari_utilities::ByteArray; +use uuid::Uuid; + +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod models; +use diesel::prelude::*; +use std::fs; +use tari_common_types::types::PublicKey; + +pub struct SqliteDbFactory { + database_url: String, +} +impl SqliteDbFactory { + pub fn new(data_dir: &Path) -> Self { + fs::create_dir_all(data_dir) + .expect(&format!("Could not create data directory: {:?}", data_dir)); + let database_url = data_dir + .join("collectibles.sqlite") + .into_os_string() + .into_string() + .unwrap(); + + Self { database_url } + } + + pub fn create_db(&self) -> Result { + let connection = SqliteConnection::establish(self.database_url.as_str())?; + connection.execute("PRAGMA foreign_keys = ON;"); + // Create the db + embed_migrations!("./migrations"); + embedded_migrations::run(&connection)?; + Ok(SqliteCollectiblesStorage { + database_url: self.database_url.clone(), + }) + } +} + +pub struct SqliteCollectiblesStorage { + database_url: String, +} + +impl CollectiblesStorage for SqliteCollectiblesStorage { + type Accounts = SqliteAccountsTableGateway; + + fn accounts(&self) -> Self::Accounts { + SqliteAccountsTableGateway { + database_url: self.database_url.clone(), + } + } +} + +pub struct SqliteAccountsTableGateway { + database_url: String, +} + +impl AccountsTableGateway for SqliteAccountsTableGateway { + fn list(&self) -> Result, StorageError> { + todo!() + } + + fn insert(&self, account: NewAccount) -> Result { + let id = Uuid::new_v4(); + let sql_model = models::Account { + id: Vec::from(id.as_bytes().as_slice()), + asset_public_key: Vec::from(account.asset_public_key.as_bytes()), + }; + let conn = SqliteConnection::establish(self.database_url.as_str())?; + + use crate::schema::accounts; + diesel::insert_into(accounts::table) + .values(sql_model) + .execute(&conn)?; + let result = Account { + id, + asset_public_key: account.asset_public_key, + }; + Ok(result) + } +} diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs new file mode 100644 index 0000000000..3802d8562c --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs @@ -0,0 +1,30 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::schema::*; +use diesel::prelude::*; + +#[derive(Queryable, Insertable)] +pub struct Account { + pub id: Vec, + pub asset_public_key: Vec, +} diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/mod.rs new file mode 100644 index 0000000000..a13d48aa50 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod account; +pub use account::Account; diff --git a/applications/tari_collectibles/src-tauri/src/storage/storage_error.rs b/applications/tari_collectibles/src-tauri/src/storage/storage_error.rs new file mode 100644 index 0000000000..97a96583c2 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/storage/storage_error.rs @@ -0,0 +1,43 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use diesel; +use diesel_migrations; + +#[derive(Debug, thiserror::Error)] +pub enum StorageError { + #[error("Could not connect to database: {source}")] + ConnectionError { + #[from] + source: diesel::ConnectionError, + }, + #[error("General diesel error: {source}")] + DieselError { + #[from] + source: diesel::result::Error, + }, + #[error("Could not migrate the database")] + MigrationError { + #[from] + source: diesel_migrations::RunMigrationsError, + }, +} diff --git a/applications/tari_collectibles/src-tauri/temp.sqlite b/applications/tari_collectibles/src-tauri/temp.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..1a61cf83d9c515720fdb1dcb10a69e2c984a7c46 GIT binary patch literal 24576 zcmeI&O^?z*7zgkv1y)H4dLkZ3o`VOPtS$vNX7{pIth%w_T8as$Nr7=~EbVSz)IH)? z@#0rnKZMENJUS)m#x-f;!HeYoWG0=Lr@;J}%TOk7Y(EwBE{a!PN&|L>DGGZ+#2Bl{ zT9>tmyk9Lk@~ZsSE^AiU!=p>B^^?`KE7rVhebug--&Okl05JMB(KS^$TUEZJNrLdz*D~zVt;R0-h}1iuajdb+uT%6%rQSX@%dE;j7B|Z0RJosS<$Fq#RcU=^vLGM;0SG_< z0uX=z1Rwwb2tWV=5cux|?klQ3==b`DjGkc(dIKZxJuqbHkzpJ@Iyl^a+>q;1T0fX9 z2nav`0uX=z1Rwwb2tWV=5P$##wnE^ps^7l(lR*CbU;gPI0Rad=00Izz00bZa0SG_< z0uX?}77OIh|8f4`;x8CWg8&2|009U<00Izz00bZa0SHI|od2;6AOHafKmY;|fB*y_ N009U<00P@D@C&|)%tZhI literal 0 HcmV?d00001 diff --git a/applications/tari_collectibles/web-app/src/WatchedAssetDetails.js b/applications/tari_collectibles/web-app/src/AccountDashboard.js similarity index 89% rename from applications/tari_collectibles/web-app/src/WatchedAssetDetails.js rename to applications/tari_collectibles/web-app/src/AccountDashboard.js index e275914eaf..95d738755f 100644 --- a/applications/tari_collectibles/web-app/src/WatchedAssetDetails.js +++ b/applications/tari_collectibles/web-app/src/AccountDashboard.js @@ -24,14 +24,16 @@ import React from "react"; import {withRouter} from "react-router-dom"; import {Container, Stack, Typography} from "@mui/material"; -class WatchedAssetDetails extends React.Component { +class AccountDashboard extends React.Component { constructor(props) { super(props); this.state = { error: null, isSaving: false, - assetPublicKey: "" + assetPublicKey: "", + tip101: false, + tip102: false, }; } @@ -41,11 +43,12 @@ class WatchedAssetDetails extends React.Component { Asset Details - Templates Implemented + + Balance: ); } } -export default withRouter(WatchedAssetDetails); +export default withRouter(AccountDashboard); diff --git a/applications/tari_collectibles/web-app/src/App.js b/applications/tari_collectibles/web-app/src/App.js index f757ee5d87..bbfe0f87d8 100644 --- a/applications/tari_collectibles/web-app/src/App.js +++ b/applications/tari_collectibles/web-app/src/App.js @@ -52,8 +52,8 @@ import * as React from "react"; import PropTypes from "prop-types"; import Manage from "./Manage"; import AssetManager from "./AssetManager"; -import NewWatchedAsset from "./NewWatchedAsset"; -import WatchedAssetDetails from "./WatchedAssetDetails"; +import AccountDashboard from "./AccountDashboard"; +import NewAccount from "./NewAccount"; const mdTheme = createTheme({ palette: { @@ -126,7 +126,7 @@ function App() { /> } to="/assets/watched/new"> + } to="/accounts/new"> }>My Assets Issued Assets @@ -147,13 +147,14 @@ function App() { sx={{ flexGrow: 1, height: "100vh", overflow: "auto" }} > - - + + - - + + + diff --git a/applications/tari_collectibles/web-app/src/NewWatchedAsset.js b/applications/tari_collectibles/web-app/src/NewAccount.js similarity index 95% rename from applications/tari_collectibles/web-app/src/NewWatchedAsset.js rename to applications/tari_collectibles/web-app/src/NewAccount.js index 677db31ed7..9484c0d6dd 100644 --- a/applications/tari_collectibles/web-app/src/NewWatchedAsset.js +++ b/applications/tari_collectibles/web-app/src/NewAccount.js @@ -25,7 +25,7 @@ import {withRouter} from "react-router-dom"; import {Alert, Button, Container, Stack, TextField, Typography} from "@mui/material"; import binding from "./binding"; -class NewWatchedAsset extends React.Component { +class NewAccount extends React.Component { constructor(props) { super(props); @@ -45,7 +45,7 @@ class NewWatchedAsset extends React.Component { console.log(this.state.assetPublicKey); this.setState({isSaving: true, error: null}); try{ - await binding.command_assets_watched_create(this.state.assetPublicKey); + await binding.command_accounts_create(this.state.assetPublicKey); let history = this.props.history; let path =`/assets/watched/details/${this.state.assetPublicKey}`; console.log(path); @@ -86,4 +86,4 @@ console.log(path); } } -export default withRouter(NewWatchedAsset); +export default withRouter(NewAccount); diff --git a/applications/tari_collectibles/web-app/src/binding.js b/applications/tari_collectibles/web-app/src/binding.js index 8c4f9a2fe7..5053ad5a4e 100644 --- a/applications/tari_collectibles/web-app/src/binding.js +++ b/applications/tari_collectibles/web-app/src/binding.js @@ -45,15 +45,15 @@ async function command_asset_issue_simple_tokens( }); } -async function command_assets_watched_create(assetPubKey) { - return await invoke("assets_watched_create", {assetPubKey}); +async function command_accounts_create(assetPubKey) { + return await invoke("accounts_create", {assetPubKey}); } const commands = { command_assets_create, command_assets_list_owned, command_assets_list_registered_assets, command_asset_issue_simple_tokens, - command_assets_watched_create + command_accounts_create }; export default commands; diff --git a/dan_layer/core/proto/tips/tip001.proto b/dan_layer/core/proto/tips/tip001.proto new file mode 100644 index 0000000000..abde69b28e --- /dev/null +++ b/dan_layer/core/proto/tips/tip001.proto @@ -0,0 +1,12 @@ + +service Tip001 { + rpc ReadMetada(ReadMetadataRequest) returns ReadMetadataResponse; +} + +message ReadMetadataRequest { + +} + +message ReadMetadataResponse { + +} \ No newline at end of file diff --git a/dan_layer/core/proto/tips/tip002.proto b/dan_layer/core/proto/tips/tip002.proto new file mode 100644 index 0000000000..7e7fcaf30d --- /dev/null +++ b/dan_layer/core/proto/tips/tip002.proto @@ -0,0 +1,22 @@ +service Tip002 { + rpc Info(InfoRequest) returns InfoResponse; + rpc BalanceOf(BalanceOfRequest) returns BalanceOfResponse; + } + + message InfoRequest { + + } + + message InfoResponse { + string symbol = 1; + byte decimals = 2; + uint256 total_supply =3; + } + + message BalanceOfRequest { + uint256 owner = 1; + } + + message BalanceOfResponse { + uint256 balance = 1; + } \ No newline at end of file From d23c1fdb44368eeebc0c9e2bcaf642c881847625 Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 10 Nov 2021 15:58:04 +0200 Subject: [PATCH 12/14] read asset metadata --- .../tari_app_grpc/proto/base_node.proto | 13 ++++ .../src/grpc/base_node_grpc_server.rs | 65 +++++++++++++++++- .../2021-11-10-113031_init_schema/up.sql | 5 +- .../src-tauri/src/base_node_client.rs | 20 ++++++ .../src-tauri/src/commands/accounts/mod.rs | 18 +++-- .../src-tauri/src/models/account.rs | 6 ++ .../tari_collectibles/src-tauri/src/schema.rs | 3 + .../src-tauri/src/storage/sqlite/mod.rs | 6 ++ .../src/storage/sqlite/models/account.rs | 3 + .../tari_collectibles/src-tauri/temp.sqlite | Bin 24576 -> 24576 bytes .../comms_interface/comms_request.rs | 6 ++ .../comms_interface/comms_response.rs | 4 ++ .../comms_interface/inbound_handlers.rs | 9 +++ .../comms_interface/local_interface.rs | 14 ++++ 14 files changed, 165 insertions(+), 7 deletions(-) diff --git a/applications/tari_app_grpc/proto/base_node.proto b/applications/tari_app_grpc/proto/base_node.proto index 0d98660415..b7a568d473 100644 --- a/applications/tari_app_grpc/proto/base_node.proto +++ b/applications/tari_app_grpc/proto/base_node.proto @@ -85,6 +85,19 @@ service BaseNode { rpc GetTokens(GetTokensRequest) returns (stream GetTokensResponse); rpc ListAssetRegistrations(ListAssetRegistrationsRequest) returns (stream ListAssetRegistrationsResponse); + rpc GetAssetMetadata(GetAssetMetadataRequest) returns (GetAssetMetadataResponse); +} + +message GetAssetMetadataRequest { + bytes asset_public_key = 1; +} + +message GetAssetMetadataResponse { + optional string name = 2; + optional string description =3; + optional string image = 4; + bytes owner_commitment = 5; + OutputFeatures features = 6; } message ListAssetRegistrationsRequest { diff --git a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs index 4563dc6270..a6c5cdbd57 100644 --- a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs +++ b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs @@ -47,7 +47,7 @@ use tari_core::{ StateMachineHandle, }, blocks::{Block, BlockHeader, NewBlockTemplate}, - chain_storage::ChainStorageError, + chain_storage::{ChainStorageError, PrunedOutput}, consensus::{emission::Emission, ConsensusManager, NetworkConsensus}, iterators::NonOverlappingIntegerPairIter, mempool::{service::LocalMempoolService, TxStorageResponse}, @@ -464,6 +464,69 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { Ok(Response::new(rx)) } + async fn get_asset_metadata( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + + let mut handler = self.node_service.clone(); + let metadata = handler + .get_asset_metadata( + PublicKey::from_bytes(&request.asset_public_key) + .map_err(|e| Status::invalid_argument("Not a valid asset public key"))?, + ) + .await + .map_err(|e| Status::internal(e.to_string()))?; + + if let Some(m) = metadata { + match m.output { + PrunedOutput::Pruned { + output_hash, + witness_hash, + } => return Err(Status::not_found("Output has been pruned")), + PrunedOutput::NotPruned { output } => { + if let Some(ref asset) = output.features.asset { + const ASSET_METADATA_TEMPLATE_ID: u32 = 1; + if asset.template_ids_implemented.contains(&ASSET_METADATA_TEMPLATE_ID) { + // TODO: move to a better location, or better yet, have the grpc caller split the metadata + let m = String::from_utf8(output.features.metadata.clone()).unwrap(); + let mut m = m + .as_str() + .split('|') + .map(|s| s.to_string()) + .collect::>() + .into_iter(); + let name = m.next(); + let description = m.next(); + let image = m.next(); + + // TODO Perhaps this should just return metadata and have the client read the metadata in a + // pattern described by the template + return Ok(Response::new(tari_rpc::GetAssetMetadataResponse { + name, + description, + image, + owner_commitment: Vec::from(output.commitment.as_bytes()), + features: Some(output.features.clone().into()), + })); + } + } + return Ok(Response::new(tari_rpc::GetAssetMetadataResponse { + name: None, + description: None, + image: None, + owner_commitment: Vec::from(output.commitment.as_bytes()), + features: Some(output.features.clone().into()), + })); + }, + }; + Err(Status::unknown("Could not find a matching arm")) + } else { + Err(Status::not_found("Could not find any utxo")) + } + } + async fn list_asset_registrations( &self, request: Request, diff --git a/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql index 7b7785c561..de414ac899 100644 --- a/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql +++ b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql @@ -1,4 +1,7 @@ create table accounts ( id blob not null primary key, - asset_public_key blob not null unique + asset_public_key blob not null unique, + name text, + description text, + image text ); \ No newline at end of file diff --git a/applications/tari_collectibles/src-tauri/src/base_node_client.rs b/applications/tari_collectibles/src-tauri/src/base_node_client.rs index 60560b2690..9c87ce88da 100644 --- a/applications/tari_collectibles/src-tauri/src/base_node_client.rs +++ b/applications/tari_collectibles/src-tauri/src/base_node_client.rs @@ -22,6 +22,8 @@ use futures::StreamExt; use tari_app_grpc::tari_rpc as grpc; +use tari_common_types::types::PublicKey; +use tari_utilities::ByteArray; pub struct BaseNodeClient { client: grpc::base_node_client::BaseNodeClient, @@ -63,6 +65,24 @@ impl BaseNodeClient { Ok(assets) } + pub async fn get_asset_metadata( + &mut self, + asset_public_key: PublicKey, + ) -> Result { + let client = self.client_mut(); + let request = grpc::GetAssetMetadataRequest { + asset_public_key: Vec::from(asset_public_key.as_bytes()), + }; + dbg!(&request); + let response = client + .get_asset_metadata(request) + .await + .map(|response| response.into_inner()) + .map_err(|s| format!("Could not get asset metadata: {}", s))?; + dbg!(&response); + Ok(response) + } + fn client_mut( &mut self, ) -> &mut grpc::base_node_client::BaseNodeClient { diff --git a/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs index d89a6c8946..423146af52 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs @@ -32,12 +32,20 @@ pub(crate) async fn accounts_create( asset_pub_key: String, state: tauri::State<'_, ConcurrentAppState>, ) -> Result { - // Connect to storage - // save to storage - let new_account = NewAccount { - asset_public_key: PublicKey::from_hex(asset_pub_key.as_str()) - .map_err(|e| format!("Invalid public key:{}", e))?, + let asset_pub_key = + PublicKey::from_hex(asset_pub_key.as_str()).map_err(|e| format!("Invalid public key:{}", e))?; + let mut new_account = NewAccount { + asset_public_key: asset_pub_key.clone(), + name: None, + description: None, + image: None, }; + + let mut client = state.connect_base_node_client().await?; + let chain_registration_data = client.get_asset_metadata(asset_pub_key).await?; + new_account.name = chain_registration_data.name.clone(); + new_account.description = chain_registration_data.description.clone(); + new_account.image = chain_registration_data.image.clone(); let result = state .create_db() .await diff --git a/applications/tari_collectibles/src-tauri/src/models/account.rs b/applications/tari_collectibles/src-tauri/src/models/account.rs index 72b4bfd7ad..8a7891dbd1 100644 --- a/applications/tari_collectibles/src-tauri/src/models/account.rs +++ b/applications/tari_collectibles/src-tauri/src/models/account.rs @@ -28,8 +28,14 @@ use uuid::Uuid; pub struct Account { pub id: Uuid, pub asset_public_key: PublicKey, + pub name: Option, + pub description: Option, + pub image: Option, } pub struct NewAccount { pub asset_public_key: PublicKey, + pub name: Option, + pub description: Option, + pub image: Option, } diff --git a/applications/tari_collectibles/src-tauri/src/schema.rs b/applications/tari_collectibles/src-tauri/src/schema.rs index 29ff0ec25d..7623e58d62 100644 --- a/applications/tari_collectibles/src-tauri/src/schema.rs +++ b/applications/tari_collectibles/src-tauri/src/schema.rs @@ -2,5 +2,8 @@ table! { accounts (id) { id -> Binary, asset_public_key -> Binary, + name -> Nullable, + description -> Nullable, + image -> Nullable, } } diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs index d913a405b5..0b99660e37 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs @@ -89,6 +89,9 @@ impl AccountsTableGateway for SqliteAccountsTableGateway { let sql_model = models::Account { id: Vec::from(id.as_bytes().as_slice()), asset_public_key: Vec::from(account.asset_public_key.as_bytes()), + name: account.name.clone(), + description: account.description.clone(), + image: account.image.clone(), }; let conn = SqliteConnection::establish(self.database_url.as_str())?; @@ -99,6 +102,9 @@ impl AccountsTableGateway for SqliteAccountsTableGateway { let result = Account { id, asset_public_key: account.asset_public_key, + name: account.name, + description: account.description, + image: account.image, }; Ok(result) } diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs index 3802d8562c..c8e4c007c4 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs @@ -27,4 +27,7 @@ use diesel::prelude::*; pub struct Account { pub id: Vec, pub asset_public_key: Vec, + pub name: Option, + pub description: Option, + pub image: Option, } diff --git a/applications/tari_collectibles/src-tauri/temp.sqlite b/applications/tari_collectibles/src-tauri/temp.sqlite index 1a61cf83d9c515720fdb1dcb10a69e2c984a7c46..bfb0714405c84e9a4878456ba3c2f727ea2433aa 100644 GIT binary patch delta 119 zcmZoTz}RqraY7c)9tOUf{EK<_@U7r0-`MEJ%hhPX%q}h~%h=>R`8RK+l@2eLf`UR` zVs5HJNoqw2gqxCDoLrPyP?DLSha{Sro0tw!#ml9s&pLS(pX%m$e9?mJ##SaKRt6S} G6dVA(FC(Y` delta 67 zcmV-J0KETzzyW~30g!48@c<6l52Fq74yX=yvk?$`4GMt+0|yr!9RYz^lg15s1q})* ZF9nmT4kfdm4qFiiF*-CkIyE<, }, + FetchAssetMetadata { + asset_public_key: PublicKey, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -104,6 +107,9 @@ impl Display for NodeCommsRequest { FetchAssetRegistrations { .. } => { write!(f, "FetchAllNonFungibleTokens") }, + FetchAssetMetadata { .. } => { + write!(f, "FetchAssetMetadata") + }, } } } diff --git a/base_layer/core/src/base_node/comms_interface/comms_response.rs b/base_layer/core/src/base_node/comms_interface/comms_response.rs index 81bb9b55f0..bea4ccd0c8 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_response.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_response.rs @@ -55,6 +55,9 @@ pub enum NodeCommsResponse { FetchAssetRegistrationsResponse { outputs: Vec, }, + FetchAssetMetadataResponse { + output: Option, + }, } impl Display for NodeCommsResponse { @@ -84,6 +87,7 @@ impl Display for NodeCommsResponse { MmrNodes(_, _) => write!(f, "MmrNodes"), FetchTokensResponse { .. } => write!(f, "FetchTokensResponse"), FetchAssetRegistrationsResponse { .. } => write!(f, "FetchAssetRegistrationsResponse"), + FetchAssetMetadataResponse { .. } => write!(f, "FetchAssetMetadataResponse"), } } } diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index 5dc96f8db6..342763adb5 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -19,6 +19,7 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + use crate::{ base_node::comms_interface::{ error::CommsInterfaceError, @@ -43,6 +44,7 @@ use strum_macros::Display; use tari_common_types::types::{BlockHash, HashOutput, PublicKey}; use tari_comms::peer_manager::NodeId; use tari_crypto::tari_utilities::{hash::Hashable, hex::Hex}; +use tari_utilities::ByteArray; use tokio::sync::Semaphore; const LOG_TARGET: &str = "c::bn::comms_interface::inbound_handler"; @@ -441,6 +443,13 @@ where T: BlockchainBackend + 'static .collect(); Ok(NodeCommsResponse::FetchAssetRegistrationsResponse { outputs }) }, + NodeCommsRequest::FetchAssetMetadata { asset_public_key } => { + let output = self + .blockchain_db + .fetch_utxo_by_unique_id(None, Vec::from(asset_public_key.as_bytes()), None) + .await?; + Ok(NodeCommsResponse::FetchAssetMetadataResponse { output }) + }, } } diff --git a/base_layer/core/src/base_node/comms_interface/local_interface.rs b/base_layer/core/src/base_node/comms_interface/local_interface.rs index b0c885d944..c611207e01 100644 --- a/base_layer/core/src/base_node/comms_interface/local_interface.rs +++ b/base_layer/core/src/base_node/comms_interface/local_interface.rs @@ -303,4 +303,18 @@ impl LocalNodeCommsInterface { _ => Err(CommsInterfaceError::UnexpectedApiResponse), } } + + pub async fn get_asset_metadata( + &mut self, + asset_public_key: PublicKey, + ) -> Result, CommsInterfaceError> { + match self + .request_sender + .call(NodeCommsRequest::FetchAssetMetadata { asset_public_key }) + .await?? + { + NodeCommsResponse::FetchAssetMetadataResponse { output } => Ok(output), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), + } + } } From 4e42b42601f3e7af60d087fcf5a538bd931b707b Mon Sep 17 00:00:00 2001 From: Mike the Tike Date: Wed, 10 Nov 2021 18:50:09 +0200 Subject: [PATCH 13/14] save sidechain committee to db --- .../2021-11-10-113031_init_schema/up.sql | 4 +- .../src-tauri/src/base_node_client.rs | 48 +++++++++++++++++- .../src-tauri/src/commands/accounts/mod.rs | 34 ++++++++++++- .../tari_collectibles/src-tauri/src/main.rs | 3 +- .../src-tauri/src/models/account.rs | 2 + .../tari_collectibles/src-tauri/src/schema.rs | 2 + .../src-tauri/src/storage/sqlite/mod.rs | 45 +++++++++++++++- .../src/storage/sqlite/models/account.rs | 2 + .../tari_collectibles/src-tauri/temp.sqlite | Bin 24576 -> 24576 bytes .../tari_collectibles/web-app/src/App.js | 14 +++++ .../tari_collectibles/web-app/src/binding.js | 8 ++- 11 files changed, 156 insertions(+), 6 deletions(-) diff --git a/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql index de414ac899..f764251842 100644 --- a/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql +++ b/applications/tari_collectibles/src-tauri/migrations/2021-11-10-113031_init_schema/up.sql @@ -3,5 +3,7 @@ create table accounts ( asset_public_key blob not null unique, name text, description text, - image text + image text, + committee_length integer not null, + committee_pub_keys blob not null ); \ No newline at end of file diff --git a/applications/tari_collectibles/src-tauri/src/base_node_client.rs b/applications/tari_collectibles/src-tauri/src/base_node_client.rs index 9c87ce88da..5c172ec1cf 100644 --- a/applications/tari_collectibles/src-tauri/src/base_node_client.rs +++ b/applications/tari_collectibles/src-tauri/src/base_node_client.rs @@ -67,7 +67,7 @@ impl BaseNodeClient { pub async fn get_asset_metadata( &mut self, - asset_public_key: PublicKey, + asset_public_key: &PublicKey, ) -> Result { let client = self.client_mut(); let request = grpc::GetAssetMetadataRequest { @@ -83,6 +83,52 @@ impl BaseNodeClient { Ok(response) } + // TODO: probably can get the full checkpoint instead + pub async fn get_sidechain_committee( + &mut self, + asset_public_key: &PublicKey, + ) -> Result, String> { + let client = self.client_mut(); + let request = grpc::GetTokensRequest { + asset_public_key: Vec::from(asset_public_key.as_bytes()), + unique_ids: vec![vec![3u8; 32]], + }; + + dbg!(&request); + let mut stream = client + .get_tokens(request) + .await + .map(|response| response.into_inner()) + .map_err(|s| format!("Could not get asset sidechain checkpoint"))?; + let mut i = 0; + // Could def do this better + #[allow(clippy::never_loop)] + while let Some(response) = stream.next().await { + i += 1; + if i > 10 { + break; + } + dbg!(&response); + let features = response + .map_err(|status| format!("Got an error status from GRPC:{}", status))? + .features; + if let Some(sidechain) = features.and_then(|f| f.sidechain_checkpoint) { + let pub_keys = sidechain + .committee + .iter() + .map(|s| PublicKey::from_bytes(s).map_err(|e| format!("Not a valid public key:{}", e))) + .collect::>()?; + return Ok(pub_keys); + } else { + return Err("Found utxo but was missing sidechain data".to_string()); + } + } + Err(format!( + "No side chain tokens were found out of {} streamed", + i + )) + } + fn client_mut( &mut self, ) -> &mut grpc::base_node_client::BaseNodeClient { diff --git a/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs index 423146af52..a7bd304fae 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/accounts/mod.rs @@ -39,13 +39,30 @@ pub(crate) async fn accounts_create( name: None, description: None, image: None, + committee: None, }; let mut client = state.connect_base_node_client().await?; - let chain_registration_data = client.get_asset_metadata(asset_pub_key).await?; + let chain_registration_data = client.get_asset_metadata(&asset_pub_key).await?; new_account.name = chain_registration_data.name.clone(); new_account.description = chain_registration_data.description.clone(); new_account.image = chain_registration_data.image.clone(); + + let sidechain_committee = match client.get_sidechain_committee(&asset_pub_key).await { + Ok(s) => { + if s.is_empty() { + None + } else { + Some(s) + } + } + Err(e) => { + dbg!(e); + None + } + }; + new_account.committee = sidechain_committee; + let result = state .create_db() .await @@ -55,3 +72,18 @@ pub(crate) async fn accounts_create( .map_err(|e| format!("Could not save account: {}", e))?; Ok(result) } + +#[tauri::command] +pub(crate) async fn accounts_list( + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, String> { + let db = state + .create_db() + .await + .map_err(|e| format!("Could not connect to DB:{}", e))?; + let result = db + .accounts() + .list() + .map_err(|e| format!("Could list accounts from DB: {}", e))?; + Ok(result) +} diff --git a/applications/tari_collectibles/src-tauri/src/main.rs b/applications/tari_collectibles/src-tauri/src/main.rs index 4fd51374e4..d49582974f 100644 --- a/applications/tari_collectibles/src-tauri/src/main.rs +++ b/applications/tari_collectibles/src-tauri/src/main.rs @@ -31,7 +31,8 @@ fn main() { commands::assets::assets_list_owned, commands::assets::assets_list_registered_assets, commands::assets::assets_issue_simple_tokens, - commands::accounts::accounts_create + commands::accounts::accounts_create, + commands::accounts::accounts_list ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/applications/tari_collectibles/src-tauri/src/models/account.rs b/applications/tari_collectibles/src-tauri/src/models/account.rs index 8a7891dbd1..ce9f1f71e6 100644 --- a/applications/tari_collectibles/src-tauri/src/models/account.rs +++ b/applications/tari_collectibles/src-tauri/src/models/account.rs @@ -31,6 +31,7 @@ pub struct Account { pub name: Option, pub description: Option, pub image: Option, + pub committee: Option>, } pub struct NewAccount { @@ -38,4 +39,5 @@ pub struct NewAccount { pub name: Option, pub description: Option, pub image: Option, + pub committee: Option>, } diff --git a/applications/tari_collectibles/src-tauri/src/schema.rs b/applications/tari_collectibles/src-tauri/src/schema.rs index 7623e58d62..4a5e97d3c6 100644 --- a/applications/tari_collectibles/src-tauri/src/schema.rs +++ b/applications/tari_collectibles/src-tauri/src/schema.rs @@ -5,5 +5,7 @@ table! { name -> Nullable, description -> Nullable, image -> Nullable, + committee_length -> Integer, + committee_pub_keys -> Binary, } } diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs index 0b99660e37..68bc4bd9ab 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs @@ -29,6 +29,7 @@ use uuid::Uuid; // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod models; +use crate::schema::{self, accounts::dsl::accounts, *}; use diesel::prelude::*; use std::fs; use tari_common_types::types::PublicKey; @@ -81,17 +82,58 @@ pub struct SqliteAccountsTableGateway { impl AccountsTableGateway for SqliteAccountsTableGateway { fn list(&self) -> Result, StorageError> { - todo!() + let conn = SqliteConnection::establish(self.database_url.as_str())?; + let results: Vec = schema::accounts::table + .order_by(schema::accounts::name.asc()) + .load(&conn)?; + Ok( + results + .iter() + .map(|r| { + let mut committee = Vec::with_capacity(r.committee_length as usize); + for i in 0..r.committee_length as usize { + committee + .push(PublicKey::from_bytes(&r.committee_pub_keys[i * 32..(i + 1) * 32]).unwrap()); + } + Account { + id: Uuid::from_slice(&r.id).unwrap(), + asset_public_key: PublicKey::from_bytes(&r.asset_public_key).unwrap(), + name: r.name.clone(), + description: r.description.clone(), + image: r.image.clone(), + committee: if committee.is_empty() { + None + } else { + Some(committee) + }, + } + }) + .collect(), + ) } fn insert(&self, account: NewAccount) -> Result { let id = Uuid::new_v4(); + let mut committee_pub_keys = vec![]; + if let Some(pub_keys) = account.committee.as_ref() { + for key in pub_keys { + committee_pub_keys.extend_from_slice(key.as_bytes()); + } + } + // let committee_pub_keys = if committee_pub_keys.is_empty() { None} else {Some(committee_pub_keys)}; + let sql_model = models::Account { id: Vec::from(id.as_bytes().as_slice()), asset_public_key: Vec::from(account.asset_public_key.as_bytes()), name: account.name.clone(), description: account.description.clone(), image: account.image.clone(), + committee_length: account + .committee + .as_ref() + .map(|s| s.len() as i32) + .unwrap_or(0i32), + committee_pub_keys, }; let conn = SqliteConnection::establish(self.database_url.as_str())?; @@ -105,6 +147,7 @@ impl AccountsTableGateway for SqliteAccountsTableGateway { name: account.name, description: account.description, image: account.image, + committee: account.committee, }; Ok(result) } diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs index c8e4c007c4..f6f5908ba5 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs @@ -30,4 +30,6 @@ pub struct Account { pub name: Option, pub description: Option, pub image: Option, + pub committee_length: i32, + pub committee_pub_keys: Vec, } diff --git a/applications/tari_collectibles/src-tauri/temp.sqlite b/applications/tari_collectibles/src-tauri/temp.sqlite index bfb0714405c84e9a4878456ba3c2f727ea2433aa..a3afcb033173898e1bef962260071f8cdb9e577a 100644 GIT binary patch delta 143 zcmZoTz}RqraY7bPJ_FxP{>8lcd@K0MH#Yv{;c8-KW*3*0Wo(X}e2jNpkPa`Gf`USF zer|4NNl9vId`@a!dP#;tW?o5ZdTNnEUVe!}UTIDay0U`Or1 { + let a = await binding.command_accounts_list(); + console.log(a); + setAccounts(a); + + }); + return (
@@ -129,6 +140,9 @@ function App() { } to="/accounts/new"> }>My Assets + { accounts.map((item) => { + return (); + })} Issued Assets Date: Fri, 12 Nov 2021 12:30:50 +0200 Subject: [PATCH 14/14] save sidechain committee to db --- applications/tari_app_grpc/build.rs | 9 +++- .../tari_app_grpc/proto/base_node.proto | 1 - .../proto}/validator_node.proto | 3 +- .../src-tauri/src/app_state.rs | 18 ++++++- .../src/{ => clients}/base_node_client.rs | 0 .../src-tauri/src/clients/mod.rs | 28 ++++++++++ .../src/clients/validator_node_client.rs | 44 +++++++++++++++ .../src/{ => clients}/wallet_client.rs | 0 .../src-tauri/src/commands/mod.rs | 1 + .../src-tauri/src/commands/tips/mod.rs | 23 ++++++++ .../src-tauri/src/commands/tips/tip002.rs | 53 +++++++++++++++++++ .../tari_collectibles/src-tauri/src/main.rs | 3 +- .../src-tauri/src/models/mod.rs | 2 + .../src-tauri/src/models/tip002_info.rs | 29 ++++++++++ .../src-tauri/src/settings.rs | 5 +- .../src-tauri/src/storage/mod.rs | 2 + .../src-tauri/src/storage/sqlite/mod.rs | 52 +++++++++++------- .../src/storage/sqlite/models/account.rs | 2 +- applications/tari_validator_node/build.rs | 5 -- .../src/grpc/conversions.rs | 10 ++-- .../tari_validator_node/src/grpc/mod.rs | 4 -- .../src/grpc/validator_node_grpc_server.rs | 2 +- applications/tari_validator_node/src/main.rs | 9 +--- 23 files changed, 254 insertions(+), 51 deletions(-) rename applications/{tari_validator_node/proto/grpc => tari_app_grpc/proto}/validator_node.proto (98%) rename applications/tari_collectibles/src-tauri/src/{ => clients}/base_node_client.rs (100%) create mode 100644 applications/tari_collectibles/src-tauri/src/clients/mod.rs create mode 100644 applications/tari_collectibles/src-tauri/src/clients/validator_node_client.rs rename applications/tari_collectibles/src-tauri/src/{ => clients}/wallet_client.rs (100%) create mode 100644 applications/tari_collectibles/src-tauri/src/commands/tips/mod.rs create mode 100644 applications/tari_collectibles/src-tauri/src/commands/tips/tip002.rs create mode 100644 applications/tari_collectibles/src-tauri/src/models/tip002_info.rs diff --git a/applications/tari_app_grpc/build.rs b/applications/tari_app_grpc/build.rs index 8d75fb8e6c..bff25c2e61 100644 --- a/applications/tari_app_grpc/build.rs +++ b/applications/tari_app_grpc/build.rs @@ -3,6 +3,13 @@ fn main() -> Result<(), Box> { .build_client(true) .build_server(true) .format(false) - .compile(&["proto/base_node.proto", "proto/wallet.proto"], &["proto"])?; + .compile( + &[ + "proto/base_node.proto", + "proto/wallet.proto", + "proto/validator_node.proto", + ], + &["proto"], + )?; Ok(()) } diff --git a/applications/tari_app_grpc/proto/base_node.proto b/applications/tari_app_grpc/proto/base_node.proto index b7a568d473..a371b6dc68 100644 --- a/applications/tari_app_grpc/proto/base_node.proto +++ b/applications/tari_app_grpc/proto/base_node.proto @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; - import "types.proto"; package tari.rpc; diff --git a/applications/tari_validator_node/proto/grpc/validator_node.proto b/applications/tari_app_grpc/proto/validator_node.proto similarity index 98% rename from applications/tari_validator_node/proto/grpc/validator_node.proto rename to applications/tari_app_grpc/proto/validator_node.proto index c0971aeeb9..bda36b219f 100644 --- a/applications/tari_validator_node/proto/grpc/validator_node.proto +++ b/applications/tari_app_grpc/proto/validator_node.proto @@ -21,7 +21,8 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. syntax = "proto3"; -package tari.validator_node.rpc; +import "types.proto"; +package tari.rpc; service ValidatorNode { rpc GetMetadata(GetMetadataRequest) returns (GetMetadataResponse); diff --git a/applications/tari_collectibles/src-tauri/src/app_state.rs b/applications/tari_collectibles/src-tauri/src/app_state.rs index 024895d383..c0f4256565 100644 --- a/applications/tari_collectibles/src-tauri/src/app_state.rs +++ b/applications/tari_collectibles/src-tauri/src/app_state.rs @@ -21,16 +21,16 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - base_node_client::BaseNodeClient, + clients::{BaseNodeClient, GrpcValidatorNodeClient, ValidatorNodeClient, WalletClient}, settings::Settings, storage::{ sqlite::{SqliteCollectiblesStorage, SqliteDbFactory}, StorageError, }, - wallet_client::WalletClient, }; use diesel::SqliteConnection; use std::sync::Arc; +use tari_common_types::types::PublicKey; use tauri::async_runtime::RwLock; pub struct AppState { @@ -65,6 +65,20 @@ impl ConcurrentAppState { Ok(client) } + pub async fn connect_validator_node_client( + &self, + _public_key: PublicKey, + ) -> Result { + // todo: convert this GRPC to tari comms + let lock = self.inner.read().await; + let client = GrpcValidatorNodeClient::connect(format!( + "http://{}", + lock.config.validator_node_grpc_address + )) + .await?; + Ok(client) + } + pub async fn create_db(&self) -> Result { self.inner.read().await.db_factory.create_db() } diff --git a/applications/tari_collectibles/src-tauri/src/base_node_client.rs b/applications/tari_collectibles/src-tauri/src/clients/base_node_client.rs similarity index 100% rename from applications/tari_collectibles/src-tauri/src/base_node_client.rs rename to applications/tari_collectibles/src-tauri/src/clients/base_node_client.rs diff --git a/applications/tari_collectibles/src-tauri/src/clients/mod.rs b/applications/tari_collectibles/src-tauri/src/clients/mod.rs new file mode 100644 index 0000000000..04c83bc776 --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/clients/mod.rs @@ -0,0 +1,28 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod base_node_client; +pub use base_node_client::BaseNodeClient; +mod wallet_client; +pub use wallet_client::WalletClient; +mod validator_node_client; +pub use validator_node_client::{GrpcValidatorNodeClient, ValidatorNodeClient}; diff --git a/applications/tari_collectibles/src-tauri/src/clients/validator_node_client.rs b/applications/tari_collectibles/src-tauri/src/clients/validator_node_client.rs new file mode 100644 index 0000000000..5beabd74fe --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/clients/validator_node_client.rs @@ -0,0 +1,44 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use tari_app_grpc::tari_rpc as grpc; + +pub trait ValidatorNodeClient {} + +pub struct GrpcValidatorNodeClient { + client: grpc::validator_node_client::ValidatorNodeClient, +} + +impl GrpcValidatorNodeClient { + pub async fn connect(endpoint: String) -> Result { + let s = Self { + client: grpc::validator_node_client::ValidatorNodeClient::connect(endpoint.clone()) + .await + .map_err(|e| { + format!( + "No connection to validator node. Is it running with gRPC on '{}'? Error: {}", + endpoint, e + ) + })?, + }; + Ok(s) + } +} diff --git a/applications/tari_collectibles/src-tauri/src/wallet_client.rs b/applications/tari_collectibles/src-tauri/src/clients/wallet_client.rs similarity index 100% rename from applications/tari_collectibles/src-tauri/src/wallet_client.rs rename to applications/tari_collectibles/src-tauri/src/clients/wallet_client.rs diff --git a/applications/tari_collectibles/src-tauri/src/commands/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/mod.rs index b3c417f12d..f16bd7b166 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/mod.rs @@ -22,3 +22,4 @@ pub mod accounts; pub mod assets; +pub mod tips; diff --git a/applications/tari_collectibles/src-tauri/src/commands/tips/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/tips/mod.rs new file mode 100644 index 0000000000..e1cc5afffa --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/commands/tips/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod tip002; diff --git a/applications/tari_collectibles/src-tauri/src/commands/tips/tip002.rs b/applications/tari_collectibles/src-tauri/src/commands/tips/tip002.rs new file mode 100644 index 0000000000..9a9715e17f --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/commands/tips/tip002.rs @@ -0,0 +1,53 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + app_state::ConcurrentAppState, + models::Tip002Info, + storage::{AccountsTableGateway, CollectiblesStorage}, +}; +use uuid::Uuid; + +#[tauri::command] +pub async fn tip002_get_info( + account_id: Uuid, + asset_public_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, String> { + // let db = state + // .create_db() + // .await + // .map_err(|e| format!("Could not open DB:{}", e))?; + // let account = db + // .accounts() + // .find(account_id) + // .map_err(|e| format!("Could not find account: {}", e))?; + // let committee = account.committee; + // let validator_client = state.connect_validator_node_client(committee[0]).await?; + // let asset_public_key = PublicKey::from_hex(asset_public_key + // let template_data = tari_tips::tip002::InfoRequest{ + // + // }; + // let template_data = template_data.encode_to_vec(); + // validator_client.call(comittee[0], "tip002", ) + todo!() +} diff --git a/applications/tari_collectibles/src-tauri/src/main.rs b/applications/tari_collectibles/src-tauri/src/main.rs index d49582974f..74a67eca37 100644 --- a/applications/tari_collectibles/src-tauri/src/main.rs +++ b/applications/tari_collectibles/src-tauri/src/main.rs @@ -13,13 +13,12 @@ extern crate diesel; extern crate diesel_migrations; mod app_state; -mod base_node_client; +mod clients; mod commands; mod models; mod schema; mod settings; mod storage; -mod wallet_client; fn main() { let state = ConcurrentAppState::new(); diff --git a/applications/tari_collectibles/src-tauri/src/models/mod.rs b/applications/tari_collectibles/src-tauri/src/models/mod.rs index e60acb6e12..ae96611dfb 100644 --- a/applications/tari_collectibles/src-tauri/src/models/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/models/mod.rs @@ -26,3 +26,5 @@ mod registered_asset_info; pub use registered_asset_info::RegisteredAssetInfo; mod account; pub use account::{Account, NewAccount}; +mod tip002_info; +pub use tip002_info::Tip002Info; diff --git a/applications/tari_collectibles/src-tauri/src/models/tip002_info.rs b/applications/tari_collectibles/src-tauri/src/models/tip002_info.rs new file mode 100644 index 0000000000..023c1ee83e --- /dev/null +++ b/applications/tari_collectibles/src-tauri/src/models/tip002_info.rs @@ -0,0 +1,29 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use serde::Serialize; +#[derive(Serialize)] +pub struct Tip002Info { + symbol: String, + decimals: u8, + total_supply: u128, // TODO: Should be 256 +} diff --git a/applications/tari_collectibles/src-tauri/src/settings.rs b/applications/tari_collectibles/src-tauri/src/settings.rs index 014dd54dc3..c9c090587a 100644 --- a/applications/tari_collectibles/src-tauri/src/settings.rs +++ b/applications/tari_collectibles/src-tauri/src/settings.rs @@ -25,7 +25,7 @@ use std::{env, path::PathBuf}; pub struct Settings { pub(crate) wallet_grpc_address: String, pub(crate) base_node_grpc_address: String, - pub(crate) _favourite_assets: Vec, + pub(crate) validator_node_grpc_address: String, pub(crate) data_dir: PathBuf, } @@ -44,7 +44,8 @@ impl Settings { .unwrap_or_else(|_| "localhost:18143".to_string()), base_node_grpc_address: env::var("BASE_NODE_GRPC_ADDRESS") .unwrap_or_else(|_| "localhost:18142".to_string()), - _favourite_assets: vec!["1234".to_string()], + validator_node_grpc_address: env::var("VALIDATOR_NODE_GRPC_ADDRESS") + .unwrap_or_else(|_| "localhost:18144".to_string()), data_dir, } } diff --git a/applications/tari_collectibles/src-tauri/src/storage/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/mod.rs index 699cbbef70..509cf84c4e 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/mod.rs @@ -24,6 +24,7 @@ use crate::models::{Account, NewAccount}; pub mod sqlite; mod storage_error; pub use storage_error::StorageError; +use uuid::Uuid; pub trait CollectiblesStorage { type Accounts: AccountsTableGateway; @@ -33,4 +34,5 @@ pub trait CollectiblesStorage { pub trait AccountsTableGateway { fn list(&self) -> Result, StorageError>; fn insert(&self, account: NewAccount) -> Result; + fn find(&self, account_id: Uuid) -> Result; } diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs index 68bc4bd9ab..f2e34d60ea 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/mod.rs @@ -89,26 +89,8 @@ impl AccountsTableGateway for SqliteAccountsTableGateway { Ok( results .iter() - .map(|r| { - let mut committee = Vec::with_capacity(r.committee_length as usize); - for i in 0..r.committee_length as usize { - committee - .push(PublicKey::from_bytes(&r.committee_pub_keys[i * 32..(i + 1) * 32]).unwrap()); - } - Account { - id: Uuid::from_slice(&r.id).unwrap(), - asset_public_key: PublicKey::from_bytes(&r.asset_public_key).unwrap(), - name: r.name.clone(), - description: r.description.clone(), - image: r.image.clone(), - committee: if committee.is_empty() { - None - } else { - Some(committee) - }, - } - }) - .collect(), + .map(|r| SqliteAccountsTableGateway::convert_account(r)) + .collect::>()?, ) } @@ -151,4 +133,34 @@ impl AccountsTableGateway for SqliteAccountsTableGateway { }; Ok(result) } + + fn find(&self, account_id: Uuid) -> Result { + let conn = SqliteConnection::establish(self.database_url.as_str())?; + let db_account = schema::accounts::table + .find(Vec::from(account_id.as_bytes().as_slice())) + .get_result(&conn)?; + + SqliteAccountsTableGateway::convert_account(&db_account) + } +} + +impl SqliteAccountsTableGateway { + fn convert_account(r: &models::Account) -> Result { + let mut committee = Vec::with_capacity(r.committee_length as usize); + for i in 0..r.committee_length as usize { + committee.push(PublicKey::from_bytes(&r.committee_pub_keys[i * 32..(i + 1) * 32]).unwrap()); + } + Ok(Account { + id: Uuid::from_slice(&r.id).unwrap(), + asset_public_key: PublicKey::from_bytes(&r.asset_public_key).unwrap(), + name: r.name.clone(), + description: r.description.clone(), + image: r.image.clone(), + committee: if committee.is_empty() { + None + } else { + Some(committee) + }, + }) + } } diff --git a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs index f6f5908ba5..d7ace4fe49 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/sqlite/models/account.rs @@ -23,7 +23,7 @@ use crate::schema::*; use diesel::prelude::*; -#[derive(Queryable, Insertable)] +#[derive(Queryable, Insertable, Identifiable)] pub struct Account { pub id: Vec, pub asset_public_key: Vec, diff --git a/applications/tari_validator_node/build.rs b/applications/tari_validator_node/build.rs index 54391752d1..c6ba71ff75 100644 --- a/applications/tari_validator_node/build.rs +++ b/applications/tari_validator_node/build.rs @@ -21,11 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. fn main() -> Result<(), Box> { - tonic_build::configure() - .build_server(true) - .format(false) - .compile(&["proto/grpc/validator_node.proto"], &["proto"])?; - tari_common::build::ProtobufCompiler::new() .proto_paths(&["proto/p2p"]) .emit_rerun_if_changed_directives() diff --git a/applications/tari_validator_node/src/grpc/conversions.rs b/applications/tari_validator_node/src/grpc/conversions.rs index dbad8bcfb7..ff60b743fd 100644 --- a/applications/tari_validator_node/src/grpc/conversions.rs +++ b/applications/tari_validator_node/src/grpc/conversions.rs @@ -19,16 +19,18 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::grpc::validator_node_rpc as rpc; +use tari_app_grpc::tari_rpc; use tari_crypto::tari_utilities::ByteArray; use tari_dan_core::models::SidechainMetadata; -impl From for rpc::SidechainMetadata { +pub struct _st(tari_rpc::SidechainMetadata); + +impl From for _st { fn from(source: SidechainMetadata) -> Self { - Self { + Self(tari_rpc::SidechainMetadata { asset_public_key: source.asset_public_key().as_bytes().to_vec(), committed_height: source.committed_height().into(), committed_hash: source.committed_hash().clone().into(), - } + }) } } diff --git a/applications/tari_validator_node/src/grpc/mod.rs b/applications/tari_validator_node/src/grpc/mod.rs index de7a0e6019..125971e942 100644 --- a/applications/tari_validator_node/src/grpc/mod.rs +++ b/applications/tari_validator_node/src/grpc/mod.rs @@ -22,7 +22,3 @@ pub(crate) mod conversions; pub mod services; pub(crate) mod validator_node_grpc_server; - -pub mod validator_node_rpc { - tonic::include_proto!("tari.validator_node.rpc"); -} diff --git a/applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs b/applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs index f9a04bef3b..5e46861b7a 100644 --- a/applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs +++ b/applications/tari_validator_node/src/grpc/validator_node_grpc_server.rs @@ -19,8 +19,8 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::grpc::validator_node_rpc as rpc; use std::marker::PhantomData; +use tari_app_grpc::tari_rpc as rpc; use tari_crypto::tari_utilities::ByteArray; use tari_dan_core::{ models::{Instruction, TemplateId, TokenId}, diff --git a/applications/tari_validator_node/src/main.rs b/applications/tari_validator_node/src/main.rs index e57dc1c769..96e812750b 100644 --- a/applications/tari_validator_node/src/main.rs +++ b/applications/tari_validator_node/src/main.rs @@ -25,19 +25,14 @@ mod cmd_args; mod dan_node; mod grpc; mod p2p; -use crate::{ - dan_node::DanNode, - grpc::{ - validator_node_grpc_server::ValidatorNodeGrpcServer, - validator_node_rpc::validator_node_server::ValidatorNodeServer, - }, -}; +use crate::{dan_node::DanNode, grpc::validator_node_grpc_server::ValidatorNodeGrpcServer}; use futures::FutureExt; use log::*; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, process, }; +use tari_app_grpc::tari_rpc::validator_node_server::ValidatorNodeServer; use tari_app_utilities::initialization::init_configuration; use tari_common::{configuration::bootstrap::ApplicationType, exit_codes::ExitCodes, GlobalConfig}; use tari_dan_core::{