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 }