-
Notifications
You must be signed in to change notification settings - Fork 2
Upward Unknown will be handled to raise an error #47
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
base: main
Are you sure you want to change the base?
Conversation
Jira-Id: COR-1780
…nsactionOutcome>>
src/main.rs
Outdated
tx.query_opt(&self.insert_cti, &values).await?; | ||
let unwrapped_upwards = match ts.summary.affected_contracts() { | ||
Upward::Known(contracts) => contracts, | ||
Upward::Unknown => Vec::new(), // TODO: is this what we want to do for Unknown? |
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 think clarifications at this level are best handle before the PR, e.g. in the squad channel. Such that we have the fundamental clarifications done before opening the PR.
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.
But basically we should fail if any data we index on is unknown.
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.
throwing err
src/main.rs
Outdated
} | ||
Upward::Unknown => { | ||
//TODO: is this what we want to do for Unknown? | ||
println!( |
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.
Always use the logging framework (log
create here)), never write directly to stdout
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.
done
src/main.rs
Outdated
&id, | ||
]; | ||
tx.query_opt(&self.insert_cti, &values).await?; | ||
let upward_affected_contracts = match ts.summary.affected_contracts() { |
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 think you will better describe your intent here by using the projection BlockItemSummaryDetails::account_transaction
and then call affected_contracts
on that if not None
.
src/main.rs
Outdated
}; | ||
|
||
for upward_contract_address in upward_affected_contracts { | ||
match upward_contract_address { |
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.
Try using let affected = upward_contract_address.known_or_else(|| ...)?;
, I think you will get a more clean looking code instead of using match. These functional combinators are often preferred in Rust
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.
changing
src/main.rs
Outdated
@@ -494,31 +519,42 @@ async fn get_last_block_height( | |||
/// | |||
/// The return value of [`None`] means there are no understandable CIS2 logs | |||
/// produced. | |||
fn get_cis2_events(bi: &BlockItemSummary) -> Option<Vec<(ContractAddress, Vec<cis2::Event>)>> { | |||
fn get_cis2_events(bi: &BlockItemSummary) -> Cis2Result { |
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'm not convinced using type aliases specific to a function makes the code more readable. Maybe the type ContractEffects
is ok, as it is not specific to a function, but Cis2Result
is too much aliasing.
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.
this one came about when the clippy command was run, complained about the type in the signature being too complex of some sort. Hence, why I broke them down to types. Will take a look again if I can lessen the number of types.
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 you don't include Result
and Option
in the alias, I think it will be more readable, and clippy should not complain still.
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.
have amendded
src/main.rs
Outdated
let affected_addresses = match summary.affected_addresses() { | ||
Upward::Known(addresses) => addresses, | ||
Upward::Unknown => Vec::new(), // returning empty vec if unknown, the next line will be processing zero length vec anyway, so nothing happens | ||
}; | ||
|
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.
This is not correct behaviour, since you will ignore transaction types that are unknown. And these transaction may actually affect accounts and we should index those effects then. You can project to account_transaction
again here before calling affected_addresses
, but you will need to fail when that function returns Unknown
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.
adding an error throwing
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.
throwing an error seems missing.
src/main.rs
Outdated
Upward::Known(special_transaction_outcome) => { | ||
Ok(Some(special_transaction_outcome)) | ||
} | ||
Upward::Unknown => Ok(None), // we ignore unknown special events, treat this as no special ones |
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 ignore unknown special events? We may need to index them
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.
changing to throwing an error
src/main.rs
Outdated
None => { | ||
let init = bi.contract_init()?; | ||
let cis2 = init | ||
let init = bi.contract_init().ok_or(DatabaseError::OtherError(anyhow!( |
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.
You changed the semantics here I believe? Before None
was returned if contract_init()
returned None
, now it returns an Err
src/main.rs
Outdated
.ok()?; | ||
Some(vec![(init.address, cis2)]) | ||
.collect::<Result<_, _>>() | ||
.map_err(|_| { |
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.
Same 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.
I think some of the implemented behaviour is not correct when considering the role of the indexer, as noted in the comments above.
In addition, it will help to document any decisions made in the PR description (here about how to handle Unknown
, but also in general).
CHANGELOG.md
Outdated
@@ -2,6 +2,8 @@ | |||
|
|||
## Unreleased changes | |||
|
|||
- Added Upward usage in the transaction-logger service and throwing errors for unknown cases for affected_contracts |
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.
Please correctly highlight name/fileName/pathName/typeNames/functionName...
as I already pointed that out in the previous two PRs. I think no need to mention transaction-logger
service as this is the changelog
about the transaction-logger
(meaning redundant info).
src/main.rs
Outdated
@@ -258,7 +263,7 @@ impl PreparedStatements { | |||
block_time: Timestamp, | |||
block_height: AbsoluteBlockHeight, | |||
ts: &BlockItemSummaryWithCanonicalAddresses, | |||
) -> Result<(), postgres::Error> { | |||
) -> Result<(), DatabaseError> { |
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 our Unkown
error is not really a DatabaseError
(the error doesn't come from the database), I would create a new error type e.g.:
/// A collection of possible errors that can happen while indexing blocks into the database.
#[derive(Debug, Error)]
pub enum IndexingError {
/// Database error.
#[error("Error using the database {0}.")]
PostgresError(#[from] postgres::Error),
/// ...
#[error("Please update the rust SDK. Type {0} is unkown.")]
Unkown(String),
}
This general error case is a bit weird in my opinion (meaning an error for everything).
/// Other errors while processing database data.
#[error("Error happened on database thread {0}.")]
OtherError(#[from] anyhow::Error),
src/main.rs
Outdated
logs.iter().map(cis2::Event::try_from).collect(); | ||
evs.map(|ev| (ca, ev)).map_err(|_| { | ||
DatabaseError::OtherError(anyhow!("Failed to parse CIS2 event")) | ||
}) |
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.
This seems not a database
error (meaning that a problem with the database exists). I am also not a fan of having everything be an otherError
. I think it would be also good to propagate error messages such as e
.
logs.iter().map(cis2::Event::try_from).collect(); | |
evs.map(|ev| (ca, ev)).map_err(|_| { | |
DatabaseError::OtherError(anyhow!("Failed to parse CIS2 event")) | |
}) | |
logs.iter().map(cis2::Event::try_from).collect(); | |
evs.map(|ev| (ca, ev)).map_err(|e| { | |
IndexerError::Parsing(anyhow!("Failed to parse CIS2 event: {e} ")) | |
}) |
src/main.rs
Outdated
), | ||
.collect(); | ||
|
||
events.map(Some) |
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.
when does the function get_cis2_events
return Ok(None)
? An option as return value only makes sense if None
is returned at some point?
src/main.rs
Outdated
DatabaseError::OtherError(anyhow!("failed to parse CIS2 event: {e}")) | ||
})?; | ||
|
||
Ok(Some(vec![(init.address, cis2)])) |
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.
when does the function get_cis2_events
return Ok(None)
? An option as return value only makes sense if None
is returned at some point?
src/main.rs
Outdated
.map(|upward| match upward { | ||
Upward::Known((ca, logs)) => { | ||
let evs: Result<Vec<_>, _> = | ||
logs.iter().map(cis2::Event::try_from).collect(); |
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 you have transaction logs here (that are not smart contract cis2 logs) e.g. a contract not related to tokens, then you will try to parse the logs into cis2 events and fail the parsing and return an error which will stop the indexer from progressing. I don't think that is what we want.
src/main.rs
Outdated
let affected_addresses = match summary.affected_addresses() { | ||
Upward::Known(addresses) => addresses, | ||
Upward::Unknown => Vec::new(), // returning empty vec if unknown, the next line will be processing zero length vec anyway, so nothing happens | ||
}; | ||
|
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.
throwing an error seems missing.
src/main.rs
Outdated
Upward::Known(special_transaction_outcome) => { | ||
Ok(Some(special_transaction_outcome)) | ||
} | ||
Upward::Unknown => Err(Status::unknown( |
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.
This seems not a NodeError
.
/// Error establishing connection.
#[error("GRPC error during node query {0}.")]
NetworkError(#[from] v2::Status),
```
also amended the general text of the errors IndexingError
CHANGELOG.md
Outdated
- Handling Upward Known and Unknown usage from main fn get_cis2_events | ||
- Handling Upward Known and Unknown usage from BlockItemSummary fn affected_contracts | ||
- Handling Upward Known and Unknown usage from Client fn get_block_special_events | ||
- Added IndexingError type in lib |
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 don't think this is a very useful changelog. I think it's best if the changelog focuses on behavioural changes, or what the effects of the changes are, rather than the implementation details, which are difficult to interpret without context. In this case, I think the relevant point is updating the Rust SDK for better forwards compatibility with future node versions, and revised error reporting for unknown transaction and event types.
src/lib.rs
Outdated
/// Parsing errors. | ||
#[error("Error happened parsing {0}.")] | ||
ParsingError(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.
This doesn't seem to be used anywhere - what is it for?
src/lib.rs
Outdated
#[error("Please update the rust SDK. Error could be due to {0}.")] | ||
Unknown(String), | ||
/// Other errors while processing database data. | ||
#[error("Error happened on database thread {0}.")] | ||
OtherError(#[from] anyhow::Error), |
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.
Some of these error descriptions are a bit vague or unhelpful.
src/main.rs
Outdated
let upward_affected_contracts = ts.summary.affected_contracts().known_or_else(|| { | ||
log::error!("Unknown upward error on ContractAddress {:?}", ts.summary); | ||
IndexingError::Unknown("Unknown upward error on ContractAddress".to_string()) | ||
})?; // if Unknown, throw an error |
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.
An error occurs here if the BlockItemSummary is of an unknown type. A more appropriate error might be something like: "Could not determine affected contracts for a BlockItemSummary of unknown type."
src/main.rs
Outdated
for upward_contract_address in upward_affected_contracts { | ||
let affected = upward_contract_address.known_or_else(|| { | ||
log::error!("Unknown upward error on ContractAddress {:?}", ts.summary); | ||
IndexingError::Unknown("Unknown upward error on ContractAddress".to_string()) | ||
})?; // encountered unknown contract_address, throw an error to prevent transaction insertion |
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.
Here, the error would be due to an unknown AccountTransactionEffects
type. "Could not determine affected contracts of an unknown type of AccountTransactionEffects."
src/main.rs
Outdated
return Err(IndexingError::Unknown( | ||
"Unknown error in contract_update_logs".to_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.
This occurs if the contract invocation summary includes an unknown ContractTraceElement.
src/main.rs
Outdated
let init = bi | ||
.contract_init() | ||
.ok_or_else(|| IndexingError::OtherError(anyhow!("Contract init failed")))?; |
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 don't think this is supposed to fail in this way. contract_init
will return None
if the event is not a contract initialization - in which case this function should also return None
(or an empty vector).
src/main.rs
Outdated
.filter_map(|ev| cis2::Event::try_from(ev).ok()) | ||
.collect(); |
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 like this may be a change in behaviour - but possibly it was incorrect before. It appears that previously if any of the events was not a CIS2 event, then previously the function would return None
, whereas now non-CIS2 events are ignored, but CIS2 events are retained. Probably this change should be documented if it's intentional.
src/main.rs
Outdated
Ok(Some(special_transaction_outcome)) | ||
} | ||
Upward::Unknown => Err(Status::unknown( | ||
"Unknown upward error on SpecialTransactionOutcome", |
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.
"Unknown upward error on SpecialTransactionOutcome", | |
"Unknown SpecialTransactionOutcome type", |
src/main.rs
Outdated
let affected_addresses = summary.affected_addresses(); | ||
let affected_addresses = summary | ||
.affected_addresses() | ||
.known_or_else(|| Status::unknown("Unknown upward error on AccountAddress"))?; // if unknown, throw Err Status::unknown |
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.
Maybe the error should be something like "Could not determine affected addresses for BlockItem".
Jira-Id: COR-1780
Purpose
Enabling forward compatibility in transaction-logger with the recent changes on rust SDK
Changes
Ensuring function calls are handling Upward and if Upward.Unknown is encountered, an error will be raised. The insert_db overall would see this error and complain.
Have introduced also IndexingError, to wrap the different error types currently used within here, so that we could return at the higher level of the call. Using DatabaseError.Other does not appear to fit or could lead to inaccurate error display.
Checklist
hard-to-understand areas.