mirror of https://github.com/poanetwork/gecko.git
211 lines
6.2 KiB
Go
211 lines
6.2 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package platformvm
|
|
|
|
import (
|
|
"container/heap"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/utils/formatting"
|
|
"github.com/ava-labs/gecko/utils/json"
|
|
)
|
|
|
|
// Note that since an AVA network has exactly one Platform Chain,
|
|
// and the Platform Chain defines the genesis state of the network
|
|
// (who is staking, which chains exist, etc.), defining the genesis
|
|
// state of the Platform Chain is the same as defining the genesis
|
|
// state of the network.
|
|
|
|
var (
|
|
errAccountHasNoValue = errors.New("account has no value")
|
|
errValidatorAddsNoValue = errors.New("validator would have already unstaked")
|
|
)
|
|
|
|
// StaticService defines the static API methods exposed by the platform VM
|
|
type StaticService struct{}
|
|
|
|
// APIAccount is an account on the Platform Chain
|
|
// that exists at the chain's genesis.
|
|
type APIAccount struct {
|
|
Address ids.ShortID `json:"address"`
|
|
Nonce json.Uint64 `json:"nonce"`
|
|
Balance json.Uint64 `json:"balance"`
|
|
}
|
|
|
|
// APIValidator is a validator.
|
|
// [Amount] is the amount of $AVA being staked.
|
|
// [Endtime] is the Unix time repr. of when they are done staking
|
|
// [ID] is the node ID of the staker
|
|
// [Destination] is the address where the staked $AVA (and, if applicable, reward)
|
|
// is sent when this staker is done staking.
|
|
type APIValidator struct {
|
|
StartTime json.Uint64 `json:"startTime"`
|
|
EndTime json.Uint64 `json:"endtime"`
|
|
Weight *json.Uint64 `json:"weight,omitempty"`
|
|
StakeAmount *json.Uint64 `json:"stakeAmount,omitempty"`
|
|
ID ids.ShortID `json:"id"`
|
|
}
|
|
|
|
func (v *APIValidator) weight() uint64 {
|
|
switch {
|
|
case v.Weight != nil:
|
|
return uint64(*v.Weight)
|
|
case v.StakeAmount != nil:
|
|
return uint64(*v.StakeAmount)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// APIDefaultSubnetValidator is a validator of the default subnet
|
|
type APIDefaultSubnetValidator struct {
|
|
APIValidator
|
|
|
|
Destination ids.ShortID `json:"destination"`
|
|
DelegationFeeRate json.Uint32 `json:"delegationFeeRate"`
|
|
}
|
|
|
|
// APIChain defines a chain that exists
|
|
// at the network's genesis.
|
|
// [GenesisData] is the initial state of the chain.
|
|
// [VMID] is the ID of the VM this chain runs.
|
|
// [FxIDs] are the IDs of the Fxs the chain supports.
|
|
// [Name] is a human-readable, non-unique name for the chain.
|
|
type APIChain struct {
|
|
GenesisData formatting.CB58 `json:"genesisData"`
|
|
VMID ids.ID `json:"vmID"`
|
|
FxIDs []ids.ID `json:"fxIDs"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// BuildGenesisArgs are the arguments used to create
|
|
// the genesis data of the Platform Chain.
|
|
// [NetworkID] is the ID of the network
|
|
// [Accounts] are the accounts on the Platform Chain
|
|
// that exists at genesis.
|
|
// [Validators] are the validators of the default subnet at genesis.
|
|
// [Chains] are the chains that exist at genesis.
|
|
// [Time] is the Platform Chain's time at network genesis.
|
|
type BuildGenesisArgs struct {
|
|
NetworkID json.Uint32 `json:"address"`
|
|
Accounts []APIAccount `json:"accounts"`
|
|
Validators []APIDefaultSubnetValidator `json:"defaultSubnetValidators"`
|
|
Chains []APIChain `json:"chains"`
|
|
Time json.Uint64 `json:"time"`
|
|
}
|
|
|
|
// BuildGenesisReply is the reply from BuildGenesis
|
|
type BuildGenesisReply struct {
|
|
Bytes formatting.CB58 `json:"bytes"`
|
|
}
|
|
|
|
// Genesis represents a genesis state of the platform chain
|
|
type Genesis struct {
|
|
Accounts []Account `serialize:"true"`
|
|
Validators *EventHeap `serialize:"true"`
|
|
Chains []*CreateChainTx `serialize:"true"`
|
|
Timestamp uint64 `serialize:"true"`
|
|
}
|
|
|
|
// Initialize ...
|
|
func (g *Genesis) Initialize() error {
|
|
for _, tx := range g.Validators.Txs {
|
|
if err := tx.initialize(nil); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, chain := range g.Chains {
|
|
if err := chain.initialize(nil); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BuildGenesis build the genesis state of the Platform Chain (and thereby the AVA network.)
|
|
func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, reply *BuildGenesisReply) error {
|
|
// Specify the accounts on the Platform chain that exist at genesis.
|
|
accounts := []Account(nil)
|
|
for _, account := range args.Accounts {
|
|
if account.Balance == 0 {
|
|
return errAccountHasNoValue
|
|
}
|
|
accounts = append(accounts, newAccount(
|
|
account.Address, // ID
|
|
0, // nonce
|
|
uint64(account.Balance), // balance
|
|
))
|
|
}
|
|
|
|
// Specify the validators that are validating the default subnet at genesis.
|
|
validators := &EventHeap{}
|
|
for _, validator := range args.Validators {
|
|
weight := validator.weight()
|
|
if weight == 0 {
|
|
return errValidatorAddsNoValue
|
|
}
|
|
if uint64(validator.EndTime) <= uint64(args.Time) {
|
|
return errValidatorAddsNoValue
|
|
}
|
|
|
|
tx := &addDefaultSubnetValidatorTx{
|
|
UnsignedAddDefaultSubnetValidatorTx: UnsignedAddDefaultSubnetValidatorTx{
|
|
DurationValidator: DurationValidator{
|
|
Validator: Validator{
|
|
NodeID: validator.ID,
|
|
Wght: weight,
|
|
},
|
|
Start: uint64(args.Time),
|
|
End: uint64(validator.EndTime),
|
|
},
|
|
NetworkID: uint32(args.NetworkID),
|
|
Nonce: 0,
|
|
Destination: validator.Destination,
|
|
},
|
|
}
|
|
if err := tx.initialize(nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
heap.Push(validators, tx)
|
|
}
|
|
|
|
// Specify the chains that exist at genesis.
|
|
chains := []*CreateChainTx{}
|
|
for _, chain := range args.Chains {
|
|
// Ordinarily we sign a createChainTx. For genesis, there is no key.
|
|
// We generate the ID of this tx by hashing the bytes of the unsigned transaction
|
|
// TODO: Should we just sign this tx with a private key that we share publicly?
|
|
tx := &CreateChainTx{
|
|
UnsignedCreateChainTx: UnsignedCreateChainTx{
|
|
NetworkID: uint32(args.NetworkID),
|
|
Nonce: 0,
|
|
ChainName: chain.Name,
|
|
VMID: chain.VMID,
|
|
FxIDs: chain.FxIDs,
|
|
GenesisData: chain.GenesisData.Bytes,
|
|
},
|
|
}
|
|
if err := tx.initialize(nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
chains = append(chains, tx)
|
|
}
|
|
|
|
// genesis holds the genesis state
|
|
genesis := Genesis{
|
|
Accounts: accounts,
|
|
Validators: validators,
|
|
Chains: chains,
|
|
Timestamp: uint64(args.Time),
|
|
}
|
|
// Marshal genesis to bytes
|
|
bytes, err := Codec.Marshal(genesis)
|
|
reply.Bytes.Bytes = bytes
|
|
return err
|
|
}
|