profile
viewpoint

benma/blockchain-parser-hs 9

Haskell Bitcoin Blockchain Parser

benma/bfc 3

Toy language to brainfuck compiler

benma/bitd 1

Blockchain indexer

benma/2FA-app 0

Smartphone app for Digital Bitbox QR-code verification (2FA)

benma/addlicense 0

A program which ensures source code files have copyright license headers by scanning directory patterns recursively

benma/Advanced-Course 0

Slides from the 21 Lectures Advanced Course

Pull request review commentdigitalbitbox/bitbox-wallet-app

Small moonpay fixes

 import * as style from './moonpay.css';  interface BuyProps {     accounts: AccountInterface[];-    code?: string;+    code: string;

why?

benma

comment created time in 15 minutes

Pull request review commentdigitalbitbox/bitbox-wallet-app

Small moonpay fixes

 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());
benma

comment created time in 16 minutes

startedbenma/visual-regexp-steroids.el

started time in an hour

PR opened digitalbitbox/bitbox-wallet-app

Reviewers
frontend: remove apple and samsung pay from moonpay

Removing it for now, can be enabled later.

+0 -7

0 comment

2 changed files

pr created time in 2 hours

startedtrekhleb/javascript-algorithms

started time in 6 hours

PR merged digitalbitbox/bitbox-wallet-app

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.

Screen Shot 2021-01-25 at 10 52 36 PM

cc @jadzeidan

+19 -3

1 comment

3 changed files

thisconnect

pr closed time in 19 hours

push eventdigitalbitbox/bitbox-wallet-app

thisconnect

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.

view details

thisconnect

commit sha 2a2240b9aadf3f47d8acf9a4b501a6b07e13174d

Merge branch 'frontend-buybotton-mobile'

view details

push time in 19 hours

PR opened digitalbitbox/bitbox-wallet-app

Reviewers
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.

Screen Shot 2021-01-25 at 10 52 36 PM

cc @jadzeidan

+19 -3

0 comment

3 changed files

pr created time in 20 hours

Pull request review commentdigitalbitbox/bitbox02-firmware

commander: move RootFingerprintRequest to Rust

+// Copyright 2020 Shift Crypto AG

2021?

benma

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

benma

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(&params).await {+                    return Err(Error::Generic);

how about UserAbort?

benma

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(&params).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(&params).await {+            return Err(Error::Generic);+        }+        // Ignore error

That is clear from the next line, that the error is ignored... but why? :)

benma

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(&params).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(&params).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.

benma

comment created time in 21 hours

Pull request review commentdigitalbitbox/bitbox02-firmware

api: port RestoreFromMnemonic to Rust

+// Copyright 2020 Shift Crypto AG

2021?

benma

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.

fu123456

comment created time in a day

PR merged digitalbitbox/bitbox-wallet-app

frontend,backend: correct and unify/combine whitelists 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.

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.

+46 -51

1 comment

3 changed files

x1ddos

pr closed time in a day

push eventdigitalbitbox/bitbox-wallet-app

alex

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.

view details

alex

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.

view details

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.

x1ddos

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

thisconnect

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.

x1ddos

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);
thisconnect

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

thisconnect

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

thisconnect

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 🙈

thisconnect

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.

x1ddos

comment created time in a day

PR closed digitalbitbox/bitbox-wallet-app

Frontend RTL
+105 -2

1 comment

11 changed files

thisconnect

pr closed time in a day

pull request commentdigitalbitbox/bitbox-wallet-app

Frontend RTL

Sorry for keeping this open so long, will revisit later

thisconnect

comment created time in a day

PR opened digitalbitbox/bitbox-wallet-app

Reviewers
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. There were disaligned after switching routes back and forth.

+2 -2

0 comment

2 changed files

pr created time in a day

PR opened digitalbitbox/bitbox-wallet-app

frontend: fix continue button not clickable in wizard

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.

+24 -19

0 comment

1 changed file

pr created time in a day

more