mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
Deep audit changes: - Replace stub signature aggregator with real SDK warp implementation - Remove dead pkg/lpm package (SDK lpmintegration used instead) - Fix fs imports: use local utils.FileExists, DirExists, ExpandHome - Fix secp256k1fx imports: use node/vms/secp256k1fx - Fix platformvm imports: use node/vms/platformvm - Fix e2e test TODOs: uncomment BootstrapValidators assertions - Improve localnet stub implementations with real logic - Fix node.go fs.FileExists reference Note: Build requires GOWORK=off due to workspace dependency issues
452 lines
14 KiB
Go
452 lines
14 KiB
Go
// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package vm
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/luxfi/cli/pkg/application"
|
|
"github.com/luxfi/cli/pkg/statemachine"
|
|
"github.com/luxfi/crypto"
|
|
"github.com/luxfi/evm/params"
|
|
"github.com/luxfi/evm/params/extras"
|
|
"github.com/luxfi/evm/precompile/allowlist"
|
|
"github.com/luxfi/evm/precompile/contracts/deployerallowlist"
|
|
"github.com/luxfi/evm/precompile/contracts/feemanager"
|
|
"github.com/luxfi/evm/precompile/contracts/nativeminter"
|
|
"github.com/luxfi/evm/precompile/contracts/rewardmanager"
|
|
"github.com/luxfi/evm/precompile/contracts/txallowlist"
|
|
"github.com/luxfi/evm/precompile/precompileconfig"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/sdk/prompts"
|
|
)
|
|
|
|
// AllowList represents an allow list configuration
|
|
type AllowList struct {
|
|
AdminAddresses []common.Address
|
|
ManagerAddresses []common.Address
|
|
EnabledAddresses []common.Address
|
|
}
|
|
|
|
// GenerateAllowList prompts the user to configure an allow list
|
|
func GenerateAllowList(
|
|
app *application.Lux,
|
|
config AllowList,
|
|
action string,
|
|
vmVersion string,
|
|
) (AllowList, bool, error) {
|
|
// Prompt for admin addresses
|
|
adminPrompt := "Configure admin addresses for " + action
|
|
adminInfo := "\nAdmin addresses have full control over the allow list and can grant/revoke permissions.\n"
|
|
adminAddrs, cancelled, err := getAddressList(adminPrompt, adminInfo, app)
|
|
if err != nil || cancelled {
|
|
return config, cancelled, err
|
|
}
|
|
|
|
// Prompt for manager addresses
|
|
managerPrompt := "Configure manager addresses for " + action
|
|
managerInfo := "\nManager addresses can manage the enabled list but cannot modify admin/manager lists.\n"
|
|
managerAddrs, cancelled, err := getAddressList(managerPrompt, managerInfo, app)
|
|
if err != nil || cancelled {
|
|
return config, cancelled, err
|
|
}
|
|
|
|
// Prompt for enabled addresses
|
|
enabledPrompt := "Configure enabled addresses for " + action
|
|
enabledInfo := "\nEnabled addresses are allowed to perform the action: " + action + "\n"
|
|
enabledAddrs, cancelled, err := getAddressList(enabledPrompt, enabledInfo, app)
|
|
if err != nil || cancelled {
|
|
return config, cancelled, err
|
|
}
|
|
|
|
return AllowList{
|
|
AdminAddresses: convertAddresses(adminAddrs),
|
|
ManagerAddresses: convertAddresses(managerAddrs),
|
|
EnabledAddresses: convertAddresses(enabledAddrs),
|
|
}, false, nil
|
|
}
|
|
|
|
// convertAddresses converts []crypto.Address to []common.Address
|
|
func convertAddresses(addrs []crypto.Address) []common.Address {
|
|
result := make([]common.Address, len(addrs))
|
|
for i, addr := range addrs {
|
|
result[i] = common.Address(addr)
|
|
}
|
|
return result
|
|
}
|
|
|
|
type Precompile string
|
|
|
|
const (
|
|
NativeMint = "Native Minting"
|
|
ContractAllowList = "Contract Deployment Allow List"
|
|
TxAllowList = "Transaction Allow List"
|
|
FeeManager = "Manage Fee Settings"
|
|
RewardManager = "RewardManagerConfig"
|
|
)
|
|
|
|
func PrecompileToUpgradeString(p Precompile) string {
|
|
switch p {
|
|
case NativeMint:
|
|
return "contractNativeMinterConfig"
|
|
case ContractAllowList:
|
|
return "contractDeployerAllowListConfig"
|
|
case TxAllowList:
|
|
return "txAllowListConfig"
|
|
case FeeManager:
|
|
return "feeManagerConfig"
|
|
case RewardManager:
|
|
return "rewardManagerConfig"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func configureRewardManager(app *application.Lux) (rewardmanager.Config, bool, error) {
|
|
config := rewardmanager.Config{}
|
|
adminPrompt := "Configure reward manager admins"
|
|
enabledPrompt := "Configure reward manager enabled addresses"
|
|
info := "\nThis precompile allows to configure the fee reward mechanism " +
|
|
"on your chain, including burning or sending fees.\nFor more information visit " +
|
|
"https://docs.lux.network/chains/customize-a-chain#changing-fee-reward-mechanisms\n\n"
|
|
|
|
admins, cancelled, err := getAddressList(adminPrompt, info, app)
|
|
if err != nil || cancelled {
|
|
return config, false, err
|
|
}
|
|
|
|
enabled, cancelled, err := getAddressList(enabledPrompt, info, app)
|
|
if err != nil {
|
|
return config, false, err
|
|
}
|
|
|
|
config.AllowListConfig = allowlist.AllowListConfig{
|
|
AdminAddresses: convertAddresses(admins),
|
|
EnabledAddresses: convertAddresses(enabled),
|
|
}
|
|
zero := uint64(0)
|
|
config.Upgrade = precompileconfig.Upgrade{
|
|
BlockTimestamp: &zero,
|
|
}
|
|
config.InitialRewardConfig, err = ConfigureInitialRewardConfig(app)
|
|
if err != nil {
|
|
return config, false, err
|
|
}
|
|
|
|
return config, cancelled, nil
|
|
}
|
|
|
|
func ConfigureInitialRewardConfig(app *application.Lux) (*rewardmanager.InitialRewardConfig, error) {
|
|
config := &rewardmanager.InitialRewardConfig{}
|
|
|
|
burnPrompt := "Should fees be burnt?"
|
|
burnFees, err := app.Prompt.CaptureYesNo(burnPrompt)
|
|
if err != nil {
|
|
return config, err
|
|
}
|
|
if burnFees {
|
|
return config, nil
|
|
}
|
|
|
|
feeRcpdPrompt := "Allow block producers to claim fees?"
|
|
allowFeeRecipients, err := app.Prompt.CaptureYesNo(feeRcpdPrompt)
|
|
if err != nil {
|
|
return config, err
|
|
}
|
|
if allowFeeRecipients {
|
|
config.AllowFeeRecipients = true
|
|
return config, nil
|
|
}
|
|
|
|
rewardPrompt := "Provide the address to which fees will be sent to"
|
|
rewardAddress, err := app.Prompt.CaptureAddress(rewardPrompt)
|
|
if err != nil {
|
|
return config, err
|
|
}
|
|
config.RewardAddress = common.Address(rewardAddress)
|
|
return config, nil
|
|
}
|
|
|
|
func getAddressList(initialPrompt string, info string, app *application.Lux) ([]crypto.Address, bool, error) {
|
|
label := "Address"
|
|
|
|
return prompts.CaptureListDecision(
|
|
app.Prompt,
|
|
initialPrompt,
|
|
app.Prompt.CaptureAddress,
|
|
"Enter Address ",
|
|
label,
|
|
info,
|
|
)
|
|
}
|
|
|
|
func configureContractAllowList(app *application.Lux) (deployerallowlist.Config, bool, error) {
|
|
config := deployerallowlist.Config{}
|
|
adminPrompt := "Configure contract deployment admin allow list"
|
|
enabledPrompt := "Configure contract deployment enabled addresses list"
|
|
info := "\nThis precompile restricts who has the ability to deploy contracts " +
|
|
"on your chain.\nFor more information visit " +
|
|
"https://docs.lux.network/chains/customize-a-chain/#restricting-smart-contract-deployers\n\n"
|
|
|
|
admins, cancelled, err := getAddressList(adminPrompt, info, app)
|
|
if err != nil || cancelled {
|
|
return config, false, err
|
|
}
|
|
|
|
enabled, cancelled, err := getAddressList(enabledPrompt, info, app)
|
|
if err != nil {
|
|
return config, false, err
|
|
}
|
|
|
|
config.AllowListConfig = allowlist.AllowListConfig{
|
|
AdminAddresses: convertAddresses(admins),
|
|
EnabledAddresses: convertAddresses(enabled),
|
|
}
|
|
zero := uint64(0)
|
|
config.Upgrade = precompileconfig.Upgrade{
|
|
BlockTimestamp: &zero,
|
|
}
|
|
|
|
return config, cancelled, nil
|
|
}
|
|
|
|
func configureTransactionAllowList(app *application.Lux) (txallowlist.Config, bool, error) {
|
|
config := txallowlist.Config{}
|
|
adminPrompt := "Configure transaction allow list admin addresses"
|
|
enabledPrompt := "Configure transaction allow list enabled addresses"
|
|
info := "\nThis precompile restricts who has the ability to issue transactions " +
|
|
"on your chain.\nFor more information visit " +
|
|
"https://docs.lux.network/chains/customize-a-chain/#restricting-who-can-submit-transactions\n\n"
|
|
|
|
admins, cancelled, err := getAddressList(adminPrompt, info, app)
|
|
if err != nil || cancelled {
|
|
return config, false, err
|
|
}
|
|
|
|
enabled, cancelled, err := getAddressList(enabledPrompt, info, app)
|
|
if err != nil {
|
|
return config, false, err
|
|
}
|
|
|
|
config.AllowListConfig = allowlist.AllowListConfig{
|
|
AdminAddresses: convertAddresses(admins),
|
|
EnabledAddresses: convertAddresses(enabled),
|
|
}
|
|
zero := uint64(0)
|
|
config.Upgrade = precompileconfig.Upgrade{
|
|
BlockTimestamp: &zero,
|
|
}
|
|
|
|
return config, cancelled, nil
|
|
}
|
|
|
|
func configureMinterList(app *application.Lux) (nativeminter.Config, bool, error) {
|
|
config := nativeminter.Config{}
|
|
adminPrompt := "Configure native minting allow list"
|
|
enabledPrompt := "Configure native minting enabled addresses"
|
|
info := "\nThis precompile allows admins to permit designated contracts to mint the native token " +
|
|
"on your chain.\nFor more information visit " +
|
|
"https://docs.lux.network/chains/customize-a-chain#minting-native-coins\n\n"
|
|
|
|
admins, cancelled, err := getAddressList(adminPrompt, info, app)
|
|
if err != nil || cancelled {
|
|
return config, false, err
|
|
}
|
|
|
|
enabled, cancelled, err := getAddressList(enabledPrompt, info, app)
|
|
if err != nil {
|
|
return config, false, err
|
|
}
|
|
|
|
config.AllowListConfig = allowlist.AllowListConfig{
|
|
AdminAddresses: convertAddresses(admins),
|
|
EnabledAddresses: convertAddresses(enabled),
|
|
}
|
|
zero := uint64(0)
|
|
config.Upgrade = precompileconfig.Upgrade{
|
|
BlockTimestamp: &zero,
|
|
}
|
|
|
|
return config, cancelled, nil
|
|
}
|
|
|
|
func configureFeeConfigAllowList(app *application.Lux) (feemanager.Config, bool, error) {
|
|
config := feemanager.Config{}
|
|
adminPrompt := "Configure fee manager allow list"
|
|
enabledPrompt := "Configure native minting enabled addresses"
|
|
info := "\nThis precompile allows admins to adjust chain gas and fee parameters without " +
|
|
"performing a hardfork.\nFor more information visit " +
|
|
"https://docs.lux.network/chains/customize-a-chain#configuring-dynamic-fees\n\n"
|
|
|
|
admins, cancelled, err := getAddressList(adminPrompt, info, app)
|
|
if err != nil || cancelled {
|
|
return config, cancelled, err
|
|
}
|
|
|
|
enabled, cancelled, err := getAddressList(enabledPrompt, info, app)
|
|
if err != nil {
|
|
return config, false, err
|
|
}
|
|
|
|
config.AllowListConfig = allowlist.AllowListConfig{
|
|
AdminAddresses: convertAddresses(admins),
|
|
EnabledAddresses: convertAddresses(enabled),
|
|
}
|
|
zero := uint64(0)
|
|
config.Upgrade = precompileconfig.Upgrade{
|
|
BlockTimestamp: &zero,
|
|
}
|
|
|
|
return config, cancelled, nil
|
|
}
|
|
|
|
func removePrecompile(arr []string, s string) ([]string, error) {
|
|
for i, val := range arr {
|
|
if val == s {
|
|
return append(arr[:i], arr[i+1:]...), nil
|
|
}
|
|
}
|
|
return arr, errors.New("string not in array")
|
|
}
|
|
|
|
func getPrecompiles(config params.ChainConfig, app *application.Lux) (
|
|
params.ChainConfig,
|
|
statemachine.StateDirection,
|
|
error,
|
|
) {
|
|
const cancel = "Cancel"
|
|
|
|
first := true
|
|
|
|
remainingPrecompiles := []string{NativeMint, ContractAllowList, TxAllowList, FeeManager, RewardManager, cancel}
|
|
|
|
for {
|
|
firstStr := "Advanced: Would you like to add a custom precompile to modify the EVM?"
|
|
secondStr := "Would you like to add additional precompiles?"
|
|
|
|
var promptStr string
|
|
if promptStr = secondStr; first {
|
|
promptStr = firstStr
|
|
first = false
|
|
}
|
|
|
|
addPrecompile, err := app.Prompt.CaptureList(promptStr, []string{prompts.No, prompts.Yes, goBackMsg})
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
|
|
switch addPrecompile {
|
|
case prompts.No:
|
|
return config, statemachine.Forward, nil
|
|
case goBackMsg:
|
|
return config, statemachine.Backward, nil
|
|
}
|
|
|
|
precompileDecision, err := app.Prompt.CaptureList(
|
|
"Choose precompile",
|
|
remainingPrecompiles,
|
|
)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
|
|
switch precompileDecision {
|
|
case NativeMint:
|
|
mintConfig, cancelled, err := configureMinterList(app)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
if !cancelled {
|
|
// Store the configuration for genesis
|
|
extraConfig := params.GetExtra(&config)
|
|
if extraConfig.GenesisPrecompiles == nil {
|
|
extraConfig.GenesisPrecompiles = make(extras.Precompiles)
|
|
}
|
|
extraConfig.GenesisPrecompiles[nativeminter.ConfigKey] = &mintConfig
|
|
remainingPrecompiles, err = removePrecompile(remainingPrecompiles, NativeMint)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
}
|
|
case ContractAllowList:
|
|
contractConfig, cancelled, err := configureContractAllowList(app)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
if !cancelled {
|
|
// Store the configuration for genesis
|
|
extraConfig := params.GetExtra(&config)
|
|
if extraConfig.GenesisPrecompiles == nil {
|
|
extraConfig.GenesisPrecompiles = make(extras.Precompiles)
|
|
}
|
|
extraConfig.GenesisPrecompiles[deployerallowlist.ConfigKey] = &contractConfig
|
|
remainingPrecompiles, err = removePrecompile(remainingPrecompiles, ContractAllowList)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
}
|
|
case TxAllowList:
|
|
txConfig, cancelled, err := configureTransactionAllowList(app)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
if !cancelled {
|
|
// Store the configuration for genesis
|
|
extraConfig := params.GetExtra(&config)
|
|
if extraConfig.GenesisPrecompiles == nil {
|
|
extraConfig.GenesisPrecompiles = make(extras.Precompiles)
|
|
}
|
|
extraConfig.GenesisPrecompiles[txallowlist.ConfigKey] = &txConfig
|
|
remainingPrecompiles, err = removePrecompile(remainingPrecompiles, TxAllowList)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
}
|
|
case FeeManager:
|
|
feeConfig, cancelled, err := configureFeeConfigAllowList(app)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
if !cancelled {
|
|
// Store the configuration for genesis
|
|
extraConfig := params.GetExtra(&config)
|
|
if extraConfig.GenesisPrecompiles == nil {
|
|
extraConfig.GenesisPrecompiles = make(extras.Precompiles)
|
|
}
|
|
extraConfig.GenesisPrecompiles[feemanager.ConfigKey] = &feeConfig
|
|
remainingPrecompiles, err = removePrecompile(remainingPrecompiles, FeeManager)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
}
|
|
case RewardManager:
|
|
rewardManagerConfig, cancelled, err := configureRewardManager(app)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
if !cancelled {
|
|
// Store the configuration for genesis
|
|
extraConfig := params.GetExtra(&config)
|
|
if extraConfig.GenesisPrecompiles == nil {
|
|
extraConfig.GenesisPrecompiles = make(extras.Precompiles)
|
|
}
|
|
extraConfig.GenesisPrecompiles[rewardmanager.ConfigKey] = &rewardManagerConfig
|
|
remainingPrecompiles, err = removePrecompile(remainingPrecompiles, RewardManager)
|
|
if err != nil {
|
|
return config, statemachine.Stop, err
|
|
}
|
|
}
|
|
|
|
case cancel:
|
|
return config, statemachine.Forward, nil
|
|
}
|
|
|
|
// When all precompiles have been added, the len of remainingPrecompiles will be 1
|
|
// (the cancel option stays in the list). Safe to return.
|
|
if len(remainingPrecompiles) == 1 {
|
|
return config, statemachine.Forward, nil
|
|
}
|
|
}
|
|
}
|