Skip to content

Commit c8c515e

Browse files
authored
Merge branch 'electra-support' into pk910/validator-lifecycle-test-v3
Signed-off-by: pk910 <philipp@pk910.de>
2 parents ffa61c4 + 54b3a39 commit c8c515e

File tree

5 files changed

+164
-32
lines changed

5 files changed

+164
-32
lines changed

.hack/devnet/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ENCLAVE_NAME="${ENCLAVE_NAME:-assertoor}"
1919
if kurtosis enclave inspect "$ENCLAVE_NAME" > /dev/null; then
2020
echo "Kurtosis enclave '$ENCLAVE_NAME' is already up."
2121
else
22-
kurtosis run github.com/ethpandaops/ethereum-package --enclave "$ENCLAVE_NAME" --args-file "$args_file" --non-blocking-tasks
22+
kurtosis run github.com/ethpandaops/ethereum-package --enclave "$ENCLAVE_NAME" --args-file "$args_file" --non-blocking-tasks --image-download always
2323

2424
# Stop assertoor instance within ethereum-package if running
2525
kurtosis service stop "$ENCLAVE_NAME" assertoor > /dev/null || true

pkg/coordinator/tasks/generate_deposits/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ The `generate_deposits` task focuses on creating deposit transactions and sendin
2323
- **`indexCount`**:\
2424
The total number of validator keys to generate from the mnemonic. This number determines how many unique deposit transactions will be created.
2525

26+
- **`publicKey`**\
27+
The publickey of the validator to generate a deposit for. This is for topup deposits only.
28+
2629
- **`walletPrivkey`**:\
2730
The private key of the wallet from which the deposit will be made. This key is crucial for initiating the deposit transaction.
2831

@@ -41,6 +44,9 @@ The `generate_deposits` task focuses on creating deposit transactions and sendin
4144
- **`withdrawalCredentials`**:\
4245
Specifies the withdrawal credentials for the deposited ETH. If left empty, it defaults to a standard `0x00...` credentials based on the validator mnemonic.
4346

47+
- **`topUpDeposit`**:\
48+
Specifies if the deposit should be a topup deposit (without withdrawal credentials or signature)
49+
4450
- **`clientPattern`**:\
4551
A regex pattern to select specific client endpoints for sending deposit transactions. If left blank, any available endpoint will be used.
4652

@@ -76,12 +82,14 @@ Default settings for the `generate_deposits` task:
7682
mnemonic: ""
7783
startIndex: 0
7884
indexCount: 0
85+
publicKey: ""
7986
walletPrivkey: ""
8087
depositContract: ""
8188
depositAmount: 32
8289
depositTxFeeCap: 100000000000
8390
depositTxTipCap: 1000000000
8491
withdrawalCredentials: ""
92+
topUpDeposit: false
8593
clientPattern: ""
8694
excludeClientPattern: ""
8795
awaitReceipt: false

pkg/coordinator/tasks/generate_deposits/config.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ type Config struct {
1111
Mnemonic string `yaml:"mnemonic" json:"mnemonic"`
1212
StartIndex int `yaml:"startIndex" json:"startIndex"`
1313
IndexCount int `yaml:"indexCount" json:"indexCount"`
14+
PublicKey string `yaml:"publicKey" json:"publicKey"`
1415
WalletPrivkey string `yaml:"walletPrivkey" json:"walletPrivkey"`
1516
DepositContract string `yaml:"depositContract" json:"depositContract"`
1617
DepositAmount uint64 `yaml:"depositAmount" json:"depositAmount"`
1718
DepositTxFeeCap int64 `yaml:"depositTxFeeCap" json:"depositTxFeeCap"`
1819
DepositTxTipCap int64 `yaml:"depositTxTipCap" json:"depositTxTipCap"`
1920
WithdrawalCredentials string `yaml:"withdrawalCredentials" json:"withdrawalCredentials"`
21+
TopUpDeposit bool `yaml:"topUpDeposit" json:"topUpDeposit"`
2022
ClientPattern string `yaml:"clientPattern" json:"clientPattern"`
2123
ExcludeClientPattern string `yaml:"excludeClientPattern" json:"excludeClientPattern"`
2224
AwaitReceipt bool `yaml:"awaitReceipt" json:"awaitReceipt"`
@@ -40,8 +42,13 @@ func (c *Config) Validate() error {
4042
return errors.New("either limitPerSlot or limitTotal or indexCount must be set")
4143
}
4244

43-
if c.Mnemonic == "" {
44-
return errors.New("mnemonic must be set")
45+
switch {
46+
case c.Mnemonic == "" && c.PublicKey == "":
47+
return errors.New("mnemonic or publickey must be set")
48+
case c.Mnemonic != "" && c.PublicKey != "":
49+
return errors.New("only one of mnemonic or publickey must be set")
50+
case c.PublicKey != "" && !c.TopUpDeposit:
51+
return errors.New("publicKey can only be used with topUpDeposit")
4552
}
4653

4754
if c.WalletPrivkey == "" {

pkg/coordinator/tasks/generate_deposits/task.go

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/protolambda/ztyp/tree"
2727
"github.com/sirupsen/logrus"
2828
"github.com/tyler-smith/go-bip39"
29+
e2types "github.com/wealdtech/go-eth2-types/v2"
2930
util "github.com/wealdtech/go-eth2-util"
3031

3132
depositcontract "github.com/ethpandaops/assertoor/pkg/coordinator/tasks/generate_deposits/deposit_contract"
@@ -90,12 +91,14 @@ func (t *Task) LoadConfig() error {
9091
return valerr
9192
}
9293

93-
t.valkeySeed, err = t.mnemonicToSeed(config.Mnemonic)
94-
if err != nil {
95-
return err
96-
}
94+
if config.Mnemonic != "" {
95+
t.valkeySeed, err = t.mnemonicToSeed(config.Mnemonic)
96+
if err != nil {
97+
return err
98+
}
9799

98-
t.logger.Infof("validator key seed: 0x%x", t.valkeySeed)
100+
t.logger.Infof("validator key seed: 0x%x", t.valkeySeed)
101+
}
99102

100103
t.walletPrivKey, err = crypto.HexToECDSA(config.WalletPrivkey)
101104
if err != nil {
@@ -274,20 +277,29 @@ func (t *Task) Execute(ctx context.Context) error {
274277

275278
func (t *Task) generateDeposit(ctx context.Context, accountIdx uint64, onConfirm func(tx *ethtypes.Transaction, receipt *ethtypes.Receipt)) (*common.BLSPubkey, *ethtypes.Transaction, error) {
276279
clientPool := t.ctx.Scheduler.GetServices().ClientPool()
277-
validatorKeyPath := fmt.Sprintf("m/12381/3600/%d/0/0", accountIdx)
278-
withdrAccPath := fmt.Sprintf("m/12381/3600/%d/0", accountIdx)
280+
validatorSet := t.ctx.Scheduler.GetServices().ClientPool().GetConsensusPool().GetValidatorSet()
279281

280-
validatorPrivkey, err := util.PrivateKeyFromSeedAndPath(t.valkeySeed, validatorKeyPath)
281-
if err != nil {
282-
return nil, nil, fmt.Errorf("failed generating validator key %v: %w", validatorKeyPath, err)
283-
}
282+
var validatorPubkey []byte
284283

285-
validatorSet := t.ctx.Scheduler.GetServices().ClientPool().GetConsensusPool().GetValidatorSet()
284+
var validatorPrivkey *e2types.BLSPrivateKey
286285

287-
var validator *v1.Validator
286+
if t.valkeySeed != nil {
287+
validatorKeyPath := fmt.Sprintf("m/12381/3600/%d/0/0", accountIdx)
288+
289+
validatorPriv, err := util.PrivateKeyFromSeedAndPath(t.valkeySeed, validatorKeyPath)
290+
if err != nil {
291+
return nil, nil, fmt.Errorf("failed generating validator key %v: %w", validatorKeyPath, err)
292+
}
288293

289-
validatorPubkey := validatorPrivkey.PublicKey().Marshal()
290-
t.logger.Debugf("generated validator pubkey %v: 0x%x", validatorKeyPath, validatorPubkey)
294+
validatorPrivkey = validatorPriv
295+
296+
validatorPubkey = validatorPrivkey.PublicKey().Marshal()
297+
t.logger.Debugf("generated validator pubkey %v: 0x%x", validatorKeyPath, validatorPubkey)
298+
} else {
299+
validatorPubkey = ethcommon.FromHex(t.config.PublicKey)
300+
}
301+
302+
var validator *v1.Validator
291303

292304
for _, val := range validatorSet {
293305
if bytes.Equal(val.Validator.PublicKey[:], validatorPubkey) {
@@ -296,8 +308,10 @@ func (t *Task) generateDeposit(ctx context.Context, accountIdx uint64, onConfirm
296308
}
297309
}
298310

299-
if validator != nil {
311+
if t.valkeySeed != nil && validator != nil {
300312
t.logger.Warnf("validator already exists on chain (index: %v)", validator.Index)
313+
} else if t.valkeySeed == nil && validator == nil {
314+
t.logger.Warnf("validator not found on chain for topup deposit")
301315
}
302316

303317
var pub common.BLSPubkey
@@ -306,7 +320,12 @@ func (t *Task) generateDeposit(ctx context.Context, accountIdx uint64, onConfirm
306320

307321
copy(pub[:], validatorPubkey)
308322

309-
if t.config.WithdrawalCredentials == "" {
323+
switch {
324+
case t.config.TopUpDeposit:
325+
withdrCreds = ethcommon.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000")
326+
case t.config.WithdrawalCredentials == "":
327+
withdrAccPath := fmt.Sprintf("m/12381/3600/%d/0", accountIdx)
328+
310329
withdrPrivkey, err2 := util.PrivateKeyFromSeedAndPath(t.valkeySeed, withdrAccPath)
311330
if err2 != nil {
312331
return nil, nil, fmt.Errorf("failed generating key %v: %w", withdrAccPath, err2)
@@ -318,7 +337,7 @@ func (t *Task) generateDeposit(ctx context.Context, accountIdx uint64, onConfirm
318337
withdrKeyHash := hashing.Hash(withdrPubKey)
319338
withdrCreds = withdrKeyHash[:]
320339
withdrCreds[0] = common.BLS_WITHDRAWAL_PREFIX
321-
} else {
340+
default:
322341
withdrCreds = ethcommon.FromHex(t.config.WithdrawalCredentials)
323342
}
324343

@@ -328,20 +347,23 @@ func (t *Task) generateDeposit(ctx context.Context, accountIdx uint64, onConfirm
328347
Amount: common.Gwei(t.config.DepositAmount * 1000000000),
329348
Signature: common.BLSSignature{},
330349
}
331-
msgRoot := data.ToMessage().HashTreeRoot(tree.GetHashFn())
332350

333-
var secKey hbls.SecretKey
351+
if !t.config.TopUpDeposit {
352+
msgRoot := data.ToMessage().HashTreeRoot(tree.GetHashFn())
334353

335-
err = secKey.Deserialize(validatorPrivkey.Marshal())
336-
if err != nil {
337-
return nil, nil, fmt.Errorf("cannot convert validator priv key")
338-
}
354+
var secKey hbls.SecretKey
355+
356+
err := secKey.Deserialize(validatorPrivkey.Marshal())
357+
if err != nil {
358+
return nil, nil, fmt.Errorf("cannot convert validator priv key")
359+
}
339360

340-
genesis := clientPool.GetConsensusPool().GetBlockCache().GetGenesis()
341-
dom := common.ComputeDomain(common.DOMAIN_DEPOSIT, common.Version(genesis.GenesisForkVersion), common.Root{})
342-
msg := common.ComputeSigningRoot(msgRoot, dom)
343-
sig := secKey.SignHash(msg[:])
344-
copy(data.Signature[:], sig.Serialize())
361+
genesis := clientPool.GetConsensusPool().GetBlockCache().GetGenesis()
362+
dom := common.ComputeDomain(common.DOMAIN_DEPOSIT, common.Version(genesis.GenesisForkVersion), common.Root{})
363+
msg := common.ComputeSigningRoot(msgRoot, dom)
364+
sig := secKey.SignHash(msg[:])
365+
copy(data.Signature[:], sig.Serialize())
366+
}
345367

346368
dataRoot := data.HashTreeRoot(tree.GetHashFn())
347369

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
id: fillup-deposit-queue
3+
name: "Fillup deposit queue"
4+
timeout: 1h
5+
config:
6+
walletPrivkey: ""
7+
depositCount: 1010
8+
depositMaxIndex: 100
9+
depositContract: "0x4242424242424242424242424242424242424242"
10+
tasks:
11+
- name: check_clients_are_healthy
12+
title: "Check if at least one client is ready"
13+
timeout: 5m
14+
config:
15+
minClientCount: 1
16+
17+
- name: run_shell
18+
id: prepare
19+
title: "Prepare workers"
20+
config:
21+
envVars:
22+
depositCount: "depositCount"
23+
depositMaxIndex: "depositMaxIndex"
24+
command: |
25+
depositCount=$(echo $depositCount | jq -r .)
26+
depositMaxIndex=$(echo $depositMaxIndex | jq -r .)
27+
28+
minDepositCount=$(expr $depositCount \/ $depositMaxIndex)
29+
plusOneDepositCount=$(expr $depositCount - $minDepositCount \* $depositMaxIndex)
30+
31+
workers="[]"
32+
33+
while read index; do
34+
depositCount=$minDepositCount
35+
if [ "$index" -lt "$plusOneDepositCount" ]; then
36+
depositCount=$(expr $depositCount + 1)
37+
fi
38+
39+
worker=$(echo "{\"index\": $index, \"depositCount\": $depositCount}" )
40+
workers=$(echo $workers | jq -c ". += [$worker]")
41+
done <<< $(seq 0 1 $(expr $depositMaxIndex - 1))
42+
43+
echo "::set-out-json workers $workers"
44+
45+
- name: run_task_matrix
46+
title: "Generate ${depositCount} topup deposits for first ${depositMaxIndex} keys"
47+
configVars:
48+
matrixValues: "tasks.prepare.outputs.workers"
49+
config:
50+
runConcurrent: true
51+
matrixVar: "worker"
52+
task:
53+
name: run_tasks
54+
title: "Generate ${{worker.depositCount}} topup deposits for key ${{worker.index}}"
55+
config:
56+
tasks:
57+
- name: check_consensus_validator_status
58+
title: "Get validator pubkey for key ${{worker.index}}"
59+
id: "get_validator"
60+
timeout: 1m
61+
configVars:
62+
validatorIndex: "worker.index"
63+
64+
- name: generate_child_wallet
65+
id: depositor_wallet
66+
title: "Generate wallet for lifecycle test"
67+
configVars:
68+
walletSeed: "| \"fillup-deposit-queue-\" + (.worker.index | tostring)"
69+
prefundMinBalance: "| (.worker.depositCount + 1) * 1000000000000000000"
70+
privateKey: "walletPrivkey"
71+
72+
- name: sleep
73+
title: "Sleep 10s to ensure propagation of last block with wallet fundings to all clients"
74+
config:
75+
duration: 10s
76+
77+
- name: run_task_options
78+
title: "Generate ${{worker.depositCount}} top up deposits with 1 ETH each"
79+
config:
80+
task:
81+
name: generate_deposits
82+
title: "Generate top up deposits for key ${{worker.index}} (${{tasks.get_validator.outputs.pubkey}})"
83+
config:
84+
limitPerSlot: 20
85+
limitPending: 40
86+
depositAmount: 1
87+
topUpDeposit: true
88+
awaitReceipt: true
89+
failOnReject: true
90+
configVars:
91+
limitTotal: "worker.depositCount"
92+
walletPrivkey: "tasks.depositor_wallet.outputs.childWallet.privkey"
93+
publicKey: "tasks.get_validator.outputs.pubkey"
94+
depositContract: "depositContract"
95+

0 commit comments

Comments
 (0)