From 7959e8aad99fbcf64766a4f61d1ad5f04afa409b Mon Sep 17 00:00:00 2001 From: Ethen Date: Thu, 5 Sep 2024 13:33:53 -0400 Subject: [PATCH] fix: Commitment decoding bug && server handler tests (#112) * fix: Commitment decoding bug && svr handler tests * fix: Commitment decoding bug && svr handler tests - lint fix * fix: Commitment decoding bug && svr handler tests - mv gosec to golangci lint * fix: Commitment decoding bug && svr handler tests - rm false positives --- .github/workflows/lint.yml | 11 -- .golangci.yml | 1 + Makefile | 4 + commitments/mode.go | 8 +- go.mod | 1 + go.sum | 1 + mocks/router.go | 95 ++++++++++++++ server/load_store.go | 2 +- server/server.go | 10 +- server/server_test.go | 248 +++++++++++++++++++++++++++++++++++++ store/memory.go | 4 +- store/router.go | 24 ++-- verify/cert.go | 2 +- verify/certificate.go | 6 +- 14 files changed, 385 insertions(+), 32 deletions(-) create mode 100644 mocks/router.go create mode 100644 server/server_test.go diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 267af97..2f1f47b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,14 +25,3 @@ jobs: version: v1.60 args: --timeout 3m - go-sec: - runs-on: ubuntu-latest - env: - GO111MODULE: on - steps: - - name: Checkout Source - uses: actions/checkout@v3 - - name: Run Gosec Security Scanner - uses: securego/gosec@master - with: - args: ./... diff --git a/.golangci.yml b/.golangci.yml index f9d64ea..7422e19 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -116,6 +116,7 @@ linters: disable-all: true enable: ## enabled by default + - gosec # Golang Security Checker - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases - gosimple # specializes in simplifying a code - ineffassign # detects when assignments to existing variables are not used diff --git a/Makefile b/Makefile index 15e4ecd..50d0e8a 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,10 @@ lint: @golangci-lint run +go-gen-mocks: + @echo "generating go mocks..." + @GO111MODULE=on go generate --run "mockgen*" ./... + install-lint: @echo "Installing golangci-lint..." @sh -c $(GET_LINT_CMD) diff --git a/commitments/mode.go b/commitments/mode.go index a0e4323..d1c1139 100644 --- a/commitments/mode.go +++ b/commitments/mode.go @@ -27,10 +27,6 @@ func StringToCommitmentMode(s string) (CommitmentMode, error) { } func StringToDecodedCommitment(key string, c CommitmentMode) ([]byte, error) { - if len(key) <= 2 { - return nil, fmt.Errorf("commitment is empty") - } - offset := 0 if key[:2] == "0x" { offset = 2 @@ -41,6 +37,10 @@ func StringToDecodedCommitment(key string, c CommitmentMode) ([]byte, error) { return nil, err } + if len(b) < 3 { + return nil, fmt.Errorf("commitment is too short") + } + switch c { case OptimismGeneric: // [op_type, ...] return b[1:], nil diff --git a/go.mod b/go.mod index e48fc2a..01e2d91 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/consensys/gnark-crypto v0.12.1 github.com/ethereum-optimism/optimism v1.9.0 github.com/ethereum/go-ethereum v1.14.0 + github.com/golang/mock v1.2.0 github.com/joho/godotenv v1.5.1 github.com/minio/minio-go/v7 v7.0.74 github.com/prometheus/client_golang v1.19.1 diff --git a/go.sum b/go.sum index 013ec0b..e6035a4 100644 --- a/go.sum +++ b/go.sum @@ -340,6 +340,7 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/mocks/router.go b/mocks/router.go new file mode 100644 index 0000000..ab69f12 --- /dev/null +++ b/mocks/router.go @@ -0,0 +1,95 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/Layr-Labs/eigenda-proxy/store (interfaces: IRouter) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + commitments "github.com/Layr-Labs/eigenda-proxy/commitments" + store "github.com/Layr-Labs/eigenda-proxy/store" + gomock "github.com/golang/mock/gomock" +) + +// MockIRouter is a mock of IRouter interface. +type MockIRouter struct { + ctrl *gomock.Controller + recorder *MockIRouterMockRecorder +} + +// MockIRouterMockRecorder is the mock recorder for MockIRouter. +type MockIRouterMockRecorder struct { + mock *MockIRouter +} + +// NewMockIRouter creates a new mock instance. +func NewMockIRouter(ctrl *gomock.Controller) *MockIRouter { + mock := &MockIRouter{ctrl: ctrl} + mock.recorder = &MockIRouterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIRouter) EXPECT() *MockIRouterMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockIRouter) Get(arg0 context.Context, arg1 []byte, arg2 commitments.CommitmentMode) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockIRouterMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockIRouter)(nil).Get), arg0, arg1, arg2) +} + +// GetEigenDAStore mocks base method. +func (m *MockIRouter) GetEigenDAStore() store.KeyGeneratedStore { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEigenDAStore") + ret0, _ := ret[0].(store.KeyGeneratedStore) + return ret0 +} + +// GetEigenDAStore indicates an expected call of GetEigenDAStore. +func (mr *MockIRouterMockRecorder) GetEigenDAStore() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEigenDAStore", reflect.TypeOf((*MockIRouter)(nil).GetEigenDAStore)) +} + +// GetS3Store mocks base method. +func (m *MockIRouter) GetS3Store() store.PrecomputedKeyStore { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetS3Store") + ret0, _ := ret[0].(store.PrecomputedKeyStore) + return ret0 +} + +// GetS3Store indicates an expected call of GetS3Store. +func (mr *MockIRouterMockRecorder) GetS3Store() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetS3Store", reflect.TypeOf((*MockIRouter)(nil).GetS3Store)) +} + +// Put mocks base method. +func (m *MockIRouter) Put(arg0 context.Context, arg1 commitments.CommitmentMode, arg2, arg3 []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Put", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Put indicates an expected call of Put. +func (mr *MockIRouterMockRecorder) Put(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockIRouter)(nil).Put), arg0, arg1, arg2, arg3) +} diff --git a/server/load_store.go b/server/load_store.go index 941b9c7..bc144f1 100644 --- a/server/load_store.go +++ b/server/load_store.go @@ -11,7 +11,7 @@ import ( ) // LoadStoreRouter ... creates storage backend clients and instruments them into a storage routing abstraction -func LoadStoreRouter(ctx context.Context, cfg CLIConfig, log log.Logger) (*store.Router, error) { +func LoadStoreRouter(ctx context.Context, cfg CLIConfig, log log.Logger) (store.IRouter, error) { // create S3 backend store (if enabled) var err error var s3 *store.S3Store diff --git a/server/server.go b/server/server.go index 6a1aa7f..e3cea9d 100644 --- a/server/server.go +++ b/server/server.go @@ -38,14 +38,14 @@ const ( type Server struct { log log.Logger endpoint string - router *store.Router + router store.IRouter m metrics.Metricer tls *rpc.ServerTLSConfig httpServer *http.Server listener net.Listener } -func NewServer(host string, port int, router *store.Router, log log.Logger, +func NewServer(host string, port int, router store.IRouter, log log.Logger, m metrics.Metricer) *Server { endpoint := net.JoinHostPort(host, strconv.Itoa(port)) return &Server{ @@ -266,7 +266,7 @@ func ReadCommitmentMode(r *http.Request) (commitments.CommitmentMode, error) { } commit := path.Base(r.URL.Path) - if len(commit) > 0 && commit != "put" { // provided commitment in request params + if len(commit) > 0 && commit != "put" { // provided commitment in request params (op keccak256) if !strings.HasPrefix(commit, "0x") { commit = "0x" + commit } @@ -276,6 +276,10 @@ func ReadCommitmentMode(r *http.Request) (commitments.CommitmentMode, error) { return commitments.SimpleCommitmentMode, err } + if len(decodedCommit) < 3 { + return commitments.SimpleCommitmentMode, fmt.Errorf("commitment is too short") + } + switch decodedCommit[0] { case byte(commitments.GenericCommitmentType): return commitments.OptimismAltDA, nil diff --git a/server/server_test.go b/server/server_test.go new file mode 100644 index 0000000..cdc6ddf --- /dev/null +++ b/server/server_test.go @@ -0,0 +1,248 @@ +package server + +import ( + "bytes" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Layr-Labs/eigenda-proxy/metrics" + "github.com/Layr-Labs/eigenda-proxy/mocks" + "github.com/ethereum/go-ethereum/log" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +const ( + genericPrefix = "\x00" + + // [alt-da, da layer, cert version] + opKeccakPrefix = "\x00" + opGenericPrefixStr = "\x01\x00\x00" + + testCommitStr = "9a7d4f1c3e5b8a09d1c0fa4b3f8e1d7c6b29f1e6d8c4a7b3c2d4e5f6a7b8c9d0" +) + +func TestGetHandler(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockRouter := mocks.NewMockIRouter(ctrl) + + server := NewServer("localhost", 8080, mockRouter, log.New(), metrics.NoopMetrics) + + tests := []struct { + name string + url string + mockBehavior func() + expectedCode int + expectedBody string + expectError bool + }{ + { + name: "Failure - Op Mode InvalidCommitmentKey", + url: "/get/0x", + mockBehavior: func() { + // Error is triggered before calling the router + }, + expectedCode: http.StatusBadRequest, + expectedBody: "", + expectError: true, + }, + { + name: "Failure - Op Mode InvalidCommitmentKey", + url: "/get/0x1", + mockBehavior: func() { + // Error is triggered before calling the router + }, + expectedCode: http.StatusBadRequest, + expectedBody: "", + expectError: true, + }, + { + name: "Failure - Op Mode InvalidCommitmentKey", + url: "/get/0x999", + mockBehavior: func() { + // Error is triggered before calling the router + }, + expectedCode: http.StatusBadRequest, + expectedBody: "", + expectError: true, + }, + { + name: "Failure - OP Keccak256 Internal Server Error", + url: fmt.Sprintf("/get/0x00%s", testCommitStr), + mockBehavior: func() { + mockRouter.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("internal error")) + }, + expectedCode: http.StatusInternalServerError, + expectedBody: "", + expectError: true, + }, + { + name: "Success - OP Keccak256", + url: fmt.Sprintf("/get/0x00%s", testCommitStr), + mockBehavior: func() { + mockRouter.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(testCommitStr), nil) + }, + expectedCode: http.StatusOK, + expectedBody: testCommitStr, + expectError: false, + }, + { + name: "Failure - OP Alt-DA Internal Server Error", + url: fmt.Sprintf("/get/0x010000%s", testCommitStr), + mockBehavior: func() { + mockRouter.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("internal error")) + }, + expectedCode: http.StatusInternalServerError, + expectedBody: "", + expectError: true, + }, + { + name: "Success - OP Alt-DA", + url: fmt.Sprintf("/get/0x010000%s", testCommitStr), + mockBehavior: func() { + mockRouter.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(testCommitStr), nil) + }, + expectedCode: http.StatusOK, + expectedBody: testCommitStr, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.mockBehavior() + + req := httptest.NewRequest(http.MethodGet, tt.url, nil) + rec := httptest.NewRecorder() + + err := server.HandleGet(rec, req) + if tt.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.expectedCode, rec.Code) + require.Equal(t, tt.expectedBody, rec.Body.String()) + }) + } +} + +func TestPutHandler(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockRouter := mocks.NewMockIRouter(ctrl) + server := NewServer("localhost", 8080, mockRouter, log.New(), metrics.NoopMetrics) + + tests := []struct { + name string + url string + body []byte + mockBehavior func() + expectedCode int + expectedBody string + expectError bool + }{ + { + name: "Failure OP Keccak256 - TooShortCommitmentKey", + url: "/put/0x", + body: []byte("some data"), + mockBehavior: func() { + // Error is triggered before calling the router + }, + expectedCode: http.StatusBadRequest, + expectedBody: "", + expectError: true, + }, + { + name: "Failure OP Keccak256 - TooShortCommitmentKey", + url: "/put/0x1", + body: []byte("some data"), + mockBehavior: func() { + // Error is triggered before calling the router + }, + expectedCode: http.StatusBadRequest, + expectedBody: "", + expectError: true, + }, + { + name: "Failure OP Keccak256 - InvalidCommitmentPrefixBytes", + url: fmt.Sprintf("/put/0x999%s", testCommitStr), + body: []byte("some data"), + mockBehavior: func() { + // Error is triggered before calling the router + }, + expectedCode: http.StatusBadRequest, + expectedBody: "", + expectError: true, + }, + { + name: "Failure OP Keccak256 - InternalServerError", + url: "/put/", + body: []byte("some data that will trigger an internal error"), + mockBehavior: func() { + mockRouter.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("internal error")) + }, + expectedCode: http.StatusInternalServerError, + expectedBody: "", + expectError: true, + }, + { + name: "Success OP Mode Alt-DA", + url: "/put/", + body: []byte("some data that will successfully be written to EigenDA"), + mockBehavior: func() { + mockRouter.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(testCommitStr), nil) + }, + expectedCode: http.StatusOK, + expectedBody: opGenericPrefixStr + testCommitStr, + expectError: false, + }, + { + name: "Success OP Mode Keccak256", + url: fmt.Sprintf("/put/0x00%s", testCommitStr), + body: []byte("some data that will successfully be written to EigenDA"), + mockBehavior: func() { + mockRouter.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(testCommitStr), nil) + }, + expectedCode: http.StatusOK, + expectedBody: opKeccakPrefix + testCommitStr, + expectError: false, + }, + { + name: "Success Simple Commitment Mode", + url: "/put/?commitment_mode=simple", + body: []byte("some data that will successfully be written to EigenDA"), + mockBehavior: func() { + mockRouter.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(testCommitStr), nil) + }, + expectedCode: http.StatusOK, + expectedBody: genericPrefix + testCommitStr, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.mockBehavior() + + req := httptest.NewRequest(http.MethodPut, tt.url, bytes.NewReader(tt.body)) + rec := httptest.NewRecorder() + + err := server.HandlePut(rec, req) + if tt.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.expectedCode, rec.Code) + if !tt.expectError { + require.Equal(t, []byte(tt.expectedBody), rec.Body.Bytes()) + } + }) + } +} diff --git a/store/memory.go b/store/memory.go index a368ec7..19d63c6 100644 --- a/store/memory.go +++ b/store/memory.go @@ -150,7 +150,7 @@ func (e *MemStore) Put(_ context.Context, value []byte) ([]byte, error) { mockBatchRoot := crypto.Keccak256Hash(entropy) blockNum, _ := rand.Int(rand.Reader, big.NewInt(1000)) - num := uint32(blockNum.Uint64()) + num := uint32(blockNum.Uint64()) // #nosec G115 cert := &verify.Certificate{ BlobHeader: &disperser.BlobHeader{ @@ -158,7 +158,7 @@ func (e *MemStore) Put(_ context.Context, value []byte) ([]byte, error) { X: commitment.X.Marshal(), Y: commitment.Y.Marshal(), }, - DataLength: uint32(len(encodedVal)), + DataLength: uint32(len(encodedVal)), // #nosec G115 BlobQuorumParams: []*disperser.BlobQuorumParam{ { QuorumNumber: 1, diff --git a/store/router.go b/store/router.go index 641bc08..fe3a4a9 100644 --- a/store/router.go +++ b/store/router.go @@ -1,3 +1,5 @@ +//go:generate mockgen -package mocks --destination ../mocks/router.go . IRouter + package store import ( @@ -11,6 +13,14 @@ import ( "github.com/ethereum/go-ethereum/log" ) +type IRouter interface { + Get(ctx context.Context, key []byte, cm commitments.CommitmentMode) ([]byte, error) + Put(ctx context.Context, cm commitments.CommitmentMode, key, value []byte) ([]byte, error) + + GetEigenDAStore() KeyGeneratedStore + GetS3Store() PrecomputedKeyStore +} + // Router ... storage backend routing layer type Router struct { log log.Logger @@ -25,7 +35,7 @@ type Router struct { } func NewRouter(eigenda KeyGeneratedStore, s3 PrecomputedKeyStore, l log.Logger, - caches []PrecomputedKeyStore, fallbacks []PrecomputedKeyStore) (*Router, error) { + caches []PrecomputedKeyStore, fallbacks []PrecomputedKeyStore) (IRouter, error) { return &Router{ log: l, eigenda: eigenda, @@ -109,9 +119,9 @@ func (r *Router) Put(ctx context.Context, cm commitments.CommitmentMode, key, va switch cm { case commitments.OptimismGeneric: // caching and fallbacks are unsupported for this commitment mode - return r.PutWithKey(ctx, key, value) + return r.putWithKey(ctx, key, value) case commitments.OptimismAltDA, commitments.SimpleCommitmentMode: - commit, err = r.PutWithoutKey(ctx, value) + commit, err = r.putWithoutKey(ctx, value) default: return nil, fmt.Errorf("unknown commitment mode") } @@ -200,8 +210,8 @@ func (r *Router) multiSourceRead(ctx context.Context, commitment []byte, fallbac return nil, errors.New("no data found in any redundant backend") } -// PutWithoutKey ... inserts a value into a storage backend that computes the key on-demand (i.e, EigenDA) -func (r *Router) PutWithoutKey(ctx context.Context, value []byte) ([]byte, error) { +// putWithoutKey ... inserts a value into a storage backend that computes the key on-demand (i.e, EigenDA) +func (r *Router) putWithoutKey(ctx context.Context, value []byte) ([]byte, error) { if r.eigenda != nil { r.log.Debug("Storing data to EigenDA backend") return r.eigenda.Put(ctx, value) @@ -210,8 +220,8 @@ func (r *Router) PutWithoutKey(ctx context.Context, value []byte) ([]byte, error return nil, errors.New("no DA storage backend found") } -// PutWithKey ... only supported for S3 storage backends using OP's alt-da keccak256 commitment type -func (r *Router) PutWithKey(ctx context.Context, key []byte, value []byte) ([]byte, error) { +// putWithKey ... only supported for S3 storage backends using OP's alt-da keccak256 commitment type +func (r *Router) putWithKey(ctx context.Context, key []byte, value []byte) ([]byte, error) { if r.s3 == nil { return nil, errors.New("S3 is disabled but is only supported for posting known commitment keys") } diff --git a/verify/cert.go b/verify/cert.go index 3386747..94a2e88 100644 --- a/verify/cert.go +++ b/verify/cert.go @@ -115,7 +115,7 @@ func (cv *CertVerifier) getContextBlock() (*big.Int, error) { } blockNumber = new(big.Int) - blockNumber.Sub(blockHeader.Number(), big.NewInt(int64(cv.ethConfirmationDepth-1))) + blockNumber.Sub(blockHeader.Number(), big.NewInt(int64(cv.ethConfirmationDepth-1))) // #nosec G115 return blockNumber, nil } diff --git a/verify/certificate.go b/verify/certificate.go index ef2f09c..9defd89 100644 --- a/verify/certificate.go +++ b/verify/certificate.go @@ -43,9 +43,9 @@ func (c *Certificate) ReadBlobHeader() BlobHeader { qps := make([]QuorumBlobParam, len(c.BlobHeader.BlobQuorumParams)) for i, qp := range c.BlobHeader.BlobQuorumParams { qps[i] = QuorumBlobParam{ - QuorumNumber: uint8(qp.QuorumNumber), - AdversaryThresholdPercentage: uint8(qp.AdversaryThresholdPercentage), - ConfirmationThresholdPercentage: uint8(qp.ConfirmationThresholdPercentage), + QuorumNumber: uint8(qp.QuorumNumber), // #nosec G115 + AdversaryThresholdPercentage: uint8(qp.AdversaryThresholdPercentage), // #nosec G115 + ConfirmationThresholdPercentage: uint8(qp.ConfirmationThresholdPercentage), // #nosec G115 ChunkLength: qp.ChunkLength, } }