Skip to content
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

make KK parse its own TXes to avoid type hell #363

Merged
merged 10 commits into from
Nov 12, 2021
4 changes: 0 additions & 4 deletions integration/src/bitcoin/bitcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa
vin: [
{
vout: 1,
valueSat: 200000,
sequence: 4294967295,
scriptSig: {
hex: "483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503",
Expand All @@ -231,7 +230,6 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa
},
{
vout: 1,
valueSat: 200000,
sequence: 4294967295,
scriptSig: {
hex: "48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214",
Expand Down Expand Up @@ -300,7 +298,6 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa
vin: [
{
vout: 1,
valueSat: 200000,
sequence: 4294967295,
scriptSig: {
hex: "483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503",
Expand All @@ -309,7 +306,6 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa
},
{
vout: 1,
valueSat: 200000,
sequence: 4294967295,
scriptSig: {
hex: "48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214",
Expand Down
2 changes: 1 addition & 1 deletion integration/src/wallets/trezor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ export function name(): string {
export async function createWallet(): Promise<core.HDWallet> {
let keyring = new core.Keyring();
let transport = new MockTransport(keyring);
return trezor.create(transport as trezor.TrezorTransport, true);
return trezor.create(transport as trezor.TrezorTransport);
}

export function createInfo(): core.HDWalletInfo {
Expand Down
3 changes: 2 additions & 1 deletion packages/hdwallet-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"dependencies": {
"eventemitter2": "^5.0.1",
"lodash": "^4.17.21",
"rxjs": "^6.4.0"
"rxjs": "^6.4.0",
"type-assertions": "^1.1.0"
},
"devDependencies": {
"@types/google-protobuf": "^3.15.1",
Expand Down
30 changes: 26 additions & 4 deletions packages/hdwallet-core/src/bitcoin.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as ta from "type-assertions";

import { addressNListToBIP32, slip44ByCoin } from "./utils";
import { BIP32Path, Coin, ExchangeType, HDWallet, HDWalletInfo, PathDescription } from "./wallet";

Expand Down Expand Up @@ -28,7 +30,6 @@ export interface BitcoinScriptSig {
* Deserialized representation of an already-signed input of a transaction.
*/
interface BitcoinInputBase {
valueSat: number;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no clue where this came from, but we don't use it anywhere.

sequence: number;
}
export type BitcoinInput = GuardedUnion<
Expand Down Expand Up @@ -106,12 +107,19 @@ type BTCSignTxInputKKBase = BTCSignTxInputBase & {

type BTCSignTxInputKKSegwit = BTCSignTxInputKKBase & {
scriptType: BTCInputScriptType.SpendWitness | BTCInputScriptType.SpendP2SHWitness | BTCInputScriptType.External;
hex?: string;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just to make GuardedUnion happy with BTCSignTxInputSafe, but I don't really care at this point

};

type BTCSignTxInputKKNonSegwit = BTCSignTxInputKKBase & {
scriptType: Exclude<BTCInputScriptType, BTCSignTxInputKKSegwit["scriptType"]>;
tx: BitcoinTx;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole BitcoinTx type is non-standard and used only here. We should deprecate it in a later breaking change, and nuke it entirely.

};
} & (
| {
tx: BitcoinTx;
}
| {
hex: string;
}
);

type BTCSignTxInputKKUnguarded = BTCSignTxInputKKNonSegwit | BTCSignTxInputKKSegwit;
export type BTCSignTxInputKK = GuardedUnion<BTCSignTxInputKKUnguarded>;
Expand All @@ -129,7 +137,21 @@ export type BTCSignTxInputLedger = BTCSignTxInputBase & {
};

export type BTCSignTxInput = BTCSignTxInputNative & BTCSignTxInputKK & BTCSignTxInputTrezor & BTCSignTxInputLedger;
export type BTCSignTxInputUnguarded = BTCSignTxInputNativeUnguarded & BTCSignTxInputKKUnguarded & BTCSignTxInputTrezor & BTCSignTxInputLedger;
export type BTCSignTxInputUnguarded = BTCSignTxInputNativeUnguarded &
BTCSignTxInputKKUnguarded &
BTCSignTxInputTrezor &
BTCSignTxInputLedger;

// Stick to this common subset of input fields to avoid type hell.
export type BTCSignTxInputSafe = {
addressNList: BIP32Path;
scriptType: BTCInputScriptType;
hex: string;
txid: string;
amount: string;
vout: number;
};
ta.assert<ta.Extends<BTCSignTxInputSafe, BTCSignTxInput>>();

/**
* Output for a transaction we're about to sign.
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ export interface ETHAccountSuffix {
export interface ETHGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}

export interface ETHSignTx {
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/fio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { BIP32Path, HDWallet, HDWalletInfo, PathDescription } from "./wallet";
export interface FioGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}

export interface FioGetAccountPaths {
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/kava.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ export interface KavaGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
testnet?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}

export namespace Kava {
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/ripple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { BIP32Path, HDWallet, HDWalletInfo } from "./wallet";
export interface RippleGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}
declare namespace Ripple {
namespace sdk {
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/secret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ export interface SecretGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
testnet?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}

export namespace Secret {
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/terra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ export interface TerraGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
testnet?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}

export namespace Terra {
Expand Down
2 changes: 0 additions & 2 deletions packages/hdwallet-core/src/thorchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ export interface ThorchainGetAddress {
addressNList: BIP32Path;
showDisplay?: boolean;
testnet?: boolean;
/** Optional. Required for showDisplay == true. */
address?: string;
}

export namespace Thorchain {
Expand Down
2 changes: 1 addition & 1 deletion packages/hdwallet-core/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export abstract class Transport extends eventemitter2.EventEmitter2 {
/**
* Must emit outgoing message events and communicate with underlying interface
*/
public abstract call(...args: any[]): Promise<any>;
public abstract call(...args: any): Promise<unknown>;

/**
* Optional method to bootstrap connection to device
Expand Down
33 changes: 33 additions & 0 deletions packages/hdwallet-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,36 @@ export function compatibleBufferConcat(list: Uint8Array[]): Buffer {
if (!checkBufferConcat()) return Buffer.concat(list);
return Buffer.concat(list.map(x => Buffer.isBuffer(x) ? x : Buffer.from(x)));
}


/**
* Type guard for things that might have (string-keyed) properties. Useful to make
* TypeScript happy when you want to check if an object of unknown type has a particular
* property set.
* @example
* try {
* foo();
* } catch (e: unknown) {
* // Not allowed because there's no index signature for `unknown`:
* // if (e.bar === "baz") return "foobar";
* if (isIndexable(e) && e.bar === "baz") return "foobar";
* throw e;
* }
* @example
* isIndexable({}) === true
* @example
* isIndexable(() => {}) === true
* @example
* isIndexable(Object.create(null)) === true
* @example
* isIndexable(String("foo")) === true
* @example
* isIndexable(null) === false
* @example
* isIndexable(3.14) === false
* @example
* isIndexable("foo") === false
*/
export function isIndexable(x: unknown): x is Record<string | number | symbol, unknown> {
return x !== null && ["object", "function"].includes(typeof x)
}
27 changes: 5 additions & 22 deletions packages/hdwallet-core/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,17 @@ export type Coin = string;
export type Symbol = string;

/**
* Type guard for BTCWallet Support
* Type guards
*
* Example Usage:
```typescript
const wallet: HDWallet = ...
if (supportsBTC(wallet)) {
wallet.btcGetAddress(...)
}
```
*/

export function supportsBTC(wallet: HDWallet): wallet is BTCWallet {
return _.isObject(wallet) && (wallet as any)._supportsBTC;
}
Expand All @@ -123,16 +125,6 @@ export function infoBTC(info: HDWalletInfo): info is BTCWalletInfo {
return _.isObject(info) && (info as any)._supportsBTCInfo;
}

/**
* Type guard for ETHWallet Support
*
* Example Usage:
```typescript
if (supportsETH(wallet)) {
wallet.ethGetAddress(...)
}
```
*/
export function supportsETH(wallet: HDWallet): wallet is ETHWallet {
return _.isObject(wallet) && (wallet as any)._supportsETH;
}
Expand Down Expand Up @@ -205,16 +197,6 @@ export function infoKava(info: HDWalletInfo): info is KavaWalletInfo {
return _.isObject(info) && (info as any)._supportsKavaInfo;
}

/**
* Type guard for RippleWallet Support
*
* Example Usage:
```typescript
if (supportsripple(wallet)) {
wallet.xrpGetAddress(...)
}
```
*/
export function supportsRipple(wallet: HDWallet): wallet is RippleWallet {
return _.isObject(wallet) && (wallet as any)._supportsRipple;
}
Expand Down Expand Up @@ -298,7 +280,8 @@ export interface HDWallet extends HDWalletInfo {
/**
* Get device specific features
*/
getFeatures(): Promise<Record<string, any>>;
getFeatures(): Promise<Record<string, unknown>>;

/**
* Retrieve the wallet's firmware version
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/hdwallet-keepkey-nodewebusb/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ export class TransportDelegate implements keepkey.TransportDelegate {
try {
await this.usbDevice.claimInterface(0);
} catch (e) {
if (e.code === 18)
if (core.isIndexable(e) && e.code === 18)
// "The requested interface implements a protected class"
throw new core.FirmwareUpdateRequired("KeepKey", "6.1.0");
if (e.code === 19)
if (core.isIndexable(e) && e.code === 19)
// "Unable to claim interface"
throw new core.ConflictingApp("KeepKey");
throw e;
Expand Down
2 changes: 1 addition & 1 deletion packages/hdwallet-keepkey-webusb/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const AdapterDelegate = {
if (out.serialNumber === undefined) throw new Error("expected serial number");
return out as Device;
} catch (e) {
throw new core.WebUSBCouldNotPair("KeepKey", e.message);
throw new core.WebUSBCouldNotPair("KeepKey", String(core.isIndexable(e) ? e.message : e));
}
},
async getTransportDelegate(device: Device) {
Expand Down
4 changes: 2 additions & 2 deletions packages/hdwallet-keepkey-webusb/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ export class TransportDelegate implements keepkey.TransportDelegate {
await this.usbDevice.claimInterface(0);
} catch (e) {
console.error("Could not claim interface 0", this.usbDevice, { e });
if (e.code === 18)
if (core.isIndexable(e) && e.code === 18)
// "The requested interface implements a protected class"
throw new core.FirmwareUpdateRequired("KeepKey", "6.1.0");
if (e.code === 19)
if (core.isIndexable(e) && e.code === 19)
// "Unable to claim interface"
throw new core.ConflictingApp("KeepKey");
throw e;
Expand Down
1 change: 1 addition & 0 deletions packages/hdwallet-keepkey/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@ethereumjs/common": "^2.4.0",
"@ethereumjs/tx": "^3.3.0",
"@keepkey/device-protocol": "^7.2.4",
"@shapeshiftoss/bitcoinjs-lib": "5.2.0-shapeshift.2",
"@shapeshiftoss/hdwallet-core": "1.17.1",
"bignumber.js": "^9.0.1",
"bnb-javascript-sdk-nobroadcast": "^2.16.14",
Expand Down
20 changes: 11 additions & 9 deletions packages/hdwallet-keepkey/src/binance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ export async function binanceSignTx(
let resp = await transport.call(
Messages.MessageType.MESSAGETYPE_BINANCESIGNTX,
signTx,
core.LONG_TIMEOUT,
/*omitLock=*/ true
{
msgTimeout: core.LONG_TIMEOUT,
omitLock: true
}
);
if (resp.message_type === core.Events.FAILURE) throw resp;

const outputAmount = new BigNumber(message.outputs[0].coins[0].amount);
const inputAmount = new BigNumber(message.inputs[0].coins[0].amount);
Expand Down Expand Up @@ -84,11 +85,12 @@ export async function binanceSignTx(
resp = await transport.call(
Messages.MessageType.MESSAGETYPE_BINANCETRANSFERMSG,
send,
core.LONG_TIMEOUT,
/*omitLock=*/ true
{
msgTimeout: core.LONG_TIMEOUT,
omitLock: true
}
);

if (resp.message_type === core.Events.FAILURE) throw resp;
if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_BINANCESIGNEDTX) {
throw new Error(`binance: unexpected response ${resp.message_type}`);
}
Expand Down Expand Up @@ -121,9 +123,9 @@ export async function binanceGetAddress(transport: Transport, msg: core.BinanceG
const getAddr = new BinanceMessages.BinanceGetAddress();
getAddr.setAddressNList(msg.addressNList);
getAddr.setShowDisplay(msg.showDisplay !== false);
const response = await transport.call(Messages.MessageType.MESSAGETYPE_BINANCEGETADDRESS, getAddr, core.LONG_TIMEOUT);

if (response.message_type === core.Events.FAILURE) throw response;
const response = await transport.call(Messages.MessageType.MESSAGETYPE_BINANCEGETADDRESS, getAddr, {
msgTimeout: core.LONG_TIMEOUT,
});

const binanceAddress = response.proto as BinanceMessages.BinanceAddress;
return core.mustBeDefined(binanceAddress.getAddress());
Expand Down
Loading