github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/api/static_service.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package api
     5  
     6  import (
     7  	"cmp"
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  
    12  	"github.com/ava-labs/avalanchego/ids"
    13  	"github.com/ava-labs/avalanchego/utils"
    14  	"github.com/ava-labs/avalanchego/utils/formatting"
    15  	"github.com/ava-labs/avalanchego/utils/formatting/address"
    16  	"github.com/ava-labs/avalanchego/utils/json"
    17  	"github.com/ava-labs/avalanchego/utils/math"
    18  	"github.com/ava-labs/avalanchego/vms/components/avax"
    19  	"github.com/ava-labs/avalanchego/vms/platformvm/genesis"
    20  	"github.com/ava-labs/avalanchego/vms/platformvm/signer"
    21  	"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
    22  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    23  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/txheap"
    24  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    25  )
    26  
    27  // Note that since an Avalanche network has exactly one Platform Chain,
    28  // and the Platform Chain defines the genesis state of the network
    29  // (who is staking, which chains exist, etc.), defining the genesis
    30  // state of the Platform Chain is the same as defining the genesis
    31  // state of the network.
    32  
    33  var (
    34  	errUTXOHasNoValue         = errors.New("genesis UTXO has no value")
    35  	errValidatorHasNoWeight   = errors.New("validator has not weight")
    36  	errValidatorAlreadyExited = errors.New("validator would have already unstaked")
    37  	errStakeOverflow          = errors.New("validator stake exceeds limit")
    38  
    39  	_ utils.Sortable[UTXO] = UTXO{}
    40  )
    41  
    42  // StaticService defines the static API methods exposed by the platform VM
    43  type StaticService struct{}
    44  
    45  // UTXO is a UTXO on the Platform Chain that exists at the chain's genesis.
    46  type UTXO struct {
    47  	Locktime json.Uint64 `json:"locktime"`
    48  	Amount   json.Uint64 `json:"amount"`
    49  	Address  string      `json:"address"`
    50  	Message  string      `json:"message"`
    51  }
    52  
    53  // TODO can we define this on *UTXO?
    54  func (utxo UTXO) Compare(other UTXO) int {
    55  	if locktimeCmp := cmp.Compare(utxo.Locktime, other.Locktime); locktimeCmp != 0 {
    56  		return locktimeCmp
    57  	}
    58  	if amountCmp := cmp.Compare(utxo.Amount, other.Amount); amountCmp != 0 {
    59  		return amountCmp
    60  	}
    61  
    62  	utxoAddr, err := bech32ToID(utxo.Address)
    63  	if err != nil {
    64  		return 0
    65  	}
    66  
    67  	otherAddr, err := bech32ToID(other.Address)
    68  	if err != nil {
    69  		return 0
    70  	}
    71  
    72  	return utxoAddr.Compare(otherAddr)
    73  }
    74  
    75  // TODO: Refactor APIStaker, APIValidators and merge them together for
    76  //       PermissionedValidators + PermissionlessValidators.
    77  
    78  // APIStaker is the representation of a staker sent via APIs.
    79  // [TxID] is the txID of the transaction that added this staker.
    80  // [Amount] is the amount of tokens being staked.
    81  // [StartTime] is the Unix time when they start staking
    82  // [Endtime] is the Unix time repr. of when they are done staking
    83  // [NodeID] is the node ID of the staker
    84  // [Uptime] is the observed uptime of this staker
    85  type Staker struct {
    86  	TxID      ids.ID      `json:"txID"`
    87  	StartTime json.Uint64 `json:"startTime"`
    88  	EndTime   json.Uint64 `json:"endTime"`
    89  	Weight    json.Uint64 `json:"weight"`
    90  	NodeID    ids.NodeID  `json:"nodeID"`
    91  
    92  	// Deprecated: Use Weight instead
    93  	// TODO: remove [StakeAmount] after enough time for dependencies to update
    94  	StakeAmount *json.Uint64 `json:"stakeAmount,omitempty"`
    95  }
    96  
    97  // GenesisValidator should to be used for genesis validators only.
    98  type GenesisValidator Staker
    99  
   100  // Owner is the repr. of a reward owner sent over APIs.
   101  type Owner struct {
   102  	Locktime  json.Uint64 `json:"locktime"`
   103  	Threshold json.Uint32 `json:"threshold"`
   104  	Addresses []string    `json:"addresses"`
   105  }
   106  
   107  // PermissionlessValidator is the repr. of a permissionless validator sent over
   108  // APIs.
   109  type PermissionlessValidator struct {
   110  	Staker
   111  	// Deprecated: RewardOwner has been replaced by ValidationRewardOwner and
   112  	//             DelegationRewardOwner.
   113  	RewardOwner *Owner `json:"rewardOwner,omitempty"`
   114  	// The owner of the rewards from the validation period, if applicable.
   115  	ValidationRewardOwner *Owner `json:"validationRewardOwner,omitempty"`
   116  	// The owner of the rewards from delegations during the validation period,
   117  	// if applicable.
   118  	DelegationRewardOwner  *Owner       `json:"delegationRewardOwner,omitempty"`
   119  	PotentialReward        *json.Uint64 `json:"potentialReward,omitempty"`
   120  	AccruedDelegateeReward *json.Uint64 `json:"accruedDelegateeReward,omitempty"`
   121  	DelegationFee          json.Float32 `json:"delegationFee"`
   122  	ExactDelegationFee     *json.Uint32 `json:"exactDelegationFee,omitempty"`
   123  	// Uptime is deprecated for Subnet Validators.
   124  	// It will be available only for Primary Network Validators.
   125  	Uptime *json.Float32 `json:"uptime,omitempty"`
   126  	// Connected is deprecated for Subnet Validators.
   127  	// It will be available only for Primary Network Validators.
   128  	Connected bool                      `json:"connected"`
   129  	Staked    []UTXO                    `json:"staked,omitempty"`
   130  	Signer    *signer.ProofOfPossession `json:"signer,omitempty"`
   131  
   132  	// The delegators delegating to this validator
   133  	DelegatorCount  *json.Uint64        `json:"delegatorCount,omitempty"`
   134  	DelegatorWeight *json.Uint64        `json:"delegatorWeight,omitempty"`
   135  	Delegators      *[]PrimaryDelegator `json:"delegators,omitempty"`
   136  }
   137  
   138  // GenesisPermissionlessValidator should to be used for genesis validators only.
   139  type GenesisPermissionlessValidator struct {
   140  	GenesisValidator
   141  	RewardOwner        *Owner                    `json:"rewardOwner,omitempty"`
   142  	DelegationFee      json.Float32              `json:"delegationFee"`
   143  	ExactDelegationFee *json.Uint32              `json:"exactDelegationFee,omitempty"`
   144  	Staked             []UTXO                    `json:"staked,omitempty"`
   145  	Signer             *signer.ProofOfPossession `json:"signer,omitempty"`
   146  }
   147  
   148  // PermissionedValidator is the repr. of a permissioned validator sent over APIs.
   149  type PermissionedValidator struct {
   150  	Staker
   151  	// The owner the staking reward, if applicable, will go to
   152  	Connected bool          `json:"connected"`
   153  	Uptime    *json.Float32 `json:"uptime,omitempty"`
   154  }
   155  
   156  // PrimaryDelegator is the repr. of a primary network delegator sent over APIs.
   157  type PrimaryDelegator struct {
   158  	Staker
   159  	RewardOwner     *Owner       `json:"rewardOwner,omitempty"`
   160  	PotentialReward *json.Uint64 `json:"potentialReward,omitempty"`
   161  }
   162  
   163  // Chain defines a chain that exists
   164  // at the network's genesis.
   165  // [GenesisData] is the initial state of the chain.
   166  // [VMID] is the ID of the VM this chain runs.
   167  // [FxIDs] are the IDs of the Fxs the chain supports.
   168  // [Name] is a human-readable, non-unique name for the chain.
   169  // [SubnetID] is the ID of the subnet that validates the chain
   170  type Chain struct {
   171  	GenesisData string   `json:"genesisData"`
   172  	VMID        ids.ID   `json:"vmID"`
   173  	FxIDs       []ids.ID `json:"fxIDs"`
   174  	Name        string   `json:"name"`
   175  	SubnetID    ids.ID   `json:"subnetID"`
   176  }
   177  
   178  // BuildGenesisArgs are the arguments used to create
   179  // the genesis data of the Platform Chain.
   180  // [NetworkID] is the ID of the network
   181  // [UTXOs] are the UTXOs on the Platform Chain that exist at genesis.
   182  // [Validators] are the validators of the primary network at genesis.
   183  // [Chains] are the chains that exist at genesis.
   184  // [Time] is the Platform Chain's time at network genesis.
   185  type BuildGenesisArgs struct {
   186  	AvaxAssetID   ids.ID                           `json:"avaxAssetID"`
   187  	NetworkID     json.Uint32                      `json:"networkID"`
   188  	UTXOs         []UTXO                           `json:"utxos"`
   189  	Validators    []GenesisPermissionlessValidator `json:"validators"`
   190  	Chains        []Chain                          `json:"chains"`
   191  	Time          json.Uint64                      `json:"time"`
   192  	InitialSupply json.Uint64                      `json:"initialSupply"`
   193  	Message       string                           `json:"message"`
   194  	Encoding      formatting.Encoding              `json:"encoding"`
   195  }
   196  
   197  // BuildGenesisReply is the reply from BuildGenesis
   198  type BuildGenesisReply struct {
   199  	Bytes    string              `json:"bytes"`
   200  	Encoding formatting.Encoding `json:"encoding"`
   201  }
   202  
   203  // bech32ToID takes bech32 address and produces a shortID
   204  func bech32ToID(addrStr string) (ids.ShortID, error) {
   205  	_, addrBytes, err := address.ParseBech32(addrStr)
   206  	if err != nil {
   207  		return ids.ShortID{}, err
   208  	}
   209  	return ids.ToShortID(addrBytes)
   210  }
   211  
   212  // BuildGenesis build the genesis state of the Platform Chain (and thereby the Avalanche network.)
   213  func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, reply *BuildGenesisReply) error {
   214  	// Specify the UTXOs on the Platform chain that exist at genesis.
   215  	utxos := make([]*genesis.UTXO, 0, len(args.UTXOs))
   216  	for i, apiUTXO := range args.UTXOs {
   217  		if apiUTXO.Amount == 0 {
   218  			return errUTXOHasNoValue
   219  		}
   220  		addrID, err := bech32ToID(apiUTXO.Address)
   221  		if err != nil {
   222  			return err
   223  		}
   224  
   225  		utxo := avax.UTXO{
   226  			UTXOID: avax.UTXOID{
   227  				TxID:        ids.Empty,
   228  				OutputIndex: uint32(i),
   229  			},
   230  			Asset: avax.Asset{ID: args.AvaxAssetID},
   231  			Out: &secp256k1fx.TransferOutput{
   232  				Amt: uint64(apiUTXO.Amount),
   233  				OutputOwners: secp256k1fx.OutputOwners{
   234  					Locktime:  0,
   235  					Threshold: 1,
   236  					Addrs:     []ids.ShortID{addrID},
   237  				},
   238  			},
   239  		}
   240  		if apiUTXO.Locktime > args.Time {
   241  			utxo.Out = &stakeable.LockOut{
   242  				Locktime:        uint64(apiUTXO.Locktime),
   243  				TransferableOut: utxo.Out.(avax.TransferableOut),
   244  			}
   245  		}
   246  		messageBytes, err := formatting.Decode(args.Encoding, apiUTXO.Message)
   247  		if err != nil {
   248  			return fmt.Errorf("problem decoding UTXO message bytes: %w", err)
   249  		}
   250  		utxos = append(utxos, &genesis.UTXO{
   251  			UTXO:    utxo,
   252  			Message: messageBytes,
   253  		})
   254  	}
   255  
   256  	// Specify the validators that are validating the primary network at genesis.
   257  	vdrs := txheap.NewByEndTime()
   258  	for _, vdr := range args.Validators {
   259  		weight := uint64(0)
   260  		stake := make([]*avax.TransferableOutput, len(vdr.Staked))
   261  		utils.Sort(vdr.Staked)
   262  		for i, apiUTXO := range vdr.Staked {
   263  			addrID, err := bech32ToID(apiUTXO.Address)
   264  			if err != nil {
   265  				return err
   266  			}
   267  
   268  			utxo := &avax.TransferableOutput{
   269  				Asset: avax.Asset{ID: args.AvaxAssetID},
   270  				Out: &secp256k1fx.TransferOutput{
   271  					Amt: uint64(apiUTXO.Amount),
   272  					OutputOwners: secp256k1fx.OutputOwners{
   273  						Locktime:  0,
   274  						Threshold: 1,
   275  						Addrs:     []ids.ShortID{addrID},
   276  					},
   277  				},
   278  			}
   279  			if apiUTXO.Locktime > args.Time {
   280  				utxo.Out = &stakeable.LockOut{
   281  					Locktime:        uint64(apiUTXO.Locktime),
   282  					TransferableOut: utxo.Out,
   283  				}
   284  			}
   285  			stake[i] = utxo
   286  
   287  			newWeight, err := math.Add(weight, uint64(apiUTXO.Amount))
   288  			if err != nil {
   289  				return errStakeOverflow
   290  			}
   291  			weight = newWeight
   292  		}
   293  
   294  		if weight == 0 {
   295  			return errValidatorHasNoWeight
   296  		}
   297  		if uint64(vdr.EndTime) <= uint64(args.Time) {
   298  			return errValidatorAlreadyExited
   299  		}
   300  
   301  		owner := &secp256k1fx.OutputOwners{
   302  			Locktime:  uint64(vdr.RewardOwner.Locktime),
   303  			Threshold: uint32(vdr.RewardOwner.Threshold),
   304  		}
   305  		for _, addrStr := range vdr.RewardOwner.Addresses {
   306  			addrID, err := bech32ToID(addrStr)
   307  			if err != nil {
   308  				return err
   309  			}
   310  			owner.Addrs = append(owner.Addrs, addrID)
   311  		}
   312  		utils.Sort(owner.Addrs)
   313  
   314  		delegationFee := uint32(0)
   315  		if vdr.ExactDelegationFee != nil {
   316  			delegationFee = uint32(*vdr.ExactDelegationFee)
   317  		}
   318  
   319  		var (
   320  			baseTx = txs.BaseTx{BaseTx: avax.BaseTx{
   321  				NetworkID:    uint32(args.NetworkID),
   322  				BlockchainID: ids.Empty,
   323  			}}
   324  			validator = txs.Validator{
   325  				NodeID: vdr.NodeID,
   326  				Start:  uint64(args.Time),
   327  				End:    uint64(vdr.EndTime),
   328  				Wght:   weight,
   329  			}
   330  			tx *txs.Tx
   331  		)
   332  		if vdr.Signer == nil {
   333  			tx = &txs.Tx{Unsigned: &txs.AddValidatorTx{
   334  				BaseTx:           baseTx,
   335  				Validator:        validator,
   336  				StakeOuts:        stake,
   337  				RewardsOwner:     owner,
   338  				DelegationShares: delegationFee,
   339  			}}
   340  		} else {
   341  			tx = &txs.Tx{Unsigned: &txs.AddPermissionlessValidatorTx{
   342  				BaseTx:                baseTx,
   343  				Validator:             validator,
   344  				Signer:                vdr.Signer,
   345  				StakeOuts:             stake,
   346  				ValidatorRewardsOwner: owner,
   347  				DelegatorRewardsOwner: owner,
   348  				DelegationShares:      delegationFee,
   349  			}}
   350  		}
   351  
   352  		if err := tx.Initialize(txs.GenesisCodec); err != nil {
   353  			return err
   354  		}
   355  
   356  		vdrs.Add(tx)
   357  	}
   358  
   359  	// Specify the chains that exist at genesis.
   360  	chains := []*txs.Tx{}
   361  	for _, chain := range args.Chains {
   362  		genesisBytes, err := formatting.Decode(args.Encoding, chain.GenesisData)
   363  		if err != nil {
   364  			return fmt.Errorf("problem decoding chain genesis data: %w", err)
   365  		}
   366  		tx := &txs.Tx{Unsigned: &txs.CreateChainTx{
   367  			BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   368  				NetworkID:    uint32(args.NetworkID),
   369  				BlockchainID: ids.Empty,
   370  			}},
   371  			SubnetID:    chain.SubnetID,
   372  			ChainName:   chain.Name,
   373  			VMID:        chain.VMID,
   374  			FxIDs:       chain.FxIDs,
   375  			GenesisData: genesisBytes,
   376  			SubnetAuth:  &secp256k1fx.Input{},
   377  		}}
   378  		if err := tx.Initialize(txs.GenesisCodec); err != nil {
   379  			return err
   380  		}
   381  
   382  		chains = append(chains, tx)
   383  	}
   384  
   385  	validatorTxs := vdrs.List()
   386  
   387  	// genesis holds the genesis state
   388  	g := genesis.Genesis{
   389  		UTXOs:         utxos,
   390  		Validators:    validatorTxs,
   391  		Chains:        chains,
   392  		Timestamp:     uint64(args.Time),
   393  		InitialSupply: uint64(args.InitialSupply),
   394  		Message:       args.Message,
   395  	}
   396  
   397  	// Marshal genesis to bytes
   398  	bytes, err := genesis.Codec.Marshal(genesis.CodecVersion, g)
   399  	if err != nil {
   400  		return fmt.Errorf("couldn't marshal genesis: %w", err)
   401  	}
   402  	reply.Bytes, err = formatting.Encode(args.Encoding, bytes)
   403  	if err != nil {
   404  		return fmt.Errorf("couldn't encode genesis as string: %w", err)
   405  	}
   406  	reply.Encoding = args.Encoding
   407  	return nil
   408  }