PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@vechain/sdk-core/src/vcdm/encoding/rlp

Просмотр файла: RLP.ts

import { RLP as EthereumjsRLP } from '@ethereumjs/rlp';
import { bytesToNumberBE } from '@noble/ciphers/utils';
import { InvalidDataType, InvalidRLP } from '@vechain/sdk-errors';
import { Hex } from '../../Hex';
import { type VeChainDataModel } from '../../VeChainDataModel';
import { ScalarKind, type RLPProfile } from './kind/ScalarKind';
import { type RLPInput, type RLPValidObject, type RLPValueType } from './types';

class RLP implements VeChainDataModel<RLP> {
    public readonly encoded: Uint8Array;
    public readonly decoded: RLPInput;

    protected constructor(data: RLPInput);
    protected constructor(data: Uint8Array);
    protected constructor(data: RLPInput | Uint8Array) {
        // ArrayBuffer.isView so we support https://github.com/vitest-dev/vitest/issues/5183
        this.decoded = ArrayBuffer.isView(data)
            ? EthereumjsRLP.decode(data)
            : data;
        this.encoded = ArrayBuffer.isView(data)
            ? data
            : EthereumjsRLP.encode(data);
    }

    /**
     * Returns the bigint representation of the encoded data in the RLP instance.
     * @returns {bigint} The bigint representation of the encoded data.
     */
    get bi(): bigint {
        return bytesToNumberBE(this.bytes);
    }

    /**
     * Returns the encoded data as a Uint8Array.
     * @returns {Uint8Array} The encoded data.
     */
    get bytes(): Uint8Array {
        return this.encoded;
    }

    /**
     * Returns the number representation of the encoded data in the RLP instance.
     * @returns {number} The number representation of the encoded data.
     */
    get n(): number {
        const bi = this.bi;
        if (bi <= Number.MAX_SAFE_INTEGER) {
            return Number(bi);
        }
        throw new InvalidDataType('RLP.n', 'not in the safe number range', {
            bytes: this.bytes
        });
    }

    /**
     * Compares the current RLP instance with another RLP instance.
     * @param {RLP} that The RLP instance to compare.
     * @returns 0 if the RLP instances are equal, -1/1 if they are not.
     */
    public compareTo(that: RLP): number {
        if (this.encoded.length !== that.encoded.length) {
            return -1;
        }

        for (let i = 0; i < this.encoded.length; i++) {
            if (this.encoded[i] !== that.encoded[i]) {
                return 1;
            }
        }

        return 0;
    }

    /**
     * Relies on compareTo to check if the RLP instances are equal.
     * @param {RLP} that The RLP instance to compare.
     * @returns true if the RLP instances are equal, false otherwise.
     */
    public isEqual(that: RLP): boolean {
        return this.compareTo(that) === 0;
    }

    /**
     * Creates {@link Hex} instance from the RLP encoded value.
     * @returns {Hex} The Hex instance.
     */
    public toHex(): Hex {
        return Hex.of(this.bytes);
    }

    /**
     * Returns an RLP instance from a plain value.
     * @param data - The plain data
     * @returns {RLP} The RLP instance.
     */
    public static of(data: RLPInput): RLP {
        try {
            return new RLP(data);
        } catch (error) {
            throw new InvalidRLP(
                'RLP.of()',
                `Error when creating an RLP instance for data ${data}`,
                {
                    context:
                        'This method creates an RLP instance from a plain value.',
                    data: {
                        data
                    }
                },
                error
            );
        }
    }

    /**
     * Returns an RLP instancen from an encoded value.
     * @param {Uint8Array} encodedData - The RLP-encoded data.
     * @returns The decoded data or null if decoding fails.
     */
    public static ofEncoded(encodedData: Uint8Array): RLP {
        try {
            return new RLP(encodedData);
        } catch (error) {
            throw new InvalidRLP(
                'RLP.ofEncoded()',
                `Error when creating an RLP instance for encoded data.`,
                {
                    context:
                        'This method creates an RLP instance from an encoded value.',
                    data: {
                        encodedData
                    }
                },
                error
            );
        }
    }

    /**
     * Handles the RLP packing of data.
     * Recursively processes through object properties or array elements to prepare data for RLP encoding.
     *
     * @param obj - The object data to be packed.
     * @param profile - Profile for encoding structures.
     * @param context - Encoding context for error tracing.
     * @returns Packed data as RLPInput.
     * @throws {InvalidRLP}
     *
     */
    protected static packData(
        obj: RLPValidObject,
        profile: RLPProfile,
        context: string
    ): RLPInput {
        context = context !== '' ? context + '.' + profile.name : profile.name;
        const kind = profile.kind;

        // ScalarKind: direct encoding using the provided method.
        if (kind instanceof ScalarKind) {
            return kind.data(obj, context).encode();
        }

        // StructKind: recursively pack each struct member based on its profile.
        if (Array.isArray(kind)) {
            return kind.map((k) =>
                this.packData(obj[k.name] as RLPValidObject, k, context)
            );
        }

        // Valid RLP array
        if (!Array.isArray(obj)) {
            throw new InvalidRLP(
                'RLP.packData()',
                `Validation error: Expected an array in ${context}.`,
                {
                    context,
                    data: {
                        obj,
                        profile
                    }
                }
            );
        }

        // ArrayKind: recursively pack each array item based on the shared item profile.
        if ('item' in kind && Array.isArray(obj)) {
            const item = kind.item;
            return obj.map((part, i) =>
                this.packData(
                    part as RLPValidObject,
                    { name: '#' + i, kind: item },
                    context
                )
            );
        }
    }

    /**
     * Handles the RLP unpacking of data.
     * Recursively processes through packed properties or elements to prepare data post RLP decoding.
     *
     * @param packed - The packed data to be unpacked.
     * @param profile - Profile for decoding structures.
     * @param context - Decoding context for error tracing.
     * @returns Unpacked data as RLPValueType.
     * @throws {InvalidRLP}
     *
     */
    protected static unpackData(
        packed: RLPInput,
        profile: RLPProfile,
        context: string
    ): RLPValueType {
        context = context !== '' ? context + '.' + profile.name : profile.name;

        const kind = profile.kind;

        // ScalarKind: Direct decoding using the provided method.
        if (kind instanceof ScalarKind) {
            // ArrayBuffer.isView so we support https://github.com/vitest-dev/vitest/issues/5183
            if (!ArrayBuffer.isView(packed)) {
                throw new InvalidRLP(
                    'RLP.unpackData()',
                    `Unpacking error: Expected data type is Uint8Array.`,
                    {
                        context,
                        data: {
                            packed,
                            profile
                        }
                    }
                );
            }

            return kind.buffer(packed, context).decode();
        }

        // StructKind: Recursively unpack each struct member based on its profile.
        if (Array.isArray(kind) && Array.isArray(packed)) {
            const parts = packed;

            if (kind.length !== parts.length) {
                throw new InvalidRLP(
                    'RLP.unpackData()',
                    `Unpacking error: Expected ${kind.length} items, but got ${parts.length}.`,
                    {
                        context,
                        data: {
                            packed,
                            profile
                        }
                    }
                );
            }

            return kind.reduce(
                (obj: RLPValidObject, profile: RLPProfile, index: number) => {
                    obj[profile.name] = this.unpackData(
                        parts[index],
                        profile,
                        context
                    );

                    return obj;
                },
                {}
            );
        }

        // Valid RLP array
        if (!Array.isArray(packed)) {
            throw new InvalidRLP(
                'RLP.unpackData()',
                `Validation error: Expected an array in ${context}.`,
                {
                    context,
                    data: {
                        packed,
                        profile
                    }
                }
            );
        }

        // ArrayKind: Recursively unpack each array item based on the shared item profile.
        if ('item' in kind && Array.isArray(packed)) {
            const item = kind.item;

            return packed.map((part, index) =>
                this.unpackData(
                    part,
                    { name: '#' + index, kind: item },
                    context
                )
            ) as RLPValueType;
        }
    }
}

export { RLP };

Выполнить команду


Для локальной разработки. Не используйте в интернете!