diff --git a/CHANGELOG.md b/CHANGELOG.md index 22ada0208b8..1308231ef50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ - [11301](https://github.com/vegaprotocol/vega/issues/11301) - Sum vesting reward bonus multiplier across all derived keys for party. - [11329](https://github.com/vegaprotocol/vega/issues/11329) - Add source chain ID when available to transaction event attributes - [10634](https://github.com/vegaprotocol/vega/issues/10634) - Add spam checks for orders/liquidity provision -- [11337](https://github.com/vegaprotocol/vega/issues/11337) - Enhance transaction reordering with per market control and configurable delay. +- [11337](https://github.com/vegaprotocol/vega/issues/11337) - Enhance transaction reordering with per market control and configurable delay. - [11344](https://github.com/vegaprotocol/vega/issues/11344) - Include derived parties in accounts API - [11268](https://github.com/vegaprotocol/vega/issues/11268) - Include derived parties vesting stats API - [11266](https://github.com/vegaprotocol/vega/issues/11266) - Include derived parties rewards API @@ -36,6 +36,8 @@ - [11297](https://github.com/vegaprotocol/vega/issues/11297) - Handle properly asset decimals < market decimals when uncrossing the order book upon leaving auction. - [11304](https://github.com/vegaprotocol/vega/issues/11304) - Correctly verify pegged order offset with respect to tick size in the right units. - [11319](https://github.com/vegaprotocol/vega/issues/11319) - Do not leak Ethereum client secrets in the logs. +- [11336](https://github.com/vegaprotocol/vega/issues/11336) - Add support for decay factor in governance recurring transfers and report the proposal amount rather than 0 when the proposal gets enacted. +- [11368](https://github.com/vegaprotocol/vega/issues/11368) - Add support for update vesting stats in REST API and fix summing the quantum balance for vesting stats. ## 0.76.1 diff --git a/core/events/party.go b/core/events/party.go index 43e12b2183e..5bb852503d1 100644 --- a/core/events/party.go +++ b/core/events/party.go @@ -23,6 +23,7 @@ import ( vegapb "code.vegaprotocol.io/vega/protos/vega" eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" + "golang.org/x/exp/maps" "golang.org/x/exp/slices" ) @@ -98,13 +99,25 @@ func NewPartyProfileUpdatedEvent(ctx context.Context, p *types.PartyProfile) *Pa return strings.Compare(a.Key, b.Key) }) + // Ensure deterministic order in event. + slices.SortStableFunc(metadata, func(a, b *vegapb.Metadata) int { + return strings.Compare(a.Key, b.Key) + }) + + derivedKeys := maps.Keys(p.DerivedKeys) + + slices.SortStableFunc(derivedKeys, func(a, b string) int { + return strings.Compare(a, b) + }) + return &PartyProfileUpdated{ Base: newBase(ctx, PartyProfileUpdatedEvent), e: eventspb.PartyProfileUpdated{ UpdatedProfile: &vegapb.PartyProfile{ - PartyId: p.PartyID.String(), - Alias: p.Alias, - Metadata: metadata, + PartyId: p.PartyID.String(), + Alias: p.Alias, + Metadata: metadata, + DerivedKeys: derivedKeys, }, }, } diff --git a/core/execution/amm/engine.go b/core/execution/amm/engine.go index ecc244b8346..d69d0f0134b 100644 --- a/core/execution/amm/engine.go +++ b/core/execution/amm/engine.go @@ -697,7 +697,7 @@ func (e *Engine) Confirm( e.add(pool) e.sendUpdate(ctx, pool) - e.parties.AssignDeriveKey(types.PartyID(pool.owner), pool.AMMParty) + e.parties.AssignDeriveKey(ctx, types.PartyID(pool.owner), pool.AMMParty) } // Amend takes the details of an amendment to an AMM and returns a copy of that pool with the updated curves along with the current pool. diff --git a/core/execution/amm/engine_test.go b/core/execution/amm/engine_test.go index 1a162e38f61..a2f0095d3d2 100644 --- a/core/execution/amm/engine_test.go +++ b/core/execution/amm/engine_test.go @@ -728,7 +728,7 @@ func getTestEngine(t *testing.T) *tstEngine { mat := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) parties := cmocks.NewMockParties(ctrl) - parties.EXPECT().AssignDeriveKey(gomock.Any(), gomock.Any()).AnyTimes() + parties.EXPECT().AssignDeriveKey(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() eng := New(logging.NewTestLogger(), broker, col, marketID, assetID, pos, num.DecimalOne(), num.DecimalOne(), mat, parties) diff --git a/core/execution/common/interfaces.go b/core/execution/common/interfaces.go index fbffc5e98b1..6a5f21c9124 100644 --- a/core/execution/common/interfaces.go +++ b/core/execution/common/interfaces.go @@ -242,7 +242,7 @@ type Banking interface { } type Parties interface { - AssignDeriveKey(party types.PartyID, derivedKey string) + AssignDeriveKey(ctx context.Context, party types.PartyID, derivedKey string) } type LiquidityEngine interface { diff --git a/core/execution/common/mocks/mocks.go b/core/execution/common/mocks/mocks.go index a7a7c490ec6..9654e22e095 100644 --- a/core/execution/common/mocks/mocks.go +++ b/core/execution/common/mocks/mocks.go @@ -2681,15 +2681,15 @@ func (m *MockParties) EXPECT() *MockPartiesMockRecorder { } // AssignDeriveKey mocks base method. -func (m *MockParties) AssignDeriveKey(arg0 types.PartyID, arg1 string) { +func (m *MockParties) AssignDeriveKey(arg0 context.Context, arg1 types.PartyID, arg2 string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AssignDeriveKey", arg0, arg1) + m.ctrl.Call(m, "AssignDeriveKey", arg0, arg1, arg2) } // AssignDeriveKey indicates an expected call of AssignDeriveKey. -func (mr *MockPartiesMockRecorder) AssignDeriveKey(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockPartiesMockRecorder) AssignDeriveKey(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignDeriveKey", reflect.TypeOf((*MockParties)(nil).AssignDeriveKey), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignDeriveKey", reflect.TypeOf((*MockParties)(nil).AssignDeriveKey), arg0, arg1, arg2) } // MockDelayTransactionsTarget is a mock of DelayTransactionsTarget interface. diff --git a/core/parties/engine.go b/core/parties/engine.go index 67b292c0357..7ae5f6f2175 100644 --- a/core/parties/engine.go +++ b/core/parties/engine.go @@ -47,7 +47,7 @@ func (e *Engine) OnMinBalanceForUpdatePartyProfileUpdated(_ context.Context, min return nil } -func (e *Engine) AssignDeriveKey(party types.PartyID, derivedKey string) { +func (e *Engine) AssignDeriveKey(ctx context.Context, party types.PartyID, derivedKey string) { if _, ok := e.profiles[party]; !ok { e.profiles[party] = &types.PartyProfile{ PartyID: party, diff --git a/core/parties/engine_test.go b/core/parties/engine_test.go index b7b995cdf09..1d0326ddb7b 100644 --- a/core/parties/engine_test.go +++ b/core/parties/engine_test.go @@ -114,8 +114,8 @@ func TestAssigningDerivedKeys(t *testing.T) { require.False(t, te.engine.CheckDerivedKeyOwnership(party1, "derivedKey1")) // Assigning derived keys create profile if it doesn't exist - te.engine.AssignDeriveKey(party1, "derivedKey1") - te.engine.AssignDeriveKey(party1, "derivedKey2") + te.engine.AssignDeriveKey(ctx, party1, "derivedKey1") + te.engine.AssignDeriveKey(ctx, party1, "derivedKey2") require.True(t, te.engine.CheckDerivedKeyOwnership(party1, "derivedKey1")) require.True(t, te.engine.CheckDerivedKeyOwnership(party1, "derivedKey2")) @@ -162,7 +162,7 @@ func TestAssigningDerivedKeys(t *testing.T) { })) // Assign key for party 2 - te.engine.AssignDeriveKey(party2, "derivedKey3") + te.engine.AssignDeriveKey(ctx, party2, "derivedKey3") // Attempt using alias from party 2. require.Error(t, te.engine.UpdateProfile(ctx, party1, &commandspb.UpdatePartyProfile{ diff --git a/core/vesting/engine.go b/core/vesting/engine.go index f6918bcaaae..fd5cdd34ad3 100644 --- a/core/vesting/engine.go +++ b/core/vesting/engine.go @@ -151,6 +151,7 @@ func (e *Engine) OnRewardVestingMinimumTransferUpdate(_ context.Context, minimum func (e *Engine) OnEpochEvent(ctx context.Context, epoch types.Epoch) { if epoch.Action == proto.EpochAction_EPOCH_ACTION_END { + e.clearMultiplierCache() e.moveLocked() e.distributeVested(ctx) e.broadcastVestingStatsUpdate(ctx, epoch.Seq) @@ -212,9 +213,6 @@ func (e *Engine) GetSingleAndSummedRewardBonusMultipliers(party string) (Multipl single, foundSingle := e.rewardBonusMultiplierCache[key] if !foundSingle { quantumBalanceForKey := e.c.GetAllVestingQuantumBalance(key) - if quantumBalanceForKey.IsZero() { - continue - } single.QuantumBalance = quantumBalanceForKey single.Multiplier = e.rewardBonusMultiplier(quantumBalanceForKey) diff --git a/protos/data-node/api/v2/trading_data.pb.gw.go b/protos/data-node/api/v2/trading_data.pb.gw.go index 76ec0300b0f..031bb0b0e4e 100644 --- a/protos/data-node/api/v2/trading_data.pb.gw.go +++ b/protos/data-node/api/v2/trading_data.pb.gw.go @@ -4291,6 +4291,58 @@ func local_request_TradingDataService_GetVolumeDiscountStats_0(ctx context.Conte } +func request_TradingDataService_GetPartyVestingStats_0(ctx context.Context, marshaler runtime.Marshaler, client TradingDataServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetPartyVestingStatsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["party_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "party_id") + } + + protoReq.PartyId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "party_id", err) + } + + msg, err := client.GetPartyVestingStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TradingDataService_GetPartyVestingStats_0(ctx context.Context, marshaler runtime.Marshaler, server TradingDataServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetPartyVestingStatsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["party_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "party_id") + } + + protoReq.PartyId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "party_id", err) + } + + msg, err := server.GetPartyVestingStats(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_TradingDataService_ObserveTransactionResults_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -6836,6 +6888,29 @@ func RegisterTradingDataServiceHandlerServer(ctx context.Context, mux *runtime.S }) + mux.Handle("GET", pattern_TradingDataService_GetPartyVestingStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/datanode.api.v2.TradingDataService/GetPartyVestingStats", runtime.WithHTTPPathPattern("/api/v2/party/vesting/stats/{party_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TradingDataService_GetPartyVestingStats_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TradingDataService_GetPartyVestingStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_TradingDataService_ObserveTransactionResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -9209,6 +9284,26 @@ func RegisterTradingDataServiceHandlerClient(ctx context.Context, mux *runtime.S }) + mux.Handle("GET", pattern_TradingDataService_GetPartyVestingStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/datanode.api.v2.TradingDataService/GetPartyVestingStats", runtime.WithHTTPPathPattern("/api/v2/party/vesting/stats/{party_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TradingDataService_GetPartyVestingStats_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TradingDataService_GetPartyVestingStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_TradingDataService_ObserveTransactionResults_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -9591,6 +9686,8 @@ var ( pattern_TradingDataService_GetVolumeDiscountStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "volume-discount-programs", "stats"}, "")) + pattern_TradingDataService_GetPartyVestingStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v2", "party", "vesting", "stats", "party_id"}, "")) + pattern_TradingDataService_ObserveTransactionResults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "stream", "transaction-results"}, "")) pattern_TradingDataService_EstimateTransferFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "transfers", "estimate-fee"}, "")) @@ -9827,6 +9924,8 @@ var ( forward_TradingDataService_GetVolumeDiscountStats_0 = runtime.ForwardResponseMessage + forward_TradingDataService_GetPartyVestingStats_0 = runtime.ForwardResponseMessage + forward_TradingDataService_ObserveTransactionResults_0 = runtime.ForwardResponseStream forward_TradingDataService_EstimateTransferFee_0 = runtime.ForwardResponseMessage diff --git a/protos/embed_test.go b/protos/embed_test.go index 1527aa73db6..3db70deae44 100644 --- a/protos/embed_test.go +++ b/protos/embed_test.go @@ -45,7 +45,7 @@ func Test_DataNodeBindings(t *testing.T) { t.Run("CoreBindings should return the core http bindings", func(t *testing.T) { bindings, err := protos.DataNodeBindings() require.NoError(t, err) - wantCount := 117 + wantCount := 118 assert.Len(t, bindings.HTTP.Rules, wantCount) diff --git a/protos/sources/data-node/grpc-rest-bindings.yml b/protos/sources/data-node/grpc-rest-bindings.yml index addb10ac086..d26b6a1b37e 100644 --- a/protos/sources/data-node/grpc-rest-bindings.yml +++ b/protos/sources/data-node/grpc-rest-bindings.yml @@ -79,6 +79,8 @@ http: get: "/api/v2/successor_markets/{market_id}" - selector: datanode.api.v2.TradingDataService.GetPartyActivityStreak get: "/api/v2/party/activity/streak/{party_id}" + - selector: datanode.api.v2.TradingDataService.GetPartyVestingStats + get: "/api/v2/party/vesting/stats/{party_id}" - selector: datanode.api.v2.TradingDataService.GetParty get: "/api/v2/party/{party_id}" - selector: datanode.api.v2.TradingDataService.ListParties