Files

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
}