Skip to content

Commit 98a622a

Browse files
authored
Merge pull request #116 from qubic/feature/decentralisation
Feature/decentralisation: Add score check for quorum vote verification
2 parents 3701fd3 + 95e7af5 commit 98a622a

File tree

10 files changed

+185
-476
lines changed

10 files changed

+185
-476
lines changed

.github/workflows/custom-branch-deployment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212

1313
strategy:
1414
matrix:
15-
go-version: [ '1.22.x' ]
15+
go-version: [ '1.24.x' ]
1616

1717
steps:
1818
- name: "Git checkout"

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jobs:
44
test-nocache:
55
strategy:
66
matrix:
7-
go-version: [1.22.x]
7+
go-version: [1.24.x]
88
os: [ubuntu-latest]
99
runs-on: ${{ matrix.os }}
1010
steps:

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.22 AS builder
1+
FROM golang:1.24 AS builder
22
ENV CGO_ENABLED=0
33

44
WORKDIR /src

go.mod

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
module github.com/qubic/go-archiver
22

3-
go 1.22.2
3+
go 1.24.0
44

55
require (
66
github.com/ardanlabs/conf v1.5.0
7-
github.com/cloudflare/circl v1.5.0
8-
github.com/cockroachdb/pebble v1.1.2
9-
github.com/google/go-cmp v0.6.0
10-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0
7+
github.com/cloudflare/circl v1.6.1
8+
github.com/cockroachdb/pebble v1.1.5
9+
github.com/google/go-cmp v0.7.0
10+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
1111
github.com/pkg/errors v0.9.1
12-
github.com/qubic/go-node-connector v0.11.0
12+
github.com/qubic/go-node-connector v0.13.0
1313
github.com/qubic/go-schnorrq v1.0.1
1414
github.com/stretchr/testify v1.10.0
1515
go.uber.org/zap v1.27.0
16-
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9
17-
google.golang.org/grpc v1.67.1
18-
google.golang.org/protobuf v1.36.1
16+
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34
17+
google.golang.org/grpc v1.72.0
18+
google.golang.org/protobuf v1.36.6
1919
)
2020

2121
require (
@@ -32,26 +32,27 @@ require (
3232
github.com/davecgh/go-spew v1.1.1 // indirect
3333
github.com/getsentry/sentry-go v0.27.0 // indirect
3434
github.com/gogo/protobuf v1.3.2 // indirect
35-
github.com/golang/protobuf v1.5.3 // indirect
35+
github.com/golang/protobuf v1.5.4 // indirect
3636
github.com/golang/snappy v0.0.4 // indirect
3737
github.com/klauspost/compress v1.16.0 // indirect
3838
github.com/kr/pretty v0.3.1 // indirect
3939
github.com/kr/text v0.2.0 // indirect
4040
github.com/linckode/circl v1.3.71 // indirect
41-
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
41+
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
4242
github.com/pmezard/go-difflib v1.0.0 // indirect
43-
github.com/prometheus/client_golang v1.12.0 // indirect
44-
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect
45-
github.com/prometheus/common v0.32.1 // indirect
46-
github.com/prometheus/procfs v0.7.3 // indirect
43+
github.com/prometheus/client_golang v1.15.0 // indirect
44+
github.com/prometheus/client_model v0.3.0 // indirect
45+
github.com/prometheus/common v0.42.0 // indirect
46+
github.com/prometheus/procfs v0.9.0 // indirect
4747
github.com/rogpeppe/go-internal v1.13.1 // indirect
4848
github.com/silenceper/pool v1.0.0 // indirect
4949
github.com/sirupsen/logrus v1.9.0 // indirect
5050
go.uber.org/multierr v1.10.0 // indirect
51+
golang.org/x/crypto v0.36.0 // indirect
5152
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
52-
golang.org/x/net v0.28.0 // indirect
53-
golang.org/x/sys v0.26.0 // indirect
54-
golang.org/x/text v0.17.0 // indirect
55-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect
53+
golang.org/x/net v0.37.0 // indirect
54+
golang.org/x/sys v0.31.0 // indirect
55+
golang.org/x/text v0.23.0 // indirect
56+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect
5657
gopkg.in/yaml.v3 v3.0.1 // indirect
5758
)

go.sum

Lines changed: 58 additions & 448 deletions
Large diffs are not rendered by default.

store/keys.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
EmptyTicksPerEpoch = 0x13
2222
LastTickQuorumDataPerEpochInterval = 0x14
2323
EmptyTickListPerEpoch = 0x15
24+
TargetTickVoteSignature = 0x16
2425
)
2526

2627
func emptyTickListPerEpochKey(epoch uint32) []byte {
@@ -139,3 +140,10 @@ func tickTxStatusKey(tickNumber uint64) []byte {
139140

140141
return key
141142
}
143+
144+
func targetTickVoteSignatureKey(epoch uint32) []byte {
145+
key := []byte{TargetTickVoteSignature}
146+
key = binary.BigEndian.AppendUint32(key, epoch)
147+
148+
return key
149+
}

store/store.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,3 +921,31 @@ func (s *PebbleStore) DeleteEmptyTickListKeyForEpoch(epoch uint32) error {
921921
}
922922
return nil
923923
}
924+
925+
func (s *PebbleStore) SetTargetTickVoteSignature(epoch, value uint32) error {
926+
key := targetTickVoteSignatureKey(epoch)
927+
928+
data := make([]byte, 4)
929+
binary.LittleEndian.PutUint32(data, value)
930+
931+
err := s.db.Set(key, data, pebble.Sync)
932+
if err != nil {
933+
return errors.Wrapf(err, "saving target tick vote signature for epoch %d", epoch)
934+
}
935+
return nil
936+
}
937+
938+
func (s *PebbleStore) GetTargetTickVoteSignature(epoch uint32) (uint32, error) {
939+
key := targetTickVoteSignatureKey(epoch)
940+
941+
value, closer, err := s.db.Get(key)
942+
if err != nil {
943+
if errors.Is(err, pebble.ErrNotFound) {
944+
return 0, ErrNotFound
945+
}
946+
return 0, errors.Wrap(err, "getting target tick vote signature")
947+
}
948+
defer closer.Close()
949+
950+
return binary.LittleEndian.Uint32(value), nil
951+
}

validator/quorum/validator.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ package quorum
22

33
import (
44
"context"
5+
"encoding/binary"
56
"github.com/pkg/errors"
67
"github.com/qubic/go-archiver/store"
78
"github.com/qubic/go-archiver/utils"
89
"github.com/qubic/go-node-connector/types"
910
"log"
11+
"slices"
1012
)
1113

1214
// Validate validates the quorum votes and if success returns the aligned votes back
13-
func Validate(ctx context.Context, sigVerifierFunc utils.SigVerifierFunc, quorumVotes types.QuorumVotes, computors types.Computors) (types.QuorumVotes, error) {
15+
func Validate(ctx context.Context, sigVerifierFunc utils.SigVerifierFunc, quorumVotes types.QuorumVotes, computors types.Computors, targetTickVoteSignature uint32) (types.QuorumVotes, error) {
1416
if len(quorumVotes) < types.MinimumQuorumVotes {
1517
return nil, errors.New("not enough quorum votes")
1618
}
@@ -26,7 +28,7 @@ func Validate(ctx context.Context, sigVerifierFunc utils.SigVerifierFunc, quorum
2628
}
2729

2830
log.Printf("Proceed to validate total quorum sigs: %d\n", len(alignedVotes))
29-
err = quorumTickSigVerify(ctx, sigVerifierFunc, alignedVotes, computors)
31+
err = quorumTickSigVerify(ctx, sigVerifierFunc, alignedVotes, computors, targetTickVoteSignature)
3032
if err != nil {
3133
return nil, errors.Wrap(err, "quorum tick signature verification failed")
3234
}
@@ -105,7 +107,7 @@ func getAlignedVotes(quorumVotes types.QuorumVotes) (types.QuorumVotes, error) {
105107
return alignedVotes, nil
106108
}
107109

108-
func quorumTickSigVerify(ctx context.Context, sigVerifierFunc utils.SigVerifierFunc, quorumVotes types.QuorumVotes, computors types.Computors) error {
110+
func quorumTickSigVerify(ctx context.Context, sigVerifierFunc utils.SigVerifierFunc, quorumVotes types.QuorumVotes, computors types.Computors, targetTickVoteSignature uint32) error {
109111
var successVotes = 0
110112
failedIndexes := make([]uint16, 0, 0)
111113
failedIdentites := make([]string, 0, 0)
@@ -116,7 +118,7 @@ func quorumTickSigVerify(ctx context.Context, sigVerifierFunc utils.SigVerifierF
116118
return errors.Wrap(err, "getting digest from tick data")
117119
}
118120
computorPubKey := computors.PubKeys[quorumTickData.ComputorIndex]
119-
if err := sigVerifierFunc(ctx, computorPubKey, digest, quorumTickData.Signature); err != nil {
121+
if err := verifyTickVoteSignature(ctx, sigVerifierFunc, computorPubKey, digest, quorumTickData.Signature, targetTickVoteSignature); err != nil {
120122
//return errors.Wrapf(err, "quorum tick signature verification failed for computor index: %d", quorumTickData.ComputorIndex)
121123
//log.Printf("Quorum tick signature verification failed for computor index: %d. Err: %s\n", quorumTickData.ComputorIndex, err.Error())
122124
failedIndexes = append(failedIndexes, quorumTickData.ComputorIndex)
@@ -142,6 +144,25 @@ func quorumTickSigVerify(ctx context.Context, sigVerifierFunc utils.SigVerifierF
142144
return nil
143145
}
144146

147+
func verifyTickVoteSignature(ctx context.Context, sigVerifierFunc utils.SigVerifierFunc, computorPubKey, digest [32]byte, signature [64]byte, targetTickVoteSignature uint32) error {
148+
invertedSignatureSection := swapBytes(signature[:4])
149+
score := binary.LittleEndian.Uint32(invertedSignatureSection)
150+
151+
if score > targetTickVoteSignature {
152+
return errors.New("vote signature score over target tick vote signature")
153+
}
154+
155+
return sigVerifierFunc(ctx, computorPubKey, digest, signature)
156+
}
157+
158+
func swapBytes(input []byte) []byte {
159+
output := make([]byte, len(input))
160+
copy(output, input)
161+
slices.Reverse(output)
162+
163+
return output
164+
}
165+
145166
func getDigestFromQuorumTickData(data types.QuorumTickVote) ([32]byte, error) {
146167
// xor computor index with 8
147168
data.ComputorIndex ^= 3

validator/quorum/validator_test.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestValidateVotes(t *testing.T) {
6565
},
6666
}
6767

68-
_, err := Validate(context.Background(), mockSigVerifierFunc, originalData, types.Computors{})
68+
_, err := Validate(context.Background(), mockSigVerifierFunc, originalData, types.Computors{}, 0)
6969
require.ErrorContains(t, err, "not enough quorum votes")
7070

7171
cases := []struct {
@@ -258,3 +258,27 @@ func deepCopy(votes types.QuorumVotes) types.QuorumVotes {
258258

259259
return cp
260260
}
261+
262+
func TestByteSwap(t *testing.T) {
263+
264+
testData := []struct {
265+
name string
266+
data []byte
267+
expected []byte
268+
}{
269+
{
270+
name: "Test_ByteSwap1",
271+
data: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
272+
expected: []byte{0x06, 0x05, 0x04, 0x03, 0x02, 0x01},
273+
},
274+
}
275+
276+
for _, testCase := range testData {
277+
t.Run(testCase.name, func(t *testing.T) {
278+
279+
got := swapBytes(testCase.data)
280+
require.Equal(t, testCase.expected, got)
281+
})
282+
}
283+
284+
}

validator/validator.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,24 @@ func (v *Validator) ValidateTick(ctx context.Context, initialEpochTick, tickNumb
7676
return errors.Wrap(err, "storing computors")
7777
}
7878

79-
alignedVotes, err := quorum.Validate(ctx, GoSchnorrqVerify, quorumVotes, comps)
79+
targetTickVoteSignature, err := v.store.GetTargetTickVoteSignature(uint32(epoch))
80+
if err != nil {
81+
if !errors.Is(err, store.ErrNotFound) {
82+
return errors.Wrap(err, "getting target tick vote signature")
83+
}
84+
85+
systemInfo, err := v.qu.GetSystemInfo(ctx)
86+
if err != nil {
87+
return errors.Wrap(err, "fetching system info")
88+
}
89+
err = v.store.SetTargetTickVoteSignature(uint32(systemInfo.Epoch), systemInfo.TargetTickVoteSignature)
90+
if err != nil {
91+
return errors.Wrap(err, "storing target tick vote signature")
92+
}
93+
targetTickVoteSignature = systemInfo.TargetTickVoteSignature
94+
}
95+
96+
alignedVotes, err := quorum.Validate(ctx, GoSchnorrqVerify, quorumVotes, comps, targetTickVoteSignature)
8097
if err != nil {
8198
return errors.Wrap(err, "validating quorum")
8299
}

0 commit comments

Comments
 (0)