rlp-encoding
Version:
Abstract-encoding interface for RLP
115 lines (93 loc) • 3.95 kB
JavaScript
const Length = require('./length')
const { bufferCopy, toBuffer } = require('./util')
function encode (value, buffer, offset = 0) {
let buf
if (Array.isArray(value)) {
buf = Buffer.concat(value.map((item) => encode(item)))
buf = Buffer.concat([ Length.encode(buf.length, 0xc0), buf ])
} else {
buf = toBuffer(value)
if (!(buf.length === 1 && buf[0] < 0x80)) {
buf = Buffer.concat([ Length.encode(buf.length, 0x80), buf ])
}
}
if (buffer !== undefined) {
if (offset + buf.length > buffer.length) throw new Error('Not enough buffer size')
buf.copy(buffer, offset)
buf = buffer
}
encode.bytes = buf.length
return buf
}
function decode (buffer, start = 0, end = buffer.length) {
if (start >= end) throw new Error('Not enough data for decode')
const firstByte = buffer[start]
// A single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding.
if (firstByte <= 0x7f) {
decode.bytes = 1
return Buffer.from([ firstByte ])
}
// String is 0-55 bytes long.
// A single byte with value 0x80 plus the length of the string followed by the string.
// The range of the first byte is [0x80, 0xb7]
if (firstByte <= 0xb7) {
const length = firstByte - 0x80
if (length === 1 && buffer[start + 1] < 0x80) throw new Error('First byte must be less than 0x80')
const value = bufferCopy(buffer, start + 1, length)
if (value.length !== length) throw new Error('Not enough data for decode')
decode.bytes = 1 + length
return value
}
// String with length more than 55 bytes long.
// A single byte with value 0xb7 plus the length in bytes of the length of the string in binary form,
// followed by the length of the string, followed by the string.
// The range of the first byte is thus [0xb8, 0xbf]
if (firstByte <= 0xbf) {
const lengthLength = firstByte - 0xb7
const length = Length.decode(buffer, start + 1, lengthLength)
if (length <= 55) throw new Error('Invalid length')
const value = bufferCopy(buffer, start + 1 + lengthLength, length)
if (value.length !== length) throw new Error('Not enough data for decode')
decode.bytes = 1 + lengthLength + length
return value
}
// If the total payload of a list is 0-55 bytes long,
// the RLP encoding consists of a single byte with value 0xc0 plus the length of the list
// followed by the concatenation of the RLP encodings of the items.
// The range of the first byte is thus [0xc0, 0xf7]
if (firstByte <= 0xf7) {
const length = firstByte - 0xc0
const value = decodeList(buffer, start + 1, length)
decode.bytes = 1 + length
return value
}
// If the total payload of a list is more than 55 bytes long,
// the RLP encoding consists of a single byte with value 0xf7 plus the length in bytes of the length
// of the payload in binary form, followed by the length of the payload,
// followed by the concatenation of the RLP encodings of the items.
// The range of the first byte is thus [0xf8, 0xff]
const lengthLength = firstByte - 0xf7
const length = Length.decode(buffer, start + 1, lengthLength)
if (length < 55) throw new Error('Invalid length')
const value = decodeList(buffer, start + 1 + lengthLength, length)
decode.bytes = 1 + lengthLength + length
return value
}
function decodeList (buffer, start, length) {
const lst = []
for (const end = start + length; start !== end; start += decode.bytes) {
lst.push(decode(buffer, start, end))
}
return lst
}
function encodingLength (value) {
if (Array.isArray(value)) {
const length = value.reduce((total, item) => total + encodingLength(item), 0)
return Length.encodingLength(length) + length
}
const buffer = toBuffer(value)
let length = buffer.length
if (!(buffer.length === 1 && buffer[0] < 0x80)) length += Length.encodingLength(length)
return length
}
module.exports = { encode, decode, encodingLength }