Files

317 lines
8.7 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package contract
import (
"fmt"
"math/big"
"github.com/luxfi/cli/v2/pkg/application"
"github.com/luxfi/cli/v2/pkg/key"
"github.com/luxfi/cli/v2/pkg/models"
"github.com/luxfi/cli/v2/pkg/utils"
"github.com/luxfi/evm/v2/precompile/contracts/nativeminter"
"github.com/ethereum/go-ethereum/common"
)
// returns information for the blockchain default allocation key
// if found, returns
// key name, address, private key
func GetDefaultBlockchainAirdropKeyInfo(
app *application.Lux,
blockchainName string,
) (string, string, string, error) {
keyName := utils.GetDefaultBlockchainAirdropKeyName(blockchainName)
keyPath := app.GetKeyPath(keyName)
if utils.FileExists(keyPath) {
k, err := key.LoadSoft(models.NewLocalNetwork().ID, keyPath)
if err != nil {
return "", "", "", err
}
return keyName, k.C(), k.PrivKeyHex(), nil
}
return "", "", "", nil
}
// from a given genesis, look for known private keys inside it, giving
// preference to the ones expected to be default
// it searches for:
// 1) default CLI allocation key for blockchains
// 2) ewoq
// 3) all other stored keys managed by CLI
// returns address + private key when found
func GetBlockchainAirdropKeyInfo(
app *application.Lux,
network models.Network,
blockchainName string,
genesisData []byte,
) (string, string, string, error) {
genesis, err := utils.ByteSliceToSubnetEvmGenesis(genesisData)
if err != nil {
return "", "", "", err
}
if blockchainName != "" {
airdropKeyName, airdropAddress, airdropPrivKey, err := GetDefaultBlockchainAirdropKeyInfo(app, blockchainName)
if err != nil {
return "", "", "", err
}
for address := range genesis.Alloc {
if address.Hex() == airdropAddress {
return airdropKeyName, airdropAddress, airdropPrivKey, nil
}
}
}
ewoq, err := app.GetKey("ewoq", network, false)
if err != nil {
return "", "", "", err
}
for address := range genesis.Alloc {
if address.Hex() == ewoq.C() {
return "ewoq", ewoq.C(), ewoq.PrivKeyHex(), nil
}
}
maxBalance := big.NewInt(0)
maxBalanceKeyName := ""
maxBalanceAddr := ""
maxBalancePrivKey := ""
for address, alloc := range genesis.Alloc {
if alloc.Balance == nil {
continue
}
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, address, false)
if err != nil {
return "", "", "", err
}
if found && alloc.Balance.Cmp(maxBalance) > 0 {
maxBalance = alloc.Balance
maxBalanceKeyName = keyName
maxBalanceAddr = addressStr
maxBalancePrivKey = privKey
}
}
return maxBalanceKeyName, maxBalanceAddr, maxBalancePrivKey, nil
}
func SearchForManagedKey(
app *application.Lux,
network models.Network,
address common.Address,
includeEwoq bool,
) (bool, string, string, string, error) {
keyNames, err := utils.GetKeyNames(app.GetKeyDir(), includeEwoq)
if err != nil {
return false, "", "", "", err
}
for _, keyName := range keyNames {
if k, err := app.GetKey(keyName, network, false); err != nil {
return false, "", "", "", err
} else if address.Hex() == k.C() {
return true, keyName, k.C(), k.PrivKeyHex(), nil
}
}
return false, "", "", "", nil
}
// get the deployed blockchain genesis, and then look for known
// private keys inside it
// returns address + private key when found
func GetEVMSubnetPrefundedKey(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (string, string, error) {
genesisData, err := GetBlockchainGenesis(
app,
network,
chainSpec,
)
if err != nil {
return "", "", err
}
if !utils.ByteSliceIsSubnetEvmGenesis(genesisData) {
return "", "", fmt.Errorf("search for prefunded key is only supported on EVM based vms")
}
_, genesisAddress, genesisPrivateKey, err := GetBlockchainAirdropKeyInfo(
app,
network,
chainSpec.BlockchainName,
genesisData,
)
if err != nil {
return "", "", err
}
return genesisAddress, genesisPrivateKey, nil
}
// get the deployed blockchain genesis
func GetBlockchainGenesis(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) ([]byte, error) {
blockchainID, err := GetBlockchainID(app, network, chainSpec)
if err != nil {
return nil, err
}
createChainTx, err := utils.GetBlockchainTx(network.Endpoint, blockchainID)
if err != nil {
return nil, err
}
return createChainTx.GenesisData, err
}
func sumGenesisSupply(
genesisData []byte,
) (*big.Int, error) {
sum := new(big.Int)
genesis, err := utils.ByteSliceToSubnetEvmGenesis(genesisData)
if err != nil {
return sum, err
}
for _, allocation := range genesis.Alloc {
sum.Add(sum, allocation.Balance)
}
return sum, nil
}
func GetEVMSubnetGenesisSupply(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (*big.Int, error) {
genesisData, err := GetBlockchainGenesis(
app,
network,
chainSpec,
)
if err != nil {
return nil, err
}
if !utils.ByteSliceIsSubnetEvmGenesis(genesisData) {
return nil, fmt.Errorf("genesis supply calculation is only supported on EVM based vms")
}
return sumGenesisSupply(genesisData)
}
func getGenesisNativeMinterAdmin(
app *application.Lux,
network models.Network,
genesisData []byte,
) (bool, bool, string, string, string, error) {
genesis, err := utils.ByteSliceToSubnetEvmGenesis(genesisData)
if err != nil {
return false, false, "", "", "", err
}
if genesis.Config != nil && genesis.Config.GenesisPrecompiles[nativeminter.ConfigKey] != nil {
allowListCfg, ok := genesis.Config.GenesisPrecompiles[nativeminter.ConfigKey].(*nativeminter.Config)
if !ok {
return false, false, "", "", "", fmt.Errorf(
"expected config of type nativeminter.AllowListConfig, but got %T",
allowListCfg,
)
}
if len(allowListCfg.AllowListConfig.AdminAddresses) == 0 {
return false, false, "", "", "", nil
}
for _, admin := range allowListCfg.AllowListConfig.AdminAddresses {
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, admin, true)
if err != nil {
return false, false, "", "", "", err
}
if found {
return true, true, keyName, addressStr, privKey, nil
}
}
return true, false, "", allowListCfg.AllowListConfig.AdminAddresses[0].Hex(), "", nil
}
return false, false, "", "", "", nil
}
func getGenesisNativeMinterManager(
app *application.Lux,
network models.Network,
genesisData []byte,
) (bool, bool, string, string, string, error) {
genesis, err := utils.ByteSliceToSubnetEvmGenesis(genesisData)
if err != nil {
return false, false, "", "", "", err
}
if genesis.Config != nil && genesis.Config.GenesisPrecompiles[nativeminter.ConfigKey] != nil {
allowListCfg, ok := genesis.Config.GenesisPrecompiles[nativeminter.ConfigKey].(*nativeminter.Config)
if !ok {
return false, false, "", "", "", fmt.Errorf(
"expected config of type nativeminter.AllowListConfig, but got %T",
allowListCfg,
)
}
if len(allowListCfg.AllowListConfig.ManagerAddresses) == 0 {
return false, false, "", "", "", nil
}
for _, admin := range allowListCfg.AllowListConfig.ManagerAddresses {
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, admin, true)
if err != nil {
return false, false, "", "", "", err
}
if found {
return true, true, keyName, addressStr, privKey, nil
}
}
return true, false, "", allowListCfg.AllowListConfig.ManagerAddresses[0].Hex(), "", nil
}
return false, false, "", "", "", nil
}
func GetEVMSubnetGenesisNativeMinterAdmin(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (bool, bool, string, string, string, error) {
genesisData, err := GetBlockchainGenesis(
app,
network,
chainSpec,
)
if err != nil {
return false, false, "", "", "", err
}
if !utils.ByteSliceIsSubnetEvmGenesis(genesisData) {
return false, false, "", "", "", fmt.Errorf("genesis native minter admin query is only supported on EVM based vms")
}
return getGenesisNativeMinterAdmin(app, network, genesisData)
}
func GetEVMSubnetGenesisNativeMinterManager(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (bool, bool, string, string, string, error) {
genesisData, err := GetBlockchainGenesis(
app,
network,
chainSpec,
)
if err != nil {
return false, false, "", "", "", err
}
if !utils.ByteSliceIsSubnetEvmGenesis(genesisData) {
return false, false, "", "", "", fmt.Errorf("genesis native minter manager query is only supported on EVM based vms")
}
return getGenesisNativeMinterManager(app, network, genesisData)
}
func ContractAddressIsInGenesisData(
genesisData []byte,
contractAddress common.Address,
) (bool, error) {
genesis, err := utils.ByteSliceToSubnetEvmGenesis(genesisData)
if err != nil {
return false, err
}
for address, allocation := range genesis.Alloc {
if address == contractAddress {
return len(allocation.Code) > 0, nil
}
}
return false, nil
}