mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Optimized Slasher Parameters (#9361)
* add in parameters and helpers files * add in small set of changes * build file * viz
This commit is contained in:
27
beacon-chain/slasher/BUILD.bazel
Normal file
27
beacon-chain/slasher/BUILD.bazel
Normal file
@@ -0,0 +1,27 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"params.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/slasher",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["params_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
],
|
||||
)
|
||||
163
beacon-chain/slasher/params.go
Normal file
163
beacon-chain/slasher/params.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
)
|
||||
|
||||
// Parameters for slashing detection.
|
||||
//
|
||||
// To properly access the element at epoch `e` for a validator index `i`, we leverage helper
|
||||
// functions from these parameter values as nice abstractions. the following parameters are
|
||||
// required for the helper functions defined in this file.
|
||||
//
|
||||
// (C) chunkSize defines how many elements are in a chunk for a validator
|
||||
// min or max span slice.
|
||||
// (K) validatorChunkSize defines how many validators' chunks we store in a single
|
||||
// flat byte slice on disk.
|
||||
// (H) historyLength defines how many epochs we keep of min or max spans.
|
||||
type Parameters struct {
|
||||
chunkSize uint64
|
||||
validatorChunkSize uint64
|
||||
historyLength types.Epoch
|
||||
}
|
||||
|
||||
// DefaultParams defines default values for slasher's important parameters, defined
|
||||
// based on optimization analysis for best and worst case scenarios for
|
||||
// slasher's performance.
|
||||
//
|
||||
// The default values for chunkSize and validatorChunkSize were
|
||||
// decided after an optimization analysis performed by the Sigma Prime team.
|
||||
// See: https://hackmd.io/@sproul/min-max-slasher#1D-vs-2D for more information.
|
||||
// We decide to keep 4096 epochs worth of data in each validator's min max spans.
|
||||
func DefaultParams() *Parameters {
|
||||
return &Parameters{
|
||||
chunkSize: 16,
|
||||
validatorChunkSize: 256,
|
||||
historyLength: 4096,
|
||||
}
|
||||
}
|
||||
|
||||
// Validator min and max spans are split into chunks of length C = chunkSize.
|
||||
// That is, if we are keeping N epochs worth of attesting history, finding what
|
||||
// chunk a certain epoch, e, falls into can be computed as (e % N) / C. For example,
|
||||
// if we are keeping 6 epochs worth of data, and we have chunks of size 2, then epoch
|
||||
// 4 will fall into chunk index (4 % 6) / 2 = 2.
|
||||
//
|
||||
// span = [-, -, -, -, -, -]
|
||||
// chunked = [[-, -], [-, -], [-, -]]
|
||||
// |-> epoch 4, chunk idx 2
|
||||
//
|
||||
func (p *Parameters) chunkIndex(epoch types.Epoch) uint64 {
|
||||
return uint64(epoch.Mod(uint64(p.historyLength)).Div(p.chunkSize))
|
||||
}
|
||||
|
||||
// When storing data on disk, we take K validators' chunks. To figure out
|
||||
// which validator chunk index a validator index is for, we simply divide
|
||||
// the validator index, i, by K.
|
||||
func (p *Parameters) validatorChunkIndex(validatorIndex types.ValidatorIndex) uint64 {
|
||||
return uint64(validatorIndex.Div(p.validatorChunkSize))
|
||||
}
|
||||
|
||||
// Returns the epoch at the 0th index of a chunk at the specified chunk index.
|
||||
// For example, if we have chunks of length 3 and we ask to give us the
|
||||
// first epoch of chunk1, then:
|
||||
//
|
||||
// chunk0 chunk1 chunk2
|
||||
// | | |
|
||||
// [[-, -, -], [-, -, -], [-, -, -], ...]
|
||||
// |
|
||||
// -> first epoch of chunk 1 equals 3
|
||||
//
|
||||
func (p *Parameters) firstEpoch(chunkIndex uint64) types.Epoch {
|
||||
return types.Epoch(chunkIndex * p.chunkSize)
|
||||
}
|
||||
|
||||
// Returns the epoch at the last index of a chunk at the specified chunk index.
|
||||
// For example, if we have chunks of length 3 and we ask to give us the
|
||||
// last epoch of chunk1, then:
|
||||
//
|
||||
// chunk0 chunk1 chunk2
|
||||
// | | |
|
||||
// [[-, -, -], [-, -, -], [-, -, -], ...]
|
||||
// |
|
||||
// -> last epoch of chunk 1 equals 5
|
||||
//
|
||||
func (p *Parameters) lastEpoch(chunkIndex uint64) types.Epoch {
|
||||
return p.firstEpoch(chunkIndex).Add(p.chunkSize - 1)
|
||||
}
|
||||
|
||||
// Given a validator index, and epoch, we compute the exact index
|
||||
// into our flat slice on disk which stores K validators' chunks, each
|
||||
// chunk of size C. For example, if C = 3 and K = 3, the data we store
|
||||
// on disk is a flat slice as follows:
|
||||
//
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [-, -, -, -, -, -, -, -, -]
|
||||
//
|
||||
// Then, figuring out the exact cell index for epoch 1 for validator 2 is computed
|
||||
// with (validatorIndex % K)*C + (epoch % C), which gives us:
|
||||
//
|
||||
// (2 % 3)*3 + (1 % 3) =
|
||||
// (2*3) + (1) =
|
||||
// 7
|
||||
//
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [-, -, -, -, -, -, -, -, -]
|
||||
// |-> epoch 1 for val2
|
||||
//
|
||||
func (p *Parameters) cellIndex(validatorIndex types.ValidatorIndex, epoch types.Epoch) uint64 {
|
||||
validatorChunkOffset := p.validatorOffset(validatorIndex)
|
||||
chunkOffset := p.chunkOffset(epoch)
|
||||
return validatorChunkOffset*p.chunkSize + chunkOffset
|
||||
}
|
||||
|
||||
// Computes the start index of a chunk given an epoch.
|
||||
func (p *Parameters) chunkOffset(epoch types.Epoch) uint64 {
|
||||
return uint64(epoch.Mod(p.chunkSize))
|
||||
}
|
||||
|
||||
// Computes the start index of a validator chunk given a validator index.
|
||||
func (p *Parameters) validatorOffset(validatorIndex types.ValidatorIndex) uint64 {
|
||||
return uint64(validatorIndex.Mod(p.validatorChunkSize))
|
||||
}
|
||||
|
||||
// Construct a key for our database schema given a validator chunk index and chunk index.
|
||||
// This calculation gives us a uint encoded as bytes that uniquely represents
|
||||
// a 2D chunk given a validator index and epoch value.
|
||||
// First, we compute the validator chunk index for the validator index,
|
||||
// Then, we compute the chunk index for the epoch.
|
||||
// If chunkSize C = 3 and validatorChunkSize K = 3, and historyLength H = 12,
|
||||
// if we are looking for epoch 6 and validator 6, then
|
||||
//
|
||||
// validatorChunkIndex = 6 / 3 = 2
|
||||
// chunkIndex = (6 % historyLength) / 3 = (6 % 12) / 3 = 2
|
||||
//
|
||||
// Then we compute how many chunks there are per max span, known as the "width"
|
||||
//
|
||||
// width = H / C = 12 / 3 = 4
|
||||
//
|
||||
// So every span has 4 chunks. Then, we have a disk key calculated by
|
||||
//
|
||||
// validatorChunkIndex * width + chunkIndex = 2*4 + 2 = 10
|
||||
//
|
||||
func (p *Parameters) flatSliceID(validatorChunkIndex, chunkIndex uint64) []byte {
|
||||
width := p.historyLength.Div(p.chunkSize)
|
||||
return ssz.MarshalUint64(make([]byte, 0), uint64(width.Mul(validatorChunkIndex).Add(chunkIndex)))
|
||||
}
|
||||
|
||||
// Given a validator chunk index, we determine all of the validator
|
||||
// indices that will belong in that chunk.
|
||||
func (p *Parameters) validatorIndicesInChunk(validatorChunkIdx uint64) []types.ValidatorIndex {
|
||||
validatorIndices := make([]types.ValidatorIndex, 0)
|
||||
low := validatorChunkIdx * p.validatorChunkSize
|
||||
high := (validatorChunkIdx + 1) * p.validatorChunkSize
|
||||
for i := low; i < high; i++ {
|
||||
validatorIndices = append(validatorIndices, types.ValidatorIndex(i))
|
||||
}
|
||||
return validatorIndices
|
||||
}
|
||||
540
beacon-chain/slasher/params_test.go
Normal file
540
beacon-chain/slasher/params_test.go
Normal file
@@ -0,0 +1,540 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
)
|
||||
|
||||
func TestDefaultParams(t *testing.T) {
|
||||
def := DefaultParams()
|
||||
assert.Equal(t, true, def.chunkSize > 0)
|
||||
assert.Equal(t, true, def.validatorChunkSize > 0)
|
||||
assert.Equal(t, true, def.historyLength > 0)
|
||||
}
|
||||
|
||||
func TestParams_cellIndex(t *testing.T) {
|
||||
type args struct {
|
||||
validatorIndex types.ValidatorIndex
|
||||
epoch types.Epoch
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
args args
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "epoch 0 and validator index 0",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 0,
|
||||
epoch: 0,
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
// |-> epoch 1, validator 2
|
||||
name: "epoch < chunkSize and validatorIndex < validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 2,
|
||||
epoch: 1,
|
||||
},
|
||||
want: 7,
|
||||
},
|
||||
{
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
// |-> epoch 4, validator 2 (wrap around)
|
||||
name: "epoch > chunkSize and validatorIndex < validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 2,
|
||||
epoch: 4,
|
||||
},
|
||||
want: 7,
|
||||
},
|
||||
{
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
// |-> epoch 3, validator 2 (wrap around)
|
||||
name: "epoch = chunkSize and validatorIndex < validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 2,
|
||||
epoch: 3,
|
||||
},
|
||||
want: 6,
|
||||
},
|
||||
{
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
// |-> epoch 0, validator 3 (wrap around)
|
||||
name: "epoch < chunkSize and validatorIndex = validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 3,
|
||||
epoch: 0,
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
// |-> epoch 0, validator 4 (wrap around)
|
||||
name: "epoch < chunkSize and validatorIndex > validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 4,
|
||||
epoch: 0,
|
||||
},
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
// val0 val1 val2
|
||||
// | | |
|
||||
// { } { } { }
|
||||
// [2, 2, 2, 2, 2, 2, 2, 2, 2]
|
||||
// |-> epoch 3, validator 3 (wrap around)
|
||||
name: "epoch = chunkSize and validatorIndex = validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
args: args{
|
||||
validatorIndex: 3,
|
||||
epoch: 3,
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
chunkSize: tt.fields.chunkSize,
|
||||
validatorChunkSize: tt.fields.validatorChunkSize,
|
||||
historyLength: tt.fields.historyLength,
|
||||
}
|
||||
if got := c.cellIndex(tt.args.validatorIndex, tt.args.epoch); got != tt.want {
|
||||
t.Errorf("cellIndex() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_chunkIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
epoch types.Epoch
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "epoch 0",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 0,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch < historyLength, epoch < chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 2,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch = historyLength, epoch < chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 4,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 3,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch > historyLength, epoch < chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 5,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 4,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch < historyLength, epoch < chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 2,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch = historyLength, epoch < chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 4,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 3,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch < historyLength, epoch = chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 2,
|
||||
historyLength: 3,
|
||||
},
|
||||
epoch: 2,
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "epoch < historyLength, epoch > chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 2,
|
||||
historyLength: 4,
|
||||
},
|
||||
epoch: 3,
|
||||
want: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
chunkSize: tt.fields.chunkSize,
|
||||
historyLength: tt.fields.historyLength,
|
||||
}
|
||||
if got := c.chunkIndex(tt.epoch); got != tt.want {
|
||||
t.Errorf("chunkIndex() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_flatSliceID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
validatorChunkIndex uint64
|
||||
chunkIndex uint64
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "Proper disk key for 0, 0",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
historyLength: 6,
|
||||
},
|
||||
chunkIndex: 0,
|
||||
validatorChunkIndex: 0,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "Proper disk key for epoch < historyLength, validator < validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
historyLength: 6,
|
||||
},
|
||||
chunkIndex: 1,
|
||||
validatorChunkIndex: 1,
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "Proper disk key for epoch > historyLength, validator > validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
validatorChunkSize: 3,
|
||||
historyLength: 6,
|
||||
},
|
||||
chunkIndex: 10,
|
||||
validatorChunkIndex: 10,
|
||||
want: 30,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
chunkSize: tt.fields.chunkSize,
|
||||
validatorChunkSize: tt.fields.validatorChunkSize,
|
||||
historyLength: tt.fields.historyLength,
|
||||
}
|
||||
got := c.flatSliceID(tt.validatorChunkIndex, tt.chunkIndex)
|
||||
decoded := ssz.UnmarshallUint64(got)
|
||||
if decoded != tt.want {
|
||||
t.Errorf("diskKey() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_validatorChunkIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
validatorIndex types.ValidatorIndex
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "validator index < validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorIndex: 2,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "validator index = validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorIndex: 3,
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "validator index > validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorIndex: 99,
|
||||
want: 33,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
validatorChunkSize: tt.fields.validatorChunkSize,
|
||||
}
|
||||
if got := c.validatorChunkIndex(tt.validatorIndex); got != tt.want {
|
||||
t.Errorf("validatorChunkIndex() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_chunkOffset(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
epoch types.Epoch
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "epoch < chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
},
|
||||
epoch: 2,
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "epoch = chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
},
|
||||
epoch: 3,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "epoch > chunkSize",
|
||||
fields: &Parameters{
|
||||
chunkSize: 3,
|
||||
},
|
||||
epoch: 5,
|
||||
want: 2,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
chunkSize: tt.fields.chunkSize,
|
||||
}
|
||||
if got := c.chunkOffset(tt.epoch); got != tt.want {
|
||||
t.Errorf("chunkOffset() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_validatorOffset(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
validatorIndex types.ValidatorIndex
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
name: "validatorIndex < validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorIndex: 2,
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "validatorIndex = validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorIndex: 3,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "validatorIndex > validatorChunkSize",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorIndex: 5,
|
||||
want: 2,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
validatorChunkSize: tt.fields.validatorChunkSize,
|
||||
}
|
||||
if got := c.validatorOffset(tt.validatorIndex); got != tt.want {
|
||||
t.Errorf("validatorOffset() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_validatorIndicesInChunk(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields *Parameters
|
||||
validatorChunkIdx uint64
|
||||
want []types.ValidatorIndex
|
||||
}{
|
||||
{
|
||||
name: "Returns proper indices",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 3,
|
||||
},
|
||||
validatorChunkIdx: 2,
|
||||
want: []types.ValidatorIndex{6, 7, 8},
|
||||
},
|
||||
{
|
||||
name: "0 validator chunk size returs empty",
|
||||
fields: &Parameters{
|
||||
validatorChunkSize: 0,
|
||||
},
|
||||
validatorChunkIdx: 100,
|
||||
want: []types.ValidatorIndex{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Parameters{
|
||||
validatorChunkSize: tt.fields.validatorChunkSize,
|
||||
}
|
||||
if got := c.validatorIndicesInChunk(tt.validatorChunkIdx); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("validatorIndicesInChunk() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParameters_firstEpoch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params *Parameters
|
||||
chunkIndex uint64
|
||||
want types.Epoch
|
||||
}{
|
||||
{
|
||||
name: "first epoch of chunk 0 is 0",
|
||||
params: DefaultParams(),
|
||||
chunkIndex: 0,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "with chunk_size = 3, first epoch of chunk 1 is 3",
|
||||
params: &Parameters{
|
||||
chunkSize: 3,
|
||||
},
|
||||
chunkIndex: 1,
|
||||
want: 3,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.params.firstEpoch(tt.chunkIndex); got != tt.want {
|
||||
t.Errorf("firstEpoch() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParameters_lastEpoch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params *Parameters
|
||||
chunkIndex uint64
|
||||
want types.Epoch
|
||||
}{
|
||||
{
|
||||
name: "with chunk_size = 3, last epoch of chunk 0 is 2",
|
||||
params: &Parameters{
|
||||
chunkSize: 3,
|
||||
},
|
||||
chunkIndex: 0,
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "with chunk_size = 3, last epoch of chunk 1 is 5",
|
||||
params: &Parameters{
|
||||
chunkSize: 3,
|
||||
},
|
||||
chunkIndex: 1,
|
||||
want: 5,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.params.lastEpoch(tt.chunkIndex); got != tt.want {
|
||||
t.Errorf("lastEpoch() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
11
beacon-chain/slasher/service.go
Normal file
11
beacon-chain/slasher/service.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package slasher
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "slasher")
|
||||
|
||||
type Service struct {
|
||||
params *Parameters
|
||||
}
|
||||
Reference in New Issue
Block a user