diff --git a/tests/integration/audit/evidence_determinism_test.go b/tests/integration/audit/evidence_determinism_test.go new file mode 100644 index 00000000..0412c2a5 --- /dev/null +++ b/tests/integration/audit/evidence_determinism_test.go @@ -0,0 +1,93 @@ +package audit_test + +import ( + "bytes" + "fmt" + "testing" + + "cosmossdk.io/core/address" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + auditkeeper "github.com/LumeraProtocol/lumera/x/audit/v1/keeper" + auditmodule "github.com/LumeraProtocol/lumera/x/audit/v1/module" + audittypes "github.com/LumeraProtocol/lumera/x/audit/v1/types" + supernodemocks "github.com/LumeraProtocol/lumera/x/supernode/v1/mocks" +) + +type integrationFixture struct { + ctx sdk.Context + keeper auditkeeper.Keeper + addressCodec address.Codec +} + +func initIntegrationFixture(t *testing.T) *integrationFixture { + t.Helper() + + ctrl := gomock.NewController(t) + t.Cleanup(ctrl.Finish) + + encCfg := moduletestutil.MakeTestEncodingConfig(auditmodule.AppModuleBasic{}) + addressCodec := addresscodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()) + storeKey := storetypes.NewKVStoreKey(audittypes.StoreKey) + storeService := runtime.NewKVStoreService(storeKey) + ctx := testutil.DefaultContextWithDB(t, storeKey, storetypes.NewTransientStoreKey("transient_test")).Ctx + authority := authtypes.NewModuleAddress(govtypes.ModuleName) + snKeeper := supernodemocks.NewMockSupernodeKeeper(ctrl) + + k := auditkeeper.NewKeeper( + encCfg.Codec, + addressCodec, + storeService, + log.NewNopLogger(), + authority, + snKeeper, + ) + require.NoError(t, k.SetParams(ctx, audittypes.DefaultParams())) + + return &integrationFixture{ctx: ctx, keeper: k, addressCodec: addressCodec} +} + +func TestSubmitEvidence_CascadeClientFailure_DeterministicMetadataBytes(t *testing.T) { + f := initIntegrationFixture(t) + + reporter, err := f.addressCodec.BytesToString(bytes.Repeat([]byte{0x11}, 20)) + require.NoError(t, err) + subject, err := f.addressCodec.BytesToString(bytes.Repeat([]byte{0x22}, 20)) + require.NoError(t, err) + + jsonVariants := []string{ + `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"action_id":"123637","error":"download failed: insufficient symbols","iteration":"1","operation":"download","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","supernode_endpoint":"18.190.53.108:4444","task_id":"9700ec8a"}}`, + `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"task_id":"9700ec8a","supernode_endpoint":"18.190.53.108:4444","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","operation":"download","iteration":"1","error":"download failed: insufficient symbols","action_id":"123637"}}`, + } + + var first []byte + for i, metaJSON := range jsonVariants { + evidenceID, err := f.keeper.CreateEvidence( + f.ctx, + reporter, + subject, + fmt.Sprintf("action-%d", i), + audittypes.EvidenceType_EVIDENCE_TYPE_CASCADE_CLIENT_FAILURE, + metaJSON, + ) + require.NoError(t, err) + + ev, found := f.keeper.GetEvidence(f.ctx, evidenceID) + require.True(t, found) + if i == 0 { + first = append([]byte(nil), ev.Metadata...) + continue + } + require.Equal(t, first, ev.Metadata) + } +} diff --git a/tests/integration/supernode/metrics_determinism_test.go b/tests/integration/supernode/metrics_determinism_test.go new file mode 100644 index 00000000..3d1f3bca --- /dev/null +++ b/tests/integration/supernode/metrics_determinism_test.go @@ -0,0 +1,38 @@ +package integration_test + +import ( + "testing" + + gogoproto "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/require" + + sntypes "github.com/LumeraProtocol/lumera/x/supernode/v1/types" +) + +func TestMetricsAggregateMarshalDeterministicAcrossMapInsertionOrder(t *testing.T) { + m1 := &sntypes.MetricsAggregate{ + Metrics: map[string]float64{ + "cpu_usage": 85.5, + "mem_usage": 63.2, + "disk_usage": 71.9, + }, + ReportCount: 10, + Height: 12345, + } + + m2 := &sntypes.MetricsAggregate{ + Metrics: map[string]float64{ + "disk_usage": 71.9, + "mem_usage": 63.2, + "cpu_usage": 85.5, + }, + ReportCount: 10, + Height: 12345, + } + + b1, err := gogoproto.Marshal(m1) + require.NoError(t, err) + b2, err := gogoproto.Marshal(m2) + require.NoError(t, err) + require.Equal(t, b1, b2) +} diff --git a/tests/systemtests/audit_evidence_determinism_system_test.go b/tests/systemtests/audit_evidence_determinism_system_test.go new file mode 100644 index 00000000..fd3ec79d --- /dev/null +++ b/tests/systemtests/audit_evidence_determinism_system_test.go @@ -0,0 +1,191 @@ +//go:build system_test + +package system + +import ( + "context" + "fmt" + "testing" + + client "github.com/cometbft/cometbft/rpc/client/http" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" +) + +func submitCascadeClientFailureEvidence(t *testing.T, cli LumeradCli, fromNode, subjectAddr, actionID, metadataJSON string) string { + t.Helper() + tx := cli.CustomCommand( + "tx", "audit", "submit-evidence", + subjectAddr, + "cascade-client-failure", + actionID, + metadataJSON, + "--from", fromNode, + ) + RequireTxSuccess(t, tx) + return tx +} + +func latestHeightAndAppHashAtHeight(t *testing.T, rpcAddr string, height int64) (int64, string) { + t.Helper() + httpClient, err := client.New(rpcAddr, "/websocket") + require.NoError(t, err) + require.NoError(t, httpClient.Start()) + defer func() { _ = httpClient.Stop() }() + + status, err := httpClient.Status(context.Background()) + require.NoError(t, err) + latest := status.SyncInfo.LatestBlockHeight + + res, err := httpClient.Block(context.Background(), &height) + require.NoError(t, err) + return latest, fmt.Sprintf("%X", res.Block.Header.AppHash) +} + +func assertChainProgressAndSingleAppHash(t *testing.T, blocks int) { + t.Helper() + nodes := sut.AllNodes(t) + require.NotEmpty(t, nodes) + + lastMinHeight := int64(0) + for i := 0; i < blocks; i++ { + minHeight := int64(1<<62 - 1) + for _, n := range nodes { + rpc := fmt.Sprintf("tcp://localhost:%d", n.RPCPort) + h, _ := latestHeightAndAppHashAtHeight(t, rpc, 1) + if h < minHeight { + minHeight = h + } + } + require.Greater(t, minHeight, int64(0)) + + var expectedHash string + for _, n := range nodes { + rpc := fmt.Sprintf("tcp://localhost:%d", n.RPCPort) + _, hash := latestHeightAndAppHashAtHeight(t, rpc, minHeight) + if expectedHash == "" { + expectedHash = hash + continue + } + require.Equal(t, expectedHash, hash, "app hash mismatch at height %d", minHeight) + } + + if i > 0 { + require.GreaterOrEqual(t, minHeight, lastMinHeight) + } + lastMinHeight = minHeight + sut.AwaitNextBlock(t) + } + require.Greater(t, lastMinHeight, int64(1), "chain did not progress") +} + +func queryEvidenceMetadataBase64ByAction(t *testing.T, cli LumeradCli, actionID string) string { + t.Helper() + out := cli.CustomQuery("q", "audit", "evidence-by-action", actionID) + meta := gjson.Get(out, "evidence.0.metadata") + if !meta.Exists() { + meta = gjson.Get(out, "evidences.0.metadata") + } + require.True(t, meta.Exists(), "missing metadata in response: %s", out) + return meta.String() +} + +func bootFreshChain(t *testing.T) { + t.Helper() + sut.ResetChain(t) + sut.StartChain(t) + t.Cleanup(func() { sut.StopChain() }) +} + +func TestAuditEvidenceDeterminism_A_ChainProgressSingleAppHash(t *testing.T) { + bootFreshChain(t) + cli := NewLumeradCLI(t, sut, true) + n0 := getNodeIdentity(t, cli, "node0") + n1 := getNodeIdentity(t, cli, "node1") + + metadata := `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"action_id":"123637","error":"download failed: insufficient symbols","iteration":"1","operation":"download","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","supernode_endpoint":"18.190.53.108:4444","task_id":"9700ec8a"}}` + submitCascadeClientFailureEvidence(t, *cli, n0.nodeName, n1.accAddr, "sys-a-1", metadata) + assertChainProgressAndSingleAppHash(t, 8) +} + +func TestAuditEvidenceDeterminism_B_JSONPermutationStableMetadata(t *testing.T) { + bootFreshChain(t) + cli := NewLumeradCLI(t, sut, true) + n0 := getNodeIdentity(t, cli, "node0") + n1 := getNodeIdentity(t, cli, "node1") + + meta1 := `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"action_id":"123637","error":"download failed: insufficient symbols","iteration":"1","operation":"download","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","supernode_endpoint":"18.190.53.108:4444","task_id":"9700ec8a"}}` + meta2 := `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"task_id":"9700ec8a","supernode_endpoint":"18.190.53.108:4444","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","operation":"download","iteration":"1","error":"download failed: insufficient symbols","action_id":"123637"}}` + + submitCascadeClientFailureEvidence(t, *cli, n0.nodeName, n1.accAddr, "sys-b-1", meta1) + submitCascadeClientFailureEvidence(t, *cli, n0.nodeName, n1.accAddr, "sys-b-2", meta2) + sut.AwaitNextBlock(t) + + m1 := queryEvidenceMetadataBase64ByAction(t, *cli, "sys-b-1") + m2 := queryEvidenceMetadataBase64ByAction(t, *cli, "sys-b-2") + require.Equal(t, m1, m2) + assertChainProgressAndSingleAppHash(t, 6) +} + +func TestAuditEvidenceDeterminism_C_ReplayAfterHeightTransition(t *testing.T) { + bootFreshChain(t) + cli := NewLumeradCLI(t, sut, true) + n0 := getNodeIdentity(t, cli, "node0") + n1 := getNodeIdentity(t, cli, "node1") + metadata := `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"action_id":"123637","error":"download failed: insufficient symbols","iteration":"1","operation":"download","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","supernode_endpoint":"18.190.53.108:4444","task_id":"9700ec8a"}}` + + submitCascadeClientFailureEvidence(t, *cli, n0.nodeName, n1.accAddr, "sys-c-1", metadata) + assertChainProgressAndSingleAppHash(t, 4) + + // High-level replay across further block transitions. + targetHeight := sut.AwaitNextBlock(t) + 8 + sut.AwaitBlockHeight(t, targetHeight) + + submitCascadeClientFailureEvidence(t, *cli, n0.nodeName, n1.accAddr, "sys-c-2", metadata) + assertChainProgressAndSingleAppHash(t, 8) +} + +func TestAuditEvidenceDeterminism_D_RestartKeepsDeterministicState(t *testing.T) { + bootFreshChain(t) + cli := NewLumeradCLI(t, sut, true) + n0 := getNodeIdentity(t, cli, "node0") + n1 := getNodeIdentity(t, cli, "node1") + metadata := `{"reporter_component":2,"target_supernode_accounts":["lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6"],"details":{"action_id":"123637","error":"download failed: insufficient symbols","iteration":"1","operation":"download","supernode_account":"lumera1mfldjaqc7ec5rlh4k58yttv3cd978gzl070zk6","supernode_endpoint":"18.190.53.108:4444","task_id":"9700ec8a"}}` + + submitCascadeClientFailureEvidence(t, *cli, n0.nodeName, n1.accAddr, "sys-d-1", metadata) + h := sut.AwaitNextBlock(t) + nodes := sut.AllNodes(t) + require.NotEmpty(t, nodes) + _, beforeHash := latestHeightAndAppHashAtHeight(t, fmt.Sprintf("tcp://localhost:%d", nodes[0].RPCPort), h) + + // restart full validator set without reset and verify deterministic state remains consistent. + sut.StopChain() + sut.StartChain(t) + sut.AwaitNodeUp(t, "tcp://localhost:26657") + sut.AwaitBlockHeight(t, h+3) + + for _, n := range sut.AllNodes(t) { + _, got := latestHeightAndAppHashAtHeight(t, fmt.Sprintf("tcp://localhost:%d", n.RPCPort), h) + require.Equal(t, beforeHash, got) + } + assertChainProgressAndSingleAppHash(t, 6) +} + +func TestAuditEvidenceDeterminism_E_ReservedEvidenceTypeRejectedChainContinues(t *testing.T) { + bootFreshChain(t) + cli := NewLumeradCLI(t, sut, true) + n0 := getNodeIdentity(t, cli, "node0") + n1 := getNodeIdentity(t, cli, "node1") + + // ACTION_EXPIRED is reserved for action module; direct msg submission must fail. + resp := cli.CustomCommand( + "tx", "audit", "submit-evidence", + n1.accAddr, + "action-expired", + "sys-e-1", + `{"top_10_validator_addresses":[]}`, + "--from", n0.nodeName, + ) + RequireTxFailure(t, resp, "reserved for the action module") + assertChainProgressAndSingleAppHash(t, 6) +} diff --git a/x/audit/v1/keeper/evidence.go b/x/audit/v1/keeper/evidence.go index 5e75fa53..ab1583e0 100644 --- a/x/audit/v1/keeper/evidence.go +++ b/x/audit/v1/keeper/evidence.go @@ -3,6 +3,7 @@ package keeper import ( "context" "fmt" + "sort" "strings" errorsmod "cosmossdk.io/errors" @@ -11,6 +12,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/gogoproto/jsonpb" gogoproto "github.com/cosmos/gogoproto/proto" + "google.golang.org/protobuf/encoding/protowire" ) const ( @@ -171,6 +173,45 @@ func (k Keeper) CreateEvidence( return evidenceID, nil } +func marshalEvidenceMetadataDeterministic(msg gogoproto.Message) ([]byte, error) { + return gogoproto.Marshal(msg) +} + +func marshalCascadeClientFailureEvidenceMetadataDeterministic(m *types.CascadeClientFailureEvidenceMetadata) []byte { + out := make([]byte, 0, 256) + + if m.ReporterComponent != types.CascadeClientFailureReporterComponent_CASCADE_CLIENT_FAILURE_REPORTER_COMPONENT_UNSPECIFIED { + out = protowire.AppendTag(out, 1, protowire.VarintType) + out = protowire.AppendVarint(out, uint64(m.ReporterComponent)) + } + + for _, acct := range m.TargetSupernodeAccounts { + out = protowire.AppendTag(out, 2, protowire.BytesType) + out = protowire.AppendString(out, acct) + } + + if len(m.Details) > 0 { + keys := make([]string, 0, len(m.Details)) + for k := range m.Details { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + entry := make([]byte, 0, len(k)+len(m.Details[k])+8) + entry = protowire.AppendTag(entry, 1, protowire.BytesType) + entry = protowire.AppendString(entry, k) + entry = protowire.AppendTag(entry, 2, protowire.BytesType) + entry = protowire.AppendString(entry, m.Details[k]) + + out = protowire.AppendTag(out, 3, protowire.BytesType) + out = protowire.AppendBytes(out, entry) + } + } + + return out +} + func marshalEvidenceMetadataJSON(evidenceType types.EvidenceType, metadataJSON string) ([]byte, error) { u := &jsonpb.Unmarshaler{} @@ -180,35 +221,35 @@ func marshalEvidenceMetadataJSON(evidenceType types.EvidenceType, metadataJSON s if err := u.Unmarshal(strings.NewReader(metadataJSON), &m); err != nil { return nil, fmt.Errorf("unmarshal ActionExpiredEvidenceMetadata: %w", err) } - return gogoproto.Marshal(&m) + return marshalEvidenceMetadataDeterministic(&m) case types.EvidenceType_EVIDENCE_TYPE_ACTION_FINALIZATION_SIGNATURE_FAILURE: var m types.ActionFinalizationSignatureFailureEvidenceMetadata if err := u.Unmarshal(strings.NewReader(metadataJSON), &m); err != nil { return nil, fmt.Errorf("unmarshal ActionFinalizationSignatureFailureEvidenceMetadata: %w", err) } - return gogoproto.Marshal(&m) + return marshalEvidenceMetadataDeterministic(&m) case types.EvidenceType_EVIDENCE_TYPE_ACTION_FINALIZATION_NOT_IN_TOP_10: var m types.ActionFinalizationNotInTop10EvidenceMetadata if err := u.Unmarshal(strings.NewReader(metadataJSON), &m); err != nil { return nil, fmt.Errorf("unmarshal ActionFinalizationNotInTop10EvidenceMetadata: %w", err) } - return gogoproto.Marshal(&m) + return marshalEvidenceMetadataDeterministic(&m) case types.EvidenceType_EVIDENCE_TYPE_STORAGE_CHALLENGE_FAILURE: var m types.StorageChallengeFailureEvidenceMetadata if err := u.Unmarshal(strings.NewReader(metadataJSON), &m); err != nil { return nil, fmt.Errorf("unmarshal StorageChallengeFailureEvidenceMetadata: %w", err) } - return gogoproto.Marshal(&m) + return marshalEvidenceMetadataDeterministic(&m) case types.EvidenceType_EVIDENCE_TYPE_CASCADE_CLIENT_FAILURE: var m types.CascadeClientFailureEvidenceMetadata if err := u.Unmarshal(strings.NewReader(metadataJSON), &m); err != nil { return nil, fmt.Errorf("unmarshal CascadeClientFailureEvidenceMetadata: %w", err) } - return gogoproto.Marshal(&m) + return marshalCascadeClientFailureEvidenceMetadataDeterministic(&m), nil default: return nil, fmt.Errorf("unsupported evidence_type: %s", evidenceType.String()) diff --git a/x/audit/v1/keeper/evidence_test.go b/x/audit/v1/keeper/evidence_test.go index dbe86fd3..abf3582b 100644 --- a/x/audit/v1/keeper/evidence_test.go +++ b/x/audit/v1/keeper/evidence_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "bytes" "encoding/json" + "fmt" "testing" "github.com/LumeraProtocol/lumera/x/audit/v1/keeper" @@ -153,3 +154,52 @@ func TestCreateEvidence_CascadeClientFailure_InvalidMetadata(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), types.ErrInvalidMetadata.Error()) } + +func TestCreateEvidence_CascadeClientFailure_MetadataMarshalDeterministic(t *testing.T) { + f := initFixture(t) + + reporter, err := f.addressCodec.BytesToString(bytes.Repeat([]byte{0x11}, 20)) + require.NoError(t, err) + subject, err := f.addressCodec.BytesToString(bytes.Repeat([]byte{0x22}, 20)) + require.NoError(t, err) + target, err := f.addressCodec.BytesToString(bytes.Repeat([]byte{0x33}, 20)) + require.NoError(t, err) + + meta := types.CascadeClientFailureEvidenceMetadata{ + ReporterComponent: types.CascadeClientFailureReporterComponent_CASCADE_CLIENT_FAILURE_REPORTER_COMPONENT_SN_API_SERVER, + TargetSupernodeAccounts: []string{target}, + Details: map[string]string{ + "action_id": "123637", + "error": "decode symbols using RaptorQ: insufficient symbols to attempt decode", + "iteration": "1", + "operation": "download", + "supernode_account": target, + "supernode_endpoint": "18.190.53.108:4444", + "task_id": "9700ec8a", + }, + } + metaJSON, err := json.Marshal(meta) + require.NoError(t, err) + + var first []byte + for i := 0; i < 40; i++ { + actionID := fmt.Sprintf("action-cascade-determinism-%d", i) + evidenceID, err := f.keeper.CreateEvidence( + f.ctx, + reporter, + subject, + actionID, + types.EvidenceType_EVIDENCE_TYPE_CASCADE_CLIENT_FAILURE, + string(metaJSON), + ) + require.NoError(t, err) + + got, found := f.keeper.GetEvidence(f.ctx, evidenceID) + require.True(t, found) + if i == 0 { + first = append([]byte(nil), got.Metadata...) + continue + } + require.Equal(t, first, got.Metadata) + } +} diff --git a/x/audit/v1/types/evidence_metadata.pb.go b/x/audit/v1/types/evidence_metadata.pb.go index 3b5c0492..084a672a 100644 --- a/x/audit/v1/types/evidence_metadata.pb.go +++ b/x/audit/v1/types/evidence_metadata.pb.go @@ -10,6 +10,7 @@ import ( io "io" math "math" math_bits "math/bits" + sort "sort" ) // Reference imports to suppress errors if they are not otherwise used. @@ -307,7 +308,7 @@ func (m *StorageChallengeFailureEvidenceMetadata) GetTranscriptHash() string { type CascadeClientFailureEvidenceMetadata struct { // reporter_component identifies the emitting component. ReporterComponent CascadeClientFailureReporterComponent `protobuf:"varint,1,opt,name=reporter_component,json=reporterComponent,proto3,enum=lumera.audit.v1.CascadeClientFailureReporterComponent" json:"reporter_component,omitempty"` - // target_supernode_accounts are implicated supernode accounts, when known. + // target_supernode_accounts are implicated supernode accounts TargetSupernodeAccounts []string `protobuf:"bytes,2,rep,name=target_supernode_accounts,json=targetSupernodeAccounts,proto3" json:"target_supernode_accounts,omitempty"` // details contains free-form diagnostic attributes (e.g. trace, endpoint, error). Details map[string]string `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -323,16 +324,12 @@ func (m *CascadeClientFailureEvidenceMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *CascadeClientFailureEvidenceMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CascadeClientFailureEvidenceMetadata.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } + return b[:n], nil } func (m *CascadeClientFailureEvidenceMetadata) XXX_Merge(src proto.Message) { xxx_messageInfo_CascadeClientFailureEvidenceMetadata.Merge(m, src) @@ -616,7 +613,13 @@ func (m *CascadeClientFailureEvidenceMetadata) MarshalToSizedBuffer(dAtA []byte) var l int _ = l if len(m.Details) > 0 { + keysForDetails := make([]string, 0, len(m.Details)) for k := range m.Details { + keysForDetails = append(keysForDetails, k) + } + sort.Strings(keysForDetails) + for iNdEx := len(keysForDetails) - 1; iNdEx >= 0; iNdEx-- { + k := keysForDetails[iNdEx] v := m.Details[k] baseI := i i -= len(v) diff --git a/x/supernode/v1/types/metrics_aggregate.pb.go b/x/supernode/v1/types/metrics_aggregate.pb.go index 7175067a..c10e7490 100644 --- a/x/supernode/v1/types/metrics_aggregate.pb.go +++ b/x/supernode/v1/types/metrics_aggregate.pb.go @@ -12,6 +12,7 @@ import ( io "io" math "math" math_bits "math/bits" + sort "sort" ) // Reference imports to suppress errors if they are not otherwise used. @@ -41,16 +42,12 @@ func (m *MetricsAggregate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *MetricsAggregate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MetricsAggregate.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } + return b[:n], nil } func (m *MetricsAggregate) XXX_Merge(src proto.Message) { xxx_messageInfo_MetricsAggregate.Merge(m, src) @@ -147,7 +144,13 @@ func (m *MetricsAggregate) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x10 } if len(m.Metrics) > 0 { + keysForMetrics := make([]string, 0, len(m.Metrics)) for k := range m.Metrics { + keysForMetrics = append(keysForMetrics, k) + } + sort.Strings(keysForMetrics) + for iNdEx := len(keysForMetrics) - 1; iNdEx >= 0; iNdEx-- { + k := keysForMetrics[iNdEx] v := m.Metrics[k] baseI := i i -= 8