Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions pkg/tasks/generate_batch_deposits/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
## `generate_batch_deposits` Task

### Description
The `generate_batch_deposits` task spams the chain with valid, unique validator deposits by routing them through a small forwarder contract (`BatchDeposit`). Every deposit carries a freshly-derived BLS keypair and a fully-valid signature, which forces the consensus layer through the worst-case per-deposit signature verification path.

If `batchContract` is empty the task deploys a fresh forwarder bound to the configured deposit contract before generation starts and exposes its address via task outputs.

### Use Cases

- Stress-test consensus client deposit signature verification.
- Fill the deposit/pending queue at maximum throughput against a devnet.
- Reproduce edge cases where many deposits land in a single block.

### Configuration Parameters

- **`limitPerSlot`**: Maximum number of deposits to generate per slot. Counted in deposits, not in batches.
- **`limitTotal`**: Total deposits to generate.
- **`limitPendingBatches`**: Maximum number of in-flight batch transactions.
- **`mnemonic`**: Mnemonic phrase used to derive validator keys.
- **`startIndex`**: Index within the mnemonic at which to start key derivation.
- **`indexCount`**: Maximum number of validator keys to derive (an alternative cap to `limitTotal`).
- **`walletPrivkey`**: Private key of the wallet used to send the batch transactions and (if needed) deploy the contract.
- **`depositContract`**: Address of the beacon chain deposit contract.
- **`batchContract`**: Optional address of an already-deployed `BatchDeposit` forwarder. If empty, the task deploys one on start.
- **`batchSize`**: Number of deposits per batched transaction. Default `100`.
- **`batchTxGasLimit`**: Gas limit per batched transaction. Default `12_000_000`.
- **`depositAmount`**: ETH amount per deposit. Default `32` ETH.
- **`depositTxFeeCap`** / **`depositTxTipCap`**: Fee/tip caps (wei) for batch transactions.
- **`withdrawalCredentials`**: Required 32-byte withdrawal credentials shared by every deposit in every batch (e.g. `0x03 + 11 zero bytes + 20-byte address` for builder credentials).
- **`clientPattern`** / **`excludeClientPattern`**: Client selection regexes.
- **`awaitReceipt`**: Wait for every batch transaction receipt before completing.
- **`failOnReject`**: Fail the task if any batch transaction is rejected or reverted.
- **`awaitInclusion`**: Wait for every individual deposit to appear in a beacon block before completing.

### Outputs

- **`batchContract`**: Address of the forwarder contract used (or deployed by) this task.
- **`validatorPubkeys`**: All derived validator pubkeys, in the order they were submitted.
- **`batchTransactions`**: Hashes of the submitted batch transactions.
- **`batchReceipts`**: Receipts for the batch transactions (when `awaitReceipt` is enabled).
- **`includedDeposits`**: Number of deposits confirmed on the beacon chain (when `awaitInclusion` is enabled).

### Defaults

```yaml
- name: generate_batch_deposits
config:
limitPerSlot: 0
limitTotal: 0
limitPendingBatches: 0
mnemonic: ""
startIndex: 0
indexCount: 0
walletPrivkey: ""
depositContract: ""
batchContract: ""
batchSize: 100
batchTxGasLimit: 12000000
depositAmount: 32
depositTxFeeCap: 100000000000
depositTxTipCap: 1000000000
withdrawalCredentials: ""
clientPattern: ""
excludeClientPattern: ""
awaitReceipt: false
failOnReject: false
awaitInclusion: false
```
54 changes: 54 additions & 0 deletions pkg/tasks/generate_batch_deposits/batch_contract/BatchDeposit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IDepositContract {
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
}

/// @notice Forwards a batch of deposits to the beacon-chain deposit contract in a single transaction.
/// @dev All deposits in a batch share the same withdrawal credentials and amount.
/// Each deposit must still carry its own pubkey, signature, and data root, so the
/// consensus layer is forced to verify every BLS signature individually.
contract BatchDeposit {
IDepositContract public immutable depositContract;

constructor(address _depositContract) {
require(_depositContract != address(0), "BatchDeposit: zero deposit contract");
depositContract = IDepositContract(_depositContract);
}

/// @param pubkeys Concatenated BLS pubkeys, 48 bytes per deposit.
/// @param signatures Concatenated BLS signatures, 96 bytes per deposit.
/// @param dataRoots One deposit_data_root per deposit.
/// @param withdrawalCredentials Shared 32-byte withdrawal credentials.
/// @param amountWei Amount (in wei) forwarded per deposit. msg.value must equal amountWei * dataRoots.length.
function batchDeposit(
bytes calldata pubkeys,
bytes calldata signatures,
bytes32[] calldata dataRoots,
bytes calldata withdrawalCredentials,
uint256 amountWei
) external payable {
uint256 count = dataRoots.length;
require(count > 0, "BatchDeposit: empty batch");
require(pubkeys.length == count * 48, "BatchDeposit: bad pubkeys length");
require(signatures.length == count * 96, "BatchDeposit: bad signatures length");
require(withdrawalCredentials.length == 32, "BatchDeposit: bad creds length");
require(msg.value == amountWei * count, "BatchDeposit: bad value");

IDepositContract dc = depositContract;
for (uint256 i = 0; i < count; ++i) {
dc.deposit{value: amountWei}(
pubkeys[i * 48:(i + 1) * 48],
withdrawalCredentials,
signatures[i * 96:(i + 1) * 96],
dataRoots[i]
);
}
}
}
Loading
Loading