All files / ethers.js/src.ts/utils rlp-decode.ts

100% Statements 103/103
100% Branches 18/18
100% Functions 6/6
100% Lines 103/103

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1051x 1x 1x 1x 1x 1x 1x 1x 1x 17444x 17444x 17444x 17444x 17444x 1x 61939x 61939x 61939x 67462x 67462x 61939x 61939x 1x 1x 1x 1x 1x 1x 63965x 63965x 63965x 63965x 310186x 310186x 310186x 310186x 310186x 310186x 310186x 310186x 310186x 63964x 63964x 63964x 1x 1x 336129x 336129x 336129x 336129x 336129x 336129x 380623x 380623x 380623x 336129x 336129x 336129x 336129x 48886x 48886x 48886x 48886x 48886x 48886x 48886x 48886x 336129x 15080x 15080x 15080x 15080x 15080x 287242x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 272162x 241665x 241665x 241665x 241665x 241665x 241665x 17444x 17444x 17444x 1x 1x 1x 1x 1x 25943x 25943x 25943x 25943x 25943x    
//See: https://github.com/ethereum/wiki/wiki/RLP
 
import { hexlify } from "./data.js";
import { assert, assertArgument } from "./errors.js";
import { getBytes } from "./data.js";
 
import type { BytesLike, RlpStructuredData } from "./index.js";
 
 
function hexlifyByte(value: number): string {
    let result = value.toString(16);
    while (result.length < 2) { result = "0" + result; }
    return "0x" + result;
}
 
function unarrayifyInteger(data: Uint8Array, offset: number, length: number): number {
    let result = 0;
    for (let i = 0; i < length; i++) {
        result = (result * 256) + data[offset + i];
    }
    return result;
}
 
type Decoded = {
    result: any;
    consumed: number;
};
 
function _decodeChildren(data: Uint8Array, offset: number, childOffset: number, length: number): Decoded {
    const result: Array<any> = [];
 
    while (childOffset < offset + 1 + length) {
        const decoded = _decode(data, childOffset);
 
        result.push(decoded.result);
 
        childOffset += decoded.consumed;
        assert(childOffset <= offset + 1 + length, "child data too short", "BUFFER_OVERRUN", {
            buffer: data, length, offset
        });
    }
 
    return {consumed: (1 + length), result: result};
}
 
// returns { consumed: number, result: Object }
function _decode(data: Uint8Array, offset: number): { consumed: number, result: any } {
    assert(data.length !== 0, "data too short", "BUFFER_OVERRUN", {
        buffer: data, length: 0, offset: 1
    });
 
    const checkOffset = (offset: number) => {
        assert(offset <= data.length, "data short segment too short", "BUFFER_OVERRUN", {
            buffer: data, length: data.length, offset
        });
    };
 
    // Array with extra length prefix
    if (data[offset] >= 0xf8) {
        const lengthLength = data[offset] - 0xf7;
        checkOffset(offset + 1 + lengthLength);
 
        const length = unarrayifyInteger(data, offset + 1, lengthLength);
        checkOffset(offset + 1 + lengthLength + length);
 
        return _decodeChildren(data, offset, offset + 1 + lengthLength, lengthLength + length);
 
    } else if (data[offset] >= 0xc0) {
        const length = data[offset] - 0xc0;
        checkOffset(offset + 1 + length);
 
        return _decodeChildren(data, offset, offset + 1, length);
 
    } else if (data[offset] >= 0xb8) {
        const lengthLength = data[offset] - 0xb7;
        checkOffset(offset + 1 + lengthLength);
 
        const length = unarrayifyInteger(data, offset + 1, lengthLength);
        checkOffset(offset + 1 + lengthLength + length);
 
        const result = hexlify(data.slice(offset + 1 + lengthLength, offset + 1 + lengthLength + length));
        return { consumed: (1 + lengthLength + length), result: result }
 
    } else if (data[offset] >= 0x80) {
        const length = data[offset] - 0x80;
        checkOffset(offset + 1 + length);
 
        const result = hexlify(data.slice(offset + 1, offset + 1 + length));
        return { consumed: (1 + length), result: result }
    }
 
    return { consumed: 1, result: hexlifyByte(data[offset]) };
}
 
/**
 *  Decodes %%data%% into the structured data it represents.
 */
export function decodeRlp(_data: BytesLike): RlpStructuredData {
    const data = getBytes(_data, "data");
    const decoded = _decode(data, 0);
    assertArgument(decoded.consumed === data.length, "unexpected junk after rlp payload", "data", _data);
    return decoded.result;
}