// The RLP format // Serialization and deserialization for the BytesTree type, under the following grammar: // | First byte | Meaning | // | ---------- | -------------------------------------------------------------------------- | // | 0 to 127 | HEX(leaf) | // | 128 to 183 | HEX(length_of_leaf + 128) + HEX(leaf) | // | 184 to 191 | HEX(length_of_length_of_leaf + 128 + 55) + HEX(length_of_leaf) + HEX(leaf) | // | 192 to 247 | HEX(length_of_node + 192) + HEX(node) | // | 248 to 255 | HEX(length_of_length_of_node + 128 + 55) + HEX(length_of_node) + HEX(node) | const encode = tree => { const padEven = str => str.length % 2 === 0 ? str : "0" + str; const uint = num => padEven(num.toString(16)); const length = (len, add) => len < 56 ? uint(add + len) : uint(add + uint(len).length / 2 + 55) + uint(len); const dataTree = tree => { if (typeof tree === "string") { const hex = tree.slice(2); const pre = hex.length != 2 || hex >= "80" ? length(hex.length / 2, 128) : ""; return pre + hex; } else { const hex = tree.map(dataTree).join(""); const pre = length(hex.length / 2, 192); return pre + hex; } }; return "0x" + dataTree(tree); }; const decode = hex => { let i = 2; const parseTree = () => { if (i >= hex.length) throw ""; const head = hex.slice(i, i + 2); return head < "80" ? (i += 2, "0x" + head) : head < "c0" ? parseHex() : parseList(); }; const parseLength = () => { const len = parseInt(hex.slice(i, i += 2), 16) % 64; return len < 56 ? len : parseInt(hex.slice(i, i += (len - 55) * 2), 16); }; const parseHex = () => { const len = parseLength(); return "0x" + hex.slice(i, i += len * 2); }; const parseList = () => { const lim = parseLength() * 2 + i; let list = []; while (i < lim) list.push(parseTree()); return list; }; try { return parseTree(); } catch (e) { return []; } }; module.exports = { encode, decode };