code.vegaprotocol.io/vega@v0.79.0/vegatools/checkpoint/type.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 checkpoint 17 18 import ( 19 "bytes" 20 "encoding/hex" 21 "encoding/json" 22 "fmt" 23 "strings" 24 "time" 25 26 "code.vegaprotocol.io/vega/core/types" 27 "code.vegaprotocol.io/vega/protos/vega" 28 checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1" 29 events "code.vegaprotocol.io/vega/protos/vega/events/v1" 30 31 "github.com/golang/protobuf/jsonpb" 32 "github.com/golang/protobuf/proto" 33 "golang.org/x/crypto/sha3" 34 "google.golang.org/protobuf/reflect/protoreflect" 35 ) 36 37 type all struct { 38 messages map[string]proto.Message 39 } 40 41 func newAll() *all { 42 return &all{ 43 messages: map[string]proto.Message{ 44 "governance": &checkpoint.Proposals{}, 45 "assets": &checkpoint.Assets{}, 46 "collateral": &checkpoint.Collateral{}, 47 "network_parameters": &checkpoint.NetParams{}, 48 "delegation": &checkpoint.Delegate{}, 49 "epoch": &events.EpochEvent{}, 50 "block": &checkpoint.Block{}, 51 "rewards": &checkpoint.Rewards{}, 52 "banking": &checkpoint.Banking{}, 53 "validators": &checkpoint.Validators{}, 54 "staking": &checkpoint.Staking{}, 55 "multisig_control": &checkpoint.MultisigControl{}, 56 "market_tracker": &checkpoint.MarketTracker{}, 57 "execution": &checkpoint.ExecutionState{}, 58 }, 59 } 60 } 61 62 type allJSON map[string]json.RawMessage 63 64 // AssetErr a convenience error type. 65 type AssetErr []error 66 67 func (a *all) CheckAssetsCollateral() error { 68 assets, ok := a.messages["assets"].(*checkpoint.Assets) 69 if !ok { 70 return fmt.Errorf("assets not found") 71 } 72 73 assetIDSet := make(map[string]struct{}, len(assets.Assets)) 74 for _, ass := range assets.Assets { 75 assetIDSet[ass.Id] = struct{}{} 76 } 77 78 cAssets := make(map[string]struct{}, len(assetIDSet)) // should be no more than total assets 79 for _, c := range a.messages["collateral"].(*checkpoint.Collateral).Balances { 80 cAssets[c.Asset] = struct{}{} 81 } 82 83 var errs []error 84 85 for ca := range cAssets { 86 if _, ok := assetIDSet[ca]; !ok { 87 errs = append(errs, fmt.Errorf("collateral contains '%s' asset, asset checkpoint does not", ca)) 88 } 89 } 90 91 if len(errs) != 0 { 92 return AssetErr(errs) 93 } 94 95 return nil 96 } 97 98 func (a *all) JSON() ([]byte, error) { 99 // format nicely 100 marshaler := jsonpb.Marshaler{ 101 Indent: " ", 102 } 103 104 allJsn := allJSON{} 105 106 for k, v := range a.messages { 107 var buf bytes.Buffer 108 if err := marshaler.Marshal(&buf, v); err != nil { 109 return nil, err 110 } 111 allJsn[k] = buf.Bytes() 112 } 113 114 b, err := json.MarshalIndent(allJsn, "", " ") 115 if err != nil { 116 return nil, err 117 } 118 return b, nil 119 } 120 121 // fromJSON can be used in the future to load JSON input and generate a checkpoint file. 122 func fromJSON(in []byte) (*all, error) { 123 allJsn := allJSON{} 124 if err := json.Unmarshal(in, &allJsn); err != nil { 125 return nil, err 126 } 127 128 a := newAll() 129 130 for k, v := range allJsn { 131 reader := bytes.NewReader(v) 132 if err := jsonpb.Unmarshal(reader, a.messages[k]); err != nil { 133 return nil, err 134 } 135 } 136 137 return a, nil 138 } 139 140 // hash returns the hash for a checkpoint (from core repo - needs to be kept in sync). 141 func hash(data []byte) string { 142 h := sha3.New256() 143 _, _ = h.Write(data) 144 return hex.EncodeToString(h.Sum(nil)) 145 } 146 147 func allBytes(cp *checkpoint.Checkpoint) []byte { 148 buf := types.NewCheckpointFromProto(cp).HashBytes() 149 return buf.Bytes() 150 } 151 152 func (a *all) CheckpointData() ([]byte, string, error) { 153 if len(a.messages) == 0 { 154 return nil, "", fmt.Errorf("no checkpoint data found") 155 } 156 157 cp := &checkpoint.Checkpoint{ 158 Governance: []byte{0}, 159 Assets: []byte{0}, 160 Collateral: []byte{0}, 161 NetworkParameters: []byte{0}, 162 Delegation: []byte{0}, 163 Epoch: []byte{0}, 164 Block: []byte{0}, 165 Rewards: []byte{0}, 166 Banking: []byte{0}, 167 Validators: []byte{0}, 168 Staking: []byte{0}, 169 MultisigControl: []byte{0}, 170 MarketTracker: []byte{0}, 171 } 172 cp.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { 173 name := string(fd.Name()) 174 v, ok := a.messages[name] 175 if !ok { 176 return true 177 } 178 msg, err := proto.Marshal(v) 179 if err != nil { 180 return true 181 } 182 cp.ProtoReflect().Set(fd, protoreflect.ValueOf(msg)) 183 return true 184 }) 185 186 ret, err := proto.Marshal(cp) 187 if err != nil { 188 return nil, "", err 189 } 190 191 if len(ret) == 0 { 192 return nil, "", fmt.Errorf("failed to parse checkpoint data") 193 } 194 195 return ret, hash(allBytes(cp)), nil 196 } 197 198 // Error outputs the mismatches in an easy to read way. 199 func (a AssetErr) Error() string { 200 out := make([]string, 0, len(a)+1) 201 out = append(out, "unexpected asset/collateral data found:") 202 for _, e := range a { 203 out = append(out, fmt.Sprintf("\t%s", e.Error())) 204 } 205 return strings.Join(out, "\n") 206 } 207 208 func dummy() *all { 209 ae := &checkpoint.AssetEntry{ 210 Id: "ETH", 211 AssetDetails: &vega.AssetDetails{ 212 Name: "ETH", 213 Symbol: "ETH", 214 Decimals: 5, 215 Quantum: "", 216 Source: &vega.AssetDetails_BuiltinAsset{ 217 BuiltinAsset: &vega.BuiltinAsset{ 218 MaxFaucetAmountMint: "100000000000", 219 }, 220 }, 221 }, 222 } 223 bal := &checkpoint.AssetBalance{ 224 Party: "deadbeef007", 225 Asset: "ETH", 226 Balance: "1000000", 227 } 228 prop := &vega.Proposal{ 229 Id: "prop-1", 230 Reference: "dummy-proposal", 231 PartyId: "deadbeef007", 232 State: vega.Proposal_STATE_ENACTED, 233 Timestamp: time.Now().Add(-1 * time.Hour).Unix(), 234 Terms: &vega.ProposalTerms{ 235 ClosingTimestamp: time.Now().Add(24 * time.Hour).Unix(), 236 EnactmentTimestamp: time.Now().Add(-10 * time.Minute).Unix(), 237 ValidationTimestamp: time.Now().Add(-1*time.Hour - time.Second).Unix(), 238 Change: &vega.ProposalTerms_NewMarket{ 239 NewMarket: &vega.NewMarket{ 240 Changes: &vega.NewMarketConfiguration{ 241 Instrument: &vega.InstrumentConfiguration{ 242 Name: "ETH/FOO", 243 Code: "bar", 244 Product: &vega.InstrumentConfiguration_Future{ 245 Future: &vega.FutureProduct{ // omitted oracle spec for now 246 SettlementAsset: "ETH", 247 QuoteName: "ETH", 248 }, 249 }, 250 }, 251 DecimalPlaces: 5, 252 PriceMonitoringParameters: &vega.PriceMonitoringParameters{ 253 Triggers: []*vega.PriceMonitoringTrigger{ 254 { 255 Horizon: 10, 256 Probability: "0.95", 257 AuctionExtension: 10, 258 }, 259 }, 260 }, 261 LiquidityMonitoringParameters: &vega.LiquidityMonitoringParameters{ 262 TargetStakeParameters: &vega.TargetStakeParameters{ 263 TimeWindow: 10, 264 ScalingFactor: 0.7, 265 }, 266 }, 267 RiskParameters: &vega.NewMarketConfiguration_LogNormal{ 268 LogNormal: &vega.LogNormalRiskModel{ 269 RiskAversionParameter: 0.1, 270 Tau: 0.2, 271 Params: &vega.LogNormalModelParams{ 272 Mu: 0.3, 273 R: 0.3, 274 Sigma: 0.3, 275 }, 276 }, 277 }, 278 LiquiditySlaParameters: &vega.LiquiditySLAParameters{ 279 PriceRange: "0.95", 280 CommitmentMinTimeFraction: "0.5", 281 PerformanceHysteresisEpochs: 4, 282 SlaCompetitionFactor: "0.5", 283 }, 284 }, 285 }, 286 }, 287 }, 288 } 289 del := &checkpoint.Delegate{ 290 Active: []*checkpoint.DelegateEntry{ 291 { 292 Party: "deadbeef007", 293 Node: "node0", 294 Amount: "100", 295 EpochSeq: 0, 296 }, 297 }, 298 Pending: []*checkpoint.DelegateEntry{ 299 { 300 Party: "deadbeef007", 301 Node: "node0", 302 Amount: "100", 303 Undelegate: true, 304 EpochSeq: 1, 305 }, 306 }, 307 AutoDelegation: []string{ 308 "deadbeef007", 309 }, 310 } 311 t := time.Now() 312 return &all{ 313 messages: map[string]proto.Message{ 314 "assets": &checkpoint.Assets{ 315 Assets: []*checkpoint.AssetEntry{ae}, 316 }, 317 "collateral": &checkpoint.Collateral{ 318 Balances: []*checkpoint.AssetBalance{bal}, 319 }, 320 "governance": &checkpoint.Proposals{ 321 Proposals: []*vega.Proposal{prop}, 322 }, 323 "network_parameters": &checkpoint.NetParams{ 324 Params: []*vega.NetworkParameter{ 325 { 326 Key: "foo", 327 Value: "bar", 328 }, 329 }, 330 }, 331 "delegation": del, 332 "epoch": &events.EpochEvent{ 333 Seq: 0, 334 Action: vega.EpochAction_EPOCH_ACTION_START, 335 StartTime: t.UnixNano(), 336 ExpireTime: t.Add(24 * time.Hour).UnixNano(), 337 EndTime: t.Add(25 * time.Hour).UnixNano(), 338 }, 339 "block": &checkpoint.Block{ 340 Height: 1, 341 }, 342 "banking": &checkpoint.Banking{ 343 RecurringTransfers: &checkpoint.RecurringTransfers{ 344 RecurringTransfers: []*events.Transfer{ 345 { 346 Id: "someid", 347 From: "somefrom", 348 FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, 349 To: "someto", 350 ToAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, 351 Asset: "someasset", 352 Amount: "100", 353 Reference: "someref", 354 Status: events.Transfer_STATUS_PENDING, 355 Kind: &events.Transfer_Recurring{ 356 Recurring: &events.RecurringTransfer{ 357 StartEpoch: 10, 358 EndEpoch: func() *uint64 { e := uint64(100); return &e }(), 359 Factor: "1", 360 }, 361 }, 362 }, 363 }, 364 }, 365 }, 366 }, 367 } 368 }