예제 #1
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
func (a *BuyActivity) requestMissingChunks(log bitwrk.Logger, client *http.Client) (io.ReadCloser, error) {
	// Send chunk list of work to client
	pipeIn, pipeOut := io.Pipe()
	defer pipeIn.Close()
	mwriter := multipart.NewWriter(pipeOut)

	// Write chunk hashes into pipe for HTTP request
	go func() {
		defer pipeOut.Close()
		if err := a.encodeChunkedFirstTransmission(log, mwriter); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		if err := mwriter.Close(); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		log.Printf("Work chunk hashes transmitted successfully.")
	}()

	if r, err := a.postToSeller(pipeIn, mwriter.FormDataContentType(), false, client); err != nil {
		return nil, fmt.Errorf("Error sending work chunk hashes to seller: %v", err)
	} else {
		return r, nil
	}
}
예제 #2
0
파일: buy.go 프로젝트: DaGitNah/bitwrk
func (a *BuyActivity) sendMissingChunksAndReturnResult(log bitwrk.Logger, client *http.Client, wishList []byte) (io.ReadCloser, error) {
	// Send data of missing chunks to seller
	pipeIn, pipeOut := io.Pipe()
	defer pipeIn.Close()
	mwriter := multipart.NewWriter(pipeOut)

	// Write work chunks into pipe for HTTP request
	go func() {
		defer pipeOut.Close()
		if part, err := mwriter.CreateFormFile("chunkdata", "chunkdata.bin"); err != nil {
			pipeOut.CloseWithError(err)
			return
		} else if err := cafs.WriteRequestedChunks(a.workFile, wishList, part); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		if err := mwriter.Close(); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		log.Printf("Missing chunk data transmitted successfully.")
	}()

	if r, err := a.postToSeller(pipeIn, mwriter.FormDataContentType(), client); err != nil {
		return nil, fmt.Errorf("Error sending work chunk data to seller: %v", err)
	} else {
		return r, nil
	}
}
예제 #3
0
func (t *Trade) updateTransaction(log bitwrk.Logger) error {
	attemptsLeft := 3
	for attemptsLeft > 0 {
		attemptsLeft--
		if tx, etag, err := FetchTx(t.txId, ""); err != nil {
			log.Printf("Error updating transaction: %v (attempts left: %d)", err, attemptsLeft)
			if attemptsLeft > 0 {
				time.Sleep(5 * time.Second)
			} else {
				return err
			}
		} else {
			expired := false
			func() {
				t.condition.L.Lock()
				defer t.condition.L.Unlock()
				if etag != t.txETag {
					t.tx = tx
					t.txETag = etag
					expired = t.tx.State != bitwrk.StateActive
					t.condition.Broadcast()
					log.Printf("Tx change detected: phase=%v, expired=%v", t.tx.Phase, expired)
				}
			}()
			if expired {
				break
			}
		}
	}
	return nil
}
예제 #4
0
파일: sell.go 프로젝트: DaGitNah/bitwrk
// Manages the complete lifecycle of a sell
func (a *SellActivity) PerformSell(log bitwrk.Logger, receiveManager *ReceiveManager, interrupt <-chan bool) error {
	defer log.Println("Sell finished")
	err := a.doPerformSell(log, receiveManager, interrupt)
	if err != nil {
		a.lastError = err
	}
	a.alive = false
	return err
}
예제 #5
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
func (a *BuyActivity) sendMissingChunksAndReturnResult(log bitwrk.Logger, client *http.Client, wishList []byte, compressed bool) (io.ReadCloser, error) {
	// Send data of missing chunks to seller
	pipeIn, pipeOut := io.Pipe()
	defer pipeIn.Close()

	// Setup compression layer with dummy impl in case of uncompressed transmisison
	var compressor io.Writer
	var closeCompressor func() error
	if compressed {
		c := gzip.NewWriter(pipeOut)
		compressor = c
		closeCompressor = c.Close
	} else {
		compressor = pipeOut
		closeCompressor = func() error { return nil }
	}

	mwriter := multipart.NewWriter(compressor)

	// Communicate status back
	progressCallback := func(bytesToTransfer, bytesTransferred uint64) {
		a.execSync(func() {
			a.bytesToTransfer = bytesToTransfer
			a.bytesTransferred = bytesTransferred
		})
	}

	// Write work chunks into pipe for HTTP request
	go func() {
		defer pipeOut.Close()
		if part, err := mwriter.CreateFormFile("chunkdata", "chunkdata.bin"); err != nil {
			pipeOut.CloseWithError(err)
			return
		} else if err := cafs.WriteRequestedChunks(a.workFile, wishList, part, progressCallback); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		if err := mwriter.Close(); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		if err := closeCompressor(); err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		log.Printf("Missing chunk data transmitted successfully.")
	}()

	if r, err := a.postToSeller(pipeIn, mwriter.FormDataContentType(), compressed, client); err != nil {
		return nil, fmt.Errorf("Error sending work chunk data to seller: %v", err)
	} else {
		return r, nil
	}
}
예제 #6
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
func (a *BuyActivity) transmitWorkChunked(log bitwrk.Logger, client *http.Client, compressed bool) (io.ReadCloser, error) {
	if r, err := a.requestMissingChunks(log.New("request missing chunks"), client); err != nil {
		return nil, err
	} else {
		defer r.Close()
		numChunks := a.workFile.NumChunks()
		if numChunks > MaxNumberOfChunksInWorkFile {
			return nil, fmt.Errorf("Work file too big: %d chunks (only %d allowed).", numChunks, MaxNumberOfChunksInWorkFile)
		}
		wishList := make([]byte, int(numChunks+7)/8)
		if _, err := io.ReadFull(r, wishList); err != nil {
			return nil, fmt.Errorf("Error decoding list of missing chunks: %v", err)
		}
		return a.sendMissingChunksAndReturnResult(log.New("send work chunk data"), client, wishList, compressed)
	}
}
예제 #7
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
func (a *BuyActivity) encodeChunkedFirstTransmission(log bitwrk.Logger, mwriter *multipart.Writer) (err error) {
	part, err := mwriter.CreateFormFile("a32chunks", "a32chunks.bin")
	if err != nil {
		return
	}
	log.Printf("Sending work chunk hashes to seller [%v].", *a.tx.WorkerURL)
	err = cafs.WriteChunkHashes(a.workFile, part)
	if err != nil {
		return
	}
	log.Printf("Sending buyer's secret to seller.")
	err = mwriter.WriteField("buyersecret", a.buyerSecret.String())
	if err != nil {
		return
	}
	return mwriter.Close()
}
예제 #8
0
// Closes resources upon exit of a function or when some condition no longer holds
// Arguments:
//  - exitChan: Signals the watchdog to exit
//  - closerChan: Signals the watchdog to add an io.Closer to the list of closers
//  - f: Defines the OK condition. When false, all current closers are closed
func (t *Trade) watchdog(log bitwrk.Logger, exitChan <-chan bool, closerChan <-chan io.Closer, f func() bool) {
	closers := make([]io.Closer, 0, 1)
	for {
		select {
		case closer := <-closerChan:
			closers = append(closers, closer)
		case <-exitChan:
			// Exit from watchdog if surrounding function has terminated
			log.Print("Watchdog exiting by request")
			return
		default:
		}

		// Check if condition still holds
		t.condition.L.Lock()
		ok := f()
		t.condition.L.Unlock()
		if !ok {
			log.Printf("Watchdog: closing %v channels", len(closers))
			for _, c := range closers {
				err := c.Close()
				if err != nil {
					log.Printf("Error closing channel: %v", err)
				}
			}
			closers = closers[:0] // clear list of current closers
		}

		time.Sleep(250 * time.Millisecond)
	}
}
예제 #9
0
파일: worker.go 프로젝트: DaGitNah/bitwrk
func (s *WorkerState) offer(log bitwrk.Logger) {
	defer log.Printf("Stopped offering")
	s.cond.L.Lock()
	defer s.cond.L.Unlock()
	interrupt := make(chan bool, 1)
	for {
		// Interrupt if unregistered, stop iterating
		if s.Unregistered {
			log.Printf("No longer registered")
			interrupt <- true
			break
		}
		if s.Blockers == 0 {
			s.LastError = ""
			if sell, err := s.m.activityManager.NewSell(s); err != nil {
				s.LastError = fmt.Sprintf("Error creating sell: %v", err)
				log.Println(s.LastError)
				s.blockFor(20 * time.Second)
			} else {
				s.Blockers++
				go s.executeSell(log, sell, interrupt)
			}
		}
		s.cond.Wait()
	}
}
예제 #10
0
func (t *Trade) awaitTransaction(log bitwrk.Logger) error {
	lastETag := ""
	for count := 1; ; count++ {
		if bid, etag, err := FetchBid(t.bidId, lastETag); err != nil {
			return fmt.Errorf("Error in FetchBid awaiting transaction: %v", err)
		} else if bid != nil {
			log.Printf("Bid: %#v ETag: %v lastETag: %v", *bid, etag, lastETag)
			t.bid = bid
			lastETag = etag
			if t.bid.State == bitwrk.Matched {
				t.txId = *t.bid.Transaction
				break
			} else if t.bid.State == bitwrk.Expired {
				return ErrBidExpired
			}
		}

		// Sleep for gradually longer durations
		time.Sleep(time.Duration(count) * 500 * time.Millisecond)
	}
	return nil
}
예제 #11
0
func NewWorkReceiver(log bitwrk.Logger,
	info string,
	receiveManager *ReceiveManager,
	storage cafs.FileStorage,
	key bitwrk.Tkey,
	handler WorkHandler) WorkReceiver {
	result := &endpointReceiver{
		endpoint:     receiveManager.NewEndpoint(info),
		storage:      storage,
		log:          log,
		handler:      handler,
		info:         info,
		encResultKey: key,
	}
	result.endpoint.SetHandler(func(w http.ResponseWriter, r *http.Request) {
		if err := result.handleRequest(w, r); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Printf("Error in request: %v", err)
			result.Dispose()
		}
	})
	return result
}
예제 #12
0
func NewWorkReceiver(log bitwrk.Logger,
	info string,
	receiveManager *ReceiveManager,
	storage cafs.FileStorage,
	key bitwrk.Tkey,
	handler WorkHandler) WorkReceiver {
	result := &endpointReceiver{
		endpoint:     receiveManager.NewEndpoint(info),
		storage:      storage,
		log:          log,
		handler:      handler,
		info:         info,
		encResultKey: key,
	}
	handlerFunc := func(w http.ResponseWriter, r *http.Request) {
		if err := result.handleRequest(w, r); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Printf("Error in request: %v", err)
			result.doDispose(fmt.Errorf("Error handling request from %v: %v", r.RemoteAddr, err))
		}
	}
	result.endpoint.SetHandler(withCompression(handlerFunc))
	return result
}
예제 #13
0
// Polls the transaction state in a separate go-routine. Returns on abort signal, or
// when the polled transaction expires.
func (t *Trade) pollTransaction(log bitwrk.Logger, abort <-chan bool) {
	defer func() {
		log.Printf("Transaction polling has stopped")
	}()

	for count := 1; ; count++ {
		select {
		case <-abort:
			log.Printf("Aborting transaction polling while transaction active")
			return
		default:
		}

		if tx, etag, err := FetchTx(t.txId, ""); err != nil {
			log.Printf("Error polling transaction: %v", err)
		} else if etag != t.txETag {
			t.condition.L.Lock()
			t.tx = tx
			t.txETag = etag
			expired := t.tx.State != bitwrk.StateActive
			t.condition.Broadcast()
			t.condition.L.Unlock()
			log.Printf("Tx change detected: phase=%v, expired=%v", t.tx.Phase, expired)
			if expired {
				break
			}
		}

		// Sleep for gradually longer durations
		time.Sleep(time.Duration(count) * 500 * time.Millisecond)
	}

	log.Printf("Transaction has expired.")
	// This is necessary so that the surrounding function call doesn't deadlock
	<-abort
}
예제 #14
0
func (a *Trade) beginTrade(log bitwrk.Logger, interrupt <-chan bool) error {
	// wait for grant or reject
	log.Println("Waiting for permission")

	// Get a permission for the sell
	if err := a.awaitPermission(interrupt); err != nil {
		return fmt.Errorf("Error awaiting permission: %v", err)
	}
	log.Printf("Got permission. Price: %v", a.price)

	// Prevent too many unmatched bids on server
	key := fmt.Sprintf("%v-%v", a.bidType, a.article)
	if err := a.manager.checkoutToken(key, NumUnmatchedBids, interrupt); err != nil {
		return err
	}
	defer a.manager.returnToken(key)

	if err := a.awaitBid(); err != nil {
		return fmt.Errorf("Error awaiting bid: %v", err)
	}
	log.Printf("Got bid id: %v", a.bidId)

	if err := a.awaitTransaction(log); err != nil {
		return fmt.Errorf("Error awaiting transaction: %v", err)
	}
	log.Printf("Got transaction id: %v", a.txId)

	if tx, etag, err := FetchTx(a.txId, ""); err != nil {
		return err
	} else {
		a.tx = tx
		a.txETag = etag
	}

	return nil
}
예제 #15
0
파일: worker.go 프로젝트: DaGitNah/bitwrk
func (s *WorkerState) executeSell(log bitwrk.Logger, sell *SellActivity, interrupt <-chan bool) {
	defer func() {
		s.cond.L.Lock()
		s.Blockers--
		s.cond.Broadcast()
		s.cond.L.Unlock()
	}()
	defer sell.Dispose()
	if err := sell.PerformSell(log.Newf("Sell #%v", sell.GetKey()), s.m.receiveManager, interrupt); err != nil {
		s.LastError = fmt.Sprintf("Error performing sell (delaying next sell by 20s): %v", err)
		log.Println(s.LastError)
		s.cond.L.Lock()
		s.blockFor(20 * time.Second)
		s.cond.L.Unlock()
	} else {
		log.Printf("Returned from PerformSell successfully")
	}
}
예제 #16
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
func (a *BuyActivity) transmitWorkLinear(log bitwrk.Logger, client *http.Client) (io.ReadCloser, error) {
	// Send work to client
	pipeIn, pipeOut := io.Pipe()
	mwriter := multipart.NewWriter(pipeOut)

	// Write work file into pipe for HTTP request
	go func() {
		part, err := mwriter.CreateFormFile("work", "workfile.bin")
		if err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		work := a.workFile.Open()
		log.Printf("Sending work data to seller [%v].", *a.tx.WorkerURL)
		_, err = io.Copy(part, work)
		work.Close()
		if err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		log.Printf("Sending buyer's secret to seller.")
		err = mwriter.WriteField("buyersecret", a.buyerSecret.String())
		if err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		err = mwriter.Close()
		if err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		err = pipeOut.Close()
		if err != nil {
			pipeOut.CloseWithError(err)
			return
		}
		log.Printf("Work transmitted successfully.")
	}()

	return a.postToSeller(pipeIn, mwriter.FormDataContentType(), false, client)
}
예제 #17
0
func (t *Trade) waitForTransactionPhase(log bitwrk.Logger, phase bitwrk.TxPhase, viaPhases ...bitwrk.TxPhase) error {
	log.Printf("Waiting for transaction phase %v...", phase)

	if err := t.updateTransaction(log); err != nil {
		return err
	}

	var currentPhase bitwrk.TxPhase
	var currentState bitwrk.TxState
	t.waitWhile(func() bool {
		currentPhase = t.tx.Phase
		currentState = t.tx.State
		log.Printf("Phase: %v - State: %v", currentPhase, currentState)
		if currentState != bitwrk.StateActive {
			return false
		}
		if currentPhase == phase {
			return false
		}
		valid := false
		for _, via := range viaPhases {
			if currentPhase == via {
				valid = true
				break
			}
		}
		return valid
	})
	if currentState != bitwrk.StateActive {
		return ErrTxExpired
	}

	if currentPhase == phase {
		log.Printf("Phase %v reached.", phase)
		return nil
	}

	return ErrTxUnexpectedState
}
예제 #18
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
func (a *BuyActivity) doPerformBuy(log bitwrk.Logger, interrupt <-chan bool) (cafs.File, error) {
	if err := a.beginTrade(log, interrupt); err != nil {
		return nil, err
	}

	// draw random bytes for buyer's secret
	var secret bitwrk.Thash
	if _, err := rand.Reader.Read(secret[:]); err != nil {
		return nil, err
	}
	a.execSync(func() { a.buyerSecret = &secret })
	log.Printf("Computed buyer's secret.")

	// Get work hash
	var workHash, workSecretHash bitwrk.Thash
	workHash = bitwrk.Thash(a.workFile.Key())

	// compute workSecretHash = hash(workHash | secret)
	hash := sha256.New()
	hash.Write(workHash[:])
	hash.Write(secret[:])
	hash.Sum(workSecretHash[:0])

	// Start polling for transaction state changes in background
	abortPolling := make(chan bool)
	defer func() {
		abortPolling <- true // Stop polling when sell has ended
	}()
	go func() {
		a.pollTransaction(log, abortPolling)
	}()

	if err := SendTxMessageEstablishBuyer(a.txId, a.identity, workHash, workSecretHash); err != nil {
		return nil, fmt.Errorf("Error establishing buyer: %v", err)
	}

	if err := a.waitForTransactionPhase(log.New("establishing"), bitwrk.PhaseTransmitting, bitwrk.PhaseEstablishing, bitwrk.PhaseSellerEstablished, bitwrk.PhaseBuyerEstablished); err != nil {
		return nil, fmt.Errorf("Error awaiting TRANSMITTING phase: %v", err)
	}

	var sellerErr error
	if err := a.interactWithSeller(log.New("transmitting")); err != nil {
		sellerErr = fmt.Errorf("Error transmitting work and receiving encrypted result: %v", err)
	}

	var phaseErr error
	if err := a.waitForTransactionPhase(log, bitwrk.PhaseUnverified, bitwrk.PhaseTransmitting, bitwrk.PhaseWorking); err != nil {
		phaseErr = fmt.Errorf("Error awaiting UNVERIFIED phase: %v", err)
	}

	if sellerErr == nil && phaseErr == nil {
		// Everythong went fine, continue
	} else if sellerErr == nil {
		return nil, phaseErr
	} else if phaseErr == nil {
		return nil, sellerErr
	} else {
		return nil, fmt.Errorf("%v. Additionally: %v", phaseErr, sellerErr)
	}

	a.execSync(func() { a.encResultKey = a.tx.ResultDecryptionKey })

	if err := a.decryptResult(); err != nil {
		return nil, fmt.Errorf("Error decrypting result: %v", err)
	}

	// In normal buys (without verifying), we can leave the rest as homework
	// for a goroutine and exit here.
	go func() {
		if err := a.finishBuy(log); err != nil {
			log.Printf("Error finishing buy: %v", err)
		}
	}()

	return a.resultFile, nil
}
예제 #19
0
파일: sell.go 프로젝트: DaGitNah/bitwrk
func (a *SellActivity) HandleWork(log bitwrk.Logger, workFile cafs.File, buyerSecret bitwrk.Thash) (io.ReadCloser, error) {
	// Wait for buyer to establish
	active := true
	var workHash, workSecretHash *bitwrk.Thash
	log.Printf("Watching transaction state...")
	a.waitWhile(func() bool {
		active = a.tx.State == bitwrk.StateActive
		workHash, workSecretHash = a.tx.WorkHash, a.tx.WorkSecretHash
		log.Printf("  state: %v    phase: %v", a.tx.State, a.tx.Phase)
		return active && a.tx.Phase != bitwrk.PhaseTransmitting
	})

	if !active {
		return nil, fmt.Errorf("Transaction timed out waiting for buyer to establish")
	}

	// Verify work hash
	if *workHash != bitwrk.Thash(workFile.Key()) {
		return nil, fmt.Errorf("WorkHash and received data do not match")
	}

	if err := verifyBuyerSecret(workHash, workSecretHash, &buyerSecret); err != nil {
		return nil, err
	}

	log.Println("Got valid work data. Publishing buyer's secret.")
	if err := SendTxMessagePublishBuyerSecret(a.txId, a.identity, &buyerSecret); err != nil {
		return nil, fmt.Errorf("Error publishing buyer's secret: %v", err)
	}

	log.Println("Starting to work...")
	r, err := a.dispatchWork(log, workFile)
	if err != nil {
		log.Printf("Rejecting work because of error '%v'", err)
		if err := SendTxMessageRejectWork(a.txId, a.identity); err != nil {
			log.Printf("Rejecting work failed: %v", err)
		}
	}
	return r, err
}
예제 #20
0
파일: buy.go 프로젝트: TimSylvester/bitwrk
// Performs all buyer to seller conact.
// First queries the seller via HTTP OPTIONS whether chunked transmission is supported.
// If yes, a chunk list is transmitted, followed by data of missing work data chunks.
// Otherwise, work data is transferred linearly.
// The result is either an error or nil. In the latter case, a.encResultFile contains
// the result data encrypted with a key that the seller will hand out after we have signed
// a receipt for the encrypted result.
func (a *BuyActivity) interactWithSeller(log bitwrk.Logger) error {
	// Use a watchdog to make sure that all connnections created in the call time of this
	// function are closed when the transaction leaves the active state or the allowed
	// phases.
	// Transaction polling is guaranteed by the calling function.
	exitChan := make(chan bool)
	connChan := make(chan io.Closer)
	go a.watchdog(log, exitChan, connChan, func() bool {
		return a.tx.State == bitwrk.StateActive &&
			(a.tx.Phase == bitwrk.PhaseSellerEstablished ||
				a.tx.Phase == bitwrk.PhaseTransmitting ||
				a.tx.Phase == bitwrk.PhaseWorking)
	})
	defer func() {
		exitChan <- true
	}()

	st := NewScopedTransport()
	connChan <- st
	defer st.Close()
	scopedClient := NewClient(&st.Transport)

	chunked := false
	compressed := false
	if a.workFile.IsChunked() {
		if chunkedSupported, compressedSupported, err := a.testSellerForCapabilities(log, scopedClient); err != nil {
			log.Printf("Failed to probe seller for capabilities: %v", err)
		} else {
			chunked = chunkedSupported
			compressed = compressedSupported
			log.Printf("Chunked/compressed work transmission supported by seller: %v/%v", chunked, compressed)
		}
	}

	var response io.ReadCloser
	var transmissionError error
	if chunked {
		response, transmissionError = a.transmitWorkChunked(log, scopedClient, compressed)
	} else {
		response, transmissionError = a.transmitWorkLinear(log, scopedClient)
	}
	if response != nil {
		defer response.Close()
	}
	if transmissionError != nil {
		return transmissionError
	}

	temp := a.manager.storage.Create(fmt.Sprintf("Buy #%v: encrypted result", a.GetKey()))
	defer temp.Dispose()

	if _, err := io.Copy(temp, response); err != nil {
		return err
	}
	if err := response.Close(); err != nil {
		return err
	}
	if err := temp.Close(); err != nil {
		return err
	}

	a.execSync(func() { a.encResultFile = temp.File() })

	if err := a.signReceipt(scopedClient); err != nil {
		return fmt.Errorf("Error signing receipt for encrypted result: %v", err)
	}

	return nil
}