Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base avalanche #9

Merged
merged 11 commits into from
May 23, 2024
21 changes: 21 additions & 0 deletions avalanche/avalanche.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avalanche

const (
SubnetEVMRepoName = "subnet-evm"
)

// Client provides client interface for Avalanche operations
type Client struct {
felipemadero marked this conversation as resolved.
Show resolved Hide resolved
// The logger writer interface to write logging messages to.
Logger LeveledLoggerInterface
}

func New() *Client {
client := &Client{
Logger: DefaultLeveledLogger,
}
return client
}
18 changes: 18 additions & 0 deletions avalanche/keychain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avalanche

import "github.com/ava-labs/avalanchego/utils/crypto/keychain"

type Keychain struct {
Network Network

Keychain keychain.Keychain

Ledger keychain.Ledger

UsesLedger bool

LedgerIndices []uint32
}
145 changes: 145 additions & 0 deletions avalanche/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

felipemadero marked this conversation as resolved.
Show resolved Hide resolved
package avalanche

import (
"fmt"
"io"
"os"
)

sukantoraymond marked this conversation as resolved.
Show resolved Hide resolved
//
// Public constants
//

const (
// LevelNull sets a logger to show no messages at all.
LevelNull Level = 0

// LevelError sets a logger to show error messages only.
LevelError Level = 1

// LevelWarn sets a logger to show warning messages or anything more
// severe.
LevelWarn Level = 2

// LevelInfo sets a logger to show informational messages or anything more
// severe.
LevelInfo Level = 3

// LevelDebug sets a logger to show informational messages or anything more
// severe.
LevelDebug Level = 4
)

//
// Public variables
//

// DefaultLeveledLogger is the default logger that the library will use to log
// errors, warnings, and informational messages.
//
// LeveledLoggerInterface is implemented by LeveledLogger, and one can be
// initialized at the desired level of logging. LeveledLoggerInterface also
// provides out-of-the-box compatibility with a Logrus Logger, but may require
// a thin shim for use with other logging libraries that use less standard
// conventions like Zap.
//
// This Logger will be inherited by any backends created by default, but will
// be overridden if a backend is created with GetBackendWithConfig with a
// custom LeveledLogger set.
var DefaultLeveledLogger LeveledLoggerInterface = &LeveledLogger{
Level: LevelError,
}

//
// Public types
//

// Level represents a logging level.
type Level uint32

// LeveledLogger is a leveled logger implementation.
//
// It prints warnings and errors to `os.Stderr` and other messages to
// `os.Stdout`.
type LeveledLogger struct {
// Level is the minimum logging level that will be emitted by this logger.
//
// For example, a Level set to LevelWarn will emit warnings and errors, but
// not informational or debug messages.
//
// Always set this with a constant like LevelWarn because the individual
// values are not guaranteed to be stable.
Level Level

// Internal testing use only.
stderrOverride io.Writer
stdoutOverride io.Writer
}

// Debugf logs a debug message using Printf conventions.
func (l *LeveledLogger) Debugf(format string, v ...interface{}) {
if l.Level >= LevelDebug {
fmt.Fprintf(l.stdout(), "[DEBUG] "+format+"\n", v...)
}
}

// Errorf logs a warning message using Printf conventions.
func (l *LeveledLogger) Errorf(format string, v ...interface{}) {
// Infof logs a debug message using Printf conventions.
if l.Level >= LevelError {
fmt.Fprintf(l.stderr(), "[ERROR] "+format+"\n", v...)
}
}

// Infof logs an informational message using Printf conventions.
func (l *LeveledLogger) Infof(format string, v ...interface{}) {
if l.Level >= LevelInfo {
fmt.Fprintf(l.stdout(), "[INFO] "+format+"\n", v...)
}
}

// Warnf logs a warning message using Printf conventions.
func (l *LeveledLogger) Warnf(format string, v ...interface{}) {
if l.Level >= LevelWarn {
fmt.Fprintf(l.stderr(), "[WARN] "+format+"\n", v...)
}
}

func (l *LeveledLogger) stderr() io.Writer {
if l.stderrOverride != nil {
return l.stderrOverride
}

return os.Stderr
}

func (l *LeveledLogger) stdout() io.Writer {
if l.stdoutOverride != nil {
return l.stdoutOverride
}

return os.Stdout
}

// LeveledLoggerInterface provides a basic leveled logging interface for
// printing debug, informational, warning, and error messages.
//
// It's implemented by LeveledLogger and also provides out-of-the-box
// compatibility with a Logrus Logger, but may require a thin shim for use with
// other logging libraries that you use less standard conventions like Zap.
type LeveledLoggerInterface interface {
// Debugf logs a debug message using Printf conventions.
Debugf(format string, v ...interface{})

// Errorf logs a warning message using Printf conventions.
Errorf(format string, v ...interface{})

// Infof logs an informational message using Printf conventions.
Infof(format string, v ...interface{})

// Warnf logs a warning message using Printf conventions.
Warnf(format string, v ...interface{})
}
16 changes: 16 additions & 0 deletions avalanche/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avalanche

type NetworkKind int64

type Network struct {
Kind NetworkKind

ID uint32

Endpoint string

ClusterName string
}
29 changes: 29 additions & 0 deletions avalanche/vm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avalanche

type VMType string

const (
SubnetEvm = "Subnet-EVM"
CustomVM = "Custom"
)

func VMTypeFromString(s string) VMType {
switch s {
case SubnetEvm:
return SubnetEvm
default:
return CustomVM
}
}

func (v VMType) RepoName() string {
switch v {
case SubnetEvm:
return SubnetEVMRepoName
default:
return "unknown"
}
}
116 changes: 116 additions & 0 deletions subnet/deploy_subnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package subnet

import (
"avalanche-tooling-sdk-go/avalanche"
"avalanche-tooling-sdk-go/utils"
"context"
"fmt"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/keychain"
"github.com/ava-labs/avalanchego/utils/formatting/address"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/ava-labs/avalanchego/wallet/subnet/primary"
"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
)

// createSubnetTx creates uncommitted createSubnet transaction
func createSubnetTx(subnet *Subnet, wallet primary.Wallet) (*txs.Tx, error) {
felipemadero marked this conversation as resolved.
Show resolved Hide resolved
addrs, err := address.ParseToIDs(subnet.ControlKeys)
if err != nil {
return nil, fmt.Errorf("failure parsing control keys: %w", err)
}
owners := &secp256k1fx.OutputOwners{
Addrs: addrs,
Threshold: subnet.Threshold,
Locktime: 0,
}
unsignedTx, err := wallet.P().Builder().NewCreateSubnetTx(
owners,
)
if err != nil {
return nil, fmt.Errorf("error building tx: %w", err)
}
tx := txs.Tx{Unsigned: unsignedTx}
if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil {
return nil, fmt.Errorf("error signing tx: %w", err)
}
return &tx, nil
}

// createBlockchainTx creates uncommitted createBlockchain transaction
func createBlockchainTx(subnet *Subnet, wallet primary.Wallet, network avalanche.Network, keyChain avalanche.Keychain) (*txs.Tx, error) {
felipemadero marked this conversation as resolved.
Show resolved Hide resolved
wallet, err := loadCacheWallet(network, keyChain, wallet, subnet.SubnetID, subnet.TransferSubnetOwnershipTxID)
if err != nil {
return nil, err
}
fxIDs := make([]ids.ID, 0)
options := getMultisigTxOptions(keyChain.Keychain, subnet.SubnetAuthKeys)
// create tx
unsignedTx, err := wallet.P().Builder().NewCreateChainTx(
subnet.SubnetID,
subnet.Genesis,
subnet.VMID,
fxIDs,
subnet.Name,
options...,
)
if err != nil {
return nil, fmt.Errorf("error building tx: %w", err)
}
tx := txs.Tx{Unsigned: unsignedTx}
// sign with current wallet
if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil {
return nil, fmt.Errorf("error signing tx: %w", err)
}
return &tx, nil
}

func getMultisigTxOptions(keychain keychain.Keychain, subnetAuthKeys []ids.ShortID) []common.Option {
options := []common.Option{}
walletAddrs := keychain.Addresses().List()
changeAddr := walletAddrs[0]
// addrs to use for signing
customAddrsSet := set.Set[ids.ShortID]{}
customAddrsSet.Add(walletAddrs...)
customAddrsSet.Add(subnetAuthKeys...)
options = append(options, common.WithCustomAddresses(customAddrsSet))
// set change to go to wallet addr (instead of any other subnet auth key)
changeOwner := &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{changeAddr},
}
options = append(options, common.WithChangeOwner(changeOwner))
return options
}

func loadCacheWallet(network avalanche.Network, keyChain avalanche.Keychain, wallet primary.Wallet, preloadTxs ...ids.ID) (primary.Wallet, error) {
var err error
if wallet == nil {
wallet, err = loadWallet(network, keyChain, preloadTxs...)
}
return wallet, err
}

func loadWallet(network avalanche.Network, keyChain avalanche.Keychain, preloadTxs ...ids.ID) (primary.Wallet, error) {
ctx := context.Background()
// filter out ids.Empty txs
filteredTxs := utils.Filter(preloadTxs, func(e ids.ID) bool { return e != ids.Empty })
wallet, err := primary.MakeWallet(
ctx,
&primary.WalletConfig{
URI: network.Endpoint,
AVAXKeychain: keyChain.Keychain,
EthKeychain: secp256k1fx.NewKeychain(),
PChainTxsToFetch: set.Of(filteredTxs...),
},
)
if err != nil {
return nil, err
}
return wallet, nil
}
Loading