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 }