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

Minor enhancements #135

Merged
merged 12 commits into from
Sep 20, 2022
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ cardano-hw-cli address key-gen
--path PATH Derivation path to the key we want to sign with.
--verification-key-file FILE Output filepath of the verification key.
--hw-signing-file FILE Output filepath of the hardware wallet signing file.
--derivation-type TYPE Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).
```
Arguments can be specified multiple times for bulk export.

Expand All @@ -39,6 +40,7 @@ cardano-hw-cli transaction sign
--change-output-key-file FILE Input filepath of the hardware wallet signing file (so hw cli can match the keys of the change address, if present, and let the device hide it).
--mainnet | --testnet-magic NATURAL Use the mainnet magic id or specify testnet magic id.
--out-file FILE Output filepath of the Tx.
--derivation-type TYPE Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).
```

## Witness transaction
Expand All @@ -50,6 +52,7 @@ cardano-hw-cli transaction witness
--change-output-key-file FILE Input filepath of the hardware wallet signing file (so hw cli can match the keys of the change address, if present, and let the device hide it).
--mainnet | --testnet-magic NATURAL Use the mainnet magic id or specify testnet magic id.
--out-file FILE Output filepath of the witness (one or more witness files can be specified).
--derivation-type TYPE Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).
```

## Validate raw transaction
Expand Down Expand Up @@ -102,16 +105,18 @@ cardano-hw-cli address show
--payment-path PAYMENTPATH Payment derivation path.
--staking-path STAKINGPATH Stake derivation path.
--address-file ADDRESS Input filepath of the address.
--derivation-type TYPE Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).
```

## Issue operational certificate
```
cardano-hw-cli node issue-op-cert
--kes-verification-key-file FILE Input filepath of the file with KES vkey.
--operational-certificate-issue-counter FILE Input filepath of the file with certificate counter.
--kes-period UINT64 Kes period for the certificate.
--out-file FILE Output filepath for node certificate.
--hw-signing-file FILE Input filepath of the hardware wallet signing file.
--kes-verification-key-file FILE Input filepath of the file with KES vkey.
--operational-certificate-issue-counter FILE Input filepath of the issue counter file. This option is DEPRECATED. Please use --operational-certificate-issue-counter-file instead.
--operational-certificate-issue-counter-file FILE Input filepath of the file with certificate counter.
--kes-period UINT64 Kes period for the certificate.
--out-file FILE Output filepath for node certificate.
--hw-signing-file FILE Input filepath of the hardware wallet signing file.
```

## Catalyst voting registration
Expand All @@ -124,15 +129,17 @@ cardano-hw-cli catalyst voting-key-registration-metadata
--nonce NONCE Current slot number.
--reward-address-signing-key FILE Input filepath of the reward address signing files.
--metadata-cbor-out-file FILE Output filepath of metadata cbor.
--derivation-type TYPE Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).
```

see [Catalyst voting registration example](docs/catalyst-voting-registration-example.md)

## Policy id generation
```
cardano-hw-cli transaction policyid
--script-file Path to a native script file
--hw-signing-file Input filepath of the hardware wallet signing file
--script-file Path to a native script file
--hw-signing-file Input filepath of the hardware wallet signing file
--derivation-type TYPE Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).
```

see [Policy id](docs/token-minting.md#policy-id)
Expand Down
2 changes: 1 addition & 1 deletion docs/pool-registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ This step creates an operational certificate. It is required for running the blo
cardano-hw-cli node issue-op-cert \
--kes-verification-key-file kes.vkey \
--hw-signing-file cold.hwsfile \
--operational-certificate-issue-counter cold.counter \
--operational-certificate-issue-counter-file cold.counter \
--kes-period 99 \
--out-file node.cert
```
Expand Down
2 changes: 1 addition & 1 deletion scripts/autocomplete.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ _cardano_hw_cli_completions()
COMPREPLY=( $(compgen -W "--path --hw-signing-file --cold-verification-key-file --operational-certificate-issue-counter-file" -- "${COMP_WORDS[-1]}") )
fi
if [ "${COMP_WORDS[2]}" = "issue-op-cert" ]; then
COMPREPLY=( $(compgen -W "--kes-verification-key-file --kes-period --operational-certificate-issue-counter --hw-signing-file --out-file" -- "${COMP_WORDS[-1]}") )
COMPREPLY=( $(compgen -W "--kes-verification-key-file --kes-period --operational-certificate-issue-counter-file --hw-signing-file --out-file" -- "${COMP_WORDS[-1]}") )
fi
if [ "${COMP_WORDS[2]}" = "voting-key-registration-metadata" ]; then
COMPREPLY=( $(compgen -W "--mainnet --testnet-magic --vote-public-key --reward-address --stake-signing-key --nonce --reward-address-signing-key --metadata-cbor-out-file" -- "${COMP_WORDS[-1]}") )
Expand Down
28 changes: 24 additions & 4 deletions src/command-parser/parserConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,18 @@ import {
parseVotePubFile,
parseScriptHashHex,
parseNativeScriptFile,
parseDerivationType,
} from './parsers'

const derivationTypeArg = {
'--derivation-type': {
required: false,
type: (name?: string) => parseDerivationType(name),
dest: 'derivationType',
help: 'Derivation type - currently applies only to Trezor. Options: LEDGER, ICARUS or ICARUS_TREZOR (default).',
},
}

const keyGenArgs = {
'--path': {
required: true,
Expand All @@ -33,6 +43,7 @@ const keyGenArgs = {
dest: 'verificationKeyFiles',
help: 'Output filepath of the hardware wallet signing file.',
},
...derivationTypeArg,
}

const nodeKeyGenArgs = {
Expand Down Expand Up @@ -103,6 +114,7 @@ const txSigningArgs = {
type: (path: string) => parseHwSigningFile(path),
help: 'Input filepath of change output file.',
},
...derivationTypeArg,
}

const opCertSigningArgs = {
Expand All @@ -118,10 +130,15 @@ const opCertSigningArgs = {
type: (kesPeriod: string) => BigInt(kesPeriod),
help: 'KES period.',
},
'--operational-certificate-issue-counter': {
required: true,
dest: 'issueCounterFile',
help: 'Input filepath of the issue counter file.',
'_mutually-exclusive-group-required-operational-certificate-issue-counter': {
'--operational-certificate-issue-counter': {
dest: 'issueCounterFile',
help: 'Input filepath of the issue counter file. This option is DEPRECATED. Please use --operational-certificate-issue-counter-file instead.',
},
'--operational-certificate-issue-counter-file': {
dest: 'issueCounterFile',
help: 'Input filepath of the issue counter file.',
},
},
'--hw-signing-file': {
dest: 'hwSigningFileData',
Expand Down Expand Up @@ -205,6 +222,7 @@ export const parserConfig = {
dest: 'address',
help: 'Input filepath of the address.',
},
...derivationTypeArg,
},
},
'transaction': {
Expand All @@ -230,6 +248,7 @@ export const parserConfig = {
type: (path: string) => parseHwSigningFile(path),
help: 'Input filepath of the hardware wallet signing file.',
},
...derivationTypeArg,
},
'witness': {
...txSigningArgs,
Expand Down Expand Up @@ -337,6 +356,7 @@ export const parserConfig = {
dest: 'outFile',
help: 'Output metadata cbor filepath.',
},
...derivationTypeArg,
},
},
}
17 changes: 16 additions & 1 deletion src/command-parser/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import {
txTypeToCardanoEra,
} from '../constants'
import {
isBIP32Path, isCborHex, isHwSigningData, isRawTxFileData, isTxFileData, isVotePublicKeyHex,
isBIP32Path,
isCborHex,
isDerivationType,
isHwSigningData,
isRawTxFileData,
isTxFileData,
isVotePublicKeyHex,
} from '../guards'
import { Errors } from '../errors'
import {
Address,
BIP32Path,
DerivationType,
HwSigningData,
HwSigningType,
NativeScript,
Expand Down Expand Up @@ -267,3 +274,11 @@ export const parseNativeScriptFile = (path: string): NativeScript => {

return parseNativeScriptData(data)
}

export const parseDerivationType = (
name?: string,
): DerivationType | undefined => {
if (!name) return undefined
if (isDerivationType(name)) return name
throw Error(Errors.InvalidDerivationTypeError)
}
16 changes: 11 additions & 5 deletions src/commandExecutor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as InteropLib from 'cardano-hw-interop-lib'
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid-noevents'
import { CryptoProvider } from './crypto-providers/types'
import { CryptoProvider, SigningParameters } from './crypto-providers/types'
import {
constructTxFileOutput,
constructHwSigningKeyOutput,
Expand Down Expand Up @@ -81,10 +81,12 @@ const CommandExecutor = async () => {
}

const createSigningKeyFile = async (
{ paths, hwSigningFiles, verificationKeyFiles }: ParsedAddressKeyGenArguments,
{
paths, hwSigningFiles, verificationKeyFiles, derivationType,
}: ParsedAddressKeyGenArguments,
) => {
validateKeyGenInputs(paths, hwSigningFiles, verificationKeyFiles)
const xPubKeys = await cryptoProvider.getXPubKeys(paths)
const xPubKeys = await cryptoProvider.getXPubKeys(paths, derivationType)
xPubKeys.forEach((xPubKey, i) => writeOutputData(
hwSigningFiles[i],
constructHwSigningKeyOutput(xPubKey, paths[i]),
Expand Down Expand Up @@ -125,14 +127,15 @@ const CommandExecutor = async () => {

const txBody = (rawTx?.body ?? tx?.body)!
const era = (args.rawTxFileData?.era ?? args.txFileData?.era)!
const signingParameters = {
const signingParameters: SigningParameters = {
signingMode: determineSigningMode(txBody, args.hwSigningFileData),
rawTx,
tx,
txBodyHashHex: getTxBodyHash(txBody),
hwSigningFileData: args.hwSigningFileData,
network: args.network,
era,
derivationType: args.derivationType,
}
validateSigning(signingParameters)

Expand All @@ -147,6 +150,7 @@ const CommandExecutor = async () => {
args.nativeScript,
args.hwSigningFileData,
NativeScriptDisplayFormat.POLICY_ID,
args.derivationType,
)

// eslint-disable-next-line no-console
Expand All @@ -171,14 +175,15 @@ const CommandExecutor = async () => {

const txBody = (rawTx?.body ?? tx?.body)!
const era = (args.rawTxFileData?.era ?? args.txFileData?.era)!
const signingParameters = {
const signingParameters: SigningParameters = {
signingMode: determineSigningMode(txBody, args.hwSigningFileData),
rawTx,
tx,
txBodyHashHex: getTxBodyHash(txBody),
hwSigningFileData: args.hwSigningFileData,
network: args.network,
era,
derivationType: args.derivationType,
}
validateWitnessing(signingParameters)
const {
Expand Down Expand Up @@ -271,6 +276,7 @@ const CommandExecutor = async () => {
args.votePublicKey,
args.network,
args.nonce,
args.derivationType,
)

writeCbor(args.outFile, Buffer.from(votingRegistrationMetaData, 'hex') as Cbor)
Expand Down
46 changes: 15 additions & 31 deletions src/crypto-providers/ledgerCryptoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ import {
} from './types'
import {
findSigningPathForKeyHash,
getSigningPath,
PathTypes,
classifyPath,
getAddressAttributes,
ipv4ToString,
ipv6ToString,
Expand All @@ -48,6 +45,7 @@ import {
rewardAccountToStakeCredential,
areAddressParamsAllowed,
pathEquals,
isByronPath,
} from './util'

const { bech32 } = require('cardano-crypto.js')
Expand Down Expand Up @@ -104,18 +102,13 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi
}

const prepareInput = (
signingMode: SigningMode,
input: TxTypes.TransactionInput,
path: BIP32Path | null,
): LedgerTypes.TxInput => {
const pathToUse = (signingMode === SigningMode.POOL_REGISTRATION_AS_OWNER)
? null // inputs are required to be given without path in this case
: path
if (input.index > Number.MAX_SAFE_INTEGER) {
throw Error(Errors.InvalidInputError)
}
return {
path: pathToUse,
path: null, // all payment paths are added added as additionalWitnessRequests
txHashHex: input.transactionId.toString('hex'),
outputIndex: Number(input.index),
}
Expand Down Expand Up @@ -531,13 +524,12 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi

const prepareCollateralInput = (
collateralInput: TxTypes.TransactionInput,
path: BIP32Path | null,
): LedgerTypes.TxInput => {
if (collateralInput.index > Number.MAX_SAFE_INTEGER) {
throw Error(Errors.InvalidCollateralInputError)
}
return {
path,
path: null, // all payment paths are added added as additionalWitnessRequests
txHashHex: collateralInput.transactionId.toString('hex'),
outputIndex: Number(collateralInput.index),
}
Expand All @@ -560,13 +552,15 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi
}

const prepareAdditionalWitnessRequests = (
paymentSigningFiles: HwSigningData[],
mintSigningFiles: HwSigningData[],
multisigSigningFiles: HwSigningData[],
) => (
// Even though Plutus txs might require additional payment/stake signatures, Plutus scripts
// Payment signing files are always added here, so that the inputs are witnessed.
// Even though Plutus txs might require additional stake signatures, Plutus scripts
// don't see signatures directly - they can only access requiredSigners, and their witnesses
// are gathered above.
[...mintSigningFiles, ...multisigSigningFiles].map((f) => f.path)
[...paymentSigningFiles, ...mintSigningFiles, ...multisigSigningFiles].map((f) => f.path)
)

const createWitnesses = (
Expand All @@ -581,8 +575,6 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi
throw Error(Errors.MissingHwSigningDataAtPathError)
}

const isByronPath = (path: BIP32Path) => classifyPath(path) === PathTypes.PATH_WALLET_SPENDING_KEY_BYRON

const witnessesWithKeys = ledgerWitnesses.map((witness) => {
const { pubKey, chainCode } = splitXPubKeyCborHex(
getSigningFileDataByPath(witness.path as BIP32Path).cborXPubKeyHex,
Expand Down Expand Up @@ -644,10 +636,7 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi
paymentSigningFiles, stakeSigningFiles, poolColdSigningFiles, mintSigningFiles, multisigSigningFiles,
} = filterSigningFiles(hwSigningFileData)

const inputs = body.inputs.map(
// assign first `inputs.length` signing files to inputs
(input, i) => prepareInput(signingMode, input, getSigningPath(paymentSigningFiles, i)),
)
const inputs = body.inputs.map(prepareInput)
const outputs = body.outputs.map(
(output) => prepareOutput(output, network, changeOutputFiles, signingMode),
)
Expand All @@ -668,14 +657,7 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi
const auxiliaryData = prepareAuxiliaryDataHashHex(body.auxiliaryDataHash)
const mint = body.mint ? prepareTokenBundle(body.mint) : null
const scriptDataHashHex = prepareScriptDataHash(body.scriptDataHash)
const collateralInputs = body.collateralInputs?.map(
(collateralInput, i) => prepareCollateralInput(
// first `inputs.length` signing files were assigned to inputs,
// assign the following `collaterals.length` signing files to collateral inputs
collateralInput,
getSigningPath(paymentSigningFiles, inputs.length + i),
),
)
const collateralInputs = body.collateralInputs?.map(prepareCollateralInput)
const requiredSigners = body.requiredSigners?.map(
(requiredSigner) => prepareRequiredSigner(
requiredSigner,
Expand All @@ -687,11 +669,13 @@ export const LedgerCryptoProvider: (transport: Transport) => Promise<CryptoProvi
? prepareOutput(body.collateralReturnOutput, network, changeOutputFiles, signingMode)
: undefined
const totalCollateral = body.totalCollateral !== undefined ? `${body.totalCollateral}` : undefined
const referenceInputs = body.referenceInputs?.map(
(referenceInput) => prepareInput(signingMode, referenceInput, null),
)
const referenceInputs = body.referenceInputs?.map(prepareInput)

const additionalWitnessRequests = prepareAdditionalWitnessRequests(mintSigningFiles, multisigSigningFiles)
const additionalWitnessRequests = prepareAdditionalWitnessRequests(
paymentSigningFiles,
mintSigningFiles,
multisigSigningFiles,
)

const response = await ledger.signTransaction({
signingMode: signingModeToLedgerType(signingMode),
Expand Down
Loading