-
Notifications
You must be signed in to change notification settings - Fork 272
Expand file tree
/
Copy pathvalidator_credentials.go
More file actions
155 lines (136 loc) · 5.29 KB
/
validator_credentials.go
File metadata and controls
155 lines (136 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2025, Berachain Foundation. All rights reserved.
// Use of this software is governed by the Business Source License included
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
//
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
// VERSIONS OF THE LICENSED WORK.
//
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
//
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
// AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
// TITLE.
package merkle
import (
"fmt"
ctypes "github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/errors"
"github.com/berachain/beacon-kit/node-api/handlers/proof/types"
"github.com/berachain/beacon-kit/primitives/common"
"github.com/berachain/beacon-kit/primitives/constants"
"github.com/berachain/beacon-kit/primitives/math"
"github.com/berachain/beacon-kit/primitives/merkle"
)
// ProveWithdrawalCredentialsInState generates a proof for a validator's
// withdrawal credentials in the beacon state. The validatorOffset must be
// computed as (ValidatorWithdrawalCredentialsGIndexOffset * validatorIndex).
func ProveWithdrawalCredentialsInState(
forkVersion common.Version,
bsm types.BeaconStateMarshallable,
validatorOffset math.U64,
) ([]common.Root, common.Root, error) {
stateProofTree, err := bsm.GetTree()
if err != nil {
return nil, common.Root{}, err
}
// Determine the starting generalized index for the 0-th validator's
// withdrawal credentials for this fork.
zeroWithdrawalGIndexState, err := GetZeroValidatorCredentialsGIndexState(forkVersion)
if err != nil {
return nil, common.Root{}, err
}
// int conversion is safe on 64-bit architectures: (2^40-1)*8 < 2^43 < 2^63.
gIndex := zeroWithdrawalGIndexState + int(validatorOffset) // #nosec G115 -- offset bounded by caller.
withdrawalProof, err := stateProofTree.Prove(gIndex)
if err != nil {
return nil, common.Root{}, err
}
proof := make([]common.Root, len(withdrawalProof.Hashes))
for i, hash := range withdrawalProof.Hashes {
proof[i] = common.NewRootFromBytes(hash)
}
return proof, common.NewRootFromBytes(withdrawalProof.Leaf), nil
}
// ProveWithdrawalCredentialsInBlock generates a proof for a validator's
// withdrawal credentials in the beacon block. The proof is verified against
// the beacon block root as a sanity check and the "correct" beacon block root
// is returned alongside the proof.
func ProveWithdrawalCredentialsInBlock(
validatorIndex math.U64,
bbh *ctypes.BeaconBlockHeader,
bsm types.BeaconStateMarshallable,
) ([]common.Root, common.Root, error) {
// Bound validatorIndex for subsequent cast to int.
if validatorIndex.Unwrap() >= constants.ValidatorsRegistryLimit {
return nil, common.Root{}, fmt.Errorf(
"validator index %d exceeds registry limit %d",
validatorIndex, uint64(constants.ValidatorsRegistryLimit),
)
}
forkVersion := bsm.GetForkVersion()
// Calculate the validator-specific offset.
validatorOffset := ValidatorGIndexOffset * validatorIndex
// 1. Proof inside the state.
withdrawalInStateProof, leaf, err := ProveWithdrawalCredentialsInState(
forkVersion, bsm, validatorOffset,
)
if err != nil {
return nil, common.Root{}, err
}
// 2. Proof of the state inside the block.
stateInBlockProof, err := ProveBeaconStateInBlock(bbh, false)
if err != nil {
return nil, common.Root{}, err
}
// 3. Combine proofs: state-level hashes come first, followed by block-level
// hashes (same order as ProveProposerPubkeyInBlock).
//
//nolint:gocritic // ok.
combinedProof := append(withdrawalInStateProof, stateInBlockProof...)
// 4. Verify the combined proof against the beacon block root.
beaconRoot, err := verifyWithdrawalCredentialsInBlock(
forkVersion, bbh, validatorOffset, combinedProof, leaf,
)
if err != nil {
return nil, common.Root{}, err
}
return combinedProof, beaconRoot, nil
}
// verifyWithdrawalCredentialsInBlock verifies the provided Merkle proof of a
// validator's withdrawal credentials inside the beacon block and returns the
// beacon block root that the proof was verified against.
//
// NOTE: Proof verification is not strictly necessary for operation, but we do
// it as a sanity check to avoid propagating malformed proofs downstream.
func verifyWithdrawalCredentialsInBlock(
forkVersion common.Version,
bbh *ctypes.BeaconBlockHeader,
validatorOffset math.U64,
proof []common.Root,
leaf common.Root,
) (common.Root, error) {
zeroWithdrawalGIndexBlock, err := GetZeroValidatorCredentialsGIndexBlock(forkVersion)
if err != nil {
return common.Root{}, err
}
beaconRoot := bbh.HashTreeRoot()
if !merkle.VerifyProof(
beaconRoot,
leaf,
zeroWithdrawalGIndexBlock+validatorOffset.Unwrap(),
proof,
) {
return common.Root{}, errors.Wrapf(
errors.New("withdrawal credentials proof failed to verify against beacon root"),
"beacon root: 0x%s", beaconRoot,
)
}
return beaconRoot, nil
}