Development Artifact Cleanup: ✅ BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location - dev/test-nodes/brother_node/: Moved from root directory for better organization - Contains development configuration, test logs, and test chain data - No impact on production systems - purely development/testing artifact ✅ DEVELOPMENT ARTIFACTS IDENTIFIED: - Chain ID: aitbc-brother-chain (test/development chain) - Ports: 8010 (P2P) and 8011 (RPC) - different from production - Environment: .env file with test configuration - Logs: rpc.log and node.log from development testing session (March 15, 2026) ✅ ROOT DIRECTORY CLEANUP: Removed development clutter from production directory - brother_node/ moved to dev/test-nodes/brother_node/ - Root directory now contains only production-ready components - Development artifacts properly organized in dev/ subdirectory DIRECTORY STRUCTURE IMPROVEMENT: 📁 dev/test-nodes/: Development and testing node configurations 🏗️ Root Directory: Clean production structure with only essential components 🧪 Development Isolation: Test environments separated from production BENEFITS: ✅ Clean Production Directory: No development artifacts in root ✅ Better Organization: Development nodes grouped in dev/ subdirectory ✅ Clear Separation: Production vs development environments clearly distinguished ✅ Maintainability: Easier to identify and manage development components RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
1675 lines
70 KiB
JavaScript
Executable File
1675 lines
70 KiB
JavaScript
Executable File
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Phase0BeaconState = exports.Phase0SignedBeaconBlock = exports.Phase0BeaconBlock = exports.AltairBeaconState = exports.AltairSignedBeaconBlock = exports.AltairBeaconBlock = exports.BellatrixBeaconState = exports.BellatrixSignedBeaconBlock = exports.BellatrixBeaconBlock = exports.BellatrixExecutionPayloadHeader = exports.CapellaBeaconState = exports.CapellaSignedBeaconBlock = exports.CapellaBeaconBlock = exports.CapellaExecutionPayloadHeader = exports.ETH2_PROFILES = exports.ETH2_CONSENSUS = exports.ETH2_TYPES = exports.bytes = exports.bool = exports.bit = exports.byte = exports.profile = exports.stableContainer = exports.bytevector = exports.bytelist = exports.union = exports.bitlist = exports.bitvector = exports.container = exports.list = exports.vector = exports.boolean = exports.uint256 = exports.uint128 = exports.uint64 = exports.uint32 = exports.uint16 = exports.uint8 = exports.ForkSlots = void 0;
|
|
const sha2_1 = require("@noble/hashes/sha2");
|
|
const P = require("micro-packed");
|
|
const utils_ts_1 = require("./utils.js");
|
|
/*
|
|
|
|
Simple serialize (SSZ) is the serialization method used on the Beacon Chain.
|
|
SSZ is designed to be deterministic and also to Merkleize efficiently.
|
|
SSZ can be thought of as having two components:
|
|
a serialization scheme and a Merkleization scheme
|
|
that is designed to work efficiently with the serialized data structure.
|
|
|
|
- https://github.com/ethereum/consensus-specs/blob/f5277700e3b89c4d62bd4e88a559c2b938c6b0a5/ssz/simple-serialize.md
|
|
- https://github.com/ethereum/consensus-specs/blob/f5277700e3b89c4d62bd4e88a559c2b938c6b0a5/ssz/merkle-proofs.md
|
|
- https://www.ssz.dev/show
|
|
|
|
API difference:
|
|
- containers (vec/list) have arguments like (len, child).
|
|
this is different from other SSZ library, but compatible with packed.
|
|
there is good reason to do that: it allows create anonymous structures inside:
|
|
`const t = SSZ.vector(10, SSZ.container({
|
|
...long multiline definition here...
|
|
}))`
|
|
if length is second argument it would look more complex and harder to read.
|
|
- bytes provided as bytes instead of hex strings (same as other libs)
|
|
|
|
*/
|
|
const BYTES_PER_CHUNK = 32; // Should be equal to digest size of hash
|
|
const EMPTY_CHUNK = new Uint8Array(BYTES_PER_CHUNK);
|
|
exports.ForkSlots = {
|
|
Phase0: 0,
|
|
Altair: 2375680,
|
|
Bellatrix: 4700013,
|
|
Capella: 6209536,
|
|
Deneb: 8626176,
|
|
};
|
|
// Utils for hashing
|
|
function chunks(data) {
|
|
const res = [];
|
|
for (let i = 0; i < Math.ceil(data.length / BYTES_PER_CHUNK); i++) {
|
|
const chunk = data.subarray(i * BYTES_PER_CHUNK, (i + 1) * BYTES_PER_CHUNK);
|
|
if (chunk.length === BYTES_PER_CHUNK)
|
|
res.push(chunk);
|
|
else {
|
|
const tmp = EMPTY_CHUNK.slice();
|
|
tmp.set(chunk);
|
|
res.push(tmp);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
const hash = (a, b) => sha2_1.sha256.create().update(a).update(b).digest();
|
|
const mixInLength = (root, length) => hash(root, P.U256LE.encode(BigInt(length)));
|
|
// Will OOM without this, because tree padded to next power of two.
|
|
const zeroHashes = /* @__PURE__ */ (() => {
|
|
const res = [EMPTY_CHUNK];
|
|
for (let i = 0; i < 64; i++)
|
|
res.push(hash(res[i], res[i]));
|
|
return res;
|
|
})();
|
|
const merkleize = (chunks, limit) => {
|
|
let chunksLen = chunks.length;
|
|
if (limit !== undefined) {
|
|
if (limit < chunks.length) {
|
|
throw new Error(`SSZ/merkleize: limit (${limit}) is less than the number of chunks (${chunks.length})`);
|
|
}
|
|
chunksLen = limit;
|
|
}
|
|
// log2(next power of two), we cannot use binary ops since it can be bigger than 2**32.
|
|
const depth = Math.ceil(Math.log2(chunksLen));
|
|
if (chunks.length == 0)
|
|
return zeroHashes[depth];
|
|
for (let l = 0; l < depth; l++) {
|
|
const level = [];
|
|
for (let i = 0; i < chunks.length; i += 2)
|
|
level.push(hash(chunks[i], i + 1 < chunks.length ? chunks[i + 1] : zeroHashes[l]));
|
|
chunks = level;
|
|
}
|
|
return chunks[0];
|
|
};
|
|
const checkSSZ = (o) => {
|
|
if (typeof o !== 'object' ||
|
|
o === null ||
|
|
typeof o.encode !== 'function' ||
|
|
typeof o.decode !== 'function' ||
|
|
typeof o.merkleRoot !== 'function' ||
|
|
typeof o.composite !== 'boolean' ||
|
|
typeof o.chunkCount !== 'number') {
|
|
throw new Error(`SSZ: wrong element: ${o} (${typeof o})`);
|
|
}
|
|
};
|
|
// TODO: improve
|
|
const isStableCompat = (a, b) => {
|
|
if (a === b)
|
|
return true; // fast path
|
|
const _a = a;
|
|
const _b = b;
|
|
if (_a.info && _b.info) {
|
|
const aI = _a.info;
|
|
const bI = _b.info;
|
|
// Bitlist[N] / Bitvector[N] field types are compatible if they share the same capacity N.
|
|
const bitTypes = ['bitList', 'bitVector'];
|
|
if (bitTypes.includes(aI.type) && bitTypes.includes(bI.type) && aI.N === bI.N)
|
|
return true;
|
|
// List[T, N] / Vector[T, N] field types are compatible if T is compatible and if they also share the same capacity N.
|
|
const listTypes = ['list', 'vector'];
|
|
if (listTypes.includes(aI.type) &&
|
|
listTypes.includes(bI.type) &&
|
|
aI.N === bI.N &&
|
|
aI.inner._isStableCompat(bI.inner)) {
|
|
return true;
|
|
}
|
|
// Container / StableContainer[N] field types are compatible if all inner field types are compatible,
|
|
// if they also share the same field names in the same order, and for StableContainer[N] if they also
|
|
// share the same capacity N.
|
|
const contType = ['container', 'stableContainer'];
|
|
if (contType.includes(aI.type) && contType.includes(bI.type)) {
|
|
// both stable containers, but different capacity
|
|
if (aI.N !== undefined && bI.N !== undefined && aI.N !== bI.N)
|
|
return false;
|
|
const kA = Object.keys(aI.fields);
|
|
const kB = Object.keys(bI.fields);
|
|
if (kA.length !== kB.length)
|
|
return false;
|
|
for (let i = 0; i < kA.length; i++) {
|
|
const fA = kA[i];
|
|
const fB = kB[i];
|
|
if (fA !== fB)
|
|
return false;
|
|
if (!aI.fields[fA]._isStableCompat(bI.fields[fA]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
// Profile[X] field types are compatible with StableContainer types compatible with X, and
|
|
// are compatible with Profile[Y] where Y is compatible with X if also all inner field types
|
|
// are compatible. Differences solely in optionality do not affect merkleization compatibility.
|
|
if (aI.type === 'profile' || bI.type === 'profile') {
|
|
//console.log('PROF PROF?', aI.type, bI.type, aI.container._isStableCompat(bI));
|
|
if (aI.type === 'profile' && bI.type === 'stableContainer')
|
|
return aI.container._isStableCompat(b);
|
|
if (aI.type === 'stableContainer' && bI.type === 'profile')
|
|
return a._isStableCompat(bI.container);
|
|
if (aI.type === 'profile' && bI.type === 'profile')
|
|
return aI.container._isStableCompat(bI.container);
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
const basic = (type, inner, def) => ({
|
|
...inner,
|
|
default: def,
|
|
chunkCount: 1,
|
|
composite: false,
|
|
info: { type },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
chunks(value) {
|
|
return [this.merkleRoot(value)];
|
|
},
|
|
merkleRoot: (value) => {
|
|
const res = new Uint8Array(32);
|
|
res.set(inner.encode(value));
|
|
return res;
|
|
},
|
|
});
|
|
const int = (len, small = true) => P.apply(P.bigint(len, true), {
|
|
encode: (from) => {
|
|
if (!small)
|
|
return from;
|
|
if (BigInt(Number(from)) !== BigInt(from))
|
|
throw new Error('ssz int: small integer is too big');
|
|
return Number(from);
|
|
},
|
|
decode: (to) => {
|
|
if (typeof to === 'bigint')
|
|
return to;
|
|
if (typeof to !== 'number' || !Number.isSafeInteger(to))
|
|
throw new Error(`wrong type=${typeof to} expected number`);
|
|
return BigInt(to);
|
|
},
|
|
});
|
|
const _0n = BigInt(0);
|
|
exports.uint8 = basic('uint8', int(1), 0);
|
|
exports.uint16 = basic('uint16', int(2), 0);
|
|
exports.uint32 = basic('uint32', int(4), 0);
|
|
exports.uint64 = basic('uint64', int(8, false), _0n);
|
|
exports.uint128 = basic('uint128', int(16, false), _0n);
|
|
exports.uint256 = basic('uint256', int(32, false), _0n);
|
|
exports.boolean = basic('boolean', P.bool, false);
|
|
const array = (len, inner) => {
|
|
checkSSZ(inner);
|
|
let arr = P.array(len, inner);
|
|
// variable size arrays
|
|
if (inner.size === undefined) {
|
|
arr = P.wrap({
|
|
encodeStream: P.array(len, P.pointer(P.U32LE, inner)).encodeStream,
|
|
decodeStream: (r) => {
|
|
const res = [];
|
|
if (!r.leftBytes)
|
|
return res;
|
|
const first = P.U32LE.decodeStream(r);
|
|
const len = (first - r.pos) / P.U32LE.size;
|
|
if (!Number.isSafeInteger(len))
|
|
throw r.err('SSZ/array: wrong fixed size length');
|
|
const rest = P.array(len, P.U32LE).decodeStream(r);
|
|
const offsets = [first, ...rest];
|
|
// SSZ decoding requires very specific encoding and should throw on data constructed differently.
|
|
// There is also ZST problem here (as in ETH ABI), but it is impossible to exploit since
|
|
// definitions are hardcoded. Also, pointers very strict here.
|
|
for (let i = 0; i < offsets.length; i++) {
|
|
const pos = offsets[i];
|
|
const next = i + 1 < offsets.length ? offsets[i + 1] : r.totalBytes;
|
|
if (next < pos)
|
|
throw r.err('SSZ/array: decreasing offset');
|
|
const len = next - pos;
|
|
if (r.pos !== pos)
|
|
throw r.err('SSZ/array: wrong offset');
|
|
res.push(inner.decode(r.bytes(len)));
|
|
}
|
|
return res;
|
|
},
|
|
});
|
|
}
|
|
return arr;
|
|
};
|
|
/**
|
|
* Vector: fixed size ('len') array of elements 'inner'
|
|
*/
|
|
const vector = (len, inner) => {
|
|
if (!Number.isSafeInteger(len) || len <= 0)
|
|
throw new Error(`SSZ/vector: wrong length=${len} (should be positive integer)`);
|
|
return {
|
|
...array(len, inner),
|
|
info: { type: 'vector', N: len, inner },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
default: new Array(len).fill(inner.default),
|
|
composite: true,
|
|
chunkCount: inner.composite ? Math.ceil((len * inner.size) / 32) : len,
|
|
chunks(value) {
|
|
if (!inner.composite)
|
|
return chunks(this.encode(value));
|
|
return value.map((i) => inner.merkleRoot(i));
|
|
},
|
|
merkleRoot(value) {
|
|
return merkleize(this.chunks(value));
|
|
},
|
|
};
|
|
};
|
|
exports.vector = vector;
|
|
/**
|
|
* List: dynamic array of 'inner' elements with length limit maxLen
|
|
*/
|
|
const list = (maxLen, inner) => {
|
|
checkSSZ(inner);
|
|
const coder = P.validate(array(null, inner), (value) => {
|
|
if (!Array.isArray(value) || value.length > maxLen)
|
|
throw new Error(`SSZ/list: wrong value=${value} (len=${value.length} maxLen=${maxLen})`);
|
|
return value;
|
|
});
|
|
return {
|
|
...coder,
|
|
info: { type: 'list', N: maxLen, inner },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
composite: true,
|
|
chunkCount: !inner.composite ? Math.ceil((maxLen * inner.size) / BYTES_PER_CHUNK) : maxLen,
|
|
default: [],
|
|
chunks(value) {
|
|
if (inner.composite)
|
|
return value.map((i) => inner.merkleRoot(i));
|
|
return chunks(this.encode(value));
|
|
},
|
|
merkleRoot(value) {
|
|
return mixInLength(merkleize(this.chunks(value), this.chunkCount), value.length);
|
|
},
|
|
};
|
|
};
|
|
exports.list = list;
|
|
const wrapPointer = (p) => (p.size === undefined ? P.pointer(P.U32LE, p) : p);
|
|
const wrapRawPointer = (p) => (p.size === undefined ? P.U32LE : p);
|
|
// TODO: improve, unclear how
|
|
const fixOffsets = (r, fields, offsetFields, obj, offset) => {
|
|
const offsets = [];
|
|
for (const f of offsetFields)
|
|
offsets.push(obj[f] + offset);
|
|
for (let i = 0; i < offsets.length; i++) {
|
|
// TODO: how to merge this with array?
|
|
const name = offsetFields[i];
|
|
const pos = offsets[i];
|
|
const next = i + 1 < offsets.length ? offsets[i + 1] : r.totalBytes;
|
|
if (next < pos)
|
|
throw r.err('SSZ/container: decreasing offset');
|
|
const len = next - pos;
|
|
if (r.pos !== pos)
|
|
throw r.err('SSZ/container: wrong offset');
|
|
obj[name] = fields[name].decode(r.bytes(len));
|
|
}
|
|
return obj;
|
|
};
|
|
/**
|
|
* Container: Encodes object with multiple fields. P.struct for SSZ.
|
|
*/
|
|
const container = (fields) => {
|
|
if (!Object.keys(fields).length)
|
|
throw new Error('SSZ/container: no fields');
|
|
const ptrCoder = P.struct(Object.fromEntries(Object.entries(fields).map(([k, v]) => [k, wrapPointer(v)])));
|
|
const fixedCoder = P.struct(Object.fromEntries(Object.entries(fields).map(([k, v]) => [k, wrapRawPointer(v)])));
|
|
const offsetFields = Object.keys(fields).filter((i) => fields[i].size === undefined);
|
|
const coder = P.wrap({
|
|
encodeStream: ptrCoder.encodeStream,
|
|
decodeStream: (r) => fixOffsets(r, fields, offsetFields, fixedCoder.decodeStream(r), 0),
|
|
});
|
|
return {
|
|
...coder,
|
|
info: { type: 'container', fields },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
size: offsetFields.length ? undefined : fixedCoder.size, // structure is fixed size if all fields is fixed size
|
|
default: Object.fromEntries(Object.entries(fields).map(([k, v]) => [k, v.default])),
|
|
composite: true,
|
|
chunkCount: Object.keys(fields).length,
|
|
chunks(value) {
|
|
return Object.entries(fields).map(([k, v]) => v.merkleRoot(value[k]));
|
|
},
|
|
merkleRoot(value) {
|
|
return merkleize(this.chunks(value));
|
|
},
|
|
};
|
|
};
|
|
exports.container = container;
|
|
// Like 'P.bits', but different direction
|
|
const bitsCoder = (len) => ({
|
|
encode: (data) => {
|
|
const res = [];
|
|
for (const byte of data)
|
|
for (let i = 0; i < 8; i++)
|
|
res.push(!!(byte & (1 << i)));
|
|
for (let i = len; i < res.length; i++) {
|
|
if (res[i])
|
|
throw new Error('SSZ/bitsCoder/encode: non-zero padding');
|
|
}
|
|
return res.slice(0, len);
|
|
},
|
|
decode: (data) => {
|
|
const res = new Uint8Array(Math.ceil(len / 8));
|
|
for (let i = 0; i < data.length; i++)
|
|
if (data[i])
|
|
res[Math.floor(i / 8)] |= 1 << i % 8;
|
|
return res;
|
|
},
|
|
});
|
|
/**
|
|
* BitVector: array of booleans with fixed size
|
|
*/
|
|
const bitvector = (len) => {
|
|
if (!Number.isSafeInteger(len) || len <= 0)
|
|
throw new Error(`SSZ/bitVector: wrong length=${len} (should be positive integer)`);
|
|
const bytesLen = Math.ceil(len / 8);
|
|
const coder = P.apply(P.bytes(bytesLen), bitsCoder(len));
|
|
return {
|
|
...coder,
|
|
info: { type: 'bitVector', N: len },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
default: new Array(len).fill(false),
|
|
composite: true,
|
|
chunkCount: Math.ceil(len / 256),
|
|
chunks(value) {
|
|
return chunks(this.encode(value));
|
|
},
|
|
merkleRoot(value) {
|
|
return merkleize(this.chunks(value), this.chunkCount);
|
|
},
|
|
};
|
|
};
|
|
exports.bitvector = bitvector;
|
|
/**
|
|
* BitList: array of booleans with dynamic size (but maxLen limit)
|
|
*/
|
|
const bitlist = (maxLen) => {
|
|
if (!Number.isSafeInteger(maxLen) || maxLen <= 0)
|
|
throw new Error(`SSZ/bitList: wrong max length=${maxLen} (should be positive integer)`);
|
|
let coder = P.wrap({
|
|
encodeStream: (w, value) => {
|
|
w.bytes(bitsCoder(value.length + 1).decode([...value, true])); // last true bit is terminator
|
|
},
|
|
decodeStream: (r) => {
|
|
const bytes = r.bytes(r.leftBytes); // use everything
|
|
if (!bytes.length || bytes[bytes.length - 1] === 0)
|
|
throw new Error('SSZ/bitlist: empty trailing byte');
|
|
const bits = bitsCoder(bytes.length * 8).encode(bytes);
|
|
const terminator = bits.lastIndexOf(true);
|
|
if (terminator === -1)
|
|
throw new Error('SSZ/bitList: no terminator');
|
|
return bits.slice(0, terminator);
|
|
},
|
|
});
|
|
coder = P.validate(coder, (value) => {
|
|
if (!Array.isArray(value) || value.length > maxLen)
|
|
throw new Error(`SSZ/bitList/encode: wrong value=${value} (${typeof value})`);
|
|
return value;
|
|
});
|
|
return {
|
|
...coder,
|
|
info: { type: 'bitList', N: maxLen },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
size: undefined,
|
|
default: [],
|
|
chunkCount: Math.ceil(maxLen / 256),
|
|
composite: true,
|
|
chunks(value) {
|
|
const data = value.length ? (0, exports.bitvector)(value.length).encode(value) : EMPTY_CHUNK;
|
|
return chunks(data);
|
|
},
|
|
merkleRoot(value) {
|
|
return mixInLength(merkleize(this.chunks(value), this.chunkCount), value.length);
|
|
},
|
|
};
|
|
};
|
|
exports.bitlist = bitlist;
|
|
/**
|
|
* Union type (None is null)
|
|
* */
|
|
const union = (...types) => {
|
|
if (types.length < 1 || types.length >= 128)
|
|
throw Error('SSZ/union: should have [1...128) types');
|
|
if (types[0] === null && types.length < 2)
|
|
throw new Error('SSZ/union: should have at least 2 types if first is null');
|
|
for (let i = 0; i < types.length; i++) {
|
|
if (i > 0 && types[i] === null)
|
|
throw new Error('SSZ/union: only first type can be null');
|
|
if (types[i] !== null)
|
|
checkSSZ(types[i]);
|
|
}
|
|
const coder = P.apply(P.tag(P.U8, Object.fromEntries(types.map((t, i) => [i, t === null ? P.magicBytes(P.EMPTY) : P.prefix(null, t)]))), {
|
|
encode: ({ TAG, data }) => ({ selector: TAG, value: data }),
|
|
decode: ({ selector, value }) => ({ TAG: selector, data: value }),
|
|
});
|
|
return {
|
|
...coder,
|
|
size: undefined, // union is always variable size
|
|
chunkCount: NaN,
|
|
default: { selector: 0, value: types[0] === null ? null : types[0].default },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
composite: true,
|
|
chunks({ selector, value }) {
|
|
const type = types[selector];
|
|
if (type === null)
|
|
return EMPTY_CHUNK;
|
|
return [types[selector].merkleRoot(value)];
|
|
},
|
|
merkleRoot: ({ selector, value }) => {
|
|
const type = types[selector];
|
|
if (type === null)
|
|
return mixInLength(EMPTY_CHUNK, 0);
|
|
return mixInLength(types[selector].merkleRoot(value), selector);
|
|
},
|
|
};
|
|
};
|
|
exports.union = union;
|
|
/**
|
|
* ByteList: same as List(len, SSZ.byte), but returns Uint8Array
|
|
*/
|
|
const bytelist = (maxLen) => {
|
|
const coder = P.validate(P.bytes(null), (value) => {
|
|
if (!(0, utils_ts_1.isBytes)(value) || value.length > maxLen)
|
|
throw new Error(`SSZ/bytelist: wrong value=${value}`);
|
|
return value;
|
|
});
|
|
return {
|
|
...coder,
|
|
info: { type: 'list', N: maxLen, inner: exports.byte },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
default: new Uint8Array([]),
|
|
composite: true,
|
|
chunkCount: Math.ceil(maxLen / 32),
|
|
chunks(value) {
|
|
return chunks(this.encode(value));
|
|
},
|
|
merkleRoot(value) {
|
|
return mixInLength(merkleize(this.chunks(value), this.chunkCount), value.length);
|
|
},
|
|
};
|
|
};
|
|
exports.bytelist = bytelist;
|
|
/**
|
|
* ByteVector: same as Vector(len, SSZ.byte), but returns Uint8Array
|
|
*/
|
|
const bytevector = (len) => {
|
|
if (!Number.isSafeInteger(len) || len <= 0)
|
|
throw new Error(`SSZ/vector: wrong length=${len} (should be positive integer)`);
|
|
return {
|
|
...P.bytes(len),
|
|
info: { type: 'vector', N: len, inner: exports.byte },
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
default: new Uint8Array(len),
|
|
composite: true,
|
|
chunkCount: Math.ceil(len / 32),
|
|
chunks(value) {
|
|
return chunks(this.encode(value));
|
|
},
|
|
merkleRoot(value) {
|
|
return merkleize(this.chunks(value));
|
|
},
|
|
};
|
|
};
|
|
exports.bytevector = bytevector;
|
|
/**
|
|
* Same as container, but all values are optional using bitvector as prefix which indicates active fields
|
|
*/
|
|
const stableContainer = (N, fields) => {
|
|
const fieldsNames = Object.keys(fields);
|
|
const fieldsLen = fieldsNames.length;
|
|
if (!fieldsLen)
|
|
throw new Error('SSZ/stableContainer: no fields');
|
|
if (fieldsLen > N)
|
|
throw new Error('SSZ/stableContainer: more fields than N');
|
|
const bv = (0, exports.bitvector)(N);
|
|
const coder = P.wrap({
|
|
encodeStream: (w, value) => {
|
|
const bsVal = new Array(N).fill(false);
|
|
for (let i = 0; i < fieldsLen; i++)
|
|
if (value[fieldsNames[i]] !== undefined)
|
|
bsVal[i] = true;
|
|
bv.encodeStream(w, bsVal);
|
|
const activeFields = fieldsNames.filter((_, i) => bsVal[i]);
|
|
const ptrCoder = P.struct(Object.fromEntries(activeFields.map((k) => [k, wrapPointer(fields[k])])));
|
|
w.bytes(ptrCoder.encode(value));
|
|
},
|
|
decodeStream: (r) => {
|
|
const bsVal = bv.decodeStream(r);
|
|
for (let i = fieldsLen; i < bsVal.length; i++) {
|
|
if (bsVal[i] !== false)
|
|
throw new Error('stableContainer: non-zero padding');
|
|
}
|
|
const activeFields = fieldsNames.filter((_, i) => bsVal[i]);
|
|
const fixedCoder = P.struct(Object.fromEntries(activeFields.map((k) => [k, wrapRawPointer(fields[k])])));
|
|
const offsetFields = activeFields.filter((i) => fields[i].size === undefined);
|
|
return fixOffsets(r, fields, offsetFields, fixedCoder.decodeStream(r), bv.size);
|
|
},
|
|
});
|
|
return {
|
|
...coder,
|
|
info: { type: 'stableContainer', N, fields },
|
|
size: undefined,
|
|
default: Object.fromEntries(Object.entries(fields).map(([k, _v]) => [k, undefined])),
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
composite: true,
|
|
chunkCount: N,
|
|
chunks(value) {
|
|
const res = Object.entries(fields).map(([k, v]) => value[k] === undefined ? new Uint8Array(32) : v.merkleRoot(value[k]));
|
|
while (res.length < N)
|
|
res.push(new Uint8Array(32));
|
|
return res;
|
|
},
|
|
merkleRoot(value) {
|
|
const bsVal = new Array(N).fill(false);
|
|
for (let i = 0; i < fieldsLen; i++)
|
|
if (value[fieldsNames[i]] !== undefined)
|
|
bsVal[i] = true;
|
|
return hash(merkleize(this.chunks(value)), bv.merkleRoot(bsVal));
|
|
},
|
|
};
|
|
};
|
|
exports.stableContainer = stableContainer;
|
|
/**
|
|
* Profile - fixed subset of stableContainer.
|
|
* - fields and order of fields is exactly same as in underlying container
|
|
* - some fields may be excluded or required in profile (all fields in stable container are always optional)
|
|
* - adding new fields to underlying container won't change profile's constructed on top of it,
|
|
* because it is required to provide all list of optional fields.
|
|
* - type of field can be changed inside profile (but we should be very explicit about this) to same shape type.
|
|
*
|
|
* @example
|
|
* // class Shape(StableContainer[4]):
|
|
* // side: Optional[uint16]
|
|
* // color: Optional[uint8]
|
|
* // radius: Optional[uint16]
|
|
*
|
|
* // class Square(Profile[Shape]):
|
|
* // side: uint16
|
|
* // color: uint8
|
|
*
|
|
* // class Circle(Profile[Shape]):
|
|
* // color: uint8
|
|
* // radius: Optional[uint16]
|
|
* // ->
|
|
* const Shape = SSZ.stableContainer(4, {
|
|
* side: SSZ.uint16,
|
|
* color: SSZ.uint8,
|
|
* radius: SSZ.uint16,
|
|
* });
|
|
* const Square = profile(Shape, [], ['side', 'color']);
|
|
* const Circle = profile(Shape, ['radius'], ['color']);
|
|
* const Circle2 = profile(Shape, ['radius'], ['color'], { color: SSZ.byte });
|
|
*/
|
|
const profile = (c, optFields, requiredFields = [], replaceType = {}) => {
|
|
checkSSZ(c);
|
|
if (c.info.type !== 'stableContainer')
|
|
throw new Error('profile: expected stableContainer');
|
|
const containerFields = new Set(Object.keys(c.info.fields));
|
|
if (!Array.isArray(optFields))
|
|
throw new Error('profile: optional fields should be array');
|
|
const optFS = new Set(optFields);
|
|
for (const f of optFS) {
|
|
if (!containerFields.has(f))
|
|
throw new Error(`profile: unexpected optional field ${f}`);
|
|
}
|
|
if (!Array.isArray(requiredFields))
|
|
throw new Error('profile: required fields should be array');
|
|
const reqFS = new Set(requiredFields);
|
|
for (const f of reqFS) {
|
|
if (!containerFields.has(f))
|
|
throw new Error(`profile: unexpected required field ${f}`);
|
|
if (optFS.has(f))
|
|
throw new Error(`profile: field ${f} is declared both as optional and required`);
|
|
}
|
|
if (!(0, utils_ts_1.isObject)(replaceType))
|
|
throw new Error('profile: replaceType should be object');
|
|
for (const k in replaceType) {
|
|
if (!containerFields.has(k))
|
|
throw new Error(`profile/replaceType: unexpected field ${k}`);
|
|
if (!replaceType[k]._isStableCompat(c.info.fields[k]))
|
|
throw new Error(`profile/replaceType: incompatible field ${k}`);
|
|
}
|
|
// Order should be same
|
|
const allFields = Object.keys(c.info.fields).filter((i) => optFS.has(i) || reqFS.has(i));
|
|
// bv is omitted if all fields are required!
|
|
const fieldCoders = { ...c.info.fields, ...replaceType };
|
|
let coder;
|
|
if (optFS.size === 0) {
|
|
// All fields are required, it is just container, possible with size
|
|
coder = (0, exports.container)(Object.fromEntries(allFields.map((k) => [k, fieldCoders[k]])));
|
|
}
|
|
else {
|
|
// NOTE: we cannot merge this with stable container,
|
|
// because some fields are active and some is not (based on required/non-required)
|
|
const bv = (0, exports.bitvector)(optFS.size);
|
|
const forFields = (fn) => {
|
|
let optPos = 0;
|
|
for (const f of allFields) {
|
|
const isOpt = optFS.has(f);
|
|
fn(f, isOpt ? optPos : undefined);
|
|
if (isOpt)
|
|
optPos++;
|
|
}
|
|
};
|
|
coder = {
|
|
...P.wrap({
|
|
encodeStream: (w, value) => {
|
|
const bsVal = new Array(optFS.size).fill(false);
|
|
const ptrCoder = {};
|
|
forFields((f, optPos) => {
|
|
const val = value[f];
|
|
if (optPos !== undefined && val !== undefined)
|
|
bsVal[optPos] = true;
|
|
if (optPos === undefined && val === undefined)
|
|
throw new Error(`profile.encode: empty required field ${f}`);
|
|
if (val !== undefined)
|
|
ptrCoder[f] = wrapPointer(fieldCoders[f]);
|
|
});
|
|
bv.encodeStream(w, bsVal);
|
|
w.bytes(P.struct(ptrCoder).encode(value));
|
|
},
|
|
decodeStream: (r) => {
|
|
let bsVal = bv.decodeStream(r);
|
|
const fixedCoder = {};
|
|
const offsetFields = [];
|
|
forFields((f, optPos) => {
|
|
if (optPos !== undefined && bsVal[optPos] === false)
|
|
return;
|
|
if (fieldCoders[f].size === undefined)
|
|
offsetFields.push(f);
|
|
fixedCoder[f] = wrapRawPointer(fieldCoders[f]);
|
|
});
|
|
return fixOffsets(r, fieldCoders, offsetFields, P.struct(fixedCoder).decodeStream(r), bv.size);
|
|
},
|
|
}),
|
|
size: undefined,
|
|
};
|
|
}
|
|
return {
|
|
...coder,
|
|
info: { type: 'profile', container: c },
|
|
default: Object.fromEntries(Array.from(reqFS).map((f) => [f, fieldCoders[f].default])),
|
|
_isStableCompat(other) {
|
|
return isStableCompat(this, other);
|
|
},
|
|
composite: true,
|
|
chunkCount: c.info.N,
|
|
chunks(value) {
|
|
return c.chunks(value);
|
|
},
|
|
merkleRoot(value) {
|
|
return c.merkleRoot(value);
|
|
},
|
|
};
|
|
};
|
|
exports.profile = profile;
|
|
// Aliases
|
|
exports.byte = exports.uint8;
|
|
exports.bit = exports.boolean;
|
|
exports.bool = exports.boolean;
|
|
exports.bytes = exports.bytevector;
|
|
// TODO: this required for tests, but can be useful for other ETH related stuff.
|
|
// Also, blobs here. Since lib is pretty small (thanks to packed), why not?
|
|
// Deneb (last eth2 fork) types:
|
|
const MAX_VALIDATORS_PER_COMMITTEE = 2048;
|
|
const MAX_PROPOSER_SLASHINGS = 16;
|
|
const MAX_ATTESTER_SLASHINGS = 2;
|
|
const MAX_ATTESTATIONS = 128;
|
|
const MAX_DEPOSITS = 16;
|
|
const MAX_VOLUNTARY_EXITS = 16;
|
|
const MAX_TRANSACTIONS_PER_PAYLOAD = 1048576;
|
|
const BYTES_PER_LOGS_BLOOM = 256;
|
|
const MAX_EXTRA_DATA_BYTES = 32;
|
|
const DEPOSIT_CONTRACT_TREE_DEPTH = 2 ** 5;
|
|
const SYNC_COMMITTEE_SIZE = 512;
|
|
const MAX_BYTES_PER_TRANSACTION = 1073741824;
|
|
const MAX_BLS_TO_EXECUTION_CHANGES = 16;
|
|
const MAX_WITHDRAWALS_PER_PAYLOAD = 16;
|
|
const MAX_BLOB_COMMITMENTS_PER_BLOCK = 4096;
|
|
const SLOTS_PER_HISTORICAL_ROOT = 8192;
|
|
const HISTORICAL_ROOTS_LIMIT = 16777216;
|
|
const SLOTS_PER_EPOCH = 32;
|
|
const EPOCHS_PER_ETH1_VOTING_PERIOD = 64;
|
|
const VALIDATOR_REGISTRY_LIMIT = 1099511627776;
|
|
const EPOCHS_PER_HISTORICAL_VECTOR = 65536;
|
|
const EPOCHS_PER_SLASHINGS_VECTOR = 8192;
|
|
const JUSTIFICATION_BITS_LENGTH = 4;
|
|
const BYTES_PER_FIELD_ELEMENT = 32;
|
|
const FIELD_ELEMENTS_PER_BLOB = 4096;
|
|
const KZG_COMMITMENT_INCLUSION_PROOF_DEPTH = 17;
|
|
const SYNC_COMMITTEE_SUBNET_COUNT = 4;
|
|
const NEXT_SYNC_COMMITTEE_DEPTH = 5;
|
|
const BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH = 4;
|
|
const FINALIZED_ROOT_DEPTH = 6;
|
|
// Electra
|
|
const MAX_COMMITTEES_PER_SLOT = 64;
|
|
const PENDING_PARTIAL_WITHDRAWALS_LIMIT = 134217728;
|
|
const PENDING_BALANCE_DEPOSITS_LIMIT = 134217728;
|
|
const PENDING_CONSOLIDATIONS_LIMIT = 262144;
|
|
const MAX_ATTESTER_SLASHINGS_ELECTRA = 1;
|
|
const MAX_ATTESTATIONS_ELECTRA = 8;
|
|
const MAX_DEPOSIT_REQUESTS_PER_PAYLOAD = 8192;
|
|
const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD = 16;
|
|
const MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD = 1;
|
|
// We can reduce size if we inline these. But updates for new forks would be hard.
|
|
const Slot = exports.uint64;
|
|
const Epoch = exports.uint64;
|
|
const CommitteeIndex = exports.uint64;
|
|
const ValidatorIndex = exports.uint64;
|
|
const WithdrawalIndex = exports.uint64;
|
|
const BlobIndex = exports.uint64;
|
|
const Gwei = exports.uint64;
|
|
const Root = (0, exports.bytevector)(32);
|
|
const Hash32 = (0, exports.bytevector)(32);
|
|
const Bytes32 = (0, exports.bytevector)(32);
|
|
const Version = (0, exports.bytevector)(4);
|
|
const DomainType = (0, exports.bytevector)(4);
|
|
const ForkDigest = (0, exports.bytevector)(4);
|
|
const Domain = (0, exports.bytevector)(32);
|
|
const BLSPubkey = (0, exports.bytevector)(48);
|
|
const KZGCommitment = (0, exports.bytevector)(48);
|
|
const KZGProof = (0, exports.bytevector)(48);
|
|
const BLSSignature = (0, exports.bytevector)(96);
|
|
const Ether = exports.uint64;
|
|
const ParticipationFlags = exports.uint8;
|
|
const ExecutionAddress = (0, exports.bytevector)(20);
|
|
const PayloadId = (0, exports.bytevector)(8);
|
|
const Transaction = (0, exports.bytelist)(MAX_BYTES_PER_TRANSACTION);
|
|
const Blob = (0, exports.bytevector)(BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB);
|
|
const Checkpoint = (0, exports.container)({ epoch: Epoch, root: Root });
|
|
const AttestationData = (0, exports.container)({
|
|
slot: Slot,
|
|
index: CommitteeIndex,
|
|
beacon_block_root: Root,
|
|
source: Checkpoint,
|
|
target: Checkpoint,
|
|
});
|
|
const Attestation = (0, exports.container)({
|
|
aggregation_bits: (0, exports.bitlist)(MAX_VALIDATORS_PER_COMMITTEE),
|
|
data: AttestationData,
|
|
signature: BLSSignature,
|
|
});
|
|
const AggregateAndProof = (0, exports.container)({
|
|
aggregator_index: ValidatorIndex,
|
|
aggregate: Attestation,
|
|
selection_proof: BLSSignature,
|
|
});
|
|
const IndexedAttestation = (0, exports.container)({
|
|
attesting_indices: (0, exports.list)(MAX_VALIDATORS_PER_COMMITTEE, ValidatorIndex),
|
|
data: AttestationData,
|
|
signature: BLSSignature,
|
|
});
|
|
const AttesterSlashing = (0, exports.container)({
|
|
attestation_1: IndexedAttestation,
|
|
attestation_2: IndexedAttestation,
|
|
});
|
|
const BLSToExecutionChange = (0, exports.container)({
|
|
validator_index: ValidatorIndex,
|
|
from_bls_pubkey: BLSPubkey,
|
|
to_execution_address: ExecutionAddress,
|
|
});
|
|
const Withdrawal = (0, exports.container)({
|
|
index: WithdrawalIndex,
|
|
validator_index: ValidatorIndex,
|
|
address: ExecutionAddress,
|
|
amount: Gwei,
|
|
});
|
|
const ExecutionPayload = (0, exports.container)({
|
|
parent_hash: Hash32,
|
|
fee_recipient: ExecutionAddress,
|
|
state_root: Bytes32,
|
|
receipts_root: Bytes32,
|
|
logs_bloom: (0, exports.bytevector)(BYTES_PER_LOGS_BLOOM),
|
|
prev_randao: Bytes32,
|
|
block_number: exports.uint64,
|
|
gas_limit: exports.uint64,
|
|
gas_used: exports.uint64,
|
|
timestamp: exports.uint64,
|
|
extra_data: (0, exports.bytelist)(MAX_EXTRA_DATA_BYTES),
|
|
base_fee_per_gas: exports.uint256,
|
|
block_hash: Hash32,
|
|
transactions: (0, exports.list)(MAX_TRANSACTIONS_PER_PAYLOAD, Transaction),
|
|
withdrawals: (0, exports.list)(MAX_WITHDRAWALS_PER_PAYLOAD, Withdrawal),
|
|
blob_gas_used: exports.uint64,
|
|
excess_blob_gas: exports.uint64,
|
|
});
|
|
MAX_WITHDRAWALS_PER_PAYLOAD;
|
|
const SigningData = (0, exports.container)({ object_root: Root, domain: Domain });
|
|
const BeaconBlockHeader = (0, exports.container)({
|
|
slot: Slot,
|
|
proposer_index: ValidatorIndex,
|
|
parent_root: Root,
|
|
state_root: Root,
|
|
body_root: Root,
|
|
});
|
|
const SignedBeaconBlockHeader = (0, exports.container)({ message: BeaconBlockHeader, signature: BLSSignature });
|
|
const ProposerSlashing = (0, exports.container)({
|
|
signed_header_1: SignedBeaconBlockHeader,
|
|
signed_header_2: SignedBeaconBlockHeader,
|
|
});
|
|
const DepositData = (0, exports.container)({
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: Gwei,
|
|
signature: BLSSignature,
|
|
});
|
|
const Deposit = (0, exports.container)({
|
|
proof: (0, exports.vector)(DEPOSIT_CONTRACT_TREE_DEPTH + 1, Bytes32),
|
|
data: DepositData,
|
|
});
|
|
const VoluntaryExit = (0, exports.container)({ epoch: Epoch, validator_index: ValidatorIndex });
|
|
const SyncAggregate = (0, exports.container)({
|
|
sync_committee_bits: (0, exports.bitvector)(SYNC_COMMITTEE_SIZE),
|
|
sync_committee_signature: BLSSignature,
|
|
});
|
|
const Eth1Data = (0, exports.container)({
|
|
deposit_root: Root,
|
|
deposit_count: exports.uint64,
|
|
block_hash: Hash32,
|
|
});
|
|
const SignedVoluntaryExit = (0, exports.container)({ message: VoluntaryExit, signature: BLSSignature });
|
|
const SignedBLSToExecutionChange = (0, exports.container)({
|
|
message: BLSToExecutionChange,
|
|
signature: BLSSignature,
|
|
});
|
|
const BeaconBlockBody = (0, exports.container)({
|
|
randao_reveal: BLSSignature,
|
|
eth1_data: Eth1Data,
|
|
graffiti: Bytes32,
|
|
proposer_slashings: (0, exports.list)(MAX_PROPOSER_SLASHINGS, ProposerSlashing),
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS, AttesterSlashing),
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS, Attestation),
|
|
deposits: (0, exports.list)(MAX_DEPOSITS, Deposit),
|
|
voluntary_exits: (0, exports.list)(MAX_VOLUNTARY_EXITS, SignedVoluntaryExit),
|
|
sync_aggregate: SyncAggregate,
|
|
execution_payload: ExecutionPayload,
|
|
bls_to_execution_changes: (0, exports.list)(MAX_BLS_TO_EXECUTION_CHANGES, SignedBLSToExecutionChange),
|
|
blob_kzg_commitments: (0, exports.list)(MAX_BLOB_COMMITMENTS_PER_BLOCK, KZGCommitment),
|
|
});
|
|
const BeaconBlock = (0, exports.container)({
|
|
slot: Slot,
|
|
proposer_index: ValidatorIndex,
|
|
parent_root: Root,
|
|
state_root: Root,
|
|
body: BeaconBlockBody,
|
|
});
|
|
const SyncCommittee = (0, exports.container)({
|
|
pubkeys: (0, exports.vector)(SYNC_COMMITTEE_SIZE, BLSPubkey),
|
|
aggregate_pubkey: BLSPubkey,
|
|
});
|
|
const Fork = (0, exports.container)({
|
|
previous_version: Version,
|
|
current_version: Version,
|
|
epoch: Epoch,
|
|
});
|
|
const Validator = (0, exports.container)({
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
effective_balance: Gwei,
|
|
slashed: exports.boolean,
|
|
activation_eligibility_epoch: Epoch,
|
|
activation_epoch: Epoch,
|
|
exit_epoch: Epoch,
|
|
withdrawable_epoch: Epoch,
|
|
});
|
|
const ExecutionPayloadHeader = (0, exports.container)({
|
|
parent_hash: Hash32,
|
|
fee_recipient: ExecutionAddress,
|
|
state_root: Bytes32,
|
|
receipts_root: Bytes32,
|
|
logs_bloom: (0, exports.bytevector)(BYTES_PER_LOGS_BLOOM),
|
|
prev_randao: Bytes32,
|
|
block_number: exports.uint64,
|
|
gas_limit: exports.uint64,
|
|
gas_used: exports.uint64,
|
|
timestamp: exports.uint64,
|
|
extra_data: (0, exports.bytelist)(MAX_EXTRA_DATA_BYTES),
|
|
base_fee_per_gas: exports.uint256,
|
|
block_hash: Hash32,
|
|
transactions_root: Root,
|
|
withdrawals_root: Root,
|
|
blob_gas_used: exports.uint64,
|
|
excess_blob_gas: exports.uint64,
|
|
});
|
|
const HistoricalSummary = (0, exports.container)({
|
|
block_summary_root: Root,
|
|
state_summary_root: Root,
|
|
});
|
|
const BeaconState = (0, exports.container)({
|
|
genesis_time: exports.uint64,
|
|
genesis_validators_root: Root,
|
|
slot: Slot,
|
|
fork: Fork,
|
|
latest_block_header: BeaconBlockHeader,
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, Root),
|
|
historical_roots: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, Root),
|
|
eth1_data: Eth1Data,
|
|
eth1_data_votes: (0, exports.list)(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH, Eth1Data),
|
|
eth1_deposit_index: exports.uint64,
|
|
validators: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, Validator),
|
|
balances: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, Gwei),
|
|
randao_mixes: (0, exports.vector)(EPOCHS_PER_HISTORICAL_VECTOR, Bytes32),
|
|
slashings: (0, exports.vector)(EPOCHS_PER_SLASHINGS_VECTOR, Gwei),
|
|
previous_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, ParticipationFlags),
|
|
current_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, ParticipationFlags),
|
|
justification_bits: (0, exports.bitvector)(JUSTIFICATION_BITS_LENGTH),
|
|
previous_justified_checkpoint: Checkpoint,
|
|
current_justified_checkpoint: Checkpoint,
|
|
finalized_checkpoint: Checkpoint,
|
|
inactivity_scores: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.uint64),
|
|
current_sync_committee: SyncCommittee,
|
|
next_sync_committee: SyncCommittee,
|
|
latest_execution_payload_header: ExecutionPayloadHeader,
|
|
next_withdrawal_index: WithdrawalIndex,
|
|
next_withdrawal_validator_index: ValidatorIndex,
|
|
historical_summaries: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, HistoricalSummary),
|
|
});
|
|
const BlobIdentifier = (0, exports.container)({
|
|
block_root: Root,
|
|
index: BlobIndex,
|
|
});
|
|
const BlobSidecar = (0, exports.container)({
|
|
index: BlobIndex,
|
|
blob: Blob,
|
|
kzg_commitment: KZGCommitment,
|
|
kzg_proof: KZGProof,
|
|
signed_block_header: SignedBeaconBlockHeader,
|
|
kzg_commitment_inclusion_proof: (0, exports.vector)(KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, Bytes32),
|
|
});
|
|
const SyncCommitteeContribution = (0, exports.container)({
|
|
slot: Slot,
|
|
beacon_block_root: Root,
|
|
subcommittee_index: exports.uint64,
|
|
aggregation_bits: (0, exports.bitvector)(SYNC_COMMITTEE_SIZE / SYNC_COMMITTEE_SUBNET_COUNT),
|
|
signature: BLSSignature,
|
|
});
|
|
const ContributionAndProof = (0, exports.container)({
|
|
aggregator_index: ValidatorIndex,
|
|
contribution: SyncCommitteeContribution,
|
|
selection_proof: BLSSignature,
|
|
});
|
|
const DepositMessage = (0, exports.container)({
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: Gwei,
|
|
});
|
|
const Eth1Block = (0, exports.container)({
|
|
timestamp: exports.uint64,
|
|
deposit_root: Root,
|
|
deposit_count: exports.uint64,
|
|
});
|
|
const ForkData = (0, exports.container)({ current_version: Version, genesis_validators_root: Root });
|
|
const HistoricalBatch = (0, exports.container)({
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, Root),
|
|
});
|
|
const PendingAttestation = (0, exports.container)({
|
|
aggregation_bits: (0, exports.bitlist)(MAX_VALIDATORS_PER_COMMITTEE),
|
|
data: AttestationData,
|
|
inclusion_delay: Slot,
|
|
proposer_index: ValidatorIndex,
|
|
});
|
|
const PowBlock = (0, exports.container)({
|
|
block_hash: Hash32,
|
|
parent_hash: Hash32,
|
|
total_difficulty: exports.uint256,
|
|
});
|
|
const SignedAggregateAndProof = (0, exports.container)({ message: AggregateAndProof, signature: BLSSignature });
|
|
const SignedBeaconBlock = (0, exports.container)({ message: BeaconBlock, signature: BLSSignature });
|
|
const SignedContributionAndProof = (0, exports.container)({
|
|
message: ContributionAndProof,
|
|
signature: BLSSignature,
|
|
});
|
|
const SyncAggregatorSelectionData = (0, exports.container)({ slot: Slot, subcommittee_index: exports.uint64 });
|
|
const SyncCommitteeMessage = (0, exports.container)({
|
|
slot: Slot,
|
|
beacon_block_root: Root,
|
|
validator_index: ValidatorIndex,
|
|
signature: BLSSignature,
|
|
});
|
|
const LightClientHeader = (0, exports.container)({
|
|
beacon: BeaconBlockHeader,
|
|
execution: ExecutionPayloadHeader,
|
|
execution_branch: (0, exports.vector)(BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH, Bytes32),
|
|
});
|
|
const LightClientBootstrap = (0, exports.container)({
|
|
header: LightClientHeader,
|
|
current_sync_committee: SyncCommittee,
|
|
current_sync_committee_branch: (0, exports.vector)(NEXT_SYNC_COMMITTEE_DEPTH, Bytes32),
|
|
});
|
|
const LightClientUpdate = (0, exports.container)({
|
|
attested_header: LightClientHeader,
|
|
next_sync_committee: SyncCommittee,
|
|
next_sync_committee_branch: (0, exports.vector)(NEXT_SYNC_COMMITTEE_DEPTH, Bytes32),
|
|
finalized_header: LightClientHeader,
|
|
finality_branch: (0, exports.vector)(FINALIZED_ROOT_DEPTH, Bytes32),
|
|
sync_aggregate: SyncAggregate,
|
|
signature_slot: Slot,
|
|
});
|
|
const LightClientFinalityUpdate = (0, exports.container)({
|
|
attested_header: LightClientHeader,
|
|
finalized_header: LightClientHeader,
|
|
finality_branch: (0, exports.vector)(FINALIZED_ROOT_DEPTH, Bytes32),
|
|
sync_aggregate: SyncAggregate,
|
|
signature_slot: Slot,
|
|
});
|
|
const LightClientOptimisticUpdate = (0, exports.container)({
|
|
attested_header: LightClientHeader,
|
|
sync_aggregate: SyncAggregate,
|
|
signature_slot: Slot,
|
|
});
|
|
// Electra
|
|
const DepositRequest = (0, exports.container)({
|
|
pubkey: BLSPubkey,
|
|
withdrawal_credentials: Bytes32,
|
|
amount: Gwei,
|
|
signature: BLSSignature,
|
|
index: exports.uint64,
|
|
});
|
|
const WithdrawalRequest = (0, exports.container)({
|
|
source_address: ExecutionAddress,
|
|
validator_pubkey: BLSPubkey,
|
|
amount: Gwei,
|
|
});
|
|
const ConsolidationRequest = (0, exports.container)({
|
|
source_address: ExecutionAddress,
|
|
source_pubkey: BLSPubkey,
|
|
target_pubkey: BLSPubkey,
|
|
});
|
|
const PendingBalanceDeposit = (0, exports.container)({
|
|
index: ValidatorIndex,
|
|
amount: Gwei,
|
|
});
|
|
const PendingPartialWithdrawal = (0, exports.container)({
|
|
index: ValidatorIndex,
|
|
amount: Gwei,
|
|
withdrawable_epoch: Epoch,
|
|
});
|
|
const PendingConsolidation = (0, exports.container)({
|
|
source_index: ValidatorIndex,
|
|
target_index: ValidatorIndex,
|
|
});
|
|
exports.ETH2_TYPES = {
|
|
Slot,
|
|
Epoch,
|
|
CommitteeIndex,
|
|
ValidatorIndex,
|
|
WithdrawalIndex,
|
|
Gwei,
|
|
Root,
|
|
Hash32,
|
|
Bytes32,
|
|
Version,
|
|
DomainType,
|
|
ForkDigest,
|
|
Domain,
|
|
BLSPubkey,
|
|
BLSSignature,
|
|
Ether,
|
|
ParticipationFlags,
|
|
ExecutionAddress,
|
|
PayloadId,
|
|
KZGCommitment,
|
|
KZGProof,
|
|
// Containters
|
|
Checkpoint,
|
|
AttestationData,
|
|
Attestation,
|
|
AggregateAndProof,
|
|
IndexedAttestation,
|
|
AttesterSlashing,
|
|
BLSToExecutionChange,
|
|
ExecutionPayload,
|
|
SyncAggregate,
|
|
VoluntaryExit,
|
|
BeaconBlockHeader,
|
|
SigningData,
|
|
SignedBeaconBlockHeader,
|
|
ProposerSlashing,
|
|
DepositData,
|
|
Deposit,
|
|
SignedVoluntaryExit,
|
|
Eth1Data,
|
|
Withdrawal,
|
|
BeaconBlockBody,
|
|
BeaconBlock,
|
|
SyncCommittee,
|
|
Fork,
|
|
Validator,
|
|
ExecutionPayloadHeader,
|
|
HistoricalSummary,
|
|
BeaconState,
|
|
BlobIdentifier,
|
|
BlobSidecar,
|
|
ContributionAndProof,
|
|
DepositMessage,
|
|
Eth1Block,
|
|
ForkData,
|
|
HistoricalBatch,
|
|
PendingAttestation,
|
|
PowBlock,
|
|
Transaction,
|
|
SignedAggregateAndProof,
|
|
SignedBLSToExecutionChange,
|
|
SignedBeaconBlock,
|
|
SignedContributionAndProof,
|
|
SyncAggregatorSelectionData,
|
|
SyncCommitteeContribution,
|
|
SyncCommitteeMessage,
|
|
// Light client
|
|
LightClientHeader,
|
|
LightClientBootstrap,
|
|
LightClientUpdate,
|
|
LightClientOptimisticUpdate,
|
|
LightClientFinalityUpdate,
|
|
// Electra
|
|
DepositRequest,
|
|
WithdrawalRequest,
|
|
ConsolidationRequest,
|
|
PendingBalanceDeposit,
|
|
PendingPartialWithdrawal,
|
|
PendingConsolidation,
|
|
};
|
|
// EIP-7688
|
|
const MAX_ATTESTATION_FIELDS = 8;
|
|
const MAX_INDEXED_ATTESTATION_FIELDS = 8;
|
|
const MAX_EXECUTION_PAYLOAD_FIELDS = 64;
|
|
const MAX_BEACON_BLOCK_BODY_FIELDS = 64;
|
|
const MAX_BEACON_STATE_FIELDS = 128;
|
|
const MAX_EXECUTION_REQUESTS_FIELDS = 16;
|
|
const StableAttestation = (0, exports.stableContainer)(MAX_ATTESTATION_FIELDS, {
|
|
aggregation_bits: (0, exports.bitlist)(MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT),
|
|
data: AttestationData,
|
|
signature: BLSSignature,
|
|
committee_bits: (0, exports.bitvector)(MAX_COMMITTEES_PER_SLOT),
|
|
});
|
|
const StableIndexedAttestation = (0, exports.stableContainer)(MAX_INDEXED_ATTESTATION_FIELDS, {
|
|
attesting_indices: (0, exports.list)(MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT, ValidatorIndex),
|
|
data: AttestationData,
|
|
signature: BLSSignature,
|
|
});
|
|
const StableAttesterSlashing = (0, exports.container)({
|
|
attestation_1: StableIndexedAttestation,
|
|
attestation_2: StableIndexedAttestation,
|
|
});
|
|
const StableExecutionRequests = (0, exports.stableContainer)(MAX_EXECUTION_REQUESTS_FIELDS, {
|
|
deposits: (0, exports.list)(MAX_DEPOSIT_REQUESTS_PER_PAYLOAD, DepositRequest), // [New in Electra:EIP6110]
|
|
withdrawals: (0, exports.list)(MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, WithdrawalRequest), // [New in Electra:EIP7002:EIP7251]
|
|
consolidations: (0, exports.list)(MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD, ConsolidationRequest), // [New in Electra:EIP7251]
|
|
});
|
|
const StableExecutionPayload = (0, exports.stableContainer)(MAX_EXECUTION_PAYLOAD_FIELDS, {
|
|
parent_hash: Hash32,
|
|
fee_recipient: ExecutionAddress,
|
|
state_root: Bytes32,
|
|
receipts_root: Bytes32,
|
|
logs_bloom: (0, exports.bytevector)(BYTES_PER_LOGS_BLOOM),
|
|
prev_randao: Bytes32,
|
|
block_number: exports.uint64,
|
|
gas_limit: exports.uint64,
|
|
gas_used: exports.uint64,
|
|
timestamp: exports.uint64,
|
|
extra_data: (0, exports.bytelist)(MAX_EXTRA_DATA_BYTES),
|
|
base_fee_per_gas: exports.uint256,
|
|
block_hash: Hash32,
|
|
transactions: (0, exports.list)(MAX_TRANSACTIONS_PER_PAYLOAD, Transaction),
|
|
withdrawals: (0, exports.list)(MAX_WITHDRAWALS_PER_PAYLOAD, Withdrawal), // [New in Capella]
|
|
blob_gas_used: exports.uint64,
|
|
excess_blob_gas: exports.uint64,
|
|
deposit_requests: (0, exports.list)(MAX_DEPOSIT_REQUESTS_PER_PAYLOAD, DepositRequest), // [New in Electra:EIP6110]
|
|
withdrawal_requests: (0, exports.list)(MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, WithdrawalRequest), // [New in Electra:EIP7002:EIP7251]
|
|
consolidation_requests: (0, exports.list)(MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD, ConsolidationRequest), // [New in Electra:EIP7251]
|
|
});
|
|
const StableExecutionPayloadHeader = (0, exports.stableContainer)(MAX_EXECUTION_PAYLOAD_FIELDS, {
|
|
parent_hash: Hash32,
|
|
fee_recipient: ExecutionAddress,
|
|
state_root: Bytes32,
|
|
receipts_root: Bytes32,
|
|
logs_bloom: (0, exports.bytevector)(BYTES_PER_LOGS_BLOOM),
|
|
prev_randao: Bytes32,
|
|
block_number: exports.uint64,
|
|
gas_limit: exports.uint64,
|
|
gas_used: exports.uint64,
|
|
timestamp: exports.uint64,
|
|
extra_data: (0, exports.bytelist)(MAX_EXTRA_DATA_BYTES),
|
|
base_fee_per_gas: exports.uint256,
|
|
block_hash: Hash32,
|
|
transactions_root: Root,
|
|
withdrawals_root: Root, // [New in Capella]
|
|
blob_gas_used: exports.uint64, // [New in Deneb:EIP4844]
|
|
excess_blob_gas: exports.uint64, // [New in Deneb:EIP4844]
|
|
deposit_requests_root: Root, // [New in Electra:EIP6110]
|
|
withdrawal_requests_root: Root, // [New in Electra:EIP7002:EIP7251]
|
|
consolidation_requests_root: Root, // [New in Electra:EIP7251]
|
|
});
|
|
const StableBeaconBlockBody = (0, exports.stableContainer)(MAX_BEACON_BLOCK_BODY_FIELDS, {
|
|
randao_reveal: BLSSignature,
|
|
eth1_data: Eth1Data,
|
|
graffiti: Bytes32,
|
|
proposer_slashings: (0, exports.list)(MAX_PROPOSER_SLASHINGS, ProposerSlashing),
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS_ELECTRA, StableAttesterSlashing), // [Modified in Electra:EIP7549]
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS_ELECTRA, StableAttestation), // [Modified in Electra:EIP7549]
|
|
deposits: (0, exports.list)(MAX_DEPOSITS, Deposit),
|
|
voluntary_exits: (0, exports.list)(MAX_VOLUNTARY_EXITS, SignedVoluntaryExit),
|
|
sync_aggregate: SyncAggregate,
|
|
execution_payload: StableExecutionPayload,
|
|
bls_to_execution_changes: (0, exports.list)(MAX_BLS_TO_EXECUTION_CHANGES, SignedBLSToExecutionChange),
|
|
blob_kzg_commitments: (0, exports.list)(MAX_BLOB_COMMITMENTS_PER_BLOCK, KZGCommitment),
|
|
execution_requests: StableExecutionRequests,
|
|
});
|
|
const StableBeaconState = (0, exports.stableContainer)(MAX_BEACON_STATE_FIELDS, {
|
|
genesis_time: exports.uint64,
|
|
genesis_validators_root: Root,
|
|
slot: Slot,
|
|
fork: Fork,
|
|
latest_block_header: BeaconBlockHeader,
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, Root),
|
|
historical_roots: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, Root),
|
|
eth1_data: Eth1Data,
|
|
eth1_data_votes: (0, exports.list)(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH, Eth1Data),
|
|
eth1_deposit_index: exports.uint64,
|
|
validators: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, Validator),
|
|
balances: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, Gwei),
|
|
randao_mixes: (0, exports.vector)(EPOCHS_PER_HISTORICAL_VECTOR, Bytes32),
|
|
slashings: (0, exports.vector)(EPOCHS_PER_SLASHINGS_VECTOR, Gwei),
|
|
previous_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, ParticipationFlags),
|
|
current_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, ParticipationFlags),
|
|
justification_bits: (0, exports.bitvector)(JUSTIFICATION_BITS_LENGTH),
|
|
previous_justified_checkpoint: Checkpoint,
|
|
current_justified_checkpoint: Checkpoint,
|
|
finalized_checkpoint: Checkpoint,
|
|
inactivity_scores: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.uint64),
|
|
current_sync_committee: SyncCommittee,
|
|
next_sync_committee: SyncCommittee,
|
|
latest_execution_payload_header: StableExecutionPayloadHeader,
|
|
next_withdrawal_index: WithdrawalIndex,
|
|
next_withdrawal_validator_index: ValidatorIndex,
|
|
historical_summaries: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, HistoricalSummary),
|
|
deposit_requests_start_index: exports.uint64, // [New in Electra:EIP6110]
|
|
deposit_balance_to_consume: Gwei, // [New in Electra:EIP7251]
|
|
exit_balance_to_consume: Gwei, // [New in Electra:EIP7251]
|
|
earliest_exit_epoch: Epoch, // [New in Electra:EIP7251]
|
|
consolidation_balance_to_consume: Gwei, // [New in Electra:EIP7251]
|
|
earliest_consolidation_epoch: Epoch, // [New in Electra:EIP7251]
|
|
pending_balance_deposits: (0, exports.list)(PENDING_BALANCE_DEPOSITS_LIMIT, PendingBalanceDeposit), // [New in Electra:EIP7251]
|
|
pending_partial_withdrawals: (0, exports.list)(PENDING_PARTIAL_WITHDRAWALS_LIMIT, PendingPartialWithdrawal), // [New in Electra:EIP7251]
|
|
pending_consolidations: (0, exports.list)(PENDING_CONSOLIDATIONS_LIMIT, PendingConsolidation), // [New in Electra:EIP7251]
|
|
});
|
|
exports.ETH2_CONSENSUS = {
|
|
StableAttestation,
|
|
StableIndexedAttestation,
|
|
StableAttesterSlashing,
|
|
StableExecutionPayload,
|
|
StableExecutionRequests,
|
|
StableExecutionPayloadHeader,
|
|
StableBeaconBlockBody,
|
|
StableBeaconState,
|
|
};
|
|
// Tests (electra profiles): https://github.com/ethereum/consensus-specs/pull/3844#issuecomment-2239285376
|
|
// NOTE: these are different from EIP-7688 by some reasons, but since nothing is merged/completed in eth side, we just trying
|
|
// to pass these tests for now.
|
|
const IndexedAttestationElectra = (0, exports.profile)(StableIndexedAttestation, [], ['attesting_indices', 'data', 'signature']);
|
|
const AttesterSlashingElectra = (0, exports.container)({
|
|
attestation_1: IndexedAttestationElectra,
|
|
attestation_2: IndexedAttestationElectra,
|
|
});
|
|
const ExecutionPayloadHeaderElectra = (0, exports.profile)(StableExecutionPayloadHeader, [], [
|
|
'parent_hash',
|
|
'fee_recipient',
|
|
'state_root',
|
|
'receipts_root',
|
|
'logs_bloom',
|
|
'prev_randao',
|
|
'block_number',
|
|
'gas_limit',
|
|
'gas_used',
|
|
'timestamp',
|
|
'extra_data',
|
|
'base_fee_per_gas',
|
|
'block_hash',
|
|
'transactions_root',
|
|
'withdrawals_root',
|
|
'blob_gas_used',
|
|
'excess_blob_gas',
|
|
]);
|
|
const ExecutionRequests = (0, exports.profile)(StableExecutionRequests, [], ['deposits', 'withdrawals', 'consolidations']);
|
|
const AttestationElectra = (0, exports.profile)(StableAttestation, [], ['aggregation_bits', 'data', 'signature', 'committee_bits']);
|
|
const ExecutionPayloadElectra = (0, exports.profile)(StableExecutionPayload, [], [
|
|
'parent_hash',
|
|
'fee_recipient',
|
|
'state_root',
|
|
'receipts_root',
|
|
'logs_bloom',
|
|
'prev_randao',
|
|
'block_number',
|
|
'gas_limit',
|
|
'gas_used',
|
|
'timestamp',
|
|
'extra_data',
|
|
'base_fee_per_gas',
|
|
'block_hash',
|
|
'transactions',
|
|
'withdrawals',
|
|
'blob_gas_used',
|
|
'excess_blob_gas',
|
|
]);
|
|
exports.ETH2_PROFILES = {
|
|
electra: {
|
|
Attestation: AttestationElectra,
|
|
AttesterSlashing: AttesterSlashingElectra,
|
|
IndexedAttestation: IndexedAttestationElectra,
|
|
ExecutionRequests,
|
|
ExecutionPayloadHeader: ExecutionPayloadHeaderElectra,
|
|
ExecutionPayload: ExecutionPayloadElectra,
|
|
BeaconBlockBody: (0, exports.profile)(StableBeaconBlockBody, [], [
|
|
'randao_reveal',
|
|
'eth1_data',
|
|
'graffiti',
|
|
'proposer_slashings',
|
|
'attester_slashings',
|
|
'attestations',
|
|
'deposits',
|
|
'voluntary_exits',
|
|
'sync_aggregate',
|
|
'execution_payload',
|
|
'bls_to_execution_changes',
|
|
'blob_kzg_commitments',
|
|
'execution_requests',
|
|
], {
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS_ELECTRA, AttesterSlashingElectra),
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS_ELECTRA, AttestationElectra),
|
|
execution_payload: ExecutionPayloadElectra,
|
|
execution_requests: ExecutionRequests,
|
|
}),
|
|
BeaconState: (0, exports.profile)(StableBeaconState, [], [
|
|
'genesis_time',
|
|
'genesis_validators_root',
|
|
'slot',
|
|
'fork',
|
|
'latest_block_header',
|
|
'block_roots',
|
|
'state_roots',
|
|
'historical_roots',
|
|
'eth1_data',
|
|
'eth1_data_votes',
|
|
'eth1_deposit_index',
|
|
'validators',
|
|
'balances',
|
|
'randao_mixes',
|
|
'slashings',
|
|
'previous_epoch_participation',
|
|
'current_epoch_participation',
|
|
'justification_bits',
|
|
'previous_justified_checkpoint',
|
|
'current_justified_checkpoint',
|
|
'finalized_checkpoint',
|
|
'inactivity_scores',
|
|
'current_sync_committee',
|
|
'next_sync_committee',
|
|
'latest_execution_payload_header',
|
|
'next_withdrawal_index',
|
|
'next_withdrawal_validator_index',
|
|
'historical_summaries',
|
|
'deposit_requests_start_index',
|
|
'deposit_balance_to_consume',
|
|
'exit_balance_to_consume',
|
|
'earliest_exit_epoch',
|
|
'consolidation_balance_to_consume',
|
|
'earliest_consolidation_epoch',
|
|
'pending_balance_deposits',
|
|
'pending_partial_withdrawals',
|
|
'pending_consolidations',
|
|
], {
|
|
latest_execution_payload_header: ExecutionPayloadHeaderElectra,
|
|
}),
|
|
},
|
|
};
|
|
/** Capella Types */
|
|
exports.CapellaExecutionPayloadHeader = (0, exports.container)({
|
|
parent_hash: exports.ETH2_TYPES.Hash32,
|
|
fee_recipient: exports.ETH2_TYPES.ExecutionAddress,
|
|
state_root: exports.ETH2_TYPES.Bytes32,
|
|
receipts_root: exports.ETH2_TYPES.Bytes32,
|
|
logs_bloom: (0, exports.bytevector)(BYTES_PER_LOGS_BLOOM),
|
|
prev_randao: exports.ETH2_TYPES.Bytes32,
|
|
block_number: exports.uint64,
|
|
gas_limit: exports.uint64,
|
|
gas_used: exports.uint64,
|
|
timestamp: exports.uint64,
|
|
extra_data: (0, exports.bytelist)(MAX_EXTRA_DATA_BYTES),
|
|
base_fee_per_gas: exports.uint256,
|
|
block_hash: exports.ETH2_TYPES.Hash32,
|
|
transactions_root: exports.ETH2_TYPES.Root,
|
|
withdrawals_root: exports.ETH2_TYPES.Root,
|
|
});
|
|
const CapellaBeaconBlockBody = (0, exports.container)({
|
|
randao_reveal: exports.ETH2_TYPES.BLSSignature,
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
graffiti: exports.ETH2_TYPES.Bytes32,
|
|
proposer_slashings: (0, exports.list)(MAX_PROPOSER_SLASHINGS, exports.ETH2_TYPES.ProposerSlashing),
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS, exports.ETH2_TYPES.AttesterSlashing),
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS, exports.ETH2_TYPES.Attestation),
|
|
deposits: (0, exports.list)(MAX_DEPOSITS, exports.ETH2_TYPES.Deposit),
|
|
voluntary_exits: (0, exports.list)(MAX_VOLUNTARY_EXITS, exports.ETH2_TYPES.SignedVoluntaryExit),
|
|
sync_aggregate: exports.ETH2_TYPES.SyncAggregate,
|
|
execution_payload: exports.CapellaExecutionPayloadHeader,
|
|
bls_to_execution_changes: (0, exports.list)(MAX_BLS_TO_EXECUTION_CHANGES, exports.ETH2_TYPES.SignedBLSToExecutionChange),
|
|
});
|
|
exports.CapellaBeaconBlock = (0, exports.container)({
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
proposer_index: exports.ETH2_TYPES.ValidatorIndex,
|
|
parent_root: exports.ETH2_TYPES.Root,
|
|
state_root: exports.ETH2_TYPES.Root,
|
|
body: CapellaBeaconBlockBody,
|
|
});
|
|
exports.CapellaSignedBeaconBlock = (0, exports.container)({
|
|
message: exports.CapellaBeaconBlock,
|
|
signature: exports.ETH2_TYPES.BLSSignature,
|
|
});
|
|
exports.CapellaBeaconState = (0, exports.container)({
|
|
genesis_time: exports.uint64,
|
|
genesis_validators_root: exports.ETH2_TYPES.Root,
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
fork: exports.ETH2_TYPES.Fork,
|
|
latest_block_header: exports.ETH2_TYPES.BeaconBlockHeader,
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
historical_roots: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, exports.ETH2_TYPES.Root),
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
eth1_data_votes: (0, exports.list)(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH, exports.ETH2_TYPES.Eth1Data),
|
|
eth1_deposit_index: exports.uint64,
|
|
validators: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Validator),
|
|
balances: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Gwei),
|
|
randao_mixes: (0, exports.vector)(EPOCHS_PER_HISTORICAL_VECTOR, exports.ETH2_TYPES.Bytes32),
|
|
slashings: (0, exports.vector)(EPOCHS_PER_SLASHINGS_VECTOR, exports.ETH2_TYPES.Gwei),
|
|
previous_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
current_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
justification_bits: (0, exports.bitvector)(JUSTIFICATION_BITS_LENGTH),
|
|
previous_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
current_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
finalized_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
inactivity_scores: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.uint64),
|
|
current_sync_committee: exports.ETH2_TYPES.SyncCommittee,
|
|
next_sync_committee: exports.ETH2_TYPES.SyncCommittee,
|
|
latest_execution_payload_header: exports.CapellaExecutionPayloadHeader,
|
|
next_withdrawal_index: exports.uint64,
|
|
next_withdrawal_validator_index: exports.uint64,
|
|
historical_summaries: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, exports.ETH2_TYPES.HistoricalSummary),
|
|
});
|
|
/** Bellatrix Types */
|
|
exports.BellatrixExecutionPayloadHeader = (0, exports.container)({
|
|
parent_hash: exports.ETH2_TYPES.Hash32,
|
|
fee_recipient: exports.ETH2_TYPES.ExecutionAddress,
|
|
state_root: exports.ETH2_TYPES.Bytes32,
|
|
receipts_root: exports.ETH2_TYPES.Bytes32,
|
|
logs_bloom: (0, exports.bytevector)(BYTES_PER_LOGS_BLOOM),
|
|
prev_randao: exports.ETH2_TYPES.Bytes32,
|
|
block_number: exports.uint64,
|
|
gas_limit: exports.uint64,
|
|
gas_used: exports.uint64,
|
|
timestamp: exports.uint64,
|
|
extra_data: (0, exports.bytelist)(MAX_EXTRA_DATA_BYTES),
|
|
base_fee_per_gas: exports.uint256,
|
|
block_hash: exports.ETH2_TYPES.Hash32,
|
|
transactions_root: exports.ETH2_TYPES.Root,
|
|
});
|
|
const BellatrixBeaconBlockBody = (0, exports.container)({
|
|
randao_reveal: exports.ETH2_TYPES.BLSSignature,
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
graffiti: exports.ETH2_TYPES.Bytes32,
|
|
proposer_slashings: (0, exports.list)(MAX_PROPOSER_SLASHINGS, exports.ETH2_TYPES.ProposerSlashing),
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS, exports.ETH2_TYPES.AttesterSlashing),
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS, exports.ETH2_TYPES.Attestation),
|
|
deposits: (0, exports.list)(MAX_DEPOSITS, exports.ETH2_TYPES.Deposit),
|
|
voluntary_exits: (0, exports.list)(MAX_VOLUNTARY_EXITS, exports.ETH2_TYPES.SignedVoluntaryExit),
|
|
sync_aggregate: exports.ETH2_TYPES.SyncAggregate,
|
|
execution_payload: exports.BellatrixExecutionPayloadHeader,
|
|
bls_to_execution_changes: (0, exports.list)(MAX_BLS_TO_EXECUTION_CHANGES, exports.ETH2_TYPES.SignedBLSToExecutionChange),
|
|
});
|
|
exports.BellatrixBeaconBlock = (0, exports.container)({
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
proposer_index: exports.ETH2_TYPES.ValidatorIndex,
|
|
parent_root: exports.ETH2_TYPES.Root,
|
|
state_root: exports.ETH2_TYPES.Root,
|
|
body: BellatrixBeaconBlockBody,
|
|
});
|
|
exports.BellatrixSignedBeaconBlock = (0, exports.container)({
|
|
message: exports.BellatrixBeaconBlock,
|
|
signature: exports.ETH2_TYPES.BLSSignature,
|
|
});
|
|
exports.BellatrixBeaconState = (0, exports.container)({
|
|
genesis_time: exports.uint64,
|
|
genesis_validators_root: exports.ETH2_TYPES.Root,
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
fork: exports.ETH2_TYPES.Fork,
|
|
latest_block_header: exports.ETH2_TYPES.BeaconBlockHeader,
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
historical_roots: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, exports.ETH2_TYPES.Root),
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
eth1_data_votes: (0, exports.list)(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH, exports.ETH2_TYPES.Eth1Data),
|
|
eth1_deposit_index: exports.uint64,
|
|
validators: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Validator),
|
|
balances: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Gwei),
|
|
randao_mixes: (0, exports.vector)(EPOCHS_PER_HISTORICAL_VECTOR, exports.ETH2_TYPES.Bytes32),
|
|
slashings: (0, exports.vector)(EPOCHS_PER_SLASHINGS_VECTOR, exports.ETH2_TYPES.Gwei),
|
|
previous_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
current_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
justification_bits: (0, exports.bitvector)(JUSTIFICATION_BITS_LENGTH),
|
|
previous_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
current_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
finalized_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
inactivity_scores: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.uint64),
|
|
current_sync_committee: exports.ETH2_TYPES.SyncCommittee,
|
|
next_sync_committee: exports.ETH2_TYPES.SyncCommittee,
|
|
latest_execution_payload_header: exports.BellatrixExecutionPayloadHeader,
|
|
});
|
|
/** Altair Types */
|
|
const AltairBeaconBlockBody = (0, exports.container)({
|
|
randao_reveal: exports.ETH2_TYPES.BLSSignature,
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
graffiti: exports.ETH2_TYPES.Bytes32,
|
|
proposer_slashings: (0, exports.list)(MAX_PROPOSER_SLASHINGS, exports.ETH2_TYPES.ProposerSlashing),
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS, exports.ETH2_TYPES.AttesterSlashing),
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS, exports.ETH2_TYPES.Attestation),
|
|
deposits: (0, exports.list)(MAX_DEPOSITS, exports.ETH2_TYPES.Deposit),
|
|
voluntary_exits: (0, exports.list)(MAX_VOLUNTARY_EXITS, exports.ETH2_TYPES.SignedVoluntaryExit),
|
|
sync_aggregate: exports.ETH2_TYPES.SyncAggregate,
|
|
});
|
|
exports.AltairBeaconBlock = (0, exports.container)({
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
proposer_index: exports.ETH2_TYPES.ValidatorIndex,
|
|
parent_root: exports.ETH2_TYPES.Root,
|
|
state_root: exports.ETH2_TYPES.Root,
|
|
body: AltairBeaconBlockBody,
|
|
});
|
|
exports.AltairSignedBeaconBlock = (0, exports.container)({
|
|
message: exports.AltairBeaconBlock,
|
|
signature: exports.ETH2_TYPES.BLSSignature,
|
|
});
|
|
exports.AltairBeaconState = (0, exports.container)({
|
|
genesis_time: exports.uint64,
|
|
genesis_validators_root: exports.ETH2_TYPES.Root,
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
fork: exports.ETH2_TYPES.Fork,
|
|
latest_block_header: exports.ETH2_TYPES.BeaconBlockHeader,
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
historical_roots: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, exports.ETH2_TYPES.Root),
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
eth1_data_votes: (0, exports.list)(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH, exports.ETH2_TYPES.Eth1Data),
|
|
eth1_deposit_index: exports.uint64,
|
|
validators: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Validator),
|
|
balances: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Gwei),
|
|
randao_mixes: (0, exports.vector)(EPOCHS_PER_HISTORICAL_VECTOR, exports.ETH2_TYPES.Bytes32),
|
|
slashings: (0, exports.vector)(EPOCHS_PER_SLASHINGS_VECTOR, exports.ETH2_TYPES.Gwei),
|
|
previous_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
current_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
justification_bits: (0, exports.bitvector)(JUSTIFICATION_BITS_LENGTH),
|
|
previous_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
current_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
finalized_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
inactivity_scores: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.uint64),
|
|
current_sync_committee: exports.ETH2_TYPES.SyncCommittee,
|
|
next_sync_committee: exports.ETH2_TYPES.SyncCommittee,
|
|
});
|
|
/** Phase0 Types */
|
|
const Phase0BeaconBlockBody = (0, exports.container)({
|
|
randao_reveal: exports.ETH2_TYPES.BLSSignature,
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
graffiti: exports.ETH2_TYPES.Bytes32,
|
|
proposer_slashings: (0, exports.list)(MAX_PROPOSER_SLASHINGS, exports.ETH2_TYPES.ProposerSlashing),
|
|
attester_slashings: (0, exports.list)(MAX_ATTESTER_SLASHINGS, exports.ETH2_TYPES.AttesterSlashing),
|
|
attestations: (0, exports.list)(MAX_ATTESTATIONS, exports.ETH2_TYPES.Attestation),
|
|
deposits: (0, exports.list)(MAX_DEPOSITS, exports.ETH2_TYPES.Deposit),
|
|
voluntary_exits: (0, exports.list)(MAX_VOLUNTARY_EXITS, exports.ETH2_TYPES.SignedVoluntaryExit),
|
|
});
|
|
exports.Phase0BeaconBlock = (0, exports.container)({
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
proposer_index: exports.ETH2_TYPES.ValidatorIndex,
|
|
parent_root: exports.ETH2_TYPES.Root,
|
|
state_root: exports.ETH2_TYPES.Root,
|
|
body: Phase0BeaconBlockBody,
|
|
});
|
|
exports.Phase0SignedBeaconBlock = (0, exports.container)({
|
|
message: exports.Phase0BeaconBlock,
|
|
signature: exports.ETH2_TYPES.BLSSignature,
|
|
});
|
|
exports.Phase0BeaconState = (0, exports.container)({
|
|
genesis_time: exports.uint64,
|
|
genesis_validators_root: exports.ETH2_TYPES.Root,
|
|
slot: exports.ETH2_TYPES.Slot,
|
|
fork: exports.ETH2_TYPES.Fork,
|
|
latest_block_header: exports.ETH2_TYPES.BeaconBlockHeader,
|
|
block_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
state_roots: (0, exports.vector)(SLOTS_PER_HISTORICAL_ROOT, exports.ETH2_TYPES.Root),
|
|
historical_roots: (0, exports.list)(HISTORICAL_ROOTS_LIMIT, exports.ETH2_TYPES.Root),
|
|
eth1_data: exports.ETH2_TYPES.Eth1Data,
|
|
eth1_data_votes: (0, exports.list)(EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH, exports.ETH2_TYPES.Eth1Data),
|
|
eth1_deposit_index: exports.uint64,
|
|
validators: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Validator),
|
|
balances: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.Gwei),
|
|
randao_mixes: (0, exports.vector)(EPOCHS_PER_HISTORICAL_VECTOR, exports.ETH2_TYPES.Bytes32),
|
|
slashings: (0, exports.vector)(EPOCHS_PER_SLASHINGS_VECTOR, exports.ETH2_TYPES.Gwei),
|
|
previous_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
current_epoch_participation: (0, exports.list)(VALIDATOR_REGISTRY_LIMIT, exports.ETH2_TYPES.ParticipationFlags),
|
|
justification_bits: (0, exports.bitvector)(JUSTIFICATION_BITS_LENGTH),
|
|
previous_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
current_justified_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
finalized_checkpoint: exports.ETH2_TYPES.Checkpoint,
|
|
});
|
|
//# sourceMappingURL=ssz.js.map
|