Haskell Bitcoin Blockchain Parser
Toy language to brainfuck compiler
Blockchain indexer
Smartphone app for Digital Bitbox QR-code verification (2FA)
A program which ensures source code files have copyright license headers by scanning directory patterns recursively
Slides from the 21 Lectures Advanced Course
Pull request review commentdigitalbitbox/bitbox-wallet-app
import * as style from './moonpay.css'; interface BuyProps { accounts: AccountInterface[];- code?: string;+ code: string;
why?
comment created time in 15 minutes
Pull request review commentdigitalbitbox/bitbox-wallet-app
class BuyInfo extends Component<Props, State> { // @ts-ignore .then(accounts => accounts.map(({ name, code }) => ({ text: name, value: code }))) .then(options => {- if (this.state.status === 'choose' && options.length === 1) {- route(`/buy/moonpay/${options[0].value}`);- } else {- this.setState({ options });- }+ this.setState({ options });+ this.maybeProceed();
It is unclear when preact updates the state after this.setState({ options })
, use the 2nd argument of setState that is a callback when the state has updated: this.setState({ options }, this.maybeProceed);
or
this.setState({ options }, () => this.maybeProceed());
comment created time in 16 minutes
startedbenma/visual-regexp-steroids.el
started time in an hour
PR opened digitalbitbox/bitbox-wallet-app
Removing it for now, can be enabled later.
pr created time in 2 hours
startedtrekhleb/javascript-algorithms
started time in 6 hours
PR merged digitalbitbox/bitbox-wallet-app
The new buy was miss-aligned, the buttons now have their own line and are all equally spaced out and use the same width.
cc @jadzeidan
pr closed time in 19 hours
push eventdigitalbitbox/bitbox-wallet-app
commit sha 21846b1b9972dd1bf89794dc87b92b8547a9c96d
frontend: better actions buttons layout on mobile The new buy was miss-aligned, the buttons now have their own line and are all equally spaced out and use the same width.
commit sha 2a2240b9aadf3f47d8acf9a4b501a6b07e13174d
Merge branch 'frontend-buybotton-mobile'
push time in 19 hours
PR opened digitalbitbox/bitbox-wallet-app
The new buy was miss-aligned, the buttons now have their own line and are all equally spaced out and use the same width.
cc @jadzeidan
pr created time in 20 hours
Pull request review commentdigitalbitbox/bitbox02-firmware
commander: move RootFingerprintRequest to Rust
+// Copyright 2020 Shift Crypto AG
2021?
comment created time in 20 hours
Pull request review commentdigitalbitbox/bitbox02-firmware
api: port RestoreFromMnemonic to Rust
+// Copyright 2020 Shift Crypto AG+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// http://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++use super::Error;+use crate::pb;++use pb::response::Response;++use crate::workflow::{confirm, mnemonic, password, status, unlock};++pub async fn from_mnemonic(
would be very very nice to have at least a couple tests
comment created time in 20 hours
Pull request review commentdigitalbitbox/bitbox02-firmware
api: port RestoreFromMnemonic to Rust
+// Copyright 2020 Shift Crypto AG+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// http://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++use super::Error;+use crate::pb;++use pb::response::Response;++use crate::workflow::{confirm, mnemonic, password, status, unlock};++pub async fn from_mnemonic(+ #[cfg_attr(not(feature = "app-u2f"), allow(unused_variables))]+ &pb::RestoreFromMnemonicRequest {+ timestamp,+ timezone_offset,+ }: &pb::RestoreFromMnemonicRequest,+) -> Result<Response, Error> {+ let mnemonic = mnemonic::get().await?;+ let seed = match bitbox02::keystore::bip39_mnemonic_to_seed(&mnemonic) {+ Ok(seed) => seed,+ Err(()) => {+ status::status("Recovery words\ninvalid", false).await;+ return Err(Error::Generic);+ }+ };+ status::status("Recovery words\nvalid", true).await;++ // If entering password fails (repeat password does not match the first), we don't want to abort+ // the process immediately. We break out only if the user confirms.+ let password = loop {+ match password::enter_twice().await {+ Err(()) => {+ let params = confirm::Params {+ title: "",+ body: "Passwords\ndo not match.\nTry again?",+ ..Default::default()+ };+ if !confirm::confirm(¶ms).await {+ return Err(Error::Generic);
how about UserAbort
?
comment created time in 21 hours
Pull request review commentdigitalbitbox/bitbox02-firmware
api: port RestoreFromMnemonic to Rust
+// Copyright 2020 Shift Crypto AG+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// http://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++use super::Error;+use crate::pb;++use pb::response::Response;++use crate::workflow::{confirm, mnemonic, password, status, unlock};++pub async fn from_mnemonic(+ #[cfg_attr(not(feature = "app-u2f"), allow(unused_variables))]+ &pb::RestoreFromMnemonicRequest {+ timestamp,+ timezone_offset,+ }: &pb::RestoreFromMnemonicRequest,+) -> Result<Response, Error> {+ let mnemonic = mnemonic::get().await?;+ let seed = match bitbox02::keystore::bip39_mnemonic_to_seed(&mnemonic) {+ Ok(seed) => seed,+ Err(()) => {+ status::status("Recovery words\ninvalid", false).await;+ return Err(Error::Generic);+ }+ };+ status::status("Recovery words\nvalid", true).await;++ // If entering password fails (repeat password does not match the first), we don't want to abort+ // the process immediately. We break out only if the user confirms.+ let password = loop {+ match password::enter_twice().await {+ Err(()) => {+ let params = confirm::Params {+ title: "",+ body: "Passwords\ndo not match.\nTry again?",+ ..Default::default()+ };+ if !confirm::confirm(¶ms).await {+ return Err(Error::Generic);+ }+ }+ Ok(password) => break password,+ }+ };++ if bitbox02::keystore::encrypt_and_store_seed(&seed, password.as_str()).is_err() {+ status::status("Could not\nrestore backup", false).await;+ return Err(Error::Generic);+ };++ #[cfg(feature = "app-u2f")]+ {+ let datetime_string = bitbox02::format_datetime(timestamp, timezone_offset, false);+ let params = confirm::Params {+ title: "Is now?",+ body: &datetime_string,+ ..Default::default()+ };+ if !confirm::confirm(¶ms).await {+ return Err(Error::Generic);+ }+ // Ignore error
That is clear from the next line, that the error is ignored... but why? :)
comment created time in 21 hours
Pull request review commentdigitalbitbox/bitbox02-firmware
api: port RestoreFromMnemonic to Rust
+// Copyright 2020 Shift Crypto AG+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// http://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++use super::Error;+use crate::pb;++use pb::response::Response;++use crate::workflow::{confirm, mnemonic, password, status, unlock};++pub async fn from_mnemonic(+ #[cfg_attr(not(feature = "app-u2f"), allow(unused_variables))]+ &pb::RestoreFromMnemonicRequest {+ timestamp,+ timezone_offset,+ }: &pb::RestoreFromMnemonicRequest,+) -> Result<Response, Error> {+ let mnemonic = mnemonic::get().await?;+ let seed = match bitbox02::keystore::bip39_mnemonic_to_seed(&mnemonic) {+ Ok(seed) => seed,+ Err(()) => {+ status::status("Recovery words\ninvalid", false).await;+ return Err(Error::Generic);+ }+ };+ status::status("Recovery words\nvalid", true).await;++ // If entering password fails (repeat password does not match the first), we don't want to abort+ // the process immediately. We break out only if the user confirms.+ let password = loop {+ match password::enter_twice().await {+ Err(()) => {+ let params = confirm::Params {+ title: "",+ body: "Passwords\ndo not match.\nTry again?",+ ..Default::default()+ };+ if !confirm::confirm(¶ms).await {+ return Err(Error::Generic);+ }+ }+ Ok(password) => break password,+ }+ };++ if bitbox02::keystore::encrypt_and_store_seed(&seed, password.as_str()).is_err() {+ status::status("Could not\nrestore backup", false).await;+ return Err(Error::Generic);+ };++ #[cfg(feature = "app-u2f")]+ {+ let datetime_string = bitbox02::format_datetime(timestamp, timezone_offset, false);+ let params = confirm::Params {+ title: "Is now?",+ body: &datetime_string,+ ..Default::default()+ };+ if !confirm::confirm(¶ms).await {+ return Err(Error::Generic);+ }+ // Ignore error+ let _ = bitbox02::securechip::u2f_counter_set(timestamp);+ }++ bitbox02::memory::set_initialized()?;
Would've been nice if this resulted in Error:Memory
, on error.
comment created time in 21 hours
Pull request review commentdigitalbitbox/bitbox02-firmware
api: port RestoreFromMnemonic to Rust
+// Copyright 2020 Shift Crypto AG
2021?
comment created time in 21 hours
startedmolleindustria/likelike-online
started time in a day
issue commentbenma/visual-regexp.el
occur with "minibuffer-inactive ... ..."
Ah, interesting: there seems to be some odd interaction between this package and feebleline. If I disable that package, everything works fine.
comment created time in a day
PR merged digitalbitbox/bitbox-wallet-app
At the moment, we have only /buy/moonpay/ but the idea is we will possibly add more in the future.
This commit also unifies Qt and Android frontend whitelists. They were disaligned after switching routes back and forth.
There's no reason why keep two separate whitelists in the SystemOpen function. They serve the same purpose and can be easily combined into one.
So far, there doesn't seem to be a reason to use a more onerous regexp compared to just a string prefix. This commit in fact does the latter and all existing patters line up nicely.
If there's really a need in the future, can always switch to a regexp based approach.
pr closed time in a day
push eventdigitalbitbox/bitbox-wallet-app
commit sha bbc589f6e87cdefc85dcac0fd70f85bfbd0ed129
frontend: whitelist all /buy/ subpages for opening external URLs At the moment, we have only /buy/moonpay/ but the idea is we will possibly add more in the future. This commit also unifies Qt and Android frontend whitelists. They were disaligned after switching routes back and forth.
commit sha 976197d7876173e54db75196beecd87e0167335f
backend: unify exact match and regexp into prefixed URL matching There's no reason why keep two separate whitelists in the SystemOpen function. They serve the same purpose and can be easily combined into one. So far, there doesn't seem to be a reason to use a more onerous regexp compared to just a string prefix. This commit in fact does the latter and all existing patters line up nicely. If there's really a need in the future, can always switch to a regexp based approach.
push time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend,backend: correct and unify/combine whitelists for opening external URLs
func init() { electrum.SetClientSoftwareVersion(Version) } +// fixedURLWhitelist is always allowed by SystemOpen, in addition to some+// adhoc URLs. See SystemOpen for details.+var fixedURLWhitelist = []string{+ // Shift Crypto owned domains.+ "https://shiftcrypto.ch/",+ "https://ext.shiftcrypto.ch/",+ "https://shop.shiftcrypto.ch/",+ "https://guides.shiftcrypto.ch/",+ // Exchange rates.+ "https://www.coingecko.com/",+ "https://www.cryptocompare.com/",+ // Block explorers.+ "https://blockstream.info/tx/",+ "https://blockstream.info/testnet/tx/",+ "http://explorer.litecointools.com/tx/",+ "https://insight.litecore.io/tx/",+ "https://etherscan.io/tx/",+ "https://rinkeby.etherscan.io/tx/",+ "https://ropsten.etherscan.io/tx/",+ // Moonpay onramp+ "https://www.moonpay.com/",
anywa, can always change to regexp in the future! if it turns out there's actually a good reason for it.
comment created time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend: fix continue button not clickable in wizard
class BitBox02 extends Component<Props, State> { <div className={style.stepContext}> <p>{t('bitbox02Wizard.stepBackup.createBackup')}</p> <p className="m-bottom-default">{t('bitbox02Wizard.stepBackup.beforeProceed')}</p>- <form ref={this.setDisclaimerRef}>+ <form> <div className="m-top-quarter"> <Checkbox onChange={this.handleDisclaimerCheck} className={style.wizardCheckbox} id="agreement1" label={t('bitbox02Wizard.backup.userConfirmation1')} />
fixed and force pushed https://github.com/digitalbitbox/bitbox-wallet-app/compare/fd680efa61c2ea9244ce3a6c3646f9b6ecf47704..f20078846e3a01774b7715db466574c38d13395c
comment created time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend,backend: correct and unify/combine whitelists for opening external URLs
func init() { electrum.SetClientSoftwareVersion(Version) } +// fixedURLWhitelist is always allowed by SystemOpen, in addition to some+// adhoc URLs. See SystemOpen for details.+var fixedURLWhitelist = []string{+ // Shift Crypto owned domains.+ "https://shiftcrypto.ch/",+ "https://ext.shiftcrypto.ch/",+ "https://shop.shiftcrypto.ch/",+ "https://guides.shiftcrypto.ch/",+ // Exchange rates.+ "https://www.coingecko.com/",+ "https://www.cryptocompare.com/",+ // Block explorers.+ "https://blockstream.info/tx/",+ "https://blockstream.info/testnet/tx/",+ "http://explorer.litecointools.com/tx/",+ "https://insight.litecore.io/tx/",+ "https://etherscan.io/tx/",+ "https://rinkeby.etherscan.io/tx/",+ "https://ropsten.etherscan.io/tx/",+ // Moonpay onramp+ "https://www.moonpay.com/",
right, only the exact match never worked for some at all. for example, we don't actually link to the moonpay.com root, ony some /privacy URL.
the whitelist purpose is to minimize some vulnerable URL injection, right. suppose an example.org/foo
is whitelisted. if example.org
is compromized, most likely so is its /foo
page: not much benefit in exact match.
now, you could say but what if a web page has an XSS and a specially crafted example.org/foo/%20<script>alert()
would pass. but then what about existing whitelisted URLs like blockstream.info/tx/
. even with a regexp, they don't verify anything past /tx/
. so, even with the original regexp, the same issue remains.
my point is, each URL to check should either be decomposed and compared against a domain, path and query params, or the benefits in the regexp vs prefix matching are questionable here.
yes we could change everything to match up to a single character, like in blockstream\.info/tx/[a-fA-F0-9]{64}
and all the redirects in ext.shiftcrypto.ch
... the list will get very long and I'm not sure how big of an advantage it would be considering a much more complicated list to maintain.
comment created time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend: fix continue button not clickable in wizard
class BitBox02 extends Component<Props, State> { }); } - private setDisclaimerRef = (element: HTMLElement) => {- this.disclaimerForm = element;- }-- private handleDisclaimerCheck = () => {- const checkboxes = this.disclaimerForm.querySelectorAll('input');- let result = true;- for (const checkbox of checkboxes) {- if (!checkbox.checked) {- result = false;- }- }- this.setState({ readDisclaimers: result });+ private handleDisclaimerCheck = (event: InputEvent) => {+ const target = event.target as HTMLInputElement;+ const name = target.id as 'argement1' | 'argement2' | 'argement3' | 'argement4' | 'argement5';+ this.setState(prevState => ({+ ...prevState,
It seems Typescript doesn't like ES6 style dynamic property keys, but doesn't complain about oldschool
const obj = {};
obj[target.id] = target.checked;
this.setState(obj);
comment created time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend: fix continue button not clickable in wizard
class BitBox02 extends Component<Props, State> { }); } - private setDisclaimerRef = (element: HTMLElement) => {- this.disclaimerForm = element;- }-- private handleDisclaimerCheck = () => {- const checkboxes = this.disclaimerForm.querySelectorAll('input');- let result = true;- for (const checkbox of checkboxes) {- if (!checkbox.checked) {- result = false;- }- }- this.setState({ readDisclaimers: result });+ private handleDisclaimerCheck = (event: InputEvent) => {+ const target = event.target as HTMLInputElement;+ const name = target.id as 'argement1' | 'argement2' | 'argement3' | 'argement4' | 'argement5';+ this.setState(prevState => ({+ ...prevState,
same issue https://stackoverflow.com/questions/42090191/picks-k-type-with-dynamic-computed-keys
comment created time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend: fix continue button not clickable in wizard
class BitBox02 extends Component<Props, State> { <div className={style.stepContext}> <p>{t('bitbox02Wizard.stepBackup.createBackup')}</p> <p className="m-bottom-default">{t('bitbox02Wizard.stepBackup.beforeProceed')}</p>- <form ref={this.setDisclaimerRef}>+ <form> <div className="m-top-quarter"> <Checkbox onChange={this.handleDisclaimerCheck} className={style.wizardCheckbox} id="agreement1" label={t('bitbox02Wizard.backup.userConfirmation1')} />
Thanks, yes you are right
comment created time in a day
Pull request review commentdigitalbitbox/bitbox-wallet-app
frontend: fix continue button not clickable in wizard
class BitBox02 extends Component<Props, State> { }); } - private setDisclaimerRef = (element: HTMLElement) => {- this.disclaimerForm = element;- }-- private handleDisclaimerCheck = () => {- const checkboxes = this.disclaimerForm.querySelectorAll('input');- let result = true;- for (const checkbox of checkboxes) {- if (!checkbox.checked) {- result = false;- }- }- this.setState({ readDisclaimers: result });+ private handleDisclaimerCheck = (event: InputEvent) => {+ const target = event.target as HTMLInputElement;+ const name = target.id as 'argement1' | 'argement2' | 'argement3' | 'argement4' | 'argement5';+ this.setState(prevState => ({+ ...prevState,
The normal reason why you would do that is if you want to change the state based on the current state, i.e. toggle something on/off, this would be important if you click faster than react updating the state. In that case we can pass an async function instead of an object.
see https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
// bad
this.setState({
counter: this.state.counter + this.props.increment,
});
// good
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
but in this particular case, it is because TypeScript didn't like it... I didn't really deeply investigate deeply why 🙈
comment created time in a day
pull request commentdigitalbitbox/bitbox-wallet-app
frontend,backend: correct and unify/combine whitelists for opening external URLs
Sorry, I've realized this wasn't enough so I added another commit. PTAL.
comment created time in a day
PR closed digitalbitbox/bitbox-wallet-app
pr closed time in a day
pull request commentdigitalbitbox/bitbox-wallet-app
Sorry for keeping this open so long, will revisit later
comment created time in a day
PR opened digitalbitbox/bitbox-wallet-app
At the moment, we have only /buy/moonpay/ but the idea is we will possibly add more in the future.
This commit also unifies Qt and Android frontend whitelists. There were disaligned after switching routes back and forth.
pr created time in a day
PR opened digitalbitbox/bitbox-wallet-app
Unfortunatelly I could only reproduce it once and got the error js: Uncaught TypeError: Cannot read property 'querySelectorAll' of null on line https://github.com/digitalbitbox/bitbox-wallet-app/blob/6ed4ff598ce0a352e13bb5bc3a42edcebc83d47f/frontends/web/src/components/devices/bitbox02/bitbox02.tsx#L357
This fix removes the dom reference and instead adds each checkbox as a boolean to the state. That way we don't depend on the form to be there or having to querySelect the input elements and check if they are checked.
pr created time in a day