Skip to content

Commit

Permalink
btc: Expose sign_message() to TS API and create UI
Browse files Browse the repository at this point in the history
After implementing BTC message signing for Rust API, further extensions
are added to make wasm expose this functionality to TypeScript API.
Finally, an extra field added under the Bitcoin section of sandbox UI
app for BTC message signing.

Signed-off-by: asi345 <[email protected]>
  • Loading branch information
asi345 committed Oct 26, 2023
1 parent e4ddcbf commit 62b4661
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
87 changes: 87 additions & 0 deletions sandbox/src/Bitcoin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,90 @@ function BtcSignPSBT({ bb02 }: Props) {
);
}

function BtcSignMessage({ bb02 }: Props) {
const [coin, setCoin] = useState<bitbox.BtcCoin>('btc');
const [simpleType, setSimpleType] = useState<bitbox.BtcSimpleType>('p2wpkhP2sh');
const [keypath, setKeypath] = useState("m/49'/0'/0'/0/0");
const [message, setMessage] = useState('message');
const [running, setRunning] = useState(false);
const [result, setResult] = useState<bitbox.BtcSignMessageSignature | undefined>();
const [err, setErr] = useState<bitbox.Error>();

const simpleTypeOptions: bitbox.BtcSimpleType[] = ['p2wpkhP2sh', 'p2wpkh', 'p2tr'];

const script_config: bitbox.BtcScriptConfig = { simpleType };
const script_config_w_keypath: bitbox.BtcScriptConfigWithKeypath = { scriptConfig: script_config, keypath: "m/49'/0'/0'/0/0" };

const stringToUint8Array = (str: string) => {
const arr = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
arr[i] = str.charCodeAt(i);
}
return arr;
}

const parsedResult = result ? JSON.stringify(result, undefined, 2) : '';

const submitForm = async (e: FormEvent) => {
e.preventDefault();
setRunning(true);
setResult(undefined);
setErr(undefined);
try {
const signature = await bb02.btcSignMessage(coin, script_config_w_keypath, stringToUint8Array(message));
setResult(signature);
} catch (err) {
setErr(bitbox.ensureError(err));
} finally {
setRunning(false);
}
}

return (
<div>
<h4>Sign Message</h4>
<form className="verticalForm" onSubmit={submitForm}>
<label>
Coin
<select value={coin} onChange={(e: ChangeEvent<HTMLSelectElement>) => setCoin(e.target.value as bitbox.BtcCoin)}>
{btcCoinOptions.map(option => <option key={option} value={option}>{option}</option>)}
</select>
</label>
<label>
Simple Type
<select value={simpleType} onChange={(e: ChangeEvent<HTMLSelectElement>) => setSimpleType(e.target.value as bitbox.BtcSimpleType)}>
{simpleTypeOptions.map(option => <option key={option} value={option} disabled={option === 'p2tr' && (coin === 'ltc' || coin === 'tltc')}>{option}</option>)}
</select>
</label>
<label>
Keypath
</label>
<textarea value={keypath} onChange={e => setKeypath(e.target.value)} rows={1} cols={80} />
<label>
Message
</label>
<textarea value={message} onChange={e => setMessage(e.target.value)} rows={4} cols={80} />
<button type='submit' disabled={running}>Sign message</button>
{result ? (
<div className="resultContainer">
<label>Result:
{
<textarea
rows={32}
readOnly
defaultValue={parsedResult}
/>
}
</label>
</div>
) : null }
<ShowError err={err} />
</form>
</div>
);

}

function BtcMiniscriptAddress({ bb02 }: Props) {
const [running, setRunning] = useState(false);
const [result, setResult] = useState('');
Expand Down Expand Up @@ -307,6 +391,9 @@ export function Bitcoin({ bb02 } : Props) {
<div className="action">
<BtcSignPSBT bb02={bb02} />
</div>
<div className="action">
<BtcSignMessage bb02={bb02} />
</div>
<div className="action">
<BtcMiniscriptAddress bb02={bb02} />
</div>
Expand Down
30 changes: 30 additions & 0 deletions src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,36 @@ impl PairedBitBox {
Ok(psbt.to_string())
}

#[wasm_bindgen(js_name = btcSignMessage)]
pub async fn btc_sign_message(
&self,
coin: types::TsBtcCoin,
script_config: Option<types::TsBtcScriptConfigWithKeypath>,
msg: &[u8],
) -> Result<types::TsBtcSignMessageSignature, JavascriptError> {
let signature = self
.0
.btc_sign_message(
coin.try_into()?,
match script_config {
Some(sc) => Some(sc.try_into()?),
None => None,
},
msg,
)
.await?;

Ok(
serde_wasm_bindgen::to_value(&types::BtcSignMessageSignature {
sig: signature.sig,
recid: signature.recid,
electrum_sig65: signature.electrum_sig65,
})
.unwrap()
.into(),
)
}

/// Does this device support ETH functionality? Currently this means BitBox02 Multi.
#[wasm_bindgen(js_name = ethSupported)]
pub fn eth_supported(&self) -> bool {
Expand Down
14 changes: 14 additions & 0 deletions src/wasm/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ type BtcScriptConfigWithKeypath = {
scriptConfig: BtcScriptConfig;
keypath: Keypath;
};
type BtcSignMessageSignature = {
sig: Uint8Array,
recid: bigint,
electrum_sig65: Uint8Array,
}
// nonce, gasPrice, gasLimit and value must be big-endian encoded, no trailing zeroes.
type EthTransaction = {
nonce: Uint8Array;
Expand Down Expand Up @@ -155,6 +160,8 @@ extern "C" {
pub type TsBtcScriptConfig;
#[wasm_bindgen(typescript_type = "BtcScriptConfigWithKeypath")]
pub type TsBtcScriptConfigWithKeypath;
#[wasm_bindgen(typescript_type = "BtcSignMessageSignature")]
pub type TsBtcSignMessageSignature;
#[wasm_bindgen(typescript_type = "EthTransaction")]
pub type TsEthTransaction;
#[wasm_bindgen(typescript_type = "EthSignature")]
Expand Down Expand Up @@ -279,3 +286,10 @@ pub struct EthSignature {
pub s: Vec<u8>,
pub v: Vec<u8>,
}

#[derive(serde::Serialize)]
pub struct BtcSignMessageSignature {
pub sig: Vec<u8>,
pub recid: u8,
pub electrum_sig65: Vec<u8>,
}

0 comments on commit 62b4661

Please sign in to comment.