Commit 4a598b6f authored by Pavle Batuta's avatar Pavle Batuta Committed by GitHub

Add deploy command to bee (#1314)

parent d1920d8f
...@@ -100,6 +100,10 @@ func newCommand(opts ...option) (c *command, err error) { ...@@ -100,6 +100,10 @@ func newCommand(opts ...option) (c *command, err error) {
return nil, err return nil, err
} }
if err := c.initDeployCmd(); err != nil {
return nil, err
}
c.initVersionCmd() c.initVersionCmd()
if err := c.initConfigurateOptionsCmd(); err != nil { if err := c.initConfigurateOptionsCmd(); err != nil {
......
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"io/ioutil"
"strings"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/node"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func (c *command) initDeployCmd() error {
cmd := &cobra.Command{
Use: "deploy",
Short: "Deploy and fund the chequebook contract",
RunE: func(cmd *cobra.Command, args []string) (err error) {
if (len(args)) > 0 {
return cmd.Help()
}
var logger logging.Logger
switch v := strings.ToLower(c.config.GetString(optionNameVerbosity)); v {
case "0", "silent":
logger = logging.New(ioutil.Discard, 0)
case "1", "error":
logger = logging.New(cmd.OutOrStdout(), logrus.ErrorLevel)
case "2", "warn":
logger = logging.New(cmd.OutOrStdout(), logrus.WarnLevel)
case "3", "info":
logger = logging.New(cmd.OutOrStdout(), logrus.InfoLevel)
case "4", "debug":
logger = logging.New(cmd.OutOrStdout(), logrus.DebugLevel)
case "5", "trace":
logger = logging.New(cmd.OutOrStdout(), logrus.TraceLevel)
default:
return fmt.Errorf("unknown verbosity level %q", v)
}
dataDir := c.config.GetString(optionNameDataDir)
factoryAddress := c.config.GetString(optionNameSwapFactoryAddress)
swapInitialDeposit := c.config.GetString(optionNameSwapInitialDeposit)
swapEndpoint := c.config.GetString(optionNameSwapEndpoint)
stateStore, err := node.InitStateStore(logger, dataDir)
if err != nil {
return
}
defer stateStore.Close()
signerConfig, err := c.configureSigner(cmd, logger)
if err != nil {
return
}
signer := signerConfig.signer
ctx := cmd.Context()
swapBackend, overlayEthAddress, chainID, transactionService, err := node.InitChain(
ctx,
logger,
stateStore,
swapEndpoint,
signer,
)
if err != nil {
return
}
defer swapBackend.Close()
chequebookFactory, err := node.InitChequebookFactory(
logger,
swapBackend,
chainID,
transactionService,
factoryAddress,
)
if err != nil {
return
}
_, err = node.InitChequebookService(
ctx,
logger,
stateStore,
signer,
chainID,
swapBackend,
overlayEthAddress,
transactionService,
chequebookFactory,
swapInitialDeposit,
)
return
},
PreRunE: func(cmd *cobra.Command, args []string) error {
return c.config.BindPFlags(cmd.Flags())
},
}
c.setAllFlags(cmd)
c.root.AddCommand(cmd)
return nil
}
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package node
import (
"context"
"errors"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p/libp2p"
"github.com/ethersphere/bee/pkg/settlement/swap"
"github.com/ethersphere/bee/pkg/settlement/swap/chequebook"
"github.com/ethersphere/bee/pkg/settlement/swap/swapprotocol"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction"
"github.com/ethersphere/bee/pkg/storage"
"gopkg.in/src-d/go-log.v1"
)
const (
maxDelay = 1 * time.Minute
)
// InitChain will initialize the Ethereum backend at the given endpoint and
// set up the Transacton Service to interact with it using the provided signer.
func InitChain(
ctx context.Context,
logger logging.Logger,
stateStore storage.StateStorer,
endpoint string,
signer crypto.Signer,
) (*ethclient.Client, common.Address, int64, transaction.Service, error) {
backend, err := ethclient.Dial(endpoint)
if err != nil {
return nil, common.Address{}, 0, nil, fmt.Errorf("dial eth client: %w", err)
}
chainID, err := backend.ChainID(ctx)
if err != nil {
logger.Infof("could not connect to backend at %v. In a swap-enabled network a working blockchain node (for goerli network in production) is required. Check your node or specify another node using --swap-endpoint.", endpoint)
return nil, common.Address{}, 0, nil, fmt.Errorf("get chain id: %w", err)
}
transactionService, err := transaction.NewService(logger, backend, signer, stateStore, chainID)
if err != nil {
return nil, common.Address{}, 0, nil, fmt.Errorf("new transaction service: %w", err)
}
overlayEthAddress, err := signer.EthereumAddress()
if err != nil {
return nil, common.Address{}, 0, nil, fmt.Errorf("eth address: %w", err)
}
// Sync the with the given Ethereum backend:
isSynced, err := transaction.IsSynced(ctx, backend, maxDelay)
if err != nil {
return nil, common.Address{}, 0, nil, fmt.Errorf("is synced: %w", err)
}
if !isSynced {
log.Infof("waiting to sync with the Ethereum backend")
err := transaction.WaitSynced(ctx, backend, maxDelay)
if err != nil {
return nil, common.Address{}, 0, nil, fmt.Errorf("waiting backend sync: %w", err)
}
}
return backend, overlayEthAddress, chainID.Int64(), transactionService, nil
}
// InitChequebookFactory will initialize the chequebook factory with the given
// chain backend.
func InitChequebookFactory(
logger logging.Logger,
backend *ethclient.Client,
chainID int64,
transactionService transaction.Service,
factoryAddress string,
) (chequebook.Factory, error) {
var addr common.Address
if factoryAddress == "" {
var found bool
addr, found = chequebook.DiscoverFactoryAddress(chainID)
if !found {
return nil, errors.New("no known factory address for this network")
}
log.Infof("using default factory address for chain id %d: %x", chainID, addr)
} else if !common.IsHexAddress(factoryAddress) {
return nil, errors.New("malformed factory address")
} else {
addr = common.HexToAddress(factoryAddress)
log.Infof("using custom factory address: %x", factoryAddress)
}
chequebookFactory, err := chequebook.NewFactory(
backend,
transactionService,
addr,
chequebook.NewSimpleSwapFactoryBindingFunc,
)
if err != nil {
return nil, fmt.Errorf("new factory: %w", err)
}
return chequebookFactory, nil
}
// InitChequebookService will initialize the chequebook service with the given
// chequebook factory and chain backend.
func InitChequebookService(
ctx context.Context,
logger logging.Logger,
stateStore storage.StateStorer,
signer crypto.Signer,
chainID int64,
backend *ethclient.Client,
overlayEthAddress common.Address,
transactionService transaction.Service,
chequebookFactory chequebook.Factory,
initialDeposit string,
) (chequebook.Service, error) {
chequeSigner := chequebook.NewChequeSigner(signer, chainID)
deposit, ok := new(big.Int).SetString(initialDeposit, 10)
if !ok {
return nil, fmt.Errorf("initial swap deposit \"%s\" cannot be parsed", initialDeposit)
}
chequebookService, err := chequebook.Init(
ctx,
chequebookFactory,
stateStore,
logger,
deposit,
transactionService,
backend,
chainID,
overlayEthAddress,
chequeSigner,
chequebook.NewSimpleSwapBindings,
chequebook.NewERC20Bindings,
)
if err != nil {
return nil, fmt.Errorf("chequebook init: %w", err)
}
return chequebookService, nil
}
func initChequeStoreCashout(
stateStore storage.StateStorer,
swapBackend transaction.Backend,
chequebookFactory chequebook.Factory,
chainID int64,
overlayEthAddress common.Address,
transactionService transaction.Service,
) (chequebook.ChequeStore, chequebook.CashoutService, error) {
chequeStore := chequebook.NewChequeStore(
stateStore,
swapBackend,
chequebookFactory,
chainID,
overlayEthAddress,
chequebook.NewSimpleSwapBindings,
chequebook.RecoverCheque,
)
cashout, err := chequebook.NewCashoutService(
stateStore,
chequebook.NewSimpleSwapBindings,
swapBackend,
transactionService,
chequeStore,
)
if err != nil {
return nil, nil, err
}
return chequeStore, cashout, nil
}
// InitSwap will initialize and register the swap service.
func InitSwap(
p2ps *libp2p.Service,
logger logging.Logger,
stateStore storage.StateStorer,
networkID uint64,
overlayEthAddress common.Address,
chequebookService chequebook.Service,
chequeStore chequebook.ChequeStore,
cashoutService chequebook.CashoutService,
) (*swap.Service, error) {
swapProtocol := swapprotocol.New(p2ps, logger, overlayEthAddress)
swapAddressBook := swap.NewAddressbook(stateStore)
swapService := swap.New(
swapProtocol,
logger,
stateStore,
chequebookService,
chequeStore,
swapAddressBook,
networkID,
cashoutService,
p2ps,
)
swapProtocol.SetSwap(swapService)
err := p2ps.AddProtocol(swapProtocol.Protocol())
if err != nil {
return nil, err
}
return swapService, nil
}
// Copyright 2020 The Swarm Authors. All rights reserved. // Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
...@@ -10,7 +10,6 @@ package node ...@@ -10,7 +10,6 @@ package node
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
...@@ -50,10 +49,7 @@ import ( ...@@ -50,10 +49,7 @@ import (
"github.com/ethersphere/bee/pkg/settlement/pseudosettle" "github.com/ethersphere/bee/pkg/settlement/pseudosettle"
"github.com/ethersphere/bee/pkg/settlement/swap" "github.com/ethersphere/bee/pkg/settlement/swap"
"github.com/ethersphere/bee/pkg/settlement/swap/chequebook" "github.com/ethersphere/bee/pkg/settlement/swap/chequebook"
"github.com/ethersphere/bee/pkg/settlement/swap/swapprotocol"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction" "github.com/ethersphere/bee/pkg/settlement/swap/transaction"
"github.com/ethersphere/bee/pkg/statestore/leveldb"
mockinmem "github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/storage" "github.com/ethersphere/bee/pkg/storage"
"github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/bee/pkg/tags" "github.com/ethersphere/bee/pkg/tags"
...@@ -81,6 +77,7 @@ type Bee struct { ...@@ -81,6 +77,7 @@ type Bee struct {
pullerCloser io.Closer pullerCloser io.Closer
pullSyncCloser io.Closer pullSyncCloser io.Closer
pssCloser io.Closer pssCloser io.Closer
ethClientCloser func()
recoveryHandleCleanup func() recoveryHandleCleanup func()
} }
...@@ -131,103 +128,75 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, ...@@ -131,103 +128,75 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
tracerCloser: tracerCloser, tracerCloser: tracerCloser,
} }
var stateStore storage.StateStorer stateStore, err := InitStateStore(logger, o.DataDir)
if o.DataDir == "" { if err != nil {
stateStore = mockinmem.NewStateStore() return nil, err
logger.Warning("using in-mem state store. no node state will be persisted")
} else {
stateStore, err = leveldb.NewStateStore(filepath.Join(o.DataDir, "statestore"), logger)
if err != nil {
return nil, fmt.Errorf("statestore: %w", err)
}
} }
b.stateStoreCloser = stateStore b.stateStoreCloser = stateStore
addressbook := addressbook.New(stateStore) addressbook := addressbook.New(stateStore)
var swapBackend *ethclient.Client
var overlayEthAddress common.Address
var chainID int64
var transactionService transaction.Service
var chequebookFactory chequebook.Factory
var chequebookService chequebook.Service var chequebookService chequebook.Service
var chequeStore chequebook.ChequeStore var chequeStore chequebook.ChequeStore
var cashoutService chequebook.CashoutService var cashoutService chequebook.CashoutService
var overlayEthAddress common.Address
if o.SwapEnable { if o.SwapEnable {
swapBackend, err := ethclient.Dial(o.SwapEndpoint) swapBackend, overlayEthAddress, chainID, transactionService, err = InitChain(
p2pCtx,
logger,
stateStore,
o.SwapEndpoint,
signer,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.ethClientCloser = swapBackend.Close
chainID, err := swapBackend.ChainID(p2pCtx) chequebookFactory, err = InitChequebookFactory(
if err != nil { logger,
logger.Infof("could not connect to backend at %v. In a swap-enabled network a working blockchain node (for goerli network in production) is required. Check your node or specify another node using --swap-endpoint.", o.SwapEndpoint) swapBackend,
return nil, fmt.Errorf("could not get chain id from ethereum backend: %w", err) chainID,
} transactionService,
o.SwapFactoryAddress,
transactionService, err := transaction.NewService(logger, swapBackend, signer, stateStore, chainID) )
if err != nil {
return nil, err
}
overlayEthAddress, err = signer.EthereumAddress()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var factoryAddress common.Address if err = chequebookFactory.VerifyBytecode(p2pCtx); err != nil {
if o.SwapFactoryAddress == "" { return nil, fmt.Errorf("factory fail: %w", err)
var found bool
factoryAddress, found = chequebook.DiscoverFactoryAddress(chainID.Int64())
if !found {
return nil, errors.New("no known factory address for this network")
}
logger.Infof("using default factory address for chain id %d: %x", chainID, factoryAddress)
} else if !common.IsHexAddress(o.SwapFactoryAddress) {
return nil, errors.New("malformed factory address")
} else {
factoryAddress = common.HexToAddress(o.SwapFactoryAddress)
logger.Infof("using custom factory address: %x", factoryAddress)
} }
chequebookFactory, err := chequebook.NewFactory(swapBackend, transactionService, factoryAddress, chequebook.NewSimpleSwapFactoryBindingFunc) chequebookService, err = InitChequebookService(
if err != nil { p2pCtx,
return nil, err
}
chequeSigner := chequebook.NewChequeSigner(signer, chainID.Int64())
maxDelay := 1 * time.Minute
synced, err := transaction.IsSynced(p2pCtx, swapBackend, maxDelay)
if err != nil {
return nil, err
}
if !synced {
logger.Infof("waiting for ethereum backend to be synced.")
err = transaction.WaitSynced(p2pCtx, swapBackend, maxDelay)
if err != nil {
return nil, fmt.Errorf("could not wait for ethereum backend to sync: %w", err)
}
}
swapInitialDeposit, ok := new(big.Int).SetString(o.SwapInitialDeposit, 10)
if !ok {
return nil, fmt.Errorf("invalid initial deposit: %s", swapInitialDeposit)
}
// initialize chequebook logic
chequebookService, err = chequebook.Init(p2pCtx,
chequebookFactory,
stateStore,
logger, logger,
swapInitialDeposit, stateStore,
transactionService, signer,
chainID,
swapBackend, swapBackend,
chainID.Int64(),
overlayEthAddress, overlayEthAddress,
chequeSigner, transactionService,
chequebook.NewSimpleSwapBindings, chequebookFactory,
chequebook.NewERC20Bindings) o.SwapInitialDeposit,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
chequeStore = chequebook.NewChequeStore(stateStore, swapBackend, chequebookFactory, chainID.Int64(), overlayEthAddress, chequebook.NewSimpleSwapBindings, chequebook.RecoverCheque) chequeStore, cashoutService, err = initChequeStoreCashout(
stateStore,
cashoutService, err = chequebook.NewCashoutService(stateStore, chequebook.NewSimpleSwapBindings, swapBackend, transactionService, chequeStore) swapBackend,
chequebookFactory,
chainID,
overlayEthAddress,
transactionService,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -294,12 +263,18 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, ...@@ -294,12 +263,18 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
var swapService *swap.Service var swapService *swap.Service
if o.SwapEnable { if o.SwapEnable {
swapProtocol := swapprotocol.New(p2ps, logger, overlayEthAddress) swapService, err = InitSwap(
swapAddressBook := swap.NewAddressbook(stateStore) p2ps,
swapService = swap.New(swapProtocol, logger, stateStore, chequebookService, chequeStore, swapAddressBook, networkID, cashoutService, p2ps) logger,
swapProtocol.SetSwap(swapService) stateStore,
if err = p2ps.AddProtocol(swapProtocol.Protocol()); err != nil { networkID,
return nil, fmt.Errorf("swap protocol: %w", err) overlayEthAddress,
chequebookService,
chequeStore,
cashoutService,
)
if err != nil {
return nil, err
} }
settlement = swapService settlement = swapService
} else { } else {
...@@ -582,6 +557,10 @@ func (b *Bee) Shutdown(ctx context.Context) error { ...@@ -582,6 +557,10 @@ func (b *Bee) Shutdown(ctx context.Context) error {
errs.add(fmt.Errorf("p2p server: %w", err)) errs.add(fmt.Errorf("p2p server: %w", err))
} }
if c := b.ethClientCloser; c != nil {
c()
}
if err := b.tracerCloser.Close(); err != nil { if err := b.tracerCloser.Close(); err != nil {
errs.add(fmt.Errorf("tracer: %w", err)) errs.add(fmt.Errorf("tracer: %w", err))
} }
......
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package node
import (
"path/filepath"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/statestore/leveldb"
"github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/storage"
)
// InitStateStore will initialze the stateStore with the given path to the
// data directory. When given an empty directory path, the function will instead
// initialize an in-memory state store that will not be persisted.
func InitStateStore(log logging.Logger, dataDir string) (ret storage.StateStorer, err error) {
if dataDir == "" {
ret = mock.NewStateStore()
log.Warning("using in-mem state store, no node state will be persisted")
return ret, nil
}
return leveldb.NewStateStore(filepath.Join(dataDir, "statestore"), log)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment