diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 71149c310..f0680c487 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -17,21 +17,25 @@ package main import ( + "context" "crypto/ecdsa" "fmt" "io/ioutil" + "math/big" "os" "os/signal" "runtime" "strconv" "strings" "syscall" + "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/contracts/ens" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/internal/debug" @@ -40,6 +44,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/swarm" bzzapi "github.com/ethereum/go-ethereum/swarm/api" "gopkg.in/urfave/cli.v1" @@ -87,15 +92,27 @@ var ( Name: "swap", Usage: "Swarm SWAP enabled (default false)", } + SwarmSwapAPIFlag = cli.StringFlag{ + Name: "swap-api", + Usage: "URL of the Ethereum API provider to use to settle SWAP payments", + } SwarmSyncEnabledFlag = cli.BoolTFlag{ Name: "sync", Usage: "Swarm Syncing enabled (default true)", } EthAPIFlag = cli.StringFlag{ Name: "ethapi", - Usage: "URL of the Ethereum API provider", + Usage: "DEPRECATED: please use --ens-api and --swap-api", + } + EnsAPIFlag = cli.StringFlag{ + Name: "ens-api", + Usage: "URL of the Ethereum API provider to use for ENS record lookups", Value: node.DefaultIPCEndpoint("geth"), } + EnsAddrFlag = cli.StringFlag{ + Name: "ens-addr", + Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)", + } SwarmApiFlag = cli.StringFlag{ Name: "bzzapi", Usage: "Swarm HTTP endpoint", @@ -250,8 +267,11 @@ Cleans database of corrupted entries. // bzzd-specific flags CorsStringFlag, EthAPIFlag, + EnsAPIFlag, + EnsAddrFlag, SwarmConfigPathFlag, SwarmSwapEnabledFlag, + SwarmSwapAPIFlag, SwarmSyncEnabledFlag, SwarmListenAddrFlag, SwarmPortFlag, @@ -333,6 +353,38 @@ func bzzd(ctx *cli.Context) error { return nil } +// detectEnsAddr determines the ENS contract address by getting both the +// version and genesis hash using the client and matching them to either +// mainnet or testnet addresses +func detectEnsAddr(client *rpc.Client) (common.Address, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + var version string + if err := client.CallContext(ctx, &version, "net_version"); err != nil { + return common.Address{}, err + } + + block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0)) + if err != nil { + return common.Address{}, err + } + + switch { + + case version == "1" && block.Hash() == params.MainNetGenesisHash: + log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress) + return ens.MainNetAddress, nil + + case version == "3" && block.Hash() == params.TestNetGenesisHash: + log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress) + return ens.TestNetAddress, nil + + default: + return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash()) + } +} + func registerBzzService(ctx *cli.Context, stack *node.Node) { prvkey := getAccount(ctx, stack) @@ -357,19 +409,56 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name) ethapi := ctx.GlobalString(EthAPIFlag.Name) + if ethapi != "" { + log.Warn("DEPRECATED: --ethapi is deprecated and will be removed in a future version, please use --ens-api and --swap-api") + } + + swapapi := ctx.GlobalString(SwarmSwapAPIFlag.Name) + if swapEnabled && swapapi == "" { + utils.Fatalf("SWAP is enabled but --swap-api is not set") + } + + ensapi := ctx.GlobalString(EnsAPIFlag.Name) + // use the deprecated --ethapi if --ens-api is not set + if ensapi == "" { + ensapi = ethapi + } + ensAddr := ctx.GlobalString(EnsAddrFlag.Name) + cors := ctx.GlobalString(CorsStringFlag.Name) boot := func(ctx *node.ServiceContext) (node.Service, error) { - var client *ethclient.Client - if len(ethapi) > 0 { - client, err = ethclient.Dial(ethapi) + var swapClient *ethclient.Client + if swapapi != "" { + log.Info("connecting to SWAP API", "url", swapapi) + swapClient, err = ethclient.Dial(swapapi) if err != nil { - utils.Fatalf("Can't connect: %v", err) + return nil, fmt.Errorf("error connecting to SWAP API %s: %s", swapapi, err) } - } else { - swapEnabled = false } - return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors) + + var ensClient *ethclient.Client + if ensapi != "" { + log.Info("connecting to ENS API", "url", ensapi) + client, err := rpc.Dial(ensapi) + if err != nil { + return nil, fmt.Errorf("error connecting to ENS API %s: %s", ensapi, err) + } + ensClient = ethclient.NewClient(client) + + if ensAddr != "" { + bzzconfig.EnsRoot = common.HexToAddress(ensAddr) + } else { + ensAddr, err := detectEnsAddr(client) + if err == nil { + bzzconfig.EnsRoot = ensAddr + } else { + log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", bzzconfig.EnsRoot), "err", err) + } + } + } + + return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, swapEnabled, syncEnabled, cors) } if err := stack.Register(boot); err != nil { utils.Fatalf("Failed to register the Swarm service: %v", err) diff --git a/contracts/ens/ens.go b/contracts/ens/ens.go index 83ca9b63b..c292a1714 100644 --- a/contracts/ens/ens.go +++ b/contracts/ens/ens.go @@ -29,6 +29,11 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +var ( + MainNetAddress = common.HexToAddress("0x314159265dD8dbb310642f98f50C066173C1259b") + TestNetAddress = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010") +) + // swarm domain name registry and resolver type ENS struct { *contract.ENSSession diff --git a/swarm/api/config.go b/swarm/api/config.go index 647c153ed..d8d25b1c8 100644 --- a/swarm/api/config.go +++ b/swarm/api/config.go @@ -25,6 +25,7 @@ import ( "path/filepath" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/ens" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/services/swap" @@ -36,10 +37,6 @@ const ( DefaultHTTPPort = "8500" ) -var ( - ensRootAddress = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010") -) - // separate bzz directories // allow several bzz nodes running in parallel type Config struct { @@ -84,7 +81,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n Swap: swap.DefaultSwapParams(contract, prvKey), PublicKey: pubkeyhex, BzzKey: keyhex, - EnsRoot: ensRootAddress, + EnsRoot: ens.TestNetAddress, NetworkId: networkId, } data, err = ioutil.ReadFile(confpath) @@ -129,7 +126,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, n self.Swap.SetKey(prvKey) if (self.EnsRoot == common.Address{}) { - self.EnsRoot = ensRootAddress + self.EnsRoot = ens.TestNetAddress } return diff --git a/swarm/swarm.go b/swarm/swarm.go index 4f93a30b7..830490843 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -76,7 +76,7 @@ func (self *Swarm) API() *SwarmAPI { // creates a new swarm service instance // implements node.Service -func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config, swapEnabled, syncEnabled bool, cors string) (self *Swarm, err error) { +func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *ethclient.Client, config *api.Config, swapEnabled, syncEnabled bool, cors string) (self *Swarm, err error) { if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { return nil, fmt.Errorf("empty public key") } @@ -136,10 +136,10 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api. // set up high level api transactOpts := bind.NewKeyedTransactor(self.privateKey) - if backend == (*ethclient.Client)(nil) { - log.Warn("No ENS, please specify non-empty --ethapi to use domain name resolution") + if ensClient == nil { + log.Warn("No ENS, please specify non-empty --ens-api to use domain name resolution") } else { - self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend) + self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, ensClient) if err != nil { return nil, err }