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 61943x 61943x 61943x 67466x 67466x 61943x 61943x 1x 1x 1x 1x 1x 1x 63973x 63973x 63973x 63973x 310234x 310234x 310234x 310234x 310234x 310234x 310234x 310234x 310234x 63972x 63972x 63972x 1x 1x 336181x 336181x 336181x 336181x 336181x 336181x 380677x 380677x 380677x 336181x 336181x 336181x 336181x 48890x 48890x 48890x 48890x 48890x 48890x 48890x 48890x 336181x 15084x 15084x 15084x 15084x 15084x 287290x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 13053x 272206x 241707x 241707x 241707x 241707x 241707x 241707x 17446x 17446x 17446x 1x 1x 1x 1x 1x 25947x 25947x 25947x 25947x 25947x    
//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;
}