-
Notifications
You must be signed in to change notification settings - Fork 125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(state-transitions): verify deposits against contract #2115
base: main
Are you sure you want to change the base?
Changes from 36 commits
f4a7d79
0f46172
22c7717
05cba80
443ac1b
10efd5e
099716d
151a533
9818c7e
160cc88
6a191d1
64d19e5
d94bf97
4a9fe1c
67f2597
e048be4
8bf34db
d90a95a
e17d29c
7b2bf91
6286b20
df81bae
af8c5e0
023ebfd
d66b298
18ba094
e47219a
a3cd2d9
3a0923e
712d3fe
6b90b87
83ad2fd
69d568b
a7143e8
6ce250d
0fc3868
187bf67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -186,6 +186,14 @@ func (d *Deposit) GetTree() (*fastssz.Node, error) { | |||||||||||||||||||||||||||||||||||||
/* -------------------------------------------------------------------------- */ | ||||||||||||||||||||||||||||||||||||||
/* Getters and Setters */ | ||||||||||||||||||||||||||||||||||||||
/* -------------------------------------------------------------------------- */ | ||||||||||||||||||||||||||||||||||||||
// Equals returns true if the Deposit is equal to the other. | ||||||||||||||||||||||||||||||||||||||
func (d *Deposit) Equals(rhs *Deposit) bool { | ||||||||||||||||||||||||||||||||||||||
return d.Pubkey == rhs.Pubkey && | ||||||||||||||||||||||||||||||||||||||
d.Credentials == rhs.Credentials && | ||||||||||||||||||||||||||||||||||||||
d.Amount == rhs.Amount && | ||||||||||||||||||||||||||||||||||||||
d.Signature == rhs.Signature && | ||||||||||||||||||||||||||||||||||||||
d.Index == rhs.Index | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+189
to
+196
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Add documentation explaining the equality comparison's role in deposit validation. The implementation looks good and covers all necessary fields. Consider adding a doc comment explaining how this method helps prevent fraudulent validators by enabling comparison against contract events. +// Equals returns true if the Deposit is equal to another deposit.
+// This method is crucial for validating deposits against contract events
+// to prevent fraudulent validator creation.
func (d *Deposit) Equals(rhs *Deposit) bool { 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// GetAmount returns the deposit amount in gwei. | ||||||||||||||||||||||||||||||||||||||
func (d *Deposit) GetAmount() math.Gwei { | ||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ type StateProcessorInput[ | |
ExecutionPayloadT, ExecutionPayloadHeaderT, WithdrawalsT, | ||
], | ||
ExecutionPayloadHeaderT ExecutionPayloadHeader[ExecutionPayloadHeaderT], | ||
DepositT Deposit[DepositT, *ForkData, WithdrawalCredentials], | ||
WithdrawalT Withdrawal[WithdrawalT], | ||
WithdrawalsT Withdrawals[WithdrawalT], | ||
] struct { | ||
|
@@ -50,7 +51,8 @@ type StateProcessorInput[ | |
PayloadID, | ||
WithdrawalsT, | ||
] | ||
Signer crypto.BLSSigner | ||
DepositStore DepositStore[DepositT] | ||
Signer crypto.BLSSigner | ||
} | ||
|
||
// ProvideStateProcessor provides the state processor to the depinject | ||
|
@@ -70,6 +72,7 @@ func ProvideStateProcessor[ | |
], | ||
BeaconStateMarshallableT any, | ||
DepositT Deposit[DepositT, *ForkData, WithdrawalCredentials], | ||
DepositStoreT DepositStore[DepositT], | ||
ExecutionPayloadT ExecutionPayload[ | ||
ExecutionPayloadT, ExecutionPayloadHeaderT, WithdrawalsT, | ||
], | ||
|
@@ -84,7 +87,7 @@ func ProvideStateProcessor[ | |
in StateProcessorInput[ | ||
LoggerT, | ||
ExecutionPayloadT, ExecutionPayloadHeaderT, | ||
WithdrawalT, WithdrawalsT, | ||
DepositT, WithdrawalT, WithdrawalsT, | ||
], | ||
) *core.StateProcessor[ | ||
BeaconBlockT, BeaconBlockBodyT, BeaconBlockHeaderT, | ||
|
@@ -114,6 +117,7 @@ func ProvideStateProcessor[ | |
in.Logger.With("service", "state-processor"), | ||
in.ChainSpec, | ||
in.ExecutionEngine, | ||
in.DepositStore, | ||
in.Signer, | ||
crypto.GetAddressFromPubKey, | ||
Comment on lines
+120
to
122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Consider enhancing logging for deposit verification. The implementation correctly integrates the deposit store. Given the PR objectives mention improving logging capabilities, consider adding structured logging fields for deposit verification outcomes. Add logging fields to track deposit verification: - in.Logger.With("service", "state-processor"),
+ in.Logger.With(
+ "service", "state-processor",
+ "features", []string{"deposit-verification"},
+ ),
|
||
) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -51,7 +51,7 @@ type StateProcessor[ | |||||||||||
ValidatorT, ValidatorsT, WithdrawalT, | ||||||||||||
], | ||||||||||||
ContextT Context, | ||||||||||||
DepositT Deposit[ForkDataT, WithdrawalCredentialsT], | ||||||||||||
DepositT Deposit[DepositT, ForkDataT, WithdrawalCredentialsT], | ||||||||||||
Eth1DataT interface { | ||||||||||||
New(common.Root, math.U64, common.ExecutionHash) Eth1DataT | ||||||||||||
GetDepositCount() math.U64 | ||||||||||||
|
@@ -92,6 +92,8 @@ type StateProcessor[ | |||||||||||
executionEngine ExecutionEngine[ | ||||||||||||
ExecutionPayloadT, ExecutionPayloadHeaderT, WithdrawalsT, | ||||||||||||
] | ||||||||||||
// ds allows checking payload deposits against the deposit contract | ||||||||||||
ds DepositStore[DepositT] | ||||||||||||
Comment on lines
+95
to
+96
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Enhance deposit store field documentation. While the comment explains what the field does, it could be more descriptive about its purpose in preventing fraudulent validators and censoring legitimate ones. Consider updating the comment to: - // ds allows checking payload deposits against the deposit contract
+ // ds allows verifying payload deposits against the deposit contract events
+ // to prevent fraudulent validators and ensure legitimate validators aren't censored 📝 Committable suggestion
Suggested change
|
||||||||||||
|
||||||||||||
// processingGenesis allows initializing correctly | ||||||||||||
// eth1 deposit index upon genesis | ||||||||||||
|
@@ -116,7 +118,7 @@ func NewStateProcessor[ | |||||||||||
KVStoreT, ValidatorT, ValidatorsT, WithdrawalT, | ||||||||||||
], | ||||||||||||
ContextT Context, | ||||||||||||
DepositT Deposit[ForkDataT, WithdrawalCredentialsT], | ||||||||||||
DepositT Deposit[DepositT, ForkDataT, WithdrawalCredentialsT], | ||||||||||||
Eth1DataT interface { | ||||||||||||
New(common.Root, math.U64, common.ExecutionHash) Eth1DataT | ||||||||||||
GetDepositCount() math.U64 | ||||||||||||
|
@@ -148,6 +150,7 @@ func NewStateProcessor[ | |||||||||||
executionEngine ExecutionEngine[ | ||||||||||||
ExecutionPayloadT, ExecutionPayloadHeaderT, WithdrawalsT, | ||||||||||||
], | ||||||||||||
ds DepositStore[DepositT], | ||||||||||||
signer crypto.BLSSigner, | ||||||||||||
fGetAddressFromPubKey func(crypto.BLSPubkey) ([]byte, error), | ||||||||||||
) *StateProcessor[ | ||||||||||||
|
@@ -167,6 +170,7 @@ func NewStateProcessor[ | |||||||||||
executionEngine: executionEngine, | ||||||||||||
signer: signer, | ||||||||||||
fGetAddressFromPubKey: fGetAddressFromPubKey, | ||||||||||||
ds: ds, | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,18 +48,19 @@ func TestInitialize(t *testing.T) { | |
](t) | ||
mocksSigner := &cryptomocks.BLSSigner{} | ||
|
||
kvStore, depositStore, err := initTestStores() | ||
require.NoError(t, err) | ||
beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) | ||
|
||
Comment on lines
+51
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Consider implementing a store provider interface. Since both the block builder and state-transition processor now require deposit store access, consider introducing a common interface or provider pattern to standardize this access. This would:
Also applies to: 58-58, 173-176, 180-180 |
||
sp := createStateProcessor( | ||
cs, | ||
execEngine, | ||
depositStore, | ||
mocksSigner, | ||
dummyProposerAddressVerifier, | ||
) | ||
|
||
// create test inputs | ||
kvStore, err := initStore() | ||
require.NoError(t, err) | ||
beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) | ||
|
||
// create test input | ||
var ( | ||
deposits = []*types.Deposit{ | ||
{ | ||
|
@@ -169,18 +170,19 @@ func TestInitializeBartio(t *testing.T) { | |
](t) | ||
mocksSigner := &cryptomocks.BLSSigner{} | ||
|
||
kvStore, depositStore, err := initTestStores() | ||
require.NoError(t, err) | ||
beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) | ||
|
||
sp := createStateProcessor( | ||
cs, | ||
execEngine, | ||
depositStore, | ||
mocksSigner, | ||
dummyProposerAddressVerifier, | ||
) | ||
|
||
// create test inputs | ||
kvStore, err := initStore() | ||
require.NoError(t, err) | ||
beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) | ||
|
||
var ( | ||
deposits = []*types.Deposit{ | ||
{ | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -40,26 +40,41 @@ func (sp *StateProcessor[ | |||||||||||||||||
st BeaconStateT, | ||||||||||||||||||
blk BeaconBlockT, | ||||||||||||||||||
) error { | ||||||||||||||||||
// Verify that outstanding deposits are processed up to the maximum number | ||||||||||||||||||
// of deposits. | ||||||||||||||||||
deposits := blk.GetBody().GetDeposits() | ||||||||||||||||||
index, err := st.GetEth1DepositIndex() | ||||||||||||||||||
// Verify that outstanding deposits matches those listed by contract | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Fix grammatical error in comment Correct the comment for grammatical accuracy. Apply this diff: -// Verify that outstanding deposits matches those listed by contract
+// Verify that outstanding deposits match those listed by contract 📝 Committable suggestion
Suggested change
|
||||||||||||||||||
depositIndex, err := st.GetEth1DepositIndex() | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return err | ||||||||||||||||||
} | ||||||||||||||||||
eth1Data, err := st.GetEth1Data() | ||||||||||||||||||
|
||||||||||||||||||
stateDeposits, err := sp.ds.GetDepositsByIndex( | ||||||||||||||||||
depositIndex+1, | ||||||||||||||||||
sp.cs.MaxDepositsPerBlock(), | ||||||||||||||||||
) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return err | ||||||||||||||||||
} | ||||||||||||||||||
depositCount := min( | ||||||||||||||||||
sp.cs.MaxDepositsPerBlock(), | ||||||||||||||||||
eth1Data.GetDepositCount().Unwrap()-index, | ||||||||||||||||||
|
||||||||||||||||||
deposits := blk.GetBody().GetDeposits() | ||||||||||||||||||
sp.logger.Info( | ||||||||||||||||||
"Expected deposit index from payload", depositIndex, | ||||||||||||||||||
"deposits withdrawals length", len(deposits), | ||||||||||||||||||
) | ||||||||||||||||||
Comment on lines
+58
to
61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Clarify logging message for better readability Improve the clarity of the logging message for better understanding. Apply this diff to enhance the logging message: - sp.logger.Info(
- "Expected deposit index from payload", depositIndex,
- "deposits withdrawals length", len(deposits),
- )
+ sp.logger.Info(
+ "Processing deposits starting from index", depositIndex,
+ "number of deposits in payload", len(deposits),
+ ) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||
_ = depositCount | ||||||||||||||||||
// TODO: Update eth1data count and check this. | ||||||||||||||||||
// if uint64(len(deposits)) != depositCount { | ||||||||||||||||||
// return errors.New("deposit count mismatch") | ||||||||||||||||||
// } | ||||||||||||||||||
|
||||||||||||||||||
if len(stateDeposits) != len(deposits) { | ||||||||||||||||||
return fmt.Errorf("deposits mismatched lengths, state: %d, payload: %d", | ||||||||||||||||||
len(stateDeposits), | ||||||||||||||||||
len(deposits), | ||||||||||||||||||
) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
for i, sd := range stateDeposits { | ||||||||||||||||||
if !sd.Equals(deposits[i]) { | ||||||||||||||||||
return fmt.Errorf("deposits mismatched, state: %#v, payload: %#v", | ||||||||||||||||||
sd, deposits[i], | ||||||||||||||||||
) | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+72
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid logging sensitive data in error messages Using Adjust the error message to include only essential details: - return fmt.Errorf("deposits mismatched, state: %#v, payload: %#v",
- sd, deposits[i],
- )
+ return fmt.Errorf("deposit mismatch at index %d", i) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
Comment on lines
+43
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Consider adding unit tests for deposit verification logic To ensure the correctness and reliability of the new deposit verification implemented in |
||||||||||||||||||
return sp.processDeposits(st, deposits) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -37,6 +37,8 @@ import ( | |||||||||||||
"github.com/stretchr/testify/require" | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
// TestTransitionUpdateValidators shows that when validator is | ||||||||||||||
// updated (increasing amount), corrensponding balance is updated. | ||||||||||||||
Comment on lines
+40
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Fix typo in test comment. "corrensponding" should be "corresponding" |
||||||||||||||
func TestTransitionUpdateValidators(t *testing.T) { | ||||||||||||||
// Create state processor to test | ||||||||||||||
cs := spec.BetnetChainSpec() | ||||||||||||||
|
@@ -48,19 +50,20 @@ func TestTransitionUpdateValidators(t *testing.T) { | |||||||||||||
mocksSigner := &cryptomocks.BLSSigner{} | ||||||||||||||
dummyProposerAddr := []byte{0xff} | ||||||||||||||
|
||||||||||||||
kvStore, depositStore, err := initTestStores() | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) | ||||||||||||||
|
||||||||||||||
sp := createStateProcessor( | ||||||||||||||
cs, | ||||||||||||||
execEngine, | ||||||||||||||
depositStore, | ||||||||||||||
mocksSigner, | ||||||||||||||
func(bytes.B48) ([]byte, error) { | ||||||||||||||
return dummyProposerAddr, nil | ||||||||||||||
}, | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
kvStore, err := initStore() | ||||||||||||||
require.NoError(t, err) | ||||||||||||||
beaconState := new(TestBeaconStateT).NewFromDB(kvStore, cs) | ||||||||||||||
|
||||||||||||||
var ( | ||||||||||||||
maxBalance = math.Gwei(cs.MaxEffectiveBalance()) | ||||||||||||||
minBalance = math.Gwei(cs.EffectiveBalanceIncrement()) | ||||||||||||||
|
@@ -116,7 +119,7 @@ func TestTransitionUpdateValidators(t *testing.T) { | |||||||||||||
Pubkey: genDeposits[0].Pubkey, | ||||||||||||||
Credentials: emptyCredentials, | ||||||||||||||
Amount: minBalance, // avoid breaching maxBalance | ||||||||||||||
Index: genDeposits[0].Index, | ||||||||||||||
Index: uint64(len(genDeposits)), | ||||||||||||||
}, | ||||||||||||||
} | ||||||||||||||
) | ||||||||||||||
|
@@ -137,6 +140,9 @@ func TestTransitionUpdateValidators(t *testing.T) { | |||||||||||||
}, | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
// make sure included deposit is already available in deposit store | ||||||||||||||
require.NoError(t, depositStore.EnqueueDeposits(blkDeposits)) | ||||||||||||||
|
||||||||||||||
Comment on lines
+143
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Consider enhancing deposit verification error handling. While the deposit verification step is good and aligns with PR objectives, consider adding more descriptive error messaging to help debug test failures. - require.NoError(t, depositStore.EnqueueDeposits(blkDeposits))
+ err = depositStore.EnqueueDeposits(blkDeposits)
+ require.NoError(t, err, "failed to enqueue deposits in store before transition") 📝 Committable suggestion
Suggested change
|
||||||||||||||
// run the test | ||||||||||||||
vals, err := sp.Transition(ctx, beaconState, blk) | ||||||||||||||
|
||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -129,9 +129,12 @@ type Context interface { | |
|
||
// Deposit is the interface for a deposit. | ||
type Deposit[ | ||
DepositT any, | ||
ForkDataT any, | ||
WithdrawlCredentialsT ~[32]byte, | ||
] interface { | ||
// Equals returns true if the Deposit is equal to the other. | ||
Equals(DepositT) bool | ||
// GetAmount returns the amount of the deposit. | ||
GetAmount() math.Gwei | ||
// GetPubkey returns the public key of the validator. | ||
|
@@ -149,6 +152,15 @@ type Deposit[ | |
) error | ||
} | ||
|
||
// DepositStore defines the interface for deposit storage. | ||
type DepositStore[DepositT any] interface { | ||
// GetDepositsByIndex returns `numView` expected deposits. | ||
GetDepositsByIndex( | ||
startIndex uint64, | ||
numView uint64, | ||
) ([]DepositT, error) | ||
} | ||
Comment on lines
+155
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Consider adding deposit pruning strategy. While the interface looks good for deposit retrieval, consider adding methods for deposit pruning to prevent unbounded growth of the store. This is particularly important as the past review comments mention deposit store pruning process issues. Consider adding these methods to the interface:
This would help prevent potential memory issues and align with the mentioned deposit store pruning requirements. |
||
|
||
type ExecutionPayload[ | ||
ExecutionPayloadT, ExecutionPayloadHeaderT, WithdrawalsT any, | ||
] interface { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider using constant-time comparison for sensitive fields.
To prevent potential timing attacks when comparing cryptographic fields, consider using constant-time comparison for
Pubkey
,Credentials
, andSignature
.