Skip to content

Commit

Permalink
Merge pull request #9121 from LedgerHQ/fix/zcash-locktime-txversion
Browse files Browse the repository at this point in the history
Fix/zcash locktime txversion
  • Loading branch information
Wozacosta authored Feb 14, 2025
2 parents d423a3e + 035be8c commit 15a8f00
Show file tree
Hide file tree
Showing 13 changed files with 500 additions and 10 deletions.
7 changes: 7 additions & 0 deletions .changeset/clean-hats-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@ledgerhq/hw-app-btc": patch
"@ledgerhq/coin-bitcoin": patch
"@ledgerhq/live-common": patch
---

fix: zcash nu5/nu6
13 changes: 12 additions & 1 deletion libs/coin-modules/coin-bitcoin/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,22 @@ export type SignerTransaction = {
extraData?: Buffer;
};
export type CreateTransaction = {
inputs: Array<[SignerTransaction, number, string | null | undefined, number | null | undefined]>;
inputs:
| Array<[SignerTransaction, number, string | null | undefined, number | null | undefined]>
| Array<
[
SignerTransaction,
number,
string | null | undefined,
number | null | undefined,
number | null | undefined,
]
>;
associatedKeysets: string[];
changePath?: string | undefined;
outputScriptHex: string;
lockTime?: number | undefined;
blockHeight?: number | undefined;
sigHashType?: number | undefined;
segwit?: boolean | undefined;
additionals: Array<string>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import coininfo from "coininfo";
import BigNumber from "bignumber.js";
import { DerivationModes } from "../types";
import Xpub from "../xpub";
import BitcoinLikeExplorer from "../explorer";
import BitcoinLikeStorage from "../storage";
import { Merge } from "../pickingstrategies/Merge";
import BitcoinLikeWallet from "../wallet";
import MockBtcSigner from "../../mockBtcSigner";
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
import ZCash from "../crypto/zec";

describe("testing zcash transactions", () => {
const wallet = new BitcoinLikeWallet();
const explorer = new BitcoinLikeExplorer({
cryptoCurrency: getCryptoCurrencyById("zcash"),
});

const network = coininfo.zcash.main.toBitcoinJS();
const crypto = new ZCash({ network });

const storage = new BitcoinLikeStorage();
const xpub = new Xpub({
storage,
explorer,
crypto,
xpub: "xpub6CkCmmUPLcfa5ok52mSD8LdqxY7ixWLVuVruLLxmQHdefjJx6DtJdXdp8TDXMrfHYB4mybYYaFxMbDW2MzuZgudpNW44KRhyzAog5kd1Tda",
derivationMode: DerivationModes.LEGACY,
});
it("testing zcash transactions created with proper blockheight info being passed", async () => {
const utxoPickingStrategy = new Merge(xpub.crypto, xpub.derivationMode, []);
const changeAddress = await xpub.getNewAddress(1, 0);
const OUTPUT_VALUE_1 = 1_000_000;
const OUTPUT_VALUE_2 = 2_000_000;
xpub.storage.appendTxs([
{
id: "fca8f0b145a04e8cd1286dba61f7ad8bee389d12a70a0137aa4af6d1cb7efbb8",
received_at: "2024-12-04T08:31:06Z",
fees: 226226,
inputs: [
{
output_hash: "34a59cc1fc6dc9b5a6c4ec0c3640fed91baf90eede0ed3f7e1bcd1299e14aab5",
output_index: 1,
value: "40638286",
address: "t1eQ9NKV1rLx7DyehHVKDDMX5TqNdg5A2jZ",
sequence: 0,
},
],
outputs: [
{
output_index: 0,
value: `${OUTPUT_VALUE_1}`,
address: "t1UevC8vTmRDTMzntzp8XG9An114ToGQu6w",
output_hash: "fca8f0b145a04e8cd1286dba61f7ad8bee389d12a70a0137aa4af6d1cb7efbb8",
block_height: 2738883,
rbf: true,
},
{
output_index: 1,
value: "40312060",
address: "t1fGmLoo3AuQYU7kytozzcLmTa7q2978Efw",
output_hash: "fca8f0b145a04e8cd1286dba61f7ad8bee389d12a70a0137aa4af6d1cb7efbb8",
block_height: 2738883,
rbf: true,
},
],
block: {
hash: "00000000011e5982320559c1beb83d3a307f5229cf09d2cf6d7a803e0dae1860",
height: 2738883,
time: "2024-12-04T08:31:06Z",
},
account: 0,
index: 8,
address: "t1UevC8vTmRDTMzntzp8XG9An114ToGQu6w",
},

{
id: "db9e8290e1f6ecd7159e122419918f14be2047c5493de0e46f52d63107aa9c98",
received_at: "2022-09-12T11:37:00Z",
fees: 2486,
inputs: [
{
output_hash: "f4278db5c11680306d4f1b40568055ea286f50eda4e75e20ab7a2967a4f1731d",
output_index: 1,
value: "46382915",
address: "t1SCkDS3BayQG6SfxG9H1DP8KqzJhswget9",
sequence: 0,
},
],
outputs: [
{
output_index: 0,
value: "46330429",
address: "t1e8XpYgHoHs4VSrariNknegHhYzNAZ6F5h",
// "spent_at_height": null,
output_hash: "db9e8290e1f6ecd7159e122419918f14be2047c5493de0e46f52d63107aa9c98",
block_height: 1806010,
rbf: true,
},
{
output_index: 1,
value: `${OUTPUT_VALUE_2}`,
address: "t1grLcTkpVUjULAD28wSFAd7BEnE6uBdueR",
output_hash: "db9e8290e1f6ecd7159e122419918f14be2047c5493de0e46f52d63107aa9c98",
block_height: 1806010,
rbf: true,
},
],
block: {
hash: "0000000000bbb5e4348048f4b0ecc72f61f91d26c68c512157cc8313c989ce89",
height: 1806010,
time: "2022-09-12T11:37:00Z",
},
account: 1,
index: 5,
address: "t1grLcTkpVUjULAD28wSFAd7BEnE6uBdueR",
},
]);
const balance = await xpub.getXpubBalance();

const txInfo = await xpub.buildTx({
destAddress: "t1T8MQwJhUiDdxP2XCfcLviTPCsnQJyfcL1",
amount: new BigNumber(balance),
feePerByte: 0,
changeAddress,
utxoPickingStrategy,
sequence: 0,
});
const account = await wallet.generateAccount(
{
xpub: xpub.xpub,
path: "44'/0'",
index: 0,
currency: "zcash",
network: "mainnet",
derivationMode: DerivationModes.LEGACY,
},
getCryptoCurrencyById("zcash"),
);

const CURRENT_BLOCK_HEIGHT = 2810986;
account.xpub.currentBlockHeight = CURRENT_BLOCK_HEIGHT;

// Mock `createPaymentTransaction`
const mockBtcSigner = new MockBtcSigner();
mockBtcSigner.createPaymentTransaction = jest.fn().mockResolvedValue("signed_tx");

await wallet.signAccountTx({
btc: mockBtcSigner,
fromAccount: account,
txInfo,
});

expect(mockBtcSigner.createPaymentTransaction).toHaveBeenCalledTimes(1);

expect(mockBtcSigner.createPaymentTransaction).toHaveBeenCalledWith(
expect.objectContaining({
inputs: expect.arrayContaining([
expect.arrayContaining([1806010]),
expect.arrayContaining([2738883]),
]),
blockHeight: CURRENT_BLOCK_HEIGHT,
}),
);
}, 100000);
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface Input {
output_hash: string;
output_index: number;
sequence: number;
block_height?: number | null;
}

export interface Output {
Expand Down
5 changes: 5 additions & 0 deletions libs/coin-modules/coin-bitcoin/src/wallet-btc/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class BitcoinLikeWallet {
onDeviceSignatureGranted,
onDeviceStreaming,
} = params;
const blockHeight = fromAccount.xpub.currentBlockHeight;
let length = txInfo.outputs.reduce((sum, output) => {
return sum + 8 + output.script.length + 1;
}, 1);
Expand Down Expand Up @@ -251,6 +252,7 @@ class BitcoinLikeWallet {
number,
string | null | undefined,
number | null | undefined,
number | null | undefined, // NOTE: blockheight
][];
const inputs: Inputs = txInfo.inputs.map(i => {
log("hw", `splitTransaction`, {
Expand All @@ -264,6 +266,7 @@ class BitcoinLikeWallet {
i.output_index,
null,
i.sequence,
i.block_height,
];
});

Expand All @@ -273,6 +276,7 @@ class BitcoinLikeWallet {
inputs,
associatedKeysets,
outputScriptHex,
blockHeight,
...(params.lockTime && { lockTime: params.lockTime }),
...(params.sigHashType && { sigHashType: params.sigHashType }),
...(params.segwit && { segwit: params.segwit }),
Expand All @@ -287,6 +291,7 @@ class BitcoinLikeWallet {
inputs,
associatedKeysets,
outputScriptHex,
blockHeight,
...(params.lockTime && { lockTime: params.lockTime }),
...(params.sigHashType && { sigHashType: params.sigHashType }),
...(params.segwit && { segwit: params.segwit }),
Expand Down
1 change: 1 addition & 0 deletions libs/coin-modules/coin-bitcoin/src/wallet-btc/xpub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ class Xpub {
output_hash: utxo.output_hash,
output_index: utxo.output_index,
sequence,
block_height: utxo.block_height || null,
};
});

Expand Down
2 changes: 1 addition & 1 deletion libs/ledger-live-common/src/apps/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const appConfig: Record<string, ConfigInfo> = {
config_nanoapp_zcash: {
type: "object",
default: {
minVersion: "2.1.0",
minVersion: "2.3.1",
},
},
config_nanoapp_near: {
Expand Down
5 changes: 3 additions & 2 deletions libs/ledgerjs/packages/hw-app-btc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,15 +517,16 @@ Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/

### CreateTransactionArg

Type: {inputs: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<\[[Transaction](#transaction), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))]>, associatedKeysets: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, changePath: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, outputScriptHex: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), lockTime: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, sigHashType: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, segwit: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, additionals: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, expiryHeight: [Buffer](https://nodejs.org/api/buffer.html)?, useTrustedInputForSegwit: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, onDeviceStreaming: function (arg0: {progress: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), total: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), index: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)}): void?, onDeviceSignatureRequested: function (): void?, onDeviceSignatureGranted: function (): void?}
Type: {inputs: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<\[[Transaction](#transaction), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))?]>, associatedKeysets: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, changePath: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?, outputScriptHex: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), lockTime: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, blockHeight: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, sigHashType: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?, segwit: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, additionals: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>, expiryHeight: [Buffer](https://nodejs.org/api/buffer.html)?, useTrustedInputForSegwit: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, onDeviceStreaming: function (arg0: {progress: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), total: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), index: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)}): void?, onDeviceSignatureRequested: function (): void?, onDeviceSignatureGranted: function (): void?}

#### Properties

* `inputs` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<\[[Transaction](#transaction), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))]>**&#x20;
* `inputs` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<\[[Transaction](#transaction), [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)), ([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number) | null | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))?]>**&#x20;
* `associatedKeysets` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**&#x20;
* `changePath` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?**&#x20;
* `outputScriptHex` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**&#x20;
* `lockTime` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**&#x20;
* `blockHeight` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**&#x20;
* `sigHashType` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**&#x20;
* `segwit` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?**&#x20;
* `additionals` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**&#x20;
Expand Down
1 change: 1 addition & 0 deletions libs/ledgerjs/packages/hw-app-btc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
},
"scripts": {
"clean": "rimraf lib lib-es",
"coverage": "jest --coverage --testPathIgnorePatterns='/.test.ts.disabled|node_modules|lib-es|lib/' --passWithNoTests && mv coverage/coverage-final.json coverage/coverage-hw-app-btc.json",
"build": "tsc && tsc -m esnext --moduleResolution bundler --outDir lib-es",
"prewatch": "pnpm build",
"watch": "tsc --watch",
Expand Down
8 changes: 7 additions & 1 deletion libs/ledgerjs/packages/hw-app-btc/src/BtcNew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,13 @@ export default class BtcNew {
private async setInput(
psbt: PsbtV2,
i: number,
input: [Transaction, number, string | null | undefined, number | null | undefined],
input: [
Transaction,
number,
string | null | undefined,
number | null | undefined,
(number | null | undefined)?,
],
pathElements: number[],
accountType: AccountType,
masterFP: Buffer,
Expand Down
3 changes: 3 additions & 0 deletions libs/ledgerjs/packages/hw-app-btc/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ export const OP_EQUAL = 0x87;
export const OP_EQUALVERIFY = 0x88;
export const OP_CHECKSIG = 0xac;
export const OP_RETURN = 0x6a;

// https://zips.z.cash/zip-0253
export const ZCASH_NU6_ACTIVATION_HEIGHT = 2726400;
Loading

0 comments on commit 15a8f00

Please sign in to comment.