Skip to content

Commit

Permalink
feat: limit code in LLC by adding simpler serialiazer for signer
Browse files Browse the repository at this point in the history
Signed-off-by: Stéphane Prohaszka <[email protected]>
  • Loading branch information
sprohaszka-ledger committed May 23, 2024
1 parent 5dfa1bd commit f0d6ab8
Show file tree
Hide file tree
Showing 5 changed files with 453 additions and 259 deletions.
64 changes: 58 additions & 6 deletions libs/coin-modules/coin-cardano/src/js-signOperation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BigNumber } from "bignumber.js";
import { Observable } from "rxjs";
import { Bip32PublicKey } from "@stricahq/bip32ed25519";
import { HashType } from "@stricahq/typhonjs/dist/types";
import { FeeNotLoaded } from "@ledgerhq/errors";
import type {
CardanoAccount,
Expand All @@ -17,9 +19,14 @@ import { getNetworkParameters } from "./networks";
import { MEMO_LABEL } from "./constants";
import { OperationType, SignOperationEvent, SignOperationFnSignature } from "@ledgerhq/types-live";
import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
import { HashType } from "@stricahq/typhonjs/dist/types";
import { SignerContext } from "@ledgerhq/coin-framework/signer";
import { CardanoSigner } from "./signer";
import { CardanoSigner, Witness } from "./signer";
import {
prepareCertificate,
prepareLedgerInput,
prepareLedgerOutput,
prepareWithdrawal,
} from "./stricaTypeSerializer";

const buildOptimisticOperation = (
account: CardanoAccount,
Expand Down Expand Up @@ -202,17 +209,41 @@ const buildSignOperation =

const unsignedTransaction = await buildTransaction(account as CardanoAccount, transaction);

const ledgerAppInputs = unsignedTransaction
.getInputs()
.map(i => prepareLedgerInput(i, account.index));

const ledgerAppOutputs = unsignedTransaction
.getOutputs()
.map(o => prepareLedgerOutput(o, account.index));

const ledgerCertificates = unsignedTransaction.getCertificates().map(prepareCertificate);

const ledgerWithdrawals = unsignedTransaction.getWithdrawals().map(prepareWithdrawal);

const auxiliaryDataHashHex = unsignedTransaction.getAuxiliaryDataHashHex();

const accountPubKey = getExtendedPublicKeyFromHex(account.xpub as string);
const networkParams = getNetworkParameters(account.currency.id);

const signed = await signerContext(deviceId, signer =>
const signerTransaction = {
inputs: ledgerAppInputs,
outputs: ledgerAppOutputs,
certificates: ledgerCertificates,
withdrawals: ledgerWithdrawals,
fee: unsignedTransaction.getFee().toString(),
ttl: unsignedTransaction.getTTL()?.toString(),
validityIntervalStart: null,
auxiliaryData: auxiliaryDataHashHex ?? null,
};

const signedData = await signerContext(deviceId, signer =>
signer.sign({
unsignedTransaction,
accountPubKey,
accountIndex: account.index,
transaction: signerTransaction,
networkParams,
}),
);
const signed = signTx(unsignedTransaction, accountPubKey, signedData.witnesses);

o.next({ type: "device-signature-granted" });

Expand All @@ -236,4 +267,25 @@ const buildSignOperation =
);
});

/**
* Adds signatures to unsigned transaction
*/
export const signTx = (
unsignedTransaction: TyphonTransaction,
accountKey: Bip32PublicKey,
witnesses: Array<Witness>,
) => {
witnesses.forEach(witness => {
const [, , , chainType, index] = witness.path;
const publicKey = accountKey.derive(chainType).derive(index).toPublicKey().toBytes();
const vKeyWitness: TyphonTypes.VKeyWitness = {
signature: Buffer.from(witness.witnessSignatureHex, "hex"),
publicKey: Buffer.from(publicKey),
};
unsignedTransaction.addWitness(vKeyWitness);
});

return unsignedTransaction.buildTransaction();
};

export default buildSignOperation;
77 changes: 72 additions & 5 deletions libs/coin-modules/coin-cardano/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ export type CardanoAddress = {
address: string;
publicKey: string;
};
// Coming from @cardano-foundation/ledgerjs-hw-app-cardano code (type SignedTransactionData)
type BIP32Path = Array<number>;
export type Witness = {
path: BIP32Path;
witnessSignatureHex: string;
};
export type CardanoSignature = {
hash: string;
payload: string;
txHashHex: string;
witnesses: Array<Witness>;
};
// Coming from @cardano-foundation/ledgerjs-hw-app-cardano code (type ExtendedPublicKey)
export type CardanoExtendedPublicKey = {
Expand All @@ -21,10 +27,71 @@ export type GetAddressRequest = {
networkParams: CardanoLikeNetworkParameters;
verify?: boolean;
};

export type SignerTxInput = {
txHashHex: string;
outputIndex: number;
path: string | null;
};
export type SignerTxOutput = {
amount: string;
destination:
| {
isDeviceOwnedAddress: false;
params: {
addressHex: string;
};
}
| {
isDeviceOwnedAddress: true;
params: {
spendingPath: string;
stakingPath: string;
};
};
tokenBundle: Array<{
policyIdHex: string;
tokens: Array<{
assetNameHex: string;
amount: string;
}>;
}>;
};
export type SignerTxCertificate =
| {
type: "REGISTRATION" | "DEREGISTRATION";
params: {
stakeCredential: {
keyPath: string;
};
};
}
| {
type: "DELEGATION";
params: {
stakeCredential: {
keyPath: string;
};
poolKeyHashHex: string;
};
};
export type SignerTxWithdrawal = {
stakeCredential: {
keyPath: string;
};
amount: string;
};
export type SignerTransaction = {
inputs: Array<SignerTxInput>;
outputs: Array<SignerTxOutput>;
certificates: Array<SignerTxCertificate>;
withdrawals: Array<SignerTxWithdrawal>;
fee: string;
ttl?: string;
auxiliaryData: string | null;
};
export type CardanoSignRequest = {
unsignedTransaction: TyphonTransaction;
accountPubKey: Bip32PublicKey;
accountIndex: number;
transaction: SignerTransaction;
networkParams: CardanoLikeNetworkParameters;
};
export interface CardanoSigner {
Expand Down
Loading

0 comments on commit f0d6ab8

Please sign in to comment.