From fe322e8afce17d31e34db957ad9123d1bd0963f6 Mon Sep 17 00:00:00 2001 From: Jeremy Letang Date: Tue, 15 Oct 2024 14:20:35 +0100 Subject: [PATCH 1/7] feat: add staking staking from collateral asset - staking from general account with a transfer to the locked_for_staking account - staking from vesting and vested account, as soon as a reward is payed on the vesting account - unstaking as soon as funds are transfered back to the general account from the locked_for_staking - unstaking as soon as funds are transfered out of the vested account. Signed-off-by: Jeremy Letang --- commands/transfer_funds.go | 5 +- core/banking/engine.go | 17 +- core/banking/engine_test.go | 5 +- core/banking/mocks/mocks.go | 37 +- core/banking/oneoff_transfers.go | 62 +++ core/collateral/engine.go | 12 + core/integration/setup_test.go | 4 +- .../integration/stubs/staking_account_stub.go | 2 + core/protocol/all_services.go | 12 +- core/rewards/engine.go | 4 +- core/rewards/engine_test.go | 2 +- core/rewards/mocks/mocks.go | 8 +- core/types/collateral.go | 1 + core/vesting/engine.go | 65 ++- core/vesting/engine_test.go | 14 +- core/vesting/helper_for_test.go | 14 +- core/vesting/mocks/mocks.go | 76 +++- core/vesting/snapshot.go | 18 +- core/vesting/snapshot_test.go | 20 +- datanode/gateway/graphql/schema.graphql | 2 + protos/sources/vega/vega.proto | 3 + protos/vega/vega.pb.go | 406 +++++++++--------- 22 files changed, 550 insertions(+), 239 deletions(-) diff --git a/commands/transfer_funds.go b/commands/transfer_funds.go index 43e66a632d0..9b5697653c6 100644 --- a/commands/transfer_funds.go +++ b/commands/transfer_funds.go @@ -74,7 +74,8 @@ func checkTransfer(cmd *commandspb.Transfer) (e Errors) { } if cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_GENERAL && - cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_VESTED_REWARDS { + cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_VESTED_REWARDS && + cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING { errs.AddForProperty("transfer.from_account_type", ErrIsNotValid) } @@ -109,7 +110,7 @@ func checkTransfer(cmd *commandspb.Transfer) (e Errors) { } else { switch k := cmd.Kind.(type) { case *commandspb.Transfer_OneOff: - if cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GLOBAL_REWARD && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GENERAL && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_BUY_BACK_FEES { + if cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GLOBAL_REWARD && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GENERAL && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_BUY_BACK_FEES && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING { errs.AddForProperty("transfer.to_account_type", errors.New("account type is not valid for one off transfer")) } if k.OneOff.GetDeliverOn() < 0 { diff --git a/core/banking/engine.go b/core/banking/engine.go index f9567b69997..318c1bdae50 100644 --- a/core/banking/engine.go +++ b/core/banking/engine.go @@ -40,7 +40,7 @@ import ( "golang.org/x/exp/maps" ) -//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/banking Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties +//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/banking Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties,StakeAccounting var ( ErrWrongAssetTypeUsedInBuiltinAssetChainEvent = errors.New("non builtin asset used for builtin asset chain event") @@ -106,6 +106,11 @@ type Topology interface { IsValidator() bool } +// StakeAccounting ... +type StakeAccounting interface { + AddEvent(ctx context.Context, evt *types.StakeLinking) +} + type MarketActivityTracker interface { CalculateMetricForIndividuals(ctx context.Context, ds *vega.DispatchStrategy) []*types.PartyContributionScore CalculateMetricForTeams(ctx context.Context, ds *vega.DispatchStrategy) ([]*types.PartyContributionScore, map[string][]*types.PartyContributionScore) @@ -223,6 +228,9 @@ type Engine struct { // transient cache used to market a dispatch strategy as checked for eligibility for this round so we don't check again. dispatchRequiredCache map[string]bool + + stakingAsset string + stakeAccounting StakeAccounting } type withdrawalRef struct { @@ -244,6 +252,7 @@ func New(log *logging.Logger, secondaryBridgeView ERC20BridgeView, ethEventSource EthereumEventSource, parties Parties, + stakeAccounting StakeAccounting, ) (e *Engine) { log = log.Named(namedLogger) log.SetLevel(cfg.Level.Get()) @@ -290,9 +299,15 @@ func New(log *logging.Logger, primaryBridgeView: primaryBridgeView, secondaryBridgeView: secondaryBridgeView, dispatchRequiredCache: map[string]bool{}, + stakeAccounting: stakeAccounting, } } +func (e *Engine) OnStakingAsset(_ context.Context, a string) error { + e.stakingAsset = a + return nil +} + func (e *Engine) OnMaxFractionChanged(ctx context.Context, f num.Decimal) error { e.maxGovTransferFraction = f return nil diff --git a/core/banking/engine_test.go b/core/banking/engine_test.go index a2d14c00abd..8d822c55c4f 100644 --- a/core/banking/engine_test.go +++ b/core/banking/engine_test.go @@ -56,6 +56,7 @@ type testEngine struct { ethSource *mocks.MockEthereumEventSource secondaryBridgeView *mocks.MockERC20BridgeView parties *mocks.MockParties + stakeAccounting *mocks.MockStakeAccounting } func getTestEngine(t *testing.T) *testEngine { @@ -73,6 +74,7 @@ func getTestEngine(t *testing.T) *testEngine { broker := bmocks.NewMockBroker(ctrl) top := mocks.NewMockTopology(ctrl) epoch := mocks.NewMockEpochService(ctrl) + stakeAccounting := mocks.NewMockStakeAccounting(ctrl) primaryBridgeView := mocks.NewMockERC20BridgeView(ctrl) secondaryBridgeView := mocks.NewMockERC20BridgeView(ctrl) marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl) @@ -82,7 +84,7 @@ func getTestEngine(t *testing.T) *testEngine { notary.EXPECT().OfferSignatures(gomock.Any(), gomock.Any()).AnyTimes() epoch.EXPECT().NotifyOnEpoch(gomock.Any(), gomock.Any()).AnyTimes() parties := mocks.NewMockParties(ctrl) - eng := banking.New(logging.NewTestLogger(), banking.NewDefaultConfig(), col, witness, tsvc, assets, notary, broker, top, marketActivityTracker, primaryBridgeView, secondaryBridgeView, ethSource, parties) + eng := banking.New(logging.NewTestLogger(), banking.NewDefaultConfig(), col, witness, tsvc, assets, notary, broker, top, marketActivityTracker, primaryBridgeView, secondaryBridgeView, ethSource, parties, stakeAccounting) require.NoError(t, eng.OnMaxQuantumAmountUpdate(context.Background(), num.DecimalOne())) eng.OnPrimaryEthChainIDUpdated("1", "hello") @@ -103,6 +105,7 @@ func getTestEngine(t *testing.T) *testEngine { marketActivityTracker: marketActivityTracker, ethSource: ethSource, parties: parties, + stakeAccounting: stakeAccounting, } } diff --git a/core/banking/mocks/mocks.go b/core/banking/mocks/mocks.go index 1ba00cf6c38..edbdd08013e 100644 --- a/core/banking/mocks/mocks.go +++ b/core/banking/mocks/mocks.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/vega/core/banking (interfaces: Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties) +// Source: code.vegaprotocol.io/vega/core/banking (interfaces: Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties,StakeAccounting) // Package mocks is a generated GoMock package. package mocks @@ -784,3 +784,38 @@ func (mr *MockPartiesMockRecorder) CheckDerivedKeyOwnership(arg0, arg1 interface mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckDerivedKeyOwnership", reflect.TypeOf((*MockParties)(nil).CheckDerivedKeyOwnership), arg0, arg1) } + +// MockStakeAccounting is a mock of StakeAccounting interface. +type MockStakeAccounting struct { + ctrl *gomock.Controller + recorder *MockStakeAccountingMockRecorder +} + +// MockStakeAccountingMockRecorder is the mock recorder for MockStakeAccounting. +type MockStakeAccountingMockRecorder struct { + mock *MockStakeAccounting +} + +// NewMockStakeAccounting creates a new mock instance. +func NewMockStakeAccounting(ctrl *gomock.Controller) *MockStakeAccounting { + mock := &MockStakeAccounting{ctrl: ctrl} + mock.recorder = &MockStakeAccountingMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakeAccounting) EXPECT() *MockStakeAccountingMockRecorder { + return m.recorder +} + +// AddEvent mocks base method. +func (m *MockStakeAccounting) AddEvent(arg0 context.Context, arg1 *types.StakeLinking) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddEvent", arg0, arg1) +} + +// AddEvent indicates an expected call of AddEvent. +func (mr *MockStakeAccountingMockRecorder) AddEvent(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddEvent", reflect.TypeOf((*MockStakeAccounting)(nil).AddEvent), arg0, arg1) +} diff --git a/core/banking/oneoff_transfers.go b/core/banking/oneoff_transfers.go index 1e8d1c58cef..9531c506610 100644 --- a/core/banking/oneoff_transfers.go +++ b/core/banking/oneoff_transfers.go @@ -24,6 +24,8 @@ import ( "code.vegaprotocol.io/vega/core/events" "code.vegaprotocol.io/vega/core/types" + vgcontext "code.vegaprotocol.io/vega/libs/context" + "code.vegaprotocol.io/vega/libs/crypto" "code.vegaprotocol.io/vega/logging" checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1" ) @@ -61,6 +63,65 @@ func scheduledTransferFromProto(p *checkpoint.ScheduledTransfer) (scheduledTrans }, nil } +func (e *Engine) updateStakingAccounts( + ctx context.Context, transfer *types.OneOffTransfer, +) { + if transfer.Asset != e.stakingAsset { + // nothing to do + return + } + + var ( + now = e.timeService.GetTimeNow().Unix() + height, _ = vgcontext.BlockHeightFromContext(ctx) + txhash, _ = vgcontext.TxHashFromContext(ctx) + id = crypto.HashStrToHex(fmt.Sprintf("%v%v", txhash, height)) + stakeLinking *types.StakeLinking + ) + + // manually send funds from the general account to the locked for staking + if transfer.FromAccountType == types.AccountTypeGeneral && transfer.ToAccountType == types.AccountTypeLockedForStaking { + stakeLinking = &types.StakeLinking{ + ID: id, + Type: types.StakeLinkingTypeDeposited, + TS: now, + Party: transfer.From, + Amount: transfer.Amount.Clone(), + Status: types.StakeLinkingStatusAccepted, + FinalizedAt: now, + TxHash: txhash, + BlockHeight: height, + BlockTime: now, + LogIndex: 1, + EthereumAddress: transfer.From, + } + } + + // from staking account or vested rewards, we send a remove event + if (transfer.FromAccountType == types.AccountTypeLockedForStaking && transfer.ToAccountType == types.AccountTypeGeneral) || + (transfer.FromAccountType == types.AccountTypeVestedRewards && transfer.ToAccountType == types.AccountTypeGeneral) { + stakeLinking = &types.StakeLinking{ + ID: id, + Type: types.StakeLinkingTypeRemoved, + TS: now, + Party: transfer.From, + Amount: transfer.Amount.Clone(), + Status: types.StakeLinkingStatusAccepted, + FinalizedAt: now, + TxHash: txhash, + BlockHeight: height, + BlockTime: now, + LogIndex: 1, + EthereumAddress: transfer.From, + } + } + + if stakeLinking != nil { + e.stakeAccounting.AddEvent(ctx, stakeLinking) + e.broker.Send(events.NewStakeLinking(ctx, *stakeLinking)) + } +} + func (e *Engine) oneOffTransfer( ctx context.Context, transfer *types.OneOffTransfer, @@ -70,6 +131,7 @@ func (e *Engine) oneOffTransfer( e.broker.Send(events.NewOneOffTransferFundsEventWithReason(ctx, transfer, err.Error())) } else { e.broker.Send(events.NewOneOffTransferFundsEvent(ctx, transfer)) + e.updateStakingAccounts(ctx, transfer) } }() diff --git a/core/collateral/engine.go b/core/collateral/engine.go index 298133c20ee..655fa042228 100644 --- a/core/collateral/engine.go +++ b/core/collateral/engine.go @@ -356,6 +356,18 @@ func (e *Engine) GetAllVestingQuantumBalance(party string) num.Decimal { return balance } +func (e *Engine) GetAllVestingAndVestedAccountForAsset(asset string) []*types.Account { + accs := []*types.Account{} + + for _, v := range e.hashableAccs { + if v.Asset == asset && (v.Type == types.AccountTypeVestingRewards || v.Type == types.AccountTypeVestedRewards) { + accs = append(accs, v.Clone()) + } + } + + return accs +} + func (e *Engine) GetVestingRecovery() map[string]map[string]*num.Uint { out := e.vesting e.vesting = map[string]map[string]*num.Uint{} diff --git a/core/integration/setup_test.go b/core/integration/setup_test.go index 601e44a92e7..a2164c0fdc4 100644 --- a/core/integration/setup_test.go +++ b/core/integration/setup_test.go @@ -202,7 +202,7 @@ func newExecutionTestSetup() *executionTestSetup { execsetup.epochEngine.NotifyOnEpoch(execsetup.volumeDiscountProgram.OnEpoch, execsetup.volumeDiscountProgram.OnEpochRestore) execsetup.volumeRebateProgram = volumerebate.New(execsetup.broker, execsetup.marketActivityTracker) - execsetup.banking = banking.New(execsetup.log, banking.NewDefaultConfig(), execsetup.collateralEngine, execsetup.witness, execsetup.timeService, execsetup.assetsEngine, execsetup.notary, execsetup.broker, execsetup.topology, execsetup.marketActivityTracker, stubs.NewBridgeViewStub(), stubs.NewBridgeViewStub(), eventHeartbeat, execsetup.profilesEngine) + execsetup.banking = banking.New(execsetup.log, banking.NewDefaultConfig(), execsetup.collateralEngine, execsetup.witness, execsetup.timeService, execsetup.assetsEngine, execsetup.notary, execsetup.broker, execsetup.topology, execsetup.marketActivityTracker, stubs.NewBridgeViewStub(), stubs.NewBridgeViewStub(), eventHeartbeat, execsetup.profilesEngine, execsetup.stakingAccount) execsetup.executionEngine = newExEng( execution.NewEngine( @@ -233,7 +233,7 @@ func newExecutionTestSetup() *executionTestSetup { execsetup.activityStreak = activitystreak.New(execsetup.log, execsetup.executionEngine, execsetup.broker) execsetup.epochEngine.NotifyOnEpoch(execsetup.activityStreak.OnEpochEvent, execsetup.activityStreak.OnEpochRestore) - execsetup.vesting = vesting.New(execsetup.log, execsetup.collateralEngine, execsetup.activityStreak, execsetup.broker, execsetup.assetsEngine, execsetup.profilesEngine) + execsetup.vesting = vesting.New(execsetup.log, execsetup.collateralEngine, execsetup.activityStreak, execsetup.broker, execsetup.assetsEngine, execsetup.profilesEngine, execsetup.timeService) execsetup.rewardsEngine = rewards.New(execsetup.log, rewards.NewDefaultConfig(), execsetup.broker, execsetup.delegationEngine, execsetup.epochEngine, execsetup.collateralEngine, execsetup.timeService, execsetup.marketActivityTracker, execsetup.topology, execsetup.vesting, execsetup.banking, execsetup.activityStreak) // register this after the rewards engine is created to make sure the on epoch is called in the right order. diff --git a/core/integration/stubs/staking_account_stub.go b/core/integration/stubs/staking_account_stub.go index 21d6c084e84..f8833cb5619 100644 --- a/core/integration/stubs/staking_account_stub.go +++ b/core/integration/stubs/staking_account_stub.go @@ -32,6 +32,8 @@ type StakingAccountStub struct { currentEpoch *types.Epoch } +func (t *StakingAccountStub) AddEvent(ctx context.Context, evt *types.StakeLinking) {} + func (t *StakingAccountStub) OnEpochEvent(ctx context.Context, epoch types.Epoch) { if t.currentEpoch == nil || t.currentEpoch.Seq != epoch.Seq { t.currentEpoch = &epoch diff --git a/core/protocol/all_services.go b/core/protocol/all_services.go index e4a3f79fe92..64695e4a8e0 100644 --- a/core/protocol/all_services.go +++ b/core/protocol/all_services.go @@ -340,7 +340,7 @@ func newServices( svcs.volumeRebate = volumerebate.NewSnapshottedEngine(svcs.broker, svcs.marketActivityTracker) svcs.banking = banking.New(svcs.log, svcs.conf.Banking, svcs.collateral, svcs.witness, svcs.timeService, svcs.assets, svcs.notary, svcs.broker, svcs.topology, svcs.marketActivityTracker, svcs.primaryBridgeView, - svcs.secondaryBridgeView, svcs.forwarderHeartbeat, svcs.partiesEngine) + svcs.secondaryBridgeView, svcs.forwarderHeartbeat, svcs.partiesEngine, svcs.stakingAccounts) // instantiate the execution engine svcs.executionEngine = execution.NewEngine( @@ -392,7 +392,7 @@ func newServices( svcs.activityStreak.OnEpochRestore, ) - svcs.vesting = vesting.NewSnapshotEngine(svcs.log, svcs.collateral, svcs.activityStreak, svcs.broker, svcs.assets, svcs.partiesEngine) + svcs.vesting = vesting.NewSnapshotEngine(svcs.log, svcs.collateral, svcs.activityStreak, svcs.broker, svcs.assets, svcs.partiesEngine, svcs.timeService) svcs.timeService.NotifyOnTick(svcs.vesting.OnTick) svcs.rewards = rewards.New(svcs.log, svcs.conf.Rewards, svcs.broker, svcs.delegation, svcs.epochService, svcs.collateral, svcs.timeService, svcs.marketActivityTracker, svcs.topology, svcs.vesting, svcs.banking, svcs.activityStreak) @@ -673,6 +673,14 @@ func (svcs *allServices) setupNetParameters(powWatchers []netparams.WatchParam) } watchers := []netparams.WatchParam{ + { + Param: netparams.RewardAsset, + Watcher: svcs.banking.OnStakingAsset, + }, + { + Param: netparams.RewardAsset, + Watcher: svcs.vesting.OnStakingAssetUpdate, + }, { Param: netparams.SpamProtectionBalanceSnapshotFrequency, Watcher: svcs.collateral.OnBalanceSnapshotFrequencyUpdated, diff --git a/core/rewards/engine.go b/core/rewards/engine.go index 1270a6e3e25..257ce8b7e5e 100644 --- a/core/rewards/engine.go +++ b/core/rewards/engine.go @@ -89,7 +89,7 @@ type Teams interface { } type Vesting interface { - AddReward(party, asset string, amount *num.Uint, lockedForEpochs uint64) + AddReward(ctx context.Context, party, asset string, amount *num.Uint, lockedForEpochs uint64) GetSingleAndSummedRewardBonusMultipliers(party string) (vesting.MultiplierAndQuantBalance, vesting.MultiplierAndQuantBalance) } @@ -492,7 +492,7 @@ func (e *Engine) distributePayout(ctx context.Context, po *payout) { if po.rewardType != types.AccountTypeFeesInfrastructure { for _, party := range partyIDs { amt := po.partyToAmount[party] - e.vesting.AddReward(party, po.asset, amt, po.lockedForEpochs) + e.vesting.AddReward(ctx, party, po.asset, amt, po.lockedForEpochs) } } e.broker.Send(events.NewLedgerMovements(ctx, responses)) diff --git a/core/rewards/engine_test.go b/core/rewards/engine_test.go index 152e0dfce8b..570c5f683df 100644 --- a/core/rewards/engine_test.go +++ b/core/rewards/engine_test.go @@ -450,7 +450,7 @@ func getEngine(t *testing.T) *testEngine { topology := mocks.NewMockTopology(ctrl) marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl) vesting := mocks.NewMockVesting(ctrl) - vesting.EXPECT().AddReward(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + vesting.EXPECT().AddReward(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() transfers := mocks.NewMockTransfers(ctrl) activityStreak := mocks.NewMockActivityStreak(ctrl) engine := New(logger, conf, broker, delegation, epochEngine, collateralEng, ts, marketActivityTracker, topology, vesting, transfers, activityStreak) diff --git a/core/rewards/mocks/mocks.go b/core/rewards/mocks/mocks.go index f6b1c4a7d34..efad8c301e6 100644 --- a/core/rewards/mocks/mocks.go +++ b/core/rewards/mocks/mocks.go @@ -363,15 +363,15 @@ func (m *MockVesting) EXPECT() *MockVestingMockRecorder { } // AddReward mocks base method. -func (m *MockVesting) AddReward(arg0, arg1 string, arg2 *num.Uint, arg3 uint64) { +func (m *MockVesting) AddReward(arg0 context.Context, arg1, arg2 string, arg3 *num.Uint, arg4 uint64) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AddReward", arg0, arg1, arg2, arg3) + m.ctrl.Call(m, "AddReward", arg0, arg1, arg2, arg3, arg4) } // AddReward indicates an expected call of AddReward. -func (mr *MockVestingMockRecorder) AddReward(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockVestingMockRecorder) AddReward(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddReward", reflect.TypeOf((*MockVesting)(nil).AddReward), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddReward", reflect.TypeOf((*MockVesting)(nil).AddReward), arg0, arg1, arg2, arg3, arg4) } // GetSingleAndSummedRewardBonusMultipliers mocks base method. diff --git a/core/types/collateral.go b/core/types/collateral.go index fb0ad84bb7d..d69bebeb304 100644 --- a/core/types/collateral.go +++ b/core/types/collateral.go @@ -325,4 +325,5 @@ const ( AccountTypeBuyBackFees AccountType = proto.AccountType_ACCOUNT_TYPE_BUY_BACK_FEES // Account for eligible entities rewards. AccountTypeEligibleEntitiesReward = proto.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES + AccountTypeLockedForStaking = proto.AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING ) diff --git a/core/vesting/engine.go b/core/vesting/engine.go index fd5cdd34ad3..e817da676c7 100644 --- a/core/vesting/engine.go +++ b/core/vesting/engine.go @@ -24,6 +24,8 @@ import ( "code.vegaprotocol.io/vega/core/assets" "code.vegaprotocol.io/vega/core/events" "code.vegaprotocol.io/vega/core/types" + vgcontext "code.vegaprotocol.io/vega/libs/context" + "code.vegaprotocol.io/vega/libs/crypto" "code.vegaprotocol.io/vega/libs/num" "code.vegaprotocol.io/vega/logging" proto "code.vegaprotocol.io/vega/protos/vega" @@ -33,12 +35,13 @@ import ( "golang.org/x/exp/slices" ) -//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/vesting ActivityStreakVestingMultiplier,Assets,Parties +//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/vesting ActivityStreakVestingMultiplier,Assets,Parties,StakeAccounting,Time type Collateral interface { TransferVestedRewards(ctx context.Context, transfers []*types.Transfer) ([]*types.LedgerMovement, error) GetVestingRecovery() map[string]map[string]*num.Uint GetAllVestingQuantumBalance(party string) num.Decimal + GetAllVestingAndVestedAccountForAsset(asset string) []*types.Account } type ActivityStreakVestingMultiplier interface { @@ -47,6 +50,7 @@ type ActivityStreakVestingMultiplier interface { type Broker interface { Send(events events.Event) + Stage(event events.Event) } type Assets interface { @@ -57,6 +61,14 @@ type Parties interface { RelatedKeys(key string) (*types.PartyID, []string) } +type StakeAccounting interface { + AddEvent(ctx context.Context, evt *types.StakeLinking) +} + +type Time interface { + GetTimeNow() time.Time +} + type PartyRewards struct { // the amounts per assets still being locked in the // account and not available to be released @@ -93,6 +105,11 @@ type Engine struct { // cache the reward bonus multiplier and quantum balance rewardBonusMultiplierCache map[string]MultiplierAndQuantBalance + + stakingAsset string + stakeAccounting StakeAccounting + + t Time } func New( @@ -102,6 +119,7 @@ func New( broker Broker, assets Assets, parties Parties, + t Time, ) *Engine { log = log.Named(namedLogger) @@ -114,6 +132,7 @@ func New( parties: parties, state: map[string]*PartyRewards{}, rewardBonusMultiplierCache: map[string]MultiplierAndQuantBalance{}, + t: t, } } @@ -139,6 +158,11 @@ func (e *Engine) OnBenefitTiersUpdate(_ context.Context, v interface{}) error { return nil } +func (e *Engine) OnStakingAssetUpdate(_ context.Context, stakingAsset string) error { + e.stakingAsset = stakingAsset + return nil +} + func (e *Engine) OnRewardVestingBaseRateUpdate(_ context.Context, baseRate num.Decimal) error { e.baseRate = baseRate return nil @@ -165,11 +189,50 @@ func (e *Engine) OnEpochRestore(_ context.Context, epoch types.Epoch) { e.epochSeq = epoch.Seq } +func (e *Engine) updateStakingAccount( + ctx context.Context, + party string, + amount *num.Uint, + logIndex uint64, + brokerFunc func(events.Event), +) { + var ( + now = e.t.GetTimeNow().Unix() + height, _ = vgcontext.BlockHeightFromContext(ctx) + txhash, _ = vgcontext.TxHashFromContext(ctx) + id = crypto.HashStrToHex(fmt.Sprintf("%v%v%v", party, txhash, height)) + ) + + stakeLinking := &types.StakeLinking{ + ID: id, + Type: types.StakeLinkingTypeDeposited, + TS: now, + Party: party, + Amount: amount, + Status: types.StakeLinkingStatusAccepted, + FinalizedAt: now, + TxHash: txhash, + BlockHeight: height, + BlockTime: now, + LogIndex: logIndex, + EthereumAddress: party, + } + + e.stakeAccounting.AddEvent(context.Background(), stakeLinking) + brokerFunc(events.NewStakeLinking(ctx, *stakeLinking)) +} + func (e *Engine) AddReward( + ctx context.Context, party, asset string, amount *num.Uint, lockedForEpochs uint64, ) { + // send to staking + if asset == e.stakingAsset { + e.updateStakingAccount(ctx, party, amount.Clone(), 1, e.broker.Send) + } + // no locktime, just increase the amount in vesting if lockedForEpochs == 0 { e.increaseVestingBalance(party, asset, amount) diff --git a/core/vesting/engine_test.go b/core/vesting/engine_test.go index b4f664b3f7f..f6dbe22eeee 100644 --- a/core/vesting/engine_test.go +++ b/core/vesting/engine_test.go @@ -98,7 +98,7 @@ func TestDistributeAfterDelay(t *testing.T) { }) t.Run("Add a reward locked for 3 epochs", func(t *testing.T) { - v.AddReward(party, vegaAsset, num.NewUint(100), 3) + v.AddReward(context.Background(), party, vegaAsset, num.NewUint(100), 3) }) t.Run("Wait for 3 epochs", func(t *testing.T) { @@ -332,7 +332,7 @@ func TestDistributeWithNoDelay(t *testing.T) { }) t.Run("Add a reward without epoch lock", func(t *testing.T) { - v.AddReward(party, vegaAsset, num.NewUint(100), 0) + v.AddReward(context.Background(), party, vegaAsset, num.NewUint(100), 0) }) t.Run("First reward payment", func(t *testing.T) { @@ -514,7 +514,7 @@ func TestDistributeWithStreakRate(t *testing.T) { }) t.Run("Add a reward without epoch lock", func(t *testing.T) { - v.AddReward(party, vegaAsset, num.NewUint(100), 0) + v.AddReward(context.Background(), party, vegaAsset, num.NewUint(100), 0) }) t.Run("First reward payment", func(t *testing.T) { @@ -696,11 +696,11 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { }) t.Run("Add a reward locked for 2 epochs", func(t *testing.T) { - v.AddReward(party, vegaAsset, num.NewUint(100), 2) + v.AddReward(context.Background(), party, vegaAsset, num.NewUint(100), 2) }) t.Run("Add another reward locked for 1 epoch", func(t *testing.T) { - v.AddReward(party, vegaAsset, num.NewUint(100), 1) + v.AddReward(context.Background(), party, vegaAsset, num.NewUint(100), 1) }) t.Run("Wait for 1 epoch", func(t *testing.T) { @@ -1046,10 +1046,10 @@ func TestDistributeWithRelatedKeys(t *testing.T) { }) t.Run("Add a reward without epoch lock", func(t *testing.T) { - v.AddReward(party, vegaAsset, num.NewUint(100), 0) + v.AddReward(context.Background(), party, vegaAsset, num.NewUint(100), 0) for _, key := range derivedKeys { - v.AddReward(key, vegaAsset, num.NewUint(50), 0) + v.AddReward(context.Background(), key, vegaAsset, num.NewUint(50), 0) } }) diff --git a/core/vesting/helper_for_test.go b/core/vesting/helper_for_test.go index 8572c0cc342..44c6bfd0fe6 100644 --- a/core/vesting/helper_for_test.go +++ b/core/vesting/helper_for_test.go @@ -46,6 +46,7 @@ type testEngine struct { broker *bmocks.MockBroker assets *mocks.MockAssets parties *mocks.MockParties + t *mocks.MockTime } func getTestEngine(t *testing.T) *testEngine { @@ -57,10 +58,11 @@ func getTestEngine(t *testing.T) *testEngine { asvm := mocks.NewMockActivityStreakVestingMultiplier(ctrl) assets := mocks.NewMockAssets(ctrl) parties := mocks.NewMockParties(ctrl) + tim := mocks.NewMockTime(ctrl) return &testEngine{ Engine: vesting.New( - logger, col, asvm, broker, assets, parties, + logger, col, asvm, broker, assets, parties, tim, ), ctrl: ctrl, broker: broker, @@ -68,6 +70,7 @@ func getTestEngine(t *testing.T) *testEngine { asvm: asvm, assets: assets, parties: parties, + t: tim, } } @@ -80,6 +83,7 @@ type testSnapshotEngine struct { broker *bmocks.MockBroker assets *mocks.MockAssets parties *mocks.MockParties + t *mocks.MockTime currentEpoch uint64 } @@ -92,10 +96,11 @@ func newEngine(t *testing.T) *testSnapshotEngine { broker := bmocks.NewMockBroker(ctrl) assets := mocks.NewMockAssets(ctrl) parties := mocks.NewMockParties(ctrl) + tim := mocks.NewMockTime(ctrl) return &testSnapshotEngine{ engine: vesting.NewSnapshotEngine( - logging.NewTestLogger(), col, asvm, broker, assets, parties, + logging.NewTestLogger(), col, asvm, broker, assets, parties, tim, ), ctrl: ctrl, col: col, @@ -104,6 +109,7 @@ func newEngine(t *testing.T) *testSnapshotEngine { assets: assets, parties: parties, currentEpoch: 10, + t: tim, } } @@ -170,6 +176,10 @@ func (c *collateralMock) GetVestingQuantumBalanceCallCount() int { return c.vestingQuantumBalanceCallCount } +func (c *collateralMock) GetAllVestingAndVestedAccountForAsset(asset string) []*types.Account { + return nil +} + func newCollateralMock(t *testing.T) *collateralMock { t.Helper() diff --git a/core/vesting/mocks/mocks.go b/core/vesting/mocks/mocks.go index 2ff1049dd3f..09cef96fbf7 100644 --- a/core/vesting/mocks/mocks.go +++ b/core/vesting/mocks/mocks.go @@ -1,11 +1,13 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/vega/core/vesting (interfaces: ActivityStreakVestingMultiplier,Assets,Parties) +// Source: code.vegaprotocol.io/vega/core/vesting (interfaces: ActivityStreakVestingMultiplier,Assets,Parties,StakeAccounting,Time) // Package mocks is a generated GoMock package. package mocks import ( + context "context" reflect "reflect" + time "time" assets "code.vegaprotocol.io/vega/core/assets" types "code.vegaprotocol.io/vega/core/types" @@ -125,3 +127,75 @@ func (mr *MockPartiesMockRecorder) RelatedKeys(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RelatedKeys", reflect.TypeOf((*MockParties)(nil).RelatedKeys), arg0) } + +// MockStakeAccounting is a mock of StakeAccounting interface. +type MockStakeAccounting struct { + ctrl *gomock.Controller + recorder *MockStakeAccountingMockRecorder +} + +// MockStakeAccountingMockRecorder is the mock recorder for MockStakeAccounting. +type MockStakeAccountingMockRecorder struct { + mock *MockStakeAccounting +} + +// NewMockStakeAccounting creates a new mock instance. +func NewMockStakeAccounting(ctrl *gomock.Controller) *MockStakeAccounting { + mock := &MockStakeAccounting{ctrl: ctrl} + mock.recorder = &MockStakeAccountingMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakeAccounting) EXPECT() *MockStakeAccountingMockRecorder { + return m.recorder +} + +// AddEvent mocks base method. +func (m *MockStakeAccounting) AddEvent(arg0 context.Context, arg1 *types.StakeLinking) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddEvent", arg0, arg1) +} + +// AddEvent indicates an expected call of AddEvent. +func (mr *MockStakeAccountingMockRecorder) AddEvent(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddEvent", reflect.TypeOf((*MockStakeAccounting)(nil).AddEvent), arg0, arg1) +} + +// MockTime is a mock of Time interface. +type MockTime struct { + ctrl *gomock.Controller + recorder *MockTimeMockRecorder +} + +// MockTimeMockRecorder is the mock recorder for MockTime. +type MockTimeMockRecorder struct { + mock *MockTime +} + +// NewMockTime creates a new mock instance. +func NewMockTime(ctrl *gomock.Controller) *MockTime { + mock := &MockTime{ctrl: ctrl} + mock.recorder = &MockTimeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTime) EXPECT() *MockTimeMockRecorder { + return m.recorder +} + +// GetTimeNow mocks base method. +func (m *MockTime) GetTimeNow() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTimeNow") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetTimeNow indicates an expected call of GetTimeNow. +func (mr *MockTimeMockRecorder) GetTimeNow() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTimeNow", reflect.TypeOf((*MockTime)(nil).GetTimeNow)) +} diff --git a/core/vesting/snapshot.go b/core/vesting/snapshot.go index 883e9bf66b9..15adcd1e3a1 100644 --- a/core/vesting/snapshot.go +++ b/core/vesting/snapshot.go @@ -21,6 +21,7 @@ import ( "sort" "code.vegaprotocol.io/vega/core/types" + vgcontext "code.vegaprotocol.io/vega/libs/context" "code.vegaprotocol.io/vega/libs/num" "code.vegaprotocol.io/vega/libs/proto" "code.vegaprotocol.io/vega/logging" @@ -40,9 +41,10 @@ func NewSnapshotEngine( broker Broker, assets Assets, parties Parties, + t Time, ) *SnapshotEngine { se := &SnapshotEngine{ - Engine: New(log, c, asvm, broker, assets, parties), + Engine: New(log, c, asvm, broker, assets, parties, t), } return se @@ -79,7 +81,7 @@ func (e *SnapshotEngine) LoadState(ctx context.Context, p *types.Payload) ([]typ } } -func (e *SnapshotEngine) loadStateFromSnapshot(_ context.Context, state *snapshotpb.Vesting) { +func (e *SnapshotEngine) loadStateFromSnapshot(ctx context.Context, state *snapshotpb.Vesting) { for _, entry := range state.PartiesReward { for _, v := range entry.InVesting { balance, underflow := num.UintFromString(v.Balance, 10) @@ -101,6 +103,18 @@ func (e *SnapshotEngine) loadStateFromSnapshot(_ context.Context, state *snapsho } } } + + if vgcontext.InProgressUpgradeFrom(ctx, "v0.78.8") { + e.updateStakingOnUpgrade79(ctx) + } +} + +// updateStakingOnUpgrade79 update staking balance for party which had +// rewards vesting before the upgrade. +func (e *SnapshotEngine) updateStakingOnUpgrade79(ctx context.Context) { + for i, v := range e.c.GetAllVestingAndVestedAccountForAsset(e.stakingAsset) { + e.updateStakingAccount(ctx, v.Owner, v.Balance.Clone(), uint64(i), e.broker.Stage) + } } func (e *SnapshotEngine) serialise(k string) ([]byte, error) { diff --git a/core/vesting/snapshot_test.go b/core/vesting/snapshot_test.go index 167d618b0b1..5566b60945a 100644 --- a/core/vesting/snapshot_test.go +++ b/core/vesting/snapshot_test.go @@ -48,19 +48,19 @@ func TestSnapshotEngine(t *testing.T) { setupMocks(t, te1) setupNetParams(ctx, t, te1) - te1.engine.AddReward("party1", "eth", num.NewUint(100), 4) - te1.engine.AddReward("party1", "btc", num.NewUint(150), 1) - te1.engine.AddReward("party1", "eth", num.NewUint(200), 0) + te1.engine.AddReward(context.Background(), "party1", "eth", num.NewUint(100), 4) + te1.engine.AddReward(context.Background(), "party1", "btc", num.NewUint(150), 1) + te1.engine.AddReward(context.Background(), "party1", "eth", num.NewUint(200), 0) nextEpoch(ctx, t, te1, time.Now()) - te1.engine.AddReward("party2", "btc", num.NewUint(100), 2) - te1.engine.AddReward("party3", "btc", num.NewUint(100), 0) + te1.engine.AddReward(context.Background(), "party2", "btc", num.NewUint(100), 2) + te1.engine.AddReward(context.Background(), "party3", "btc", num.NewUint(100), 0) nextEpoch(ctx, t, te1, time.Now()) - te1.engine.AddReward("party4", "eth", num.NewUint(100), 1) - te1.engine.AddReward("party5", "doge", num.NewUint(100), 0) + te1.engine.AddReward(context.Background(), "party4", "eth", num.NewUint(100), 1) + te1.engine.AddReward(context.Background(), "party5", "doge", num.NewUint(100), 0) // Take a snapshot. hash1, err := snapshotEngine1.SnapshotNow(ctx) @@ -69,12 +69,12 @@ func TestSnapshotEngine(t *testing.T) { // This is what must be replayed after snapshot restoration. replayFn := func(te *testSnapshotEngine) { - te.engine.AddReward("party6", "doge", num.NewUint(100), 3) + te.engine.AddReward(context.Background(), "party6", "doge", num.NewUint(100), 3) nextEpoch(ctx, t, te, time.Now()) - te.engine.AddReward("party7", "eth", num.NewUint(100), 2) - te.engine.AddReward("party8", "vega", num.NewUint(100), 10) + te.engine.AddReward(context.Background(), "party7", "eth", num.NewUint(100), 2) + te.engine.AddReward(context.Background(), "party8", "vega", num.NewUint(100), 10) nextEpoch(ctx, t, te, time.Now()) } diff --git a/datanode/gateway/graphql/schema.graphql b/datanode/gateway/graphql/schema.graphql index 98eedc24de4..5ed40d1bb5a 100644 --- a/datanode/gateway/graphql/schema.graphql +++ b/datanode/gateway/graphql/schema.graphql @@ -4707,6 +4707,8 @@ enum AccountType { ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL "Reward account for the eligible entities metric" ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES + "Locked for staking" + ACCOUNT_TYPE_LOCKED_FOR_STAKING } "Types that describe why a transfer has been made" diff --git a/protos/sources/vega/vega.proto b/protos/sources/vega/vega.proto index 8c6d61d4b03..f161d02bf06 100644 --- a/protos/sources/vega/vega.proto +++ b/protos/sources/vega/vega.proto @@ -971,6 +971,9 @@ enum AccountType { // Reward account for the eligible entities metric. ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES = 33; + // Locked for staking. + ACCOUNT_TYPE_LOCKED_FOR_STAKING = 34; + // Note: If adding an enum value, add a matching entry in: // - gateway/graphql/helpers_enum.go // - gateway/graphql/schema.graphql (enum AccountType) diff --git a/protos/vega/vega.pb.go b/protos/vega/vega.pb.go index bcf21b0de07..86a7bc8cf83 100644 --- a/protos/vega/vega.pb.go +++ b/protos/vega/vega.pb.go @@ -761,6 +761,8 @@ const ( AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL AccountType = 32 // Reward account for the eligible entities metric. AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES AccountType = 33 + // Locked for staking. + AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING AccountType = 34 ) // Enum value maps for AccountType. @@ -798,6 +800,7 @@ var ( 31: "ACCOUNT_TYPE_BUY_BACK_FEES", 32: "ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL", 33: "ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES", + 34: "ACCOUNT_TYPE_LOCKED_FOR_STAKING", } AccountType_value = map[string]int32{ "ACCOUNT_TYPE_UNSPECIFIED": 0, @@ -832,6 +835,7 @@ var ( "ACCOUNT_TYPE_BUY_BACK_FEES": 31, "ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL": 32, "ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES": 33, + "ACCOUNT_TYPE_LOCKED_FOR_STAKING": 34, } ) @@ -12265,7 +12269,7 @@ var file_vega_vega_proto_rawDesc = []byte{ 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x59, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x8c, 0x09, + 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x03, 0x2a, 0xb1, 0x09, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x41, @@ -12338,212 +12342,214 @@ var file_vega_vega_proto_rawDesc = []byte{ 0x4e, 0x41, 0x4c, 0x10, 0x20, 0x12, 0x29, 0x0a, 0x25, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x45, 0x4c, 0x49, 0x47, 0x49, 0x42, 0x4c, 0x45, 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x49, 0x45, 0x53, 0x10, 0x21, - 0x22, 0x04, 0x08, 0x08, 0x10, 0x08, 0x22, 0x04, 0x08, 0x18, 0x10, 0x18, 0x2a, 0xd0, 0x0e, 0x0a, - 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, - 0x19, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, - 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, - 0x53, 0x53, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x54, - 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x54, 0x4d, - 0x5f, 0x4c, 0x4f, 0x53, 0x53, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x54, 0x4d, 0x5f, 0x57, 0x49, 0x4e, - 0x10, 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x06, - 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x07, 0x12, - 0x24, 0x0a, 0x20, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x53, 0x43, 0x41, - 0x54, 0x45, 0x44, 0x10, 0x08, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, - 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x4b, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, - 0x5f, 0x50, 0x41, 0x59, 0x10, 0x09, 0x12, 0x23, 0x0a, 0x1f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, - 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x4b, 0x45, 0x52, 0x5f, 0x46, 0x45, - 0x45, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x10, 0x0a, 0x12, 0x28, 0x0a, 0x24, 0x54, - 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x46, - 0x52, 0x41, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x46, 0x45, 0x45, 0x5f, - 0x50, 0x41, 0x59, 0x10, 0x0b, 0x12, 0x2f, 0x0a, 0x2b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, - 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x52, 0x41, 0x53, 0x54, 0x52, 0x55, - 0x43, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, - 0x42, 0x55, 0x54, 0x45, 0x10, 0x0c, 0x12, 0x23, 0x0a, 0x1f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, - 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, - 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x0d, 0x12, 0x2a, 0x0a, 0x26, 0x54, - 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, - 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, - 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x0e, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4e, 0x44, 0x5f, 0x4c, 0x4f, - 0x57, 0x10, 0x0f, 0x12, 0x1b, 0x0a, 0x17, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4e, 0x44, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x10, + 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x4b, + 0x49, 0x4e, 0x47, 0x10, 0x22, 0x22, 0x04, 0x08, 0x08, 0x10, 0x08, 0x22, 0x04, 0x08, 0x18, 0x10, + 0x18, 0x2a, 0xd0, 0x0e, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x53, 0x53, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x52, 0x41, + 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x10, 0x12, 0x12, 0x19, 0x0a, 0x15, - 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, - 0x50, 0x4f, 0x53, 0x49, 0x54, 0x10, 0x13, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4e, 0x44, 0x5f, 0x53, 0x4c, - 0x41, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x14, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x52, 0x41, 0x4e, - 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, - 0x5f, 0x50, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x10, 0x15, 0x12, 0x25, 0x0a, 0x21, 0x54, 0x52, 0x41, - 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x46, 0x55, 0x4e, 0x44, 0x53, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x16, - 0x12, 0x2b, 0x0a, 0x27, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x46, 0x55, 0x4e, 0x44, 0x53, - 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x17, 0x12, 0x1f, 0x0a, - 0x1b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, - 0x4c, 0x45, 0x41, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x18, 0x12, 0x2c, - 0x0a, 0x28, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x43, 0x48, 0x45, 0x43, 0x4b, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, - 0x43, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x19, 0x12, 0x16, 0x0a, 0x12, - 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x50, - 0x4f, 0x54, 0x10, 0x1a, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4c, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4c, 0x4f, - 0x43, 0x4b, 0x10, 0x1b, 0x12, 0x21, 0x0a, 0x1d, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4c, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, - 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x1c, 0x12, 0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, - 0x4f, 0x52, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x52, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x46, 0x52, 0x41, - 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x1d, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, - 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x45, 0x10, - 0x1e, 0x12, 0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, - 0x5f, 0x4e, 0x45, 0x54, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, - 0x1f, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x53, 0x4c, 0x41, 0x5f, 0x50, 0x45, 0x4e, 0x41, 0x4c, 0x54, 0x59, 0x5f, 0x42, - 0x4f, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x59, 0x10, 0x20, 0x12, 0x2a, 0x0a, 0x26, 0x54, - 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c, 0x41, - 0x5f, 0x50, 0x45, 0x4e, 0x41, 0x4c, 0x54, 0x59, 0x5f, 0x4c, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x5f, - 0x41, 0x50, 0x50, 0x4c, 0x59, 0x10, 0x21, 0x12, 0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, - 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x55, 0x4e, 0x50, 0x41, 0x49, 0x44, 0x5f, 0x43, 0x4f, - 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x10, 0x22, 0x12, 0x32, 0x0a, 0x2e, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c, 0x41, 0x5f, 0x50, 0x45, 0x52, - 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x42, 0x4f, 0x4e, 0x55, 0x53, 0x5f, 0x44, - 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x23, 0x12, 0x29, 0x0a, 0x25, 0x54, + 0x45, 0x5f, 0x4d, 0x54, 0x4d, 0x5f, 0x4c, 0x4f, 0x53, 0x53, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x54, + 0x4d, 0x5f, 0x57, 0x49, 0x4e, 0x10, 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x54, 0x52, 0x41, 0x4e, 0x53, + 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, + 0x4c, 0x4f, 0x57, 0x10, 0x06, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x48, 0x49, + 0x47, 0x48, 0x10, 0x07, 0x12, 0x24, 0x0a, 0x20, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, + 0x46, 0x49, 0x53, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x08, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x52, + 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x4b, 0x45, + 0x52, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x09, 0x12, 0x23, 0x0a, 0x1f, 0x54, + 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x4b, + 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x10, 0x0a, + 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x52, 0x41, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x55, 0x52, 0x45, + 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x0b, 0x12, 0x2f, 0x0a, 0x2b, 0x54, 0x52, + 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x52, + 0x41, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x44, + 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x0c, 0x12, 0x23, 0x0a, 0x1f, 0x54, + 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, + 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x0d, + 0x12, 0x2a, 0x0a, 0x26, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, + 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x0e, 0x12, 0x1a, 0x0a, 0x16, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, + 0x4e, 0x44, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x0f, 0x12, 0x1b, 0x0a, 0x17, 0x54, 0x52, 0x41, 0x4e, + 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4e, 0x44, 0x5f, 0x48, + 0x49, 0x47, 0x48, 0x10, 0x10, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x10, + 0x12, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x10, 0x13, 0x12, 0x1f, 0x0a, 0x1b, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, + 0x4e, 0x44, 0x5f, 0x53, 0x4c, 0x41, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x14, 0x12, 0x1f, 0x0a, + 0x1b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, + 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x50, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x10, 0x15, 0x12, 0x25, + 0x0a, 0x21, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x46, 0x55, 0x4e, 0x44, 0x53, 0x5f, 0x53, + 0x45, 0x4e, 0x44, 0x10, 0x16, 0x12, 0x2b, 0x0a, 0x27, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, + 0x46, 0x55, 0x4e, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, + 0x10, 0x17, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x45, 0x41, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, + 0x54, 0x10, 0x18, 0x12, 0x2c, 0x0a, 0x28, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x5f, + 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, + 0x19, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x53, 0x50, 0x4f, 0x54, 0x10, 0x1a, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x41, + 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4c, 0x44, 0x49, + 0x4e, 0x47, 0x5f, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x1b, 0x12, 0x21, 0x0a, 0x1d, 0x54, 0x52, 0x41, + 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x4c, 0x44, 0x49, + 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x1c, 0x12, 0x2e, 0x0a, 0x2a, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x55, + 0x43, 0x43, 0x45, 0x53, 0x53, 0x4f, 0x52, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x52, 0x41, 0x4e, 0x43, + 0x45, 0x5f, 0x46, 0x52, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x1d, 0x12, 0x28, 0x0a, 0x24, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, + 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, + 0x43, 0x41, 0x54, 0x45, 0x10, 0x1e, 0x12, 0x2e, 0x0a, 0x2a, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, + 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, + 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x4e, 0x45, 0x54, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, + 0x42, 0x55, 0x54, 0x45, 0x10, 0x1f, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, + 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c, 0x41, 0x5f, 0x50, 0x45, 0x4e, 0x41, + 0x4c, 0x54, 0x59, 0x5f, 0x42, 0x4f, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x59, 0x10, 0x20, + 0x12, 0x2a, 0x0a, 0x26, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x53, 0x4c, 0x41, 0x5f, 0x50, 0x45, 0x4e, 0x41, 0x4c, 0x54, 0x59, 0x5f, 0x4c, 0x50, + 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x59, 0x10, 0x21, 0x12, 0x2e, 0x0a, 0x2a, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x49, + 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x55, 0x4e, 0x50, 0x41, + 0x49, 0x44, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x10, 0x22, 0x12, 0x32, 0x0a, 0x2e, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c, + 0x41, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x42, 0x4f, + 0x4e, 0x55, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x23, + 0x12, 0x29, 0x0a, 0x25, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x50, 0x45, 0x52, 0x50, 0x45, 0x54, 0x55, 0x41, 0x4c, 0x53, 0x5f, 0x46, 0x55, 0x4e, + 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4c, 0x4f, 0x53, 0x53, 0x10, 0x24, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x50, 0x45, 0x54, 0x55, 0x41, 0x4c, 0x53, 0x5f, 0x46, 0x55, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, - 0x4c, 0x4f, 0x53, 0x53, 0x10, 0x24, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, - 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x50, 0x45, 0x54, 0x55, 0x41, - 0x4c, 0x53, 0x5f, 0x46, 0x55, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x57, 0x49, 0x4e, 0x10, 0x25, - 0x12, 0x20, 0x0a, 0x1c, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x53, 0x5f, 0x56, 0x45, 0x53, 0x54, 0x45, 0x44, - 0x10, 0x26, 0x12, 0x29, 0x0a, 0x25, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, + 0x57, 0x49, 0x4e, 0x10, 0x25, 0x12, 0x20, 0x0a, 0x1c, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, + 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x53, 0x5f, 0x56, + 0x45, 0x53, 0x54, 0x45, 0x44, 0x10, 0x26, 0x12, 0x29, 0x0a, 0x25, 0x54, 0x52, 0x41, 0x4e, 0x53, + 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x52, 0x45, 0x46, + 0x45, 0x52, 0x52, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x50, 0x41, 0x59, + 0x10, 0x27, 0x12, 0x30, 0x0a, 0x2c, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x52, 0x45, 0x46, 0x45, 0x52, 0x52, 0x45, 0x52, - 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x27, 0x12, 0x30, 0x0a, - 0x2c, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, - 0x45, 0x45, 0x5f, 0x52, 0x45, 0x46, 0x45, 0x52, 0x52, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x57, 0x41, - 0x52, 0x44, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x2c, 0x12, - 0x22, 0x0a, 0x1e, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4c, 0x4f, - 0x57, 0x10, 0x2d, 0x12, 0x23, 0x0a, 0x1f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, - 0x4e, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x2e, 0x12, 0x25, 0x0a, 0x21, 0x54, 0x52, 0x41, 0x4e, - 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, - 0x45, 0x44, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x2f, 0x12, - 0x26, 0x0a, 0x22, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, - 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x30, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x4d, 0x4d, 0x5f, 0x4c, 0x4f, 0x57, - 0x10, 0x31, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x41, 0x4d, 0x4d, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x32, 0x12, 0x1d, - 0x0a, 0x19, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x41, 0x4d, 0x4d, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x33, 0x12, 0x22, 0x0a, - 0x1e, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, - 0x52, 0x45, 0x41, 0x53, 0x55, 0x52, 0x59, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, - 0x34, 0x12, 0x22, 0x0a, 0x1e, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x42, 0x55, 0x59, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x5f, 0x46, 0x45, 0x45, 0x5f, - 0x50, 0x41, 0x59, 0x10, 0x35, 0x12, 0x2b, 0x0a, 0x27, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, - 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, 0x4d, 0x41, 0x4b, 0x45, - 0x52, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x52, 0x45, 0x42, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x41, 0x59, - 0x10, 0x36, 0x12, 0x2f, 0x0a, 0x2b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, 0x4d, 0x41, 0x4b, 0x45, 0x52, 0x5f, 0x46, - 0x45, 0x45, 0x5f, 0x52, 0x45, 0x42, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, - 0x45, 0x10, 0x37, 0x22, 0x04, 0x08, 0x03, 0x10, 0x03, 0x22, 0x04, 0x08, 0x11, 0x10, 0x11, 0x2a, - 0xb2, 0x03, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, - 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, - 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4d, 0x41, 0x4b, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, - 0x53, 0x5f, 0x50, 0x41, 0x49, 0x44, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x44, 0x49, 0x53, 0x50, + 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, + 0x54, 0x45, 0x10, 0x2c, 0x12, 0x22, 0x0a, 0x1e, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4d, 0x41, 0x52, 0x47, + 0x49, 0x4e, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x2d, 0x12, 0x23, 0x0a, 0x1f, 0x54, 0x52, 0x41, 0x4e, + 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, + 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x2e, 0x12, 0x25, 0x0a, + 0x21, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, + 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4c, + 0x4f, 0x57, 0x10, 0x2f, 0x12, 0x26, 0x0a, 0x22, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x4d, + 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x30, 0x12, 0x19, 0x0a, 0x15, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x4d, + 0x4d, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x31, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x52, 0x41, 0x4e, 0x53, + 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x4d, 0x4d, 0x5f, 0x48, 0x49, 0x47, + 0x48, 0x10, 0x32, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x4d, 0x4d, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, + 0x10, 0x33, 0x12, 0x22, 0x0a, 0x1e, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x45, 0x41, 0x53, 0x55, 0x52, 0x59, 0x5f, 0x46, 0x45, 0x45, + 0x5f, 0x50, 0x41, 0x59, 0x10, 0x34, 0x12, 0x22, 0x0a, 0x1e, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, + 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x55, 0x59, 0x5f, 0x42, 0x41, 0x43, 0x4b, + 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x35, 0x12, 0x2b, 0x0a, 0x27, 0x54, 0x52, + 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, + 0x5f, 0x4d, 0x41, 0x4b, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x52, 0x45, 0x42, 0x41, 0x54, + 0x45, 0x5f, 0x50, 0x41, 0x59, 0x10, 0x36, 0x12, 0x2f, 0x0a, 0x2b, 0x54, 0x52, 0x41, 0x4e, 0x53, + 0x46, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x5f, 0x4d, 0x41, + 0x4b, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x52, 0x45, 0x42, 0x41, 0x54, 0x45, 0x5f, 0x52, + 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x10, 0x37, 0x22, 0x04, 0x08, 0x03, 0x10, 0x03, 0x22, 0x04, + 0x08, 0x11, 0x10, 0x11, 0x2a, 0xb2, 0x03, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x50, 0x41, + 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4d, 0x41, 0x4b, 0x45, - 0x52, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x44, 0x10, - 0x02, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, - 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4c, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x43, - 0x45, 0x49, 0x56, 0x45, 0x44, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x44, 0x49, 0x53, 0x50, 0x41, - 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4d, 0x41, 0x52, 0x4b, 0x45, - 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x49, 0x53, - 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4c, - 0x41, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x10, 0x06, 0x12, 0x25, - 0x0a, 0x21, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, - 0x43, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x5f, 0x56, 0x4f, 0x4c, 0x41, 0x54, 0x49, 0x4c, - 0x49, 0x54, 0x59, 0x10, 0x07, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, - 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, - 0x4f, 0x52, 0x5f, 0x52, 0x41, 0x4e, 0x4b, 0x49, 0x4e, 0x47, 0x10, 0x08, 0x12, 0x23, 0x0a, 0x1f, + 0x52, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x5f, 0x50, 0x41, 0x49, 0x44, 0x10, 0x01, 0x12, 0x27, 0x0a, + 0x23, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, + 0x5f, 0x4d, 0x41, 0x4b, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x43, 0x45, + 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, + 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x4c, 0x50, 0x5f, 0x46, 0x45, 0x45, + 0x53, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x44, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, - 0x52, 0x45, 0x41, 0x4c, 0x49, 0x53, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x10, - 0x09, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, - 0x54, 0x52, 0x49, 0x43, 0x5f, 0x41, 0x56, 0x45, 0x52, 0x41, 0x47, 0x45, 0x5f, 0x4e, 0x4f, 0x54, - 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x0a, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x50, 0x41, - 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x45, 0x4c, 0x49, 0x47, 0x49, - 0x42, 0x4c, 0x45, 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x49, 0x45, 0x53, 0x10, 0x0b, 0x22, 0x04, - 0x08, 0x05, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x0b, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x63, - 0x6f, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x43, - 0x4f, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x43, 0x4f, 0x50, - 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x53, 0x10, 0x01, 0x12, - 0x16, 0x0a, 0x12, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, - 0x54, 0x45, 0x41, 0x4d, 0x53, 0x10, 0x02, 0x2a, 0xa7, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x64, 0x69, - 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x49, - 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, - 0x14, 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, - 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x4e, 0x44, 0x49, 0x56, - 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x5f, 0x54, - 0x45, 0x41, 0x4d, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, - 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4e, - 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x44, 0x49, 0x56, - 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x41, 0x4d, 0x4d, 0x10, - 0x04, 0x2a, 0xa9, 0x01, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, + 0x4d, 0x41, 0x52, 0x4b, 0x45, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x23, + 0x0a, 0x1f, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, + 0x43, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, + 0x4e, 0x10, 0x06, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, + 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x5f, 0x56, 0x4f, + 0x4c, 0x41, 0x54, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x07, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x49, + 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x41, 0x4e, 0x4b, 0x49, 0x4e, 0x47, 0x10, + 0x08, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, + 0x54, 0x52, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x49, 0x53, 0x45, 0x44, 0x5f, 0x52, 0x45, + 0x54, 0x55, 0x52, 0x4e, 0x10, 0x09, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, + 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x41, 0x56, 0x45, 0x52, 0x41, 0x47, + 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x0a, 0x12, 0x25, 0x0a, 0x21, + 0x44, 0x49, 0x53, 0x50, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, + 0x45, 0x4c, 0x49, 0x47, 0x49, 0x42, 0x4c, 0x45, 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x49, 0x45, + 0x53, 0x10, 0x0b, 0x22, 0x04, 0x08, 0x05, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x0b, 0x45, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x4e, 0x54, 0x49, + 0x54, 0x59, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, + 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, + 0x4c, 0x53, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x53, + 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x53, 0x10, 0x02, 0x2a, 0xa7, 0x01, 0x0a, + 0x0f, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x53, 0x63, 0x6f, 0x70, 0x65, + 0x12, 0x20, 0x0a, 0x1c, 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, + 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, + 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, + 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, + 0x5f, 0x49, 0x4e, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x49, 0x4e, + 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, 0x5f, 0x4e, + 0x4f, 0x54, 0x5f, 0x49, 0x4e, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, + 0x49, 0x4e, 0x44, 0x49, 0x56, 0x49, 0x44, 0x55, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x4f, 0x50, 0x45, + 0x5f, 0x41, 0x4d, 0x4d, 0x10, 0x04, 0x2a, 0xa9, 0x01, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, + 0x25, 0x0a, 0x21, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, + 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, + 0x50, 0x52, 0x4f, 0x5f, 0x52, 0x41, 0x54, 0x41, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x49, + 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, + 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x4b, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, - 0x45, 0x47, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x50, 0x52, 0x4f, 0x5f, 0x52, - 0x41, 0x54, 0x41, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, - 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, - 0x41, 0x4e, 0x4b, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, - 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, - 0x41, 0x4e, 0x4b, 0x5f, 0x4c, 0x4f, 0x54, 0x54, 0x45, 0x52, 0x59, 0x10, 0x03, 0x2a, 0x63, 0x0a, - 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x17, 0x4e, - 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x4f, 0x44, 0x45, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, - 0x52, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, - 0x10, 0x02, 0x2a, 0x59, 0x0a, 0x0b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x16, 0x0a, 0x12, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x50, 0x4f, 0x43, 0x48, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x02, 0x2a, 0xa7, 0x01, - 0x0a, 0x13, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x21, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, - 0x4f, 0x52, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, - 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x45, 0x4e, 0x44, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x54, - 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x5f, - 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x53, 0x41, - 0x54, 0x5a, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, - 0x52, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, - 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0x68, 0x0a, 0x0a, 0x4d, 0x61, 0x72, 0x67, 0x69, - 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, - 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x10, 0x01, - 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, - 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x10, - 0x02, 0x42, 0x27, 0x5a, 0x25, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x65, 0x67, 0x61, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x65, 0x67, 0x61, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x65, 0x67, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x4b, 0x5f, 0x4c, 0x4f, 0x54, 0x54, 0x45, 0x52, 0x59, + 0x10, 0x03, 0x2a, 0x63, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x1b, 0x0a, 0x17, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, + 0x15, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x56, 0x41, 0x4c, 0x49, + 0x44, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x02, 0x2a, 0x59, 0x0a, 0x0b, 0x45, 0x70, 0x6f, 0x63, 0x68, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, + 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x41, 0x43, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, + 0x45, 0x50, 0x4f, 0x43, 0x48, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x4e, 0x44, + 0x10, 0x02, 0x2a, 0xa7, 0x01, 0x0a, 0x13, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x21, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x24, 0x0a, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4e, + 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x45, 0x4e, 0x44, 0x45, + 0x52, 0x4d, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x45, 0x52, 0x53, 0x41, 0x54, 0x5a, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0x68, 0x0a, 0x0a, + 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x41, + 0x52, 0x47, 0x49, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x41, 0x52, 0x47, 0x49, + 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x4d, 0x41, 0x52, + 0x47, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x41, 0x52, 0x47, 0x49, 0x4e, 0x5f, + 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x53, 0x4f, 0x4c, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x41, + 0x52, 0x47, 0x49, 0x4e, 0x10, 0x02, 0x42, 0x27, 0x5a, 0x25, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x76, + 0x65, 0x67, 0x61, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x69, 0x6f, 0x2f, 0x76, + 0x65, 0x67, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x65, 0x67, 0x61, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( From 678f5c80cfeca49658ffb11a6082a89f120ab835 Mon Sep 17 00:00:00 2001 From: Jeremy Letang Date: Fri, 18 Oct 2024 16:05:29 +0100 Subject: [PATCH 2/7] feat: add test for vesting Signed-off-by: Jeremy Letang --- CHANGELOG.md | 6 ++- core/protocol/all_services.go | 2 +- core/vesting/engine.go | 2 + core/vesting/engine_test.go | 23 +++++++++++ core/vesting/helper_for_test.go | 68 ++++++++++++++++++--------------- core/vesting/snapshot.go | 3 +- 6 files changed, 69 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fad9e41364..382af696e97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,13 @@ - [11519](https://github.com/vegaprotocol/vega/issues/11519) - Add fees to position API types. - [11714](https://github.com/vegaprotocol/vega/issues/11714) - Improve `AMM` performance by caching best prices and volumes. - [11642](https://github.com/vegaprotocol/vega/issues/11642) - `AMMs` with empty price levels are now allowed. -- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added. +- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added. - [11732](https://github.com/vegaprotocol/vega/issues/11732) - `AMMs` can now have their base price automatically updated by a market's data source. +- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added. - [11726](https://github.com/vegaprotocol/vega/issues/11726) - Combined `AMM` uncrossing orders for better performance when uncrossing the book. - [11711](https://github.com/vegaprotocol/vega/issues/11711) - Manage closed team membership by updating the allow list. -- [11722](https://github.com/vegaprotocol/vega/issues/11722) - Expose active protocol automated purchase identifier in market data API. +- [11722](https://github.com/vegaprotocol/vega/issues/11722) - Expose active protocol automated purchase identifier in market data API. +- [11744](https://github.com/vegaprotocol/vega/issues/11744) - Staking from collateral bridged assets. ### 🐛 Fixes diff --git a/core/protocol/all_services.go b/core/protocol/all_services.go index 64695e4a8e0..80ebfe6b1a7 100644 --- a/core/protocol/all_services.go +++ b/core/protocol/all_services.go @@ -392,7 +392,7 @@ func newServices( svcs.activityStreak.OnEpochRestore, ) - svcs.vesting = vesting.NewSnapshotEngine(svcs.log, svcs.collateral, svcs.activityStreak, svcs.broker, svcs.assets, svcs.partiesEngine, svcs.timeService) + svcs.vesting = vesting.NewSnapshotEngine(svcs.log, svcs.collateral, svcs.activityStreak, svcs.broker, svcs.assets, svcs.partiesEngine, svcs.timeService, svcs.stakingAccounts) svcs.timeService.NotifyOnTick(svcs.vesting.OnTick) svcs.rewards = rewards.New(svcs.log, svcs.conf.Rewards, svcs.broker, svcs.delegation, svcs.epochService, svcs.collateral, svcs.timeService, svcs.marketActivityTracker, svcs.topology, svcs.vesting, svcs.banking, svcs.activityStreak) diff --git a/core/vesting/engine.go b/core/vesting/engine.go index e817da676c7..c9187fcf4b5 100644 --- a/core/vesting/engine.go +++ b/core/vesting/engine.go @@ -120,6 +120,7 @@ func New( assets Assets, parties Parties, t Time, + stakeAccounting StakeAccounting, ) *Engine { log = log.Named(namedLogger) @@ -133,6 +134,7 @@ func New( state: map[string]*PartyRewards{}, rewardBonusMultiplierCache: map[string]MultiplierAndQuantBalance{}, t: t, + stakeAccounting: stakeAccounting, } } diff --git a/core/vesting/engine_test.go b/core/vesting/engine_test.go index f6dbe22eeee..e215f483cab 100644 --- a/core/vesting/engine_test.go +++ b/core/vesting/engine_test.go @@ -18,6 +18,7 @@ package vesting_test import ( "context" "testing" + "time" "code.vegaprotocol.io/vega/core/assets" "code.vegaprotocol.io/vega/core/events" @@ -31,6 +32,28 @@ import ( "github.com/stretchr/testify/require" ) +func TestAutomatedStaking(t *testing.T) { + v := getTestEngine(t) + + ctx := context.Background() + + require.NoError(t, v.OnRewardVestingBaseRateUpdate(ctx, num.MustDecimalFromString("0.9"))) + require.NoError(t, v.OnRewardVestingMinimumTransferUpdate(ctx, num.MustDecimalFromString("1"))) + require.NoError(t, v.OnStakingAssetUpdate(ctx, "ETH")) + + // this is not the most useful test, but at least we know that on payment for the + // staking asset, the staking account are being notified of the amounts as new stake deposits + // via the calls to the mocks + t.Run("On add reward for the staking asset, stake accounting is called", func(t *testing.T) { + // one call to add event, and one call to broadcast it + // one for the time + v.stakeAccounting.EXPECT().AddEvent(gomock.Any(), gomock.Any()).Times(1) + v.broker.EXPECT().Send(gomock.Any()).Times(1) + v.t.EXPECT().GetTimeNow().Times(1).Return(time.Unix(0, 0)) + v.AddReward(ctx, "party1", "ETH", num.NewUint(1000), 0) + }) +} + func TestDistributeAfterDelay(t *testing.T) { v := getTestEngine(t) diff --git a/core/vesting/helper_for_test.go b/core/vesting/helper_for_test.go index 44c6bfd0fe6..1fc159aeebc 100644 --- a/core/vesting/helper_for_test.go +++ b/core/vesting/helper_for_test.go @@ -40,13 +40,14 @@ import ( type testEngine struct { *vesting.Engine - ctrl *gomock.Controller - col *collateralMock - asvm *mocks.MockActivityStreakVestingMultiplier - broker *bmocks.MockBroker - assets *mocks.MockAssets - parties *mocks.MockParties - t *mocks.MockTime + ctrl *gomock.Controller + col *collateralMock + asvm *mocks.MockActivityStreakVestingMultiplier + broker *bmocks.MockBroker + assets *mocks.MockAssets + parties *mocks.MockParties + t *mocks.MockTime + stakeAccounting *mocks.MockStakeAccounting } func getTestEngine(t *testing.T) *testEngine { @@ -59,31 +60,34 @@ func getTestEngine(t *testing.T) *testEngine { assets := mocks.NewMockAssets(ctrl) parties := mocks.NewMockParties(ctrl) tim := mocks.NewMockTime(ctrl) + stakeAccounting := mocks.NewMockStakeAccounting(ctrl) return &testEngine{ Engine: vesting.New( - logger, col, asvm, broker, assets, parties, tim, + logger, col, asvm, broker, assets, parties, tim, stakeAccounting, ), - ctrl: ctrl, - broker: broker, - col: col, - asvm: asvm, - assets: assets, - parties: parties, - t: tim, + ctrl: ctrl, + broker: broker, + col: col, + asvm: asvm, + assets: assets, + parties: parties, + t: tim, + stakeAccounting: stakeAccounting, } } type testSnapshotEngine struct { engine *vesting.SnapshotEngine - ctrl *gomock.Controller - col *collateralMock - asvm *mocks.MockActivityStreakVestingMultiplier - broker *bmocks.MockBroker - assets *mocks.MockAssets - parties *mocks.MockParties - t *mocks.MockTime + ctrl *gomock.Controller + col *collateralMock + asvm *mocks.MockActivityStreakVestingMultiplier + broker *bmocks.MockBroker + assets *mocks.MockAssets + parties *mocks.MockParties + t *mocks.MockTime + stakeAccounting *mocks.MockStakeAccounting currentEpoch uint64 } @@ -97,19 +101,21 @@ func newEngine(t *testing.T) *testSnapshotEngine { assets := mocks.NewMockAssets(ctrl) parties := mocks.NewMockParties(ctrl) tim := mocks.NewMockTime(ctrl) + stakeAccounting := mocks.NewMockStakeAccounting(ctrl) return &testSnapshotEngine{ engine: vesting.NewSnapshotEngine( - logging.NewTestLogger(), col, asvm, broker, assets, parties, tim, + logging.NewTestLogger(), col, asvm, broker, assets, parties, tim, stakeAccounting, ), - ctrl: ctrl, - col: col, - asvm: asvm, - broker: broker, - assets: assets, - parties: parties, - currentEpoch: 10, - t: tim, + ctrl: ctrl, + col: col, + asvm: asvm, + broker: broker, + assets: assets, + parties: parties, + currentEpoch: 10, + t: tim, + stakeAccounting: stakeAccounting, } } diff --git a/core/vesting/snapshot.go b/core/vesting/snapshot.go index 15adcd1e3a1..55087c4dbcb 100644 --- a/core/vesting/snapshot.go +++ b/core/vesting/snapshot.go @@ -42,9 +42,10 @@ func NewSnapshotEngine( assets Assets, parties Parties, t Time, + stakeAccounting StakeAccounting, ) *SnapshotEngine { se := &SnapshotEngine{ - Engine: New(log, c, asvm, broker, assets, parties, t), + Engine: New(log, c, asvm, broker, assets, parties, t, stakeAccounting), } return se From caca0537942d190706c546833470eb4210c037a4 Mon Sep 17 00:00:00 2001 From: Jeremy Letang Date: Fri, 18 Oct 2024 16:39:06 +0100 Subject: [PATCH 3/7] chore: add tests for banking Signed-off-by: Jeremy Letang --- core/banking/engine.go | 1 + core/banking/mocks/mocks.go | 15 ++ core/banking/oneoff_transfers_test.go | 194 ++++++++++++++++++++++++++ core/banking/transfers_common.go | 7 +- core/collateral/engine.go | 6 + core/types/banking.go | 43 ++++-- 6 files changed, 251 insertions(+), 15 deletions(-) diff --git a/core/banking/engine.go b/core/banking/engine.go index 318c1bdae50..8fa89f903c0 100644 --- a/core/banking/engine.go +++ b/core/banking/engine.go @@ -72,6 +72,7 @@ type Collateral interface { Withdraw(ctx context.Context, party, asset string, amount *num.Uint) (*types.LedgerMovement, error) EnableAsset(ctx context.Context, asset types.Asset) error GetPartyGeneralAccount(party, asset string) (*types.Account, error) + GetPartyLockedForStaking(party, asset string) (*types.Account, error) GetPartyVestedRewardAccount(partyID, asset string) (*types.Account, error) TransferFunds(ctx context.Context, transfers []*types.Transfer, diff --git a/core/banking/mocks/mocks.go b/core/banking/mocks/mocks.go index edbdd08013e..725282d7fed 100644 --- a/core/banking/mocks/mocks.go +++ b/core/banking/mocks/mocks.go @@ -213,6 +213,21 @@ func (mr *MockCollateralMockRecorder) GetPartyGeneralAccount(arg0, arg1 interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartyGeneralAccount", reflect.TypeOf((*MockCollateral)(nil).GetPartyGeneralAccount), arg0, arg1) } +// GetPartyLockedForStaking mocks base method. +func (m *MockCollateral) GetPartyLockedForStaking(arg0, arg1 string) (*types.Account, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPartyLockedForStaking", arg0, arg1) + ret0, _ := ret[0].(*types.Account) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPartyLockedForStaking indicates an expected call of GetPartyLockedForStaking. +func (mr *MockCollateralMockRecorder) GetPartyLockedForStaking(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartyLockedForStaking", reflect.TypeOf((*MockCollateral)(nil).GetPartyLockedForStaking), arg0, arg1) +} + // GetPartyVestedRewardAccount mocks base method. func (m *MockCollateral) GetPartyVestedRewardAccount(arg0, arg1 string) (*types.Account, error) { m.ctrl.T.Helper() diff --git a/core/banking/oneoff_transfers_test.go b/core/banking/oneoff_transfers_test.go index 9e8fd7d91bb..6af856b9381 100644 --- a/core/banking/oneoff_transfers_test.go +++ b/core/banking/oneoff_transfers_test.go @@ -35,6 +35,7 @@ func TestTransfers(t *testing.T) { t.Run("onefoff not enough funds to transfer", testOneOffTransferNotEnoughFundsToTransfer) t.Run("onefoff invalid transfers", testOneOffTransferInvalidTransfers) t.Run("valid oneoff transfer", testValidOneOffTransfer) + t.Run("valid staking transfers", testStakingTransfers) t.Run("valid oneoff with deliverOn", testValidOneOffTransferWithDeliverOn) t.Run("valid oneoff with deliverOn in the past is done straight away", testValidOneOffTransferWithDeliverOnInThePastStraightAway) t.Run("rejected if doesn't reach minimal amount", testRejectedIfDoesntReachMinimalAmount) @@ -273,6 +274,199 @@ func testValidOneOffTransfer(t *testing.T) { assert.NoError(t, e.TransferFunds(ctx, transfer)) } +func testStakingTransfers(t *testing.T) { + e := getTestEngine(t) + + // let's do a massive fee, easy to test + e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(1)) + e.OnStakingAsset(context.Background(), "ETH") + + ctx := context.Background() + + t.Run("cannot transfer to another pubkey lock_for_staking", func(t *testing.T) { + transfer := &types.TransferFunds{ + Kind: types.TransferCommandKindOneOff, + OneOff: &types.OneOffTransfer{ + TransferBase: &types.TransferBase{ + From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + FromAccountType: types.AccountTypeGeneral, + To: "10ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + ToAccountType: types.AccountTypeLockedForStaking, + Asset: assetNameETH, + Amount: num.NewUint(10), + Reference: "someref", + }, + }, + } + + // asset exists + e.assets.EXPECT().Get(gomock.Any()).Times(1).Return( + assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) + e.broker.EXPECT().Send(gomock.Any()).Times(1) + assert.EqualError(t, e.TransferFunds(ctx, transfer), "transfers to locked for staking allowed only from own general account") + }) + + t.Run("cannot transfer from lock_for_staking to another general account", func(t *testing.T) { + transfer := &types.TransferFunds{ + Kind: types.TransferCommandKindOneOff, + OneOff: &types.OneOffTransfer{ + TransferBase: &types.TransferBase{ + From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + FromAccountType: types.AccountTypeLockedForStaking, + To: "10ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + ToAccountType: types.AccountTypeGeneral, + Asset: assetNameETH, + Amount: num.NewUint(10), + Reference: "someref", + }, + }, + } + + // asset exists + e.assets.EXPECT().Get(gomock.Any()).Times(1).Return( + assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) + e.broker.EXPECT().Send(gomock.Any()).Times(1) + assert.EqualError(t, e.TransferFunds(ctx, transfer), "transfers from locked for staking allowed only to own general account") + }) + + t.Run("can only transfer from lock_for_staking to own general account", func(t *testing.T) { + transfer := &types.TransferFunds{ + Kind: types.TransferCommandKindOneOff, + OneOff: &types.OneOffTransfer{ + TransferBase: &types.TransferBase{ + From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + FromAccountType: types.AccountTypeLockedForStaking, + To: "0000000000000000000000000000000000000000000000000000000000000000", + ToAccountType: types.AccountTypeGlobalReward, + Asset: assetNameETH, + Amount: num.NewUint(10), + Reference: "someref", + }, + }, + } + + // asset exists + e.assets.EXPECT().Get(gomock.Any()).Times(1).Return( + assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) + e.broker.EXPECT().Send(gomock.Any()).Times(1) + assert.EqualError(t, e.TransferFunds(ctx, transfer), "can only transfer from locked for staking to general account") + }) + + t.Run("can transfer from general to locked_for_staking and emit stake deposited", func(t *testing.T) { + transfer := &types.TransferFunds{ + Kind: types.TransferCommandKindOneOff, + OneOff: &types.OneOffTransfer{ + TransferBase: &types.TransferBase{ + From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + FromAccountType: types.AccountTypeGeneral, + To: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + ToAccountType: types.AccountTypeLockedForStaking, + Asset: assetNameETH, + Amount: num.NewUint(10), + Reference: "someref", + }, + }, + } + + fromAcc := types.Account{ + Balance: num.NewUint(100), + } + + // asset exists + e.assets.EXPECT().Get(gomock.Any()).Times(1).Return( + assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) + e.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).Times(1).Return(&fromAcc, nil) + + // assert the calculation of fees and transfer request are correct + e.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + + e.broker.EXPECT().Send(gomock.Any()).Times(4) + + // expect a call to the stake accounting + e.stakeAccounting.EXPECT().AddEvent(gomock.Any(), gomock.Any()).Times(1).Do( + func(_ context.Context, evt *types.StakeLinking) { + assert.Equal(t, evt.Type, types.StakeLinkingTypeDeposited) + }) + assert.NoError(t, e.TransferFunds(ctx, transfer)) + }) + + t.Run("can transfer from locked_for_staking to general and emit stake removed", func(t *testing.T) { + transfer := &types.TransferFunds{ + Kind: types.TransferCommandKindOneOff, + OneOff: &types.OneOffTransfer{ + TransferBase: &types.TransferBase{ + From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + FromAccountType: types.AccountTypeLockedForStaking, + To: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + ToAccountType: types.AccountTypeGeneral, + Asset: assetNameETH, + Amount: num.NewUint(10), + Reference: "someref", + }, + }, + } + + fromAcc := types.Account{ + Balance: num.NewUint(100), + } + + // asset exists + e.assets.EXPECT().Get(gomock.Any()).Times(1).Return( + assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) + e.col.EXPECT().GetPartyLockedForStaking(gomock.Any(), gomock.Any()).Times(1).Return(&fromAcc, nil) + + // assert the calculation of fees and transfer request are correct + e.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + + e.broker.EXPECT().Send(gomock.Any()).Times(4) + + // expect a call to the stake accounting + e.stakeAccounting.EXPECT().AddEvent(gomock.Any(), gomock.Any()).Times(1).Do( + func(_ context.Context, evt *types.StakeLinking) { + assert.Equal(t, evt.Type, types.StakeLinkingTypeRemoved) + }) + assert.NoError(t, e.TransferFunds(ctx, transfer)) + }) + + t.Run("can transfer from vested to general and emit stake removed", func(t *testing.T) { + transfer := &types.TransferFunds{ + Kind: types.TransferCommandKindOneOff, + OneOff: &types.OneOffTransfer{ + TransferBase: &types.TransferBase{ + From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + FromAccountType: types.AccountTypeVestedRewards, + To: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301", + ToAccountType: types.AccountTypeGeneral, + Asset: assetNameETH, + Amount: num.NewUint(10), + Reference: "someref", + }, + }, + } + + fromAcc := types.Account{ + Balance: num.NewUint(100), + } + + // asset exists + e.assets.EXPECT().Get(gomock.Any()).Times(1).Return( + assets.NewAsset(&mockAsset{name: assetNameETH, quantum: num.DecimalFromFloat(100)}), nil) + e.col.EXPECT().GetPartyVestedRewardAccount(gomock.Any(), gomock.Any()).Times(1).Return(&fromAcc, nil) + + // assert the calculation of fees and transfer request are correct + e.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + + e.broker.EXPECT().Send(gomock.Any()).Times(4) + + // expect a call to the stake accounting + e.stakeAccounting.EXPECT().AddEvent(gomock.Any(), gomock.Any()).Times(1).Do( + func(_ context.Context, evt *types.StakeLinking) { + assert.Equal(t, evt.Type, types.StakeLinkingTypeRemoved) + }) + assert.NoError(t, e.TransferFunds(ctx, transfer)) + }) +} + func testValidOneOffTransferWithDeliverOnInThePastStraightAway(t *testing.T) { e := getTestEngine(t) diff --git a/core/banking/transfers_common.go b/core/banking/transfers_common.go index b75e16ac2e7..cd7a7dfb5d7 100644 --- a/core/banking/transfers_common.go +++ b/core/banking/transfers_common.go @@ -279,7 +279,7 @@ func (e *Engine) makeFeeTransferForFundsTransfer( } switch fromAccountType { - case types.AccountTypeGeneral, types.AccountTypeVestedRewards: + case types.AccountTypeGeneral, types.AccountTypeVestedRewards, types.AccountTypeLockedForStaking: default: e.log.Panic("from account not supported", logging.String("account-type", fromAccountType.String()), @@ -332,6 +332,11 @@ func (e *Engine) ensureEnoughFundsForTransfer( if err != nil { return err } + case types.AccountTypeLockedForStaking: + account, err = e.col.GetPartyLockedForStaking(from, asset.ID) + if err != nil { + return err + } case types.AccountTypeVestedRewards: // sending from sub account to owners general account if fromDerivedKey != nil { diff --git a/core/collateral/engine.go b/core/collateral/engine.go index 655fa042228..31a943b6a72 100644 --- a/core/collateral/engine.go +++ b/core/collateral/engine.go @@ -4091,6 +4091,12 @@ func (e *Engine) GetPartyGeneralAccount(partyID, asset string) (*types.Account, return e.GetAccountByID(generalID) } +// GetPartyLockedForStaking returns a general account given the partyID. +func (e *Engine) GetPartyLockedForStaking(partyID, asset string) (*types.Account, error) { + generalID := e.accountID(noMarket, partyID, asset, types.AccountTypeLockedForStaking) + return e.GetAccountByID(generalID) +} + // GetPartyBondAccount returns a general account given the partyID. func (e *Engine) GetPartyBondAccount(market, partyID, asset string) (*types.Account, error) { id := e.accountID( diff --git a/core/types/banking.go b/core/types/banking.go index 763a1ec29fe..596a0dd7705 100644 --- a/core/types/banking.go +++ b/core/types/banking.go @@ -45,18 +45,21 @@ const ( ) var ( - ErrMissingTransferKind = errors.New("missing transfer kind") - ErrCannotTransferZeroFunds = errors.New("cannot transfer zero funds") - ErrInvalidFromAccount = errors.New("invalid from account") - ErrInvalidFromDerivedKey = errors.New("invalid from derived key") - ErrInvalidToAccount = errors.New("invalid to account") - ErrUnsupportedFromAccountType = errors.New("unsupported from account type") - ErrUnsupportedToAccountType = errors.New("unsupported to account type") - ErrEndEpochIsZero = errors.New("end epoch is zero") - ErrStartEpochIsZero = errors.New("start epoch is zero") - ErrInvalidFactor = errors.New("invalid factor") - ErrStartEpochAfterEndEpoch = errors.New("start epoch after end epoch") - ErrInvalidToForRewardAccountType = errors.New("to party is invalid for reward account type") + ErrMissingTransferKind = errors.New("missing transfer kind") + ErrCannotTransferZeroFunds = errors.New("cannot transfer zero funds") + ErrInvalidFromAccount = errors.New("invalid from account") + ErrInvalidFromDerivedKey = errors.New("invalid from derived key") + ErrInvalidToAccount = errors.New("invalid to account") + ErrUnsupportedFromAccountType = errors.New("unsupported from account type") + ErrUnsupportedToAccountType = errors.New("unsupported to account type") + ErrEndEpochIsZero = errors.New("end epoch is zero") + ErrStartEpochIsZero = errors.New("start epoch is zero") + ErrInvalidFactor = errors.New("invalid factor") + ErrStartEpochAfterEndEpoch = errors.New("start epoch after end epoch") + ErrInvalidToForRewardAccountType = errors.New("to party is invalid for reward account type") + ErrTransferFromLockedForStakingAllowedOnlyToOwnGeneralAccount = errors.New("transfers from locked for staking allowed only to own general account") + ErrTransferToLockedForStakingAllowedOnlyFromOwnGeneralAccount = errors.New("transfers to locked for staking allowed only from own general account") + ErrCanOnlyTransferFromLockedForStakingToGeneralAccount = errors.New("can only transfer from locked for staking to general account") ) type TransferCommandKind int @@ -93,6 +96,18 @@ func (t *TransferBase) IsValid() error { return ErrCannotTransferZeroFunds } + if t.FromAccountType == AccountTypeLockedForStaking && t.ToAccountType != AccountTypeGeneral { + return ErrCanOnlyTransferFromLockedForStakingToGeneralAccount + } + + if t.FromAccountType == AccountTypeGeneral && t.ToAccountType == AccountTypeLockedForStaking && t.From != t.To { + return ErrTransferToLockedForStakingAllowedOnlyFromOwnGeneralAccount + } + + if t.ToAccountType == AccountTypeGeneral && t.FromAccountType == AccountTypeLockedForStaking && t.From != t.To { + return ErrTransferFromLockedForStakingAllowedOnlyToOwnGeneralAccount + } + // check for derived account transfer if t.FromDerivedKey != nil { if !vgcrypto.IsValidVegaPubKey(*t.FromDerivedKey) { @@ -110,7 +125,7 @@ func (t *TransferBase) IsValid() error { // check for any other transfers switch t.FromAccountType { - case AccountTypeGeneral, AccountTypeVestedRewards /*, AccountTypeLockedForStaking*/ : + case AccountTypeGeneral, AccountTypeVestedRewards, AccountTypeLockedForStaking: break default: return ErrUnsupportedFromAccountType @@ -122,7 +137,7 @@ func (t *TransferBase) IsValid() error { return ErrInvalidToForRewardAccountType } case AccountTypeGeneral, AccountTypeLPFeeReward, AccountTypeMakerReceivedFeeReward, AccountTypeMakerPaidFeeReward, AccountTypeMarketProposerReward, - AccountTypeAverageNotionalReward, AccountTypeRelativeReturnReward, AccountTypeValidatorRankingReward, AccountTypeReturnVolatilityReward, AccountTypeRealisedReturnReward, AccountTypeEligibleEntitiesReward, AccountTypeBuyBackFees: /*, AccountTypeLockedForStaking*/ + AccountTypeAverageNotionalReward, AccountTypeRelativeReturnReward, AccountTypeValidatorRankingReward, AccountTypeReturnVolatilityReward, AccountTypeRealisedReturnReward, AccountTypeEligibleEntitiesReward, AccountTypeBuyBackFees, AccountTypeLockedForStaking: break default: return ErrUnsupportedToAccountType From cad4951e6c554d8924a9c7b5502f334ed1f62559 Mon Sep 17 00:00:00 2001 From: Jeremy Letang Date: Fri, 18 Oct 2024 16:58:42 +0100 Subject: [PATCH 4/7] chore: add tests for banking Signed-off-by: Jeremy Letang --- core/integration/setup_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/integration/setup_test.go b/core/integration/setup_test.go index a2164c0fdc4..40cbe20b30b 100644 --- a/core/integration/setup_test.go +++ b/core/integration/setup_test.go @@ -233,7 +233,7 @@ func newExecutionTestSetup() *executionTestSetup { execsetup.activityStreak = activitystreak.New(execsetup.log, execsetup.executionEngine, execsetup.broker) execsetup.epochEngine.NotifyOnEpoch(execsetup.activityStreak.OnEpochEvent, execsetup.activityStreak.OnEpochRestore) - execsetup.vesting = vesting.New(execsetup.log, execsetup.collateralEngine, execsetup.activityStreak, execsetup.broker, execsetup.assetsEngine, execsetup.profilesEngine, execsetup.timeService) + execsetup.vesting = vesting.New(execsetup.log, execsetup.collateralEngine, execsetup.activityStreak, execsetup.broker, execsetup.assetsEngine, execsetup.profilesEngine, execsetup.timeService, execsetup.stakingAccount) execsetup.rewardsEngine = rewards.New(execsetup.log, rewards.NewDefaultConfig(), execsetup.broker, execsetup.delegationEngine, execsetup.epochEngine, execsetup.collateralEngine, execsetup.timeService, execsetup.marketActivityTracker, execsetup.topology, execsetup.vesting, execsetup.banking, execsetup.activityStreak) // register this after the rewards engine is created to make sure the on epoch is called in the right order. From ef17f312cf98f056d0a4d62553755dc61f5af484 Mon Sep 17 00:00:00 2001 From: Jeremy Letang Date: Sat, 19 Oct 2024 11:11:12 +0100 Subject: [PATCH 5/7] chore: Apply suggestions from code review Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> --- datanode/gateway/graphql/schema.graphql | 2 +- protos/sources/vega/vega.proto | 2 +- protos/vega/vega.pb.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/datanode/gateway/graphql/schema.graphql b/datanode/gateway/graphql/schema.graphql index 5ed40d1bb5a..f6b280496ed 100644 --- a/datanode/gateway/graphql/schema.graphql +++ b/datanode/gateway/graphql/schema.graphql @@ -4707,7 +4707,7 @@ enum AccountType { ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL "Reward account for the eligible entities metric" ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES - "Locked for staking" + "Account for assets that are locked for staking." ACCOUNT_TYPE_LOCKED_FOR_STAKING } diff --git a/protos/sources/vega/vega.proto b/protos/sources/vega/vega.proto index f161d02bf06..77d7d078688 100644 --- a/protos/sources/vega/vega.proto +++ b/protos/sources/vega/vega.proto @@ -971,7 +971,7 @@ enum AccountType { // Reward account for the eligible entities metric. ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES = 33; - // Locked for staking. + // Account for assets that are locked for staking. ACCOUNT_TYPE_LOCKED_FOR_STAKING = 34; // Note: If adding an enum value, add a matching entry in: diff --git a/protos/vega/vega.pb.go b/protos/vega/vega.pb.go index 86a7bc8cf83..df71b08d6cd 100644 --- a/protos/vega/vega.pb.go +++ b/protos/vega/vega.pb.go @@ -761,7 +761,7 @@ const ( AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL AccountType = 32 // Reward account for the eligible entities metric. AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES AccountType = 33 - // Locked for staking. + // Account for assets that are locked for staking. AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING AccountType = 34 ) From 8e4733bbeb17517dbc1ab9c1c278f18d635843fb Mon Sep 17 00:00:00 2001 From: wwestgarth Date: Tue, 22 Oct 2024 09:08:10 +0100 Subject: [PATCH 6/7] fix: include new lock-for-staking account as an allowable account in collateral TransferFunds --- core/collateral/engine.go | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/core/collateral/engine.go b/core/collateral/engine.go index 31a943b6a72..8aac500128c 100644 --- a/core/collateral/engine.go +++ b/core/collateral/engine.go @@ -2832,6 +2832,17 @@ func (e *Engine) getTransferFundsTransferRequest(ctx context.Context, t *types.T // we always pay onto the pending transfers accounts toAcc = e.GetPendingTransfersAccount(t.Amount.Asset) + case types.AccountTypeLockedForStaking: + fromAcc, err = e.GetPartyLockedForStaking(t.Owner, t.Amount.Asset) + if err != nil { + return nil, fmt.Errorf("account does not exists: %v, %v, %v", + accountType, t.Owner, t.Amount.Asset, + ) + } + + // we always pay onto the pending transfers accounts + toAcc = e.GetPendingTransfersAccount(t.Amount.Asset) + case types.AccountTypeVestedRewards: fromAcc = e.GetOrCreatePartyVestedRewardAccount(ctx, t.Owner, t.Amount.Asset) // we always pay onto the pending transfers accounts @@ -2861,6 +2872,21 @@ func (e *Engine) getTransferFundsTransferRequest(ctx context.Context, t *types.T } } + case types.AccountTypeLockedForStaking: + toAcc, err = e.GetPartyLockedForStaking(t.Owner, t.Amount.Asset) + if err != nil { + // account does not exists, let's just create it + id, err := e.CreatePartyLockedForStakingAccount(ctx, t.Owner, t.Amount.Asset) + if err != nil { + return nil, err + } + toAcc, err = e.GetAccountByID(id) + if err != nil { + // shouldn't happen, we just created it... + return nil, err + } + } + // this could not exists as well, let's just create in this case case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, types.AccountTypeNetworkTreasury, types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward, @@ -2918,6 +2944,13 @@ func (e *Engine) getTransferFundsFeesTransferRequest(ctx context.Context, t *typ accountType, t.Owner, t.Amount.Asset, ) } + case types.AccountTypeLockedForStaking: + fromAcc, err = e.GetPartyLockedForStaking(t.Owner, t.Amount.Asset) + if err != nil { + return nil, fmt.Errorf("account does not exists: %v, %v, %v", + accountType, t.Owner, t.Amount.Asset, + ) + } case types.AccountTypeVestedRewards: fromAcc = e.GetOrCreatePartyVestedRewardAccount(ctx, t.Owner, t.Amount.Asset) @@ -4137,6 +4170,32 @@ func (e *Engine) CreatePartyGeneralAccount(ctx context.Context, partyID, asset s return generalID, nil } +// CreatePartyLockedForStakingAccount create the general account for a party. +func (e *Engine) CreatePartyLockedForStakingAccount(ctx context.Context, partyID, asset string) (string, error) { + if !e.AssetExists(asset) { + return "", ErrInvalidAssetID + } + + lockedForStakingID := e.accountID(noMarket, partyID, asset, types.AccountTypeLockedForStaking) + if _, ok := e.accs[lockedForStakingID]; !ok { + acc := types.Account{ + ID: lockedForStakingID, + Asset: asset, + MarketID: noMarket, + Balance: num.UintZero(), + Owner: partyID, + Type: types.AccountTypeLockedForStaking, + } + e.accs[lockedForStakingID] = &acc + e.addPartyAccount(partyID, lockedForStakingID, &acc) + e.addAccountToHashableSlice(&acc) + e.broker.Send(events.NewPartyEvent(ctx, types.Party{Id: partyID})) + e.broker.Send(events.NewAccountEvent(ctx, acc)) + } + + return lockedForStakingID, nil +} + // GetOrCreatePartyVestingRewardAccount create the general account for a party. func (e *Engine) GetOrCreatePartyVestingRewardAccount(ctx context.Context, partyID, asset string) *types.Account { if !e.AssetExists(asset) { From cc1cdafbcb30173cb7023d434a537f213c840c7c Mon Sep 17 00:00:00 2001 From: Jeremy Letang Date: Tue, 22 Oct 2024 11:08:27 +0100 Subject: [PATCH 7/7] fix: avoid paying fee with staking related transfers Signed-off-by: Jeremy Letang --- core/banking/fee_estimate.go | 8 ++++++-- core/banking/oneoff_transfers.go | 4 ++-- core/banking/recurring_transfers.go | 2 +- core/banking/transfers_common.go | 12 ++++++++---- core/types/staking.go | 6 +++++- core/vesting/engine.go | 2 +- datanode/api/trading_data_v2.go | 2 ++ 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/core/banking/fee_estimate.go b/core/banking/fee_estimate.go index 0c2d94d45b6..c738da26fba 100644 --- a/core/banking/fee_estimate.go +++ b/core/banking/fee_estimate.go @@ -31,9 +31,10 @@ func EstimateFee( fromAccountType types.AccountType, fromDerivedKey *string, to string, + toAccountType types.AccountType, ) (fee *num.Uint, discount *num.Uint) { tFee := calculateFeeForTransfer(assetQuantum, maxQuantumAmount, transferFeeFactor, amount, from, - fromAccountType, fromDerivedKey, to) + fromAccountType, fromDerivedKey, to, toAccountType) return calculateDiscount(accumulatedDiscount, tFee) } @@ -46,12 +47,15 @@ func calculateFeeForTransfer( fromAccountType types.AccountType, fromDerivedKey *string, to string, + toAccountType types.AccountType, ) *num.Uint { feeAmount := num.UintZero() // no fee for Vested account // either from owner's vested to their general account or from derived key vested to owner's general account - if fromAccountType == types.AccountTypeVestedRewards && (from == to || fromDerivedKey != nil) { + if (fromAccountType == types.AccountTypeVestedRewards && (from == to || fromDerivedKey != nil)) || + (fromAccountType == types.AccountTypeLockedForStaking && from == to) || + (fromAccountType == types.AccountTypeGeneral && toAccountType == types.AccountTypeLockedForStaking && from == to) { return feeAmount } diff --git a/core/banking/oneoff_transfers.go b/core/banking/oneoff_transfers.go index 9531c506610..732e1cc2e6f 100644 --- a/core/banking/oneoff_transfers.go +++ b/core/banking/oneoff_transfers.go @@ -93,7 +93,7 @@ func (e *Engine) updateStakingAccounts( BlockHeight: height, BlockTime: now, LogIndex: 1, - EthereumAddress: transfer.From, + EthereumAddress: "", } } @@ -112,7 +112,7 @@ func (e *Engine) updateStakingAccounts( BlockHeight: height, BlockTime: now, LogIndex: 1, - EthereumAddress: transfer.From, + EthereumAddress: "", } } diff --git a/core/banking/recurring_transfers.go b/core/banking/recurring_transfers.go index db70b0da861..3bbc0c9ad6c 100644 --- a/core/banking/recurring_transfers.go +++ b/core/banking/recurring_transfers.go @@ -321,7 +321,7 @@ func (e *Engine) distributeRecurringTransfers(ctx context.Context, newEpoch uint ) } else { // check if the amount + fees can be covered by the party issuing the transfer - if err = e.ensureFeeForTransferFunds(a, amount, v.From, v.FromAccountType, v.FromDerivedKey, v.To); err == nil { + if err = e.ensureFeeForTransferFunds(a, amount, v.From, v.FromAccountType, v.FromDerivedKey, v.To, v.ToAccountType); err == nil { // NB: if the metric is market value we're going to transfer the bonus if any directly // to the market account of the asset/reward type - this is similar to previous behaviour and // different to how all other metric based rewards behave. The reason is that we need the context of the funder diff --git a/core/banking/transfers_common.go b/core/banking/transfers_common.go index cd7a7dfb5d7..a5390f62e11 100644 --- a/core/banking/transfers_common.go +++ b/core/banking/transfers_common.go @@ -95,7 +95,7 @@ func (e *Engine) CheckTransfer(t *types.TransferBase) error { return err } - if err = e.ensureFeeForTransferFunds(a, t.Amount, t.From, t.FromAccountType, t.FromDerivedKey, t.To); err != nil { + if err = e.ensureFeeForTransferFunds(a, t.Amount, t.From, t.FromAccountType, t.FromDerivedKey, t.To, t.ToAccountType); err != nil { return fmt.Errorf("could not transfer funds, %w", err) } return nil @@ -166,7 +166,7 @@ func (e *Engine) processTransfer( // ensure the party have enough funds for both the // amount and the fee for the transfer - feeTransfer, discount, err := e.makeFeeTransferForFundsTransfer(ctx, assetType, amount, from, fromAcc, fromDerivedKey, to) + feeTransfer, discount, err := e.makeFeeTransferForFundsTransfer(ctx, assetType, amount, from, fromAcc, fromDerivedKey, to, toAcc) if err != nil { return nil, fmt.Errorf("could not pay the fee for transfer: %w", err) } @@ -249,6 +249,7 @@ func (e *Engine) calculateFeeTransferForTransfer( fromAccountType types.AccountType, fromDerivedKey *string, to string, + toAccountType types.AccountType, ) *num.Uint { return calculateFeeForTransfer( asset.Details.Quantum, @@ -259,6 +260,7 @@ func (e *Engine) calculateFeeTransferForTransfer( fromAccountType, fromDerivedKey, to, + toAccountType, ) } @@ -270,8 +272,9 @@ func (e *Engine) makeFeeTransferForFundsTransfer( fromAccountType types.AccountType, fromDerivedKey *string, to string, + toAccountType types.AccountType, ) (*types.Transfer, *num.Uint, error) { - theoreticalFee := e.calculateFeeTransferForTransfer(asset, amount, from, fromAccountType, fromDerivedKey, to) + theoreticalFee := e.calculateFeeTransferForTransfer(asset, amount, from, fromAccountType, fromDerivedKey, to, toAccountType) feeAmount, discountAmount := e.ApplyFeeDiscount(ctx, asset.ID, from, theoreticalFee) if err := e.ensureEnoughFundsForTransfer(asset, amount, from, fromAccountType, fromDerivedKey, feeAmount); err != nil { @@ -306,9 +309,10 @@ func (e *Engine) ensureFeeForTransferFunds( fromAccountType types.AccountType, fromDerivedKey *string, to string, + toAccountType types.AccountType, ) error { assetType := asset.ToAssetType() - theoreticalFee := e.calculateFeeTransferForTransfer(assetType, amount, from, fromAccountType, fromDerivedKey, to) + theoreticalFee := e.calculateFeeTransferForTransfer(assetType, amount, from, fromAccountType, fromDerivedKey, to, toAccountType) feeAmount, _ := e.EstimateFeeDiscount(assetType.ID, from, theoreticalFee) return e.ensureEnoughFundsForTransfer(assetType, amount, from, fromAccountType, fromDerivedKey, feeAmount) } diff --git a/core/types/staking.go b/core/types/staking.go index 21e52a26d7a..87eb77fb2ce 100644 --- a/core/types/staking.go +++ b/core/types/staking.go @@ -141,6 +141,10 @@ func (s *StakeLinking) IntoProto() *eventspb.StakeLinking { func StakeLinkingFromProto(sl *eventspb.StakeLinking) *StakeLinking { amt, _ := num.UintFromString(sl.Amount, 10) + var ethereumAddress string + if len(sl.EthereumAddress) > 0 { + ethereumAddress = crypto.EthereumChecksumAddress(sl.EthereumAddress) + } return &StakeLinking{ ID: sl.Id, Type: sl.Type, @@ -153,7 +157,7 @@ func StakeLinkingFromProto(sl *eventspb.StakeLinking) *StakeLinking { BlockHeight: sl.BlockHeight, BlockTime: sl.BlockTime, LogIndex: sl.LogIndex, - EthereumAddress: crypto.EthereumChecksumAddress(sl.EthereumAddress), + EthereumAddress: ethereumAddress, } } diff --git a/core/vesting/engine.go b/core/vesting/engine.go index c9187fcf4b5..e30c7759580 100644 --- a/core/vesting/engine.go +++ b/core/vesting/engine.go @@ -217,7 +217,7 @@ func (e *Engine) updateStakingAccount( BlockHeight: height, BlockTime: now, LogIndex: logIndex, - EthereumAddress: party, + EthereumAddress: "", } e.stakeAccounting.AddEvent(context.Background(), stakeLinking) diff --git a/datanode/api/trading_data_v2.go b/datanode/api/trading_data_v2.go index d8de10a837c..b273fcefb15 100644 --- a/datanode/api/trading_data_v2.go +++ b/datanode/api/trading_data_v2.go @@ -5728,6 +5728,8 @@ func (t *TradingDataServiceV2) EstimateTransferFee(ctx context.Context, req *v2. req.FromAccountType, req.FromAmmKey, req.ToAccount, + // irrelvant here + vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED, ) return &v2.EstimateTransferFeeResponse{