// (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package genesis import ( "errors" "fmt" "math" "math/big" "regexp" "strconv" "strings" "time" "github.com/ava-labs/coreth/core" "github.com/ava-labs/go-ethereum/common" "github.com/ava-labs/go-ethereum/params" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/json" "github.com/ava-labs/gecko/utils/units" "github.com/ava-labs/gecko/vms/avm" "github.com/ava-labs/gecko/vms/components/codec" "github.com/ava-labs/gecko/vms/evm" "github.com/ava-labs/gecko/vms/nftfx" "github.com/ava-labs/gecko/vms/platformvm" "github.com/ava-labs/gecko/vms/propertyfx" "github.com/ava-labs/gecko/vms/secp256k1fx" "github.com/ava-labs/gecko/vms/spchainvm" "github.com/ava-labs/gecko/vms/spdagvm" "github.com/ava-labs/gecko/vms/timestampvm" ) // 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. // Hardcoded network IDs const ( MainnetID uint32 = 1 TestnetID uint32 = 2 CascadeID uint32 = 2 LocalID uint32 = 12345 MainnetName = "mainnet" TestnetName = "testnet" CascadeName = "cascade" LocalName = "local" ) var ( validNetworkName = regexp.MustCompile(`network-[0-9]+`) ) // Hard coded genesis constants var ( // Give special names to the mainnet and testnet NetworkIDToNetworkName = map[uint32]string{ MainnetID: MainnetName, TestnetID: CascadeName, LocalID: LocalName, } NetworkNameToNetworkID = map[string]uint32{ MainnetName: MainnetID, TestnetName: TestnetID, CascadeName: CascadeID, LocalName: LocalID, } MintAddresses = []string{ "95YUFjhDG892VePMzpwKF9JzewGKvGRi3", } FundedAddresses = []string{ "9uKvvA7E35QCwLvAaohXTCfFejbf3Rv17", "JLrYNMYXANGj43BfWXBxMMAEenUBp1Sbn", "7TUTzwrU6nbZtWHjTHEpdneUvjKBxb3EM", "77mPUXBdQKwQpPoX6rckCZGLGGdkuG1G6", "4gGWdFZ4Gax1B466YKXyKRRpWLb42Afdt", "CKTkzAPsRxCreyiDTnjGxLmjMarxF28fi", "4ABm9gFHVtsNdcKSd1xsacFkGneSgzpaa", "DpL8PTsrjtLzv5J8LL3D2A6YcnCTqrNH9", "ZdhZv6oZrmXLyFDy6ovXAu6VxmbTsT2h", "6cesTteH62Y5mLoDBUASaBvCXuL2AthL", } ParsedAddresses = []ids.ShortID{} StakerIDs = []string{ "NX4zVkuiRJZYe6Nzzav7GXN3TakUet3Co", "CMsa8cMw4eib1Hb8GG4xiUKAq5eE1BwUX", "DsMP6jLhi1MkDVc3qx9xx9AAZWx8e87Jd", "N86eodVZja3GEyZJTo3DFUPGpxEEvjGHs", "EkKeGSLUbHrrtuayBtbwgWDRUiAziC3ao", } ParsedStakerIDs = []ids.ShortID{} ) func init() { for _, addrStr := range FundedAddresses { addr, err := ids.ShortFromString(addrStr) if err != nil { panic(err) } ParsedAddresses = append(ParsedAddresses, addr) } for _, stakerIDStr := range StakerIDs { stakerID, err := ids.ShortFromString(stakerIDStr) if err != nil { panic(err) } ParsedStakerIDs = append(ParsedStakerIDs, stakerID) } } // NetworkName returns a human readable name for the network with // ID [networkID] func NetworkName(networkID uint32) string { if name, exists := NetworkIDToNetworkName[networkID]; exists { return name } return fmt.Sprintf("network-%d", networkID) } // NetworkID returns the ID of the network with name [networkName] func NetworkID(networkName string) (uint32, error) { networkName = strings.ToLower(networkName) if id, exists := NetworkNameToNetworkID[networkName]; exists { return id, nil } if id, err := strconv.ParseUint(networkName, 10, 0); err == nil { if id > math.MaxUint32 { return 0, fmt.Errorf("NetworkID %s not in [0, 2^32)", networkName) } return uint32(id), nil } if validNetworkName.MatchString(networkName) { if id, err := strconv.Atoi(networkName[8:]); err == nil { if id > math.MaxUint32 { return 0, fmt.Errorf("NetworkID %s not in [0, 2^32)", networkName) } return uint32(id), nil } } return 0, fmt.Errorf("Failed to parse %s as a network name", networkName) } // Aliases returns the default aliases based on the network ID func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases map[[32]byte][]string, vmAliases map[[32]byte][]string) { generalAliases = map[string][]string{ "vm/" + platformvm.ID.String(): []string{"vm/platform"}, "vm/" + avm.ID.String(): []string{"vm/avm"}, "vm/" + evm.ID.String(): []string{"vm/evm"}, "vm/" + spdagvm.ID.String(): []string{"vm/spdag"}, "vm/" + spchainvm.ID.String(): []string{"vm/spchain"}, "vm/" + timestampvm.ID.String(): []string{"vm/timestamp"}, "bc/" + ids.Empty.String(): []string{"P", "platform", "bc/P", "bc/platform"}, } chainAliases = map[[32]byte][]string{ ids.Empty.Key(): []string{"P", "platform"}, } vmAliases = map[[32]byte][]string{ platformvm.ID.Key(): []string{"platform"}, avm.ID.Key(): []string{"avm"}, evm.ID.Key(): []string{"evm"}, spdagvm.ID.Key(): []string{"spdag"}, spchainvm.ID.Key(): []string{"spchain"}, timestampvm.ID.Key(): []string{"timestamp"}, secp256k1fx.ID.Key(): []string{"secp256k1fx"}, nftfx.ID.Key(): []string{"nftfx"}, propertyfx.ID.Key(): []string{"propertyfx"}, } genesisBytes, _ := Genesis(networkID) genesis := &platformvm.Genesis{} // TODO let's not re-create genesis to do aliasing platformvm.Codec.Unmarshal(genesisBytes, genesis) // TODO check for error genesis.Initialize() for _, chain := range genesis.Chains { switch { case avm.ID.Equals(chain.VMID): generalAliases["bc/"+chain.ID().String()] = []string{"X", "avm", "bc/X", "bc/avm"} chainAliases[chain.ID().Key()] = []string{"X", "avm"} case evm.ID.Equals(chain.VMID): generalAliases["bc/"+chain.ID().String()] = []string{"C", "evm", "bc/C", "bc/evm"} chainAliases[chain.ID().Key()] = []string{"C", "evm"} case spdagvm.ID.Equals(chain.VMID): generalAliases["bc/"+chain.ID().String()] = []string{"bc/spdag"} chainAliases[chain.ID().Key()] = []string{"spdag"} case spchainvm.ID.Equals(chain.VMID): generalAliases["bc/"+chain.ID().String()] = []string{"bc/spchain"} chainAliases[chain.ID().Key()] = []string{"spchain"} case timestampvm.ID.Equals(chain.VMID): generalAliases["bc/"+chain.ID().String()] = []string{"bc/timestamp"} chainAliases[chain.ID().Key()] = []string{"timestamp"} } } return } // Genesis returns the genesis data of the Platform Chain. // Since the Platform Chain causes the creation of all other // chains, this function returns the genesis data of the entire network. // The ID of the new network is [networkID]. func Genesis(networkID uint32) ([]byte, error) { // Specify the genesis state of the AVM avmArgs := avm.BuildGenesisArgs{} { owners := []interface{}{avm.Owners{ Threshold: 1, Minters: MintAddresses, }} holders := []interface{}(nil) for _, addr := range FundedAddresses { holders = append(holders, avm.Holder{ Amount: json.Uint64(45 * units.MegaAva), Address: addr, }) } avmArgs.GenesisData = map[string]avm.AssetDefinition{ // The AVM starts out with one asset, $AVA "AVA": avm.AssetDefinition{ Name: "AVA", Symbol: "AVA", Denomination: 9, InitialState: map[string][]interface{}{ "variableCap": owners, "fixedCap": holders, }, }, } } avmReply := avm.BuildGenesisReply{} avmSS := avm.StaticService{} err := avmSS.BuildGenesis(nil, &avmArgs, &avmReply) if err != nil { panic(err) } // Specify the genesis state of Athereum (the built-in instance of the EVM) evmBalance, success := new(big.Int).SetString("33b2e3c9fd0804000000000", 16) if success != true { return nil, errors.New("problem creating evm genesis state") } evmArgs := core.Genesis{ Config: ¶ms.ChainConfig{ ChainID: big.NewInt(43110), HomesteadBlock: big.NewInt(0), DAOForkBlock: big.NewInt(0), DAOForkSupport: true, EIP150Block: big.NewInt(0), EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), }, Nonce: 0, Timestamp: 0, ExtraData: []byte{0}, GasLimit: 100000000, Difficulty: big.NewInt(0), Mixhash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"), Alloc: core.GenesisAlloc{ common.HexToAddress(evm.GenesisTestAddr): core.GenesisAccount{ Balance: evmBalance, }, }, Number: 0, GasUsed: 0, ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), } evmSS := evm.StaticService{} evmReply, err := evmSS.BuildGenesis(nil, &evmArgs) if err != nil { return nil, err } // Specify the genesis state of the simple payments DAG spdagvmArgs := spdagvm.BuildGenesisArgs{} for _, addr := range ParsedAddresses { spdagvmArgs.Outputs = append(spdagvmArgs.Outputs, spdagvm.APIOutput{ Amount: json.Uint64(20 * units.KiloAva), Threshold: 1, Addresses: []ids.ShortID{addr}, }, ) } spdagvmReply := spdagvm.BuildGenesisReply{} spdagvmSS := spdagvm.StaticService{} if err := spdagvmSS.BuildGenesis(nil, &spdagvmArgs, &spdagvmReply); err != nil { return nil, fmt.Errorf("problem creating simple payments DAG: %w", err) } // Specify the genesis state of the simple payments chain spchainvmArgs := spchainvm.BuildGenesisArgs{} for _, addr := range ParsedAddresses { spchainvmArgs.Accounts = append(spchainvmArgs.Accounts, spchainvm.APIAccount{ Address: addr, Balance: json.Uint64(20 * units.KiloAva), }, ) } spchainvmReply := spchainvm.BuildGenesisReply{} spchainvmSS := spchainvm.StaticService{} if err := spchainvmSS.BuildGenesis(nil, &spchainvmArgs, &spchainvmReply); err != nil { return nil, fmt.Errorf("problem creating simple payments chain: %w", err) } // Specify the initial state of the Platform Chain platformvmArgs := platformvm.BuildGenesisArgs{ NetworkID: json.Uint32(networkID), } for _, addr := range ParsedAddresses { platformvmArgs.Accounts = append(platformvmArgs.Accounts, platformvm.APIAccount{ Address: addr, Balance: json.Uint64(20 * units.KiloAva), }, ) } genesisTime := time.Date( /*year=*/ 2019, /*month=*/ time.November, /*day=*/ 1, /*hour=*/ 0, /*minute=*/ 0, /*second=*/ 0, /*nano-second=*/ 0, /*location=*/ time.UTC, ) stakingDuration := 365 * 24 * time.Hour // ~ 1 year endStakingTime := genesisTime.Add(stakingDuration) for i, validatorID := range ParsedStakerIDs { weight := json.Uint64(20 * units.KiloAva) platformvmArgs.Validators = append(platformvmArgs.Validators, platformvm.APIDefaultSubnetValidator{ APIValidator: platformvm.APIValidator{ StartTime: json.Uint64(genesisTime.Unix()), EndTime: json.Uint64(endStakingTime.Unix()), Weight: &weight, ID: validatorID, }, Destination: ParsedAddresses[i%len(ParsedAddresses)], }, ) } // Specify the chains that exist upon this network's creation platformvmArgs.Chains = []platformvm.APIChain{ platformvm.APIChain{ GenesisData: avmReply.Bytes, SubnetID: platformvm.DefaultSubnetID, VMID: avm.ID, FxIDs: []ids.ID{ secp256k1fx.ID, nftfx.ID, propertyfx.ID, }, Name: "X-Chain", }, platformvm.APIChain{ GenesisData: evmReply, SubnetID: platformvm.DefaultSubnetID, VMID: evm.ID, Name: "C-Chain", }, platformvm.APIChain{ GenesisData: spdagvmReply.Bytes, SubnetID: platformvm.DefaultSubnetID, VMID: spdagvm.ID, Name: "Simple DAG Payments", }, platformvm.APIChain{ GenesisData: spchainvmReply.Bytes, SubnetID: platformvm.DefaultSubnetID, VMID: spchainvm.ID, Name: "Simple Chain Payments", }, platformvm.APIChain{ GenesisData: formatting.CB58{Bytes: []byte{}}, // There is no genesis data SubnetID: platformvm.DefaultSubnetID, VMID: timestampvm.ID, Name: "Simple Timestamp Server", }, } platformvmArgs.Time = json.Uint64(genesisTime.Unix()) platformvmReply := platformvm.BuildGenesisReply{} platformvmSS := platformvm.StaticService{} if err := platformvmSS.BuildGenesis(nil, &platformvmArgs, &platformvmReply); err != nil { return nil, fmt.Errorf("problem while building platform chain's genesis state: %w", err) } return platformvmReply.Bytes.Bytes, nil } // VMGenesis ... func VMGenesis(networkID uint32, vmID ids.ID) *platformvm.CreateChainTx { genesisBytes, _ := Genesis(networkID) genesis := platformvm.Genesis{} platformvm.Codec.Unmarshal(genesisBytes, &genesis) if err := genesis.Initialize(); err != nil { panic(err) } for _, chain := range genesis.Chains { if chain.VMID.Equals(vmID) { return chain } } return nil } // AVAAssetID ... func AVAAssetID(networkID uint32) ids.ID { createAVM := VMGenesis(networkID, avm.ID) c := codec.NewDefault() c.RegisterType(&avm.BaseTx{}) c.RegisterType(&avm.CreateAssetTx{}) c.RegisterType(&avm.OperationTx{}) c.RegisterType(&avm.ImportTx{}) c.RegisterType(&avm.ExportTx{}) c.RegisterType(&secp256k1fx.TransferInput{}) c.RegisterType(&secp256k1fx.MintOutput{}) c.RegisterType(&secp256k1fx.TransferOutput{}) c.RegisterType(&secp256k1fx.MintOperation{}) c.RegisterType(&secp256k1fx.Credential{}) genesis := avm.Genesis{} c.Unmarshal(createAVM.GenesisData, &genesis) genesisTx := genesis.Txs[0] tx := avm.Tx{UnsignedTx: &genesisTx.CreateAssetTx} txBytes, _ := c.Marshal(&tx) tx.Initialize(txBytes) return tx.ID() }