// minerHeaderHandlerGET handles the API call that retrieves a block header // for work. func (api *API) minerHeaderHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { bhfw, target, err := api.miner.HeaderForWork() if err != nil { WriteError(w, Error{err.Error()}, http.StatusBadRequest) return } w.Write(encoding.MarshalAll(target, bhfw)) }
// minerHeaderforworkHandler handles the API call that retrieves a block header // for work. func (srv *Server) minerHeaderforworkHandler(w http.ResponseWriter, req *http.Request) { bhfw, target, err := srv.miner.HeaderForWork() if err != nil { writeError(w, "call to /miner/headerforwork failed: "+err.Error(), http.StatusBadRequest) return } w.Write(encoding.MarshalAll(target, bhfw)) }
// minerHeaderHandlerGET handles the API call that retrieves a block header // for work. func (srv *Server) minerHeaderHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { bhfw, target, err := srv.miner.HeaderForWork() if err != nil { writeError(w, err.Error(), http.StatusBadRequest) return } w.Write(encoding.MarshalAll(target, bhfw)) }
// SigHash returns the hash of the fields in a transaction covered by a given // signature. See CoveredFields for more details. func (t Transaction) SigHash(i int) crypto.Hash { cf := t.TransactionSignatures[i].CoveredFields var signedData []byte if cf.WholeTransaction { signedData = encoding.MarshalAll( t.SiacoinInputs, t.SiacoinOutputs, t.FileContracts, t.FileContractRevisions, t.StorageProofs, t.SiafundInputs, t.SiafundOutputs, t.MinerFees, t.ArbitraryData, t.TransactionSignatures[i].ParentID, t.TransactionSignatures[i].PublicKeyIndex, t.TransactionSignatures[i].Timelock, ) } else { for _, input := range cf.SiacoinInputs { signedData = append(signedData, encoding.Marshal(t.SiacoinInputs[input])...) } for _, output := range cf.SiacoinOutputs { signedData = append(signedData, encoding.Marshal(t.SiacoinOutputs[output])...) } for _, contract := range cf.FileContracts { signedData = append(signedData, encoding.Marshal(t.FileContracts[contract])...) } for _, revision := range cf.FileContractRevisions { signedData = append(signedData, encoding.Marshal(t.FileContractRevisions[revision])...) } for _, storageProof := range cf.StorageProofs { signedData = append(signedData, encoding.Marshal(t.StorageProofs[storageProof])...) } for _, siafundInput := range cf.SiafundInputs { signedData = append(signedData, encoding.Marshal(t.SiafundInputs[siafundInput])...) } for _, siafundOutput := range cf.SiafundOutputs { signedData = append(signedData, encoding.Marshal(t.SiafundOutputs[siafundOutput])...) } for _, minerFee := range cf.MinerFees { signedData = append(signedData, encoding.Marshal(t.MinerFees[minerFee])...) } for _, arbData := range cf.ArbitraryData { signedData = append(signedData, encoding.Marshal(t.ArbitraryData[arbData])...) } } for _, sig := range cf.TransactionSignatures { signedData = append(signedData, encoding.Marshal(t.TransactionSignatures[sig])...) } return crypto.HashBytes(signedData) }
// minerHeaderforworkHandler handles the API call that retrieves a block header // for work. func (srv *Server) minerHeaderforworkHandler(w http.ResponseWriter, req *http.Request) { bhfw, target := srv.miner.HeaderForWork() w.Write(encoding.MarshalAll(target, bhfw)) }
// minerBlockforworkHandler handles the API call that retrieves a block for // work. func (srv *Server) minerBlockforworkHandler(w http.ResponseWriter, req *http.Request) { bfw, _, target := srv.miner.BlockForWork() w.Write(encoding.MarshalAll(target, bfw.Header(), bfw)) }
// HashAll takes a set of objects as input, encodes them all using the encoding // package, and then hashes the result. func HashAll(objs ...interface{}) Hash { return HashBytes(encoding.MarshalAll(objs...)) }
// threadedHandleActionItem will look at a storage obligation and determine // which action is necessary for the storage obligation to succeed. func (h *Host) threadedHandleActionItem(soid types.FileContractID, wg *sync.WaitGroup) { // The calling thread is responsible for calling Add to the thread group. defer wg.Done() // Lock the storage obligation in question. h.managedLockStorageObligation(soid) defer func() { h.managedUnlockStorageObligation(soid) }() // Convert the storage obligation id into a storage obligation. var err error var so storageObligation h.mu.RLock() blockHeight := h.blockHeight err = h.db.View(func(tx *bolt.Tx) error { so, err = getStorageObligation(tx, soid) return err }) h.mu.RUnlock() if err != nil { h.log.Println("Could not get storage obligation:", err) return } // Check whether the storage obligation has already been completed. if so.ObligationStatus != obligationUnresolved { // Storage obligation has already been completed, skip action item. return } // Check whether the file contract has been seen. If not, resubmit and // queue another action item. Check for death. (signature should have a // kill height) if !so.OriginConfirmed { // Submit the transaction set again, try to get the transaction // confirmed. err := h.tpool.AcceptTransactionSet(so.OriginTransactionSet) if err != nil { h.log.Debugln("Could not get origin transaction set accepted", err) // Check if the transaction is invalid with the current consensus set. // If so, the transaction is highly unlikely to ever be confirmed, and // the storage obligation should be removed. This check should come // after logging the errror so that the function can quit. // // TODO: If the host or tpool is behind consensus, might be difficult // to have certainty about the issue. If some but not all of the // parents are confirmed, might be some difficulty. _, t := err.(modules.ConsensusConflict) if t { h.log.Println("Consensus conflict on the origin transaction set, id", so.id()) h.mu.Lock() err = h.removeStorageObligation(so, obligationRejected) h.mu.Unlock() if err != nil { h.log.Println("Error removing storage obligation:", err) } return } } // Queue another action item to check the status of the transaction. h.mu.Lock() err = h.queueActionItem(h.blockHeight+resubmissionTimeout, so.id()) h.mu.Unlock() if err != nil { h.log.Println("Error queuing action item:", err) } } // Check if the file contract revision is ready for submission. Check for death. if !so.RevisionConfirmed && len(so.RevisionTransactionSet) > 0 && blockHeight > so.expiration()-revisionSubmissionBuffer { // Sanity check - there should be a file contract revision. rtsLen := len(so.RevisionTransactionSet) if rtsLen < 1 || len(so.RevisionTransactionSet[rtsLen-1].FileContractRevisions) != 1 { h.log.Critical("transaction revision marked as unconfirmed, yet there is no transaction revision") return } // Check if the revision has failed to submit correctly. if blockHeight > so.expiration() { // TODO: Check this error. // // TODO: this is not quite right, because a previous revision may // be confirmed, and the origin transaction may be confirmed, which // would confuse the revenue stuff a bit. Might happen frequently // due to the dynamic fee pool. h.log.Println("Full time has elapsed, but the revision transaction could not be submitted to consensus, id", so.id()) h.mu.Lock() h.removeStorageObligation(so, obligationRejected) h.mu.Unlock() return } // Queue another action item to check the status of the transaction. h.mu.Lock() err := h.queueActionItem(blockHeight+resubmissionTimeout, so.id()) h.mu.Unlock() if err != nil { h.log.Println("Error queuing action item:", err) } // Add a miner fee to the transaction and submit it to the blockchain. revisionTxnIndex := len(so.RevisionTransactionSet) - 1 revisionParents := so.RevisionTransactionSet[:revisionTxnIndex] revisionTxn := so.RevisionTransactionSet[revisionTxnIndex] builder := h.wallet.RegisterTransaction(revisionTxn, revisionParents) _, feeRecommendation := h.tpool.FeeEstimation() if so.value().Div64(2).Cmp(feeRecommendation) < 0 { // There's no sense submitting the revision if the fee is more than // half of the anticipated revenue - fee market went up // unexpectedly, and the money that the renter paid to cover the // fees is no longer enough. return } txnSize := uint64(len(encoding.MarshalAll(so.RevisionTransactionSet)) + 300) requiredFee := feeRecommendation.Mul64(txnSize) err = builder.FundSiacoins(requiredFee) if err != nil { h.log.Println("Error funding transaction fees", err) } builder.AddMinerFee(requiredFee) if err != nil { h.log.Println("Error adding miner fees", err) } feeAddedRevisionTransactionSet, err := builder.Sign(true) if err != nil { h.log.Println("Error signing transaction", err) } err = h.tpool.AcceptTransactionSet(feeAddedRevisionTransactionSet) if err != nil { h.log.Println("Error submitting transaction to transaction pool", err) } so.TransactionFeesAdded = so.TransactionFeesAdded.Add(requiredFee) // return } // Check whether a storage proof is ready to be provided, and whether it // has been accepted. Check for death. if !so.ProofConfirmed && blockHeight >= so.expiration()+resubmissionTimeout { h.log.Debugln("Host is attempting a storage proof for", so.id()) // If the window has closed, the host has failed and the obligation can // be removed. if so.proofDeadline() < blockHeight || len(so.SectorRoots) == 0 { h.log.Debugln("storage proof not confirmed by deadline, id", so.id()) h.mu.Lock() err := h.removeStorageObligation(so, obligationFailed) h.mu.Unlock() if err != nil { h.log.Println("Error removing storage obligation:", err) } return } // Get the index of the segment, and the index of the sector containing // the segment. segmentIndex, err := h.cs.StorageProofSegment(so.id()) if err != nil { h.log.Debugln("Host got an error when fetching a storage proof segment:", err) return } sectorIndex := segmentIndex / (modules.SectorSize / crypto.SegmentSize) // Pull the corresponding sector into memory. sectorRoot := so.SectorRoots[sectorIndex] sectorBytes, err := h.ReadSector(sectorRoot) if err != nil { h.log.Debugln(err) return } // Build the storage proof for just the sector. sectorSegment := segmentIndex % (modules.SectorSize / crypto.SegmentSize) base, cachedHashSet := crypto.MerkleProof(sectorBytes, sectorSegment) // Using the sector, build a cached root. log2SectorSize := uint64(0) for 1<<log2SectorSize < (modules.SectorSize / crypto.SegmentSize) { log2SectorSize++ } ct := crypto.NewCachedTree(log2SectorSize) ct.SetIndex(segmentIndex) for _, root := range so.SectorRoots { ct.Push(root) } hashSet := ct.Prove(base, cachedHashSet) sp := types.StorageProof{ ParentID: so.id(), HashSet: hashSet, } copy(sp.Segment[:], base) // Create and build the transaction with the storage proof. builder := h.wallet.StartTransaction() _, feeRecommendation := h.tpool.FeeEstimation() if so.value().Cmp(feeRecommendation) < 0 { // There's no sense submitting the storage proof if the fee is more // than the anticipated revenue. h.log.Debugln("Host not submitting storage proof due to a value that does not sufficiently exceed the fee cost") return } txnSize := uint64(len(encoding.Marshal(sp)) + 300) requiredFee := feeRecommendation.Mul64(txnSize) err = builder.FundSiacoins(requiredFee) if err != nil { h.log.Println("Host error when funding a storage proof transaction fee:", err) return } builder.AddMinerFee(requiredFee) builder.AddStorageProof(sp) storageProofSet, err := builder.Sign(true) if err != nil { h.log.Println("Host error when signing the storage proof transaction:", err) return } err = h.tpool.AcceptTransactionSet(storageProofSet) if err != nil { h.log.Println("Host unable to submit storage proof transaction to transaction pool:", err) return } so.TransactionFeesAdded = so.TransactionFeesAdded.Add(requiredFee) // Queue another action item to check whether there the storage proof // got confirmed. h.mu.Lock() err = h.queueActionItem(so.proofDeadline(), so.id()) h.mu.Unlock() if err != nil { h.log.Println("Error queuing action item:", err) } } // Save the storage obligation to account for any fee changes. err = h.db.Update(func(tx *bolt.Tx) error { soBytes, err := json.Marshal(so) if err != nil { return err } return tx.Bucket(bucketStorageObligations).Put(soid[:], soBytes) }) if err != nil { h.log.Println("Error updating the storage obligations", err) } // Check if all items have succeeded with the required confirmations. Report // success, delete the obligation. if so.ProofConfirmed && blockHeight >= so.proofDeadline() { h.log.Println("file contract complete, id", so.id()) h.mu.Lock() h.removeStorageObligation(so, obligationSucceeded) h.mu.Unlock() } }