-
Notifications
You must be signed in to change notification settings - Fork 251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refine explorer api #131
refine explorer api #131
Conversation
|
http://127.0.0.1:8200/api/explorer/v1/blocks/2510
|
exonum/src/api/mod.rs
Outdated
@@ -44,6 +45,7 @@ impl ::std::fmt::Display for ApiError { | |||
impl ::std::error::Error for ApiError { | |||
fn description(&self) -> &str { | |||
match *self { | |||
ApiError::Service(ref string) => string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per @defuz request , not deepening on detailing and making errors a bit more standardized. Not to invest much time now. Or ever?
As per @defuz request no tests for explorer-api, seems to be working fine after testing manually. |
http://127.0.0.1:8200/api/explorer/v1/blocks?count=950&from=3000&skip_empty_blocks=true
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have one concern about skip_empty_blocks
: bool
flag is not very clear, especially on the call site. Can we split get_blocks
function into two?
I'm even remember a similar discussion in some other pull request...
exonum/src/explorer/explorer_api.rs
Outdated
@@ -74,11 +70,14 @@ impl Api for ExplorerApi { | |||
} | |||
_ => None, | |||
}; | |||
skip_empty_blocks = match map.find(&["skip_empty_blocks"]) { | |||
Some(&Value::String(ref skip_str)) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason not to declare skip_empty_blocks
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DarkEld3r what specific pr do you imply?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this comment: let skip_empty_blocks = match map.find(...
.
exonum/src/explorer/mod.rs
Outdated
@@ -91,22 +91,28 @@ impl<'a> BlockchainExplorer<'a> { | |||
Ok(res) | |||
} | |||
|
|||
pub fn blocks_range(&self, count: u64, from: Option<u64>) -> StorageResult<Vec<BlockInfo>> { | |||
pub fn blocks_range(&self, count: u64, upper: Option<u64>, skip_empty_blocks: bool) -> StorageResult<Vec<Block>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reasoning for skip_empty_blocks
request param? If I understand correctly, it seems to harm security because with some blocks skipped, it becomes impossible to check the integrity of the blockchain (the fact that each block references the previous one, i.e., commits to the entire previous state of the blockchain).
If the reasoning is skipping downloading some blocks for lightweight clients, skip lists (e.g., referencing not only the prev block, but say, the nearest block at a height divisible by 100) seems like a more versatile decision. For example, skip lists allow to skip non-empty blocks; and it's possible to build hierarchical skip lists in order to compact the data returned to lightweight clients even more. OTOH, there are the same security issues with skip lists as with skipping empty blocks; they increase the necessary trust to third parties from the client's side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@slowli yes, it's for client. for ui only. we'll make a separate pr for indexing nonempty blocks and then actually returning last n nonempty blocks instead of nonempty blocks among last n blocks.i'll look into the skip lists, but i assume it's a heavier change, involving storage and protocol and not local to api only.
@DarkEld3r
how to split and what for? |
OK, now I see that it will be not so easy, so it is probably not worth it. |
One more time on that. 😄 First of all, I don't insist on that, but as for me, unnamed So this can look the following way: fn blocks(&self, count: u64, upper: Option<u64>) -> StorageResult<Vec<Block>> {
// Current implementation without skipping empty blocks.
// ...
}
fn non_empty_blocks(&self, count: u64, upper: Option<u64>) -> StorageResult<Vec<Block>> {
let blocks = blocks()?;
let schema = Schema::new(&view);
blocks.filter(|block| schema.block_txs(block.height()).is_empty()).collect();
} Then again, probably that solution is suboptimal, so I'm not insisting. At second thought, it will probably not be used in performance critical parts. Plus implementation can be updated after #76 is closed. |
exonum/src/api/mod.rs
Outdated
@@ -23,6 +23,7 @@ use storage::{Result as StorageResult, Error as StorageError}; | |||
|
|||
#[derive(Debug)] | |||
pub enum ApiError { | |||
Service(String), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not Box<StdError>
?
pub from: Option<u64>, | ||
pub count: u64, | ||
} | ||
const MAX_BLOCKS_PER_REQUEST: u64 = 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is not better to give the ability to set this variable via configuration?
exonum/src/explorer/explorer_api.rs
Outdated
Err(e) => Err(ApiError::Storage(e)), | ||
fn get_blocks(&self, count: u64, from: Option<u64>, skip_empty_blocks: bool) -> Result<Vec<Block>, ApiError> { | ||
if count > MAX_BLOCKS_PER_REQUEST { | ||
return Err(ApiError::IncorrectRequest) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the IncorrectRequest
need to have a short description why the error occured. Is it possible in http responses?
@deniskolodin we could transfer to using |
@gisochre I agree that's better to change error handling in a separate PR and I'm even ready to propose one after you merge these changes. I believe that the code will be easier to maintain. |
|
ApiError::FileNotFound(_) => "FileNotFound", | ||
ApiError::NotFound => "NotFound", | ||
ApiError::FileToBig => "FileToBig", | ||
ApiError::FileTooBig => "FileToBig", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update info too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose error descriptions should be slightly different: "File not found" instead of "FileNotFound", etc.
@@ -87,7 +89,8 @@ impl From<ApiError> for IronError { | |||
use std::error::Error; | |||
|
|||
let mut body = BTreeMap::new(); | |||
body.insert("type", e.description().into()); | |||
body.insert("debug", format!("{:?}", e)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it debug, may be we should control output with some variables?
Or give another name, just to not scare user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vldm what you mean?
ApiError::FileNotFound(_) => "FileNotFound", | ||
ApiError::NotFound => "NotFound", | ||
ApiError::FileToBig => "FileToBig", | ||
ApiError::FileTooBig => "FileToBig", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose error descriptions should be slightly different: "File not found" instead of "FileNotFound", etc.
} | ||
_ => None, | ||
}; | ||
let skip_empty_blocks: bool = match map.find(&["skip_empty_blocks"]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably, type declaration isn't needed here.
It was decided to merge it AS IS, but need to be improved in separate PR (depends on #60). @gisochre |
Inherit Transaction from Message Closes #131 See merge request !98 Former-commit-id: b1ff1fdb61149eb968d875c25b6e2ef9dfb84e9f
refine explorer api Former-commit-id: 9107f2e7d5fb370a219c16d58608e24e404f330c
Inherit Transaction from Message Closes exonum#131 See merge request !98
#108
#78