diff --git a/core/sliceUtil/sliceUtil.go b/core/sliceUtil/sliceUtil.go index 73b30b5f2..70df2d32d 100644 --- a/core/sliceUtil/sliceUtil.go +++ b/core/sliceUtil/sliceUtil.go @@ -9,3 +9,39 @@ func TrimSliceSliceByte(in [][]byte) [][]byte { copy(ret, in) return ret } + +// IsIndexSetInBitmap - checks if a bit is set(1) in the given bitmap +func IsIndexSetInBitmap(index uint32, bitmap []byte) bool { + if isIndexOutOfBounds(index, bitmap) { + return false + } + + byteInMap := getByteAtIndex(index, bitmap) + mask := calcByteMask(index) + return (*byteInMap & mask) != 0 +} + +func SetIndexInBitmap(index uint32, bitmap []byte) { + if isIndexOutOfBounds(index, bitmap) { + return + } + + byteInMap := getByteAtIndex(index, bitmap) + mask := calcByteMask(index) + *byteInMap |= mask +} + +// Could panic. Do not call this unless you check before if isIndexOutOfBounds == false +func getByteAtIndex(index uint32, bitmap []byte) *byte { + bytePos := index / 8 + return &bitmap[bytePos] +} + +func calcByteMask(index uint32) byte { + bitPos := index % 8 + return 1 << bitPos +} + +func isIndexOutOfBounds(index uint32, bitmap []byte) bool { + return index >= uint32(len(bitmap))*8 +} diff --git a/core/sliceUtil/sliceUtil_test.go b/core/sliceUtil/sliceUtil_test.go index 2e3d3f1ee..54a7bbb69 100644 --- a/core/sliceUtil/sliceUtil_test.go +++ b/core/sliceUtil/sliceUtil_test.go @@ -1,10 +1,12 @@ package sliceUtil_test import ( + "strconv" "testing" "github.com/ElrondNetwork/elrond-go-core/core/sliceUtil" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTrimSliceSliceByte_EmptyInputShouldDoNothing(t *testing.T) { @@ -49,3 +51,49 @@ func TestTrimSliceSliceByte_SliceAlreadyOkShouldDoNothing(t *testing.T) { assert.Equal(t, 2, len(input)) assert.Equal(t, 2, cap(input)) } + +func TestIsIndexSetInBitmap(t *testing.T) { + byte1Map, _ := strconv.ParseInt("11001101", 2, 9) + byte2Map, _ := strconv.ParseInt("00000101", 2, 9) + bitmap := []byte{byte(byte1Map), byte(byte2Map)} + + //Byte 1 + require.True(t, sliceUtil.IsIndexSetInBitmap(0, bitmap)) + require.False(t, sliceUtil.IsIndexSetInBitmap(1, bitmap)) + require.True(t, sliceUtil.IsIndexSetInBitmap(2, bitmap)) + require.True(t, sliceUtil.IsIndexSetInBitmap(3, bitmap)) + require.False(t, sliceUtil.IsIndexSetInBitmap(4, bitmap)) + require.False(t, sliceUtil.IsIndexSetInBitmap(5, bitmap)) + require.True(t, sliceUtil.IsIndexSetInBitmap(6, bitmap)) + require.True(t, sliceUtil.IsIndexSetInBitmap(7, bitmap)) + // Byte 2 + require.True(t, sliceUtil.IsIndexSetInBitmap(8, bitmap)) + require.False(t, sliceUtil.IsIndexSetInBitmap(9, bitmap)) + require.True(t, sliceUtil.IsIndexSetInBitmap(10, bitmap)) + + for i := uint32(11); i <= 100; i++ { + require.False(t, sliceUtil.IsIndexSetInBitmap(i, bitmap)) + } +} + +func TestSetIndexInBitmap(t *testing.T) { + byte1Map, _ := strconv.ParseInt("11001101", 2, 9) + byte2Map, _ := strconv.ParseInt("00000101", 2, 9) + expectedBitMap := []byte{byte(byte1Map), byte(byte2Map)} + + bitMap := make([]byte, 2) + + sliceUtil.SetIndexInBitmap(0, bitMap) + sliceUtil.SetIndexInBitmap(2, bitMap) + sliceUtil.SetIndexInBitmap(3, bitMap) + sliceUtil.SetIndexInBitmap(6, bitMap) + sliceUtil.SetIndexInBitmap(7, bitMap) + sliceUtil.SetIndexInBitmap(8, bitMap) + sliceUtil.SetIndexInBitmap(10, bitMap) + + for i := uint32(16); i <= 100; i++ { + sliceUtil.SetIndexInBitmap(i, bitMap) + } + + require.Equal(t, expectedBitMap, bitMap) +} diff --git a/data/errors.go b/data/errors.go index 048aeac46..4900056da 100644 --- a/data/errors.go +++ b/data/errors.go @@ -78,3 +78,27 @@ var ErrScheduledRootHashNotSupported = errors.New("scheduled root hash is not su // ErrWrongTransactionsTypeSize signals that size of transactions type buffer from mini block reserved field is wrong var ErrWrongTransactionsTypeSize = errors.New("wrong transactions type size") + +// ErrNilSlashResult signals that a nil slash result has been provided +var ErrNilSlashResult = errors.New("slash result is nil") + +// ErrNilHeaderHandler signals that a nil header handler has been provided +var ErrNilHeaderHandler = errors.New("nil header handler") + +// ErrEmptyHeaderInfoList signals that an empty header info list has been provided +var ErrEmptyHeaderInfoList = errors.New("empty header info list") + +// ErrNilHeaderInfo signals that a nil header info has been provided +var ErrNilHeaderInfo = errors.New("nil header info") + +// ErrNilHash signals that a nil hash has been provided +var ErrNilHash = errors.New("nil hash provided") + +// ErrHeadersSameHash signals that headers have the same hash in a multiple header proof +var ErrHeadersSameHash = errors.New("headers have the same hash in a multiple header proof") + +// ErrNotEnoughHeadersProvided signals that not enough headers have been provided for a multiple header proof +var ErrNotEnoughHeadersProvided = errors.New("not enough headers have been provided for a multiple header proof") + +// ErrNotEnoughPublicKeysProvided signals that not enough public keys have been provided for a multiple header proof +var ErrNotEnoughPublicKeysProvided = errors.New("not enough public keys have been provided for a multiple header proof") diff --git a/data/interface.go b/data/interface.go index d7eb22c9e..848bf6968 100644 --- a/data/interface.go +++ b/data/interface.go @@ -84,6 +84,12 @@ type HeaderHandler interface { IsInterfaceNil() bool } +// HeaderInfoHandler defines a HeaderHandler and its corresponding hash +type HeaderInfoHandler interface { + GetHeaderHandler() HeaderHandler + GetHash() []byte +} + // ShardHeaderHandler defines getters and setters for the shard block header type ShardHeaderHandler interface { HeaderHandler @@ -193,7 +199,7 @@ type EpochStartShardDataHandler interface { SetPendingMiniBlockHeaders([]MiniBlockHeaderHandler) error } -// EconomicHandler defines setters and getters for Economics +// EconomicsHandler defines setters and getters for Economics type EconomicsHandler interface { GetTotalSupply() *big.Int GetTotalToDistribute() *big.Int diff --git a/data/mock/headerInfoStub.go b/data/mock/headerInfoStub.go new file mode 100644 index 000000000..1114b4027 --- /dev/null +++ b/data/mock/headerInfoStub.go @@ -0,0 +1,19 @@ +package mock + +import "github.com/ElrondNetwork/elrond-go-core/data" + +// HeaderInfoStub - +type HeaderInfoStub struct { + Header data.HeaderHandler + Hash []byte +} + +// GetHeaderHandler - +func (his *HeaderInfoStub) GetHeaderHandler() data.HeaderHandler { + return his.Header +} + +// GetHash - +func (his *HeaderInfoStub) GetHash() []byte { + return his.Hash +} diff --git a/data/slash/common.go b/data/slash/common.go new file mode 100644 index 000000000..efc3a3f8d --- /dev/null +++ b/data/slash/common.go @@ -0,0 +1,125 @@ +package slash + +import ( + "encoding/hex" + "fmt" + "sort" + + "github.com/ElrondNetwork/elrond-go-core/core/check" + "github.com/ElrondNetwork/elrond-go-core/data" +) + +// MinSlashableNoOfHeaders represents the min number of headers required for a +// multiple proposal/signing proof to be considered slashable +const MinSlashableNoOfHeaders = 2 + +// SlashingResult contains the slashable data as well as the severity(slashing level) +// for a possible malicious validator +type SlashingResult struct { + SlashingLevel ThreatLevel + Headers []data.HeaderInfoHandler +} + +// ProofTxData represents necessary data to be used in a slashing commitment proof tx by a slashing notifier. +// Each field is required to be added in a transaction.data field +type ProofTxData struct { + Round uint64 + ShardID uint32 + ProofID byte +} + +// Used by slashing notifier to create a slashing transaction +// from a proof. Each transaction identifies a different +// slashing event based on this ID +const ( + // MultipleProposalProofID = MultipleProposal's ID + MultipleProposalProofID byte = 0x1 + // MultipleSigningProofID = MultipleSigning's ID + MultipleSigningProofID byte = 0x2 +) + +const byteSize = 8 + +// CalcBitmapSize returns the min required bitmap size, which +// can hold a noOfBits +func CalcBitmapSize(noOfBits int) uint32 { + bitmapSize := noOfBits / byteSize + if noOfBits%byteSize != 0 { + bitmapSize++ + } + return uint32(bitmapSize) +} + +func getSortedHeadersV2(headersInfo []data.HeaderInfoHandler) (*HeadersV2, error) { + sortedHeaders, err := sortHeaders(headersInfo) + if err != nil { + return nil, err + } + + headersV2 := &HeadersV2{} + err = headersV2.SetHeaders(sortedHeaders) + if err != nil { + return nil, err + } + + return headersV2, nil +} + +func getHeaderHashIfUnique(headerInfo data.HeaderInfoHandler, hashes map[string]struct{}) (string, error) { + if headerInfo == nil { + return "", data.ErrNilHeaderInfo + } + + hash := headerInfo.GetHash() + if hash == nil { + return "", data.ErrNilHash + } + + headerHandler := headerInfo.GetHeaderHandler() + if check.IfNil(headerHandler) { + return "", fmt.Errorf("%w in header info for hash: %s", data.ErrNilHeaderHandler, hex.EncodeToString(hash)) + } + + hashStr := string(hash) + _, exists := hashes[hashStr] + if exists { + return "", fmt.Errorf("%w, duplicated hash: %s", data.ErrHeadersSameHash, hex.EncodeToString(hash)) + } + + return hashStr, nil +} + +func sortHeaders(headersInfo []data.HeaderInfoHandler) ([]data.HeaderHandler, error) { + if len(headersInfo) == 0 { + return nil, data.ErrEmptyHeaderInfoList + } + + sortHeadersByHash(headersInfo) + headers := make([]data.HeaderHandler, 0, len(headersInfo)) + hashes := make(map[string]struct{}) + for idx, headerInfo := range headersInfo { + hash, err := getHeaderHashIfUnique(headerInfo, hashes) + if err != nil { + return nil, fmt.Errorf("%w in sorted header list at index: %d", err, idx) + } + + headers = append(headers, headerInfo.GetHeaderHandler()) + hashes[hash] = struct{}{} + } + + return headers, nil +} + +func sortHeadersByHash(headersInfo []data.HeaderInfoHandler) { + sortFunc := func(i, j int) bool { + if headersInfo[i] == nil || headersInfo[j] == nil { + return false + } + hash1 := string(headersInfo[i].GetHash()) + hash2 := string(headersInfo[j].GetHash()) + + return hash1 < hash2 + } + + sort.Slice(headersInfo, sortFunc) +} diff --git a/data/slash/common_test.go b/data/slash/common_test.go new file mode 100644 index 000000000..c6d88dc33 --- /dev/null +++ b/data/slash/common_test.go @@ -0,0 +1,117 @@ +package slash + +import ( + "strings" + "testing" + + "github.com/ElrondNetwork/elrond-go-core/data" + "github.com/ElrondNetwork/elrond-go-core/data/block" + "github.com/ElrondNetwork/elrond-go-core/data/mock" + "github.com/stretchr/testify/require" +) + +func TestSortHeaders_NilHeaderInfoListExpectError(t *testing.T) { + sortedHeaders, err := sortHeaders(nil) + require.Nil(t, sortedHeaders) + require.Equal(t, data.ErrEmptyHeaderInfoList, err) +} + +func TestSortHeaders_EmptyHeaderInfoListExpectEmptyResult(t *testing.T) { + sortedHeaders, err := sortHeaders([]data.HeaderInfoHandler{}) + require.Nil(t, sortedHeaders) + require.Equal(t, data.ErrEmptyHeaderInfoList, err) +} + +func TestSortHeaders_NilHeaderInfoExpectError(t *testing.T) { + headerInfoList := []data.HeaderInfoHandler{nil} + sortedHeaders, err := sortHeaders(headerInfoList) + require.Nil(t, sortedHeaders) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), data.ErrNilHeaderInfo.Error())) +} + +func TestSortHeaders_NilHeaderHandlerExpectError(t *testing.T) { + headerInfo := &mock.HeaderInfoStub{ + Header: nil, + Hash: []byte("hash1"), + } + headerInfoList := []data.HeaderInfoHandler{headerInfo} + + sortedHeaders, err := sortHeaders(headerInfoList) + require.Nil(t, sortedHeaders) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), data.ErrNilHeaderHandler.Error())) +} + +func TestSortHeaders_NilHashExpectError(t *testing.T) { + header1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1}} + header2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2}} + headerInfo1 := &mock.HeaderInfoStub{ + Header: header1, + Hash: []byte("hash"), + } + headerInfo2 := &mock.HeaderInfoStub{ + Header: header2, + Hash: nil, + } + headerInfoList := []data.HeaderInfoHandler{headerInfo1, headerInfo2} + + sortedHeaders, err := sortHeaders(headerInfoList) + require.Nil(t, sortedHeaders) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), data.ErrNilHash.Error())) +} + +func TestSortHeaders_HeadersSameHashExpectError(t *testing.T) { + header1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1}} + header2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2}} + header3 := &block.HeaderV2{Header: &block.Header{TimeStamp: 3}} + headerInfo1 := &mock.HeaderInfoStub{ + Header: header1, + Hash: []byte("hash1"), + } + headerInfo2 := &mock.HeaderInfoStub{ + Header: header2, + Hash: []byte("hash2"), + } + headerInfo3 := &mock.HeaderInfoStub{ + Header: header3, + Hash: []byte("hash2"), + } + headerInfoList := []data.HeaderInfoHandler{headerInfo1, headerInfo2, headerInfo3} + + sortedHeaders, err := sortHeaders(headerInfoList) + require.Nil(t, sortedHeaders) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), data.ErrHeadersSameHash.Error())) +} + +func TestSortHeaders(t *testing.T) { + h1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1}} + h2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2}} + h3 := &block.HeaderV2{Header: &block.Header{TimeStamp: 3}} + + hInfo1 := &mock.HeaderInfoStub{ + Header: h1, + Hash: []byte("h1"), + } + hInfo2 := &mock.HeaderInfoStub{ + Header: h2, + Hash: []byte("h2"), + } + hInfo3 := &mock.HeaderInfoStub{ + Header: h3, + Hash: []byte("h3"), + } + + headersInfo := []data.HeaderInfoHandler{hInfo3, hInfo1, hInfo2} + sortedHeaders, err := sortHeaders(headersInfo) + require.Nil(t, err) + require.Equal(t, h1, sortedHeaders[0]) + require.Equal(t, h2, sortedHeaders[1]) + require.Equal(t, h3, sortedHeaders[2]) + + require.Equal(t, hInfo1, headersInfo[0]) + require.Equal(t, hInfo2, headersInfo[1]) + require.Equal(t, hInfo3, headersInfo[2]) +} diff --git a/data/slash/interface.go b/data/slash/interface.go new file mode 100644 index 000000000..035e46f65 --- /dev/null +++ b/data/slash/interface.go @@ -0,0 +1,33 @@ +package slash + +import ( + "github.com/ElrondNetwork/elrond-go-core/data" +) + +// SlashingProofHandler contains a proof for a slashing event and can be wrapped in a transaction +type SlashingProofHandler interface { + // GetProofTxData extracts proof tx data(see ProofTxData) from a slashing proof + GetProofTxData() (*ProofTxData, error) +} + +// MultipleProposalProofHandler contains proof data for a multiple header proposal slashing event +type MultipleProposalProofHandler interface { + SlashingProofHandler + // GetLevel returns the threat level of the proposer + GetLevel() ThreatLevel + //GetHeaders returns all proposed headers in a certain round + GetHeaders() []data.HeaderHandler +} + +// MultipleSigningProofHandler contains proof data for a multiple header signing slashing event +type MultipleSigningProofHandler interface { + SlashingProofHandler + // GetPubKeys returns all validator's public keys which have signed multiple headers + GetPubKeys() [][]byte + // GetAllHeaders returns all signed headers by all validators in a certain round + GetAllHeaders() []data.HeaderHandler + // GetLevel returns the threat level of a given validator + GetLevel(pubKey []byte) ThreatLevel + // GetHeaders returns the signed headers by a given validator + GetHeaders(pubKey []byte) []data.HeaderHandler +} diff --git a/data/slash/multipleHeaderProposalProof.go b/data/slash/multipleHeaderProposalProof.go new file mode 100644 index 000000000..debb6fe49 --- /dev/null +++ b/data/slash/multipleHeaderProposalProof.go @@ -0,0 +1,57 @@ +//go:generate protoc -I=. -I=$GOPATH/src/github.com/ElrondNetwork/elrond-go-core/data/block -I=$GOPATH/src -I=$GOPATH/src/github.com/ElrondNetwork/protobuf/protobuf --gogoslick_out=. multipleHeaderProposalProof.proto +package slash + +import ( + "github.com/ElrondNetwork/elrond-go-core/core/check" + "github.com/ElrondNetwork/elrond-go-core/data" +) + +// GetHeaders returns all headers that have been proposed by a possible malicious validator +func (m *MultipleHeaderProposalProof) GetHeaders() []data.HeaderHandler { + if m == nil { + return nil + } + + return m.HeadersV2.GetHeaderHandlers() +} + +// GetProofTxData returns the necessary ProofTxData to issue a commitment slash tx +func (m *MultipleHeaderProposalProof) GetProofTxData() (*ProofTxData, error) { + if m == nil { + return nil, data.ErrNilPointerReceiver + } + + headers := m.GetHeaders() + if len(headers) == 0 { + return nil, data.ErrNotEnoughHeadersProvided + } + if check.IfNil(headers[0]) { + return nil, data.ErrNilHeaderHandler + } + + return &ProofTxData{ + Round: headers[0].GetRound(), + ShardID: headers[0].GetShardID(), + ProofID: MultipleProposalProofID, + }, nil +} + +// NewMultipleProposalProof returns a MultipleProposalProofHandler from a slashing result +func NewMultipleProposalProof(slashResult *SlashingResult) (MultipleProposalProofHandler, error) { + if slashResult == nil { + return nil, data.ErrNilSlashResult + } + if slashResult.Headers == nil { + return nil, data.ErrEmptyHeaderInfoList + } + + sortedHeaders, err := getSortedHeadersV2(slashResult.Headers) + if err != nil { + return nil, err + } + + return &MultipleHeaderProposalProof{ + Level: slashResult.SlashingLevel, + HeadersV2: *sortedHeaders, + }, nil +} diff --git a/data/slash/multipleHeaderProposalProof.pb.go b/data/slash/multipleHeaderProposalProof.pb.go new file mode 100644 index 000000000..d42b47a3b --- /dev/null +++ b/data/slash/multipleHeaderProposalProof.pb.go @@ -0,0 +1,423 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: multipleHeaderProposalProof.proto + +package slash + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type MultipleHeaderProposalProof struct { + Level ThreatLevel `protobuf:"varint,1,opt,name=Level,proto3,enum=proto.ThreatLevel" json:"Level,omitempty"` + HeadersV2 HeadersV2 `protobuf:"bytes,2,opt,name=HeadersV2,proto3" json:"HeadersV2"` +} + +func (m *MultipleHeaderProposalProof) Reset() { *m = MultipleHeaderProposalProof{} } +func (*MultipleHeaderProposalProof) ProtoMessage() {} +func (*MultipleHeaderProposalProof) Descriptor() ([]byte, []int) { + return fileDescriptor_afe79f980fc6faed, []int{0} +} +func (m *MultipleHeaderProposalProof) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MultipleHeaderProposalProof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *MultipleHeaderProposalProof) XXX_Merge(src proto.Message) { + xxx_messageInfo_MultipleHeaderProposalProof.Merge(m, src) +} +func (m *MultipleHeaderProposalProof) XXX_Size() int { + return m.Size() +} +func (m *MultipleHeaderProposalProof) XXX_DiscardUnknown() { + xxx_messageInfo_MultipleHeaderProposalProof.DiscardUnknown(m) +} + +var xxx_messageInfo_MultipleHeaderProposalProof proto.InternalMessageInfo + +func (m *MultipleHeaderProposalProof) GetLevel() ThreatLevel { + if m != nil { + return m.Level + } + return Low +} + +func (m *MultipleHeaderProposalProof) GetHeadersV2() HeadersV2 { + if m != nil { + return m.HeadersV2 + } + return HeadersV2{} +} + +func init() { + proto.RegisterType((*MultipleHeaderProposalProof)(nil), "proto.MultipleHeaderProposalProof") +} + +func init() { proto.RegisterFile("multipleHeaderProposalProof.proto", fileDescriptor_afe79f980fc6faed) } + +var fileDescriptor_afe79f980fc6faed = []byte{ + // 241 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcc, 0x2d, 0xcd, 0x29, + 0xc9, 0x2c, 0xc8, 0x49, 0xf5, 0x48, 0x4d, 0x4c, 0x49, 0x2d, 0x0a, 0x28, 0xca, 0x2f, 0xc8, 0x2f, + 0x4e, 0xcc, 0x09, 0x28, 0xca, 0xcf, 0x4f, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, + 0x53, 0x52, 0xdc, 0xc5, 0x39, 0x89, 0xc5, 0x19, 0x10, 0x31, 0x29, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, + 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xf4, 0xfc, 0xf4, 0x7c, 0x7d, 0xb0, 0x70, 0x52, 0x69, + 0x1a, 0x98, 0x07, 0xe6, 0x80, 0x59, 0x10, 0xe5, 0x4a, 0xb5, 0x5c, 0xd2, 0xbe, 0xb8, 0xed, 0x11, + 0xd2, 0xe0, 0x62, 0xf5, 0x49, 0x2d, 0x4b, 0xcd, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x33, 0x12, + 0x82, 0xe8, 0xd2, 0x0b, 0xc9, 0x28, 0x4a, 0x4d, 0x2c, 0x01, 0xcb, 0x04, 0x41, 0x14, 0x08, 0x99, + 0x70, 0x71, 0x42, 0x0c, 0x28, 0x0e, 0x33, 0x92, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x36, 0x12, 0x80, + 0xaa, 0x86, 0x8b, 0x3b, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, 0x10, 0x84, 0x50, 0xe8, 0x64, 0x7f, 0xe1, + 0xa1, 0x1c, 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x1f, 0x1e, 0xca, 0x31, 0x36, 0x3c, 0x92, 0x63, 0x5c, + 0xf1, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x6f, 0x3c, 0x92, 0x63, + 0x7c, 0xf0, 0x48, 0x8e, 0xf1, 0xc5, 0x23, 0x39, 0x86, 0x0f, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, + 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0x56, 0xb0, 0xa7, 0x93, 0xd8, + 0xc0, 0x56, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x27, 0xc4, 0x81, 0x53, 0x2e, 0x01, 0x00, + 0x00, +} + +func (this *MultipleHeaderProposalProof) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MultipleHeaderProposalProof) + if !ok { + that2, ok := that.(MultipleHeaderProposalProof) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Level != that1.Level { + return false + } + if !this.HeadersV2.Equal(&that1.HeadersV2) { + return false + } + return true +} +func (this *MultipleHeaderProposalProof) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&slash.MultipleHeaderProposalProof{") + s = append(s, "Level: "+fmt.Sprintf("%#v", this.Level)+",\n") + s = append(s, "HeadersV2: "+strings.Replace(this.HeadersV2.GoString(), `&`, ``, 1)+",\n") + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringMultipleHeaderProposalProof(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *MultipleHeaderProposalProof) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MultipleHeaderProposalProof) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MultipleHeaderProposalProof) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.HeadersV2.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMultipleHeaderProposalProof(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.Level != 0 { + i = encodeVarintMultipleHeaderProposalProof(dAtA, i, uint64(m.Level)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintMultipleHeaderProposalProof(dAtA []byte, offset int, v uint64) int { + offset -= sovMultipleHeaderProposalProof(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MultipleHeaderProposalProof) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Level != 0 { + n += 1 + sovMultipleHeaderProposalProof(uint64(m.Level)) + } + l = m.HeadersV2.Size() + n += 1 + l + sovMultipleHeaderProposalProof(uint64(l)) + return n +} + +func sovMultipleHeaderProposalProof(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMultipleHeaderProposalProof(x uint64) (n int) { + return sovMultipleHeaderProposalProof(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *MultipleHeaderProposalProof) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MultipleHeaderProposalProof{`, + `Level:` + fmt.Sprintf("%v", this.Level) + `,`, + `HeadersV2:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.HeadersV2), "HeadersV2", "HeadersV2", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func valueToStringMultipleHeaderProposalProof(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *MultipleHeaderProposalProof) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderProposalProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MultipleHeaderProposalProof: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MultipleHeaderProposalProof: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Level", wireType) + } + m.Level = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderProposalProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Level |= ThreatLevel(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HeadersV2", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderProposalProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMultipleHeaderProposalProof + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMultipleHeaderProposalProof + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.HeadersV2.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMultipleHeaderProposalProof(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMultipleHeaderProposalProof + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMultipleHeaderProposalProof + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMultipleHeaderProposalProof(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMultipleHeaderProposalProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMultipleHeaderProposalProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMultipleHeaderProposalProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMultipleHeaderProposalProof + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMultipleHeaderProposalProof + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMultipleHeaderProposalProof + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMultipleHeaderProposalProof = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMultipleHeaderProposalProof = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMultipleHeaderProposalProof = fmt.Errorf("proto: unexpected end of group") +) diff --git a/data/slash/multipleHeaderProposalProof.proto b/data/slash/multipleHeaderProposalProof.proto new file mode 100644 index 000000000..fff01db84 --- /dev/null +++ b/data/slash/multipleHeaderProposalProof.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package proto; + +option go_package = "slash"; +option (gogoproto.stable_marshaler_all) = true; + +import "slash.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +message MultipleHeaderProposalProof { + ThreatLevel Level = 1; + HeadersV2 HeadersV2 = 2 [(gogoproto.nullable) = false]; +} diff --git a/data/slash/multipleHeaderProposalProof_test.go b/data/slash/multipleHeaderProposalProof_test.go new file mode 100644 index 000000000..c9e2b007f --- /dev/null +++ b/data/slash/multipleHeaderProposalProof_test.go @@ -0,0 +1,189 @@ +package slash_test + +import ( + "strings" + "testing" + + "github.com/ElrondNetwork/elrond-go-core/data" + "github.com/ElrondNetwork/elrond-go-core/data/block" + dataMock "github.com/ElrondNetwork/elrond-go-core/data/mock" + "github.com/ElrondNetwork/elrond-go-core/data/slash" + testscommon "github.com/ElrondNetwork/elrond-go-core/data/testscommon/slash" + "github.com/ElrondNetwork/elrond-go-core/hashing/blake2b" + "github.com/ElrondNetwork/elrond-go-core/marshal" + "github.com/stretchr/testify/require" +) + +func TestNewMultipleProposalProof(t *testing.T) { + tests := []struct { + args *slash.SlashingResult + expectedErr error + }{ + { + args: nil, + expectedErr: data.ErrNilSlashResult, + }, + { + args: &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: nil, + }, + expectedErr: data.ErrEmptyHeaderInfoList, + }, + { + args: &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{}, + }, + expectedErr: data.ErrEmptyHeaderInfoList, + }, + { + args: &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{nil, &dataMock.HeaderInfoStub{}}, + }, + expectedErr: data.ErrNilHeaderInfo, + }, + { + args: &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{ + &dataMock.HeaderInfoStub{Header: &block.HeaderV2{}, Hash: []byte("h")}, + &dataMock.HeaderInfoStub{Header: &block.HeaderV2{}, Hash: []byte("h")}, + }, + }, + expectedErr: data.ErrHeadersSameHash, + }, + } + + for _, currTest := range tests { + _, err := slash.NewMultipleProposalProof(currTest.args) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), currTest.expectedErr.Error())) + } +} + +func TestMultipleProposalProof_GetHeadersGetLevel(t *testing.T) { + h1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1}} + h2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2}} + h3 := &block.HeaderV2{Header: &block.Header{TimeStamp: 3}} + + hInfo1 := &dataMock.HeaderInfoStub{Header: h1, Hash: []byte("h1")} + hInfo2 := &dataMock.HeaderInfoStub{Header: h2, Hash: []byte("h2")} + hInfo3 := &dataMock.HeaderInfoStub{Header: h3, Hash: []byte("h3")} + + slashRes := &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{hInfo2, hInfo1, hInfo3}, + } + + proof, err := slash.NewMultipleProposalProof(slashRes) + require.Nil(t, err) + require.Equal(t, slash.Medium, proof.GetLevel()) + require.Equal(t, []data.HeaderHandler{h1, h2, h3}, proof.GetHeaders()) +} + +func TestMultipleHeaderProposalProof_GetProofTxDataNotEnoughHeadersExpectError(t *testing.T) { + proof := slash.MultipleHeaderProposalProof{} + + proofTxData, err := proof.GetProofTxData() + require.Nil(t, proofTxData) + require.Equal(t, data.ErrNotEnoughHeadersProvided, err) +} + +func TestMultipleHeaderProposalProof_GetProofTxDataNilHeaderExpectError(t *testing.T) { + proof := slash.MultipleHeaderProposalProof{ + HeadersV2: slash.HeadersV2{ + Headers: []*block.HeaderV2{nil}, + }, + } + + proofTxData, err := proof.GetProofTxData() + require.Nil(t, proofTxData) + require.Equal(t, data.ErrNilHeaderHandler, err) +} + +func TestMultipleHeaderProposalProof_GetProofTxData(t *testing.T) { + round := uint64(1) + shardID := uint32(2) + + header := &block.HeaderV2{ + Header: &block.Header{ + Round: round, + ShardID: shardID, + }, + } + proof := slash.MultipleHeaderProposalProof{ + HeadersV2: slash.HeadersV2{ + Headers: []*block.HeaderV2{header}, + }, + } + expectedProofTxData := &slash.ProofTxData{ + Round: round, + ShardID: shardID, + ProofID: slash.MultipleProposalProofID, + } + + proofTxData, err := proof.GetProofTxData() + require.Equal(t, expectedProofTxData, proofTxData) + require.Nil(t, err) +} + +func TestMultipleHeaderProposalProof_MarshalUnmarshal(t *testing.T) { + h1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1, LeaderSignature: []byte("sig1")}} + h2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2, LeaderSignature: []byte("sig2")}} + + hInfo1 := &dataMock.HeaderInfoStub{Header: h1, Hash: []byte("h1")} + hInfo2 := &dataMock.HeaderInfoStub{Header: h2, Hash: []byte("h2")} + + slashRes1 := &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{hInfo1, hInfo2}, + } + slashRes2 := &slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{hInfo2, hInfo1}, + } + + proof1, err1 := slash.NewMultipleProposalProof(slashRes1) + proof2, err2 := slash.NewMultipleProposalProof(slashRes2) + require.Nil(t, err1) + require.Nil(t, err2) + require.Equal(t, proof1, proof2) + + marshaller := marshal.GogoProtoMarshalizer{} + proof1Bytes, err1 := marshaller.Marshal(proof1) + proof2Bytes, err2 := marshaller.Marshal(proof2) + require.Nil(t, err1) + require.Nil(t, err2) + require.Equal(t, proof1Bytes, proof2Bytes) + + proof1Unmarshalled := &slash.MultipleHeaderProposalProof{} + proof2Unmarshalled := &slash.MultipleHeaderProposalProof{} + err1 = marshaller.Unmarshal(proof1Unmarshalled, proof1Bytes) + err2 = marshaller.Unmarshal(proof2Unmarshalled, proof2Bytes) + require.Nil(t, err1) + require.Nil(t, err2) + require.Equal(t, proof1Unmarshalled, proof1) + require.Equal(t, proof2Unmarshalled, proof2) +} + +func BenchmarkNewMultipleProposalProof(b *testing.B) { + hasher, err := blake2b.NewBlake2bWithSize(16) + require.Nil(b, err) + + noOfHeaders := uint32(100) + headers := testscommon.GenerateDistinctHeadersInfo(b, noOfHeaders, hasher) + slashRes := &slash.SlashingResult{ + SlashingLevel: testscommon.CalcThreatLevel(noOfHeaders), + Headers: headers, + } + for n := 0; n < b.N; n++ { + proof, err := slash.NewMultipleProposalProof(slashRes) + + b.StopTimer() + require.NotNil(b, proof) + require.Nil(b, err) + b.StartTimer() + } +} diff --git a/data/slash/multipleHeaderSigningProof.go b/data/slash/multipleHeaderSigningProof.go new file mode 100644 index 000000000..12bf3e56c --- /dev/null +++ b/data/slash/multipleHeaderSigningProof.go @@ -0,0 +1,177 @@ +//go:generate protoc -I=. -I=$GOPATH/src/github.com/ElrondNetwork/elrond-go-core/data/block -I=$GOPATH/src -I=$GOPATH/src/github.com/ElrondNetwork/protobuf/protobuf --gogoslick_out=. multipleHeaderSigningProof.proto +package slash + +import ( + "encoding/hex" + "fmt" + + "github.com/ElrondNetwork/elrond-go-core/core/check" + "github.com/ElrondNetwork/elrond-go-core/core/sliceUtil" + "github.com/ElrondNetwork/elrond-go-core/data" +) + +// GetPubKeys - returns all validator's public keys which have signed multiple headers +func (m *MultipleHeaderSigningProof) GetPubKeys() [][]byte { + if m == nil { + return nil + } + + ret := make([][]byte, 0, len(m.SignersSlashData)) + for pubKey := range m.SignersSlashData { + ret = append(ret, []byte(pubKey)) + } + + return ret +} + +// GetLevel returns the ThreatLevel of a possible malicious validator +func (m *MultipleHeaderSigningProof) GetLevel(pubKey []byte) ThreatLevel { + if m == nil { + return Zero + } + + slashData, exists := m.SignersSlashData[string(pubKey)] + if !exists { + return Zero + } + + return slashData.ThreatLevel +} + +// GetAllHeaders returns all header handlers stored in the proof +func (m *MultipleHeaderSigningProof) GetAllHeaders() []data.HeaderHandler { + if m == nil { + return nil + } + + return m.HeadersV2.GetHeaderHandlers() +} + +// GetHeaders returns all headers that have been signed by a possible malicious validator +func (m *MultipleHeaderSigningProof) GetHeaders(pubKey []byte) []data.HeaderHandler { + if m == nil { + return nil + } + + slashData, exists := m.SignersSlashData[string(pubKey)] + if !exists { + return nil + } + + bitmap := slashData.GetSignedHeadersBitMap() + headers := m.HeadersV2.GetHeaderHandlers() + + ret := make([]data.HeaderHandler, 0) + for idx, header := range headers { + if sliceUtil.IsIndexSetInBitmap(uint32(idx), bitmap) { + ret = append(ret, header) + } + } + + return ret +} + +// GetProofTxData returns the necessary ProofTxData to issue a commitment slash tx +func (m *MultipleHeaderSigningProof) GetProofTxData() (*ProofTxData, error) { + if m == nil { + return nil, data.ErrNilPointerReceiver + } + + pubKeys := m.GetPubKeys() + if len(pubKeys) == 0 { + return nil, data.ErrNotEnoughPublicKeysProvided + } + pubKey := pubKeys[0] + headers := m.GetHeaders(pubKey) + if len(headers) == 0 { + return nil, data.ErrNotEnoughHeadersProvided + } + if check.IfNil(headers[0]) { + return nil, data.ErrNilHeaderHandler + } + + return &ProofTxData{ + Round: headers[0].GetRound(), + ShardID: headers[0].GetShardID(), + ProofID: MultipleSigningProofID, + }, nil +} + +// NewMultipleSigningProof returns a MultipleSigningProofHandler from a slashing result +func NewMultipleSigningProof(slashResult map[string]SlashingResult) (MultipleSigningProofHandler, error) { + if slashResult == nil { + return nil, data.ErrNilSlashResult + } + + headersInfo, err := getAllUniqueHeaders(slashResult) + if err != nil { + return nil, err + } + sortedHeaders, err := getSortedHeadersV2(headersInfo) + if err != nil { + return nil, err + } + + hashIndexMap := calcHashIndexMap(headersInfo) + signersSlashData := computeSignersSlashData(hashIndexMap, slashResult) + + return &MultipleHeaderSigningProof{ + HeadersV2: *sortedHeaders, + SignersSlashData: signersSlashData, + }, nil +} + +func getAllUniqueHeaders(slashResult map[string]SlashingResult) ([]data.HeaderInfoHandler, error) { + headersInfo := make([]data.HeaderInfoHandler, 0, len(slashResult)) + hashes := make(map[string]struct{}) + + for pubKey, res := range slashResult { + hashesPerPubKey := make(map[string]struct{}) + for _, headerInfo := range res.Headers { + hash, err := getHeaderHashIfUnique(headerInfo, hashesPerPubKey) + if err != nil { + return nil, fmt.Errorf("%w in slash result for public key: %s", err, hex.EncodeToString([]byte(pubKey))) + } + + hashesPerPubKey[hash] = struct{}{} + _, exists := hashes[hash] + if exists { + continue + } + + hashes[hash] = struct{}{} + headersInfo = append(headersInfo, headerInfo) + } + } + + return headersInfo, nil +} + +func calcHashIndexMap(headersInfo []data.HeaderInfoHandler) map[string]uint32 { + hashIndexMap := make(map[string]uint32) + for idx, headerInfo := range headersInfo { + hashIndexMap[string(headerInfo.GetHash())] = uint32(idx) + } + + return hashIndexMap +} + +func computeSignersSlashData(hashIndexMap map[string]uint32, slashResult map[string]SlashingResult) map[string]SignerSlashingData { + signersSlashData := make(map[string]SignerSlashingData) + bitMapSize := CalcBitmapSize(len(hashIndexMap)) + for pubKey, res := range slashResult { + bitmap := make([]byte, bitMapSize) + for _, header := range res.Headers { + index, exists := hashIndexMap[string(header.GetHash())] + if exists { + sliceUtil.SetIndexInBitmap(index, bitmap) + } + } + signersSlashData[pubKey] = SignerSlashingData{ + SignedHeadersBitMap: bitmap, + ThreatLevel: res.SlashingLevel, + } + } + + return signersSlashData +} diff --git a/data/slash/multipleHeaderSigningProof.pb.go b/data/slash/multipleHeaderSigningProof.pb.go new file mode 100644 index 000000000..163f62ae5 --- /dev/null +++ b/data/slash/multipleHeaderSigningProof.pb.go @@ -0,0 +1,856 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: multipleHeaderSigningProof.proto + +package slash + +import ( + bytes "bytes" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type SignerSlashingData struct { + SignedHeadersBitMap []byte `protobuf:"bytes,1,opt,name=SignedHeadersBitMap,proto3" json:"SignedHeadersBitMap,omitempty"` + ThreatLevel ThreatLevel `protobuf:"varint,2,opt,name=ThreatLevel,proto3,enum=proto.ThreatLevel" json:"ThreatLevel,omitempty"` +} + +func (m *SignerSlashingData) Reset() { *m = SignerSlashingData{} } +func (*SignerSlashingData) ProtoMessage() {} +func (*SignerSlashingData) Descriptor() ([]byte, []int) { + return fileDescriptor_f61c9968ee2015ff, []int{0} +} +func (m *SignerSlashingData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignerSlashingData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *SignerSlashingData) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignerSlashingData.Merge(m, src) +} +func (m *SignerSlashingData) XXX_Size() int { + return m.Size() +} +func (m *SignerSlashingData) XXX_DiscardUnknown() { + xxx_messageInfo_SignerSlashingData.DiscardUnknown(m) +} + +var xxx_messageInfo_SignerSlashingData proto.InternalMessageInfo + +func (m *SignerSlashingData) GetSignedHeadersBitMap() []byte { + if m != nil { + return m.SignedHeadersBitMap + } + return nil +} + +func (m *SignerSlashingData) GetThreatLevel() ThreatLevel { + if m != nil { + return m.ThreatLevel + } + return Zero +} + +type MultipleHeaderSigningProof struct { + HeadersV2 HeadersV2 `protobuf:"bytes,1,opt,name=HeadersV2,proto3" json:"HeadersV2"` + SignersSlashData map[string]SignerSlashingData `protobuf:"bytes,2,rep,name=SignersSlashData,proto3" json:"SignersSlashData" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *MultipleHeaderSigningProof) Reset() { *m = MultipleHeaderSigningProof{} } +func (*MultipleHeaderSigningProof) ProtoMessage() {} +func (*MultipleHeaderSigningProof) Descriptor() ([]byte, []int) { + return fileDescriptor_f61c9968ee2015ff, []int{1} +} +func (m *MultipleHeaderSigningProof) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MultipleHeaderSigningProof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *MultipleHeaderSigningProof) XXX_Merge(src proto.Message) { + xxx_messageInfo_MultipleHeaderSigningProof.Merge(m, src) +} +func (m *MultipleHeaderSigningProof) XXX_Size() int { + return m.Size() +} +func (m *MultipleHeaderSigningProof) XXX_DiscardUnknown() { + xxx_messageInfo_MultipleHeaderSigningProof.DiscardUnknown(m) +} + +var xxx_messageInfo_MultipleHeaderSigningProof proto.InternalMessageInfo + +func (m *MultipleHeaderSigningProof) GetHeadersV2() HeadersV2 { + if m != nil { + return m.HeadersV2 + } + return HeadersV2{} +} + +func (m *MultipleHeaderSigningProof) GetSignersSlashData() map[string]SignerSlashingData { + if m != nil { + return m.SignersSlashData + } + return nil +} + +func init() { + proto.RegisterType((*SignerSlashingData)(nil), "proto.SignerSlashingData") + proto.RegisterType((*MultipleHeaderSigningProof)(nil), "proto.MultipleHeaderSigningProof") + proto.RegisterMapType((map[string]SignerSlashingData)(nil), "proto.MultipleHeaderSigningProof.SignersSlashDataEntry") +} + +func init() { proto.RegisterFile("multipleHeaderSigningProof.proto", fileDescriptor_f61c9968ee2015ff) } + +var fileDescriptor_f61c9968ee2015ff = []byte{ + // 363 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xc8, 0x2d, 0xcd, 0x29, + 0xc9, 0x2c, 0xc8, 0x49, 0xf5, 0x48, 0x4d, 0x4c, 0x49, 0x2d, 0x0a, 0xce, 0x4c, 0xcf, 0xcb, 0xcc, + 0x4b, 0x0f, 0x28, 0xca, 0xcf, 0x4f, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, + 0x52, 0xdc, 0xc5, 0x39, 0x89, 0xc5, 0x19, 0x10, 0x31, 0x29, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, + 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xf4, 0xfc, 0xf4, 0x7c, 0x7d, 0xb0, 0x70, 0x52, 0x69, 0x1a, + 0x98, 0x07, 0xe6, 0x80, 0x59, 0x10, 0xe5, 0x4a, 0x35, 0x5c, 0x42, 0x20, 0x83, 0x53, 0x8b, 0x82, + 0x41, 0x66, 0x64, 0xe6, 0xa5, 0xbb, 0x24, 0x96, 0x24, 0x0a, 0x19, 0x70, 0x09, 0x83, 0x45, 0x53, + 0x20, 0x56, 0x17, 0x3b, 0x65, 0x96, 0xf8, 0x26, 0x16, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x04, + 0x61, 0x93, 0x12, 0x32, 0xe1, 0xe2, 0x0e, 0xc9, 0x28, 0x4a, 0x4d, 0x2c, 0xf1, 0x49, 0x2d, 0x4b, + 0xcd, 0x91, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x33, 0x12, 0x82, 0x58, 0xa2, 0x87, 0x24, 0x13, 0x84, + 0xac, 0x4c, 0x69, 0x2e, 0x13, 0x97, 0x94, 0x2f, 0x4e, 0x5f, 0x0a, 0x99, 0x70, 0x71, 0x42, 0x6d, + 0x09, 0x33, 0x02, 0x5b, 0xce, 0x6d, 0x24, 0x00, 0x35, 0x12, 0x2e, 0xee, 0xc4, 0x72, 0xe2, 0x9e, + 0x3c, 0x43, 0x10, 0x42, 0xa1, 0x50, 0x26, 0x97, 0x00, 0xc4, 0x4b, 0xc5, 0x60, 0x3f, 0x81, 0x3c, + 0x24, 0xc1, 0xa4, 0xc0, 0xac, 0xc1, 0x6d, 0x64, 0x0e, 0xd5, 0x8c, 0xdb, 0x4a, 0x3d, 0x74, 0x9d, + 0xae, 0x79, 0x25, 0x45, 0x95, 0x50, 0x3b, 0x30, 0x8c, 0x95, 0x8a, 0xe3, 0x12, 0xc5, 0xaa, 0x41, + 0x48, 0x80, 0x8b, 0x39, 0x3b, 0xb5, 0x12, 0xec, 0x66, 0xce, 0x20, 0x10, 0x53, 0x48, 0x9f, 0x8b, + 0xb5, 0x2c, 0x31, 0xa7, 0x34, 0x15, 0x1c, 0x34, 0xdc, 0x46, 0x92, 0x50, 0xa7, 0x60, 0x06, 0x7e, + 0x10, 0x44, 0x9d, 0x15, 0x93, 0x05, 0xa3, 0x93, 0xfd, 0x85, 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, + 0x31, 0x7c, 0x78, 0x28, 0xc7, 0xd8, 0xf0, 0x48, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x13, 0x8f, + 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0xbc, 0xf1, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x17, + 0x8f, 0xe4, 0x18, 0x3e, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, + 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x58, 0xc1, 0x69, 0x22, 0x89, 0x0d, 0x6c, 0x8b, 0x31, 0x20, 0x00, + 0x00, 0xff, 0xff, 0x16, 0x42, 0xdd, 0x96, 0x4c, 0x02, 0x00, 0x00, +} + +func (this *SignerSlashingData) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*SignerSlashingData) + if !ok { + that2, ok := that.(SignerSlashingData) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.SignedHeadersBitMap, that1.SignedHeadersBitMap) { + return false + } + if this.ThreatLevel != that1.ThreatLevel { + return false + } + return true +} +func (this *MultipleHeaderSigningProof) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MultipleHeaderSigningProof) + if !ok { + that2, ok := that.(MultipleHeaderSigningProof) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.HeadersV2.Equal(&that1.HeadersV2) { + return false + } + if len(this.SignersSlashData) != len(that1.SignersSlashData) { + return false + } + for i := range this.SignersSlashData { + a := this.SignersSlashData[i] + b := that1.SignersSlashData[i] + if !(&a).Equal(&b) { + return false + } + } + return true +} +func (this *SignerSlashingData) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&slash.SignerSlashingData{") + s = append(s, "SignedHeadersBitMap: "+fmt.Sprintf("%#v", this.SignedHeadersBitMap)+",\n") + s = append(s, "ThreatLevel: "+fmt.Sprintf("%#v", this.ThreatLevel)+",\n") + s = append(s, "}") + return strings.Join(s, "") +} +func (this *MultipleHeaderSigningProof) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&slash.MultipleHeaderSigningProof{") + s = append(s, "HeadersV2: "+strings.Replace(this.HeadersV2.GoString(), `&`, ``, 1)+",\n") + keysForSignersSlashData := make([]string, 0, len(this.SignersSlashData)) + for k, _ := range this.SignersSlashData { + keysForSignersSlashData = append(keysForSignersSlashData, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForSignersSlashData) + mapStringForSignersSlashData := "map[string]SignerSlashingData{" + for _, k := range keysForSignersSlashData { + mapStringForSignersSlashData += fmt.Sprintf("%#v: %#v,", k, this.SignersSlashData[k]) + } + mapStringForSignersSlashData += "}" + if this.SignersSlashData != nil { + s = append(s, "SignersSlashData: "+mapStringForSignersSlashData+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringMultipleHeaderSigningProof(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *SignerSlashingData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignerSlashingData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignerSlashingData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ThreatLevel != 0 { + i = encodeVarintMultipleHeaderSigningProof(dAtA, i, uint64(m.ThreatLevel)) + i-- + dAtA[i] = 0x10 + } + if len(m.SignedHeadersBitMap) > 0 { + i -= len(m.SignedHeadersBitMap) + copy(dAtA[i:], m.SignedHeadersBitMap) + i = encodeVarintMultipleHeaderSigningProof(dAtA, i, uint64(len(m.SignedHeadersBitMap))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MultipleHeaderSigningProof) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MultipleHeaderSigningProof) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MultipleHeaderSigningProof) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SignersSlashData) > 0 { + keysForSignersSlashData := make([]string, 0, len(m.SignersSlashData)) + for k := range m.SignersSlashData { + keysForSignersSlashData = append(keysForSignersSlashData, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForSignersSlashData) + for iNdEx := len(keysForSignersSlashData) - 1; iNdEx >= 0; iNdEx-- { + v := m.SignersSlashData[string(keysForSignersSlashData[iNdEx])] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMultipleHeaderSigningProof(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(keysForSignersSlashData[iNdEx]) + copy(dAtA[i:], keysForSignersSlashData[iNdEx]) + i = encodeVarintMultipleHeaderSigningProof(dAtA, i, uint64(len(keysForSignersSlashData[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintMultipleHeaderSigningProof(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.HeadersV2.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMultipleHeaderSigningProof(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintMultipleHeaderSigningProof(dAtA []byte, offset int, v uint64) int { + offset -= sovMultipleHeaderSigningProof(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SignerSlashingData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SignedHeadersBitMap) + if l > 0 { + n += 1 + l + sovMultipleHeaderSigningProof(uint64(l)) + } + if m.ThreatLevel != 0 { + n += 1 + sovMultipleHeaderSigningProof(uint64(m.ThreatLevel)) + } + return n +} + +func (m *MultipleHeaderSigningProof) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.HeadersV2.Size() + n += 1 + l + sovMultipleHeaderSigningProof(uint64(l)) + if len(m.SignersSlashData) > 0 { + for k, v := range m.SignersSlashData { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovMultipleHeaderSigningProof(uint64(len(k))) + 1 + l + sovMultipleHeaderSigningProof(uint64(l)) + n += mapEntrySize + 1 + sovMultipleHeaderSigningProof(uint64(mapEntrySize)) + } + } + return n +} + +func sovMultipleHeaderSigningProof(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMultipleHeaderSigningProof(x uint64) (n int) { + return sovMultipleHeaderSigningProof(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *SignerSlashingData) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SignerSlashingData{`, + `SignedHeadersBitMap:` + fmt.Sprintf("%v", this.SignedHeadersBitMap) + `,`, + `ThreatLevel:` + fmt.Sprintf("%v", this.ThreatLevel) + `,`, + `}`, + }, "") + return s +} +func (this *MultipleHeaderSigningProof) String() string { + if this == nil { + return "nil" + } + keysForSignersSlashData := make([]string, 0, len(this.SignersSlashData)) + for k, _ := range this.SignersSlashData { + keysForSignersSlashData = append(keysForSignersSlashData, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForSignersSlashData) + mapStringForSignersSlashData := "map[string]SignerSlashingData{" + for _, k := range keysForSignersSlashData { + mapStringForSignersSlashData += fmt.Sprintf("%v: %v,", k, this.SignersSlashData[k]) + } + mapStringForSignersSlashData += "}" + s := strings.Join([]string{`&MultipleHeaderSigningProof{`, + `HeadersV2:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.HeadersV2), "HeadersV2", "HeadersV2", 1), `&`, ``, 1) + `,`, + `SignersSlashData:` + mapStringForSignersSlashData + `,`, + `}`, + }, "") + return s +} +func valueToStringMultipleHeaderSigningProof(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *SignerSlashingData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignerSlashingData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignerSlashingData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignedHeadersBitMap", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignedHeadersBitMap = append(m.SignedHeadersBitMap[:0], dAtA[iNdEx:postIndex]...) + if m.SignedHeadersBitMap == nil { + m.SignedHeadersBitMap = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThreatLevel", wireType) + } + m.ThreatLevel = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThreatLevel |= ThreatLevel(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMultipleHeaderSigningProof(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MultipleHeaderSigningProof) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MultipleHeaderSigningProof: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MultipleHeaderSigningProof: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HeadersV2", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.HeadersV2.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignersSlashData", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignersSlashData == nil { + m.SignersSlashData = make(map[string]SignerSlashingData) + } + var mapkey string + mapvalue := &SignerSlashingData{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &SignerSlashingData{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipMultipleHeaderSigningProof(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.SignersSlashData[mapkey] = *mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMultipleHeaderSigningProof(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMultipleHeaderSigningProof + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMultipleHeaderSigningProof(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMultipleHeaderSigningProof + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMultipleHeaderSigningProof + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMultipleHeaderSigningProof + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMultipleHeaderSigningProof + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMultipleHeaderSigningProof = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMultipleHeaderSigningProof = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMultipleHeaderSigningProof = fmt.Errorf("proto: unexpected end of group") +) diff --git a/data/slash/multipleHeaderSigningProof.proto b/data/slash/multipleHeaderSigningProof.proto new file mode 100644 index 000000000..fc70fc275 --- /dev/null +++ b/data/slash/multipleHeaderSigningProof.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package proto; + +option go_package = "slash"; +option (gogoproto.stable_marshaler_all) = true; + +import "slash.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +message SignerSlashingData { + bytes SignedHeadersBitMap = 1; + ThreatLevel ThreatLevel = 2; +} + +message MultipleHeaderSigningProof { + HeadersV2 HeadersV2 = 1 [(gogoproto.nullable) = false]; + map SignersSlashData = 2 [(gogoproto.nullable) = false]; +} diff --git a/data/slash/multipleHeaderSigningProof_test.go b/data/slash/multipleHeaderSigningProof_test.go new file mode 100644 index 000000000..5c3c1ef7a --- /dev/null +++ b/data/slash/multipleHeaderSigningProof_test.go @@ -0,0 +1,260 @@ +package slash_test + +import ( + "strings" + "testing" + + "github.com/ElrondNetwork/elrond-go-core/data" + "github.com/ElrondNetwork/elrond-go-core/data/block" + dataMock "github.com/ElrondNetwork/elrond-go-core/data/mock" + "github.com/ElrondNetwork/elrond-go-core/data/slash" + testscommon "github.com/ElrondNetwork/elrond-go-core/data/testscommon/slash" + "github.com/ElrondNetwork/elrond-go-core/marshal" + "github.com/stretchr/testify/require" +) + +func TestNewMultipleSigningProof(t *testing.T) { + tests := []struct { + args map[string]slash.SlashingResult + expectedErr error + }{ + { + args: nil, + expectedErr: data.ErrNilSlashResult, + }, + { + args: make(map[string]slash.SlashingResult), + expectedErr: data.ErrEmptyHeaderInfoList, + }, + { + args: map[string]slash.SlashingResult{ + "pubKey": {Headers: nil}, + }, + expectedErr: data.ErrEmptyHeaderInfoList, + }, + { + args: map[string]slash.SlashingResult{ + "pubKey": {Headers: []data.HeaderInfoHandler{nil}}, + }, + expectedErr: data.ErrNilHeaderInfo, + }, + { + args: map[string]slash.SlashingResult{ + "pubKey": {Headers: []data.HeaderInfoHandler{ + &dataMock.HeaderInfoStub{Header: &block.HeaderV2{}, Hash: []byte("h")}, + &dataMock.HeaderInfoStub{Header: &block.HeaderV2{}, Hash: []byte("h")}}, + }, + }, + expectedErr: data.ErrHeadersSameHash, + }, + } + + for _, currTest := range tests { + _, err := slash.NewMultipleSigningProof(currTest.args) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), currTest.expectedErr.Error())) + } +} + +func TestMultipleSigningProof_GetHeadersGetLevel(t *testing.T) { + h1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1}} + h2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2}} + h3 := &block.HeaderV2{Header: &block.Header{TimeStamp: 3}} + h4 := &block.HeaderV2{Header: &block.Header{TimeStamp: 4}} + h5 := &block.HeaderV2{Header: &block.Header{TimeStamp: 5}} + + hInfo1 := &dataMock.HeaderInfoStub{Header: h1, Hash: []byte("h1")} + hInfo2 := &dataMock.HeaderInfoStub{Header: h2, Hash: []byte("h2")} + hInfo3 := &dataMock.HeaderInfoStub{Header: h3, Hash: []byte("h3")} + hInfo4 := &dataMock.HeaderInfoStub{Header: h4, Hash: []byte("h4")} + hInfo5 := &dataMock.HeaderInfoStub{Header: h5, Hash: []byte("h5")} + + slashRes1 := slash.SlashingResult{ + SlashingLevel: slash.High, + Headers: []data.HeaderInfoHandler{hInfo4, hInfo2, hInfo1, hInfo3}, + } + slashRes2 := slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{hInfo3, hInfo5, hInfo4}, + } + slashRes3 := slash.SlashingResult{ + SlashingLevel: slash.Zero, + Headers: []data.HeaderInfoHandler{}, + } + slashRes4 := slash.SlashingResult{ + SlashingLevel: slash.Zero, + Headers: nil, + } + slashRes := map[string]slash.SlashingResult{ + "pubKey1": slashRes1, + "pubKey2": slashRes2, + "pubKey3": slashRes3, + "pubKey4": slashRes4, + } + + proof, err := slash.NewMultipleSigningProof(slashRes) + require.Nil(t, err) + + require.Equal(t, slash.High, proof.GetLevel([]byte("pubKey1"))) + require.Equal(t, slash.Medium, proof.GetLevel([]byte("pubKey2"))) + require.Equal(t, slash.Zero, proof.GetLevel([]byte("pubKey3"))) + require.Equal(t, slash.Zero, proof.GetLevel([]byte("pubKey4"))) + require.Equal(t, slash.Zero, proof.GetLevel([]byte("pubKey5"))) + + require.Equal(t, []data.HeaderHandler{h1, h2, h3, h4}, proof.GetHeaders([]byte("pubKey1"))) + require.Equal(t, []data.HeaderHandler{h3, h4, h5}, proof.GetHeaders([]byte("pubKey2"))) + require.Empty(t, proof.GetHeaders([]byte("pubKey3"))) + require.Empty(t, proof.GetHeaders([]byte("pubKey4"))) + require.Empty(t, proof.GetHeaders([]byte("pubKey5"))) +} + +func TestMultipleSigningProof_GetProofTxDataNotEnoughPublicKeysProvidedExpectError(t *testing.T) { + proof := slash.MultipleHeaderSigningProof{} + + proofTxData, err := proof.GetProofTxData() + require.Nil(t, proofTxData) + require.Equal(t, data.ErrNotEnoughPublicKeysProvided, err) +} + +func TestMultipleSigningProof_GetProofTxDataNotEnoughHeadersProvidedExpectError(t *testing.T) { + proof := slash.MultipleHeaderSigningProof{ + HeadersV2: slash.HeadersV2{}, + SignersSlashData: map[string]slash.SignerSlashingData{ + "pubKey1": {}, + }, + } + + proofTxData, err := proof.GetProofTxData() + require.Nil(t, proofTxData) + require.Equal(t, data.ErrNotEnoughHeadersProvided, err) +} + +func TestMultipleSigningProof_GetProofTxDataNilHeaderHandlerExpectError(t *testing.T) { + proof := &slash.MultipleHeaderSigningProof{ + HeadersV2: slash.HeadersV2{Headers: []*block.HeaderV2{nil}}, + SignersSlashData: map[string]slash.SignerSlashingData{ + "pubKey1": {SignedHeadersBitMap: []byte{0x1}}, + }, + } + + proofTxData, err := proof.GetProofTxData() + require.Nil(t, proofTxData) + require.Equal(t, data.ErrNilHeaderHandler, err) +} + +func TestMultipleSigningProof_GetProofTxData(t *testing.T) { + round := uint64(1) + shardID := uint32(2) + + header := &block.HeaderV2{ + Header: &block.Header{ + Round: round, + ShardID: shardID, + }, + } + headerInfo := &dataMock.HeaderInfoStub{ + Header: header, + Hash: []byte("hash"), + } + + slashResPubKey1 := slash.SlashingResult{ + SlashingLevel: slash.High, + Headers: []data.HeaderInfoHandler{headerInfo}, + } + slashRes := map[string]slash.SlashingResult{ + "pubKey1": slashResPubKey1, + } + proof, _ := slash.NewMultipleSigningProof(slashRes) + + expectedProofTxData := &slash.ProofTxData{ + Round: round, + ShardID: shardID, + ProofID: slash.MultipleSigningProofID, + } + + proofTxData, err := proof.GetProofTxData() + require.Equal(t, expectedProofTxData, proofTxData) + require.Nil(t, err) +} + +func TestMultipleSigningProof_Marshal_Unmarshal(t *testing.T) { + h1 := &block.HeaderV2{Header: &block.Header{TimeStamp: 1}} + h2 := &block.HeaderV2{Header: &block.Header{TimeStamp: 2}} + h3 := &block.HeaderV2{Header: &block.Header{TimeStamp: 3}} + h4 := &block.HeaderV2{Header: &block.Header{TimeStamp: 4}} + h5 := &block.HeaderV2{Header: &block.Header{TimeStamp: 5}} + + hInfo1 := &dataMock.HeaderInfoStub{Header: h1, Hash: []byte("h1")} + hInfo2 := &dataMock.HeaderInfoStub{Header: h2, Hash: []byte("h2")} + hInfo3 := &dataMock.HeaderInfoStub{Header: h3, Hash: []byte("h3")} + hInfo4 := &dataMock.HeaderInfoStub{Header: h4, Hash: []byte("h4")} + hInfo5 := &dataMock.HeaderInfoStub{Header: h5, Hash: []byte("h5")} + + slashResPubKey1 := slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{hInfo1, hInfo2, hInfo3}, + } + slashResPubKey2 := slash.SlashingResult{ + SlashingLevel: slash.High, + Headers: []data.HeaderInfoHandler{hInfo3, hInfo4, hInfo5}, + } + slashResProof1 := map[string]slash.SlashingResult{ + "pubKey1": slashResPubKey1, + "pubKey2": slashResPubKey2, + } + + // Same slash result for pubKey1, but change headers order + slashResPubKey1 = slash.SlashingResult{ + SlashingLevel: slash.Medium, + Headers: []data.HeaderInfoHandler{hInfo3, hInfo1, hInfo2}, + } + // Same slash result for pubKey2, but change headers order + slashResPubKey2 = slash.SlashingResult{ + SlashingLevel: slash.High, + Headers: []data.HeaderInfoHandler{hInfo4, hInfo3, hInfo5}, + } + + // Change pub keys order in map + slashResProof2 := map[string]slash.SlashingResult{ + "pubKey2": slashResPubKey2, + "pubKey1": slashResPubKey1, + } + + proof1, err1 := slash.NewMultipleSigningProof(slashResProof1) + proof2, err2 := slash.NewMultipleSigningProof(slashResProof2) + require.Nil(t, err1) + require.Nil(t, err2) + require.Equal(t, proof1, proof2) + + marshaller := marshal.GogoProtoMarshalizer{} + proof1Bytes, err1 := marshaller.Marshal(proof1) + proof2Bytes, err2 := marshaller.Marshal(proof2) + require.Nil(t, err1) + require.Nil(t, err2) + require.Equal(t, proof1Bytes, proof2Bytes) + + proof1Unmarshalled := &slash.MultipleHeaderSigningProof{} + proof2Unmarshalled := &slash.MultipleHeaderSigningProof{} + err1 = marshaller.Unmarshal(proof1Unmarshalled, proof1Bytes) + err2 = marshaller.Unmarshal(proof2Unmarshalled, proof2Bytes) + require.Nil(t, err1) + require.Nil(t, err2) + require.Equal(t, proof1Unmarshalled, proof1) + require.Equal(t, proof2Unmarshalled, proof2) +} + +func BenchmarkNewMultipleSigningProof(b *testing.B) { + // Worst case scenario: 25% of a consensus group of 400 validators on metachain signed 3 different headers + noOfPubKeys := uint32(0.25 * 400) + noOfHeaders := uint32(3) + slashRes := testscommon.GenerateSlashResults(b, noOfPubKeys, noOfHeaders) + + for n := 0; n < b.N; n++ { + proof, err := slash.NewMultipleSigningProof(slashRes) + + b.StopTimer() + require.NotNil(b, proof) + require.Nil(b, err) + b.StartTimer() + } +} diff --git a/data/slash/slash.go b/data/slash/slash.go new file mode 100644 index 000000000..8084805f8 --- /dev/null +++ b/data/slash/slash.go @@ -0,0 +1,40 @@ +//go:generate protoc -I=. -I=$GOPATH/src/github.com/ElrondNetwork/elrond-go-core/data/block -I=$GOPATH/src -I=$GOPATH/src/github.com/ElrondNetwork/protobuf/protobuf --gogoslick_out=. slash.proto +package slash + +import ( + "github.com/ElrondNetwork/elrond-go-core/data" + "github.com/ElrondNetwork/elrond-go-core/data/block" +) + +// GetHeaderHandlers returns a slice of data.HeaderHandler representing a slice of internal headers v2 +func (m *HeadersV2) GetHeaderHandlers() []data.HeaderHandler { + if m == nil { + return nil + } + + ret := make([]data.HeaderHandler, 0, len(m.Headers)) + + for _, header := range m.Headers { + ret = append(ret, header) + } + + return ret +} + +// SetHeaders sets internal headers v2 slice to a given slice of data.HeaderHandler. +func (m *HeadersV2) SetHeaders(headers []data.HeaderHandler) error { + if m == nil { + return data.ErrNilPointerReceiver + } + + for _, header := range headers { + hdr, castOk := header.(*block.HeaderV2) + if !castOk { + return data.ErrInvalidTypeAssertion + } + + m.Headers = append(m.Headers, hdr) + } + + return nil +} diff --git a/data/slash/slash.pb.go b/data/slash/slash.pb.go new file mode 100644 index 000000000..121f5819b --- /dev/null +++ b/data/slash/slash.pb.go @@ -0,0 +1,443 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: slash.proto + +package slash + +import ( + fmt "fmt" + block "github.com/ElrondNetwork/elrond-go-core/data/block" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strconv "strconv" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type ThreatLevel int32 + +const ( + Zero ThreatLevel = 0 + Low ThreatLevel = 1 + Medium ThreatLevel = 2 + High ThreatLevel = 3 +) + +var ThreatLevel_name = map[int32]string{ + 0: "Zero", + 1: "Low", + 2: "Medium", + 3: "High", +} + +var ThreatLevel_value = map[string]int32{ + "Zero": 0, + "Low": 1, + "Medium": 2, + "High": 3, +} + +func (ThreatLevel) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_7c4604f407d65f7b, []int{0} +} + +type HeadersV2 struct { + Headers []*block.HeaderV2 `protobuf:"bytes,1,rep,name=Headers,proto3" json:"Headers,omitempty"` +} + +func (m *HeadersV2) Reset() { *m = HeadersV2{} } +func (*HeadersV2) ProtoMessage() {} +func (*HeadersV2) Descriptor() ([]byte, []int) { + return fileDescriptor_7c4604f407d65f7b, []int{0} +} +func (m *HeadersV2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeadersV2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *HeadersV2) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeadersV2.Merge(m, src) +} +func (m *HeadersV2) XXX_Size() int { + return m.Size() +} +func (m *HeadersV2) XXX_DiscardUnknown() { + xxx_messageInfo_HeadersV2.DiscardUnknown(m) +} + +var xxx_messageInfo_HeadersV2 proto.InternalMessageInfo + +func (m *HeadersV2) GetHeaders() []*block.HeaderV2 { + if m != nil { + return m.Headers + } + return nil +} + +func init() { + proto.RegisterEnum("proto.ThreatLevel", ThreatLevel_name, ThreatLevel_value) + proto.RegisterType((*HeadersV2)(nil), "proto.HeadersV2") +} + +func init() { proto.RegisterFile("slash.proto", fileDescriptor_7c4604f407d65f7b) } + +var fileDescriptor_7c4604f407d65f7b = []byte{ + // 278 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0xce, 0x49, 0x2c, + 0xce, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x52, 0xba, 0xe9, 0x99, 0x25, + 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0xfa, 0x60, 0xe1, 0xa4, + 0xd2, 0x34, 0x30, 0x0f, 0xcc, 0x01, 0xb3, 0x20, 0xba, 0xa4, 0x1c, 0x90, 0x94, 0xbb, 0xe6, 0x14, + 0xe5, 0xe7, 0xa5, 0xf8, 0xa5, 0x96, 0x94, 0xe7, 0x17, 0x65, 0xeb, 0xa7, 0x82, 0x79, 0xba, 0xe9, + 0xf9, 0xba, 0xc9, 0xf9, 0x45, 0xa9, 0xfa, 0x29, 0x89, 0x25, 0x89, 0xfa, 0x49, 0x39, 0xf9, 0xc9, + 0xd9, 0x10, 0x32, 0xcc, 0x08, 0x62, 0x82, 0x92, 0x19, 0x17, 0xa7, 0x47, 0x6a, 0x62, 0x4a, 0x6a, + 0x51, 0x71, 0x98, 0x91, 0x90, 0x26, 0x17, 0x3b, 0x94, 0x23, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, + 0xc4, 0x0f, 0x51, 0xa5, 0x07, 0x11, 0x0d, 0x33, 0x0a, 0x82, 0xc9, 0x6b, 0x99, 0x71, 0x71, 0x87, + 0x64, 0x14, 0xa5, 0x26, 0x96, 0xf8, 0xa4, 0x96, 0xa5, 0xe6, 0x08, 0x71, 0x70, 0xb1, 0x44, 0xa5, + 0x16, 0xe5, 0x0b, 0x30, 0x08, 0xb1, 0x73, 0x31, 0xfb, 0xe4, 0x97, 0x0b, 0x30, 0x0a, 0x71, 0x71, + 0xb1, 0xf9, 0xa6, 0xa6, 0x64, 0x96, 0xe6, 0x0a, 0x30, 0x81, 0xa4, 0x3d, 0x32, 0xd3, 0x33, 0x04, + 0x98, 0x9d, 0xec, 0x2f, 0x3c, 0x94, 0x63, 0xb8, 0xf1, 0x50, 0x8e, 0xe1, 0xc3, 0x43, 0x39, 0xc6, + 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, + 0xe3, 0x8d, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0xbe, 0x78, 0x24, 0xc7, 0xf0, 0xe1, 0x91, + 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xc5, + 0x0a, 0x0e, 0xae, 0x24, 0x36, 0xb0, 0x8b, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x55, + 0xd0, 0xd6, 0x3e, 0x01, 0x00, 0x00, +} + +func (x ThreatLevel) String() string { + s, ok := ThreatLevel_name[int32(x)] + if ok { + return s + } + return strconv.Itoa(int(x)) +} +func (this *HeadersV2) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*HeadersV2) + if !ok { + that2, ok := that.(HeadersV2) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Headers) != len(that1.Headers) { + return false + } + for i := range this.Headers { + if !this.Headers[i].Equal(that1.Headers[i]) { + return false + } + } + return true +} +func (this *HeadersV2) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&slash.HeadersV2{") + if this.Headers != nil { + s = append(s, "Headers: "+fmt.Sprintf("%#v", this.Headers)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringSlash(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *HeadersV2) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HeadersV2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeadersV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Headers) > 0 { + for iNdEx := len(m.Headers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Headers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSlash(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintSlash(dAtA []byte, offset int, v uint64) int { + offset -= sovSlash(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *HeadersV2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Headers) > 0 { + for _, e := range m.Headers { + l = e.Size() + n += 1 + l + sovSlash(uint64(l)) + } + } + return n +} + +func sovSlash(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSlash(x uint64) (n int) { + return sovSlash(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *HeadersV2) String() string { + if this == nil { + return "nil" + } + repeatedStringForHeaders := "[]*HeaderV2{" + for _, f := range this.Headers { + repeatedStringForHeaders += strings.Replace(fmt.Sprintf("%v", f), "HeaderV2", "block.HeaderV2", 1) + "," + } + repeatedStringForHeaders += "}" + s := strings.Join([]string{`&HeadersV2{`, + `Headers:` + repeatedStringForHeaders + `,`, + `}`, + }, "") + return s +} +func valueToStringSlash(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *HeadersV2) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlash + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HeadersV2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HeadersV2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Headers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlash + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSlash + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSlash + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Headers = append(m.Headers, &block.HeaderV2{}) + if err := m.Headers[len(m.Headers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSlash(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthSlash + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthSlash + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSlash(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSlash + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSlash + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSlash + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSlash + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSlash + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSlash + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSlash = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSlash = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSlash = fmt.Errorf("proto: unexpected end of group") +) diff --git a/data/slash/slash.proto b/data/slash/slash.proto new file mode 100644 index 000000000..cfec9e6e8 --- /dev/null +++ b/data/slash/slash.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package proto; + +option go_package = "slash"; +option (gogoproto.stable_marshaler_all) = true; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/ElrondNetwork/elrond-go-core/data/block/blockV2.proto"; + +enum ThreatLevel { + Zero = 0; + Low = 1; + Medium = 2; + High = 3; +} + +message HeadersV2{ + repeated HeaderV2 Headers = 1; +} diff --git a/data/slash/slash_test.go b/data/slash/slash_test.go new file mode 100644 index 000000000..5425a6e55 --- /dev/null +++ b/data/slash/slash_test.go @@ -0,0 +1,45 @@ +package slash_test + +import ( + "testing" + + "github.com/ElrondNetwork/elrond-go-core/data" + "github.com/ElrondNetwork/elrond-go-core/data/block" + "github.com/ElrondNetwork/elrond-go-core/data/slash" + "github.com/stretchr/testify/require" +) + +func TestHeaders_SetHeadersInvalidHeadersExpectError(t *testing.T) { + header := &block.Header{TimeStamp: 1} + headers := slash.HeadersV2{} + + err := headers.SetHeaders([]data.HeaderHandler{header}) + require.Equal(t, data.ErrInvalidTypeAssertion, err) +} + +func TestHeaders_SetHeadersGetHeadersGetHeaderHandlers(t *testing.T) { + header1 := &block.HeaderV2{ + Header: &block.Header{ + TimeStamp: 1, + }, + } + header2 := &block.HeaderV2{ + Header: &block.Header{ + TimeStamp: 2, + }, + } + + headers := []data.HeaderHandler{header1, header2} + headersV2 := slash.HeadersV2{} + + err := headersV2.SetHeaders(headers) + require.Nil(t, err) + + require.Len(t, headersV2.GetHeaders(), 2) + require.Contains(t, headersV2.GetHeaders(), header1) + require.Contains(t, headersV2.GetHeaders(), header2) + + require.Len(t, headersV2.GetHeaderHandlers(), 2) + require.Contains(t, headersV2.GetHeaderHandlers(), header1) + require.Contains(t, headersV2.GetHeaderHandlers(), header2) +} diff --git a/data/testscommon/slash/generator.go b/data/testscommon/slash/generator.go new file mode 100644 index 000000000..08f9a3c2a --- /dev/null +++ b/data/testscommon/slash/generator.go @@ -0,0 +1,61 @@ +package slash + +import ( + "fmt" + "testing" + + "github.com/ElrondNetwork/elrond-go-core/core" + "github.com/ElrondNetwork/elrond-go-core/data" + "github.com/ElrondNetwork/elrond-go-core/data/block" + dataMock "github.com/ElrondNetwork/elrond-go-core/data/mock" + "github.com/ElrondNetwork/elrond-go-core/data/slash" + "github.com/ElrondNetwork/elrond-go-core/hashing" + "github.com/ElrondNetwork/elrond-go-core/hashing/blake2b" + "github.com/ElrondNetwork/elrond-go-core/marshal" + "github.com/stretchr/testify/require" +) + +func GenerateSlashResults(b *testing.B, noOfPubKeys uint32, noOfHeaders uint32) map[string]slash.SlashingResult { + hasher, err := blake2b.NewBlake2bWithSize(16) + require.Nil(b, err) + + headers := GenerateDistinctHeadersInfo(b, noOfHeaders, hasher) + slashRes := make(map[string]slash.SlashingResult, noOfPubKeys) + for i := 0; i < int(noOfPubKeys); i++ { + tmp := fmt.Sprintf("pubKey%v", i) + pubKey := hasher.Compute(tmp) + + slashRes[string(pubKey)] = slash.SlashingResult{ + Headers: headers, + SlashingLevel: CalcThreatLevel(noOfHeaders), + } + } + + return slashRes +} + +func GenerateDistinctHeadersInfo(b *testing.B, noOfHeaders uint32, hasher hashing.Hasher) []data.HeaderInfoHandler { + marshaller := &marshal.GogoProtoMarshalizer{} + headers := make([]data.HeaderInfoHandler, 0, noOfHeaders) + for i := 0; i < int(noOfHeaders); i++ { + header := &block.HeaderV2{Header: &block.Header{Round: 1, TimeStamp: uint64(i)}} + hash, err := core.CalculateHash(marshaller, hasher, header) + require.Nil(b, err) + + headerInfo := &dataMock.HeaderInfoStub{Header: header, Hash: hash} + headers = append(headers, headerInfo) + } + + return headers +} + +func CalcThreatLevel(noOfHeaders uint32) slash.ThreatLevel { + threatLevel := slash.Zero + if noOfHeaders == slash.MinSlashableNoOfHeaders { + threatLevel = slash.Medium + } else if noOfHeaders >= slash.MinSlashableNoOfHeaders { + threatLevel = slash.High + } + + return threatLevel +}