Skip to content

Commit

Permalink
standalone script data hash
Browse files Browse the repository at this point in the history
  • Loading branch information
mkv-vcm committed Nov 22, 2021
1 parent 97047e6 commit 37afba2
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/errors/invalidDataReason.ts
Expand Up @@ -132,6 +132,8 @@ export enum InvalidDataReason {

VALIDITY_INTERVAL_START_INVALID = "invalid validity interval start",

SCRIPT_DATA_HASH_WRONG_LENGTH = "script data hash not 32 long",

SIGN_MODE_UNKNOWN = "unknown signing mode",

SIGN_MODE_ORDINARY__POOL_REGISTRATION_NOT_ALLOWED =
Expand Down
3 changes: 3 additions & 0 deletions src/interactions/getVersion.ts
Expand Up @@ -41,6 +41,8 @@ export function getCompatibility(version: Version): DeviceCompatibility {
const v2_3 = isLedgerAppVersionAtLeast(version, 2, 3) && isLedgerAppVersionAtMost(version, 3, Infinity)
const v2_4 = isLedgerAppVersionAtLeast(version, 2, 4) && isLedgerAppVersionAtMost(version, 3, Infinity)
const v3_0 = isLedgerAppVersionAtLeast(version, 3, 0) && isLedgerAppVersionAtMost(version, 3, Infinity)
// placeholder until the Alonso compatible version is decided
const vAlonso = isLedgerAppVersionAtLeast(version, 3, 0) && isLedgerAppVersionAtMost(version, 3, Infinity)

return {
isCompatible: v2_2,
Expand All @@ -53,6 +55,7 @@ export function getCompatibility(version: Version): DeviceCompatibility {
supportsNativeScriptHashDerivation: v3_0,
supportsMultisigTransaction: v3_0,
supportsMint: v3_0,
supportsAlonso: vAlonso,
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/interactions/serialization/txInit.ts
Expand Up @@ -41,6 +41,9 @@ export function serializeTxInit(
const mintBuffer = getCompatibility(version).supportsMint
? _serializeOptionFlag(tx.mint != null)
: Buffer.from([])
const scriptDataHashBuffer = getCompatibility(version).supportsAlonso
? _serializeOptionFlag(tx.scriptDataHash != null)
: Buffer.from([])

return Buffer.concat([
uint8_to_buf(tx.network.networkId),
Expand All @@ -49,6 +52,7 @@ export function serializeTxInit(
_serializeOptionFlag(tx.auxiliaryData != null),
_serializeOptionFlag(tx.validityIntervalStart != null),
mintBuffer,
scriptDataHashBuffer,
_serializeSigningMode(signingMode),
uint32_to_buf(tx.inputs.length as Uint32_t),
uint32_to_buf(tx.outputs.length as Uint32_t),
Expand Down
6 changes: 5 additions & 1 deletion src/interactions/serialization/txOther.ts
@@ -1,5 +1,5 @@
import { InvalidDataReason } from "../../errors/invalidDataReason"
import type { Int64_str, ParsedAssetGroup, ParsedInput, ParsedToken, ParsedWithdrawal, Uint32_t, Uint64_str, ValidBIP32Path, Version } from "../../types/internal"
import type { Int64_str, ParsedAssetGroup, ParsedInput, ParsedToken, ParsedWithdrawal, ScriptDataHash, Uint32_t, Uint64_str, ValidBIP32Path, Version } from "../../types/internal"
import { StakeCredentialType } from "../../types/internal"
import { assert } from "../../utils/assert"
import { hex_to_buf, path_to_buf, stake_credential_to_buf,uint32_to_buf, uint64_to_buf } from "../../utils/serialize"
Expand Down Expand Up @@ -86,3 +86,7 @@ export function serializeMintBasicParams(mint: Array<ParsedAssetGroup<Int64_str>
uint32_to_buf(mint.length as Uint32_t),
])
}

export function serializeScriptDataHash(scriptDataHash: ScriptDataHash) {
return hex_to_buf(scriptDataHash)
}
26 changes: 24 additions & 2 deletions src/interactions/signTx.ts
@@ -1,5 +1,5 @@
import { DeviceVersionUnsupported } from "../errors"
import type { Int64_str, ParsedAssetGroup, ParsedCertificate, ParsedInput, ParsedOutput, ParsedSigningRequest, ParsedTransaction, ParsedTxAuxiliaryData, ParsedWithdrawal, Uint64_str, ValidBIP32Path, Version } from "../types/internal"
import type { Int64_str, ParsedAssetGroup, ParsedCertificate, ParsedInput, ParsedOutput, ParsedSigningRequest, ParsedTransaction, ParsedTxAuxiliaryData, ParsedWithdrawal, ScriptDataHash, Uint64_str, ValidBIP32Path, Version } from "../types/internal"
import { StakeCredentialType } from "../types/internal"
import { CertificateType, ED25519_SIGNATURE_LENGTH, PoolOwnerType, TX_HASH_LENGTH } from "../types/internal"
import type { SignedTransactionData, TxAuxiliaryDataSupplement} from "../types/public"
Expand All @@ -16,7 +16,7 @@ import { serializeFinancials, serializePoolInitialParams, serializePoolInitialPa
import { serializeTxAuxiliaryData } from "./serialization/txAuxiliaryData"
import { serializeTxCertificate } from "./serialization/txCertificate"
import { serializeTxInit } from "./serialization/txInit"
import { serializeAssetGroup, serializeMintBasicParams, serializeToken, serializeTxFee, serializeTxInput, serializeTxTtl, serializeTxValidityStart, serializeTxWithdrawal, serializeTxWitnessRequest } from "./serialization/txOther"
import { serializeAssetGroup, serializeMintBasicParams, serializeToken, serializeTxFee, serializeTxInput, serializeTxTtl, serializeTxValidityStart, serializeTxWithdrawal, serializeTxWitnessRequest, serializeScriptDataHash } from "./serialization/txOther"
import { serializeTxOutputBasicParams } from "./serialization/txOutput"

const enum P1 {
Expand All @@ -31,6 +31,7 @@ const enum P1 {
STAGE_VALIDITY_INTERVAL_START = 0x09,
STAGE_CONFIRM = 0x0a,
STAGE_MINT = 0x0b,
STAGE_SCRIPT_DATA_HASH = 0x0c,
STAGE_WITNESSES = 0x0f,
}

Expand Down Expand Up @@ -473,6 +474,18 @@ function* signTx_setMint(
})
}

function* signTx_setScriptDataHash(
scriptDataHash: ScriptDataHash
): Interaction<void> {
const enum P2 {
UNUSED = 0x00,
}
yield send({
p1: P1.STAGE_SCRIPT_DATA_HASH,
p2: P2.UNUSED,
data: serializeScriptDataHash(scriptDataHash),
})
}

function* signTx_awaitConfirm(
): Interaction<{ txHashHex: string; }> {
Expand Down Expand Up @@ -616,6 +629,10 @@ function ensureRequestSupportedByAppVersion(version: Version, request: ParsedSig
}
}
}

if (request?.tx?.scriptDataHash && !getCompatibility(version).supportsAlonso) {
throw new DeviceVersionUnsupported(`Alonso not supported by Ledger app version ${version}.`)
}
}

// general name, because it should work with any type if generalized
Expand Down Expand Up @@ -692,6 +709,11 @@ export function* signTransaction(version: Version, request: ParsedSigningRequest
yield* signTx_setMint(tx.mint)
}

// script data hash
if (tx.scriptDataHash != null) {
yield* signTx_setScriptDataHash(tx.scriptDataHash)
}

// confirm
const { txHashHex } = yield* signTx_awaitConfirm()

Expand Down
9 changes: 7 additions & 2 deletions src/parsing/transaction.ts
@@ -1,8 +1,8 @@
import { InvalidData } from "../errors"
import { InvalidDataReason } from "../errors/invalidDataReason"
import type { OutputDestination, ParsedAssetGroup, ParsedCertificate, ParsedInput, ParsedOutput, ParsedSigningRequest, ParsedToken, ParsedTransaction, ParsedWithdrawal } from "../types/internal"
import { OutputDestination, ParsedAssetGroup, ParsedCertificate, ParsedInput, ParsedOutput, ParsedSigningRequest, ParsedToken, ParsedTransaction, ParsedWithdrawal } from "../types/internal"
import { StakeCredentialType } from "../types/internal"
import { ASSET_NAME_LENGTH_MAX, CertificateType, SpendingDataSourceType, TOKEN_POLICY_LENGTH, TX_HASH_LENGTH } from "../types/internal"
import { ASSET_NAME_LENGTH_MAX, CertificateType, SpendingDataSourceType, TOKEN_POLICY_LENGTH, TX_HASH_LENGTH, SCRIPT_DATA_HASH_LENGTH } from "../types/internal"
import type {
AssetGroup,
Certificate,
Expand Down Expand Up @@ -137,6 +137,10 @@ export function parseTransaction(tx: Transaction): ParsedTransaction {
? null
: parseTokenBundle(tx.mint, false, parseInt64_str)

const scriptDataHash = tx.scriptDataHash == null
? null
: parseHexStringOfLength(tx.scriptDataHash, SCRIPT_DATA_HASH_LENGTH, InvalidDataReason.SCRIPT_DATA_HASH_WRONG_LENGTH)

return {
network,
inputs,
Expand All @@ -148,6 +152,7 @@ export function parseTransaction(tx: Transaction): ParsedTransaction {
certificates,
fee,
mint,
scriptDataHash,
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/types/internal.ts
Expand Up @@ -29,6 +29,7 @@ export const KES_PUBLIC_KEY_LENGTH = 32
export const VRF_KEY_HASH_LENGTH = 32
export const REWARD_ACCOUNT_HEX_LENGTH = 29
export const ED25519_SIGNATURE_LENGTH = 64
export const SCRIPT_DATA_HASH_LENGTH = 32

export const enum StakeCredentialType {
KEY_PATH = 0,
Expand Down Expand Up @@ -114,6 +115,7 @@ export type ParsedTransaction = {
auxiliaryData: ParsedTxAuxiliaryData | null
validityIntervalStart: Uint64_str | null
mint: Array<ParsedAssetGroup<Int64_str>> | null
scriptDataHash: ScriptDataHash | null
}

export type ParsedSigningRequest = {
Expand All @@ -135,6 +137,7 @@ export type ParsedWithdrawal = {
stakeCredential: ParsedStakeCredential
}

export type ScriptDataHash = FixlenHexString<typeof SCRIPT_DATA_HASH_LENGTH>

export type ParsedMargin = {
numerator: Uint64_str,
Expand Down
8 changes: 8 additions & 0 deletions src/types/public.ts
Expand Up @@ -820,6 +820,10 @@ export type DeviceCompatibility = {
* Whether we support mint
*/
supportsMint: boolean
/**
* Whether we support Alonso
*/
supportsAlonso: boolean
}

/**
Expand Down Expand Up @@ -1053,6 +1057,10 @@ export type Transaction = {
* i.e. the key with the lower value in lexical order sorts earlier.
*/
mint?: Array<AssetGroup> | null,
/**
* Script Data hash (if any)
*/
scriptDataHash?: string | null,
}

/**
Expand Down
22 changes: 22 additions & 0 deletions test/integration/__fixtures__/signTx.ts
Expand Up @@ -912,6 +912,28 @@ export const testsShelleyNoCertificates: TestcaseShelley[] = [
auxiliaryDataSupplement: null,
},
},
{
testname: "Sign tx with only script data hash",
tx: {
...shelleyBase,
outputs: [],
scriptDataHash: "ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce188",
},
signingMode: TransactionSigningMode.ORDINARY_TRANSACTION,
additionalWitnessPaths: [],
result: {
txHashHex:
"7c5aac719dd3e0888deef0c59d6daba9e578d0dc27f82ff4978fc2893cdc2202",
witnesses: [
{
path: str_to_path("1852'/1815'/0'/0/0"),
witnessSignatureHex:
"5c66a4f75359a62b4b32751fe30a1adbf7ed2839fd4cb762e9a4d2b086de82fca2310bcf07efc2b03086211faa19941dbe059bbfb747e128863f339720e71304",
},
],
auxiliaryDataSupplement: null,
},
},
]

export const testsShelleyWithCertificates: TestcaseShelley[] = [
Expand Down

0 comments on commit 37afba2

Please sign in to comment.