mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
432 lines
11 KiB
Go
432 lines
11 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package interchain
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/application"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/contract"
|
|
"github.com/luxfi/cli/v2/pkg/key"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/utils"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/cli/v2/sdk/evm"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
)
|
|
|
|
const (
|
|
releaseURL = "https://github.com/luxfi/warp-contracts/releases/download/%s/"
|
|
messengerContractAddressURLFmt = releaseURL + "/TeleporterMessenger_Contract_Address_%s.txt"
|
|
messengerDeployerAddressURLFmt = releaseURL + "/TeleporterMessenger_Deployer_Address_%s.txt"
|
|
messengerDeployerTxURLFmt = releaseURL + "/TeleporterMessenger_Deployment_Transaction_%s.txt"
|
|
registryBytecodeURLFmt = releaseURL + "/TeleporterRegistry_Bytecode_%s.txt"
|
|
)
|
|
|
|
var (
|
|
// 10 LUX
|
|
messengerDeployerRequiredBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10))
|
|
// 600 LUX
|
|
InterchainMessagingPrefundedAddressBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(600))
|
|
)
|
|
|
|
func getWarpURLs(version string) (string, string, string, string) {
|
|
messengerContractAddressURL := fmt.Sprintf(
|
|
messengerContractAddressURLFmt,
|
|
version,
|
|
version,
|
|
)
|
|
messengerDeployerAddressURL := fmt.Sprintf(
|
|
messengerDeployerAddressURLFmt,
|
|
version,
|
|
version,
|
|
)
|
|
messengerDeployerTxURL := fmt.Sprintf(
|
|
messengerDeployerTxURLFmt,
|
|
version,
|
|
version,
|
|
)
|
|
registryBydecodeURL := fmt.Sprintf(
|
|
registryBytecodeURLFmt,
|
|
version,
|
|
version,
|
|
)
|
|
return messengerContractAddressURL, messengerDeployerAddressURL, messengerDeployerTxURL, registryBydecodeURL
|
|
}
|
|
|
|
type WarpDeployer struct {
|
|
messengerContractAddress string
|
|
messengerDeployerAddress string
|
|
messengerDeployerTx string
|
|
registryBydecode string
|
|
}
|
|
|
|
func (t *WarpDeployer) GetAssets(
|
|
warpInstallDir string,
|
|
version string,
|
|
) (string, string, string, string, error) {
|
|
if err := t.DownloadAssets(warpInstallDir, version); err != nil {
|
|
return "", "", "", "", err
|
|
}
|
|
return t.messengerContractAddress, t.messengerDeployerAddress, t.messengerDeployerTx, t.registryBydecode, nil
|
|
}
|
|
|
|
func (t *WarpDeployer) CheckAssets() error {
|
|
if t.messengerContractAddress == "" || t.messengerDeployerAddress == "" || t.messengerDeployerTx == "" || t.registryBydecode == "" {
|
|
return fmt.Errorf("warp assets has not been initialized")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *WarpDeployer) SetAssetsFromPaths(
|
|
messengerContractAddressPath string,
|
|
messengerDeployerAddressPath string,
|
|
messengerDeployerTxPath string,
|
|
registryBydecodePath string,
|
|
) error {
|
|
if messengerContractAddressPath != "" {
|
|
if bs, err := os.ReadFile(messengerContractAddressPath); err != nil {
|
|
return err
|
|
} else {
|
|
t.messengerContractAddress = string(bs)
|
|
}
|
|
}
|
|
if messengerDeployerAddressPath != "" {
|
|
if bs, err := os.ReadFile(messengerDeployerAddressPath); err != nil {
|
|
return err
|
|
} else {
|
|
t.messengerDeployerAddress = string(bs)
|
|
}
|
|
}
|
|
if messengerDeployerTxPath != "" {
|
|
if bs, err := os.ReadFile(messengerDeployerTxPath); err != nil {
|
|
return err
|
|
} else {
|
|
t.messengerDeployerTx = string(bs)
|
|
}
|
|
}
|
|
if registryBydecodePath != "" {
|
|
if bs, err := os.ReadFile(registryBydecodePath); err != nil {
|
|
return err
|
|
} else {
|
|
t.registryBydecode = string(bs)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *WarpDeployer) SetAssets(
|
|
messengerContractAddress string,
|
|
messengerDeployerAddress string,
|
|
messengerDeployerTx string,
|
|
registryBydecode string,
|
|
) {
|
|
if messengerContractAddress != "" {
|
|
t.messengerContractAddress = messengerContractAddress
|
|
}
|
|
if messengerDeployerAddress != "" {
|
|
t.messengerDeployerAddress = messengerDeployerAddress
|
|
}
|
|
if messengerDeployerTx != "" {
|
|
t.messengerDeployerTx = messengerDeployerTx
|
|
}
|
|
if registryBydecode != "" {
|
|
t.registryBydecode = registryBydecode
|
|
}
|
|
}
|
|
|
|
func (t *WarpDeployer) DownloadAssets(
|
|
warpInstallDir string,
|
|
version string,
|
|
) error {
|
|
var err error
|
|
binDir := filepath.Join(warpInstallDir, version)
|
|
messengerContractAddressURL, messengerDeployerAddressURL, messengerDeployerTxURL, registryBydecodeURL := getWarpURLs(
|
|
version,
|
|
)
|
|
messengerContractAddressPath := filepath.Join(
|
|
binDir,
|
|
filepath.Base(messengerContractAddressURL),
|
|
)
|
|
messengerDeployerAddressPath := filepath.Join(
|
|
binDir,
|
|
filepath.Base(messengerDeployerAddressURL),
|
|
)
|
|
messengerDeployerTxPath := filepath.Join(
|
|
binDir,
|
|
filepath.Base(messengerDeployerTxURL),
|
|
)
|
|
registryBytecodePath := filepath.Join(
|
|
binDir,
|
|
filepath.Base(registryBydecodeURL),
|
|
)
|
|
if t.messengerContractAddress == "" {
|
|
var messengerContractAddressBytes []byte
|
|
if utils.FileExists(messengerContractAddressPath) {
|
|
messengerContractAddressBytes, err = os.ReadFile(
|
|
messengerContractAddressPath,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// get target warp messenger contract address
|
|
messengerContractAddressBytes, err = application.NewDownloader().DownloadWithTee(messengerContractAddressURL, messengerContractAddressPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t.messengerContractAddress = string(messengerContractAddressBytes)
|
|
}
|
|
if t.messengerDeployerAddress == "" {
|
|
var messengerDeployerAddressBytes []byte
|
|
if utils.FileExists(messengerDeployerAddressPath) {
|
|
messengerDeployerAddressBytes, err = os.ReadFile(
|
|
messengerDeployerAddressPath,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// get warp deployer address
|
|
messengerDeployerAddressBytes, err = application.NewDownloader().DownloadWithTee(messengerDeployerAddressURL, messengerDeployerAddressPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t.messengerDeployerAddress = string(messengerDeployerAddressBytes)
|
|
}
|
|
if t.messengerDeployerTx == "" {
|
|
var messengerDeployerTxBytes []byte
|
|
if utils.FileExists(messengerDeployerTxPath) {
|
|
messengerDeployerTxBytes, err = os.ReadFile(messengerDeployerTxPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
messengerDeployerTxBytes, err = application.NewDownloader().DownloadWithTee(messengerDeployerTxURL, messengerDeployerTxPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t.messengerDeployerTx = string(messengerDeployerTxBytes)
|
|
}
|
|
if t.registryBydecode == "" {
|
|
var registryBytecodeBytes []byte
|
|
if utils.FileExists(registryBytecodePath) {
|
|
registryBytecodeBytes, err = os.ReadFile(registryBytecodePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
registryBytecodeBytes, err = application.NewDownloader().DownloadWithTee(registryBydecodeURL, registryBytecodePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t.registryBydecode = string(registryBytecodeBytes)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *WarpDeployer) Deploy(
|
|
subnetName string,
|
|
rpcURL string,
|
|
privateKey string,
|
|
deployMessenger bool,
|
|
deployRegistry bool,
|
|
forceRegistryDeploy bool,
|
|
) (bool, string, string, error) {
|
|
var (
|
|
messengerAddress string
|
|
registryAddress string
|
|
alreadyDeployed bool
|
|
err error
|
|
)
|
|
if deployMessenger {
|
|
alreadyDeployed, messengerAddress, err = t.DeployMessenger(
|
|
subnetName,
|
|
rpcURL,
|
|
privateKey,
|
|
)
|
|
}
|
|
if err == nil && deployRegistry {
|
|
if !deployMessenger || !alreadyDeployed || forceRegistryDeploy {
|
|
registryAddress, err = t.DeployRegistry(subnetName, rpcURL, privateKey)
|
|
}
|
|
}
|
|
return alreadyDeployed, messengerAddress, registryAddress, err
|
|
}
|
|
|
|
func (t *WarpDeployer) DeployMessenger(
|
|
subnetName string,
|
|
rpcURL string,
|
|
privateKey string,
|
|
) (bool, string, error) {
|
|
if err := t.CheckAssets(); err != nil {
|
|
return false, "", err
|
|
}
|
|
// check if contract is already deployed
|
|
client, err := evm.GetClient(rpcURL)
|
|
if err != nil {
|
|
return false, "", err
|
|
}
|
|
if messengerAlreadyDeployed, err := client.ContractAlreadyDeployed(t.messengerContractAddress); err != nil {
|
|
return false, "", fmt.Errorf("failure making a request to %s: %w", rpcURL, err)
|
|
} else if messengerAlreadyDeployed {
|
|
ux.Logger.PrintToUser("Warp Messenger has already been deployed to %s", subnetName)
|
|
return true, t.messengerContractAddress, nil
|
|
}
|
|
// get warp deployer balance
|
|
messengerDeployerBalance, err := client.GetAddressBalance(
|
|
t.messengerDeployerAddress,
|
|
)
|
|
if err != nil {
|
|
return false, "", err
|
|
}
|
|
if messengerDeployerBalance.Cmp(messengerDeployerRequiredBalance) < 0 {
|
|
toFund := big.NewInt(0).
|
|
Sub(messengerDeployerRequiredBalance, messengerDeployerBalance)
|
|
if _, err := client.FundAddress(
|
|
privateKey,
|
|
t.messengerDeployerAddress,
|
|
toFund,
|
|
); err != nil {
|
|
return false, "", err
|
|
}
|
|
}
|
|
if err := client.IssueTx(t.messengerDeployerTx); err != nil {
|
|
return false, "", err
|
|
}
|
|
ux.Logger.PrintToUser(
|
|
"Warp Messenger successfully deployed to %s (%s)",
|
|
subnetName,
|
|
t.messengerContractAddress,
|
|
)
|
|
return false, t.messengerContractAddress, nil
|
|
}
|
|
|
|
func (t *WarpDeployer) DeployRegistry(
|
|
subnetName string,
|
|
rpcURL string,
|
|
privateKey string,
|
|
) (string, error) {
|
|
if err := t.CheckAssets(); err != nil {
|
|
return "", err
|
|
}
|
|
messengerContractAddress := common.HexToAddress(t.messengerContractAddress)
|
|
type ProtocolRegistryEntry struct {
|
|
Version *big.Int
|
|
ProtocolAddress common.Address
|
|
}
|
|
constructorInput := []ProtocolRegistryEntry{
|
|
{
|
|
Version: big.NewInt(1),
|
|
ProtocolAddress: messengerContractAddress,
|
|
},
|
|
}
|
|
registryAddress, err := contract.DeployContract(
|
|
rpcURL,
|
|
privateKey,
|
|
[]byte(t.registryBydecode),
|
|
"([(uint256, address)])",
|
|
constructorInput,
|
|
)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ux.Logger.PrintToUser(
|
|
"Warp Registry successfully deployed to %s (%s)",
|
|
subnetName,
|
|
registryAddress,
|
|
)
|
|
return registryAddress.Hex(), nil
|
|
}
|
|
|
|
func getPrivateKey(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
keyName string,
|
|
) (string, error) {
|
|
var (
|
|
err error
|
|
k *key.SoftKey
|
|
)
|
|
if keyName == "" {
|
|
if k, err = key.LoadEwoq(network.ID); err != nil {
|
|
return "", err
|
|
}
|
|
} else {
|
|
k, err = key.LoadSoft(network.ID, app.GetKeyPath(keyName))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
return k.PrivKeyHex(), nil
|
|
}
|
|
|
|
func SetProposerVM(
|
|
app *application.Lux,
|
|
network models.Network,
|
|
blockchainID string,
|
|
fundedKeyName string,
|
|
) error {
|
|
privKeyStr, err := getPrivateKey(app, network, fundedKeyName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
wsEndpoint := network.BlockchainWSEndpoint(blockchainID)
|
|
client, err := evm.GetClient(wsEndpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer client.Close()
|
|
return client.SetupProposerVM(privKeyStr)
|
|
}
|
|
|
|
func getWarpKeyInfo(
|
|
app *application.Lux,
|
|
keyName string,
|
|
) (string, string, *big.Int, error) {
|
|
k, err := key.LoadSoftOrCreate(models.NewLocalNetwork().ID, app.GetKeyPath(keyName))
|
|
if err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
return k.C(), k.PrivKeyHex(), InterchainMessagingPrefundedAddressBalance, nil
|
|
}
|
|
|
|
type WarpInfo struct {
|
|
Version string
|
|
FundedAddress string
|
|
FundedBalance *big.Int
|
|
MessengerDeployerAddress string
|
|
}
|
|
|
|
func GetWarpInfo(
|
|
app *application.Lux,
|
|
) (*WarpInfo, error) {
|
|
var err error
|
|
ti := WarpInfo{}
|
|
ti.FundedAddress, _, ti.FundedBalance, err = getWarpKeyInfo(
|
|
app,
|
|
constants.WarpKeyName,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ti.Version = constants.WarpVersion
|
|
deployer := WarpDeployer{}
|
|
_, ti.MessengerDeployerAddress, _, _, err = deployer.GetAssets(
|
|
app.GetWarpContractsBinDir(),
|
|
ti.Version,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ti, nil
|
|
}
|