mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
* wip * fixing tests * adding script to update workspace for eth clients * updating test sepc to 1.6.0 and fixing broadcaster test * fix specrefs * more ethspecify fixes * still trying to fix ethspecify * fixing attestation tests * fixing sha for consensus specs * removing script for now until i have something more standard * fixing more p2p tests * fixing discovery tests * attempting to fix discovery test flakeyness * attempting to fix port binding issue * more attempts to fix flakey tests * Revert "more attempts to fix flakey tests" This reverts commit25e8183703. * Revert "attempting to fix port binding issue" This reverts commit583df8000d. * Revert "attempting to fix discovery test flakeyness" This reverts commit3c76525870. * Revert "fixing discovery tests" This reverts commit8c701bf3b9. * Revert "fixing more p2p tests" This reverts commit140d5db203. * Revert "fixing attestation tests" This reverts commit26ded244cb. * fixing attestation tests * fixing more p2p tests * fixing discovery tests * attempting to fix discovery test flakeyness * attempting to fix port binding issue * more attempts to fix flakey tests * changelog * fixing import * adding some missing dependencies, but TestService_BroadcastAttestationWithDiscoveryAttempts is still failing * attempting to fix test * reverting test as it migrated to other pr * reverting test * fixing test from merge * Fix `TestService_BroadcastAttestationWithDiscoveryAttempts`. * Fix again `TestService_Start_OnlyStartsOnce`. * fixing TestListenForNewNodes * removing manual set of fulu epoch * missed a few * fixing subnet test * Update beacon-chain/rpc/eth/config/handlers_test.go Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com> * removing a few more missed spots of reverting fulu epoch setting * updating test name based on feedback * fixing rest apis, they actually need the setting of the epoch due to the guard --------- Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com> Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2454 lines
91 KiB
Go
2454 lines
91 KiB
Go
package beacon
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/OffchainLabs/go-bitfield"
|
|
"github.com/OffchainLabs/prysm/v6/api"
|
|
"github.com/OffchainLabs/prysm/v6/api/server"
|
|
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
|
blockchainmock "github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/testing"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/signing"
|
|
prysmtime "github.com/OffchainLabs/prysm/v6/beacon-chain/core/time"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/transition"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/operations/attestations"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/operations/blstoexec"
|
|
blstoexecmock "github.com/OffchainLabs/prysm/v6/beacon-chain/operations/blstoexec/mock"
|
|
slashingsmock "github.com/OffchainLabs/prysm/v6/beacon-chain/operations/slashings/mock"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/operations/synccommittee"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/operations/voluntaryexits/mock"
|
|
p2pMock "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/core"
|
|
state_native "github.com/OffchainLabs/prysm/v6/beacon-chain/state/state-native"
|
|
"github.com/OffchainLabs/prysm/v6/config/params"
|
|
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v6/crypto/bls"
|
|
"github.com/OffchainLabs/prysm/v6/crypto/bls/common"
|
|
"github.com/OffchainLabs/prysm/v6/crypto/hash"
|
|
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
|
|
"github.com/OffchainLabs/prysm/v6/encoding/ssz"
|
|
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
|
ethpbv1alpha1 "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v6/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
|
"github.com/OffchainLabs/prysm/v6/testing/util"
|
|
"github.com/OffchainLabs/prysm/v6/time/slots"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func TestListAttestationsV2(t *testing.T) {
|
|
att1 := ðpbv1alpha1.Attestation{
|
|
AggregationBits: []byte{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
}
|
|
att2 := ðpbv1alpha1.Attestation{
|
|
AggregationBits: []byte{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 4,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
}
|
|
att3 := ðpbv1alpha1.Attestation{
|
|
AggregationBits: bitfield.NewBitlist(8),
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot3"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot3"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature3"), 96),
|
|
}
|
|
att4 := ðpbv1alpha1.Attestation{
|
|
AggregationBits: bitfield.NewBitlist(8),
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 4,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot4"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot4"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature4"), 96),
|
|
}
|
|
|
|
t.Run("Pre-Electra", func(t *testing.T) {
|
|
bs, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
slot := primitives.Slot(0)
|
|
chainService := &blockchainmock.ChainService{State: bs, Slot: &slot}
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
AttestationsPool: attestations.NewPool(),
|
|
}
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.DenebForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
require.NoError(t, s.AttestationsPool.SaveAggregatedAttestations([]ethpbv1alpha1.Att{att1, att2}))
|
|
require.NoError(t, s.AttestationsPool.SaveUnaggregatedAttestations([]ethpbv1alpha1.Att{att3, att4}))
|
|
t.Run("empty request", func(t *testing.T) {
|
|
url := "http://example.com"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.Attestation
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 4, len(atts))
|
|
assert.Equal(t, "deneb", resp.Version)
|
|
})
|
|
t.Run("slot request", func(t *testing.T) {
|
|
url := "http://example.com?slot=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.Attestation
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 2, len(atts))
|
|
assert.Equal(t, "deneb", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "2", a.Data.Slot)
|
|
}
|
|
})
|
|
t.Run("index request", func(t *testing.T) {
|
|
url := "http://example.com?committee_index=4"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.Attestation
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 2, len(atts))
|
|
assert.Equal(t, "deneb", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "4", a.Data.CommitteeIndex)
|
|
}
|
|
})
|
|
t.Run("both slot + index request", func(t *testing.T) {
|
|
url := "http://example.com?slot=2&committee_index=4"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.Attestation
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 1, len(atts))
|
|
assert.Equal(t, "deneb", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "2", a.Data.Slot)
|
|
assert.Equal(t, "4", a.Data.CommitteeIndex)
|
|
}
|
|
})
|
|
})
|
|
t.Run("Post-Electra", func(t *testing.T) {
|
|
cb1 := primitives.NewAttestationCommitteeBits()
|
|
cb1.SetBitAt(1, true)
|
|
cb2 := primitives.NewAttestationCommitteeBits()
|
|
cb2.SetBitAt(2, true)
|
|
|
|
attElectra1 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: []byte{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb1,
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
}
|
|
attElectra2 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: []byte{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb2,
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
}
|
|
attElectra3 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: bitfield.NewBitlist(8),
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot3"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot3"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb1,
|
|
Signature: bytesutil.PadTo([]byte("signature3"), 96),
|
|
}
|
|
attElectra4 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: bitfield.NewBitlist(8),
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot4"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot4"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb2,
|
|
Signature: bytesutil.PadTo([]byte("signature4"), 96),
|
|
}
|
|
bs, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
config.FuluForkEpoch = config.FarFutureEpoch
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
s := &Server{
|
|
AttestationsPool: attestations.NewPool(),
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
}
|
|
// Added one pre electra attestation to ensure it is ignored.
|
|
require.NoError(t, s.AttestationsPool.SaveAggregatedAttestations([]ethpbv1alpha1.Att{attElectra1, attElectra2, att1}))
|
|
require.NoError(t, s.AttestationsPool.SaveUnaggregatedAttestations([]ethpbv1alpha1.Att{attElectra3, attElectra4, att3}))
|
|
|
|
t.Run("empty request", func(t *testing.T) {
|
|
url := "http://example.com"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 4, len(atts))
|
|
assert.Equal(t, "electra", resp.Version)
|
|
})
|
|
t.Run("slot request", func(t *testing.T) {
|
|
url := "http://example.com?slot=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 2, len(atts))
|
|
assert.Equal(t, "electra", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "2", a.Data.Slot)
|
|
}
|
|
})
|
|
t.Run("index request", func(t *testing.T) {
|
|
url := "http://example.com?committee_index=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
config.FuluForkEpoch = config.FarFutureEpoch
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 2, len(atts))
|
|
assert.Equal(t, "electra", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "0x0400000000000000", a.CommitteeBits)
|
|
}
|
|
})
|
|
t.Run("both slot + index request", func(t *testing.T) {
|
|
url := "http://example.com?slot=2&committee_index=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 1, len(atts))
|
|
assert.Equal(t, "electra", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "2", a.Data.Slot)
|
|
assert.Equal(t, "0x0400000000000000", a.CommitteeBits)
|
|
}
|
|
})
|
|
t.Run("Post-Fulu", func(t *testing.T) {
|
|
cb1 := primitives.NewAttestationCommitteeBits()
|
|
cb1.SetBitAt(1, true)
|
|
cb2 := primitives.NewAttestationCommitteeBits()
|
|
cb2.SetBitAt(2, true)
|
|
|
|
attFulu1 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: []byte{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb1,
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
}
|
|
attFulu2 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: []byte{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb2,
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
}
|
|
attFulu3 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: bitfield.NewBitlist(8),
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot3"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot3"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb1,
|
|
Signature: bytesutil.PadTo([]byte("signature3"), 96),
|
|
}
|
|
attFulu4 := ðpbv1alpha1.AttestationElectra{
|
|
AggregationBits: bitfield.NewBitlist(8),
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 0,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot4"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot4"), 32),
|
|
},
|
|
},
|
|
CommitteeBits: cb2,
|
|
Signature: bytesutil.PadTo([]byte("signature4"), 96),
|
|
}
|
|
bs, err := util.NewBeaconStateFulu()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
config.FuluForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
s := &Server{
|
|
AttestationsPool: attestations.NewPool(),
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
}
|
|
// Added one pre electra attestation to ensure it is ignored.
|
|
require.NoError(t, s.AttestationsPool.SaveAggregatedAttestations([]ethpbv1alpha1.Att{attFulu1, attFulu2, att1}))
|
|
require.NoError(t, s.AttestationsPool.SaveUnaggregatedAttestations([]ethpbv1alpha1.Att{attFulu3, attFulu4, att3}))
|
|
|
|
t.Run("empty request", func(t *testing.T) {
|
|
url := "http://example.com"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 4, len(atts))
|
|
assert.Equal(t, "fulu", resp.Version)
|
|
})
|
|
t.Run("slot request", func(t *testing.T) {
|
|
url := "http://example.com?slot=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 2, len(atts))
|
|
assert.Equal(t, "fulu", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "2", a.Data.Slot)
|
|
}
|
|
})
|
|
t.Run("index request", func(t *testing.T) {
|
|
url := "http://example.com?committee_index=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 2, len(atts))
|
|
assert.Equal(t, "fulu", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "0x0400000000000000", a.CommitteeBits)
|
|
}
|
|
})
|
|
t.Run("both slot + index request", func(t *testing.T) {
|
|
url := "http://example.com?slot=2&committee_index=2"
|
|
request := httptest.NewRequest(http.MethodGet, url, nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListAttestationsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var atts []*structs.AttestationElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &atts))
|
|
assert.Equal(t, 1, len(atts))
|
|
assert.Equal(t, "fulu", resp.Version)
|
|
for _, a := range atts {
|
|
assert.Equal(t, "2", a.Data.Slot)
|
|
assert.Equal(t, "0x0400000000000000", a.CommitteeBits)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestSubmitAttestationsV2(t *testing.T) {
|
|
transition.SkipSlotCache.Disable()
|
|
defer transition.SkipSlotCache.Enable()
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
c := params.BeaconConfig().Copy()
|
|
// Required for correct committee size calculation.
|
|
c.SlotsPerEpoch = 1
|
|
params.OverrideBeaconConfig(c)
|
|
|
|
_, keys, err := util.DeterministicDepositsAndKeys(2)
|
|
require.NoError(t, err)
|
|
validators := []*ethpbv1alpha1.Validator{
|
|
{
|
|
PublicKey: keys[0].PublicKey().Marshal(),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
{
|
|
PublicKey: keys[1].PublicKey().Marshal(),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
}
|
|
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
|
|
state.Validators = validators
|
|
state.Slot = 1
|
|
state.PreviousJustifiedCheckpoint = ðpbv1alpha1.Checkpoint{
|
|
Epoch: 0,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
}
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
b := bitfield.NewBitlist(1)
|
|
b.SetBitAt(0, true)
|
|
slot := primitives.Slot(0)
|
|
chainService := &blockchainmock.ChainService{State: bs, Slot: &slot}
|
|
s := &Server{
|
|
HeadFetcher: chainService,
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
OperationNotifier: &blockchainmock.MockOperationNotifier{},
|
|
AttestationStateFetcher: chainService,
|
|
}
|
|
|
|
t.Run("pre-electra", func(t *testing.T) {
|
|
t.Run("single", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s.Broadcaster = broadcaster
|
|
s.AttestationsPool = attestations.NewPool()
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(singleAtt)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Phase0))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
assert.Equal(t, 1, broadcaster.NumAttestations())
|
|
assert.Equal(t, "0x03", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetAggregationBits()))
|
|
assert.Equal(t, "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetSignature()))
|
|
assert.Equal(t, primitives.Slot(0), broadcaster.BroadcastAttestations[0].GetData().Slot)
|
|
assert.Equal(t, primitives.CommitteeIndex(0), broadcaster.BroadcastAttestations[0].GetData().CommitteeIndex)
|
|
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetData().BeaconBlockRoot))
|
|
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetData().Source.Root))
|
|
assert.Equal(t, primitives.Epoch(0), broadcaster.BroadcastAttestations[0].GetData().Source.Epoch)
|
|
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetData().Target.Root))
|
|
assert.Equal(t, primitives.Epoch(0), broadcaster.BroadcastAttestations[0].GetData().Target.Epoch)
|
|
assert.Equal(t, 1, s.AttestationsPool.UnaggregatedAttestationCount())
|
|
})
|
|
t.Run("multiple", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s.Broadcaster = broadcaster
|
|
s.AttestationsPool = attestations.NewPool()
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(multipleAtts)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Phase0))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
assert.Equal(t, 2, broadcaster.NumAttestations())
|
|
assert.Equal(t, 2, s.AttestationsPool.UnaggregatedAttestationCount())
|
|
})
|
|
t.Run("phase0 att post electra", func(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
config.FuluForkEpoch = config.FarFutureEpoch
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(singleAtt)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Phase0))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.ErrorContains(t, "old attestation format", errors.New(e.Message))
|
|
})
|
|
t.Run("electra att before electra", func(t *testing.T) {
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(singleAttElectra)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.ErrorContains(t, "electra attestations have not been enabled", errors.New(e.Message))
|
|
})
|
|
t.Run("no body", func(t *testing.T) {
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Phase0))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "No data submitted"))
|
|
})
|
|
t.Run("empty", func(t *testing.T) {
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString("[]")
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Phase0))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "no data submitted"))
|
|
})
|
|
t.Run("invalid", func(t *testing.T) {
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(invalidAtt)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Phase0))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &server.IndexedErrorContainer{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
require.Equal(t, 1, len(e.Failures))
|
|
assert.Equal(t, true, strings.Contains(e.Failures[0].Message, "Incorrect attestation signature"))
|
|
})
|
|
})
|
|
|
|
t.Run("post-electra", func(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
t.Run("single", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s.Broadcaster = broadcaster
|
|
s.AttestationsPool = attestations.NewPool()
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(singleAttElectra)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
assert.Equal(t, 1, broadcaster.NumAttestations())
|
|
assert.Equal(t, primitives.ValidatorIndex(1), broadcaster.BroadcastAttestations[0].GetAttestingIndex())
|
|
assert.Equal(t, "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetSignature()))
|
|
assert.Equal(t, primitives.Slot(0), broadcaster.BroadcastAttestations[0].GetData().Slot)
|
|
assert.Equal(t, primitives.CommitteeIndex(0), broadcaster.BroadcastAttestations[0].GetData().CommitteeIndex)
|
|
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetData().BeaconBlockRoot))
|
|
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetData().Source.Root))
|
|
assert.Equal(t, primitives.Epoch(0), broadcaster.BroadcastAttestations[0].GetData().Source.Epoch)
|
|
assert.Equal(t, "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", hexutil.Encode(broadcaster.BroadcastAttestations[0].GetData().Target.Root))
|
|
assert.Equal(t, primitives.Epoch(0), broadcaster.BroadcastAttestations[0].GetData().Target.Epoch)
|
|
assert.Equal(t, 1, s.AttestationsPool.UnaggregatedAttestationCount())
|
|
})
|
|
t.Run("multiple", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s.Broadcaster = broadcaster
|
|
s.AttestationsPool = attestations.NewPool()
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(multipleAttsElectra)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
assert.Equal(t, 2, broadcaster.NumAttestations())
|
|
assert.Equal(t, 2, s.AttestationsPool.UnaggregatedAttestationCount())
|
|
})
|
|
t.Run("no body", func(t *testing.T) {
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "No data submitted"))
|
|
})
|
|
t.Run("empty", func(t *testing.T) {
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString("[]")
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "no data submitted"))
|
|
})
|
|
t.Run("invalid", func(t *testing.T) {
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(invalidAttElectra)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttestationsV2(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &server.IndexedErrorContainer{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
require.Equal(t, 1, len(e.Failures))
|
|
assert.Equal(t, true, strings.Contains(e.Failures[0].Message, "Incorrect attestation signature"))
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestListVoluntaryExits(t *testing.T) {
|
|
exit1 := ðpbv1alpha1.SignedVoluntaryExit{
|
|
Exit: ðpbv1alpha1.VoluntaryExit{
|
|
Epoch: 1,
|
|
ValidatorIndex: 1,
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
}
|
|
exit2 := ðpbv1alpha1.SignedVoluntaryExit{
|
|
Exit: ðpbv1alpha1.VoluntaryExit{
|
|
Epoch: 2,
|
|
ValidatorIndex: 2,
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
}
|
|
|
|
s := &Server{
|
|
VoluntaryExitsPool: &mock.PoolMock{Exits: []*ethpbv1alpha1.SignedVoluntaryExit{exit1, exit2}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListVoluntaryExits(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.ListVoluntaryExitsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
require.Equal(t, 2, len(resp.Data))
|
|
assert.Equal(t, "0x7369676e6174757265310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", resp.Data[0].Signature)
|
|
assert.Equal(t, "1", resp.Data[0].Message.Epoch)
|
|
assert.Equal(t, "1", resp.Data[0].Message.ValidatorIndex)
|
|
assert.Equal(t, "0x7369676e6174757265320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", resp.Data[1].Signature)
|
|
assert.Equal(t, "2", resp.Data[1].Message.Epoch)
|
|
assert.Equal(t, "2", resp.Data[1].Message.ValidatorIndex)
|
|
}
|
|
|
|
func TestSubmitVoluntaryExit(t *testing.T) {
|
|
transition.SkipSlotCache.Disable()
|
|
defer transition.SkipSlotCache.Enable()
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
_, keys, err := util.DeterministicDepositsAndKeys(1)
|
|
require.NoError(t, err)
|
|
validator := ðpbv1alpha1.Validator{
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: keys[0].PublicKey().Marshal(),
|
|
}
|
|
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
|
|
state.Validators = []*ethpbv1alpha1.Validator{validator}
|
|
// Satisfy activity time required before exiting.
|
|
state.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().ShardCommitteePeriod))
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
|
VoluntaryExitsPool: &mock.PoolMock{},
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err = body.WriteString(exit1)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitVoluntaryExit(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
require.NoError(t, err)
|
|
pendingExits, err := s.VoluntaryExitsPool.PendingExits()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(pendingExits))
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
})
|
|
t.Run("across fork", func(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.AltairForkEpoch = params.BeaconConfig().ShardCommitteePeriod + 1
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
bs, _ := util.DeterministicGenesisState(t, 1)
|
|
// Satisfy activity time required before exiting.
|
|
require.NoError(t, bs.SetSlot(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().ShardCommitteePeriod))))
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
|
VoluntaryExitsPool: &mock.PoolMock{},
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(exit2)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitVoluntaryExit(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
require.NoError(t, err)
|
|
pendingExits, err := s.VoluntaryExitsPool.PendingExits()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(pendingExits))
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
})
|
|
t.Run("no body", func(t *testing.T) {
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s := &Server{}
|
|
s.SubmitVoluntaryExit(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "No data submitted"))
|
|
})
|
|
t.Run("invalid", func(t *testing.T) {
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(invalidExit1)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s := &Server{}
|
|
s.SubmitVoluntaryExit(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
})
|
|
t.Run("wrong signature", func(t *testing.T) {
|
|
bs, _ := util.DeterministicGenesisState(t, 1)
|
|
s := &Server{ChainInfoFetcher: &blockchainmock.ChainService{State: bs}}
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(invalidExit2)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitVoluntaryExit(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "Invalid exit"))
|
|
})
|
|
t.Run("invalid validator index", func(t *testing.T) {
|
|
_, keys, err := util.DeterministicDepositsAndKeys(1)
|
|
require.NoError(t, err)
|
|
validator := ðpbv1alpha1.Validator{
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: keys[0].PublicKey().Marshal(),
|
|
}
|
|
bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error {
|
|
state.Validators = []*ethpbv1alpha1.Validator{validator}
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
s := &Server{ChainInfoFetcher: &blockchainmock.ChainService{State: bs}}
|
|
|
|
var body bytes.Buffer
|
|
_, err = body.WriteString(invalidExit3)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitVoluntaryExit(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "Could not get validator"))
|
|
})
|
|
}
|
|
|
|
func TestSubmitSyncCommitteeSignatures(t *testing.T) {
|
|
st, _ := util.DeterministicGenesisStateAltair(t, 10)
|
|
|
|
t.Run("single", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
CoreService: &core.Service{
|
|
SyncCommitteePool: synccommittee.NewStore(),
|
|
P2P: broadcaster,
|
|
HeadFetcher: &blockchainmock.ChainService{
|
|
State: st,
|
|
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
|
|
},
|
|
},
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(singleSyncCommitteeMsg)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitSyncCommitteeSignatures(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
require.NoError(t, err)
|
|
msgsInPool, err := s.CoreService.SyncCommitteePool.SyncCommitteeMessages(1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(msgsInPool))
|
|
assert.Equal(t, primitives.Slot(1), msgsInPool[0].Slot)
|
|
assert.Equal(t, "0xbacd20f09da907734434f052bd4c9503aa16bab1960e89ea20610d08d064481c", hexutil.Encode(msgsInPool[0].BlockRoot))
|
|
assert.Equal(t, primitives.ValidatorIndex(1), msgsInPool[0].ValidatorIndex)
|
|
assert.Equal(t, "0xb591bd4ca7d745b6e027879645d7c014fecb8c58631af070f7607acc0c1c948a5102a33267f0e4ba41a85b254b07df91185274375b2e6436e37e81d2fd46cb3751f5a6c86efb7499c1796c0c17e122a54ac067bb0f5ff41f3241659cceb0c21c", hexutil.Encode(msgsInPool[0].Signature))
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
})
|
|
t.Run("multiple", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
CoreService: &core.Service{
|
|
SyncCommitteePool: synccommittee.NewStore(),
|
|
P2P: broadcaster,
|
|
HeadFetcher: &blockchainmock.ChainService{
|
|
State: st,
|
|
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
|
|
},
|
|
},
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(multipleSyncCommitteeMsg)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitSyncCommitteeSignatures(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
require.NoError(t, err)
|
|
msgsInPool, err := s.CoreService.SyncCommitteePool.SyncCommitteeMessages(1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(msgsInPool))
|
|
msgsInPool, err = s.CoreService.SyncCommitteePool.SyncCommitteeMessages(2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(msgsInPool))
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
})
|
|
t.Run("invalid", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
CoreService: &core.Service{
|
|
SyncCommitteePool: synccommittee.NewStore(),
|
|
P2P: broadcaster,
|
|
HeadFetcher: &blockchainmock.ChainService{
|
|
State: st,
|
|
},
|
|
},
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString(invalidSyncCommitteeMsg)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitSyncCommitteeSignatures(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
require.NoError(t, err)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
msgsInPool, err := s.CoreService.SyncCommitteePool.SyncCommitteeMessages(1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(msgsInPool))
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled.Load())
|
|
})
|
|
t.Run("empty", func(t *testing.T) {
|
|
s := &Server{}
|
|
|
|
var body bytes.Buffer
|
|
_, err := body.WriteString("[]")
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitSyncCommitteeSignatures(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "No data submitted"))
|
|
})
|
|
t.Run("no body", func(t *testing.T) {
|
|
s := &Server{}
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitSyncCommitteeSignatures(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.Equal(t, true, strings.Contains(e.Message, "No data submitted"))
|
|
})
|
|
}
|
|
|
|
func TestListBLSToExecutionChanges(t *testing.T) {
|
|
change1 := ðpbv1alpha1.SignedBLSToExecutionChange{
|
|
Message: ðpbv1alpha1.BLSToExecutionChange{
|
|
ValidatorIndex: 1,
|
|
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey1"), 48),
|
|
ToExecutionAddress: bytesutil.PadTo([]byte("address1"), 20),
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
}
|
|
change2 := ðpbv1alpha1.SignedBLSToExecutionChange{
|
|
Message: ðpbv1alpha1.BLSToExecutionChange{
|
|
ValidatorIndex: 2,
|
|
FromBlsPubkey: bytesutil.PadTo([]byte("pubkey2"), 48),
|
|
ToExecutionAddress: bytesutil.PadTo([]byte("address2"), 20),
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
}
|
|
|
|
s := &Server{
|
|
BLSChangesPool: &blstoexecmock.PoolMock{Changes: []*ethpbv1alpha1.SignedBLSToExecutionChange{change1, change2}},
|
|
}
|
|
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ListBLSToExecutionChanges(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
|
|
resp := &structs.BLSToExecutionChangesPoolResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.Equal(t, 2, len(resp.Data))
|
|
assert.DeepEqual(t, structs.SignedBLSChangeFromConsensus(change1), resp.Data[0])
|
|
assert.DeepEqual(t, structs.SignedBLSChangeFromConsensus(change2), resp.Data[1])
|
|
}
|
|
|
|
func TestSubmitSignedBLSToExecutionChanges_Ok(t *testing.T) {
|
|
transition.SkipSlotCache.Disable()
|
|
defer transition.SkipSlotCache.Enable()
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
c := params.BeaconConfig().Copy()
|
|
// Required for correct committee size calculation.
|
|
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
|
|
params.OverrideBeaconConfig(c)
|
|
|
|
spb := ðpbv1alpha1.BeaconStateCapella{
|
|
Fork: ðpbv1alpha1.Fork{
|
|
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
|
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
|
Epoch: params.BeaconConfig().CapellaForkEpoch,
|
|
},
|
|
}
|
|
numValidators := 10
|
|
validators := make([]*ethpbv1alpha1.Validator, numValidators)
|
|
blsChanges := make([]*ethpbv1alpha1.BLSToExecutionChange, numValidators)
|
|
spb.Balances = make([]uint64, numValidators)
|
|
privKeys := make([]common.SecretKey, numValidators)
|
|
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
|
|
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
|
|
|
|
for i := range validators {
|
|
v := ðpbv1alpha1.Validator{}
|
|
v.EffectiveBalance = maxEffectiveBalance
|
|
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
|
|
v.WithdrawalCredentials = make([]byte, 32)
|
|
priv, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
privKeys[i] = priv
|
|
pubkey := priv.PublicKey().Marshal()
|
|
|
|
message := ðpbv1alpha1.BLSToExecutionChange{
|
|
ToExecutionAddress: executionAddress,
|
|
ValidatorIndex: primitives.ValidatorIndex(i),
|
|
FromBlsPubkey: pubkey,
|
|
}
|
|
|
|
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
|
|
digest := hashFn.Hash(pubkey)
|
|
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
|
|
copy(v.WithdrawalCredentials, digest[:])
|
|
validators[i] = v
|
|
blsChanges[i] = message
|
|
}
|
|
spb.Validators = validators
|
|
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
|
require.NoError(t, err)
|
|
spb.Slot = slot
|
|
st, err := state_native.InitializeFromProtoCapella(spb)
|
|
require.NoError(t, err)
|
|
|
|
signedChanges := make([]*structs.SignedBLSToExecutionChange, numValidators)
|
|
for i, message := range blsChanges {
|
|
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
|
require.NoError(t, err)
|
|
signed := &structs.SignedBLSToExecutionChange{
|
|
Message: structs.BLSChangeFromConsensus(message),
|
|
Signature: hexutil.Encode(signature),
|
|
}
|
|
signedChanges[i] = signed
|
|
}
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
chainService := &blockchainmock.ChainService{State: st}
|
|
s := &Server{
|
|
HeadFetcher: chainService,
|
|
ChainInfoFetcher: chainService,
|
|
AttestationsPool: attestations.NewPool(),
|
|
Broadcaster: broadcaster,
|
|
OperationNotifier: &blockchainmock.MockOperationNotifier{},
|
|
BLSChangesPool: blstoexec.NewPool(),
|
|
}
|
|
jsonBytes, err := json.Marshal(signedChanges)
|
|
require.NoError(t, err)
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", bytes.NewReader(jsonBytes))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
s.SubmitBLSToExecutionChanges(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
time.Sleep(100 * time.Millisecond) // Delay to let the routine start
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages))
|
|
|
|
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
|
|
require.Equal(t, len(poolChanges), len(signedChanges))
|
|
require.NoError(t, err)
|
|
for i, v1alphaChange := range poolChanges {
|
|
sc, err := signedChanges[i].ToConsensus()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, v1alphaChange, sc)
|
|
}
|
|
}
|
|
|
|
func TestSubmitSignedBLSToExecutionChanges_Bellatrix(t *testing.T) {
|
|
transition.SkipSlotCache.Disable()
|
|
defer transition.SkipSlotCache.Enable()
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
c := params.BeaconConfig().Copy()
|
|
// Required for correct committee size calculation.
|
|
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
|
|
params.OverrideBeaconConfig(c)
|
|
|
|
spb := ðpbv1alpha1.BeaconStateBellatrix{
|
|
Fork: ðpbv1alpha1.Fork{
|
|
CurrentVersion: params.BeaconConfig().BellatrixForkVersion,
|
|
PreviousVersion: params.BeaconConfig().AltairForkVersion,
|
|
Epoch: params.BeaconConfig().BellatrixForkEpoch,
|
|
},
|
|
}
|
|
numValidators := 10
|
|
validators := make([]*ethpbv1alpha1.Validator, numValidators)
|
|
blsChanges := make([]*ethpbv1alpha1.BLSToExecutionChange, numValidators)
|
|
spb.Balances = make([]uint64, numValidators)
|
|
privKeys := make([]common.SecretKey, numValidators)
|
|
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
|
|
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
|
|
|
|
for i := range validators {
|
|
v := ðpbv1alpha1.Validator{}
|
|
v.EffectiveBalance = maxEffectiveBalance
|
|
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
|
|
v.WithdrawalCredentials = make([]byte, 32)
|
|
priv, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
privKeys[i] = priv
|
|
pubkey := priv.PublicKey().Marshal()
|
|
|
|
message := ðpbv1alpha1.BLSToExecutionChange{
|
|
ToExecutionAddress: executionAddress,
|
|
ValidatorIndex: primitives.ValidatorIndex(i),
|
|
FromBlsPubkey: pubkey,
|
|
}
|
|
|
|
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
|
|
digest := hashFn.Hash(pubkey)
|
|
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
|
|
copy(v.WithdrawalCredentials, digest[:])
|
|
validators[i] = v
|
|
blsChanges[i] = message
|
|
}
|
|
spb.Validators = validators
|
|
slot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
|
|
require.NoError(t, err)
|
|
spb.Slot = slot
|
|
st, err := state_native.InitializeFromProtoBellatrix(spb)
|
|
require.NoError(t, err)
|
|
|
|
spc := ðpbv1alpha1.BeaconStateCapella{
|
|
Fork: ðpbv1alpha1.Fork{
|
|
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
|
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
|
Epoch: params.BeaconConfig().CapellaForkEpoch,
|
|
},
|
|
}
|
|
slot, err = slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
|
require.NoError(t, err)
|
|
spc.Slot = slot
|
|
|
|
stc, err := state_native.InitializeFromProtoCapella(spc)
|
|
require.NoError(t, err)
|
|
|
|
signedChanges := make([]*structs.SignedBLSToExecutionChange, numValidators)
|
|
for i, message := range blsChanges {
|
|
signature, err := signing.ComputeDomainAndSign(stc, prysmtime.CurrentEpoch(stc), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
|
require.NoError(t, err)
|
|
|
|
signedChanges[i] = &structs.SignedBLSToExecutionChange{
|
|
Message: structs.BLSChangeFromConsensus(message),
|
|
Signature: hexutil.Encode(signature),
|
|
}
|
|
}
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
chainService := &blockchainmock.ChainService{State: st}
|
|
s := &Server{
|
|
HeadFetcher: chainService,
|
|
ChainInfoFetcher: chainService,
|
|
AttestationsPool: attestations.NewPool(),
|
|
Broadcaster: broadcaster,
|
|
OperationNotifier: &blockchainmock.MockOperationNotifier{},
|
|
BLSChangesPool: blstoexec.NewPool(),
|
|
}
|
|
|
|
jsonBytes, err := json.Marshal(signedChanges)
|
|
require.NoError(t, err)
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", bytes.NewReader(jsonBytes))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitBLSToExecutionChanges(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
// Check that we didn't broadcast the messages but did in fact fill in
|
|
// the pool
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled.Load())
|
|
|
|
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
|
|
require.Equal(t, len(poolChanges), len(signedChanges))
|
|
require.NoError(t, err)
|
|
for i, v1alphaChange := range poolChanges {
|
|
sc, err := signedChanges[i].ToConsensus()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, v1alphaChange, sc)
|
|
}
|
|
}
|
|
|
|
func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) {
|
|
transition.SkipSlotCache.Disable()
|
|
defer transition.SkipSlotCache.Enable()
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
c := params.BeaconConfig().Copy()
|
|
// Required for correct committee size calculation.
|
|
c.CapellaForkEpoch = c.BellatrixForkEpoch.Add(2)
|
|
params.OverrideBeaconConfig(c)
|
|
|
|
spb := ðpbv1alpha1.BeaconStateCapella{
|
|
Fork: ðpbv1alpha1.Fork{
|
|
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
|
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
|
Epoch: params.BeaconConfig().CapellaForkEpoch,
|
|
},
|
|
}
|
|
numValidators := 10
|
|
validators := make([]*ethpbv1alpha1.Validator, numValidators)
|
|
blsChanges := make([]*ethpbv1alpha1.BLSToExecutionChange, numValidators)
|
|
spb.Balances = make([]uint64, numValidators)
|
|
privKeys := make([]common.SecretKey, numValidators)
|
|
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance
|
|
executionAddress := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}
|
|
|
|
for i := range validators {
|
|
v := ðpbv1alpha1.Validator{}
|
|
v.EffectiveBalance = maxEffectiveBalance
|
|
v.WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
|
|
v.WithdrawalCredentials = make([]byte, 32)
|
|
priv, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
privKeys[i] = priv
|
|
pubkey := priv.PublicKey().Marshal()
|
|
|
|
message := ðpbv1alpha1.BLSToExecutionChange{
|
|
ToExecutionAddress: executionAddress,
|
|
ValidatorIndex: primitives.ValidatorIndex(i),
|
|
FromBlsPubkey: pubkey,
|
|
}
|
|
|
|
hashFn := ssz.NewHasherFunc(hash.CustomSHA256Hasher())
|
|
digest := hashFn.Hash(pubkey)
|
|
digest[0] = params.BeaconConfig().BLSWithdrawalPrefixByte
|
|
copy(v.WithdrawalCredentials, digest[:])
|
|
validators[i] = v
|
|
blsChanges[i] = message
|
|
}
|
|
spb.Validators = validators
|
|
slot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
|
require.NoError(t, err)
|
|
spb.Slot = slot
|
|
st, err := state_native.InitializeFromProtoCapella(spb)
|
|
require.NoError(t, err)
|
|
|
|
signedChanges := make([]*structs.SignedBLSToExecutionChange, numValidators)
|
|
for i, message := range blsChanges {
|
|
signature, err := signing.ComputeDomainAndSign(st, prysmtime.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, privKeys[i])
|
|
require.NoError(t, err)
|
|
if i == 1 {
|
|
signature[0] = 0x00
|
|
}
|
|
signedChanges[i] = &structs.SignedBLSToExecutionChange{
|
|
Message: structs.BLSChangeFromConsensus(message),
|
|
Signature: hexutil.Encode(signature),
|
|
}
|
|
}
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
chainService := &blockchainmock.ChainService{State: st}
|
|
s := &Server{
|
|
HeadFetcher: chainService,
|
|
ChainInfoFetcher: chainService,
|
|
AttestationsPool: attestations.NewPool(),
|
|
Broadcaster: broadcaster,
|
|
OperationNotifier: &blockchainmock.MockOperationNotifier{},
|
|
BLSChangesPool: blstoexec.NewPool(),
|
|
}
|
|
|
|
jsonBytes, err := json.Marshal(signedChanges)
|
|
require.NoError(t, err)
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/beacon/pool/bls_to_execution_changes", bytes.NewReader(jsonBytes))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitBLSToExecutionChanges(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
time.Sleep(10 * time.Millisecond) // Delay to allow the routine to start
|
|
require.StringContains(t, "One or more messages failed validation", writer.Body.String())
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
assert.Equal(t, numValidators, len(broadcaster.BroadcastMessages)+1)
|
|
|
|
poolChanges, err := s.BLSChangesPool.PendingBLSToExecChanges()
|
|
require.Equal(t, len(poolChanges)+1, len(signedChanges))
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, structs.SignedBLSChangeFromConsensus(poolChanges[0]), signedChanges[0])
|
|
|
|
for i := 2; i < numValidators; i++ {
|
|
require.DeepEqual(t, structs.SignedBLSChangeFromConsensus(poolChanges[i-1]), signedChanges[i])
|
|
}
|
|
}
|
|
|
|
func TestGetAttesterSlashingsV2(t *testing.T) {
|
|
slashing1PreElectra := ðpbv1alpha1.AttesterSlashing{
|
|
Attestation_1: ðpbv1alpha1.IndexedAttestation{
|
|
AttestingIndices: []uint64{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
},
|
|
Attestation_2: ðpbv1alpha1.IndexedAttestation{
|
|
AttestingIndices: []uint64{2, 20},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 2,
|
|
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 20,
|
|
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
},
|
|
}
|
|
slashing2PreElectra := ðpbv1alpha1.AttesterSlashing{
|
|
Attestation_1: ðpbv1alpha1.IndexedAttestation{
|
|
AttestingIndices: []uint64{3, 30},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 3,
|
|
CommitteeIndex: 3,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 3,
|
|
Root: bytesutil.PadTo([]byte("sourceroot3"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 30,
|
|
Root: bytesutil.PadTo([]byte("targetroot3"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature3"), 96),
|
|
},
|
|
Attestation_2: ðpbv1alpha1.IndexedAttestation{
|
|
AttestingIndices: []uint64{4, 40},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 4,
|
|
CommitteeIndex: 4,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 4,
|
|
Root: bytesutil.PadTo([]byte("sourceroot4"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 40,
|
|
Root: bytesutil.PadTo([]byte("targetroot4"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature4"), 96),
|
|
},
|
|
}
|
|
slashingPostElectra := ðpbv1alpha1.AttesterSlashingElectra{
|
|
Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{
|
|
AttestingIndices: []uint64{1, 10},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
},
|
|
Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{
|
|
AttestingIndices: []uint64{2, 20},
|
|
Data: ðpbv1alpha1.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 2,
|
|
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 20,
|
|
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
|
|
},
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
},
|
|
}
|
|
|
|
t.Run("post-electra-ok-1-pre-slashing", func(t *testing.T) {
|
|
bs, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 100
|
|
config.FuluForkEpoch = config.FarFutureEpoch
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashingPostElectra, slashing1PreElectra}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetAttesterSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
assert.Equal(t, "electra", resp.Version)
|
|
|
|
// Unmarshal resp.Data into a slice of slashings
|
|
var slashings []*structs.AttesterSlashingElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &slashings))
|
|
|
|
ss, err := structs.AttesterSlashingsElectraToConsensus(slashings)
|
|
require.NoError(t, err)
|
|
|
|
require.DeepEqual(t, slashingPostElectra, ss[0])
|
|
})
|
|
|
|
t.Run("post-electra-ok", func(t *testing.T) {
|
|
bs, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 100
|
|
config.FuluForkEpoch = config.FarFutureEpoch
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashingPostElectra}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetAttesterSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
assert.Equal(t, "electra", resp.Version)
|
|
|
|
// Unmarshal resp.Data into a slice of slashings
|
|
var slashings []*structs.AttesterSlashingElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &slashings))
|
|
|
|
ss, err := structs.AttesterSlashingsElectraToConsensus(slashings)
|
|
require.NoError(t, err)
|
|
|
|
require.DeepEqual(t, slashingPostElectra, ss[0])
|
|
})
|
|
t.Run("pre-electra-ok", func(t *testing.T) {
|
|
bs, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
slot := primitives.Slot(0)
|
|
chainService := &blockchainmock.ChainService{State: bs, Slot: &slot}
|
|
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PreElectra, slashing2PreElectra}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetAttesterSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
|
|
var slashings []*structs.AttesterSlashing
|
|
require.NoError(t, json.Unmarshal(resp.Data, &slashings))
|
|
|
|
ss, err := structs.AttesterSlashingsToConsensus(slashings)
|
|
require.NoError(t, err)
|
|
|
|
require.DeepEqual(t, slashing1PreElectra, ss[0])
|
|
require.DeepEqual(t, slashing2PreElectra, ss[1])
|
|
})
|
|
t.Run("no-slashings", func(t *testing.T) {
|
|
bs, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 100
|
|
config.FuluForkEpoch = config.FarFutureEpoch
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetAttesterSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
assert.Equal(t, "electra", resp.Version)
|
|
|
|
// Unmarshal resp.Data into a slice of slashings
|
|
var slashings []*structs.AttesterSlashingElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &slashings))
|
|
require.NotNil(t, slashings)
|
|
require.Equal(t, 0, len(slashings))
|
|
|
|
t.Run("Post-Fulu", func(t *testing.T) {
|
|
t.Run("post-fulu-ok", func(t *testing.T) {
|
|
bs, err := util.NewBeaconStateFulu()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
config.FuluForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashingPostElectra}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetAttesterSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
assert.Equal(t, "fulu", resp.Version)
|
|
|
|
// Unmarshal resp.Data into a slice of slashings
|
|
var slashings []*structs.AttesterSlashingElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &slashings))
|
|
|
|
ss, err := structs.AttesterSlashingsElectraToConsensus(slashings)
|
|
require.NoError(t, err)
|
|
|
|
require.DeepEqual(t, slashingPostElectra, ss[0])
|
|
})
|
|
t.Run("no-slashings", func(t *testing.T) {
|
|
bs, err := util.NewBeaconStateFulu()
|
|
require.NoError(t, err)
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.ElectraForkEpoch = 0
|
|
config.FuluForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{State: bs}
|
|
s := &Server{
|
|
ChainInfoFetcher: chainService,
|
|
TimeFetcher: chainService,
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetAttesterSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
assert.Equal(t, "fulu", resp.Version)
|
|
|
|
// Unmarshal resp.Data into a slice of slashings
|
|
var slashings []*structs.AttesterSlashingElectra
|
|
require.NoError(t, json.Unmarshal(resp.Data, &slashings))
|
|
require.NotNil(t, slashings)
|
|
require.Equal(t, 0, len(slashings))
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGetProposerSlashings(t *testing.T) {
|
|
bs, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
slashing1 := ðpbv1alpha1.ProposerSlashing{
|
|
Header_1: ðpbv1alpha1.SignedBeaconBlockHeader{
|
|
Header: ðpbv1alpha1.BeaconBlockHeader{
|
|
Slot: 1,
|
|
ProposerIndex: 1,
|
|
ParentRoot: bytesutil.PadTo([]byte("parentroot1"), 32),
|
|
StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32),
|
|
BodyRoot: bytesutil.PadTo([]byte("bodyroot1"), 32),
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature1"), 96),
|
|
},
|
|
Header_2: ðpbv1alpha1.SignedBeaconBlockHeader{
|
|
Header: ðpbv1alpha1.BeaconBlockHeader{
|
|
Slot: 2,
|
|
ProposerIndex: 2,
|
|
ParentRoot: bytesutil.PadTo([]byte("parentroot2"), 32),
|
|
StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32),
|
|
BodyRoot: bytesutil.PadTo([]byte("bodyroot2"), 32),
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature2"), 96),
|
|
},
|
|
}
|
|
slashing2 := ðpbv1alpha1.ProposerSlashing{
|
|
Header_1: ðpbv1alpha1.SignedBeaconBlockHeader{
|
|
Header: ðpbv1alpha1.BeaconBlockHeader{
|
|
Slot: 3,
|
|
ProposerIndex: 3,
|
|
ParentRoot: bytesutil.PadTo([]byte("parentroot3"), 32),
|
|
StateRoot: bytesutil.PadTo([]byte("stateroot3"), 32),
|
|
BodyRoot: bytesutil.PadTo([]byte("bodyroot3"), 32),
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature3"), 96),
|
|
},
|
|
Header_2: ðpbv1alpha1.SignedBeaconBlockHeader{
|
|
Header: ðpbv1alpha1.BeaconBlockHeader{
|
|
Slot: 4,
|
|
ProposerIndex: 4,
|
|
ParentRoot: bytesutil.PadTo([]byte("parentroot4"), 32),
|
|
StateRoot: bytesutil.PadTo([]byte("stateroot4"), 32),
|
|
BodyRoot: bytesutil.PadTo([]byte("bodyroot4"), 32),
|
|
},
|
|
Signature: bytesutil.PadTo([]byte("signature4"), 96),
|
|
},
|
|
}
|
|
|
|
s := &Server{
|
|
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
|
SlashingsPool: &slashingsmock.PoolMock{PendingPropSlashings: []*ethpbv1alpha1.ProposerSlashing{slashing1, slashing2}},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/beacon/pool/attester_slashings", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetProposerSlashings(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetProposerSlashingsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.NotNil(t, resp)
|
|
require.NotNil(t, resp.Data)
|
|
assert.Equal(t, 2, len(resp.Data))
|
|
}
|
|
|
|
func TestSubmitAttesterSlashingsV2(t *testing.T) {
|
|
ctx := t.Context()
|
|
|
|
transition.SkipSlotCache.Disable()
|
|
defer transition.SkipSlotCache.Enable()
|
|
|
|
attestationData1 := ðpbv1alpha1.AttestationData{
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
|
|
},
|
|
}
|
|
attestationData2 := ðpbv1alpha1.AttestationData{
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
|
|
Source: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
|
|
},
|
|
Target: ðpbv1alpha1.Checkpoint{
|
|
Epoch: 10,
|
|
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
|
|
},
|
|
}
|
|
|
|
t.Run("ok", func(t *testing.T) {
|
|
attestationData1.Slot = 1
|
|
attestationData2.Slot = 1
|
|
electraSlashing := ðpbv1alpha1.AttesterSlashingElectra{
|
|
Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{
|
|
AttestingIndices: []uint64{0},
|
|
Data: attestationData1,
|
|
Signature: make([]byte, 96),
|
|
},
|
|
Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{
|
|
AttestingIndices: []uint64{0},
|
|
Data: attestationData2,
|
|
Signature: make([]byte, 96),
|
|
},
|
|
}
|
|
|
|
_, keys, err := util.DeterministicDepositsAndKeys(1)
|
|
require.NoError(t, err)
|
|
validator := ðpbv1alpha1.Validator{
|
|
PublicKey: keys[0].PublicKey().Marshal(),
|
|
}
|
|
|
|
ebs, err := util.NewBeaconStateElectra(func(state *ethpbv1alpha1.BeaconStateElectra) error {
|
|
state.Validators = []*ethpbv1alpha1.Validator{validator}
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{electraSlashing.Attestation_1, electraSlashing.Attestation_2} {
|
|
sb, err := signing.ComputeDomainAndSign(ebs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0])
|
|
require.NoError(t, err)
|
|
sig, err := bls.SignatureFromBytes(sb)
|
|
require.NoError(t, err)
|
|
att.Signature = sig.Marshal()
|
|
}
|
|
|
|
chainmock := &blockchainmock.ChainService{State: ebs}
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
ChainInfoFetcher: chainmock,
|
|
SlashingsPool: &slashingsmock.PoolMock{},
|
|
Broadcaster: broadcaster,
|
|
OperationNotifier: chainmock.OperationNotifier(),
|
|
}
|
|
|
|
toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{electraSlashing})
|
|
b, err := json.Marshal(toSubmit[0])
|
|
require.NoError(t, err)
|
|
var body bytes.Buffer
|
|
_, err = body.Write(b)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_electras", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, ebs, true)
|
|
require.Equal(t, 1, len(pendingSlashings))
|
|
require.Equal(t, 1, broadcaster.NumMessages())
|
|
assert.DeepEqual(t, electraSlashing, pendingSlashings[0])
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra)
|
|
assert.Equal(t, true, ok)
|
|
})
|
|
t.Run("across-fork", func(t *testing.T) {
|
|
attestationData1.Slot = params.BeaconConfig().SlotsPerEpoch
|
|
attestationData2.Slot = params.BeaconConfig().SlotsPerEpoch
|
|
slashing := ðpbv1alpha1.AttesterSlashingElectra{
|
|
Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{
|
|
AttestingIndices: []uint64{0},
|
|
Data: attestationData1,
|
|
Signature: make([]byte, 96),
|
|
},
|
|
Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{
|
|
AttestingIndices: []uint64{0},
|
|
Data: attestationData2,
|
|
Signature: make([]byte, 96),
|
|
},
|
|
}
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
config := params.BeaconConfig()
|
|
config.AltairForkEpoch = 1
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
bs, keys := util.DeterministicGenesisState(t, 1)
|
|
newBs := bs.Copy()
|
|
newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch)
|
|
require.NoError(t, err)
|
|
|
|
for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} {
|
|
sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0])
|
|
require.NoError(t, err)
|
|
sig, err := bls.SignatureFromBytes(sb)
|
|
require.NoError(t, err)
|
|
att.Signature = sig.Marshal()
|
|
}
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
chainmock := &blockchainmock.ChainService{State: bs}
|
|
s := &Server{
|
|
ChainInfoFetcher: chainmock,
|
|
SlashingsPool: &slashingsmock.PoolMock{},
|
|
Broadcaster: broadcaster,
|
|
OperationNotifier: chainmock.OperationNotifier(),
|
|
}
|
|
|
|
toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing})
|
|
b, err := json.Marshal(toSubmit[0])
|
|
require.NoError(t, err)
|
|
var body bytes.Buffer
|
|
_, err = body.Write(b)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true)
|
|
require.Equal(t, 1, len(pendingSlashings))
|
|
assert.DeepEqual(t, slashing, pendingSlashings[0])
|
|
require.Equal(t, 1, broadcaster.NumMessages())
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
|
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra)
|
|
assert.Equal(t, true, ok)
|
|
})
|
|
t.Run("invalid-slashing", func(t *testing.T) {
|
|
bs, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
|
SlashingsPool: &slashingsmock.PoolMock{},
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err = body.WriteString(invalidAttesterSlashing)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body)
|
|
request.Header.Set(api.VersionHeader, version.String(version.Electra))
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitAttesterSlashingsV2(writer, request)
|
|
require.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.StringContains(t, "Invalid attester slashing", e.Message)
|
|
})
|
|
}
|
|
|
|
func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) {
|
|
bs, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
s := &Server{
|
|
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
|
|
SlashingsPool: &slashingsmock.PoolMock{},
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
var body bytes.Buffer
|
|
_, err = body.WriteString(invalidProposerSlashing)
|
|
require.NoError(t, err)
|
|
request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/proposer_slashings", &body)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.SubmitProposerSlashing(writer, request)
|
|
require.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &httputil.DefaultJsonError{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.StringContains(t, "Invalid proposer slashing", e.Message)
|
|
}
|
|
|
|
var (
|
|
singleAtt = `[
|
|
{
|
|
"aggregation_bits": "0x03",
|
|
"signature": "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
}
|
|
}
|
|
}
|
|
]`
|
|
multipleAtts = `[
|
|
{
|
|
"aggregation_bits": "0x03",
|
|
"signature": "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0x736f75726365726f6f7431000000000000000000000000000000000000000000"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0x746172676574726f6f7431000000000000000000000000000000000000000000"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"aggregation_bits": "0x03",
|
|
"signature": "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0x736f75726365726f6f7431000000000000000000000000000000000000000000"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0x746172676574726f6f7432000000000000000000000000000000000000000000"
|
|
}
|
|
}
|
|
}
|
|
]`
|
|
// signature is invalid
|
|
invalidAtt = `[
|
|
{
|
|
"aggregation_bits": "0x03",
|
|
"signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
}
|
|
}
|
|
}
|
|
]`
|
|
singleAttElectra = `[
|
|
{
|
|
"committee_index": "0",
|
|
"attester_index": "1",
|
|
"signature": "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
}
|
|
}
|
|
}
|
|
]`
|
|
multipleAttsElectra = `[
|
|
{
|
|
"committee_index": "0",
|
|
"attester_index": "0",
|
|
"signature": "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0x736f75726365726f6f7431000000000000000000000000000000000000000000"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0x746172676574726f6f7431000000000000000000000000000000000000000000"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"committee_index": "0",
|
|
"attester_index": "1",
|
|
"signature": "0x8146f4397bfd8fd057ebbcd6a67327bdc7ed5fb650533edcb6377b650dea0b6da64c14ecd60846d5c0a0cd43893d6972092500f82c9d8a955e2b58c5ed3cbe885d84008ace6bd86ba9e23652f58e2ec207cec494c916063257abf285b9b15b15",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0x736f75726365726f6f7431000000000000000000000000000000000000000000"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0x746172676574726f6f7432000000000000000000000000000000000000000000"
|
|
}
|
|
}
|
|
}
|
|
]`
|
|
// signature is invalid
|
|
invalidAttElectra = `[
|
|
{
|
|
"committee_index": "0",
|
|
"attester_index": "0",
|
|
"signature": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
"data": {
|
|
"slot": "0",
|
|
"index": "0",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"target": {
|
|
"epoch": "0",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
}
|
|
}
|
|
}
|
|
]`
|
|
exit1 = `{
|
|
"message": {
|
|
"epoch": "0",
|
|
"validator_index": "0"
|
|
},
|
|
"signature": "0xaf20377dabe56887f72273806ea7f3bab3df464fe0178b2ec9bb83d891bf038671c222e2fa7fc0b3e83a0a86ecf235f6104f8130d9e3177cdf5391953fcebb9676f906f4e366b95cb4d734f48f7fc0f116c643519a58a3bb1f7501a1f64b87d2"
|
|
}`
|
|
exit2 = fmt.Sprintf(`{
|
|
"message": {
|
|
"epoch": "%d",
|
|
"validator_index": "0"
|
|
},
|
|
"signature": "0xa430330829331089c4381427217231c32c26ac551de410961002491257b1ef50c3d49a89fc920ac2f12f0a27a95ab9b811e49f04cb08020ff7dbe03bdb479f85614608c4e5d0108052497f4ae0148c0c2ef79c05adeaf74e6c003455f2cc5716"
|
|
}`, params.BeaconConfig().ShardCommitteePeriod+1)
|
|
// epoch is invalid
|
|
invalidExit1 = `{
|
|
"message": {
|
|
"epoch": "foo",
|
|
"validator_index": "0"
|
|
},
|
|
"signature": "0xaf20377dabe56887f72273806ea7f3bab3df464fe0178b2ec9bb83d891bf038671c222e2fa7fc0b3e83a0a86ecf235f6104f8130d9e3177cdf5391953fcebb9676f906f4e366b95cb4d734f48f7fc0f116c643519a58a3bb1f7501a1f64b87d2"
|
|
}`
|
|
// signature is wrong
|
|
invalidExit2 = `{
|
|
"message": {
|
|
"epoch": "0",
|
|
"validator_index": "0"
|
|
},
|
|
"signature": "0xa430330829331089c4381427217231c32c26ac551de410961002491257b1ef50c3d49a89fc920ac2f12f0a27a95ab9b811e49f04cb08020ff7dbe03bdb479f85614608c4e5d0108052497f4ae0148c0c2ef79c05adeaf74e6c003455f2cc5716"
|
|
}`
|
|
// non-existing validator index
|
|
invalidExit3 = `{
|
|
"message": {
|
|
"epoch": "0",
|
|
"validator_index": "99"
|
|
},
|
|
"signature": "0xa430330829331089c4381427217231c32c26ac551de410961002491257b1ef50c3d49a89fc920ac2f12f0a27a95ab9b811e49f04cb08020ff7dbe03bdb479f85614608c4e5d0108052497f4ae0148c0c2ef79c05adeaf74e6c003455f2cc5716"
|
|
}`
|
|
singleSyncCommitteeMsg = `[
|
|
{
|
|
"slot": "1",
|
|
"beacon_block_root": "0xbacd20f09da907734434f052bd4c9503aa16bab1960e89ea20610d08d064481c",
|
|
"validator_index": "1",
|
|
"signature": "0xb591bd4ca7d745b6e027879645d7c014fecb8c58631af070f7607acc0c1c948a5102a33267f0e4ba41a85b254b07df91185274375b2e6436e37e81d2fd46cb3751f5a6c86efb7499c1796c0c17e122a54ac067bb0f5ff41f3241659cceb0c21c"
|
|
}
|
|
]`
|
|
multipleSyncCommitteeMsg = `[
|
|
{
|
|
"slot": "1",
|
|
"beacon_block_root": "0xbacd20f09da907734434f052bd4c9503aa16bab1960e89ea20610d08d064481c",
|
|
"validator_index": "1",
|
|
"signature": "0xb591bd4ca7d745b6e027879645d7c014fecb8c58631af070f7607acc0c1c948a5102a33267f0e4ba41a85b254b07df91185274375b2e6436e37e81d2fd46cb3751f5a6c86efb7499c1796c0c17e122a54ac067bb0f5ff41f3241659cceb0c21c"
|
|
},
|
|
{
|
|
"slot": "2",
|
|
"beacon_block_root": "0x2757f6fd8590925cd000a86a3e543f98a93eae23781783a33e34504729a8ad0c",
|
|
"validator_index": "1",
|
|
"signature": "0x99dfe11b6c8b306d2c72eb891926d37922d226ea8e1e7484d6c30fab746494f192b0daa3e40c13f1e335b35238f3362c113455a329b1fab0bc500bc47f643786f49e151d5b5052afb51af57ba5aa34a6051dc90ee4de83a26eb54a895061d89a"
|
|
}
|
|
]`
|
|
// signature is invalid
|
|
invalidSyncCommitteeMsg = `[
|
|
{
|
|
"slot": "1",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"validator_index": "1",
|
|
"signature": "foo"
|
|
},
|
|
{
|
|
"slot": "1121",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"validator_index": "1",
|
|
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
|
},
|
|
{
|
|
"slot": "1121",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"validator_index": "2",
|
|
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
|
}
|
|
]`
|
|
// signatures are invalid
|
|
invalidAttesterSlashing = `{
|
|
"attestation_1": {
|
|
"attesting_indices": [
|
|
"1"
|
|
],
|
|
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
|
|
"data": {
|
|
"slot": "1",
|
|
"index": "1",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "1",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"target": {
|
|
"epoch": "1",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
}
|
|
}
|
|
},
|
|
"attestation_2": {
|
|
"attesting_indices": [
|
|
"1"
|
|
],
|
|
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
|
|
"data": {
|
|
"slot": "1",
|
|
"index": "1",
|
|
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"source": {
|
|
"epoch": "1",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"target": {
|
|
"epoch": "1",
|
|
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
// signatures are invalid
|
|
invalidProposerSlashing = `{
|
|
"signed_header_1": {
|
|
"message": {
|
|
"slot": "1",
|
|
"proposer_index": "1",
|
|
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
|
},
|
|
"signed_header_2": {
|
|
"message": {
|
|
"slot": "1",
|
|
"proposer_index": "1",
|
|
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
|
"body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
|
|
},
|
|
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
|
}
|
|
}`
|
|
)
|