Skip to content

Commit

Permalink
feat: initial validator set
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Aug 27, 2024
1 parent 1323b2e commit d7cab93
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 58 deletions.
9 changes: 7 additions & 2 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
IAvailabilityOracle _availabilityOracle,
IFeeJuicePortal _fpcJuicePortal,
bytes32 _vkTreeRoot,
address _ares
address _ares,
address[] memory _validators
) Leonidas(_ares) {
verifier = new MockVerifier();
REGISTRY = _registry;
Expand All @@ -93,6 +94,10 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
BlockLog({archive: bytes32(0), blockHash: bytes32(0), slotNumber: 0, isProven: true});
pendingBlockCount = 1;
provenBlockCount = 1;

for (uint256 i = 0; i < _validators.length; i++) {
_addValidator(_validators[i]);
}
}

/**
Expand Down Expand Up @@ -520,7 +525,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
}

if (!isValidator(msg.sender)) {
revert Errors.Leonidas__InvalidProposer(address(0), msg.sender);
revert Errors.Leonidas__InvalidProposer(getValidatorAt(0), msg.sender);
}
return;
}
Expand Down
1 change: 1 addition & 0 deletions l1-contracts/src/core/sequencer_selection/ILeonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface ILeonidas {
function getCurrentSlot() external view returns (uint256);
function isValidator(address _validator) external view returns (bool);
function getValidatorCount() external view returns (uint256);
function getValidatorAt(uint256 _index) external view returns (address);

// Consider removing below this point
function getTimestampForSlot(uint256 _slotNumber) external view returns (uint256);
Expand Down
19 changes: 18 additions & 1 deletion l1-contracts/src/core/sequencer_selection/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ contract Leonidas is Ownable, ILeonidas {
*/
function addValidator(address _validator) external override(ILeonidas) onlyOwner {
setupEpoch();
validatorSet.add(_validator);
_addValidator(_validator);
}

/**
Expand Down Expand Up @@ -189,6 +189,15 @@ contract Leonidas is Ownable, ILeonidas {
return validatorSet.length();
}

/**
* @notice Get the number of validators in the validator set
*
* @return The number of validators in the validator set
*/
function getValidatorAt(uint256 _index) public view override(ILeonidas) returns (address) {
return validatorSet.at(_index);
}

/**
* @notice Checks if an address is in the validator set
*
Expand Down Expand Up @@ -320,6 +329,14 @@ contract Leonidas is Ownable, ILeonidas {
return committee[_computeProposerIndex(epochNumber, slot, sampleSeed, committee.length)];
}

/**
* @notice Adds a validator to the set WITHOUT setting up the epoch
* @param _validator - The validator to add
*/
function _addValidator(address _validator) internal {
validatorSet.add(_validator);
}

/**
* @notice Process a pending block from the point-of-view of sequencer selection. Will:
* - Setup the epoch if needed (if epoch committee is empty skips the rest)
Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ contract RollupTest is DecoderBase {
availabilityOracle,
IFeeJuicePortal(address(feeJuicePortal)),
bytes32(0),
address(this)
address(this),
new address[](0)
);
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));
Expand Down
7 changes: 6 additions & 1 deletion l1-contracts/test/portals/TokenPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ contract TokenPortalTest is Test {
registry = new Registry(address(this));
portalERC20 = new PortalERC20();
rollup = new Rollup(
registry, new AvailabilityOracle(), IFeeJuicePortal(address(0)), bytes32(0), address(this)
registry,
new AvailabilityOracle(),
IFeeJuicePortal(address(0)),
bytes32(0),
address(this),
new address[](0)
);
inbox = rollup.INBOX();
outbox = rollup.OUTBOX();
Expand Down
7 changes: 6 additions & 1 deletion l1-contracts/test/portals/UniswapPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ contract UniswapPortalTest is Test {

registry = new Registry(address(this));
rollup = new Rollup(
registry, new AvailabilityOracle(), IFeeJuicePortal(address(0)), bytes32(0), address(this)
registry,
new AvailabilityOracle(),
IFeeJuicePortal(address(0)),
bytes32(0),
address(this),
new address[](0)
);
registry.upgrade(address(rollup));

Expand Down
11 changes: 9 additions & 2 deletions l1-contracts/test/sparta/DevNet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ contract DevNetTest is DecoderBase {
registry = new Registry(address(this));
availabilityOracle = new AvailabilityOracle();
rollup = new Rollup(
registry, availabilityOracle, IFeeJuicePortal(address(0)), bytes32(0), address(this)
registry,
availabilityOracle,
IFeeJuicePortal(address(0)),
bytes32(0),
address(this),
new address[](0)
);
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));
Expand Down Expand Up @@ -160,7 +165,9 @@ contract DevNetTest is DecoderBase {
ree.proposer = address(uint160(uint256(keccak256(abi.encode("invalid", ree.proposer)))));
// Why don't we end up here?
vm.expectRevert(
abi.encodeWithSelector(Errors.Leonidas__InvalidProposer.selector, address(0), ree.proposer)
abi.encodeWithSelector(
Errors.Leonidas__InvalidProposer.selector, rollup.getValidatorAt(0), ree.proposer
)
);
ree.shouldRevert = true;
}
Expand Down
7 changes: 6 additions & 1 deletion l1-contracts/test/sparta/Sparta.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ contract SpartaTest is DecoderBase {
availabilityOracle = new AvailabilityOracle();
portalERC20 = new PortalERC20();
rollup = new Rollup(
registry, availabilityOracle, IFeeJuicePortal(address(0)), bytes32(0), address(this)
registry,
availabilityOracle,
IFeeJuicePortal(address(0)),
bytes32(0),
address(this),
new address[](0)
);
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));
Expand Down
70 changes: 24 additions & 46 deletions yarn-project/end-to-end/src/e2e_p2p_network.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
import { getSchnorrAccount } from '@aztec/accounts/schnorr';
import { type AztecNodeConfig, type AztecNodeService } from '@aztec/aztec-node';
import {
CompleteAddress,
type DebugLogger,
type DeployL1Contracts,
EthCheatCodes,
Fr,
GrumpkinScalar,
type SentTx,
TxStatus,
sleep,
} from '@aztec/aztec.js';
import { IS_DEV_NET } from '@aztec/circuits.js';
import { RollupAbi } from '@aztec/l1-artifacts';
import { CompleteAddress, type DebugLogger, Fr, GrumpkinScalar, type SentTx, TxStatus, sleep } from '@aztec/aztec.js';
import { EthAddress, IS_DEV_NET } from '@aztec/circuits.js';
import { type BootstrapNode } from '@aztec/p2p';
import { type PXEService, createPXEService, getPXEServiceConfig as getRpcConfig } from '@aztec/pxe';

import fs from 'fs';
import { getContract } from 'viem';
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';

import { MNEMONIC } from './fixtures/fixtures.js';
Expand All @@ -44,44 +32,34 @@ describe('e2e_p2p_network', () => {
let teardown: () => Promise<void>;
let bootstrapNode: BootstrapNode;
let bootstrapNodeEnr: string;
let deployL1ContractsValues: DeployL1Contracts;

beforeEach(async () => {
({ teardown, config, logger, deployL1ContractsValues } = await setup(0));
// It would likely be useful if we had the sequencers in such that they don't spam each other.
// However, even if they do, it should still work. Not sure what caused the failure
// Would be easier if I could see the errors from anvil as well, but those seem to be hidden.

const rollup = getContract({
address: deployL1ContractsValues.l1ContractAddresses.rollupAddress.toString(),
abi: RollupAbi,
client: deployL1ContractsValues.walletClient,
});

if (IS_DEV_NET) {
// Add just ONE of the peers as sequencer, he will be the proposer all blocks.
const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 1 });
// If we want to test with interval mining, we can use the local host and start `anvil --block-time 12`
const useLocalHost = false;
if (useLocalHost) {
jest.setTimeout(300_000);
}
const options = useLocalHost ? { l1RpcUrl: 'http://127.0.0.1:8545' } : {};

// We need the very first node to be the sequencer for this is the one doing everything throughout the setup.
// Without it we will wait forever.
const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 });
const publisherPrivKey = Buffer.from(hdAccount.getHdKey().privateKey!);
const account = privateKeyToAccount(`0x${publisherPrivKey!.toString('hex')}`);

const initialValidators = [EthAddress.fromString(account.address)];

// Add 1 extra validator if in devnet or NUM_NODES if not.
// Each of these will become a validator and sign attestations.
const limit = IS_DEV_NET ? 1 : NUM_NODES;
for (let i = 0; i < limit; i++) {
const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: i + 1 });
const publisherPrivKey = Buffer.from(hdAccount.getHdKey().privateKey!);
const account = privateKeyToAccount(`0x${publisherPrivKey!.toString('hex')}`);
await rollup.write.addValidator([account.address]);
logger.info(`Adding sequencer ${account.address}`);
} else {
// Add all nodes as validators - they will all sign attestations of each other's proposals
for (let i = 0; i < NUM_NODES; i++) {
const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: i + 1 });
const publisherPrivKey = Buffer.from(hdAccount.getHdKey().privateKey!);
const account = privateKeyToAccount(`0x${publisherPrivKey!.toString('hex')}`);
await rollup.write.addValidator([account.address]);
logger.info(`Adding sequencer ${account.address}`);
}
initialValidators.push(EthAddress.fromString(account.address));
}

// Now we jump ahead to the next epoch, such that the next epoch begins
const timeToJump = (await rollup.read.EPOCH_DURATION()) * (await rollup.read.SLOT_DURATION());

const cheatCodes = new EthCheatCodes(config.l1RpcUrl);
const timestamp = (await cheatCodes.timestamp()) + Number(timeToJump);
await cheatCodes.warp(timestamp);
({ teardown, config, logger } = await setup(0, { initialValidators, ...options }));

bootstrapNode = await createBootstrapNode(BOOT_NODE_UDP_PORT);
bootstrapNodeEnr = bootstrapNode.getENR().encodeTxt();
Expand Down
14 changes: 12 additions & 2 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { type BBNativePrivateKernelProver } from '@aztec/bb-prover';
import {
CANONICAL_AUTH_REGISTRY_ADDRESS,
CANONICAL_KEY_REGISTRY_ADDRESS,
type EthAddress,
GasSettings,
MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
computeContractAddressFromInstance,
Expand Down Expand Up @@ -113,7 +114,7 @@ export const setupL1Contracts = async (
l1RpcUrl: string,
account: HDAccount | PrivateKeyAccount,
logger: DebugLogger,
args: { salt?: number } = {},
args: { salt?: number; initialValidators?: EthAddress[] } = {},
chain: Chain = foundry,
) => {
const l1Artifacts: L1ContractArtifactsForDeployment = {
Expand Down Expand Up @@ -151,6 +152,7 @@ export const setupL1Contracts = async (
l2FeeJuiceAddress: FeeJuiceAddress,
vkTreeRoot: getVKTreeRoot(),
salt: args.salt,
initialValidators: args.initialValidators,
});

return l1Data;
Expand Down Expand Up @@ -295,6 +297,8 @@ type SetupOptions = {
skipProtocolContracts?: boolean;
/** Salt to use in L1 contract deployment */
salt?: number;
/** An initial set of validators */
initialValidators?: EthAddress[];
} & Partial<AztecNodeConfig>;

/** Context for an end-to-end test as returned by the `setup` function */
Expand Down Expand Up @@ -388,7 +392,13 @@ export async function setup(

const deployL1ContractsValues =
opts.deployL1ContractsValues ??
(await setupL1Contracts(config.l1RpcUrl, publisherHdAccount!, logger, { salt: opts.salt }, chain));
(await setupL1Contracts(
config.l1RpcUrl,
publisherHdAccount!,
logger,
{ salt: opts.salt, initialValidators: opts.initialValidators },
chain,
));

config.l1Contracts = deployL1ContractsValues.l1ContractAddresses;

Expand Down
9 changes: 8 additions & 1 deletion yarn-project/ethereum/src/deploy_l1_contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,13 @@ export const deployL1Contracts = async (
chain: Chain,
logger: DebugLogger,
contractsToDeploy: L1ContractArtifactsForDeployment,
args: { l2FeeJuiceAddress: AztecAddress; vkTreeRoot: Fr; assumeProvenUntil?: number; salt: number | undefined },
args: {
l2FeeJuiceAddress: AztecAddress;
vkTreeRoot: Fr;
assumeProvenUntil?: number;
salt: number | undefined;
initialValidators?: EthAddress[];
},
): Promise<DeployL1Contracts> => {
// We are assuming that you are running this on a local anvil node which have 1s block times
// To align better with actual deployment, we update the block interval to 12s
Expand Down Expand Up @@ -233,6 +239,7 @@ export const deployL1Contracts = async (
getAddress(feeJuicePortalAddress.toString()),
args.vkTreeRoot.toString(),
account.address.toString(),
args.initialValidators?.map(v => v.toString()) ?? [],
]);
logger.info(`Deployed Rollup at ${rollupAddress}`);

Expand Down

0 comments on commit d7cab93

Please sign in to comment.