github.com/mavryk-network/mvgo@v1.19.9/rpc/rights.go (about) 1 // Copyright (c) 2020-2024 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 package rpc 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/json" 10 "fmt" 11 "strconv" 12 "time" 13 14 "github.com/mavryk-network/mvgo/mavryk" 15 ) 16 17 // BakingRight holds information about the right to bake a specific Tezos block. 18 type BakingRight struct { 19 Delegate mavryk.Address `json:"delegate"` 20 Level int64 `json:"level"` 21 Priority int `json:"priority"` // until v011 22 Round int `json:"round"` // v012+ 23 EstimatedTime time.Time `json:"estimated_time"` 24 } 25 26 func (r BakingRight) Address() mavryk.Address { 27 return r.Delegate 28 } 29 30 // EndorsingRight holds information about the right to endorse a specific Tezos block. 31 type EndorsingRight struct { 32 Delegate mavryk.Address `json:"delegate"` 33 Level int64 `json:"level"` 34 EstimatedTime time.Time `json:"estimated_time"` 35 Slots []int `json:"slots,omitempty"` // until v011 36 FirstSlot int `json:"first_slot"` // v012+ 37 EndorsingPower int `json:"endorsing_power"` // v012+ 38 AttestationPower int `json:"attestation_power"` // v019+ 39 } 40 41 func (r EndorsingRight) Address() mavryk.Address { 42 return r.Delegate 43 } 44 45 func (r EndorsingRight) Power() int { 46 return r.EndorsingPower + len(r.Slots) 47 } 48 49 type RollSnapshotInfo struct { 50 LastRoll []string `json:"last_roll"` 51 Nonces []string `json:"nonces"` 52 RandomSeed string `json:"random_seed"` 53 RollSnapshot int `json:"roll_snapshot"` 54 } 55 56 type StakeInfo struct { 57 ActiveStake int64 `json:"active_stake,string"` 58 Baker mavryk.Address `json:"baker"` 59 } 60 61 // v012+ 62 type StakingSnapshotInfo struct { 63 Nonces []string `json:"nonces"` 64 RandomSeed string `json:"random_seed"` 65 BakerStake []StakeInfo `json:"selected_stake_distribution,omitempty"` 66 TotalActiveStake int64 `json:"total_active_stake,string"` 67 // SlashedDeposits []?? `json:"slashed_deposits"` 68 } 69 70 type SnapshotIndex struct { 71 Cycle int64 // the requested cycle that contains rights from the snapshot 72 Base int64 // the cycle where the snapshot happened 73 Index int // the index inside base where snapshot happened 74 } 75 76 type SnapshotRoll struct { 77 RollId int64 78 OwnerKey mavryk.Key 79 } 80 81 func (r *SnapshotRoll) UnmarshalJSON(data []byte) error { 82 if len(data) == 0 || bytes.Equal(data, null) { 83 return nil 84 } 85 if len(data) == 2 { 86 return nil 87 } 88 if data[0] != '[' || data[len(data)-1] != ']' { 89 return fmt.Errorf("SnapshotRoll: invalid json array '%s'", string(data)) 90 } 91 dec := json.NewDecoder(bytes.NewReader(data)) 92 dec.UseNumber() 93 unpacked := make([]interface{}, 0) 94 err := dec.Decode(&unpacked) 95 if err != nil { 96 return err 97 } 98 return r.decode(unpacked) 99 } 100 101 func (r SnapshotRoll) MarshalJSON() ([]byte, error) { 102 buf := make([]byte, 0, 2048) 103 buf = append(buf, '[') 104 buf = strconv.AppendInt(buf, r.RollId, 10) 105 buf = append(buf, ',') 106 buf = strconv.AppendQuote(buf, r.OwnerKey.String()) 107 buf = append(buf, ']') 108 return buf, nil 109 } 110 111 func (r *SnapshotRoll) decode(unpacked []interface{}) error { 112 if l := len(unpacked); l != 2 { 113 return fmt.Errorf("SnapshotRoll: invalid json array len %d", l) 114 } 115 id, err := strconv.ParseInt(unpacked[0].(json.Number).String(), 10, 64) 116 if err != nil { 117 return fmt.Errorf("SnapshotRoll: invalid roll id: %v", err) 118 } 119 if err = r.OwnerKey.UnmarshalText([]byte(unpacked[1].(string))); err != nil { 120 return err 121 } 122 r.RollId = id 123 return nil 124 } 125 126 type SnapshotOwners struct { 127 Cycle int64 `json:"cycle"` 128 Index int64 `json:"index"` 129 Rolls []SnapshotRoll `json:"rolls"` 130 } 131 132 // ListBakingRights returns information about baking rights at block id. 133 // Use max to set a max block priority (before Ithaca) or a max round (after Ithaca). 134 func (c *Client) ListBakingRights(ctx context.Context, id BlockID, max int) ([]BakingRight, error) { 135 p, err := c.GetParams(ctx, id) 136 if err != nil { 137 return nil, err 138 } 139 maxSelector := "max_priority=%d" 140 if p.Version >= 12 { 141 maxSelector = "max_round=%d" 142 } 143 if p.Version < 6 { 144 max++ 145 } 146 rights := make([]BakingRight, 0) 147 u := fmt.Sprintf("chains/main/blocks/%s/helpers/baking_rights?all=true&"+maxSelector, id, max) 148 if err := c.Get(ctx, u, &rights); err != nil { 149 return nil, err 150 } 151 return rights, nil 152 } 153 154 // ListBakingRightsCycle returns information about baking rights for an entire cycle 155 // as seen from block id. Note block and cycle must be no further than preserved cycles 156 // away from each other. Use max to set a max block priority (before Ithaca) or a max 157 // round (after Ithaca). 158 func (c *Client) ListBakingRightsCycle(ctx context.Context, id BlockID, cycle int64, max int) ([]BakingRight, error) { 159 p, err := c.GetParams(ctx, id) 160 if err != nil { 161 return nil, err 162 } 163 maxSelector := "max_priority=%d" 164 if p.Version >= 12 { 165 maxSelector = "max_round=%d" 166 } 167 if p.Version < 6 { 168 max++ 169 } 170 rights := make([]BakingRight, 0) 171 u := fmt.Sprintf("chains/main/blocks/%s/helpers/baking_rights?all=true&cycle=%d&"+maxSelector, id, cycle, max) 172 if err := c.Get(ctx, u, &rights); err != nil { 173 return nil, err 174 } 175 return rights, nil 176 } 177 178 // ListEndorsingRights returns information about block endorsing rights. 179 func (c *Client) ListEndorsingRights(ctx context.Context, id BlockID) ([]EndorsingRight, error) { 180 p, err := c.GetParams(ctx, id) 181 if err != nil { 182 return nil, err 183 } 184 rights := make([]EndorsingRight, 0) 185 // Note: future cycles are seen from current protocol (!) 186 switch { 187 case p.Version >= 19: 188 u := fmt.Sprintf("chains/main/blocks/%s/helpers/attestation_rights?all=true", id) 189 type V19Rights struct { 190 Level int64 `json:"level"` 191 Delegates []EndorsingRight `json:"delegates"` 192 EstimatedTime time.Time `json:"estimated_time"` 193 } 194 v19rights := make([]V19Rights, 0, 1) 195 if err := c.Get(ctx, u, &v19rights); err != nil { 196 return nil, err 197 } 198 for _, v := range v19rights { 199 for _, r := range v.Delegates { 200 r.Level = v.Level 201 r.EstimatedTime = v.EstimatedTime 202 rights = append(rights, r) 203 } 204 } 205 206 case p.Version >= 12: 207 u := fmt.Sprintf("chains/main/blocks/%s/helpers/endorsing_rights?all=true", id) 208 type V12Rights struct { 209 Level int64 `json:"level"` 210 Delegates []EndorsingRight `json:"delegates"` 211 EstimatedTime time.Time `json:"estimated_time"` 212 } 213 v12rights := make([]V12Rights, 0, 1) 214 if err := c.Get(ctx, u, &v12rights); err != nil { 215 return nil, err 216 } 217 for _, v := range v12rights { 218 for _, r := range v.Delegates { 219 r.Level = v.Level 220 r.EstimatedTime = v.EstimatedTime 221 rights = append(rights, r) 222 } 223 } 224 225 default: 226 u := fmt.Sprintf("chains/main/blocks/%s/helpers/endorsing_rights?all=true", id) 227 if err := c.Get(ctx, u, &rights); err != nil { 228 return nil, err 229 } 230 } 231 return rights, nil 232 } 233 234 // ListEndorsingRightsCycle returns information about endorsing rights for an entire cycle 235 // as seen from block id. Note block and cycle must be no further than preserved cycles 236 // away. 237 func (c *Client) ListEndorsingRightsCycle(ctx context.Context, id BlockID, cycle int64) ([]EndorsingRight, error) { 238 p, err := c.GetParams(ctx, id) 239 if err != nil { 240 return nil, err 241 } 242 rights := make([]EndorsingRight, 0) 243 // Note: future cycles are seen from current protocol (!) 244 switch { 245 case p.Version >= 19: 246 u := fmt.Sprintf("chains/main/blocks/%s/helpers/attestation_rights?all=true&cycle=%d", id, cycle) 247 type V19Rights struct { 248 Level int64 `json:"level"` 249 Delegates []EndorsingRight `json:"delegates"` 250 EstimatedTime time.Time `json:"estimated_time"` 251 } 252 v19rights := make([]V19Rights, 0, 8192) 253 if err := c.Get(ctx, u, &v19rights); err != nil { 254 return nil, err 255 } 256 for _, v := range v19rights { 257 for _, r := range v.Delegates { 258 r.Level = v.Level 259 r.EstimatedTime = v.EstimatedTime 260 rights = append(rights, r) 261 } 262 } 263 case p.Version >= 12: 264 u := fmt.Sprintf("chains/main/blocks/%s/helpers/endorsing_rights?all=true&cycle=%d", id, cycle) 265 type V12Rights struct { 266 Level int64 `json:"level"` 267 Delegates []EndorsingRight `json:"delegates"` 268 EstimatedTime time.Time `json:"estimated_time"` 269 } 270 v12rights := make([]V12Rights, 0, 8192) 271 if err := c.Get(ctx, u, &v12rights); err != nil { 272 return nil, err 273 } 274 for _, v := range v12rights { 275 for _, r := range v.Delegates { 276 r.Level = v.Level 277 r.EstimatedTime = v.EstimatedTime 278 rights = append(rights, r) 279 } 280 } 281 default: 282 u := fmt.Sprintf("chains/main/blocks/%s/helpers/endorsing_rights?all=true&cycle=%d", id, cycle) 283 if err := c.Get(ctx, u, &rights); err != nil { 284 return nil, err 285 } 286 } 287 return rights, nil 288 } 289 290 // GetRollSnapshotInfoCycle returns information about a roll snapshot as seen from block id. 291 // Note block and cycle must be no further than preserved cycles away. 292 func (c *Client) GetRollSnapshotInfoCycle(ctx context.Context, id BlockID, cycle int64) (*RollSnapshotInfo, error) { 293 idx := &RollSnapshotInfo{} 294 u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/cycle/%d", id, cycle) 295 if err := c.Get(ctx, u, idx); err != nil { 296 return nil, err 297 } 298 if idx.RandomSeed == "" { 299 return nil, fmt.Errorf("missing snapshot for cycle %d at block %s", cycle, id) 300 } 301 return idx, nil 302 } 303 304 // GetStakingSnapshotInfoCycle returns information about a roll snapshot as seen from block id. 305 // Note block and cycle must be no further than preserved cycles away. 306 func (c *Client) GetStakingSnapshotInfoCycle(ctx context.Context, id BlockID, cycle int64) (*StakingSnapshotInfo, error) { 307 idx := &StakingSnapshotInfo{} 308 u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/cycle/%d", id, cycle) 309 if err := c.Get(ctx, u, idx); err != nil { 310 return nil, err 311 } 312 return idx, nil 313 } 314 315 // GetSnapshotIndexCycle returns information about a roll or staking snapshot that 316 // produced rights at cycle. 317 // Note block and cycle must be no further than preserved cycles away. 318 func (c *Client) GetSnapshotIndexCycle(ctx context.Context, id BlockID, cycle int64) (*SnapshotIndex, error) { 319 p, err := c.GetParams(ctx, id) 320 if err != nil { 321 return nil, err 322 } 323 idx := &SnapshotIndex{} 324 switch { 325 case p.Version >= 19: 326 // no more snapshots 327 idx.Cycle = cycle 328 idx.Base = p.SnapshotBaseCycle(cycle) 329 idx.Index = 15 330 case p.Version >= 12: 331 idx.Cycle = cycle 332 idx.Base = p.SnapshotBaseCycle(cycle) 333 idx.Index = -1 334 if cycle >= p.ConsensusRightsDelay+1 { 335 u := fmt.Sprintf("chains/main/blocks/%s/context/selected_snapshot?cycle=%d", id, cycle) 336 if err := c.Get(ctx, u, &idx.Index); err != nil { 337 return nil, err 338 } 339 } else { 340 c.Log.Warnf("No snapshot for cycle %d", cycle) 341 } 342 default: 343 // pre-Ithaca we can at most look PRESERVED_CYCLES into the future since 344 // the snapshot happened 2 cycles back from the block we're looking from. 345 var info RollSnapshotInfo 346 u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/cycle/%d", id, cycle) 347 if err := c.Get(ctx, u, &info); err != nil { 348 return nil, err 349 } 350 if info.RandomSeed == "" { 351 return nil, fmt.Errorf("missing snapshot for cycle %d at block %s", cycle, id) 352 } 353 idx.Cycle = cycle 354 idx.Base = p.SnapshotBaseCycle(cycle) 355 idx.Index = info.RollSnapshot 356 } 357 return idx, nil 358 } 359 360 // ListSnapshotRollOwners returns information about a roll snapshot ownership. 361 // Response is a nested array `[[roll_id, pubkey]]`. Deprecated in Ithaca. 362 func (c *Client) ListSnapshotRollOwners(ctx context.Context, id BlockID, cycle, index int64) (*SnapshotOwners, error) { 363 owners := &SnapshotOwners{Cycle: cycle, Index: index} 364 u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/rolls/owner/snapshot/%d/%d?depth=1", id, cycle, index) 365 if err := c.Get(ctx, u, &owners.Rolls); err != nil { 366 return nil, err 367 } 368 return owners, nil 369 }