Skip to content

Commit

Permalink
wip: checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Jan 3, 2025
1 parent 36d0447 commit 72397fc
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/core/internal/__snapshots__/provider.test.ts.snap

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions src/core/internal/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { describe, expect, test } from 'vitest'
import { getAccount } from '../../../test/src/account.js'
import { client, delegation } from '../../../test/src/porto.js'
import * as Account from './account.js'
import * as Call from './call.js'
import * as Delegation from './delegation.js'
import * as Key from './key.js'

Expand Down Expand Up @@ -76,4 +77,65 @@ describe('sign', () => {

expect(valid).toBe(true)
})

test('key: P256 (admin)', async () => {
const key = Key.fromP256({
privateKey:
'0x0c57184baffb76254bcb3e225bb789082b9cc25f37d9805e2fbfcd5c681e72ee',
role: 'admin',
})

const { account } = await getAccount(client, {
delegation,
keys: [key],
})

// delegate
await Delegation.execute(client, {
account,
calls: [
Call.authorize({
key,
to: account.address,
}),
],
delegate: true,
})

const payload = Hex.random(32)

{
// sign
const [signature] = await Account.sign(account, {
key,
payloads: [payload],
})

// verify
const valid = await verifyHash(client, {
address: account.address,
hash: payload,
signature,
})

expect(valid).toBe(true)
}

{
// sign
const [signature] = await Account.sign(account, {
key: 1,
payloads: [payload],
})

// verify
const valid = await verifyHash(client, {
address: account.address,
hash: payload,
signature,
})

expect(valid).toBe(true)
}
})
})
2 changes: 1 addition & 1 deletion src/core/internal/call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('authorize', () => {

expect(call).toMatchInlineSnapshot(`
{
"data": "0xcebfe33600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004104ec0effa5f2f378cbf7fd2fa7ca1e8dc51cf777c129fa1c00a0e9a9205f2e511ff3f20b34a4e0b50587d055c0e0fad33d32cf1147d3bb2538fbab0d15d8e6500800000000000000000000000000000000000000000000000000000000000000",
"data": "0xcebfe336000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000040ec0effa5f2f378cbf7fd2fa7ca1e8dc51cf777c129fa1c00a0e9a9205f2e511ff3f20b34a4e0b50587d055c0e0fad33d32cf1147d3bb2538fbab0d15d8e65008",
"to": "0x0000000000000000000000000000000000000000",
}
`)
Expand Down
2 changes: 1 addition & 1 deletion src/core/internal/key.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ describe('serialize', () => {
"expiry": 0,
"isSuperAdmin": true,
"keyType": 0,
"publicKey": "0x04ec0effa5f2f378cbf7fd2fa7ca1e8dc51cf777c129fa1c00a0e9a9205f2e511ff3f20b34a4e0b50587d055c0e0fad33d32cf1147d3bb2538fbab0d15d8e65008",
"publicKey": "0xec0effa5f2f378cbf7fd2fa7ca1e8dc51cf777c129fa1c00a0e9a9205f2e511ff3f20b34a4e0b50587d055c0e0fad33d32cf1147d3bb2538fbab0d15d8e65008",
}
`)
})
Expand Down
138 changes: 96 additions & 42 deletions src/core/internal/key.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as AbiParameters from 'ox/AbiParameters'
import type * as Bytes from 'ox/Bytes'
import type * as Hex from 'ox/Hex'
import * as Hash from 'ox/Hash'
import * as Hex from 'ox/Hex'
import * as P256 from 'ox/P256'
import * as PublicKey from 'ox/PublicKey'
import * as Secp256k1 from 'ox/Secp256k1'
Expand All @@ -25,14 +27,14 @@ export type Serialized = {
}

/** Key type to serialized key type mapping. */
const toSerializedKeyType = {
export const toSerializedKeyType = {
p256: 0,
'webauthn-p256': 1,
secp256k1: 2,
} as const

/** Serialized key type to key type mapping. */
const fromSerializedKeyType = {
export const fromSerializedKeyType = {
0: 'p256',
1: 'webauthn-p256',
2: 'secp256k1',
Expand Down Expand Up @@ -332,16 +334,19 @@ export function fromP256<const role extends 'admin' | 'session'>(
) {
const { privateKey } = parameters
const publicKey = P256.getPublicKey({ privateKey })
const type = 'p256' as const
return from({
expiry: parameters.expiry ?? 0,
publicKey,
role: parameters.role as 'admin' | 'session',
async sign() {
// TODO
// return P256.sign({ payload, privateKey })
return '0x' as const
async sign({ payload }) {
const signature = Signature.toHex(P256.sign({ payload, privateKey }))
return wrapSignature(signature, {
keyType: type,
publicKey,
})
},
type: 'p256',
type,
})
}

Expand Down Expand Up @@ -386,18 +391,20 @@ export function fromSecp256k1<const role extends 'owner' | 'admin' | 'session'>(
) {
const { privateKey, role } = parameters
const publicKey = Secp256k1.getPublicKey({ privateKey })
const type = 'secp256k1' as const
return from({
expiry: parameters.expiry ?? 0,
publicKey,
role,
async sign({ payload }) {
if (role === 'owner')
return Signature.toHex(Secp256k1.sign({ payload, privateKey }))
// const signature = Secp256k1.sign({ payload, privateKey })
// TODO
return '0x' as const
const signature = Signature.toHex(Secp256k1.sign({ payload, privateKey }))
if (role === 'owner') return signature
return wrapSignature(signature, {
keyType: type,
publicKey,
})
},
type: 'secp256k1',
type,
})
}

Expand Down Expand Up @@ -444,26 +451,43 @@ export declare namespace fromSecp256k1 {
export function fromWebAuthnP256<const role extends 'admin' | 'session'>(
parameters: fromWebAuthnP256.Parameters<role>,
) {
const { credential } = parameters

const { credential, rpId } = parameters
const type = 'webauthn-p256' as const
return from({
expiry: parameters.expiry ?? 0,
publicKey: credential.publicKey,
role: parameters.role as 'admin' | 'session',
async sign() {
// const { signature, metadata } = await WebAuthnP256.sign({
// challenge: payload,
// credentialId: credential.id,
// rpId,
// })
// return {
// ...signature,
// metadata,
// }
// TODO
return '0x' as const
async sign({ payload }) {
const {
signature: { r, s },
metadata,
} = await WebAuthnP256.sign({
challenge: payload,
credentialId: credential.id,
rpId,
})
const signature = AbiParameters.encode(
AbiParameters.from([
'struct WebAuthnAuth { bytes authenticatorData; string clientDataJSON; uint256 challengeIndex; uint256 typeIndex; bytes32 r; bytes32 s; }',
'WebAuthnAuth auth',
]),
[
{
authenticatorData: metadata.authenticatorData,
challengeIndex: BigInt(metadata.challengeIndex),
clientDataJSON: metadata.clientDataJSON,
r: Hex.fromNumber(r, { size: 32 }),
s: Hex.fromNumber(s, { size: 32 }),
typeIndex: BigInt(metadata.typeIndex),
},
],
)
return wrapSignature(signature, {
keyType: type,
publicKey: credential.publicKey,
})
},
type: 'webauthn-p256',
type,
})
}

Expand Down Expand Up @@ -511,23 +535,23 @@ export function fromWebCryptoP256<const role extends 'admin' | 'session'>(
parameters: fromWebCryptoP256.Parameters<role>,
) {
const { keyPair } = parameters
const { publicKey } = keyPair
const { publicKey, privateKey } = keyPair
const type = 'p256' as const
return from({
expiry: parameters.expiry ?? 0,
publicKey,
role: parameters.role as 'admin' | 'session',
async sign() {
// const signature = await WebCryptoP256.sign({ payload, privateKey })
// return {
// ...signature,
// metadata: {
// prehash: true,
// },
// }
// TODO
return '0x' as const
async sign({ payload }) {
const signature = Signature.toHex(
await WebCryptoP256.sign({ payload, privateKey }),
)
return wrapSignature(signature, {
keyType: type,
prehash: true,
publicKey,
})
},
type: 'p256',
type,
})
}

Expand Down Expand Up @@ -565,6 +589,36 @@ export function serialize(key: Key): Serialized {
expiry,
isSuperAdmin: role === 'owner' || role === 'admin',
keyType: toSerializedKeyType[type],
publicKey: PublicKey.toHex(publicKey),
publicKey: PublicKey.toHex(publicKey, { includePrefix: false }),
}
}

///////////////////////////////////////////////////////////////////////////
// Internal
///////////////////////////////////////////////////////////////////////////

function wrapSignature(signature: Hex.Hex, options: wrapSignature.Options) {
const { keyType, prehash = false, publicKey } = options

const keyHash = Hash.keccak256(
AbiParameters.encode(
[{ type: 'uint8' }, { type: 'bytes32' }],
[
toSerializedKeyType[keyType],
Hash.keccak256(PublicKey.toHex(publicKey, { includePrefix: false })),
],
),
)
return AbiParameters.encodePacked(
['bytes', 'bytes32', 'bool'],
[signature, keyHash, prehash],
)
}

declare namespace wrapSignature {
type Options = {
keyType: Key['type']
prehash?: boolean | undefined
publicKey: PublicKey.PublicKey
}
}
5 changes: 3 additions & 2 deletions test/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export async function getAccount(
client: Client,
parameters: {
delegation: Address.Address
keys?: readonly Key.Key[] | undefined
},
) {
const { delegation } = parameters
const { delegation, keys } = parameters

const privateKey = Secp256k1.randomPrivateKey()
const address = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey }))
Expand All @@ -28,7 +29,7 @@ export async function getAccount(
const account = Account.from({
address,
delegation,
keys: [key],
keys: [key, ...(keys ?? [])],
})

return {
Expand Down
3 changes: 2 additions & 1 deletion test/src/anvil.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions test/src/anvil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function defineAnvil(parameters: AnvilParameters) {

const config = {
...parameters,
odyssey: true,
hardfork: 'Prague',
} as const

Expand Down

0 comments on commit 72397fc

Please sign in to comment.