Skip to content

Commit

Permalink
explorer: show zklogin signature (MystenLabs#14012)
Browse files Browse the repository at this point in the history
## Description 

* adds zklogin package in sui.js

| before | after |
| -- | -- |
| <img width="713" alt="Screenshot 2023-09-28 at 20 50 20"
src="https://github.com/MystenLabs/sui/assets/10210143/f16fe021-97e6-4a76-9150-f2b80b174df1">
| <img width="713" alt="Screenshot 2023-09-28 at 20 50 45"
src="https://github.com/MystenLabs/sui/assets/10210143/020e90eb-77f6-42f7-aa77-e028c28f3951">
|

closes [APPS-1770](https://mysten.atlassian.net/browse/APPS-1770)

## Test Plan 

👀 

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
pchrysochoidis authored Sep 29, 2023
1 parent 5453c0d commit 3764c46
Show file tree
Hide file tree
Showing 38 changed files with 414 additions and 207 deletions.
6 changes: 6 additions & 0 deletions .changeset/afraid-laws-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@mysten/zklogin': patch
---

- use new zklogin package from @mysten/sui.js for some of the zklogin functionality
- rename `getZkSignature` to `getZkLoginSignature`
6 changes: 6 additions & 0 deletions .changeset/hot-beers-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@mysten/sui.js': patch
---

- add support for basic parsing of zkLogin signatures
- new zklogin package
53 changes: 37 additions & 16 deletions apps/explorer/src/pages/transaction-result/Signatures.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,43 @@ import { DescriptionItem, DescriptionList } from '~/ui/DescriptionList';
import { AddressLink } from '~/ui/InternalLink';
import { TabHeader } from '~/ui/Tabs';

interface SignaturePubkeyPair {
type SignaturePubkeyPair = {
signatureScheme: SignatureScheme;
publicKey: PublicKey;
signature: Uint8Array;
}

function SignaturePanel({ title, signature }: { title: string; signature: SignaturePubkeyPair }) {
} & ({ address: string } | { publicKey: PublicKey });

function SignaturePanel({
title,
signature: data,
}: {
title: string;
signature: SignaturePubkeyPair;
}) {
const { signature, signatureScheme } = data;
return (
<TabHeader title={title}>
<DescriptionList>
<DescriptionItem title="Scheme" align="start" labelWidth="sm">
<Text variant="pBody/medium" color="steel-darker">
{signature.signatureScheme}
{signatureScheme}
</Text>
</DescriptionItem>
<DescriptionItem title="Address" align="start" labelWidth="sm">
<AddressLink noTruncate address={signature.publicKey.toSuiAddress()} />
</DescriptionItem>
<DescriptionItem title="Sui Public Key" align="start" labelWidth="sm">
<Text variant="pBody/medium" color="steel-darker">
{signature.publicKey.toSuiPublicKey()}
</Text>
<AddressLink
noTruncate
address={'address' in data ? data.address : data.publicKey.toSuiAddress()}
/>
</DescriptionItem>
{'publicKey' in data ? (
<DescriptionItem title="Sui Public Key" align="start" labelWidth="sm">
<Text variant="pBody/medium" color="steel-darker">
{data.publicKey.toSuiPublicKey()}
</Text>
</DescriptionItem>
) : null}
<DescriptionItem title="Signature" align="start" labelWidth="sm">
<Text variant="pBody/medium" color="steel-darker">
{toB64(signature.signature)}
{toB64(signature)}
</Text>
</DescriptionItem>
</DescriptionList>
Expand All @@ -51,7 +62,9 @@ function SignaturePanel({ title, signature }: { title: string; signature: Signat

function getSignatureFromAddress(signatures: SignaturePubkeyPair[], suiAddress: string) {
return signatures.find(
(signature) => signature.publicKey.toSuiAddress() === normalizeSuiAddress(suiAddress),
(signature) =>
('address' in signature ? signature.address : signature.publicKey.toSuiAddress()) ===
normalizeSuiAddress(suiAddress),
);
}

Expand All @@ -60,7 +73,9 @@ function getSignaturesExcludingAddress(
suiAddress: string,
): SignaturePubkeyPair[] {
return signatures.filter(
(signature) => signature.publicKey.toSuiAddress() !== normalizeSuiAddress(suiAddress),
(signature) =>
('address' in signature ? signature.address : signature.publicKey.toSuiAddress()) !==
normalizeSuiAddress(suiAddress),
);
}
interface Props {
Expand All @@ -79,10 +94,16 @@ export function Signatures({ transaction }: Props) {
const deserializedTransactionSignatures = transactionSignatures
.map((signature) => {
const parsed = parseSerializedSignature(signature);

if (parsed.signatureScheme === 'MultiSig') {
return parsePartialSignatures(parsed.multisig);
}
if (parsed.signatureScheme === 'ZkLogin') {
return {
signatureScheme: parsed.signatureScheme,
address: parsed.zkLogin.address,
signature: parsed.bytes,
};
}

return {
...parsed,
Expand Down
4 changes: 2 additions & 2 deletions apps/wallet/src/background/accounts/zk/ZkAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type PublicKey,
type SerializedSignature,
} from '@mysten/sui.js/cryptography';
import { computeZkAddress, genAddressSeed, getZkSignature } from '@mysten/zklogin';
import { computeZkAddress, genAddressSeed, getZkLoginSignature } from '@mysten/zklogin';
import { blake2b } from '@noble/hashes/blake2b';
import { decodeJwt } from 'jose';

Expand Down Expand Up @@ -242,7 +242,7 @@ export class ZkAccount
const { addressSeed: addressSeedObfuscated } = await this.getStoredData();
const addressSeed = await deobfuscate<string>(addressSeedObfuscated);

return getZkSignature({
return getZkLoginSignature({
inputs: { ...proofs, addressSeed },
maxEpoch,
userSignature,
Expand Down
4 changes: 2 additions & 2 deletions doc/src/build/zk_login.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,9 @@ const { bytes, signature: userSignature } = await txb.sign({

Next, serialize the zkLogin signature by combining the ZK proof and the ephemeral signature.
```typescript
import { getZkSignature } from "@mysten/zklogin";
import { getZkLoginSignature } from "@mysten/zklogin";

const zkSignature = getZkSignature({
const zkLoginSignature = getZkLoginSignature({
inputs,
maxEpoch,
userSignature,
Expand Down
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ packages:
- '!sdk/typescript/multisig'
- '!sdk/typescript/utils'
- '!sdk/typescript/bcs'
- '!sdk/typescript/zklogin'
8 changes: 7 additions & 1 deletion sdk/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"src",
"transactions",
"utils",
"verify"
"verify",
"zklogin"
],
"engines": {
"node": ">=16"
Expand Down Expand Up @@ -81,6 +82,11 @@
"source": "./src/verify/index.ts",
"import": "./dist/esm/verify/index.js",
"require": "./dist/cjs/verify/index.js"
},
"./zklogin": {
"source": "./src/zklogin/index.ts",
"import": "./dist/esm/zklogin/index.js",
"require": "./dist/cjs/zklogin/index.js"
}
},
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions sdk/typescript/src/cryptography/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

export * from './signature.js';
export * from './signature-scheme.js';
export * from './mnemonics.js';
export * from './intent.js';

Expand Down
3 changes: 2 additions & 1 deletion sdk/typescript/src/cryptography/keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { blake2b } from '@noble/hashes/blake2b';
import { bcs } from '../bcs/index.js';
import { IntentScope, messageWithIntent } from './intent.js';
import type { PublicKey } from './publickey.js';
import type { SerializedSignature, SignatureScheme } from './signature.js';
import type { SignatureScheme } from './signature-scheme.js';
import type { SerializedSignature } from './signature.js';
import { toSerializedSignature } from './signature.js';

export const PRIVATE_KEY_SIZE = 32;
Expand Down
9 changes: 7 additions & 2 deletions sdk/typescript/src/cryptography/multisig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import { Secp256r1PublicKey } from '../keypairs/secp256r1/publickey.js';
import { MAX_SIGNER_IN_MULTISIG, MIN_SIGNER_IN_MULTISIG } from '../multisig/publickey.js';
import { normalizeSuiAddress } from '../utils/sui-types.js';
import type { PublicKey } from './publickey.js';
import type { SerializedSignature, SignatureScheme } from './signature.js';
import { SIGNATURE_SCHEME_TO_FLAG } from './signature.js';
import { SIGNATURE_SCHEME_TO_FLAG } from './signature-scheme.js';
import type { SignatureScheme } from './signature-scheme.js';
import type { SerializedSignature } from './signature.js';
import type { SignaturePubkeyPair } from './utils.js';
// eslint-disable-next-line import/no-cycle
import { toSingleSignaturePubkeyPair } from './utils.js';
Expand Down Expand Up @@ -165,6 +166,10 @@ export function decodeMultiSig(signature: string): SignaturePubkeyPair[] {
throw new Error('MultiSig is not supported inside MultiSig');
}

if (scheme === 'ZkLogin') {
throw new Error('ZkLogin is not supported inside MultiSig');
}

const SIGNATURE_SCHEME_TO_PUBLIC_KEY = {
ED25519: Ed25519PublicKey,
Secp256k1: Secp256k1PublicKey,
Expand Down
28 changes: 28 additions & 0 deletions sdk/typescript/src/cryptography/signature-scheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

export const SIGNATURE_SCHEME_TO_FLAG = {
ED25519: 0x00,
Secp256k1: 0x01,
Secp256r1: 0x02,
MultiSig: 0x03,
ZkLogin: 0x05,
} as const;

export const SIGNATURE_SCHEME_TO_SIZE = {
ED25519: 32,
Secp256k1: 33,
Secp256r1: 33,
};

export const SIGNATURE_FLAG_TO_SCHEME = {
0x00: 'ED25519',
0x01: 'Secp256k1',
0x02: 'Secp256r1',
0x03: 'MultiSig',
0x05: 'ZkLogin',
} as const;

export type SignatureScheme = 'ED25519' | 'Secp256k1' | 'Secp256r1' | 'MultiSig' | 'ZkLogin';

export type SignatureFlag = keyof typeof SIGNATURE_FLAG_TO_SCHEME;
55 changes: 27 additions & 28 deletions sdk/typescript/src/cryptography/signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import { fromB64, toB64 } from '@mysten/bcs';

import { bcs } from '../bcs/index.js';
import type { MultiSigStruct } from '../multisig/publickey.js';
import { computeZkLoginAddressFromSeed } from '../zklogin/address.js';
import { extractClaimValue } from '../zklogin/jwt-utils.js';
import { parseZkLoginSignature } from '../zklogin/signature.js';
import type { PublicKey } from './publickey.js';

export type SignatureScheme = 'ED25519' | 'Secp256k1' | 'Secp256r1' | 'MultiSig';
import type { SignatureScheme } from './signature-scheme.js';
import {
SIGNATURE_FLAG_TO_SCHEME,
SIGNATURE_SCHEME_TO_FLAG,
SIGNATURE_SCHEME_TO_SIZE,
} from './signature-scheme.js';

/**
* Pair of signature and corresponding public key
Expand All @@ -28,30 +35,6 @@ export type SerializeSignatureInput = {
*/
export type SerializedSignature = string;

export const SIGNATURE_SCHEME_TO_FLAG = {
ED25519: 0x00,
Secp256k1: 0x01,
Secp256r1: 0x02,
MultiSig: 0x03,
Zk: 0x05,
};

export const SIGNATURE_SCHEME_TO_SIZE = {
ED25519: 32,
Secp256k1: 33,
Secp256r1: 33,
};

export const SIGNATURE_FLAG_TO_SCHEME = {
0x00: 'ED25519',
0x01: 'Secp256k1',
0x02: 'Secp256r1',
0x03: 'MultiSig',
0x05: 'Zk',
} as const;

export type SignatureFlag = keyof typeof SIGNATURE_FLAG_TO_SCHEME;

/**
* Takes in a signature, its associated signing scheme and a public key, then serializes this data
*/
Expand Down Expand Up @@ -92,8 +75,24 @@ export function parseSerializedSignature(serializedSignature: SerializedSignatur
};
}

if (signatureScheme === 'Zk') {
throw new Error('Unable to parse a zk signature. (not implemented yet)');
if (signatureScheme === 'ZkLogin') {
const signatureBytes = bytes.slice(1);
const { inputs, maxEpoch, userSignature } = parseZkLoginSignature(signatureBytes);
const { issBase64Details, addressSeed } = inputs;
const iss = extractClaimValue<string>(issBase64Details, 'iss');
const address = computeZkLoginAddressFromSeed(BigInt(addressSeed), iss);
return {
serializedSignature,
signatureScheme,
zkLogin: {
inputs,
maxEpoch,
userSignature,
iss,
address,
},
bytes,
};
}

if (!(signatureScheme in SIGNATURE_SCHEME_TO_SIZE)) {
Expand Down
9 changes: 5 additions & 4 deletions sdk/typescript/src/cryptography/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import type { ExportedKeypair, Keypair } from './keypair.js';
import { LEGACY_PRIVATE_KEY_SIZE, PRIVATE_KEY_SIZE } from './keypair.js';
import { decodeMultiSig } from './multisig.js';
import type { PublicKey } from './publickey.js';
import type { SerializedSignature, SignatureScheme } from './signature.js';
import { SIGNATURE_FLAG_TO_SCHEME } from './signature.js';
import { SIGNATURE_FLAG_TO_SCHEME } from './signature-scheme.js';
import type { SignatureScheme } from './signature-scheme.js';
import type { SerializedSignature } from './signature.js';

/**
* Pair of signature and corresponding public key
Expand Down Expand Up @@ -47,8 +48,8 @@ export function toParsedSignaturePubkeyPair(
}
}

if (signatureScheme === 'Zk') {
throw new Error('Unable to parse a zk signature. (not implemented yet)');
if (signatureScheme === 'ZkLogin') {
throw new Error('ZkLogin signature not supported');
}

const SIGNATURE_SCHEME_TO_PUBLIC_KEY = {
Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/src/keypairs/ed25519/keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import nacl from 'tweetnacl';
import type { ExportedKeypair } from '../../cryptography/keypair.js';
import { Keypair, PRIVATE_KEY_SIZE } from '../../cryptography/keypair.js';
import { isValidHardenedPath, mnemonicToSeedHex } from '../../cryptography/mnemonics.js';
import type { SignatureScheme } from '../../cryptography/signature.js';
import type { SignatureScheme } from '../../cryptography/signature-scheme.js';
import { derivePath } from './ed25519-hd-key.js';
import { Ed25519PublicKey } from './publickey.js';

Expand Down
6 changes: 2 additions & 4 deletions sdk/typescript/src/keypairs/ed25519/publickey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import nacl from 'tweetnacl';

import type { PublicKeyInitData } from '../../cryptography/publickey.js';
import { bytesEqual, PublicKey } from '../../cryptography/publickey.js';
import { SIGNATURE_SCHEME_TO_FLAG } from '../../cryptography/signature-scheme.js';
import type { SerializedSignature } from '../../cryptography/signature.js';
import {
parseSerializedSignature,
SIGNATURE_SCHEME_TO_FLAG,
} from '../../cryptography/signature.js';
import { parseSerializedSignature } from '../../cryptography/signature.js';

const PUBLIC_KEY_SIZE = 32;

Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/src/keypairs/secp256k1/keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ExportedKeypair } from '../../cryptography/keypair.js';
import { Keypair } from '../../cryptography/keypair.js';
import { isValidBIP32Path, mnemonicToSeed } from '../../cryptography/mnemonics.js';
import type { PublicKey } from '../../cryptography/publickey.js';
import type { SignatureScheme } from '../../cryptography/signature.js';
import type { SignatureScheme } from '../../cryptography/signature-scheme.js';
import { Secp256k1PublicKey } from './publickey.js';

export const DEFAULT_SECP256K1_DERIVATION_PATH = "m/54'/784'/0'/0/0";
Expand Down
6 changes: 2 additions & 4 deletions sdk/typescript/src/keypairs/secp256k1/publickey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import { sha256 } from '@noble/hashes/sha256';

import { bytesEqual, PublicKey } from '../../cryptography/publickey.js';
import type { PublicKeyInitData } from '../../cryptography/publickey.js';
import { SIGNATURE_SCHEME_TO_FLAG } from '../../cryptography/signature-scheme.js';
import type { SerializedSignature } from '../../cryptography/signature.js';
import {
parseSerializedSignature,
SIGNATURE_SCHEME_TO_FLAG,
} from '../../cryptography/signature.js';
import { parseSerializedSignature } from '../../cryptography/signature.js';

const SECP256K1_PUBLIC_KEY_SIZE = 33;

Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/src/keypairs/secp256r1/keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ExportedKeypair } from '../../cryptography/keypair.js';
import { Keypair } from '../../cryptography/keypair.js';
import { isValidBIP32Path, mnemonicToSeed } from '../../cryptography/mnemonics.js';
import type { PublicKey } from '../../cryptography/publickey.js';
import type { SignatureScheme } from '../../cryptography/signature.js';
import type { SignatureScheme } from '../../cryptography/signature-scheme.js';
import { Secp256r1PublicKey } from './publickey.js';

export const DEFAULT_SECP256R1_DERIVATION_PATH = "m/74'/784'/0'/0/0";
Expand Down
Loading

0 comments on commit 3764c46

Please sign in to comment.