Files

311 lines
8.6 KiB
Go

// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package contract
import (
"bytes"
"fmt"
"math/big"
"github.com/luxfi/cli/pkg/application"
"github.com/luxfi/cli/pkg/key"
"github.com/luxfi/cli/pkg/utils"
"github.com/luxfi/crypto"
"github.com/luxfi/evm/precompile/contracts/nativeminter"
"github.com/luxfi/genesis/pkg/genesis"
"github.com/luxfi/sdk/models"
)
// 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 := genesis.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) 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.ByteSliceToEVMGenesis(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
}
}
}
maxBalance := big.NewInt(0)
maxBalanceKeyName := ""
maxBalanceAddr := ""
maxBalancePrivKey := ""
for address, alloc := range genesis.Alloc {
if alloc.Balance == nil {
continue
}
// Convert geth common.Address to crypto.Address
cryptoAddr := crypto.Address(address.Bytes())
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, cryptoAddr)
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 crypto.Address,
) (bool, string, string, string, error) {
keyNames, err := utils.GetKeyNames(app.GetKeyDir())
if err != nil {
return false, "", "", "", err
}
for _, keyName := range keyNames {
keyPath := app.GetKeyPath(keyName)
if k, err := key.LoadSoft(network.ID(), keyPath); 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 GetEVMChainPrefundedKey(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (string, string, error) {
genesisData, err := GetBlockchainGenesis(
app,
network,
chainSpec,
)
if err != nil {
return "", "", err
}
if !utils.ByteSliceIsEVMGenesis(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.ByteSliceToEVMGenesis(genesisData)
if err != nil {
return sum, err
}
for _, allocation := range genesis.Alloc {
sum.Add(sum, allocation.Balance)
}
return sum, nil
}
func GetEVMChainGenesisSupply(
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.ByteSliceIsEVMGenesis(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) {
_, err := utils.ByteSliceToEVMGenesis(genesisData)
if err != nil {
return false, false, "", "", "", err
}
// Access GenesisPrecompiles through genesis extras if available
// For now, return no admin found since params.ChainConfig doesn't have GenesisPrecompiles
if false { // Will be enabled when extras.ChainConfig is available
var allowListCfg *nativeminter.Config
_ = allowListCfg
if len(allowListCfg.AdminAddresses) == 0 {
return false, false, "", "", "", nil
}
for _, admin := range allowListCfg.AdminAddresses {
// Convert geth address to crypto.Address
cryptoAddr := crypto.Address(admin.Bytes())
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, cryptoAddr)
if err != nil {
return false, false, "", "", "", err
}
if found {
return true, true, keyName, addressStr, privKey, nil
}
}
return true, false, "", allowListCfg.AdminAddresses[0].Hex(), "", nil
}
return false, false, "", "", "", nil
}
func getGenesisNativeMinterManager(
app *application.Lux,
network models.Network,
genesisData []byte,
) (bool, bool, string, string, string, error) {
_, err := utils.ByteSliceToEVMGenesis(genesisData)
if err != nil {
return false, false, "", "", "", err
}
// Access GenesisPrecompiles through genesis extras if available
// For now, return no admin found since params.ChainConfig doesn't have GenesisPrecompiles
if false { // Will be enabled when extras.ChainConfig is available
var allowListCfg *nativeminter.Config
_ = allowListCfg
if len(allowListCfg.ManagerAddresses) == 0 {
return false, false, "", "", "", nil
}
for _, admin := range allowListCfg.ManagerAddresses {
// Convert geth address to crypto.Address
cryptoAddr := crypto.Address(admin.Bytes())
found, keyName, addressStr, privKey, err := SearchForManagedKey(app, network, cryptoAddr)
if err != nil {
return false, false, "", "", "", err
}
if found {
return true, true, keyName, addressStr, privKey, nil
}
}
return true, false, "", allowListCfg.ManagerAddresses[0].Hex(), "", nil
}
return false, false, "", "", "", nil
}
func GetEVMChainGenesisNativeMinterAdmin(
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.ByteSliceIsEVMGenesis(genesisData) {
return false, false, "", "", "", fmt.Errorf("genesis native minter admin query is only supported on EVM based vms")
}
return getGenesisNativeMinterAdmin(app, network, genesisData)
}
func GetEVMChainGenesisNativeMinterManager(
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.ByteSliceIsEVMGenesis(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 crypto.Address,
) (bool, error) {
genesis, err := utils.ByteSliceToEVMGenesis(genesisData)
if err != nil {
return false, err
}
for address, allocation := range genesis.Alloc {
// Compare by converting to the same type
if bytes.Equal(address.Bytes(), contractAddress.Bytes()) {
return len(allocation.Code) > 0, nil
}
}
return false, nil
}