From c967aa623276c13631ab23794ce21b9ead5710ee Mon Sep 17 00:00:00 2001 From: Yonghui Lin <homura.dev@gmail.com> Date: Mon, 18 Jul 2022 14:30:56 +0800 Subject: [PATCH] feat(codec): molecule unpack works with BytesLike (#365) * feat(codec): molecule unpack works with BytesLike * refactor(codec): rm unnecessary XLike codec --- packages/codec/src/base.ts | 17 ++++++----------- packages/codec/src/blockchain.ts | 24 ++++++++++++++++++------ packages/codec/src/molecule/helper.ts | 15 ++++++++++----- packages/codec/src/molecule/layout.ts | 25 ++++++++++++------------- packages/codec/src/number/uint.ts | 4 ++-- packages/codec/tests/blockchain.test.ts | 13 ++++++++++++- 6 files changed, 60 insertions(+), 38 deletions(-) diff --git a/packages/codec/src/base.ts b/packages/codec/src/base.ts index ac9a17231..f7a4073e9 100644 --- a/packages/codec/src/base.ts +++ b/packages/codec/src/base.ts @@ -49,7 +49,7 @@ export type UnpackParam<T extends AnyCodec> = T extends Codec< ? Unpackable : never; -export type BytesCodec<Unpacked = any, Packable = Unpacked> = Codec< +export type Uint8ArrayCodec<Unpacked = any, Packable = Unpacked> = Codec< Uint8Array, Unpacked, Packable @@ -57,7 +57,7 @@ export type BytesCodec<Unpacked = any, Packable = Unpacked> = Codec< export type BytesLike = ArrayLike<number> | ArrayBuffer | string; -export type BytesLikeCodec<Unpacked = any, Packable = Unpacked> = Codec< +export type BytesCodec<Unpacked = any, Packable = Unpacked> = Codec< Uint8Array, Unpacked, Packable, @@ -69,8 +69,8 @@ export type BytesLikeCodec<Unpacked = any, Packable = Unpacked> = Codec< * @param codec */ export function createBytesCodec<Unpacked, Packable = Unpacked>( - codec: BytesCodec<Unpacked, Packable> -): BytesLikeCodec<Unpacked, Packable> { + codec: Uint8ArrayCodec<Unpacked, Packable> +): BytesCodec<Unpacked, Packable> { return { pack: (unpacked) => codec.pack(unpacked), unpack: (bytesLike) => codec.unpack(bytify(bytesLike)), @@ -88,11 +88,6 @@ export type FixedBytesCodec<Unpacked = any, Packable = Unpacked> = BytesCodec< > & Fixed; -export type FixedBytesLikeCodec< - Unpacked = any, - Packable = Unpacked -> = BytesLikeCodec<Unpacked, Packable> & Fixed; - export function isFixedCodec<T>( codec: BytesCodec<T> ): codec is FixedBytesCodec<T> { @@ -100,8 +95,8 @@ export function isFixedCodec<T>( } export function createFixedBytesCodec<Unpacked, Packable = Unpacked>( - codec: BytesCodec<Unpacked, Packable> & { byteLength: number } -): FixedBytesLikeCodec<Unpacked, Packable> { + codec: Uint8ArrayCodec<Unpacked, Packable> & { byteLength: number } +): FixedBytesCodec<Unpacked, Packable> { const byteLength = codec.byteLength; return { __isFixedCodec__: true, diff --git a/packages/codec/src/blockchain.ts b/packages/codec/src/blockchain.ts index 0f200de74..6ba7865c0 100644 --- a/packages/codec/src/blockchain.ts +++ b/packages/codec/src/blockchain.ts @@ -1,9 +1,11 @@ import { AnyCodec, BytesCodec, + BytesLike, createBytesCodec, createFixedBytesCodec, FixedBytesCodec, + PackParam, UnpackResult, } from "./base"; import { bytify, hexify } from "./bytes"; @@ -47,11 +49,18 @@ export function WitnessArgsOf< lock: LockCodec; input_type: InputTypeCodec; output_type: OutputTypeCodec; -}): BytesCodec<{ - lock?: UnpackResult<LockCodec>; - input_type?: UnpackResult<InputTypeCodec>; - output_type?: UnpackResult<OutputTypeCodec>; -}> { +}): BytesCodec< + { + lock?: UnpackResult<LockCodec>; + input_type?: UnpackResult<InputTypeCodec>; + output_type?: UnpackResult<OutputTypeCodec>; + }, + { + lock?: PackParam<LockCodec>; + input_type?: PackParam<InputTypeCodec>; + output_type?: PackParam<OutputTypeCodec>; + } +> { return table( { lock: option(byteVecOf(payload.lock)), @@ -62,7 +71,10 @@ export function WitnessArgsOf< ); } -const HexifyCodec = createBytesCodec<string>({ pack: bytify, unpack: hexify }); +const HexifyCodec = createBytesCodec<string, BytesLike>({ + pack: bytify, + unpack: hexify, +}); /** * diff --git a/packages/codec/src/molecule/helper.ts b/packages/codec/src/molecule/helper.ts index c3f8299ce..ea6d8bd51 100644 --- a/packages/codec/src/molecule/helper.ts +++ b/packages/codec/src/molecule/helper.ts @@ -1,6 +1,11 @@ import { assertBufferLength, assertMinBufferLength } from "../utils"; import { concat } from "../bytes"; -import { BytesCodec, createFixedBytesCodec, FixedBytesCodec } from "../base"; +import { + BytesCodec, + createBytesCodec, + createFixedBytesCodec, + FixedBytesCodec, +} from "../base"; import { Uint32LE } from "../number"; /** @@ -30,8 +35,8 @@ export function byteOf<T>(codec: BytesCodec<T>): FixedBytesCodec<T> { * a helper function to create custom codec of `vector Bytes <byte>` * @param codec */ -export function byteVecOf<T>(codec: BytesCodec<T>): BytesCodec<T> { - return { +export const byteVecOf = <T>(codec: BytesCodec<T>): BytesCodec<T> => { + return createBytesCodec({ pack(unpacked) { const payload = codec.pack(unpacked); const header = Uint32LE.pack(payload.byteLength); @@ -44,5 +49,5 @@ export function byteVecOf<T>(codec: BytesCodec<T>): BytesCodec<T> { assertBufferLength(packed.slice(4), header); return codec.unpack(packed.slice(4)); }, - }; -} + }); +}; diff --git a/packages/codec/src/molecule/layout.ts b/packages/codec/src/molecule/layout.ts index ae0215cbf..e12b95345 100644 --- a/packages/codec/src/molecule/layout.ts +++ b/packages/codec/src/molecule/layout.ts @@ -11,7 +11,7 @@ */ import type { BytesCodec, Fixed, FixedBytesCodec, UnpackResult } from "../base"; -import { createFixedBytesCodec, isFixedCodec } from "../base"; +import { createBytesCodec, createFixedBytesCodec, isFixedCodec } from "../base"; import { Uint32LE } from "../number"; import { concat } from "../bytes"; @@ -48,8 +48,7 @@ export function array<T extends FixedBytesCodec>( itemCodec: T, itemCount: number ): ArrayCodec<T> & Fixed { - return Object.freeze({ - __isFixedCodec__: true, + return createFixedBytesCodec({ byteLength: itemCodec.byteLength * itemCount, pack(items) { const itemsBuf = items.map((item) => itemCodec.pack(item)); @@ -123,7 +122,7 @@ export function struct<T extends Record<string, FixedBytesCodec>>( } export function fixvec<T extends FixedBytesCodec>(itemCodec: T): ArrayCodec<T> { - return { + return createBytesCodec({ pack(items) { return concat( Uint32LE.pack(items.length), @@ -142,11 +141,11 @@ export function fixvec<T extends FixedBytesCodec>(itemCodec: T): ArrayCodec<T> { const itemCount = Uint32LE.unpack(buf.slice(0, 4)); return array(itemCodec, itemCount).unpack(buf.slice(4)); }, - }; + }); } export function dynvec<T extends BytesCodec>(itemCodec: T): ArrayCodec<T> { - return { + return createBytesCodec({ pack(obj) { const packed = obj.reduce( (result, item) => { @@ -198,7 +197,7 @@ export function dynvec<T extends BytesCodec>(itemCodec: T): ArrayCodec<T> { return result; } }, - }; + }); } export function vector<T extends BytesCodec>(itemCodec: T): ArrayCodec<T> { @@ -213,7 +212,7 @@ export function table<T extends Record<string, BytesCodec>>( fields: (keyof T)[] ): ObjectCodec<T> { checkShape(shape, fields); - return { + return createBytesCodec({ pack(obj) { const headerLength = 4 + fields.length * 4; const packed = fields.reduce( @@ -271,14 +270,14 @@ export function table<T extends Record<string, BytesCodec>>( >; } }, - }; + }); } export function union<T extends Record<string, BytesCodec>>( itemCodec: T, fields: (keyof T)[] ): UnionCodec<T> { - return { + return createBytesCodec({ pack(obj) { const type = obj.type; const fieldIndex = fields.indexOf(type); @@ -294,11 +293,11 @@ export function union<T extends Record<string, BytesCodec>>( const type = fields[typeIndex]; return { type, value: itemCodec[type].unpack(buf.slice(4)) }; }, - }; + }); } export function option<T extends BytesCodec>(itemCodec: T): OptionCodec<T> { - return { + return createBytesCodec({ pack(obj?) { if (obj !== undefined && obj !== null) { return itemCodec.pack(obj); @@ -312,5 +311,5 @@ export function option<T extends BytesCodec>(itemCodec: T): OptionCodec<T> { } return itemCodec.unpack(buf); }, - }; + }); } diff --git a/packages/codec/src/number/uint.ts b/packages/codec/src/number/uint.ts index 47d788492..fd1dbd830 100644 --- a/packages/codec/src/number/uint.ts +++ b/packages/codec/src/number/uint.ts @@ -1,5 +1,5 @@ import { BI, BIish } from "@ckb-lumos/bi"; -import { createFixedBytesCodec, FixedBytesLikeCodec } from "../base"; +import { createFixedBytesCodec, FixedBytesCodec } from "../base"; function assertNumberRange(value: BIish, min: BIish, max: BIish): void { value = BI.from(value); @@ -14,7 +14,7 @@ function assertNumberRange(value: BIish, min: BIish, max: BIish): void { function createUintNumberCodec( byteLength: number, littleEndian = false -): FixedBytesLikeCodec<number, BIish> { +): FixedBytesCodec<number, BIish> { const codec = createUintBICodec(byteLength, littleEndian); return { __isFixedCodec__: true, diff --git a/packages/codec/tests/blockchain.test.ts b/packages/codec/tests/blockchain.test.ts index 665a6fd4b..641ebfe97 100644 --- a/packages/codec/tests/blockchain.test.ts +++ b/packages/codec/tests/blockchain.test.ts @@ -8,7 +8,7 @@ const SECP256K1_SIGNATURE_LENGTH = 65; test("secp256k1 witness args", (t) => { const unsigned = WitnessArgs.pack({ - lock: hexify(Buffer.alloc(SECP256K1_SIGNATURE_LENGTH)), + lock: Buffer.alloc(SECP256K1_SIGNATURE_LENGTH), }); t.deepEqual( @@ -18,6 +18,17 @@ test("secp256k1 witness args", (t) => { ) ); + t.deepEqual( + WitnessArgs.unpack( + "0x55000000100000005500000055000000410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ), + { + lock: hexify(Buffer.alloc(SECP256K1_SIGNATURE_LENGTH)), + input_type: undefined, + output_type: undefined, + } + ); + const signature = bytify(randomBytes(SECP256K1_SIGNATURE_LENGTH)); const signed = WitnessArgs.pack({ lock: hexify(signature) });