-
Notifications
You must be signed in to change notification settings - Fork 6
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
Create a loan backup file when confirming #226
Conversation
d5756b0
to
8db5a7a
Compare
seems this needs a proper rebase on master - looks like you built on top of Thomas' work but that was force pushed, or are Thomas' commits supposed to be 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.
Looks good - might want to tell the user that the backup is downloaded as file in the browser downloads. Not sure how feasible that is though.
One thing I find somewhat problematic: The stepper does not keep the state. If the extension is collapsed (also happens when changing focus away from the browser window to check the backup on file system...) - I have to press sign again (i.e. go through all the steps...) - this is a bit weird, because in the end I can actually not check the file because that leads to an endless loop of doing all the steps....
@@ -46,13 +46,15 @@ | |||
"@types/react": "^16.9.0", | |||
"@types/react-dom": "^16.9.0", | |||
"@types/uuid": "^8.3.1", | |||
"chakra-ui-steps": "^1.3.0", |
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.
noice! I also found that one and wanted to use it at some point :D
// We get a hold of the wallet to ensure that it is loaded. This is a security mechanism | ||
// to ensure no unauthorized access to the data. | ||
// Ideally all data is encrypted but that's just how it is :) | ||
let _ = current(&name, current_wallet).await.unwrap(); |
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.
couldn't we encrypt using the wallet's key?
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 guess so. I created a ticket for that: #234
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 want the wasm code to blow up less, I would avoid unwrap
s or at least use expect
to document the invariant of why you think it is okay to panic.
I know. @thomaseizinger, do you have an idea here? |
Signed-off-by: Philipp Hoenisch <philipp@hoenisch.at>
The feature allows you to export the loan details in json format. Signed-off-by: Philipp Hoenisch <philipp@hoenisch.at>
Signed-off-by: Philipp Hoenisch <philipp@hoenisch.at>
Signed-off-by: Philipp Hoenisch <philipp@hoenisch.at>
8db5a7a
to
ebfe3fa
Compare
I created a ticket for this: #235 |
Signed-off-by: Philipp Hoenisch <philipp@hoenisch.at>
Co-authored-by: Daniel Karzel <daniel.karzel@coblox.tech>
deec242
to
933758c
Compare
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 know. @thomaseizinger, do you have an idea here?
The issue is that the loan signing process is a 3 step process. If the user interupts it by clicking outside of the window, it starts from scratch.
Shall keep some state here?
What about the following:
Instead of having backup be a dedicated action, we could add a checkbox like:
- Create backup of loan
When ticked, the download will automatically be initiated as part of clicking sign
.
let borrower = storage | ||
.get_item::<String>("borrower_state") | ||
.map_err(Error::Load)? | ||
.ok_or(Error::EmptyState)?; | ||
let (borrower, loan_details) = | ||
serde_json::from_str::<(Borrower1, LoanDetails)>(&borrower).map_err(Error::Deserialize)?; | ||
Ok((borrower, loan_details)) |
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 Storage::get_item
was designed, the idea was that you get a type-safe interface that builds on top of the Display
/FromStr
traits to avoid boilerplate.
The repeated use of it with String
and serde_json
suggests that you actually want an API on Storage
that is built on top of a Serialize
/Deserialize
contract instead.
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.
The benefit of doing so would be that you don't need to extract functions like this because a call to storage::get_item
does exactly what you need, resulting in less indirection and easier to read code.
// load temporary loan_borrower state. When the frontend _asks_ the extension to | ||
// sign a loan, the information gets stored in the background script first. | ||
// When a request to bobtimas was made to actually take the loan, | ||
// this temporary loan details are saved in localStorage. | ||
// There can only be one pending loans at the time hence there is no identifier. |
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.
Given that this is documenting the function, having this as rustdoc on the function would be better.
#[derive(Debug, thiserror::Error)] | ||
pub enum Error { | ||
#[error("Storage error: {0}")] | ||
Storage(anyhow::Error), | ||
#[error("Failed to save item to storage: {0}")] | ||
Save(anyhow::Error), | ||
#[error("Failed to load item from storage: {0}")] | ||
Load(anyhow::Error), | ||
#[error("Loaded empty borrower state")] | ||
EmptyBorrowerState, | ||
#[error("Loan details were not found")] | ||
LoanNotFound, | ||
#[error("Serialization failed: {0}")] | ||
Serialize(serde_json::Error), | ||
#[error("Deserialization failed: {0}")] | ||
Deserialize(serde_json::Error), | ||
#[error("Loaded empty borrower state")] | ||
EmptyState, | ||
#[error("Failed to sign transaction: {0}")] | ||
Sign(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.
As outlined in #232, the current state of error isn't particularly ideal. I'd suggest to give it some thought and decide on a strategy so you get to a place where the way things are done in this component is generally applicable and can hence effortlessly "copy" the idea and apply it to new code.
/// Create a json object which can be used to backup the loan details to the transaction | ||
/// | ||
/// TODO: Pass in transaction id instead of transaction | ||
/// For now, we pass in the whole transaction just to access it's transaction id. | ||
/// This is because `sign_loan` returns the whole transaction and we have no means to deserialize | ||
/// the transaction from within TS. | ||
#[wasm_bindgen] | ||
pub async fn create_loan_backup( | ||
wallet_name: String, | ||
transaction: JsValue, | ||
) -> Result<JsValue, JsValue> { | ||
log::debug!("Received tx: {:?}", transaction); | ||
let transaction: Transaction = map_err_from_anyhow!(transaction.into_serde())?; | ||
let backup_details = map_err_from_anyhow!( | ||
wallet::create_loan_backup(wallet_name, &LOADED_WALLET, transaction.inner.txid()).await | ||
)?; | ||
let backup_details = map_err_from_anyhow!(JsValue::from_serde(&backup_details))?; | ||
Ok(backup_details) | ||
} | ||
|
||
/// Load a loan backup and store it in localStorage. | ||
#[wasm_bindgen] | ||
pub async fn load_loan_backup(backup_details: JsValue) -> Result<(), JsValue> { | ||
let backup_details: BackupDetails = map_err_from_anyhow!(backup_details.into_serde())?; | ||
map_err_from_anyhow!(wallet::load_loan_backup(backup_details))?; | ||
|
||
Ok(()) | ||
} |
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.
The extension frontend TS code should be able to access the same localStorage right?
I think we should consider just creating the backup from within TS. We don't need the wallet for anything to create the backup, so the indirection through the Rust code just feels like unnecessary boilerplate. (On the same note, calling into Rust for creating the seed-words is similarly unnecessary.)
In the long-run, we need to consider that the extension is given data that was created with a previous version and hence be more explicit about what we store rather than just dumping everything.
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 wonder if it is a good idea to call things directly from TS code instead.
Are you not afraid that the TS code and rust code diverge too much?
For example, the loan data is currently stored in localstorage under the key loan_state
. If this ever to changes in the rust code we might run in the danger that it's not changed in the TS code.
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 looked into it: in this particular case it's unfortunately a bigger task :
The issue here is that we need to know the transaction ID to load this information from the localstorage. All we have available is the whole transaction. We'd need to change quite a few things for this. It's worth it and should be done but since it's working atm I recorded an issue for this. #238
#[wasm_bindgen] | ||
pub async fn create_loan_backup( | ||
wallet_name: String, | ||
transaction: JsValue, |
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 this passed around using the hex-encoded binary representation? If yes, why isn't it just a String
? If not, why not?
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.
Good question. It's a mix right now:
{
inner: "02000...."
}
As far as I remember we had challenges to serialize this to a simple string.
// We get a hold of the wallet to ensure that it is loaded. This is a security mechanism | ||
// to ensure no unauthorized access to the data. | ||
// Ideally all data is encrypted but that's just how it is :) | ||
let _ = current(&name, current_wallet).await.unwrap(); |
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 want the wasm code to blow up less, I would avoid unwrap
s or at least use expect
to document the invariant of why you think it is okay to panic.
.then(cleanupPendingLoan); | ||
|
||
// if we receive an error, we respond directly, else we return the details | ||
return await signLoan(walletName).catch(rejectLoanSignRequest); |
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.
return await
is unnecessary, you can just remove the await
and return the promise directly.
onSigned={onSigned} | ||
/> | ||
<form | ||
onSubmit={async 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.
Doesn't need to be async. I think there is a typescript lint for such cases!
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.
There are quite a lint errors. I created a ticket for that: #236
// Note: it would be nicer to open a dialog to download the file. However, we would lose focus | ||
// of the window. We don't want that, hence we just open a new tab with the content | ||
window.open(url, "_blank"); |
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.
Nice!
This commit is candidate for squashing into the earlier one!
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 can recommend looking into https://github.com/tummychow/git-absorb for a semi-automated solution to this.
Whoops, looks like my review is coming a bit too late, sorry about that, I only got a notification this morning! In any case, I didn't see anything major that would have need to be addressed. On a stylistic note: The guide we are striving to follow for commit messages avoids dots at the end to a) save space and b) be consistent with git's internal commit messages like merges. |
Sorry for merging so fast. was just in a call with Daniel and we decided to merge. I'll have a look at your feedback and get this sorted in follow-up PR(s). |
No sweat, take it as you wish :) |
Add loan back up feature to extension.