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 17446x 17446x 17446x 17446x 17446x 1x 61941x 61941x 61941x 67464x 67464x 61941x 61941x 1x 1x 1x 1x 1x 1x 63969x 63969x 63969x 63969x 310210x 310210x 310210x 310210x 310210x 310210x 310210x 310210x 310210x 63968x 63968x 63968x 1x 1x 336155x 336155x 336155x 336155x 336155x 336155x 380649x 380649x 380649x 336155x 336155x 336155x 336155x 48888x 48888x 48888x 48888x 48888x 48888x 48888x 48888x 336155x 15082x 15082x 15082x 15082x 15082x 287266x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 272184x 241685x 241685x 241685x 241685x 241685x 241685x 17446x 17446x 17446x 1x 1x 1x 1x 1x 25945x 25945x 25945x 25945x 25945x    
//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;
}