gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/host/announce.go (about)

     1  package host
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  
     7  	"gitlab.com/NebulousLabs/errors"
     8  	"gitlab.com/SiaPrime/SiaPrime/build"
     9  	"gitlab.com/SiaPrime/SiaPrime/modules"
    10  )
    11  
    12  var (
    13  	// errAnnWalletLocked is returned during a host announcement if the wallet
    14  	// is locked.
    15  	errAnnWalletLocked = errors.New("cannot announce the host while the wallet is locked")
    16  
    17  	// errUnknownAddress is returned if the host is unable to determine a
    18  	// public address for itself to use in the announcement.
    19  	errUnknownAddress = errors.New("host cannot announce, does not seem to have a valid address")
    20  )
    21  
    22  // differentTypeIPs is a helper that returns true if two IPs are of a different
    23  // type.
    24  func differentTypeIPs(ip1, ip2 net.IP) bool {
    25  	return (ip1.To4() == nil) != (ip2.To4() == nil)
    26  }
    27  
    28  // staticVerifyAnnouncementAddress checks that the address is sane and not local.
    29  func (h *Host) staticVerifyAnnouncementAddress(addr modules.NetAddress) error {
    30  	// Check that the address is sane, and that the address is also not local.
    31  	if err := addr.IsStdValid(); err != nil {
    32  		return build.ExtendErr("announcement requested with bad net address", err)
    33  	}
    34  	if addr.IsLocal() && build.Release != "testing" {
    35  		return errors.New("announcement requested with local net address")
    36  	}
    37  	// Make sure that the host resolves to 1 or 2 IPs and if it resolves to 2
    38  	// the type should be different.
    39  	ips, err := h.dependencies.LookupIP(addr.Host())
    40  	if err != nil {
    41  		return errors.AddContext(err, "failed to lookup hostname "+addr.Host())
    42  	}
    43  	if len(ips) < 1 {
    44  		return fmt.Errorf("host %s doesn't resolve to any IP addresses", addr.Host())
    45  	}
    46  	if len(ips) == 2 && !differentTypeIPs(ips[0], ips[1]) {
    47  		return fmt.Errorf("host %s resolves to 2 IPs of the same type", addr.Host())
    48  	}
    49  	if len(ips) > 2 {
    50  		return fmt.Errorf("host %s resolves to more than 2 IP addresses", addr.Host())
    51  	}
    52  	return nil
    53  }
    54  
    55  // managedAnnounce creates an announcement transaction and submits it to the network.
    56  func (h *Host) managedAnnounce(addr modules.NetAddress) (err error) {
    57  	// Verify address first.
    58  	if err := h.staticVerifyAnnouncementAddress(addr); err != nil {
    59  		return err
    60  	}
    61  
    62  	// The wallet needs to be unlocked to add fees to the transaction, and the
    63  	// host needs to have an active unlock hash that renters can make payment
    64  	// to.
    65  	unlocked, err := h.wallet.Unlocked()
    66  	if err != nil {
    67  		return err
    68  	}
    69  	if !unlocked {
    70  		return errAnnWalletLocked
    71  	}
    72  
    73  	h.mu.Lock()
    74  	pubKey := h.publicKey
    75  	secKey := h.secretKey
    76  	err = h.checkUnlockHash()
    77  	h.mu.Unlock()
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	// Create the announcement that's going to be added to the arbitrary data
    83  	// field of the transaction.
    84  	signedAnnouncement, err := modules.CreateAnnouncement(addr, pubKey, secKey)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	// Create a transaction, with a fee, that contains the full announcement.
    90  	txnBuilder, err := h.wallet.StartTransaction()
    91  	if err != nil {
    92  		return err
    93  	}
    94  	defer func() {
    95  		if err != nil {
    96  			txnBuilder.Drop()
    97  		}
    98  	}()
    99  	_, fee := h.tpool.FeeEstimation()
   100  	fee = fee.Mul64(600) // Estimated txn size (in bytes) of a host announcement.
   101  	err = txnBuilder.FundSiacoins(fee)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	_ = txnBuilder.AddMinerFee(fee)
   106  	_ = txnBuilder.AddArbitraryData(signedAnnouncement)
   107  	txnSet, err := txnBuilder.Sign(true)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	// Add the transactions to the transaction pool.
   113  	err = h.tpool.AcceptTransactionSet(txnSet)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	h.mu.Lock()
   119  	h.announced = true
   120  	h.mu.Unlock()
   121  	h.log.Printf("INFO: Successfully announced as %v", addr)
   122  	return nil
   123  }
   124  
   125  // Announce creates a host announcement transaction.
   126  func (h *Host) Announce() error {
   127  	err := h.tg.Add()
   128  	if err != nil {
   129  		return err
   130  	}
   131  	defer h.tg.Done()
   132  
   133  	// Grab the internal net address and internal auto address, and compare
   134  	// them.
   135  	h.mu.RLock()
   136  	userSet := h.settings.NetAddress
   137  	autoSet := h.autoAddress
   138  	h.mu.RUnlock()
   139  
   140  	// Check that we have at least one address to work with.
   141  	if userSet == "" && autoSet == "" {
   142  		return errors.New("cannot announce because address could not be determined")
   143  	}
   144  
   145  	// Prefer using the userSet address, otherwise use the automatic address.
   146  	var annAddr modules.NetAddress
   147  	if userSet != "" {
   148  		annAddr = userSet
   149  	} else {
   150  		annAddr = autoSet
   151  	}
   152  
   153  	// Address has cleared inspection, perform the announcement.
   154  	return h.managedAnnounce(annAddr)
   155  }
   156  
   157  // AnnounceAddress submits a host announcement to the blockchain to announce a
   158  // specific address. If there is no error, the host's address will be updated
   159  // to the supplied address.
   160  func (h *Host) AnnounceAddress(addr modules.NetAddress) error {
   161  	err := h.tg.Add()
   162  	if err != nil {
   163  		return err
   164  	}
   165  	defer h.tg.Done()
   166  
   167  	// Attempt the actual announcement.
   168  	err = h.managedAnnounce(addr)
   169  	if err != nil {
   170  		return build.ExtendErr("unable to perform manual host announcement", err)
   171  	}
   172  
   173  	// Address is valid, update the host's internal net address to match the
   174  	// specified addr.
   175  	h.mu.Lock()
   176  	h.settings.NetAddress = addr
   177  	h.mu.Unlock()
   178  	return nil
   179  }