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  }