mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-03 00:27:38 +00:00
af203efa0c
* `helpers.go`: Improve naming consistency. * `detect_attestations.go`: Improve readability. * `receive.go`: Add `attsQueueSize` in log message. * `checkSlashableAttestations`: Improve logging. `avgBatchProcessingTime` is not displayed any more if not batch is processed. * `loadChunks`: Use explicit `chunkKind` and `chunkIndices`. * `getChunk`: Use specific `chunkIndex` and `chunkKind`. * `validatorIndicesInChunk` -> `validatorIndexesInChunk`. * `epochUpdateForValidator`: Use explicit arguments. * `getChunk`: Change order of arguments. * `latestEpochWrittenForValidator`: Use `ok` parameter. So the default value is not any more considered as the absence of value. * `applyAttestationForValidator`: Use explicit arguments. * `updateSpans`: Use explicit arguments. * `saveUpdatedChunks`: Use explicit arguments. * `checkSurrounds`: Use explicit arguments. We see here that, previously, in `checkSlashableAttestations`, `checkSurrounds` was called with the default value of `slashertypes`: `MinSpan`. Now, we set it expliciterly at `MinSpan`, which may explicit a bug. * `epochUpdateForValidator`: Set modified by the function argument first. * `applyAttestationForValidator`: Set mutated argument `chunksByChunkIdx`first. * `applyAttestationForValidator`: Rename variables. * `Test_processQueuedAttestations`: Fix test. Two tests were actually exactly the same. * `updateSpans`: Keep happy path in the outer scope. Even if in this case the "happy" path means slashing. * `checkSurrounds`: Rename variable. * `getChunk`: Avoid side effects. It adds a few lines for callers, but it does not modify any more arguments and it does what it says: getting a chunk. * `CheckSlashable`: Flatten. * `detect_attestations_test.go`: Simplify. * `CheckSlashable`: Add error log in case of missing attestation. * `processQueuedAttestations`: Extract a sub function. So testing will be easier. * `processAttesterSlashings` and `processProposerSlashings`: Improve. * `processAttesterSlashings`: Return processed slashings. * `createAttestationWrapper`: Rename variables. * `signingRoot` ==> `headerRoot` or `dataRoot`. Before this commit, there is two typse of `signing root`s floating around. - The first one is a real signing root, aka a hash tree root computed from an object root and a domain. This real signing root is the object ready to be signed. - The second one is a "false" signing root, which is actually just the hash tree root of an object. This object is either the `Data` field of an attestation, or the `Header` field of a block. Having 2 differents objects with the same name `signing root` is quite confusing. This commit renames wrongly named `signing root` objects. * `createAttestationWrapper` => `createAttestationWrapperEmptySig`. So it's clear for the user that the created attestation wrapper has an empty signature. * Implement `createAttestationWrapper`. * `processAttestations`: Return processed attester slashings. * Test `processAttestations` instead of `processQueuedAttestations`. By testing `processAttestations` instead of `processQueuedAttestations`, we get rid of a lot of tests fixtures, including the 200 ms sleep. The whole testing duration is shorter. * `Test_processAttestations`: Allow multiple steps. * `Test_processAttestations`: Add double steps tests. Some new failing tests are commented with a corresponding github issue. * `NextChunkStartEpoch`: Fix function comment. Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com> * `chunks.go`: Avoid templating log messages. * `checkSlashableAttestations`: Simplify duration computation. --------- Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
254 lines
7.4 KiB
Go
254 lines
7.4 KiB
Go
package slasher
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
|
|
dbtest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
|
|
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
|
slashingsmock "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/slashings/mock"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
|
)
|
|
|
|
func TestService_processAttesterSlashings(t *testing.T) {
|
|
ctx := context.Background()
|
|
slasherDB := dbtest.SetupSlasherDB(t)
|
|
beaconDB := dbtest.SetupDB(t)
|
|
|
|
beaconState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
|
|
privKey, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
validators := make([]*ethpb.Validator, 1)
|
|
validators[0] = ðpb.Validator{
|
|
PublicKey: privKey.PublicKey().Marshal(),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
}
|
|
err = beaconState.SetValidators(validators)
|
|
require.NoError(t, err)
|
|
|
|
mockChain := &mock.ChainService{
|
|
State: beaconState,
|
|
}
|
|
s := &Service{
|
|
serviceCfg: &ServiceConfig{
|
|
Database: slasherDB,
|
|
AttestationStateFetcher: mockChain,
|
|
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
|
|
SlashingPoolInserter: &slashingsmock.PoolMock{},
|
|
HeadStateFetcher: mockChain,
|
|
},
|
|
}
|
|
|
|
firstAtt := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{
|
|
AttestingIndices: []uint64{0},
|
|
})
|
|
secondAtt := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{
|
|
AttestingIndices: []uint64{0},
|
|
})
|
|
|
|
domain, err := signing.Domain(
|
|
beaconState.Fork(),
|
|
0,
|
|
params.BeaconConfig().DomainBeaconAttester,
|
|
beaconState.GenesisValidatorsRoot(),
|
|
)
|
|
require.NoError(t, err)
|
|
signingRoot, err := signing.ComputeSigningRoot(firstAtt.Data, domain)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("first_att_valid_sig_second_invalid", func(tt *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
// Use valid signature for the first att, but bad one for the second.
|
|
signature := privKey.Sign(signingRoot[:])
|
|
firstAtt.Signature = signature.Marshal()
|
|
secondAtt.Signature = make([]byte, 96)
|
|
|
|
slashings := []*ethpb.AttesterSlashing{
|
|
{
|
|
Attestation_1: firstAtt,
|
|
Attestation_2: secondAtt,
|
|
},
|
|
}
|
|
|
|
_, err = s.processAttesterSlashings(ctx, slashings)
|
|
require.NoError(tt, err)
|
|
require.LogsContain(tt, hook, "Invalid signature")
|
|
})
|
|
|
|
t.Run("first_att_invalid_sig_second_valid", func(tt *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
// Use invalid signature for the first att, but valid for the second.
|
|
signature := privKey.Sign(signingRoot[:])
|
|
firstAtt.Signature = make([]byte, 96)
|
|
secondAtt.Signature = signature.Marshal()
|
|
|
|
slashings := []*ethpb.AttesterSlashing{
|
|
{
|
|
Attestation_1: firstAtt,
|
|
Attestation_2: secondAtt,
|
|
},
|
|
}
|
|
|
|
_, err = s.processAttesterSlashings(ctx, slashings)
|
|
require.NoError(tt, err)
|
|
require.LogsContain(tt, hook, "Invalid signature")
|
|
})
|
|
|
|
t.Run("both_valid_att_signatures", func(tt *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
// Use valid signatures.
|
|
signature := privKey.Sign(signingRoot[:])
|
|
firstAtt.Signature = signature.Marshal()
|
|
secondAtt.Signature = signature.Marshal()
|
|
|
|
slashings := []*ethpb.AttesterSlashing{
|
|
{
|
|
Attestation_1: firstAtt,
|
|
Attestation_2: secondAtt,
|
|
},
|
|
}
|
|
|
|
_, err = s.processAttesterSlashings(ctx, slashings)
|
|
require.NoError(tt, err)
|
|
require.LogsDoNotContain(tt, hook, "Invalid signature")
|
|
})
|
|
}
|
|
|
|
func TestService_processProposerSlashings(t *testing.T) {
|
|
ctx := context.Background()
|
|
slasherDB := dbtest.SetupSlasherDB(t)
|
|
beaconDB := dbtest.SetupDB(t)
|
|
|
|
beaconState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
|
|
privKey, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
validators := make([]*ethpb.Validator, 1)
|
|
validators[0] = ðpb.Validator{
|
|
PublicKey: privKey.PublicKey().Marshal(),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
}
|
|
err = beaconState.SetValidators(validators)
|
|
require.NoError(t, err)
|
|
|
|
mockChain := &mock.ChainService{
|
|
State: beaconState,
|
|
}
|
|
s := &Service{
|
|
serviceCfg: &ServiceConfig{
|
|
Database: slasherDB,
|
|
AttestationStateFetcher: mockChain,
|
|
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
|
|
SlashingPoolInserter: &slashingsmock.PoolMock{},
|
|
HeadStateFetcher: mockChain,
|
|
},
|
|
}
|
|
|
|
parentRoot := bytesutil.ToBytes32([]byte("parent"))
|
|
err = s.serviceCfg.StateGen.SaveState(ctx, parentRoot, beaconState)
|
|
require.NoError(t, err)
|
|
|
|
firstBlockHeader := util.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{
|
|
Header: ðpb.BeaconBlockHeader{
|
|
Slot: 0,
|
|
ProposerIndex: 0,
|
|
ParentRoot: parentRoot[:],
|
|
},
|
|
})
|
|
secondBlockHeader := util.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{
|
|
Header: ðpb.BeaconBlockHeader{
|
|
Slot: 0,
|
|
ProposerIndex: 0,
|
|
ParentRoot: parentRoot[:],
|
|
},
|
|
})
|
|
|
|
domain, err := signing.Domain(
|
|
beaconState.Fork(),
|
|
0,
|
|
params.BeaconConfig().DomainBeaconProposer,
|
|
beaconState.GenesisValidatorsRoot(),
|
|
)
|
|
require.NoError(t, err)
|
|
htr, err := firstBlockHeader.Header.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
container := ðpb.SigningData{
|
|
ObjectRoot: htr[:],
|
|
Domain: domain,
|
|
}
|
|
require.NoError(t, err)
|
|
signingRoot, err := container.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
t.Run("first_header_valid_sig_second_invalid", func(tt *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
// Use valid signature for the first header, but bad one for the second.
|
|
signature := privKey.Sign(signingRoot[:])
|
|
firstBlockHeader.Signature = signature.Marshal()
|
|
secondBlockHeader.Signature = make([]byte, 96)
|
|
|
|
slashings := []*ethpb.ProposerSlashing{
|
|
{
|
|
Header_1: firstBlockHeader,
|
|
Header_2: secondBlockHeader,
|
|
},
|
|
}
|
|
|
|
err = s.processProposerSlashings(ctx, slashings)
|
|
require.NoError(tt, err)
|
|
require.LogsContain(tt, hook, "Invalid signature")
|
|
})
|
|
|
|
t.Run("first_header_invalid_sig_second_valid", func(tt *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
// Use invalid signature for the first header, but valid for the second.
|
|
signature := privKey.Sign(signingRoot[:])
|
|
firstBlockHeader.Signature = make([]byte, 96)
|
|
secondBlockHeader.Signature = signature.Marshal()
|
|
|
|
slashings := []*ethpb.ProposerSlashing{
|
|
{
|
|
Header_1: firstBlockHeader,
|
|
Header_2: secondBlockHeader,
|
|
},
|
|
}
|
|
|
|
err = s.processProposerSlashings(ctx, slashings)
|
|
require.NoError(tt, err)
|
|
require.LogsContain(tt, hook, "Invalid signature")
|
|
})
|
|
|
|
t.Run("both_valid_header_signatures", func(tt *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
// Use valid signatures.
|
|
signature := privKey.Sign(signingRoot[:])
|
|
firstBlockHeader.Signature = signature.Marshal()
|
|
secondBlockHeader.Signature = signature.Marshal()
|
|
|
|
slashings := []*ethpb.ProposerSlashing{
|
|
{
|
|
Header_1: firstBlockHeader,
|
|
Header_2: secondBlockHeader,
|
|
},
|
|
}
|
|
|
|
err = s.processProposerSlashings(ctx, slashings)
|
|
require.NoError(tt, err)
|
|
require.LogsDoNotContain(tt, hook, "Invalid signature")
|
|
})
|
|
}
|