diff --git a/Cargo.lock b/Cargo.lock index 7946ae2d82..b63b0ab6f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2534,13 +2534,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] @@ -8129,9 +8129,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.8" +version = "0.36.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" dependencies = [ "bitflags", "errno", @@ -12780,12 +12780,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] @@ -12795,7 +12795,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -12804,21 +12813,42 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.34.0" @@ -12831,6 +12861,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.34.0" @@ -12843,6 +12879,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.34.0" @@ -12855,6 +12897,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.34.0" @@ -12867,12 +12915,24 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" @@ -12885,6 +12945,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" diff --git a/chain/api/src/message.rs b/chain/api/src/message.rs index d4144fe9a0..020bd3a552 100644 --- a/chain/api/src/message.rs +++ b/chain/api/src/message.rs @@ -14,6 +14,7 @@ use starcoin_types::{ transaction::Transaction, }; use starcoin_vm_types::access_path::AccessPath; +use starcoin_vm_types::write_set::WriteSet; #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] @@ -60,6 +61,7 @@ pub enum ChainRequest { access_path: Option, }, GetBlockInfos(Vec), + GetTransactionWriteSet(HashValue), } impl ServiceRequest for ChainRequest { @@ -88,4 +90,5 @@ pub enum ChainResponse { HashVec(Vec), TransactionProof(Box>), BlockInfoVec(Box>>), + TransactionWriteSet(Option), } diff --git a/chain/api/src/service.rs b/chain/api/src/service.rs index 8ba6adce0e..a90b8ec170 100644 --- a/chain/api/src/service.rs +++ b/chain/api/src/service.rs @@ -15,6 +15,7 @@ use starcoin_types::{ startup_info::StartupInfo, }; use starcoin_vm_types::access_path::AccessPath; +use starcoin_vm_types::write_set::WriteSet; /// Readable block chain service trait pub trait ReadableChainService { @@ -72,6 +73,8 @@ pub trait ReadableChainService { ) -> Result>; fn get_block_infos(&self, ids: Vec) -> Result>>; + + fn get_transaction_write_set(&self, hash: HashValue) -> Result>; } /// Writeable block chain service trait @@ -139,6 +142,8 @@ pub trait ChainAsyncService: ) -> Result>; async fn get_block_infos(&self, hashes: Vec) -> Result>>; + + async fn get_transaction_write_set(&self, hash: HashValue) -> Result>; } #[async_trait::async_trait] @@ -436,4 +441,15 @@ where bail!("get block_infos error") } } + + async fn get_transaction_write_set(&self, hash: HashValue) -> Result> { + let response = self + .send(ChainRequest::GetTransactionWriteSet(hash)) + .await??; + if let ChainResponse::TransactionWriteSet(write_set) = response { + Ok(write_set) + } else { + bail!("get get_write_set error") + } + } } diff --git a/chain/service/src/chain_service.rs b/chain/service/src/chain_service.rs index f7b32799d1..5b593b7dac 100644 --- a/chain/service/src/chain_service.rs +++ b/chain/service/src/chain_service.rs @@ -27,6 +27,7 @@ use starcoin_types::{ }; use starcoin_vm_runtime::metrics::VMMetrics; use starcoin_vm_types::access_path::AccessPath; +use starcoin_vm_types::write_set::WriteSet; use std::sync::Arc; /// A Chain reader service to provider Reader API. @@ -232,6 +233,9 @@ impl ServiceHandler for ChainReaderService { ChainRequest::GetBlockInfos(ids) => Ok(ChainResponse::BlockInfoVec(Box::new( self.inner.get_block_infos(ids)?, ))), + ChainRequest::GetTransactionWriteSet(hash) => Ok(ChainResponse::TransactionWriteSet( + self.inner.get_transaction_write_set(hash)?, + )), } } } @@ -416,6 +420,10 @@ impl ReadableChainService for ChainReaderServiceInner { fn get_block_infos(&self, ids: Vec) -> Result>> { self.storage.get_block_infos(ids) } + + fn get_transaction_write_set(&self, hash: HashValue) -> Result> { + self.storage.get_write_set(hash) + } } #[cfg(test)] diff --git a/chain/src/chain.rs b/chain/src/chain.rs index ea118b890c..8e3d56b9b0 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -463,6 +463,7 @@ impl BlockChain { let block_id = block.id(); let txn_infos = executed_data.txn_infos; let txn_events = executed_data.txn_events; + let txn_write_set = executed_data.txn_write_sets; debug_assert!( txn_events.len() == txn_infos.len(), @@ -504,6 +505,7 @@ impl BlockChain { storage.commit_block(block.clone())?; storage.save_block_info(block_info.clone())?; + storage.save_write_set_batch(txn_write_set)?; watch(CHAIN_WATCH_NAME, "n26"); Ok(ExecutedBlock { block, block_info }) diff --git a/executor/src/block_executor.rs b/executor/src/block_executor.rs index 6318b6dbe4..4d6b9415b3 100644 --- a/executor/src/block_executor.rs +++ b/executor/src/block_executor.rs @@ -9,12 +9,14 @@ use starcoin_types::transaction::TransactionStatus; use starcoin_types::transaction::{Transaction, TransactionInfo}; use starcoin_vm_runtime::metrics::VMMetrics; use starcoin_vm_types::contract_event::ContractEvent; +use starcoin_vm_types::write_set::WriteSet; #[derive(Clone, Debug, Eq, PartialEq)] pub struct BlockExecutedData { pub state_root: HashValue, pub txn_infos: Vec, pub txn_events: Vec>, + pub txn_write_sets: Vec<(HashValue, WriteSet)>, } impl Default for BlockExecutedData { @@ -23,6 +25,7 @@ impl Default for BlockExecutedData { state_root: HashValue::zero(), txn_events: vec![], txn_infos: vec![], + txn_write_sets: vec![], } } } @@ -53,12 +56,13 @@ pub fn block_execute( } TransactionStatus::Keep(status) => { chain_state - .apply_write_set(write_set) + .apply_write_set(write_set.clone()) .map_err(BlockExecutorError::BlockChainStateErr)?; let txn_state_root = chain_state .commit() .map_err(BlockExecutorError::BlockChainStateErr)?; + #[cfg(testing)] info!("txn_hash {} gas_used {}", txn_hash, gas_used); executed_data.txn_infos.push(TransactionInfo::new( @@ -68,7 +72,11 @@ pub fn block_execute( gas_used, status, )); + executed_data.txn_events.push(events); + + // Put write set into result + executed_data.txn_write_sets.push((txn_hash, write_set)); } }; } diff --git a/rpc/api/generated_rpc_schema/chain.json b/rpc/api/generated_rpc_schema/chain.json index 78a98739ea..c3ecbe05d3 100644 --- a/rpc/api/generated_rpc_schema/chain.json +++ b/rpc/api/generated_rpc_schema/chain.json @@ -3886,6 +3886,996 @@ ] } } + }, + { + "name": "chain.get_transaction_write_set", + "params": [ + { + "name": "block_hash", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "HashValue", + "type": "string", + "format": "HashValue" + } + } + ], + "result": { + "name": "Option < TransactionWriteSetView >", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_TransactionWriteSetView", + "type": [ + "object", + "null" + ], + "required": [ + "access_write_set", + "hash_value", + "table_write_set" + ], + "properties": { + "access_write_set": { + "type": "array", + "items": { + "type": "object", + "required": [ + "access_path", + "action" + ], + "properties": { + "access_path": { + "type": "object", + "required": [ + "address", + "path" + ], + "properties": { + "address": { + "type": "string", + "format": "AccountAddress" + }, + "path": { + "oneOf": [ + { + "type": "object", + "required": [ + "Code" + ], + "properties": { + "Code": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Resource" + ], + "properties": { + "Resource": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "action": { + "type": "string", + "enum": [ + "Deletion", + "Value" + ] + }, + "value": { + "anyOf": [ + { + "oneOf": [ + { + "type": "object", + "required": [ + "Code" + ], + "properties": { + "Code": { + "type": "object", + "required": [ + "code" + ], + "properties": { + "abi": { + "type": [ + "object", + "null" + ], + "required": [ + "module_name", + "script_functions", + "structs" + ], + "properties": { + "module_name": { + "type": "string" + }, + "script_functions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "args", + "doc", + "module_name", + "name", + "returns", + "ty_args" + ], + "properties": { + "args": { + "description": "The description of regular arguments.", + "type": "array", + "items": { + "description": "The description of a (regular) argument in a script.", + "type": "object", + "required": [ + "doc", + "name", + "type_tag" + ], + "properties": { + "doc": { + "description": "The doc of the arg.", + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "type_tag": { + "description": "The expected type. In Move scripts, this does contain generics type parameters.", + "oneOf": [ + { + "type": "string", + "enum": [ + "Bool", + "U8", + "U64", + "U128", + "Address", + "Signer", + "U16", + "U32", + "U256" + ] + }, + { + "type": "object", + "required": [ + "Vector" + ], + "properties": { + "Vector": { + "$ref": "#/definitions/TypeInstantiation" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Struct" + ], + "properties": { + "Struct": { + "type": "object", + "required": [ + "abilities", + "doc", + "fields", + "module_name", + "name", + "ty_args" + ], + "properties": { + "abilities": { + "type": "string" + }, + "doc": { + "description": "The doc of the struct", + "type": "string" + }, + "fields": { + "description": "fields of the structs.", + "type": "array", + "items": { + "type": "object", + "required": [ + "doc", + "name", + "type_abi" + ], + "properties": { + "doc": { + "description": "doc of the field", + "type": "string" + }, + "name": { + "description": "field name", + "type": "string" + }, + "type_abi": { + "description": "type of the field", + "allOf": [ + { + "$ref": "#/definitions/TypeInstantiation" + } + ] + } + } + } + }, + "module_name": { + "description": "module contains the struct", + "type": "string" + }, + "name": { + "description": "name of the struct", + "type": "string" + }, + "ty_args": { + "type": "array", + "items": { + "description": "The description of a type argument in a script.", + "type": "object", + "required": [ + "abilities", + "name", + "phantom", + "ty" + ], + "properties": { + "abilities": { + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "phantom": { + "type": "boolean" + }, + "ty": { + "$ref": "#/definitions/TypeInstantiation" + } + } + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "TypeParameter" + ], + "properties": { + "TypeParameter": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Reference" + ], + "properties": { + "Reference": { + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/TypeInstantiation" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "doc": { + "description": "Some text comment.", + "type": "string" + }, + "module_name": { + "description": "The module name where the script lives.", + "type": "string" + }, + "name": { + "description": "The public name of the script.", + "type": "string" + }, + "returns": { + "description": "return types", + "type": "array", + "items": { + "oneOf": [ + { + "type": "string", + "enum": [ + "Bool", + "U8", + "U64", + "U128", + "Address", + "Signer", + "U16", + "U32", + "U256" + ] + }, + { + "type": "object", + "required": [ + "Vector" + ], + "properties": { + "Vector": { + "$ref": "#/definitions/TypeInstantiation" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Struct" + ], + "properties": { + "Struct": { + "type": "object", + "required": [ + "abilities", + "doc", + "fields", + "module_name", + "name", + "ty_args" + ], + "properties": { + "abilities": { + "type": "string" + }, + "doc": { + "description": "The doc of the struct", + "type": "string" + }, + "fields": { + "description": "fields of the structs.", + "type": "array", + "items": { + "type": "object", + "required": [ + "doc", + "name", + "type_abi" + ], + "properties": { + "doc": { + "description": "doc of the field", + "type": "string" + }, + "name": { + "description": "field name", + "type": "string" + }, + "type_abi": { + "description": "type of the field", + "allOf": [ + { + "$ref": "#/definitions/TypeInstantiation" + } + ] + } + } + } + }, + "module_name": { + "description": "module contains the struct", + "type": "string" + }, + "name": { + "description": "name of the struct", + "type": "string" + }, + "ty_args": { + "type": "array", + "items": { + "description": "The description of a type argument in a script.", + "type": "object", + "required": [ + "abilities", + "name", + "phantom", + "ty" + ], + "properties": { + "abilities": { + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "phantom": { + "type": "boolean" + }, + "ty": { + "$ref": "#/definitions/TypeInstantiation" + } + } + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "TypeParameter" + ], + "properties": { + "TypeParameter": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Reference" + ], + "properties": { + "Reference": { + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/TypeInstantiation" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + ] + } + }, + "ty_args": { + "description": "The names of the type arguments.", + "type": "array", + "items": { + "description": "The description of a type argument in a script.", + "type": "object", + "required": [ + "abilities", + "name", + "phantom" + ], + "properties": { + "abilities": { + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "phantom": { + "type": "boolean" + } + } + } + } + } + } + }, + "structs": { + "type": "array", + "items": { + "type": "object", + "required": [ + "abilities", + "doc", + "fields", + "module_name", + "name", + "ty_args" + ], + "properties": { + "abilities": { + "type": "string" + }, + "doc": { + "description": "The doc of the struct", + "type": "string" + }, + "fields": { + "description": "fields of the structs.", + "type": "array", + "items": { + "type": "object", + "required": [ + "doc", + "name", + "type_abi" + ], + "properties": { + "doc": { + "description": "doc of the field", + "type": "string" + }, + "name": { + "description": "field name", + "type": "string" + }, + "type_abi": { + "description": "type of the field", + "oneOf": [ + { + "type": "string", + "enum": [ + "Bool", + "U8", + "U64", + "U128", + "Address", + "Signer", + "U16", + "U32", + "U256" + ] + }, + { + "type": "object", + "required": [ + "Vector" + ], + "properties": { + "Vector": { + "$ref": "#/definitions/TypeInstantiation" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Struct" + ], + "properties": { + "Struct": { + "type": "object", + "required": [ + "abilities", + "doc", + "fields", + "module_name", + "name", + "ty_args" + ], + "properties": { + "abilities": { + "type": "string" + }, + "doc": { + "description": "The doc of the struct", + "type": "string" + }, + "fields": { + "description": "fields of the structs.", + "type": "array", + "items": { + "$ref": "#/definitions/FieldABI" + } + }, + "module_name": { + "description": "module contains the struct", + "type": "string" + }, + "name": { + "description": "name of the struct", + "type": "string" + }, + "ty_args": { + "type": "array", + "items": { + "description": "The description of a type argument in a script.", + "type": "object", + "required": [ + "abilities", + "name", + "phantom", + "ty" + ], + "properties": { + "abilities": { + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "phantom": { + "type": "boolean" + }, + "ty": { + "$ref": "#/definitions/TypeInstantiation" + } + } + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "TypeParameter" + ], + "properties": { + "TypeParameter": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Reference" + ], + "properties": { + "Reference": { + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/TypeInstantiation" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "module_name": { + "description": "module contains the struct", + "type": "string" + }, + "name": { + "description": "name of the struct", + "type": "string" + }, + "ty_args": { + "type": "array", + "items": { + "description": "The description of a type argument in a script.", + "type": "object", + "required": [ + "abilities", + "name", + "phantom" + ], + "properties": { + "abilities": { + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "phantom": { + "type": "boolean" + } + } + } + } + } + } + } + } + }, + "code": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Resource" + ], + "properties": { + "Resource": { + "type": "object", + "required": [ + "raw" + ], + "properties": { + "json": true, + "raw": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + { + "type": "null" + } + ] + } + } + } + }, + "hash_value": { + "type": "string", + "format": "HashValue" + }, + "table_write_set": { + "type": "array", + "items": { + "type": "object", + "required": [ + "action", + "table_item" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "Deletion", + "Value" + ] + }, + "table_item": { + "type": "object", + "required": [ + "handle", + "key" + ], + "properties": { + "handle": { + "type": "string", + "format": "AccountAddress" + }, + "key": { + "type": "string" + } + } + }, + "value": { + "type": [ + "string", + "null" + ] + } + } + } + } + }, + "definitions": { + "FieldABI": { + "type": "object", + "required": [ + "doc", + "name", + "type_abi" + ], + "properties": { + "doc": { + "description": "doc of the field", + "type": "string" + }, + "name": { + "description": "field name", + "type": "string" + }, + "type_abi": { + "description": "type of the field", + "allOf": [ + { + "$ref": "#/definitions/TypeInstantiation" + } + ] + } + } + }, + "TypeInstantiation": { + "oneOf": [ + { + "type": "string", + "enum": [ + "Bool", + "U8", + "U64", + "U128", + "Address", + "Signer", + "U16", + "U32", + "U256" + ] + }, + { + "type": "object", + "required": [ + "Vector" + ], + "properties": { + "Vector": { + "$ref": "#/definitions/TypeInstantiation" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Struct" + ], + "properties": { + "Struct": { + "type": "object", + "required": [ + "abilities", + "doc", + "fields", + "module_name", + "name", + "ty_args" + ], + "properties": { + "abilities": { + "type": "string" + }, + "doc": { + "description": "The doc of the struct", + "type": "string" + }, + "fields": { + "description": "fields of the structs.", + "type": "array", + "items": { + "$ref": "#/definitions/FieldABI" + } + }, + "module_name": { + "description": "module contains the struct", + "type": "string" + }, + "name": { + "description": "name of the struct", + "type": "string" + }, + "ty_args": { + "type": "array", + "items": { + "description": "The description of a type argument in a script.", + "type": "object", + "required": [ + "abilities", + "name", + "phantom", + "ty" + ], + "properties": { + "abilities": { + "type": "string" + }, + "name": { + "description": "The name of the argument.", + "type": "string" + }, + "phantom": { + "type": "boolean" + }, + "ty": { + "$ref": "#/definitions/TypeInstantiation" + } + } + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "TypeParameter" + ], + "properties": { + "TypeParameter": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "Reference" + ], + "properties": { + "Reference": { + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/TypeInstantiation" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + ] + } + } + } + } } ] } \ No newline at end of file diff --git a/rpc/api/src/chain/mod.rs b/rpc/api/src/chain/mod.rs index 6901020caf..d3a549fdd8 100644 --- a/rpc/api/src/chain/mod.rs +++ b/rpc/api/src/chain/mod.rs @@ -6,6 +6,7 @@ use crate::types::pubsub::EventFilter; use crate::types::{ BlockHeaderView, BlockInfoView, BlockView, ChainId, ChainInfoView, StrView, TransactionEventResponse, TransactionInfoView, TransactionInfoWithProofView, TransactionView, + TransactionWriteSetView, }; use crate::FutureResult; use jsonrpc_core::Result; @@ -122,6 +123,12 @@ pub trait ChainApi { event_index: Option, access_path: Option>, ) -> FutureResult>>>; + + #[rpc(name = "chain.get_transaction_write_set")] + fn get_transaction_write_set( + &self, + block_hash: HashValue, + ) -> FutureResult>; } #[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)] diff --git a/rpc/api/src/types.rs b/rpc/api/src/types.rs index 44822caa2c..80cfbfe517 100644 --- a/rpc/api/src/types.rs +++ b/rpc/api/src/types.rs @@ -48,12 +48,13 @@ use starcoin_vm_types::transaction::{ }; use starcoin_vm_types::transaction_argument::convert_txn_args; use starcoin_vm_types::vm_status::{DiscardedVMStatus, KeptVMStatus, StatusCode}; -use starcoin_vm_types::write_set::WriteOp; +use starcoin_vm_types::write_set::{WriteOp, WriteSet}; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; use std::str::FromStr; pub type ByteCode = Vec; + mod node_api_types; pub mod pubsub; @@ -1221,6 +1222,7 @@ impl From for TransactionOutputView { } } } + impl From<(AccessPath, WriteOp)> for TransactionOutputAction { fn from((access_path, op): (AccessPath, WriteOp)) -> Self { let (action, value) = match op { @@ -1242,6 +1244,7 @@ impl From<(AccessPath, WriteOp)> for TransactionOutputAction { } } } + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] pub struct TransactionOutputAction { pub access_path: AccessPath, @@ -1822,6 +1825,7 @@ pub struct ConnectLocal; impl ServiceRequest for ConnectLocal { type Response = RpcChannel; } + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AccumulatorInfoView { /// Accumulator root hash @@ -1928,6 +1932,41 @@ impl From for StateKeyView { } } +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct TransactionWriteSetView { + hash_value: HashValue, + access_write_set: Vec, + table_write_set: Vec, +} + +impl TransactionWriteSetView { + pub fn new(hash_value: HashValue, write_set: WriteSet) -> anyhow::Result { + let mut access_write_set = vec![]; + let mut table_item_write_set = vec![]; + for (state_key, op) in write_set { + match state_key { + StateKey::AccessPath(access_path) => { + access_write_set.push((access_path, op)); + } + StateKey::TableItem(table_item) => { + table_item_write_set.push((table_item, op)); + } + } + } + Ok(Self { + hash_value, + access_write_set: access_write_set + .into_iter() + .map(TransactionOutputAction::from) + .collect(), + table_write_set: table_item_write_set + .into_iter() + .map(TransactionOutputTableItemAction::from) + .collect(), + }) + } +} + #[cfg(test)] mod tests { use crate::types::{ByteCodeOrScriptFunction, FunctionId, StrView}; diff --git a/rpc/server/src/module/chain_rpc.rs b/rpc/server/src/module/chain_rpc.rs index 3544155169..69dee617f6 100644 --- a/rpc/server/src/module/chain_rpc.rs +++ b/rpc/server/src/module/chain_rpc.rs @@ -16,7 +16,7 @@ use starcoin_rpc_api::types::pubsub::EventFilter; use starcoin_rpc_api::types::{ BlockHeaderView, BlockInfoView, BlockTransactionsView, BlockView, ChainId, ChainInfoView, SignedUserTransactionView, StrView, TransactionEventResponse, TransactionInfoView, - TransactionInfoWithProofView, TransactionView, + TransactionInfoWithProofView, TransactionView, TransactionWriteSetView, }; use starcoin_rpc_api::FutureResult; use starcoin_state_api::StateView; @@ -469,6 +469,22 @@ where Box::pin(fut.boxed()) } + + fn get_transaction_write_set( + &self, + txn_hash: HashValue, + ) -> FutureResult> { + let service = self.service.clone(); + let fut = async move { + let write_set = service.get_transaction_write_set(txn_hash).await?; + match write_set { + None => Ok(None), + Some(w) => Ok(Some(TransactionWriteSetView::new(txn_hash, w)?)), + } + } + .map_err(map_err); + Box::pin(fut.boxed()) + } } fn try_decode_block_txns(state: &dyn StateView, block: &mut BlockView) -> anyhow::Result<()> { diff --git a/storage/src/lib.rs b/storage/src/lib.rs index e61d882e6d..8e6126e011 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -13,6 +13,7 @@ use crate::storage::{CodecKVStore, CodecWriteBatch, ColumnFamilyName, StorageIns //use crate::table_info::{TableInfoStorage, TableInfoStore}; use crate::transaction::TransactionStorage; use crate::transaction_info::{TransactionInfoHashStorage, TransactionInfoStorage}; +use crate::write_set::WriteSetStorage; use anyhow::{bail, format_err, Error, Result}; use network_types::peer_info::PeerId; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -29,9 +30,11 @@ use starcoin_types::{ startup_info::StartupInfo, }; //use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; +use starcoin_types::write_set::WriteSet; use std::collections::BTreeMap; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; + pub use upgrade::BARNARD_HARD_FORK_HASH; pub use upgrade::BARNARD_HARD_FORK_HEIGHT; @@ -53,6 +56,7 @@ mod tests; pub mod transaction; pub mod transaction_info; mod upgrade; +pub mod write_set; #[macro_use] pub mod storage_macros; @@ -76,6 +80,7 @@ pub const TRANSACTION_INFO_HASH_PREFIX_NAME: ColumnFamilyName = "transaction_inf pub const CONTRACT_EVENT_PREFIX_NAME: ColumnFamilyName = "contract_event"; pub const FAILED_BLOCK_PREFIX_NAME: ColumnFamilyName = "failed_block"; pub const TABLE_INFO_PREFIX_NAME: ColumnFamilyName = "table_info"; +pub const WRITE_SET_PRIFIX_NAME: ColumnFamilyName = "write_set"; ///db storage use prefix_name vec to init /// Please note that adding a prefix needs to be added in vec simultaneously, remember!! @@ -138,9 +143,11 @@ static VEC_PREFIX_NAME_V3: Lazy> = Lazy::new(|| { TRANSACTION_INFO_HASH_PREFIX_NAME, CONTRACT_EVENT_PREFIX_NAME, FAILED_BLOCK_PREFIX_NAME, + WRITE_SET_PRIFIX_NAME, // TABLE_INFO_PREFIX_NAME, ] }); + #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum StorageVersion { @@ -239,6 +246,7 @@ pub trait BlockTransactionInfoStore { ids: Vec, ) -> Result>>; } + pub trait ContractEventStore { /// Save events by key `txn_info_id`. /// As txn_info has accumulator root of events, so there is a one-to-one mapping. @@ -261,6 +269,12 @@ pub trait TransactionStore { fn get_transactions(&self, txn_hash_vec: Vec) -> Result>>; } +pub trait WriteSetStore { + fn get_write_set(&self, hash: HashValue) -> Result>; + fn save_write_set(&self, hash: HashValue, write_set: WriteSet) -> Result<()>; + fn save_write_set_batch(&self, write_set_vec: Vec<(HashValue, WriteSet)>) -> Result<()>; +} + // TODO: remove Arc, we can clone Storage directly. #[derive(Clone)] pub struct Storage { @@ -274,6 +288,7 @@ pub struct Storage { block_info_storage: BlockInfoStorage, event_storage: ContractEventStorage, chain_info_storage: ChainInfoStorage, + write_set_store: WriteSetStorage, // table_info_storage: TableInfoStorage, // instance: StorageInstance, } @@ -293,7 +308,8 @@ impl Storage { AccumulatorStorage::new_transaction_accumulator_storage(instance.clone()), block_info_storage: BlockInfoStorage::new(instance.clone()), event_storage: ContractEventStorage::new(instance.clone()), - chain_info_storage: ChainInfoStorage::new(instance), + chain_info_storage: ChainInfoStorage::new(instance.clone()), + write_set_store: WriteSetStorage::new(instance), // table_info_storage: TableInfoStorage::new(instance), // instance, }; @@ -331,6 +347,7 @@ impl Display for Storage { write!(f, "{}", self.clone()) } } + impl Debug for Storage { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "{}", self) @@ -543,6 +560,20 @@ impl ContractEventStore for Storage { } } +impl WriteSetStore for Storage { + fn get_write_set(&self, hash: HashValue) -> Result> { + self.write_set_store.get_write_set(hash) + } + + fn save_write_set(&self, hash: HashValue, write_set: WriteSet) -> Result<()> { + self.write_set_store.save_write_set(hash, write_set) + } + + fn save_write_set_batch(&self, write_set_vec: Vec<(HashValue, WriteSet)>) -> Result<()> { + self.write_set_store.save_write_set_batch(write_set_vec) + } +} + impl TransactionStore for Storage { fn get_transaction(&self, txn_hash: HashValue) -> Result, Error> { self.transaction_storage.get(txn_hash) @@ -572,6 +603,7 @@ pub trait Store: + TransactionStore + BlockTransactionInfoStore + ContractEventStore + + WriteSetStore + IntoSuper { fn get_transaction_info_by_block_and_index( diff --git a/storage/src/tests/mod.rs b/storage/src/tests/mod.rs index abf8a51ed0..9f2f8f9488 100644 --- a/storage/src/tests/mod.rs +++ b/storage/src/tests/mod.rs @@ -4,3 +4,4 @@ mod test_accumulator; mod test_batch; mod test_block; mod test_storage; +mod test_write_set; diff --git a/storage/src/tests/test_write_set.rs b/storage/src/tests/test_write_set.rs new file mode 100644 index 0000000000..cf34b9bd7d --- /dev/null +++ b/storage/src/tests/test_write_set.rs @@ -0,0 +1,195 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +// use crate::DiemDB; +extern crate chrono; + +use crate::cache_storage::CacheStorage; +use crate::db_storage::DBStorage; +use crate::storage::StorageInstance; +use crate::{Storage, WriteSetStore}; +use starcoin_config::RocksdbConfig; +use starcoin_crypto::HashValue; +use starcoin_types::account_address::AccountAddress; +use starcoin_vm_types::access_path::AccessPath; +use starcoin_vm_types::state_store::state_key::StateKey; +use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::write_set::{WriteOp, WriteSet, WriteSetMut}; + +fn to_write_set(access_path: AccessPath, value: Vec) -> WriteSet { + WriteSetMut::new(vec![ + ( + StateKey::AccessPath(access_path.clone()), + WriteOp::Value(value), + ), + (StateKey::AccessPath(access_path), WriteOp::Deletion), + ]) + .freeze() + .expect("freeze write_set must success.") +} + +fn to_table_item_write_set(table_item: &StateKey, value: Vec) -> WriteSet { + WriteSetMut::new(vec![ + (table_item.clone(), WriteOp::Value(value)), + (table_item.clone(), WriteOp::Deletion), + ]) + .freeze() + .expect("freeze write_set must success.") +} + +#[test] +fn test_put_and_save() { + let tmpdir = starcoin_config::temp_dir(); + let storage = Storage::new(StorageInstance::new_cache_and_db_instance( + CacheStorage::new(None), + DBStorage::new(tmpdir.path(), RocksdbConfig::default(), None).unwrap(), + )) + .unwrap(); + + let access_path = AccessPath::random_resource(); + let state0 = HashValue::random().to_vec(); + let write_set = to_write_set(access_path.clone(), state0.clone()); + let hash = HashValue::random(); + + // Check save + storage + .write_set_store + .save_write_set(hash, write_set) + .expect("Save write set failed"); + let after = storage + .write_set_store + .get_write_set(hash) + .expect("{} Write set not exists!") + .expect("{} Write set not exists!"); + assert!(!after.is_empty()); + let mut iter = after.into_iter(); + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, StateKey::AccessPath(access_path.clone())); + assert_eq!(op, WriteOp::Value(state0)); + + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, StateKey::AccessPath(access_path)); + assert_eq!(op, WriteOp::Deletion); +} +#[test] +fn test_put_and_save_batch() { + let tmpdir = starcoin_config::temp_dir(); + let storage = Storage::new(StorageInstance::new_cache_and_db_instance( + CacheStorage::new(None), + DBStorage::new(tmpdir.path(), RocksdbConfig::default(), None).unwrap(), + )) + .unwrap(); + + let access_path = AccessPath::random_resource(); + let state0 = HashValue::random().to_vec(); + let write_set = to_write_set(access_path.clone(), state0.clone()); + let hash = HashValue::random(); + + let data_batch = vec![(hash, write_set)]; + + // Check save + storage + .write_set_store + .save_write_set_batch(data_batch) + .expect("Save write set failed"); + let after = storage + .write_set_store + .get_write_set(hash) + .expect("{} Write set not exists!") + .expect("{} Write set not exists!"); + assert!(!after.is_empty()); + let mut iter = after.into_iter(); + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, StateKey::AccessPath(access_path.clone())); + assert_eq!(op, WriteOp::Value(state0)); + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, StateKey::AccessPath(access_path)); + assert_eq!(op, WriteOp::Deletion); +} + +#[test] +fn test_put_and_save_table_item() { + let tmpdir = starcoin_config::temp_dir(); + let storage = Storage::new(StorageInstance::new_cache_and_db_instance( + CacheStorage::new(None), + DBStorage::new(tmpdir.path(), RocksdbConfig::default(), None).unwrap(), + )) + .unwrap(); + + let table_item = StateKey::table_item( + TableHandle(AccountAddress::random()), + HashValue::random().to_vec(), + ); + let table_item_val = HashValue::random().to_vec(); + let hash = HashValue::random(); + + storage + .write_set_store + .save_write_set( + hash, + to_table_item_write_set(&table_item, table_item_val.clone()), + ) + .expect("Save write set failed"); + + let after = storage + .write_set_store + .get_write_set(hash) + .expect("{} Write set not exists!") + .expect("{} Write set not exists!"); + + assert!(!after.is_empty()); + + let mut iter = after.into_iter(); + + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, table_item); + assert_eq!(op, WriteOp::Value(table_item_val)); + + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, table_item); + assert_eq!(op, WriteOp::Deletion); +} + +#[test] +fn test_put_and_save_table_item_batch() { + let tmpdir = starcoin_config::temp_dir(); + let storage = Storage::new(StorageInstance::new_cache_and_db_instance( + CacheStorage::new(None), + DBStorage::new(tmpdir.path(), RocksdbConfig::default(), None).unwrap(), + )) + .unwrap(); + + let table_item = StateKey::table_item( + TableHandle(AccountAddress::random()), + HashValue::random().to_vec(), + ); + let table_item_val = HashValue::random().to_vec(); + let hash = HashValue::random(); + + let batch_data = vec![( + hash, + to_table_item_write_set(&table_item, table_item_val.clone()), + )]; + storage + .write_set_store + .save_write_set_batch(batch_data) + .expect("Save write set failed"); + + let after = storage + .write_set_store + .get_write_set(hash) + .expect("{} Write set not exists!") + .expect("{} Write set not exists!"); + + assert!(!after.is_empty()); + + let mut iter = after.into_iter(); + + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, table_item); + assert_eq!(op, WriteOp::Value(table_item_val)); + + let (st_key, op) = iter.next().expect("Error"); + assert_eq!(st_key, table_item); + assert_eq!(op, WriteOp::Deletion); +} diff --git a/storage/src/write_set/mod.rs b/storage/src/write_set/mod.rs new file mode 100644 index 0000000000..8f13298353 --- /dev/null +++ b/storage/src/write_set/mod.rs @@ -0,0 +1,41 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::storage::{CodecKVStore, CodecWriteBatch, ValueCodec}; +use crate::{define_storage, WriteSetStore, WRITE_SET_PRIFIX_NAME}; +use anyhow::Result; +use bcs_ext::BCSCodec; +use starcoin_crypto::HashValue; +use starcoin_types::write_set::WriteSet; + +define_storage!(WriteSetStorage, HashValue, WriteSet, WRITE_SET_PRIFIX_NAME); + +impl ValueCodec for WriteSet { + fn encode_value(&self) -> Result> { + self.encode() + } + + fn decode_value(data: &[u8]) -> Result { + Self::decode(data) + } +} + +impl WriteSetStore for WriteSetStorage { + fn get_write_set(&self, hash: HashValue) -> Result> { + self.get(hash) + } + + fn save_write_set(&self, hash: HashValue, write_set_vec: WriteSet) -> Result<()> { + self.put(hash, write_set_vec) + } + + fn save_write_set_batch(&self, write_set_vec: Vec<(HashValue, WriteSet)>) -> Result<()> { + let batch = CodecWriteBatch::new_puts( + write_set_vec + .into_iter() + .map(|(hash, write_set)| (hash, write_set)) + .collect(), + ); + self.write_batch(batch) + } +} diff --git a/vm/starcoin-transactional-test-harness/src/fork_chain.rs b/vm/starcoin-transactional-test-harness/src/fork_chain.rs index a1438b827b..95d44ef5e0 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_chain.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_chain.rs @@ -15,7 +15,7 @@ use starcoin_rpc_api::chain::{ChainApi, GetBlockOption}; use starcoin_rpc_api::chain::{ChainApiClient, GetBlocksOption}; use starcoin_rpc_api::types::{ BlockInfoView, BlockTransactionsView, BlockView, ChainId, ChainInfoView, - SignedUserTransactionView, TransactionInfoView, TransactionView, + SignedUserTransactionView, TransactionInfoView, TransactionView, TransactionWriteSetView, }; use starcoin_rpc_api::FutureResult; use starcoin_rpc_server::module::map_err; @@ -498,6 +498,16 @@ impl ChainApi for MockChainApi { }; Box::pin(fut.boxed().map_err(map_err)) } + + fn get_transaction_write_set( + &self, + _block_hash: HashValue, + ) -> FutureResult> { + let fut = async move { + bail!("not implemented."); + }; + Box::pin(fut.boxed().map_err(map_err)) + } } fn try_decode_block_txns(state: &dyn StateView, block: &mut BlockView) -> anyhow::Result<()> {