// Copyright 2017-2019 Weald Technology Trading // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ens import ( "crypto/rand" "errors" "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) // Name represents an ENS name, for example 'foo.bar.eth'. type Name struct { backend bind.ContractBackend // Name is the fully-qualified name of an ENS domain e.g. foo.bar.eth Name string // Domain is the domain of an ENS domain e.g. bar.eth Domain string // Label is the name part of an ENS domain e.g. foo Label string // Contracts registry *Registry registrar *BaseRegistrar controller *ETHController } // NewName creates an ENS name structure. // Note that this does not create the name on-chain. func NewName(backend bind.ContractBackend, name string) (*Name, error) { name, err := NormaliseDomain(name) if err != nil { return nil, err } domain := Domain(name) label, err := DomainPart(name, 1) if err != nil { return nil, err } registry, err := NewRegistry(backend) if err != nil { return nil, err } registrar, err := NewBaseRegistrar(backend, domain) if err != nil { return nil, err } controller, err := NewETHController(backend, domain) if err != nil { return nil, err } isValid, err := controller.IsValid(name) if err != nil { return nil, err } if !isValid { return nil, errors.New("name is not valid according to the rules of the registrar (too short, invalid characters, etc.)") } return &Name{ backend: backend, Name: name, Domain: domain, Label: label, registry: registry, registrar: registrar, controller: controller, }, nil } // IsRegistered returns true if the name is registered in the registrar func (n *Name) IsRegistered() (bool, error) { registrant, err := n.Registrant() if err != nil { return false, err } return registrant != UnknownAddress, nil } // ExtendRegistration sends a transaction that extends the registration of the name. func (n *Name) ExtendRegistration(opts *bind.TransactOpts) (*types.Transaction, error) { isRegistered, err := n.IsRegistered() if err != nil { return nil, err } if !isRegistered { return nil, errors.New("name is not registered") } rentCost, err := n.RentCost() if err != nil { return nil, err } if opts.Value == nil || opts.Value.Cmp(rentCost) < 0 { return nil, errors.New("not enough funds to extend the registration") } return n.controller.Renew(opts, n.Name) } // RegistrationInterval obtains the minimum interval between commit and reveal // when registering this name. func (n *Name) RegistrationInterval() (time.Duration, error) { interval, err := n.controller.MinCommitmentInterval() if err != nil { return time.Duration(0), err } return time.Duration(interval.Int64()) * time.Second, nil } // RegisterStageOne sends a transaction that starts the registration process. func (n *Name) RegisterStageOne(registrant common.Address, opts *bind.TransactOpts) (*types.Transaction, [32]byte, error) { var secret [32]byte _, err := rand.Read(secret[:]) if err != nil { return nil, secret, err } isRegistered, err := n.IsRegistered() if err != nil { return nil, secret, err } if isRegistered { return nil, secret, errors.New("name is already registered") } signedTx, err := n.controller.Commit(opts, n.Label, registrant, secret) return signedTx, secret, err } // RegisterStageTwo sends a transaction that completes the registration process. // The registrant must be the same as supplied in RegisterStageOne. // The secret is that returned by RegisterStageOne. // At least RegistrationInterval() time must have passed since the stage one // transaction was mined for this to work. func (n *Name) RegisterStageTwo(registrant common.Address, secret [32]byte, opts *bind.TransactOpts) (*types.Transaction, error) { commitTS, err := n.controller.CommitmentTime(n.Label, registrant, secret) if err != nil { return nil, err } if commitTS.Cmp(big.NewInt(0)) == 0 { return nil, errors.New("stage 2 attempted prior to successful stage 1 transaction") } commit := time.Unix(commitTS.Int64(), 0) minCommitIntervalTS, err := n.controller.MinCommitmentInterval() if err != nil { return nil, err } minCommitInterval := time.Duration(minCommitIntervalTS.Int64()) * time.Second minStageTwoTime := commit.Add(minCommitInterval) if time.Now().Before(minStageTwoTime) { return nil, errors.New("too early to send second transaction") } maxCommitIntervalTS, err := n.controller.MaxCommitmentInterval() if err != nil { return nil, err } maxCommitInterval := time.Duration(maxCommitIntervalTS.Int64()) * time.Second maxStageTwoTime := commit.Add(maxCommitInterval) if time.Now().After(maxStageTwoTime) { return nil, errors.New("too late to send second transaction") } return n.controller.Reveal(opts, n.Label, registrant, secret) } // Expires obtain the time at which the registration for this name expires. func (n *Name) Expires() (time.Time, error) { expiryTS, err := n.registrar.Expiry(n.Label) if err != nil { return time.Unix(0, 0), err } if expiryTS.Int64() == 0 { return time.Unix(0, 0), errors.New("not registered") } return time.Unix(expiryTS.Int64(), 0), nil } // Controller obtains the controller for this name. // The controller can carry out operations on the name such as setting // records, but cannot transfer ultimate ownership of the name. func (n *Name) Controller() (common.Address, error) { return n.registry.Owner(n.Name) } // SetController sets the controller for this name. // The controller can carry out operations on the name such as setting // records, but cannot transfer ultimate ownership of the name. func (n *Name) SetController(controller common.Address, opts *bind.TransactOpts) (*types.Transaction, error) { // Are we the current controller? curController, err := n.Controller() if err != nil { return nil, err } if curController == opts.From { return n.registry.SetOwner(opts, n.Name, controller) } // Perhaps we are the registrant registrant, err := n.Registrant() if err != nil { return nil, err } // Are we actually trying to reclaim? if registrant == opts.From && opts.From == controller { return n.Reclaim(opts) } return nil, errors.New("not authorised to change the controller") } // Reclaim reclaims controller rights by the registrant func (n *Name) Reclaim(opts *bind.TransactOpts) (*types.Transaction, error) { // Ensure the we are the registrant registrant, err := n.Registrant() if err != nil { return nil, err } if registrant != opts.From { return nil, errors.New("not the registrant") } return n.registrar.Reclaim(opts, n.Name, registrant) } // Registrant obtains the registrant for this name. func (n *Name) Registrant() (common.Address, error) { return n.registrar.Owner(n.Label) } // Transfer transfers the registration of this name to a new registrant. func (n *Name) Transfer(registrant common.Address, opts *bind.TransactOpts) (*types.Transaction, error) { // Ensure the we are the registrant currentRegistrant, err := n.Registrant() if err != nil { return nil, err } if currentRegistrant != opts.From { return nil, errors.New("not the current registrant") } return n.registrar.SetOwner(opts, n.Label, registrant) } // RentCost returns the cost of rent in Wei-per-second. func (n *Name) RentCost() (*big.Int, error) { return n.controller.RentCost(n.Label) } // CreateSubdomain creates a subdomain on the name. func (n *Name) CreateSubdomain(label string, controller common.Address, opts *bind.TransactOpts) (*types.Transaction, error) { // Confirm the subdomain does not already exist fqdn := fmt.Sprintf("%s.%s", label, n.Name) subdomainController, err := n.registry.Owner(fqdn) if err != nil { return nil, err } if subdomainController != UnknownAddress { return nil, errors.New("that subdomain already exists") } return n.registry.SetSubdomainOwner(opts, n.Name, label, controller) } // ResolverAddress fetches the address of the resolver contract for the name. func (n *Name) ResolverAddress() (common.Address, error) { return n.registry.ResolverAddress(n.Name) } // SetResolverAddress sets the resolver contract address for the name. func (n *Name) SetResolverAddress(address common.Address, opts *bind.TransactOpts) (*types.Transaction, error) { return n.registry.SetResolver(opts, n.Name, address) } // Address fetches the address of the name for a given coin type. // Coin types are defined at https://github.com/satoshilabs/slips/blob/master/slip-0044.md func (n *Name) Address(coinType uint64) ([]byte, error) { resolver, err := NewResolver(n.backend, n.Name) if err != nil { return nil, err } return resolver.MultiAddress(coinType) } // SetAddress sets the address of the name for a given coin type. // Coin types are defined at https://github.com/satoshilabs/slips/blob/master/slip-0044.md func (n *Name) SetAddress(coinType uint64, address []byte, opts *bind.TransactOpts) (*types.Transaction, error) { resolver, err := NewResolver(n.backend, n.Name) if err != nil { return nil, err } return resolver.SetMultiAddress(opts, coinType, address) }