Skip to content

Commit

Permalink
publishBlockWithBlobs implementation. A lot more moon math scaffoldin…
Browse files Browse the repository at this point in the history
…g that will be removed.
  • Loading branch information
dgcoffman committed Nov 7, 2022
1 parent d1e3e4b commit 28e9f8a
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 20 deletions.
29 changes: 21 additions & 8 deletions packages/beacon-node/src/api/impl/beacon/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ export function getBeaconBlockApi({
network,
db,
}: Pick<ApiModules, "chain" | "config" | "metrics" | "network" | "db">): routes.beacon.block.Api {
const waitForSlot = async (slot: number): Promise<void> => {
// Simple implementation of a pending block queue. Keeping the block here recycles the API logic, and keeps the
// REST request promise without any extra infrastructure.
const msToBlockSlot = computeTimeAtSlot(config, slot, chain.genesisTime) * 1000 - Date.now();
if (msToBlockSlot <= MAX_API_CLOCK_DISPARITY_MS && msToBlockSlot > 0) {
// If block is a bit early, hold it in a promise. Equivalent to a pending queue.
await sleep(msToBlockSlot);
}
};

return {
async getBlockHeaders(filters) {
// TODO - SLOW CODE: This code seems like it could be improved
Expand Down Expand Up @@ -173,14 +183,7 @@ export function getBeaconBlockApi({

async publishBlock(signedBlock) {
const seenTimestampSec = Date.now() / 1000;

// Simple implementation of a pending block queue. Keeping the block here recycles the API logic, and keeps the
// REST request promise without any extra infrastructure.
const msToBlockSlot = computeTimeAtSlot(config, signedBlock.message.slot, chain.genesisTime) * 1000 - Date.now();
if (msToBlockSlot <= MAX_API_CLOCK_DISPARITY_MS && msToBlockSlot > 0) {
// If block is a bit early, hold it in a promise. Equivalent to a pending queue.
await sleep(msToBlockSlot);
}
await waitForSlot(signedBlock.message.slot);

// TODO: Validate block

Expand All @@ -199,5 +202,15 @@ export function getBeaconBlockApi({
}),
]);
},

async publishBlockWithBlobs(signedBeaconBlockAndBlobsSidecar) {
const {message} = signedBeaconBlockAndBlobsSidecar.beaconBlock;
const seenTimestampSec = Date.now() / 1000;
await waitForSlot(message.slot);

metrics?.registerBeaconBlock(OpSource.api, seenTimestampSec, message);

await Promise.all([network.gossip.publishSignedBeaconBlockAndBlobsSidecar(signedBeaconBlockAndBlobsSidecar)]);
},
};
}
11 changes: 10 additions & 1 deletion packages/beacon-node/src/network/gossip/gossipsub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {allForks, altair, phase0} from "@lodestar/types";
import {ILogger, Map2d, Map2dArr} from "@lodestar/utils";
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";

import {SignedBeaconBlockAndBlobsSidecar} from "@lodestar/types/eip4844";
import {IMetrics} from "../../metrics/index.js";
import {Eth2Context} from "../../chain/index.js";
import {PeersData} from "../peers/peersData.js";
Expand Down Expand Up @@ -198,10 +199,18 @@ export class Eth2Gossipsub extends GossipSub {
async publishBeaconBlock(signedBlock: allForks.SignedBeaconBlock): Promise<void> {
const fork = this.config.getForkName(signedBlock.message.slot);

// TODO: For EIP-4844, switch this to GossipType.beacon_block_and_blobs_sidecar
await this.publishObject<GossipType.beacon_block>({type: GossipType.beacon_block, fork}, signedBlock);
}

async publishSignedBeaconBlockAndBlobsSidecar(blockWithBlobs: SignedBeaconBlockAndBlobsSidecar): Promise<void> {
const fork = this.config.getForkName(blockWithBlobs.beaconBlock.message.slot);

await this.publishObject<GossipType.beacon_block_and_blobs_sidecar>(
{type: GossipType.beacon_block_and_blobs_sidecar, fork},
blockWithBlobs
);
}

async publishBeaconAggregateAndProof(aggregateAndProof: phase0.SignedAggregateAndProof): Promise<number> {
const fork = this.config.getForkName(aggregateAndProof.message.aggregate.data.slot);
return await this.publishObject<GossipType.beacon_aggregate_and_proof>(
Expand Down
9 changes: 0 additions & 9 deletions packages/types/src/eip4844/sszTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@ export const BLSFieldElement = Bytes32;
export const KZGCommitment = Bytes48;
export const KZGProof = Bytes48;

// Constants
// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#constants

// Scalar field modulus of BLS12-381
export const BLS_MODULUS = BigInt("52435875175126190479447740508185965837690552500527637822603658699938581184513");

// Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field
export const ROOTS_OF_UNITY = new ListCompositeType(BLSFieldElement, FIELD_ELEMENTS_PER_BLOB);

// Beacon chain

// Custom types
Expand Down
7 changes: 5 additions & 2 deletions packages/validator/src/services/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {SignedBeaconBlockAndBlobsSidecar} from "@lodestar/types/eip4844/sszTypes
import {IClock, ILoggerVc} from "../util/index.js";
import {PubkeyHex} from "../types.js";
import {Metrics} from "../metrics.js";
import {getBlobsSidecar} from "../util/polynomialCommitments.js";
import {getBlobsSidecar} from "../util/blobs/polynomialCommitments.js";
import {ValidatorStore} from "./validatorStore.js";
import {BlockDutiesService, GENESIS_SLOT} from "./blockDuties.js";

Expand Down Expand Up @@ -101,8 +101,9 @@ export class BlockProposingService {

if (this.config.getForkSeq(block.slot) >= ForkSeq.eip4844) {
const signedBlockWithBlobs = SignedBeaconBlockAndBlobsSidecar.defaultValue();
signedBlockWithBlobs.beaconBlock = signedBlock;
signedBlockWithBlobs.beaconBlock = signedBlock as eip4844.SignedBeaconBlock;
signedBlockWithBlobs.blobsSidecar = getBlobsSidecar(this.config, block, blobs);

// TODO EIP-4844: Blinded blocks??? No clue!
await this.api.beacon.publishBlockWithBlobs(signedBlockWithBlobs).catch(onPublishError);
} else {
Expand Down Expand Up @@ -153,6 +154,8 @@ export class BlockProposingService {

const blindedBlock = await blindedBlockPromise;
const fullBlock = await fullBlockPromise;

// TODO EIP-4844 How do I get blobs here???
const blobs: eip4844.Blobs = [];

// A metric on the choice between blindedBlock and normal block can be applied
Expand Down
54 changes: 54 additions & 0 deletions packages/validator/src/util/blobs/bitReversalPermutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#bit-reversal-permutation

import {FIELD_ELEMENTS_PER_BLOB} from "@lodestar/params";

/*
All polynomials (which are always given in Lagrange form) should be interpreted as being in bit-reversal permutation. In practice, clients can implement this by storing the lists KZG_SETUP_LAGRANGE and ROOTS_OF_UNITY in bit-reversal permutation, so these functions only have to be called once at startup.
*/

// def is_power_of_two(value: int) -> bool:
// """
// Check if ``value`` is a power of two integer.
// """
// return (value > 0) and (value & (value - 1) == 0)
function isPowerOfTwo(value: number): boolean {
return value > 0 && !(value & (value - 1));
}

// def reverse_bits(n: int, order: int) -> int:
// """
// Reverse the bit order of an integer n
// """
// assert is_power_of_two(order)
// # Convert n to binary with the same number of bits as "order" - 1, then reverse its bit order
// return int(('{:0' + str(order.bit_length() - 1) + 'b}').format(n)[::-1], 2)
function reverseBits(n: number, _order: number): number {
// TODO: EIP-4844 implement this
// Prysm does:
// bits.Reverse64(uint64(i)) >> (65 - bits.Len64(params.FieldElementsPerBlob))
return n;
}

// def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]:
// """
// Return a copy with bit-reversed permutation. The permutation is an involution (inverts itself).

// The input and output are a sequence of generic type ``T`` objects.
// """
// return [sequence[reverse_bits(i, len(sequence))] for i in range(len(sequence))]
export function bitReversalPermutation<T>(sequence: T[]): T[] {
if (!isPowerOfTwo(FIELD_ELEMENTS_PER_BLOB)) {
throw new Error(
`FIELD_ELEMENTS_PER_BLOB must be a power of two. The value FIELD_ELEMENTS_PER_BLOB: ${FIELD_ELEMENTS_PER_BLOB} is not.`
);
}

const order = sequence.length;
const permutedSequence: T[] = [];

sequence.forEach((_, index: number) => {
permutedSequence[index] = sequence[reverseBits(index, order)];
});

return permutedSequence;
}
71 changes: 71 additions & 0 deletions packages/validator/src/util/blobs/bls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#bls12-381-helpers

import {KZGCommitment, BLSFieldElement} from "@lodestar/types/eip4844";
import * as ssz from "@lodestar/types/eip4844/sszTypes";
import {Bytes32} from "@lodestar/types";
import {bytesToBigInt, intToBytes, bigIntToBytes} from "@lodestar/utils";
import {BLS_MODULUS} from "./constants.js";

// def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
// """
// Convert bytes to a BLS field scalar. The output is not uniform over the BLS field.
// """
// return int.from_bytes(b, "little") % BLS_MODULUS
export function bytesToBLSField(b: Bytes32): BLSFieldElement {
return intToBytes(bytesToBigInt(b) % BLS_MODULUS, 32);
}

// def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement:
// """
// Compute the modular inverse of x
// i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0
// """
// return pow(x, -1, BLS_MODULUS) if x != 0 else 0
export function blsModularInverse(x: BLSFieldElement): BLSFieldElement {
// Maybe BLSFieldElement should actually just be "number"?
const value = bytesToBigInt(x);
if (value !== BigInt(0)) {
return intToBytes(value ** BigInt(-1) % BLS_MODULUS, 32);
}
return intToBytes(0, 32);
}

// def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement:
// """Divide two field elements: `x` by `y`"""
// return (int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS
export function div(x: BLSFieldElement, y: BLSFieldElement): BLSFieldElement {
const product = bytesToBigInt(x) * bytesToBigInt(blsModularInverse(y));
return bigIntToBytes(product % BLS_MODULUS, 32);
}

// def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElement]) -> KZGCommitment:
// """
// BLS multiscalar multiplication. This function can be optimized using Pippenger's algorithm and variants.
// """
// assert len(points) == len(scalars)
// result = bls.Z1
// for x, a in zip(points, scalars):
// result = bls.add(result, bls.multiply(bls.bytes48_to_G1(x), a))
// return KZGCommitment(bls.G1_to_bytes48(result))
export function g1Lincomb(points: KZGCommitment[], scalars: BLSFieldElement[]): KZGCommitment {
if (points.length !== scalars.length) {
throw new Error("BLS multiscalar multiplication requires points length to match scalars length.");
}

return ssz.KZGCommitment.defaultValue();
}

// def vector_lincomb(vectors: Sequence[Sequence[BLSFieldElement]],
// scalars: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]:
// """
// Given a list of ``vectors``, interpret it as a 2D matrix and compute the linear combination
// of each column with `scalars`: return the resulting vector.
// """
// result = [0] * len(vectors[0])
// for v, s in zip(vectors, scalars):
// for i, x in enumerate(v):
// result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS
// return [BLSFieldElement(x) for x in result]
export function vectorLincomb(_vectors: BLSFieldElement[][], _scalars: BLSFieldElement[]): BLSFieldElement[] {
return [];
}
12 changes: 12 additions & 0 deletions packages/validator/src/util/blobs/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Constants
// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/polynomial-commitments.md#constants

import {KZGCommitment} from "@lodestar/types/eip4844";
import {bitReversalPermutation} from "./bitReversalPermutation.js";

// Scalar field modulus of BLS12-381
export const BLS_MODULUS = BigInt("52435875175126190479447740508185965837690552500527637822603658699938581184513");

// Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field
export const ROOTS_OF_UNITY: KZGCommitment[] = [];
export const KZG_SETUP_LAGRANGE = bitReversalPermutation(ROOTS_OF_UNITY);
Loading

0 comments on commit 28e9f8a

Please sign in to comment.