diff --git a/packages/api/src/controllers/DataContractsController.js b/packages/api/src/controllers/DataContractsController.js index 5b63be0bb..6367ae65d 100644 --- a/packages/api/src/controllers/DataContractsController.js +++ b/packages/api/src/controllers/DataContractsController.js @@ -6,21 +6,25 @@ class DataContractsController { } getDataContracts = async (request, response) => { - const dataContracts = await this.knex - .select('data_contracts.identifier as identifier') - .from('data_contracts') - .orderBy('id', 'desc') - .limit(30) + const subquery = this.knex('data_contracts') + .select(this.knex.raw('data_contracts.id as id, data_contracts.identifier as identifier, data_contracts.version as version, rank() over (partition by identifier order by version desc) rank')) + .as('data_contracts') + + const rows = await this.knex(subquery) + .select('id', 'identifier', 'version', 'rank') + .where('rank', '=', 1) - response.send(dataContracts.map(dataContract => DataContract.fromJSON(dataContract))); + response.send(rows.map(dataContract => DataContract.fromJSON(dataContract))); } getDataContractByIdentifier = async (request, response) => { const {identifier} = request.params const rows = await this.knex('data_contracts') - .select('data_contracts.identifier as identifier', 'data_contracts.schema as schema') - .where('data_contracts.identifier', identifier); + .select('data_contracts.identifier as identifier', 'data_contracts.schema as schema', 'data_contracts.version as version') + .where('data_contracts.identifier', identifier) + .orderBy('id', 'desc') + .limit(1); const [row] = rows @@ -28,7 +32,7 @@ class DataContractsController { response.status(404).send({message: 'not found'}) } - response.send({identifier: row.identifier, schema: row.schema}); + response.send({identifier: row.identifier, schema: row.schema, version: row.version}); } } diff --git a/packages/api/src/models/DataContract.js b/packages/api/src/models/DataContract.js index a3ee51493..9ce65633e 100644 --- a/packages/api/src/models/DataContract.js +++ b/packages/api/src/models/DataContract.js @@ -1,11 +1,13 @@ module.exports = class DataContract { identifier + version - constructor(identifier) { + constructor(identifier, version) { this.identifier = identifier; + this.version = version; } - static fromJSON({identifier}) { - return new DataContract(identifier) + static fromJSON({identifier, version}) { + return new DataContract(identifier, version) } } diff --git a/packages/frontend/src/routes/block/block.route.js b/packages/frontend/src/routes/block/block.route.js index 131a42116..9c291a196 100644 --- a/packages/frontend/src/routes/block/block.route.js +++ b/packages/frontend/src/routes/block/block.route.js @@ -13,7 +13,6 @@ function BlockRoute() { const {block} = useLoaderData(); const txHashes = block?.txs || []; - console.log(block) return (
diff --git a/packages/frontend/src/routes/dataContract/data.contract.route.js b/packages/frontend/src/routes/dataContract/data.contract.route.js index efaaa94d1..fb9348795 100644 --- a/packages/frontend/src/routes/dataContract/data.contract.route.js +++ b/packages/frontend/src/routes/dataContract/data.contract.route.js @@ -14,7 +14,8 @@ function DataContractRoute() { return (
- Identifier: {dataContract.identifier} + Identifier: {dataContract.identifier} + Version: {dataContract.version}
{JSON.stringify(dataContract.schema, null, 2)} diff --git a/packages/frontend/src/routes/dataContract/data_contract.css b/packages/frontend/src/routes/dataContract/data_contract.css index d7108d53b..d58636f14 100644 --- a/packages/frontend/src/routes/dataContract/data_contract.css +++ b/packages/frontend/src/routes/dataContract/data_contract.css @@ -5,6 +5,13 @@ word-wrap: break-word; max-width: 100%; box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; +} + +.data_contract_identifier span { + padding: 10px; } .data_contract_schema { diff --git a/packages/indexer/migrations/V12__add_data_contract_version.sql b/packages/indexer/migrations/V12__add_data_contract_version.sql new file mode 100644 index 000000000..e5c0d67ba --- /dev/null +++ b/packages/indexer/migrations/V12__add_data_contract_version.sql @@ -0,0 +1,7 @@ +ALTER TABLE data_contracts +DROP CONSTRAINT identifier_unique; + +ALTER TABLE data_contracts +ADD COLUMN "version" int not null; + +CREATE INDEX data_contract_id_identifier ON data_contracts(id,identifier); diff --git a/packages/indexer/src/entities/data_contract.rs b/packages/indexer/src/entities/data_contract.rs new file mode 100644 index 000000000..4304fe55c --- /dev/null +++ b/packages/indexer/src/entities/data_contract.rs @@ -0,0 +1,63 @@ +use std::collections::BTreeMap; +use std::time::SystemTime; +use chrono::{DateTime, Utc}; +use dpp::data_contract::DocumentName; +use dpp::data_contract::serialized_version::DataContractInSerializationFormat; +use dpp::identifier::Identifier; +use dpp::platform_value::string_encoding::Encoding; +use dpp::platform_value::Value; +use dpp::state_transition::data_contract_create_transition::DataContractCreateTransition; +use dpp::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0; +use dpp::state_transition::data_contract_update_transition::DataContractUpdateTransition; +use dpp::state_transition::StateTransition; +use tokio_postgres::Row; + +#[derive(Clone)] +pub struct DataContract { + pub identifier: Identifier, + pub schema: BTreeMap, + pub version: u32, +} + +impl From for DataContract { + fn from(state_transition: DataContractCreateTransition) -> Self { + + match state_transition { + DataContractCreateTransition::V0(data_contract_create_transition) => { + let data_contract = data_contract_create_transition.data_contract; + + match data_contract { + DataContractInSerializationFormat::V0(data_contract) => { + let id = data_contract.id; + let version = data_contract.version; + let schema = data_contract.document_schemas; + + return DataContract{ identifier: id, schema, version }; + } + } + } + } + } +} + +impl From for DataContract { + fn from(state_transition: DataContractUpdateTransition) -> Self { + + match state_transition { + DataContractUpdateTransition::V0(data_contract_update_transition) => { + let data_contract = data_contract_update_transition.data_contract; + + match data_contract { + DataContractInSerializationFormat::V0(data_contract) => { + let id = data_contract.id; + let version = data_contract.version; + let schema = data_contract.document_schemas; + + return DataContract{ identifier: id, schema, version }; + } + } + } + } + } +} + diff --git a/packages/indexer/src/entities/mod.rs b/packages/indexer/src/entities/mod.rs index 40b9296db..72b250d26 100644 --- a/packages/indexer/src/entities/mod.rs +++ b/packages/indexer/src/entities/mod.rs @@ -1,2 +1,3 @@ pub mod block_header; pub mod block; +pub mod data_contract; diff --git a/packages/indexer/src/indexer/mod.rs b/packages/indexer/src/indexer/mod.rs index 3465d54ea..8a34ebbdf 100644 --- a/packages/indexer/src/indexer/mod.rs +++ b/packages/indexer/src/indexer/mod.rs @@ -68,7 +68,6 @@ impl Indexer { match result { Ok(_) => { - println!("Successfully indexed block with height {}", block_height); break; } Err(err) => { diff --git a/packages/indexer/src/processor/psql/dao/mod.rs b/packages/indexer/src/processor/psql/dao/mod.rs index 785efeeb7..0fad682c0 100644 --- a/packages/indexer/src/processor/psql/dao/mod.rs +++ b/packages/indexer/src/processor/psql/dao/mod.rs @@ -8,6 +8,7 @@ use dpp::state_transition::data_contract_create_transition::DataContractCreateTr use sha256::{digest}; use base64::{Engine as _, engine::{general_purpose}}; use crate::entities::block_header::BlockHeader; +use crate::entities::data_contract::DataContract; pub struct PostgresDAO { connection_pool: Pool, @@ -45,18 +46,20 @@ impl PostgresDAO { client.query(&stmt, &[&hash, &data, &st_type, &index, &block_hash]).await.unwrap(); } - pub async fn create_data_contract(&self, state_transition: DataContractCreateTransition) { - let id = state_transition.data_contract().id(); + pub async fn create_data_contract(&self, data_contract: DataContract) { + let id = data_contract.identifier; let id_str = id.to_string(Encoding::Base58); - let schema = state_transition.data_contract().document_schemas().clone(); + let schema = data_contract.schema; let schema_decoded = serde_json::to_value(schema).unwrap(); - let query = "INSERT INTO data_contracts(identifier, schema) VALUES ($1, $2);"; + let version = data_contract.version as i32; + + let query = "INSERT INTO data_contracts(identifier, schema, version) VALUES ($1, $2, $3);"; let client = self.connection_pool.get().await.unwrap(); let stmt = client.prepare_cached(query).await.unwrap(); - client.query(&stmt, &[&id_str, &schema_decoded]).await.unwrap(); + client.query(&stmt, &[&id_str, &schema_decoded, &version]).await.unwrap(); } pub async fn get_block_header_by_height(&self, block_height: i32) -> Result, PoolError> { diff --git a/packages/indexer/src/processor/psql/mod.rs b/packages/indexer/src/processor/psql/mod.rs index 3a08c220a..3d3392869 100644 --- a/packages/indexer/src/processor/psql/mod.rs +++ b/packages/indexer/src/processor/psql/mod.rs @@ -7,8 +7,10 @@ use dpp::state_transition::data_contract_create_transition::DataContractCreateTr use crate::processor::psql::dao::PostgresDAO; use base64::{Engine as _, engine::{general_purpose}}; use dpp::serialization::PlatformSerializable; +use dpp::state_transition::data_contract_update_transition::DataContractUpdateTransition; use crate::decoder::decoder::StateTransitionDecoder; use crate::entities::block::Block; +use crate::entities::data_contract::DataContract; pub enum ProcessorError { DatabaseError, @@ -50,7 +52,15 @@ impl PSQLProcessor { } pub async fn handle_data_contract_create(&self, state_transition: DataContractCreateTransition) -> () { - self.dao.create_data_contract(state_transition).await; + let data_contract = DataContract::from(state_transition); + + self.dao.create_data_contract(data_contract).await; + } + + pub async fn handle_data_contract_update(&self, state_transition: DataContractUpdateTransition) -> () { + let data_contract = DataContract::from(state_transition); + + self.dao.create_data_contract(data_contract).await; } pub async fn handle_st(&self, block_hash: String, index: i32,state_transition: StateTransition) -> () { @@ -64,13 +74,19 @@ impl PSQLProcessor { st.clone() )).unwrap(); - self.handle_data_contract_create(st).await + self.handle_data_contract_create(st).await; + + println!("Processed DataContractCreate at block hash {}", block_hash); } StateTransition::DataContractUpdate(st) => { st_type = st.state_transition_type() as i32; bytes = PlatformSerializable::serialize_to_bytes(&StateTransition::DataContractUpdate( st.clone() )).unwrap(); + + self.handle_data_contract_update(st).await; + + println!("Processed DataContractUpdate at block hash {}", block_hash); } StateTransition::DocumentsBatch(st) => { st_type = st.state_transition_type() as i32; @@ -135,8 +151,6 @@ impl PSQLProcessor { let state_transition = st_result.unwrap(); self.handle_st(block_hash.clone(), i as i32, state_transition).await; - - println!("Processed DataContractCreate at height {}", block_height); } Ok(())