code.vegaprotocol.io/vega@v0.79.0/core/parties/engine.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package parties 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "slices" 23 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/core/types" 26 "code.vegaprotocol.io/vega/libs/num" 27 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 28 29 "golang.org/x/exp/maps" 30 ) 31 32 var ( 33 ErrAliasIsReserved = errors.New("this alias is reserved") 34 ReservedPartyAliases = []string{"network"} 35 ) 36 37 type Engine struct { 38 broker Broker 39 40 // profiles tracks all parties profiles by party ID. 41 profiles map[types.PartyID]*types.PartyProfile 42 minBalanceToUpdateProfile *num.Uint 43 } 44 45 func (e *Engine) OnMinBalanceForUpdatePartyProfileUpdated(_ context.Context, min *num.Uint) error { 46 e.minBalanceToUpdateProfile = min.Clone() 47 return nil 48 } 49 50 func (e *Engine) AssignDeriveKey(ctx context.Context, party types.PartyID, derivedKey string) { 51 if _, ok := e.profiles[party]; !ok { 52 e.profiles[party] = &types.PartyProfile{ 53 PartyID: party, 54 Metadata: map[string]string{}, 55 DerivedKeys: map[string]struct{}{}, 56 } 57 } 58 59 e.profiles[party].DerivedKeys[derivedKey] = struct{}{} 60 } 61 62 func (e *Engine) CheckDerivedKeyOwnership(party types.PartyID, derivedKey string) bool { 63 partyProfile, ok := e.profiles[party] 64 if !ok { 65 return false 66 } 67 68 _, ok = partyProfile.DerivedKeys[derivedKey] 69 return ok 70 } 71 72 // RelatedKeys returns all keys related to the specified key. 73 // If a derived key is provided, it returns all other derived keys and the party key. 74 // If a party key is provided, it returns all derived keys and the party key itself. 75 // The keys will be in an indeterminate order. 76 func (e *Engine) RelatedKeys(key string) (*types.PartyID, []string) { 77 profile, ok := e.profiles[types.PartyID(key)] 78 if ok { 79 return &profile.PartyID, maps.Keys(profile.DerivedKeys) 80 } 81 82 for _, profile := range e.profiles { 83 if _, ok := profile.DerivedKeys[key]; ok { 84 return &profile.PartyID, maps.Keys(profile.DerivedKeys) 85 } 86 } 87 88 return nil, nil 89 } 90 91 func (e *Engine) CheckSufficientBalanceToUpdateProfile(party types.PartyID, balance *num.Uint) error { 92 if balance.LT(e.minBalanceToUpdateProfile) { 93 return fmt.Errorf("party %q does not have sufficient balance to update profile code, required balance %s available balance %s", party, e.minBalanceToUpdateProfile.String(), balance.String()) 94 } 95 return nil 96 } 97 98 func (e *Engine) UpdateProfile(ctx context.Context, partyID types.PartyID, cmd *commandspb.UpdatePartyProfile) error { 99 if err := e.validateProfileUpdate(partyID, cmd); err != nil { 100 return fmt.Errorf("invalid profile update: %w", err) 101 } 102 103 profile, exists := e.profiles[partyID] 104 if !exists { 105 profile = &types.PartyProfile{ 106 PartyID: partyID, 107 DerivedKeys: map[string]struct{}{}, 108 } 109 e.profiles[partyID] = profile 110 } 111 112 profile.Alias = cmd.Alias 113 114 profile.Metadata = map[string]string{} 115 for _, m := range cmd.Metadata { 116 profile.Metadata[m.Key] = m.Value 117 } 118 119 e.notifyProfileUpdate(ctx, profile) 120 121 return nil 122 } 123 124 func (e *Engine) loadPartiesFromSnapshot(partiesPayload *types.PayloadParties) { 125 for _, profilePayload := range partiesPayload.Profiles { 126 profile := &types.PartyProfile{ 127 PartyID: types.PartyID(profilePayload.PartyId), 128 Alias: profilePayload.Alias, 129 } 130 131 profile.Metadata = map[string]string{} 132 for _, m := range profilePayload.Metadata { 133 profile.Metadata[m.Key] = m.Value 134 } 135 136 profile.DerivedKeys = map[string]struct{}{} 137 for _, val := range profilePayload.DerivedKeys { 138 profile.DerivedKeys[val] = struct{}{} 139 } 140 141 e.profiles[profile.PartyID] = profile 142 } 143 } 144 145 func (e *Engine) validateProfileUpdate(partyID types.PartyID, cmd *commandspb.UpdatePartyProfile) error { 146 if err := e.ensureAliasUniqueness(partyID, cmd.Alias); err != nil { 147 return err 148 } 149 150 return nil 151 } 152 153 func (e *Engine) ensureAliasUniqueness(partyID types.PartyID, newAlias string) error { 154 if newAlias == "" { 155 return nil 156 } 157 158 if slices.Contains(ReservedPartyAliases, newAlias) { 159 return ErrAliasIsReserved 160 } 161 162 for _, profile := range e.profiles { 163 if partyID != profile.PartyID && profile.Alias == newAlias { 164 return fmt.Errorf("alias %q is already taken", newAlias) 165 } 166 } 167 168 return nil 169 } 170 171 func (e *Engine) notifyProfileUpdate(ctx context.Context, profile *types.PartyProfile) { 172 e.broker.Send(events.NewPartyProfileUpdatedEvent(ctx, profile)) 173 } 174 175 func NewEngine(broker Broker) *Engine { 176 engine := &Engine{ 177 broker: broker, 178 179 profiles: map[types.PartyID]*types.PartyProfile{}, 180 minBalanceToUpdateProfile: num.UintZero(), 181 } 182 183 return engine 184 }