<commit_before>// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"bytes"

	"github.com/roasbeef/btcd/txscript"
	"github.com/roasbeef/btcwallet/chain"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"github.com/roasbeef/btcwallet/walletdb"
	"github.com/roasbeef/btcwallet/wtxmgr"
)

func (w *Wallet) handleChainNotifications() {
	chainClient, err := w.requireChainClient()
	if err != nil {
		log.Errorf("handleChainNotifications called without RPC client")
		w.wg.Done()
		return
	}

	sync := func(w *Wallet) {
		// At the moment there is no recourse if the rescan fails for
		// some reason, however, the wallet will not be marked synced
		// and many methods will error early since the wallet is known
		// to be out of date.
		err := w.syncWithChain()
		if err != nil && !w.ShuttingDown() {
			log.Warnf("Unable to synchronize wallet to chain: %v", err)
		}
	}

	catchUpHashes := func(w *Wallet, client chain.Interface,
		height int32) error {
		// TODO(aakselrod): There's a race conditon here, which
		// happens when a reorg occurs between the
		// rescanProgress notification and the last GetBlockHash
		// call. The solution when using btcd is to make btcd
		// send blockconnected notifications with each block
		// the way Neutrino does, and get rid of the loop. The
		// other alternative is to check the final hash and,
		// if it doesn't match the original hash returned by
		// the notification, to roll back and restart the
		// rescan.
		log.Infof("Catching up block hashes to height %d, this"+
			" might take a while", height)
		err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
			ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
			startBlock := w.Manager.SyncedTo()
			for i := startBlock.Height + 1; i <= height; i++ {
				hash, err := client.GetBlockHash(int64(i))
				if err != nil {
					return err
				}
				bs := waddrmgr.BlockStamp{
					Height: i,
					Hash:   *hash,
				}
				err = w.Manager.SetSyncedTo(ns, &bs)
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			log.Errorf("Failed to update address manager "+
				"sync state for height %d: %v", height, err)
		}
		log.Info("Done catching up block hashes")
		return err
	}

	for n := range chainClient.Notifications() {
		var notificationName string
		var err error
		switch n := n.(type) {
		case chain.ClientConnected:
			go sync(w)
		case chain.BlockConnected:
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.connectBlock(tx, wtxmgr.BlockMeta(n))
			})
			notificationName = "blockconnected"
		case chain.BlockDisconnected:
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.disconnectBlock(tx, wtxmgr.BlockMeta(n))
			})
			notificationName = "blockdisconnected"
		case chain.RelevantTx:
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.addRelevantTx(tx, n.TxRecord, n.Block)
			})
			notificationName = "recvtx/redeemingtx"
		case chain.FilteredBlockConnected:
			// Atomically update for the whole block.
			if len(n.RelevantTxs) > 0 {
				err = walletdb.Update(w.db, func(
					tx walletdb.ReadWriteTx) error {
					var err error
					for _, rec := range n.RelevantTxs {
						err = w.addRelevantTx(tx, rec,
							n.Block)
						if err != nil {
							return err
						}
					}
					return nil
				})
			}
			notificationName = "filteredblockconnected"

		// The following require some database maintenance, but also
		// need to be reported to the wallet's rescan goroutine.
		case *chain.RescanProgress:
			err = catchUpHashes(w, chainClient, n.Height)
			notificationName = "rescanprogress"
			w.rescanNotifications <- n
		case *chain.RescanFinished:
			err = catchUpHashes(w, chainClient, n.Height)
			notificationName = "rescanprogress"
			w.SetChainSynced(true)
			w.rescanNotifications <- n
		}
		if err != nil {
			log.Errorf("Failed to process consensus server notification "+
				"(name: `%s`, detail: `%v`)", notificationName, err)
		}
	}
	w.wg.Done()
}

// connectBlock handles a chain server notification by marking a wallet
// that's currently in-sync with the chain server as being synced up to
// the passed block.
func (w *Wallet) connectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)

	bs := waddrmgr.BlockStamp{
		Height: b.Height,
		Hash:   b.Hash,
	}
	err := w.Manager.SetSyncedTo(addrmgrNs, &bs)
	if err != nil {
		return err
	}

	// Notify interested clients of the connected block.
	//
	// TODO: move all notifications outside of the database transaction.
	w.NtfnServer.notifyAttachedBlock(dbtx, &b)
	return nil
}

// disconnectBlock handles a chain server reorganize by rolling back all
// block history from the reorged block for a wallet in-sync with the chain
// server.
func (w *Wallet) disconnectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
	txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)

	if !w.ChainSynced() {
		return nil
	}

	// Disconnect the removed block and all blocks after it if we know about
	// the disconnected block. Otherwise, the block is in the future.
	if b.Height <= w.Manager.SyncedTo().Height {
		hash, err := w.Manager.BlockHash(addrmgrNs, b.Height)
		if err != nil {
			return err
		}
		if bytes.Equal(hash[:], b.Hash[:]) {
			bs := waddrmgr.BlockStamp{
				Height: b.Height - 1,
			}
			hash, err = w.Manager.BlockHash(addrmgrNs, bs.Height)
			if err != nil {
				return err
			}
			b.Hash = *hash
			err = w.Manager.SetSyncedTo(addrmgrNs, &bs)
			if err != nil {
				return err
			}
			err = w.TxStore.Rollback(txmgrNs, b.Height)
		}
	}

	// Notify interested clients of the disconnected block.
	w.NtfnServer.notifyDetachedBlock(&b.Hash)

	return nil
}

func (w *Wallet) addRelevantTx(dbtx walletdb.ReadWriteTx, rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
	txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)

	// At the moment all notified transactions are assumed to actually be
	// relevant.  This assumption will not hold true when SPV support is
	// added, but until then, simply insert the transaction because there
	// should either be one or more relevant inputs or outputs.
	err := w.TxStore.InsertTx(txmgrNs, rec, block)
	if err != nil {
		return err
	}

	// Check every output to determine whether it is controlled by a wallet
	// key.  If so, mark the output as a credit.
	for i, output := range rec.MsgTx.TxOut {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
			w.chainParams)
		if err != nil {
			// Non-standard outputs are skipped.
			continue
		}
		for _, addr := range addrs {
			ma, err := w.Manager.Address(addrmgrNs, addr)
			if err == nil {
				// TODO: Credits should be added with the
				// account they belong to, so wtxmgr is able to
				// track per-account balances.
				err = w.TxStore.AddCredit(txmgrNs, rec, block, uint32(i),
					ma.Internal())
				if err != nil {
					return err
				}
				err = w.Manager.MarkUsed(addrmgrNs, addr)
				if err != nil {
					return err
				}
				log.Debugf("Marked address %v used", addr)
				continue
			}

			// Missing addresses are skipped.  Other errors should
			// be propagated.
			if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
				return err
			}
		}
	}

	// Send notification of mined or unmined transaction to any interested
	// clients.
	//
	// TODO: Avoid the extra db hits.
	if block == nil {
		details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, nil)
		if err != nil {
			log.Errorf("Cannot query transaction details for notification: %v", err)
		} else {
			w.NtfnServer.notifyUnminedTransaction(dbtx, details)
		}
	} else {
		details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, &block.Block)
		if err != nil {
			log.Errorf("Cannot query transaction details for notification: %v", err)
		} else {
			w.NtfnServer.notifyMinedTransaction(dbtx, details, block)
		}
	}

	return nil
}
<commit_msg>wallet: properly set timestamp during block connect/disconnect<commit_after>// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"bytes"

	"github.com/roasbeef/btcd/txscript"
	"github.com/roasbeef/btcwallet/chain"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"github.com/roasbeef/btcwallet/walletdb"
	"github.com/roasbeef/btcwallet/wtxmgr"
)

func (w *Wallet) handleChainNotifications() {
	chainClient, err := w.requireChainClient()
	if err != nil {
		log.Errorf("handleChainNotifications called without RPC client")
		w.wg.Done()
		return
	}

	sync := func(w *Wallet) {
		// At the moment there is no recourse if the rescan fails for
		// some reason, however, the wallet will not be marked synced
		// and many methods will error early since the wallet is known
		// to be out of date.
		err := w.syncWithChain()
		if err != nil && !w.ShuttingDown() {
			log.Warnf("Unable to synchronize wallet to chain: %v", err)
		}
	}

	catchUpHashes := func(w *Wallet, client chain.Interface,
		height int32) error {
		// TODO(aakselrod): There's a race conditon here, which
		// happens when a reorg occurs between the
		// rescanProgress notification and the last GetBlockHash
		// call. The solution when using btcd is to make btcd
		// send blockconnected notifications with each block
		// the way Neutrino does, and get rid of the loop. The
		// other alternative is to check the final hash and,
		// if it doesn't match the original hash returned by
		// the notification, to roll back and restart the
		// rescan.
		log.Infof("Catching up block hashes to height %d, this"+
			" might take a while", height)
		err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
			ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
			startBlock := w.Manager.SyncedTo()
			for i := startBlock.Height + 1; i <= height; i++ {
				hash, err := client.GetBlockHash(int64(i))
				if err != nil {
					return err
				}
				bs := waddrmgr.BlockStamp{
					Height: i,
					Hash:   *hash,
				}
				err = w.Manager.SetSyncedTo(ns, &bs)
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			log.Errorf("Failed to update address manager "+
				"sync state for height %d: %v", height, err)
		}
		log.Info("Done catching up block hashes")
		return err
	}

	for n := range chainClient.Notifications() {
		var notificationName string
		var err error
		switch n := n.(type) {
		case chain.ClientConnected:
			go sync(w)
		case chain.BlockConnected:
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.connectBlock(tx, wtxmgr.BlockMeta(n))
			})
			notificationName = "blockconnected"
		case chain.BlockDisconnected:
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.disconnectBlock(tx, wtxmgr.BlockMeta(n))
			})
			notificationName = "blockdisconnected"
		case chain.RelevantTx:
			err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
				return w.addRelevantTx(tx, n.TxRecord, n.Block)
			})
			notificationName = "recvtx/redeemingtx"
		case chain.FilteredBlockConnected:
			// Atomically update for the whole block.
			if len(n.RelevantTxs) > 0 {
				err = walletdb.Update(w.db, func(
					tx walletdb.ReadWriteTx) error {
					var err error
					for _, rec := range n.RelevantTxs {
						err = w.addRelevantTx(tx, rec,
							n.Block)
						if err != nil {
							return err
						}
					}
					return nil
				})
			}
			notificationName = "filteredblockconnected"

		// The following require some database maintenance, but also
		// need to be reported to the wallet's rescan goroutine.
		case *chain.RescanProgress:
			err = catchUpHashes(w, chainClient, n.Height)
			notificationName = "rescanprogress"
			w.rescanNotifications <- n
		case *chain.RescanFinished:
			err = catchUpHashes(w, chainClient, n.Height)
			notificationName = "rescanprogress"
			w.SetChainSynced(true)
			w.rescanNotifications <- n
		}
		if err != nil {
			log.Errorf("Failed to process consensus server notification "+
				"(name: `%s`, detail: `%v`)", notificationName, err)
		}
	}
	w.wg.Done()
}

// connectBlock handles a chain server notification by marking a wallet
// that's currently in-sync with the chain server as being synced up to
// the passed block.
func (w *Wallet) connectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)

	bs := waddrmgr.BlockStamp{
		Height:    b.Height,
		Hash:      b.Hash,
		Timestamp: b.Time,
	}
	err := w.Manager.SetSyncedTo(addrmgrNs, &bs)
	if err != nil {
		return err
	}

	// Notify interested clients of the connected block.
	//
	// TODO: move all notifications outside of the database transaction.
	w.NtfnServer.notifyAttachedBlock(dbtx, &b)
	return nil
}

// disconnectBlock handles a chain server reorganize by rolling back all
// block history from the reorged block for a wallet in-sync with the chain
// server.
func (w *Wallet) disconnectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
	txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)

	if !w.ChainSynced() {
		return nil
	}

	// Disconnect the removed block and all blocks after it if we know about
	// the disconnected block. Otherwise, the block is in the future.
	if b.Height <= w.Manager.SyncedTo().Height {
		hash, err := w.Manager.BlockHash(addrmgrNs, b.Height)
		if err != nil {
			return err
		}
		if bytes.Equal(hash[:], b.Hash[:]) {
			bs := waddrmgr.BlockStamp{
				Height: b.Height - 1,
			}
			hash, err = w.Manager.BlockHash(addrmgrNs, bs.Height)
			if err != nil {
				return err
			}
			b.Hash = *hash

			client := w.ChainClient()
			header, err := client.GetBlockHeader(hash)
			if err != nil {
				return err
			}

			bs.Timestamp = header.Timestamp

			err = w.Manager.SetSyncedTo(addrmgrNs, &bs)
			if err != nil {
				return err
			}
			err = w.TxStore.Rollback(txmgrNs, b.Height)
		}
	}

	// Notify interested clients of the disconnected block.
	w.NtfnServer.notifyDetachedBlock(&b.Hash)

	return nil
}

func (w *Wallet) addRelevantTx(dbtx walletdb.ReadWriteTx, rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error {
	addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
	txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)

	// At the moment all notified transactions are assumed to actually be
	// relevant.  This assumption will not hold true when SPV support is
	// added, but until then, simply insert the transaction because there
	// should either be one or more relevant inputs or outputs.
	err := w.TxStore.InsertTx(txmgrNs, rec, block)
	if err != nil {
		return err
	}

	// Check every output to determine whether it is controlled by a wallet
	// key.  If so, mark the output as a credit.
	for i, output := range rec.MsgTx.TxOut {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
			w.chainParams)
		if err != nil {
			// Non-standard outputs are skipped.
			continue
		}
		for _, addr := range addrs {
			ma, err := w.Manager.Address(addrmgrNs, addr)
			if err == nil {
				// TODO: Credits should be added with the
				// account they belong to, so wtxmgr is able to
				// track per-account balances.
				err = w.TxStore.AddCredit(txmgrNs, rec, block, uint32(i),
					ma.Internal())
				if err != nil {
					return err
				}
				err = w.Manager.MarkUsed(addrmgrNs, addr)
				if err != nil {
					return err
				}
				log.Debugf("Marked address %v used", addr)
				continue
			}

			// Missing addresses are skipped.  Other errors should
			// be propagated.
			if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
				return err
			}
		}
	}

	// Send notification of mined or unmined transaction to any interested
	// clients.
	//
	// TODO: Avoid the extra db hits.
	if block == nil {
		details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, nil)
		if err != nil {
			log.Errorf("Cannot query transaction details for notification: %v", err)
		} else {
			w.NtfnServer.notifyUnminedTransaction(dbtx, details)
		}
	} else {
		details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, &block.Block)
		if err != nil {
			log.Errorf("Cannot query transaction details for notification: %v", err)
		} else {
			w.NtfnServer.notifyMinedTransaction(dbtx, details, block)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
	"github.com/FactomProject/factomd/database/mapdb"
	"os"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	DBO databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.DBO.DB = db
	return answer
}

func NewTXMapDB() *TXDatabaseOverlay {
	return NewTXOverlay(new(mapdb.MapDB))
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	fileInfo, err := os.Stat(boltPath)
	if err == nil { //if it exists
		if fileInfo.IsDir() { //if it is a folder though
			return nil, fmt.Errorf("The path %s is a directory.  Please specify a file name.", boltPath)
		}
	}
	if err != nil && !os.IsNotExist(err) { //some other error, besides the file not existing
		fmt.Printf("database error %s\n", err)
		return nil, err
	}

	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Could not use wallet cache database file \"%s\"\n%v\n", boltPath, r)
			os.Exit(1)
		}
	}()
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.DBO.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	_, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.DBO.FetchFBlockHead()
	if err != nil {
		return nil, err
	}
	if fblock == nil {
		return nil, fmt.Errorf("FBlock Chain has not finished syncing")
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	if factom.AddressStringType(adr) == factom.FactoidPub {
		for _, tx := range txs {
			for _, in := range tx.GetInputs() {
				if primitives.ConvertFctAddressToUserStr(in.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
			for _, out := range tx.GetOutputs() {
				if primitives.ConvertFctAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else if factom.AddressStringType(adr) == factom.ECPub {
		for _, tx := range txs {
			for _, out := range tx.GetECOutputs() {
				if primitives.ConvertECAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else {
		return nil, fmt.Errorf("not a valid address")
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	h, err := primitives.NewShaHashFromStr(keymr)
	if err != nil {
		return nil, err
	}

	fBlock, err := db.DBO.FetchFBlock(h)
	if err != nil {
		return nil, err
	}
	return fBlock, nil
}

func (db *TXDatabaseOverlay) FetchNextFBlockHeight() (uint32, error) {
	block, err := db.DBO.FetchFBlockHead()
	if err != nil {
		return 0, err
	}
	if block == nil {
		return 0, nil
	}
	return block.GetDBHeight() + 1, nil
}

func (db *TXDatabaseOverlay) InsertFBlockHead(fblock interfaces.IFBlock) error {
	return db.DBO.SaveFactoidBlockHead(fblock)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	newestFBlock, err := fblockHead()
	if err != nil {
		return "", err
	}

	start, err := db.FetchNextFBlockHeight()
	if err != nil {
		return "", err
	}

	//Making sure we didn't switch networks
	genesis, err := db.DBO.FetchFBlockByHeight(0)
	if err != nil {
		return "", err
	}
	if genesis != nil {
		genesis2, err := getfblockbyheight(0)
		if err != nil {
			return "", err
		}
		if !genesis2.GetKeyMR().IsSameAs(genesis.GetKeyMR()) {
			start = 0
		}
	}

	newestHeight := newestFBlock.GetDatabaseHeight()

	if start >= newestHeight {
		return newestFBlock.GetKeyMR().String(), nil
	}

	for i := start; i <= newestHeight; i++ {
		if i%1000 == 0 {
			if newestHeight-start > 1000 {
				fmt.Printf("Fetching block %v / %v\n", i, newestHeight)
			}
		}
		fblock, err := getfblockbyheight(i)
		if err != nil {
			return "", err
		}
		db.InsertFBlockHead(fblock)
	}
	fmt.Printf("Fetching block %v / %v\n", newestHeight, newestHeight)

	return newestFBlock.GetKeyMR().String(), nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}

func getfblockbyheight(height uint32) (interfaces.IFBlock, error) {
	p, err := factom.GetFBlockByHeight(int64(height))
	if err != nil {
		return nil, err
	}
	h, err := hex.DecodeString(p.RawData)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(h)
}
<commit_msg>Made block feting into a multibatch<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
	"github.com/FactomProject/factomd/database/mapdb"
	"os"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	DBO databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.DBO.DB = db
	return answer
}

func NewTXMapDB() *TXDatabaseOverlay {
	return NewTXOverlay(new(mapdb.MapDB))
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	fileInfo, err := os.Stat(boltPath)
	if err == nil { //if it exists
		if fileInfo.IsDir() { //if it is a folder though
			return nil, fmt.Errorf("The path %s is a directory.  Please specify a file name.", boltPath)
		}
	}
	if err != nil && !os.IsNotExist(err) { //some other error, besides the file not existing
		fmt.Printf("database error %s\n", err)
		return nil, err
	}

	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Could not use wallet cache database file \"%s\"\n%v\n", boltPath, r)
			os.Exit(1)
		}
	}()
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.DBO.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	_, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.DBO.FetchFBlockHead()
	if err != nil {
		return nil, err
	}
	if fblock == nil {
		return nil, fmt.Errorf("FBlock Chain has not finished syncing")
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	if factom.AddressStringType(adr) == factom.FactoidPub {
		for _, tx := range txs {
			for _, in := range tx.GetInputs() {
				if primitives.ConvertFctAddressToUserStr(in.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
			for _, out := range tx.GetOutputs() {
				if primitives.ConvertFctAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else if factom.AddressStringType(adr) == factom.ECPub {
		for _, tx := range txs {
			for _, out := range tx.GetECOutputs() {
				if primitives.ConvertECAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else {
		return nil, fmt.Errorf("not a valid address")
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	h, err := primitives.NewShaHashFromStr(keymr)
	if err != nil {
		return nil, err
	}

	fBlock, err := db.DBO.FetchFBlock(h)
	if err != nil {
		return nil, err
	}
	return fBlock, nil
}

func (db *TXDatabaseOverlay) FetchNextFBlockHeight() (uint32, error) {
	block, err := db.DBO.FetchFBlockHead()
	if err != nil {
		return 0, err
	}
	if block == nil {
		return 0, nil
	}
	return block.GetDBHeight() + 1, nil
}

func (db *TXDatabaseOverlay) InsertFBlockHead(fblock interfaces.IFBlock) error {
	return db.DBO.SaveFactoidBlockHead(fblock)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	newestFBlock, err := fblockHead()
	if err != nil {
		return "", err
	}

	start, err := db.FetchNextFBlockHeight()
	if err != nil {
		return "", err
	}

	//Making sure we didn't switch networks
	genesis, err := db.DBO.FetchFBlockByHeight(0)
	if err != nil {
		return "", err
	}
	if genesis != nil {
		genesis2, err := getfblockbyheight(0)
		if err != nil {
			return "", err
		}
		if !genesis2.GetKeyMR().IsSameAs(genesis.GetKeyMR()) {
			start = 0
		}
	}

	newestHeight := newestFBlock.GetDatabaseHeight()

	if start >= newestHeight {
		return newestFBlock.GetKeyMR().String(), nil
	}

	db.DBO.StartMultiBatch()
	for i := start; i <= newestHeight; i++ {
		if i%1000 == 0 {
			if newestHeight-start > 1000 {
				fmt.Printf("Fetching block %v / %v\n", i, newestHeight)
			}
		}
		fblock, err := getfblockbyheight(i)
		if err != nil {
			db.DBO.ExecuteMultiBatch()
			return "", err
		}
		db.DBO.ProcessFBlockMultiBatch(fblock)
	}
	fmt.Printf("Fetching block %v / %v\n", newestHeight, newestHeight)
	err = db.DBO.ExecuteMultiBatch()
	if err != nil {
		return "", err
	}

	return newestFBlock.GetKeyMR().String(), nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}

func getfblockbyheight(height uint32) (interfaces.IFBlock, error) {
	p, err := factom.GetFBlockByHeight(int64(height))
	if err != nil {
		return nil, err
	}
	h, err := hex.DecodeString(p.RawData)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(h)
}
<|endoftext|>


<commit_before>package pixel

import (
	"fmt"
	"image/color"
)

// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
// Color and Texture.
type TrianglesData []struct {
	Position Vec
	Color    NRGBA
	Texture  Vec
}

// Len returns the number of vertices in TrianglesData.
func (td *TrianglesData) Len() int {
	return len(*td)
}

// Draw is unimplemented for TrianglesData and panics.
func (td *TrianglesData) Draw() {
	panic(fmt.Errorf("%T.Draw: invalid operation", td))
}

func (td *TrianglesData) resize(len int) {
	if len > td.Len() {
		needAppend := len - td.Len()
		for i := 0; i < needAppend; i++ {
			*td = append(*td, struct {
				Position Vec
				Color    NRGBA
				Texture  Vec
			}{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)})
		}
	}
	if len < td.Len() {
		*td = (*td)[:len]
	}
}

func (td *TrianglesData) updateData(offset int, t Triangles) {
	// fast path optimization
	if t, ok := t.(*TrianglesData); ok {
		copy((*td)[offset:], *t)
		return
	}

	// slow path manual copy
	if t, ok := t.(TrianglesPosition); ok {
		for i := offset; i < len(*td); i++ {
			(*td)[i].Position = t.Position(i)
		}
	}
	if t, ok := t.(TrianglesColor); ok {
		for i := offset; i < len(*td); i++ {
			(*td)[i].Color = t.Color(i)
		}
	}
	if t, ok := t.(TrianglesTexture); ok {
		for i := offset; i < len(*td); i++ {
			(*td)[i].Texture = t.Texture(i)
		}
	}
}

// Update copies vertex properties from the supplied Triangles into this TrianglesData.
//
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) {
	td.resize(t.Len())
	td.updateData(0, t)
}

// Append adds supplied Triangles to the end of the TrianglesData.
func (td *TrianglesData) Append(t Triangles) {
	td.resize(td.Len() + t.Len())
	td.updateData(td.Len()-t.Len(), t)
}

// Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles {
	copyTd := make(TrianglesData, td.Len())
	copyTd.Update(td)
	return &copyTd
}

// Position returns the position property of i-th vertex.
func (td *TrianglesData) Position(i int) Vec {
	return (*td)[i].Position
}

// Color returns the color property of i-th vertex.
func (td *TrianglesData) Color(i int) NRGBA {
	return (*td)[i].Color
}

// Texture returns the texture property of i-th vertex.
func (td *TrianglesData) Texture(i int) Vec {
	return (*td)[i].Texture
}

// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
//
// It does so by creating a separate Triangles instance for each Target. The instances are
// correctly updated alongside the wrapped Triangles.
type TrianglesDrawer struct {
	Triangles

	tris  map[Target]Triangles
	dirty bool
}

func (td *TrianglesDrawer) flush() {
	if !td.dirty {
		return
	}
	td.dirty = false

	for _, t := range td.tris {
		t.Update(td.Triangles)
	}
}

// Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) {
	if td.tris == nil {
		td.tris = make(map[Target]Triangles)
	}

	td.flush()

	tri := td.tris[target]
	if tri == nil {
		tri = target.MakeTriangles(td.Triangles)
		td.tris[target] = tri
	}
	tri.Draw()
}

// Update updates the wrapped Triangles with the supplied Triangles.
//
// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Update(t Triangles) {
	td.dirty = true
	td.Triangles.Update(t)
}

// Append appends the supplied Triangles to the wrapped Triangles.
//
// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Append(t Triangles) {
	td.dirty = true
	td.Triangles.Append(t)
}

// Sprite is a picture that can be drawn onto a Target. To change the position/rotation/scale of
// the Sprite, use Target's SetTransform method.
type Sprite struct {
	td   TrianglesDrawer
	data *TrianglesData
	pic  *Picture
}

// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
// the dimensions of the Picture.
func NewSprite(pic *Picture) *Sprite {
	data := TrianglesData{
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)},
	}
	s := &Sprite{
		td:   TrianglesDrawer{Triangles: &data},
		data: &data,
	}
	s.SetPicture(pic)
	return s
}

// SetPicture changes the Picture of the Sprite and resizes it accordingly.
func (s *Sprite) SetPicture(pic *Picture) {
	w, h := pic.Bounds().Size.XY()
	(*s.data)[0].Position = V(0, 0)
	(*s.data)[2].Position = V(w, h)
	(*s.data)[1].Position = V(w, 0)
	(*s.data)[3].Position = V(0, 0)
	(*s.data)[4].Position = V(w, h)
	(*s.data)[5].Position = V(0, h)
	s.pic = pic
}

// Picture returns the current Picture of the Sprite.
func (s *Sprite) Picture() *Picture {
	return s.pic
}

// Draw draws the Sprite onto the provided Target.
func (s *Sprite) Draw(t Target) {
	t.SetPicture(s.pic)
	s.td.Draw(t)
}

// Polygon is a convex polygon shape filled with a single color.
type Polygon struct {
	td   TrianglesDrawer
	data *TrianglesData
	col  NRGBA
}

// NewPolygon creates a Polygon with specified color and points. Points can be in clock-wise or
// counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
func NewPolygon(c color.Color, points ...Vec) *Polygon {
	data := make(TrianglesData, len(points))
	p := &Polygon{
		td:   TrianglesDrawer{Triangles: &data},
		data: &data,
	}
	p.SetColor(c)
	p.SetPoints(points...)
	return p
}

// SetColor changes the color of the Polygon.
//
// If the Polygon is very large, this method might end up being too expensive. Consider using
// a color mask on a Target, in such a case.
func (p *Polygon) SetColor(c color.Color) {
	p.col = NRGBAModel.Convert(c).(NRGBA)
	for i := range *p.data {
		(*p.data)[i].Color = p.col
	}
	// dirty stuff, need to update manually
	p.td.dirty = true
}

// Color returns the current color of the Polygon.
func (p *Polygon) Color() NRGBA {
	return p.col
}

// SetPoints sets the points of the Polygon. The number of points might differ from the original
// count.
//
// This method is more effective, than creating a new Polygon with the given points.
func (p *Polygon) SetPoints(points ...Vec) {
	p.data.resize(len(points))
	for i, pt := range points {
		(*p.data)[i].Position = pt
		(*p.data)[i].Color = p.col
		(*p.data)[i].Texture = V(-1, -1)
	}
	// dirty stuff
	p.td.dirty = true
}

// Points returns a slice of points of the Polygon in the order they where supplied.
func (p *Polygon) Points() []Vec {
	points := make([]Vec, p.data.Len())
	for i := range *p.data {
		points[i] = (*p.data)[i].Position
	}
	return points
}

// Draw draws the Polygon onto the Target.
func (p *Polygon) Draw(t Target) {
	t.SetPicture(nil)
	p.td.Draw(t)
}
<commit_msg>add note to doc<commit_after>package pixel

import (
	"fmt"
	"image/color"
)

// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
// Color and Texture.
type TrianglesData []struct {
	Position Vec
	Color    NRGBA
	Texture  Vec
}

// Len returns the number of vertices in TrianglesData.
func (td *TrianglesData) Len() int {
	return len(*td)
}

// Draw is unimplemented for TrianglesData and panics.
func (td *TrianglesData) Draw() {
	panic(fmt.Errorf("%T.Draw: invalid operation", td))
}

func (td *TrianglesData) resize(len int) {
	if len > td.Len() {
		needAppend := len - td.Len()
		for i := 0; i < needAppend; i++ {
			*td = append(*td, struct {
				Position Vec
				Color    NRGBA
				Texture  Vec
			}{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)})
		}
	}
	if len < td.Len() {
		*td = (*td)[:len]
	}
}

func (td *TrianglesData) updateData(offset int, t Triangles) {
	// fast path optimization
	if t, ok := t.(*TrianglesData); ok {
		copy((*td)[offset:], *t)
		return
	}

	// slow path manual copy
	if t, ok := t.(TrianglesPosition); ok {
		for i := offset; i < len(*td); i++ {
			(*td)[i].Position = t.Position(i)
		}
	}
	if t, ok := t.(TrianglesColor); ok {
		for i := offset; i < len(*td); i++ {
			(*td)[i].Color = t.Color(i)
		}
	}
	if t, ok := t.(TrianglesTexture); ok {
		for i := offset; i < len(*td); i++ {
			(*td)[i].Texture = t.Texture(i)
		}
	}
}

// Update copies vertex properties from the supplied Triangles into this TrianglesData.
//
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) {
	td.resize(t.Len())
	td.updateData(0, t)
}

// Append adds supplied Triangles to the end of the TrianglesData.
func (td *TrianglesData) Append(t Triangles) {
	td.resize(td.Len() + t.Len())
	td.updateData(td.Len()-t.Len(), t)
}

// Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles {
	copyTd := make(TrianglesData, td.Len())
	copyTd.Update(td)
	return &copyTd
}

// Position returns the position property of i-th vertex.
func (td *TrianglesData) Position(i int) Vec {
	return (*td)[i].Position
}

// Color returns the color property of i-th vertex.
func (td *TrianglesData) Color(i int) NRGBA {
	return (*td)[i].Color
}

// Texture returns the texture property of i-th vertex.
func (td *TrianglesData) Texture(i int) Vec {
	return (*td)[i].Texture
}

// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
//
// It does so by creating a separate Triangles instance for each Target. The instances are
// correctly updated alongside the wrapped Triangles.
type TrianglesDrawer struct {
	Triangles

	tris  map[Target]Triangles
	dirty bool
}

func (td *TrianglesDrawer) flush() {
	if !td.dirty {
		return
	}
	td.dirty = false

	for _, t := range td.tris {
		t.Update(td.Triangles)
	}
}

// Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) {
	if td.tris == nil {
		td.tris = make(map[Target]Triangles)
	}

	td.flush()

	tri := td.tris[target]
	if tri == nil {
		tri = target.MakeTriangles(td.Triangles)
		td.tris[target] = tri
	}
	tri.Draw()
}

// Update updates the wrapped Triangles with the supplied Triangles.
//
// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Update(t Triangles) {
	td.dirty = true
	td.Triangles.Update(t)
}

// Append appends the supplied Triangles to the wrapped Triangles.
//
// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Append(t Triangles) {
	td.dirty = true
	td.Triangles.Append(t)
}

// Sprite is a picture that can be drawn onto a Target. To change the position/rotation/scale of
// the Sprite, use Target's SetTransform method.
type Sprite struct {
	td   TrianglesDrawer
	data *TrianglesData
	pic  *Picture
}

// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
// the dimensions of the Picture.
func NewSprite(pic *Picture) *Sprite {
	data := TrianglesData{
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)},
	}
	s := &Sprite{
		td:   TrianglesDrawer{Triangles: &data},
		data: &data,
	}
	s.SetPicture(pic)
	return s
}

// SetPicture changes the Picture of the Sprite and resizes it accordingly.
func (s *Sprite) SetPicture(pic *Picture) {
	w, h := pic.Bounds().Size.XY()
	(*s.data)[0].Position = V(0, 0)
	(*s.data)[2].Position = V(w, h)
	(*s.data)[1].Position = V(w, 0)
	(*s.data)[3].Position = V(0, 0)
	(*s.data)[4].Position = V(w, h)
	(*s.data)[5].Position = V(0, h)
	s.pic = pic
}

// Picture returns the current Picture of the Sprite.
func (s *Sprite) Picture() *Picture {
	return s.pic
}

// Draw draws the Sprite onto the provided Target.
func (s *Sprite) Draw(t Target) {
	t.SetPicture(s.pic)
	s.td.Draw(t)
}

// Polygon is a convex polygon shape filled with a single color.
type Polygon struct {
	td   TrianglesDrawer
	data *TrianglesData
	col  NRGBA
}

// NewPolygon creates a Polygon with specified color and points. Points can be in clock-wise or
// counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
func NewPolygon(c color.Color, points ...Vec) *Polygon {
	data := make(TrianglesData, len(points))
	p := &Polygon{
		td:   TrianglesDrawer{Triangles: &data},
		data: &data,
	}
	p.SetColor(c)
	p.SetPoints(points...)
	return p
}

// SetColor changes the color of the Polygon.
//
// If the Polygon is very large, this method might end up being too expensive. Consider using
// a color mask on a Target, in such a case.
func (p *Polygon) SetColor(c color.Color) {
	p.col = NRGBAModel.Convert(c).(NRGBA)
	for i := range *p.data {
		(*p.data)[i].Color = p.col
	}
	// dirty stuff, need to update manually
	p.td.dirty = true
}

// Color returns the current color of the Polygon.
func (p *Polygon) Color() NRGBA {
	return p.col
}

// SetPoints sets the points of the Polygon. The number of points might differ from the original
// count.
//
// This method is more effective, than creating a new Polygon with the given points.
//
// However, it is less expensive than using a transform on a Target.
func (p *Polygon) SetPoints(points ...Vec) {
	p.data.resize(len(points))
	for i, pt := range points {
		(*p.data)[i].Position = pt
		(*p.data)[i].Color = p.col
		(*p.data)[i].Texture = V(-1, -1)
	}
	// dirty stuff
	p.td.dirty = true
}

// Points returns a slice of points of the Polygon in the order they where supplied.
func (p *Polygon) Points() []Vec {
	points := make([]Vec, p.data.Len())
	for i := range *p.data {
		points[i] = (*p.data)[i].Position
	}
	return points
}

// Draw draws the Polygon onto the Target.
func (p *Polygon) Draw(t Target) {
	t.SetPicture(nil)
	p.td.Draw(t)
}
<|endoftext|>


<commit_before><commit_msg>simplified method and error handling<commit_after><|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// 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
//
//     https://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 main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/yaq-cc/ezcx"
)

var (
	PORT = os.Getenv("PORT")
)

func main() {
	parent := context.Background()
	lg := log.Default()
	server := ezcx.NewServer(parent, ":"+PORT, lg)
	server.HandleCx("/tell-a-joke", CxJokeHandler)
	server.ListenAndServe(parent)
}

// Sends a joke upon invocation..
func CxJokeHandler(res *ezcx.WebhookResponse, req *ezcx.WebhookRequest) error {
	lg := req.Logger()   // Access the logger via req.Logger (it's passed as a context value)
	ctx := req.Context() // Access the context, which is a proxy for (*http.Request).Context

	joke, err := defaultJokesClient.get(ctx)
	if err != nil {
		lg.Println(err)
		return err
	}
	lg.Println(joke.Joke) // added for testing purposes!
	res.AddTextResponse(joke.Joke)
	return nil
}

func CxHelloWorldHandler(res *ezcx.WebhookResponse, req *ezcx.WebhookRequest) error {
	params := req.GetSessionParameters()
	color, ok := params["color"]
	if !ok {
		res.AddTextResponse("I couldn't find the provided color.")
		return fmt.Errorf("missing session parameter: color")
	}
	// add a parameter
	params["color-processed"] = true
	// delete a parameter
	delete(params, "color")
	err := res.SetSessionParameters(params)
	if err != nil {
		return err
	}
	res.AddTextResponse(fmt.Sprintf("The provided color was %s", color))
	return nil
}<commit_msg>updates to INSTRUCTIONS.md<commit_after>// Copyright 2022 Google LLC
//
// 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
//
//     https://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 main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/yaq-cc/ezcx"
)

var (
	PORT = os.Getenv("PORT")
)

func main() {
	parent := context.Background()
	lg := log.Default()
	server := ezcx.NewServer(parent, ":"+PORT, lg)
	server.HandleCx("/tell-a-joke", CxJokeHandler)
	server.ListenAndServe(parent)
}

// Sends a joke upon invocation..
func CxJokeHandler(res *ezcx.WebhookResponse, req *ezcx.WebhookRequest) error {
	lg := req.Logger()   // Access the logger via req.Logger (it's passed as a context value)
	ctx := req.Context() // Access the context, which is a proxy for (*http.Request).Context

	joke, err := defaultJokesClient.get(ctx)
	if err != nil {
		lg.Println(err)
		return err
	}
	lg.Println(joke.Joke) // added for testing purposes!
	res.AddTextResponse(joke.Joke)
	return nil
}

func CxHelloWorldHandler(res *ezcx.WebhookResponse, req *ezcx.WebhookRequest) error {
	params := req.GetSessionParameters()
	color, ok := params["color"]
	if !ok {
		res.AddTextResponse("I couldn't find the provided color.")
		return fmt.Errorf("missing session parameter: color")
	}
	// add a parameter
	params["color-processed"] = true
	// delete a parameter ~
	delete(params, "color")
	err := res.SetSessionParameters(params)
	if err != nil {
		return err
	}
	res.AddTextResponse(fmt.Sprintf("The provided color was %s", color))
	return nil
}<|endoftext|>


<commit_before><commit_msg>Added a null logger<commit_after><|endoftext|>


<commit_before>package spvwallet

import (
	"errors"
	"net/http"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil/bloom"
	hd "github.com/btcsuite/btcutil/hdkeychain"
	btc "github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/coinset"
	"github.com/btcsuite/btcwallet/wallet/txrules"
	"github.com/btcsuite/btcwallet/wallet/txauthor"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcutil/txsort"
	"encoding/json"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"bytes"
)

func (p *Peer) PongBack(nonce uint64) {
	mpong := wire.NewMsgPong(nonce)

	p.outMsgQueue <- mpong
	return
}
func (p *Peer) UpdateFilterAndSend() {
	filt, err := p.TS.GimmeFilter()
	if err != nil {
		log.Errorf("Filter creation error: %s\n", err.Error())
		return
	}
	// send filter
	p.SendFilter(filt)
	log.Debugf("Sent filter to %s\n", p.con.RemoteAddr().String())
}

func (p *Peer) SendFilter(f *bloom.Filter) {
	p.outMsgQueue <- f.MsgFilterLoad()
	return
}

func (p *Peer) NewOutgoingTx(tx *wire.MsgTx) error {
	txid := tx.TxHash()
	// assign height of zero for txs we create

	p.OKMutex.Lock()
	p.OKTxids[txid] = 0
	p.OKMutex.Unlock()

	_, err := p.TS.Ingest(tx, 0) // our own tx; don't keep track of false positives
	if err != nil {
		return err
	}
	// make an inv message instead of a tx message to be polite
	iv1 := wire.NewInvVect(wire.InvTypeTx, &txid)
	invMsg := wire.NewMsgInv()
	err = invMsg.AddInvVect(iv1)
	if err != nil {
		return err
	}
	log.Debugf("Broadcasting tx %s to %s", tx.TxHash().String(), p.con.RemoteAddr().String())
	p.outMsgQueue <- invMsg
	return nil
}

// Rebroadcast sends an inv message of all the unconfirmed txs the db is
// aware of.  This is called after every sync.  Only txids so hopefully not
// too annoying for nodes.
func (p *Peer) Rebroadcast() {
	// get all unconfirmed txs
	invMsg, err := p.TS.GetPendingInv()
	if err != nil {
		log.Errorf("Rebroadcast error: %s", err.Error())
	}
	if len(invMsg.InvList) == 0 { // nothing to broadcast, so don't
		return
	}
	p.outMsgQueue <- invMsg
	return
}

type Coin struct {
	TxHash       *chainhash.Hash
	TxIndex      uint32
	TxValue      btc.Amount
	TxNumConfs   int64
	ScriptPubKey []byte
}

func (c *Coin) Hash() *chainhash.Hash   { return c.TxHash }
func (c *Coin) Index() uint32         { return c.TxIndex }
func (c *Coin) Value() btc.Amount     { return c.TxValue }
func (c *Coin) PkScript() []byte      { return c.ScriptPubKey }
func (c *Coin) NumConfs() int64       { return c.TxNumConfs }
func (c *Coin) ValueAge() int64       { return int64(c.TxValue) * c.TxNumConfs }

func NewCoin(txid []byte, index uint32, value btc.Amount, numConfs int64, scriptPubKey []byte) coinset.Coin {
	shaTxid, _ := chainhash.NewHash(txid)
	c := &Coin{
		TxHash:       shaTxid,
		TxIndex:      index,
		TxValue:      value,
		TxNumConfs:   numConfs,
		ScriptPubKey: scriptPubKey,
	}
	return coinset.Coin(c)
}

func (w *SPVWallet) gatherCoins() map[coinset.Coin]*hd.ExtendedKey {
	height, _ := w.state.GetDBSyncHeight()
	utxos, _ := w.db.Utxos().GetAll()
	m := make(map[coinset.Coin]*hd.ExtendedKey)
	for _, u := range(utxos) {
		if u.Freeze {
			continue
		}
		var confirmations int32
		if u.AtHeight > 0 {
			confirmations = height - u.AtHeight
		}
		c := NewCoin(u.Op.Hash.CloneBytes(), u.Op.Index, btc.Amount(u.Value), int64(confirmations), u.ScriptPubkey)
		key, err := w.state.GetKeyForScript(u.ScriptPubkey)
		if err != nil {
			continue
		}
		m[c] = key
	}
	return m
}

func (w *SPVWallet) Spend(amount int64, addr btc.Address, feeLevel FeeLevel) error {
	tx, err := w.buildTx(amount, addr, feeLevel)
	if err != nil {
		return err
	}
	// broadcast
	for _, peer := range w.peerGroup {
		peer.NewOutgoingTx(tx)
	}
	log.Infof("Broadcasting tx %s to network", tx.TxHash().String())
	return nil
}

func (w *SPVWallet) ExportRawTx(amount int64, addr btc.Address, feeLevel FeeLevel) ([]byte, error) {
	tx, err := w.buildTx(amount, addr, feeLevel)
	if err != nil {
		return nil, err
	}
	for _, txin := range tx.TxIn {
		err := w.state.db.Utxos().Freeze(Utxo{Op:txin.PreviousOutPoint})
		if err != nil {
			return nil, err
		}
	}
	output := new(bytes.Buffer)
	err = tx.Serialize(output)
	if err != nil {
		return nil, err
	}
	return output.Bytes(), nil
}

func (w *SPVWallet) buildTx(amount int64, addr btc.Address, feeLevel FeeLevel) (*wire.MsgTx, error) {
	// Check for dust
	script, _ := txscript.PayToAddrScript(addr)
	if txrules.IsDustAmount(btc.Amount(amount), len(script), txrules.DefaultRelayFeePerKb) {
		return nil, errors.New("Amount is below dust threshold")
	}

	var additionalPrevScripts map[wire.OutPoint][]byte
	var additionalKeysByAddress map[string]*btc.WIF

	// Create input source
	coinMap := w.gatherCoins()
	coins := make([]coinset.Coin, 0, len(coinMap))
	for k := range coinMap {
		coins = append(coins, k)
	}
	inputSource := func(target btc.Amount) (total btc.Amount, inputs []*wire.TxIn, scripts [][]byte, err error) {
		coinSelector := coinset.MaxValueAgeCoinSelector{MaxInputs: 10000, MinChangeAmount: btc.Amount(10000)}
		coins, err := coinSelector.CoinSelect(target, coins)
		if err != nil {
			return total, inputs, scripts, errors.New("insuffient funds")
		}
		additionalPrevScripts = make(map[wire.OutPoint][]byte)
		additionalKeysByAddress = make(map[string]*btc.WIF)
		for _, c := range(coins.Coins()) {
			total += c.Value()
			outpoint := wire.NewOutPoint(c.Hash(), c.Index())
			in := wire.NewTxIn(outpoint, []byte{})
			in.Sequence = 0 // Opt-in RBF so we can bump fees
			inputs = append(inputs, in)
			additionalPrevScripts[*outpoint] = c.PkScript()
			key := coinMap[c]
			addr, err := key.Address(w.params)
			if err != nil {
				continue
			}
			privKey, err := key.ECPrivKey()
			if err != nil {
				continue
			}
			wif, _ := btc.NewWIF(privKey, w.params, true)
			additionalKeysByAddress[addr.EncodeAddress()] = wif
		}
		return total, inputs, scripts, nil
	}

	// Get the fee per kilobyte
	feePerKB := int64(w.getFeePerByte(feeLevel)) * 1000

	// outputs
	out := wire.NewTxOut(amount, script)

	// Create change source
	changeSource := func() ([]byte, error) {
		addr := w.CurrentAddress(INTERNAL)
		script, err := txscript.PayToAddrScript(addr)
		if err != nil {
			return []byte{}, err
		}
		return script, nil
	}

	authoredTx, err := txauthor.NewUnsignedTransaction([]*wire.TxOut{out,}, btc.Amount(feePerKB), inputSource, changeSource)
	if err != nil {
		return nil, err
	}

	// BIP 69 sorting
	txsort.InPlaceSort(authoredTx.Tx)

	// Sign tx
	getKey := txscript.KeyClosure(func(addr btc.Address) (*btcec.PrivateKey, bool, error) {
		addrStr := addr.EncodeAddress()
		wif := additionalKeysByAddress[addrStr]
		return wif.PrivKey, wif.CompressPubKey, nil
	})
	getScript := txscript.ScriptClosure(func(
	addr btc.Address) ([]byte, error) {
		return []byte{}, nil
	})
	for i, txIn := range authoredTx.Tx.TxIn {
		prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint]
		script, err := txscript.SignTxOutput(w.params,
			authoredTx.Tx, i, prevOutScript, txscript.SigHashAll, getKey,
			getScript, txIn.SignatureScript)
		if err != nil {
			return nil, errors.New("Failed to sign transaction")
		}
		txIn.SignatureScript = script
	}
	return authoredTx.Tx, nil
}

type FeeLevel int

const (
	PRIOIRTY = 0
	NORMAL   = 1
	ECONOMIC = 2
)

func (w *SPVWallet) getFeePerByte(feeLevel FeeLevel) uint64 {
	defaultFee := func() uint64 {
		switch feeLevel {
		case PRIOIRTY:
			return w.priorityFee
		case NORMAL:
			return w.normalFee
		case ECONOMIC:
			return w.economicFee
		default:
			return w.normalFee
		}
	}
	if w.feeAPI == "" {
		return defaultFee()
	}

	resp, err := http.Get(w.feeAPI)
	if err != nil {
		return defaultFee()
	}

	defer resp.Body.Close()

	type Fees struct {
		FastestFee  uint64
		HalfHourFee uint64
		HourFee     uint64
	}
	fees := new(Fees)
	err = json.NewDecoder(resp.Body).Decode(&fees)
	if err != nil {
		return defaultFee()
	}
	switch feeLevel {
	case PRIOIRTY:
		if fees.FastestFee > w.maxFee {
			return w.maxFee
		} else {
			return fees.FastestFee
		}
	case NORMAL:
		if fees.HalfHourFee > w.maxFee {
			return w.maxFee
		} else {
			return fees.HalfHourFee
		}
	case ECONOMIC:
		if fees.HourFee > w.maxFee {
			return w.maxFee
		} else {
			return fees.HourFee
		}
	default:
		return w.normalFee
	}
}<commit_msg>Add raw tx broadcast method<commit_after>package spvwallet

import (
	"errors"
	"net/http"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil/bloom"
	hd "github.com/btcsuite/btcutil/hdkeychain"
	btc "github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/coinset"
	"github.com/btcsuite/btcwallet/wallet/txrules"
	"github.com/btcsuite/btcwallet/wallet/txauthor"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcutil/txsort"
	"encoding/json"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"bytes"
)

func (p *Peer) PongBack(nonce uint64) {
	mpong := wire.NewMsgPong(nonce)

	p.outMsgQueue <- mpong
	return
}
func (p *Peer) UpdateFilterAndSend() {
	filt, err := p.TS.GimmeFilter()
	if err != nil {
		log.Errorf("Filter creation error: %s\n", err.Error())
		return
	}
	// send filter
	p.SendFilter(filt)
	log.Debugf("Sent filter to %s\n", p.con.RemoteAddr().String())
}

func (p *Peer) SendFilter(f *bloom.Filter) {
	p.outMsgQueue <- f.MsgFilterLoad()
	return
}

func (p *Peer) NewOutgoingTx(tx *wire.MsgTx) error {
	txid := tx.TxHash()
	// assign height of zero for txs we create

	p.OKMutex.Lock()
	p.OKTxids[txid] = 0
	p.OKMutex.Unlock()

	_, err := p.TS.Ingest(tx, 0) // our own tx; don't keep track of false positives
	if err != nil {
		return err
	}
	// make an inv message instead of a tx message to be polite
	iv1 := wire.NewInvVect(wire.InvTypeTx, &txid)
	invMsg := wire.NewMsgInv()
	err = invMsg.AddInvVect(iv1)
	if err != nil {
		return err
	}
	log.Debugf("Broadcasting tx %s to %s", tx.TxHash().String(), p.con.RemoteAddr().String())
	p.outMsgQueue <- invMsg
	return nil
}

// Rebroadcast sends an inv message of all the unconfirmed txs the db is
// aware of.  This is called after every sync.  Only txids so hopefully not
// too annoying for nodes.
func (p *Peer) Rebroadcast() {
	// get all unconfirmed txs
	invMsg, err := p.TS.GetPendingInv()
	if err != nil {
		log.Errorf("Rebroadcast error: %s", err.Error())
	}
	if len(invMsg.InvList) == 0 { // nothing to broadcast, so don't
		return
	}
	p.outMsgQueue <- invMsg
	return
}

type Coin struct {
	TxHash       *chainhash.Hash
	TxIndex      uint32
	TxValue      btc.Amount
	TxNumConfs   int64
	ScriptPubKey []byte
}

func (c *Coin) Hash() *chainhash.Hash   { return c.TxHash }
func (c *Coin) Index() uint32         { return c.TxIndex }
func (c *Coin) Value() btc.Amount     { return c.TxValue }
func (c *Coin) PkScript() []byte      { return c.ScriptPubKey }
func (c *Coin) NumConfs() int64       { return c.TxNumConfs }
func (c *Coin) ValueAge() int64       { return int64(c.TxValue) * c.TxNumConfs }

func NewCoin(txid []byte, index uint32, value btc.Amount, numConfs int64, scriptPubKey []byte) coinset.Coin {
	shaTxid, _ := chainhash.NewHash(txid)
	c := &Coin{
		TxHash:       shaTxid,
		TxIndex:      index,
		TxValue:      value,
		TxNumConfs:   numConfs,
		ScriptPubKey: scriptPubKey,
	}
	return coinset.Coin(c)
}

func (w *SPVWallet) gatherCoins() map[coinset.Coin]*hd.ExtendedKey {
	height, _ := w.state.GetDBSyncHeight()
	utxos, _ := w.db.Utxos().GetAll()
	m := make(map[coinset.Coin]*hd.ExtendedKey)
	for _, u := range(utxos) {
		if u.Freeze {
			continue
		}
		var confirmations int32
		if u.AtHeight > 0 {
			confirmations = height - u.AtHeight
		}
		c := NewCoin(u.Op.Hash.CloneBytes(), u.Op.Index, btc.Amount(u.Value), int64(confirmations), u.ScriptPubkey)
		key, err := w.state.GetKeyForScript(u.ScriptPubkey)
		if err != nil {
			continue
		}
		m[c] = key
	}
	return m
}

func (w *SPVWallet) Spend(amount int64, addr btc.Address, feeLevel FeeLevel) error {
	tx, err := w.buildTx(amount, addr, feeLevel)
	if err != nil {
		return err
	}
	// broadcast
	for _, peer := range w.peerGroup {
		peer.NewOutgoingTx(tx)
	}
	log.Infof("Broadcasting tx %s to network", tx.TxHash().String())
	return nil
}

func (w *SPVWallet) ExportRawTx(amount int64, addr btc.Address, feeLevel FeeLevel) ([]byte, error) {
	tx, err := w.buildTx(amount, addr, feeLevel)
	if err != nil {
		return nil, err
	}
	for _, txin := range tx.TxIn {
		err := w.state.db.Utxos().Freeze(Utxo{Op:txin.PreviousOutPoint})
		if err != nil {
			return nil, err
		}
	}
	output := new(bytes.Buffer)
	err = tx.Serialize(output)
	if err != nil {
		return nil, err
	}
	return output.Bytes(), nil
}

func (w *SPVWallet) BroadcastRawTx(tx []byte) error {
	msgtx := wire.NewMsgTx()
	err := msgtx.Deserialize(bytes.NewReader(tx))
	if err != nil {
		return err
	}
	// broadcast
	for _, peer := range w.peerGroup {
		peer.NewOutgoingTx(msgtx)
	}
	log.Infof("Broadcasting tx %s to network", msgtx.TxHash().String())
	return nil
}

func (w *SPVWallet) buildTx(amount int64, addr btc.Address, feeLevel FeeLevel) (*wire.MsgTx, error) {
	// Check for dust
	script, _ := txscript.PayToAddrScript(addr)
	if txrules.IsDustAmount(btc.Amount(amount), len(script), txrules.DefaultRelayFeePerKb) {
		return nil, errors.New("Amount is below dust threshold")
	}

	var additionalPrevScripts map[wire.OutPoint][]byte
	var additionalKeysByAddress map[string]*btc.WIF

	// Create input source
	coinMap := w.gatherCoins()
	coins := make([]coinset.Coin, 0, len(coinMap))
	for k := range coinMap {
		coins = append(coins, k)
	}
	inputSource := func(target btc.Amount) (total btc.Amount, inputs []*wire.TxIn, scripts [][]byte, err error) {
		coinSelector := coinset.MaxValueAgeCoinSelector{MaxInputs: 10000, MinChangeAmount: btc.Amount(10000)}
		coins, err := coinSelector.CoinSelect(target, coins)
		if err != nil {
			return total, inputs, scripts, errors.New("insuffient funds")
		}
		additionalPrevScripts = make(map[wire.OutPoint][]byte)
		additionalKeysByAddress = make(map[string]*btc.WIF)
		for _, c := range(coins.Coins()) {
			total += c.Value()
			outpoint := wire.NewOutPoint(c.Hash(), c.Index())
			in := wire.NewTxIn(outpoint, []byte{})
			in.Sequence = 0 // Opt-in RBF so we can bump fees
			inputs = append(inputs, in)
			additionalPrevScripts[*outpoint] = c.PkScript()
			key := coinMap[c]
			addr, err := key.Address(w.params)
			if err != nil {
				continue
			}
			privKey, err := key.ECPrivKey()
			if err != nil {
				continue
			}
			wif, _ := btc.NewWIF(privKey, w.params, true)
			additionalKeysByAddress[addr.EncodeAddress()] = wif
		}
		return total, inputs, scripts, nil
	}

	// Get the fee per kilobyte
	feePerKB := int64(w.getFeePerByte(feeLevel)) * 1000

	// outputs
	out := wire.NewTxOut(amount, script)

	// Create change source
	changeSource := func() ([]byte, error) {
		addr := w.CurrentAddress(INTERNAL)
		script, err := txscript.PayToAddrScript(addr)
		if err != nil {
			return []byte{}, err
		}
		return script, nil
	}

	authoredTx, err := txauthor.NewUnsignedTransaction([]*wire.TxOut{out,}, btc.Amount(feePerKB), inputSource, changeSource)
	if err != nil {
		return nil, err
	}

	// BIP 69 sorting
	txsort.InPlaceSort(authoredTx.Tx)

	// Sign tx
	getKey := txscript.KeyClosure(func(addr btc.Address) (*btcec.PrivateKey, bool, error) {
		addrStr := addr.EncodeAddress()
		wif := additionalKeysByAddress[addrStr]
		return wif.PrivKey, wif.CompressPubKey, nil
	})
	getScript := txscript.ScriptClosure(func(
	addr btc.Address) ([]byte, error) {
		return []byte{}, nil
	})
	for i, txIn := range authoredTx.Tx.TxIn {
		prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint]
		script, err := txscript.SignTxOutput(w.params,
			authoredTx.Tx, i, prevOutScript, txscript.SigHashAll, getKey,
			getScript, txIn.SignatureScript)
		if err != nil {
			return nil, errors.New("Failed to sign transaction")
		}
		txIn.SignatureScript = script
	}
	return authoredTx.Tx, nil
}

type FeeLevel int

const (
	PRIOIRTY = 0
	NORMAL   = 1
	ECONOMIC = 2
)

func (w *SPVWallet) getFeePerByte(feeLevel FeeLevel) uint64 {
	defaultFee := func() uint64 {
		switch feeLevel {
		case PRIOIRTY:
			return w.priorityFee
		case NORMAL:
			return w.normalFee
		case ECONOMIC:
			return w.economicFee
		default:
			return w.normalFee
		}
	}
	if w.feeAPI == "" {
		return defaultFee()
	}

	resp, err := http.Get(w.feeAPI)
	if err != nil {
		return defaultFee()
	}

	defer resp.Body.Close()

	type Fees struct {
		FastestFee  uint64
		HalfHourFee uint64
		HourFee     uint64
	}
	fees := new(Fees)
	err = json.NewDecoder(resp.Body).Decode(&fees)
	if err != nil {
		return defaultFee()
	}
	switch feeLevel {
	case PRIOIRTY:
		if fees.FastestFee > w.maxFee {
			return w.maxFee
		} else {
			return fees.FastestFee
		}
	case NORMAL:
		if fees.HalfHourFee > w.maxFee {
			return w.maxFee
		} else {
			return fees.HalfHourFee
		}
	case ECONOMIC:
		if fees.HourFee > w.maxFee {
			return w.maxFee
		} else {
			return fees.HourFee
		}
	default:
		return w.normalFee
	}
}<|endoftext|>


<commit_before><commit_msg>sdk: move info required by enterChroot into a struct<commit_after><|endoftext|>


<commit_before>package captcha

import (
	"testing"

	"github.com/macaron-contrib/cache"
	"github.com/tango-contrib/renders"

	"github.com/lunny/tango"
)

type CaptchaAction struct {
	Captcha
	renders.Renderer
}

func (c *CaptchaAction) Get() {
	c.Renderer.Render("captcha.html", renders.T{
		"captcha": c.CreateHtml(),
	})
}

func (c *CaptchaAction) Post() string {
	if c.Verify() {
		return "true"
	}
	return "false"
}

func TestCaptcha(t *testing.T) {
	tg := tango.Classic()
	c, _ := cache.NewCacher("memory", cache.Options{
		Interval: 120,
	})
	tg.Use(New(Options{}, c), renders.New())
	tg.Any("/", new(CaptchaAction))
	tg.Run()
}
<commit_msg>test improved<commit_after>package captcha

import (
	"bytes"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	"github.com/macaron-contrib/cache"
	"github.com/tango-contrib/renders"

	"github.com/lunny/tango"
)

type CaptchaAction struct {
	Captcha
	renders.Renderer
}

func (c *CaptchaAction) Get() {
	c.Render("captcha.html", renders.T{
		"captcha": c.CreateHtml(),
	})
}

func (c *CaptchaAction) Post() string {
	if c.Verify() {
		return "true"
	}
	return "false"
}

func TestCaptcha(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	tg := tango.Classic()
	c, _ := cache.NewCacher("memory", cache.Options{
		Interval: 120,
	})
	tg.Use(New(Options{}, c), renders.New())
	tg.Any("/", new(CaptchaAction))

	req, err := http.NewRequest("GET", "http://localhost:3000/", nil)
	if err != nil {
		t.Error(err)
	}

	tg.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
}

/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
	if a != b {
		t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
	}
}

func refute(t *testing.T, a interface{}, b interface{}) {
	if a == b {
		t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
	}
}
<|endoftext|>


<commit_before>package configserver

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/concourse/atc"
)

func (s *Server) SaveConfig(w http.ResponseWriter, r *http.Request) {
	var config atc.Config
	err := json.NewDecoder(r.Body).Decode(&config)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	err = s.validate(config)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err)
		return
	}

	err = s.db.SaveConfig(config)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}
<commit_msg>log when saving config<commit_after>package configserver

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/concourse/atc"
	"github.com/pivotal-golang/lager"
)

func (s *Server) SaveConfig(w http.ResponseWriter, r *http.Request) {
	session := s.logger.Session("set-config")

	var config atc.Config
	err := json.NewDecoder(r.Body).Decode(&config)
	if err != nil {
		session.Error("malformed-json", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	err = s.validate(config)
	if err != nil {
		session.Error("ignoring-invalid-config", err)
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err)
		return
	}

	session.Info("saving", lager.Data{"config": config})

	err = s.db.SaveConfig(config)
	if err != nil {
		session.Error("failed-to-save-config", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	session.Info("saved")

	w.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package types

type ServiceSpec struct {
	Key            string              `json:"key"`
	Label          string              `json:"label"`
	Description    string              `json:"description"`
	Maintainer     string              `json:"maintainer"`
	RequiresVolume bool                `json:"requiresVolume"`
	IsStack        bool                `json:"isStack"`
	IsService      bool                `json:"isService"`
	Dependencies   []ServiceDependency `json:"depends"`
	Config         map[string]string   `json:"config"`
	Image          string              `json:"image"`
	Ports          []Port              `json:"ports"`
	CreatedTime    int                 `json:"createdTime"`
	UpdatedTime    int                 `json:"updateTime"`
	ReadyProbe     ReadyProbe          `json:"readinessProbe"`
	VolumeMounts   []VolumeMount       `json:"volumeMounts"`
	Args           []string            `json:"args"`
	Command        []string            `json:"command"`
	IsPublic       bool                `json:"isPublic"`
}

type Port struct {
	Port     int    `json:"port"`
	Protocol string `json:"protocol"`
}

type VolumeMount struct {
	MountPath string `json:"mountPath"`
	Name      string `json:"name"`
}
type ReadyProbe struct {
	Path         string `json:"path"`
	Port         int    `json:"port"`
	InitialDelay int    `json:"initialDelay"`
	Timeout      int    `json:"timeout"`
}

type ProjectList struct {
	Items []Project `json:"items"`
}

type Project struct {
	Id           string `json:"id"`
	Name         string `json:"name"`
	Description  string `json:"description"`
	Namespace    string `json:"namespace"`
	StorageQuota int    `json:"storageQuota"`
	EmailAddress string `json:"email"`
	Password     string `json:"password"`
}

type ServiceList struct {
	Items []Service `json:"items"`
}

type Service struct {
	Id             string              `json:"id"`
	Key            string              `json:"key"`
	Label          string              `json:"label"`
	Description    string              `json:"description"`
	Maintainer     string              `json:"maintainer"`
	RequiresVolume bool                `json:"requiresVolume"`
	IsStack        bool                `json:"isStack"`
	IsService      bool                `json:"isService"`
	Tags           []string            `json:"tags"`
	Ports          []int               `json:"ports"`
	Dependencies   []ServiceDependency `json:"depends"`
	CreatedTime    int                 `json:"createdTime"`
	UpdatedTime    int                 `json:"updateTime"`
}

type ServiceDependency struct {
	DependencyKey string `json:"key"`
	Required      bool   `json:"required"`
}

type Stack struct {
	Id          string         `json:"id"`
	Key         string         `json:"key"`
	Name        string         `json:"name"`
	Services    []StackService `json:"services"`
	Status      string         `json:"status"`
	CreatedTime int            `json:"createdTime"`
	UpdatedTime int            `json:"updateTime"`
}

type StackService struct {
	Id          string   `json:"id"`
	Stack       string   `json:"stack"`
	Service     string   `json:"service"`
	Status      string   `json:"status"`
	Endpoints   []string `json:"endpoints,omitempty"`
	CreatedTime int      `json:"createdTime"`
	UpdatedTime int      `json:"updateTime"`
}

type Volume struct {
	Id          string `json:"id"`
	Name        string `json:"name"`
	Size        int    `json:"size"`
	SizeUnit    string `json:"sizeUnit"`
	Format      string `json:"format"`
	Attached    string `json:"attached"`
	Service     string `json:"service"`
	Status      string `json:"status"`
	Formatted   bool   `json:"formatted"`
	CreatedTime int    `json:"createdTime"`
	UpdatedTime int    `json:"updateTime"`
}
<commit_msg>Added isstandalone flag to service spec to service spec to service spec to service spec<commit_after>package types

type ServiceSpec struct {
	Key            string              `json:"key"`
	Label          string              `json:"label"`
	Description    string              `json:"description"`
	Maintainer     string              `json:"maintainer"`
	RequiresVolume bool                `json:"requiresVolume"`
	Config         map[string]string   `json:"config"`
	Image          string              `json:"image"`
	Ports          []Port              `json:"ports"`
	CreatedTime    int                 `json:"createdTime"`
	UpdatedTime    int                 `json:"updateTime"`
	ReadyProbe     ReadyProbe          `json:"readinessProbe"`
	VolumeMounts   []VolumeMount       `json:"volumeMounts"`
	Args           []string            `json:"args"`
	Command        []string            `json:"command"`
	Dependencies   []ServiceDependency `json:"depends"`
	IsStack        bool                `json:"isStack"`
	IsService      bool                `json:"isService"`
	IsPublic       bool                `json:"isPublic"`
	IsStandalone   bool                `json:"isStandalone"`
}

type Port struct {
	Port     int    `json:"port"`
	Protocol string `json:"protocol"`
}

type VolumeMount struct {
	MountPath string `json:"mountPath"`
	Name      string `json:"name"`
}
type ReadyProbe struct {
	Path         string `json:"path"`
	Port         int    `json:"port"`
	InitialDelay int    `json:"initialDelay"`
	Timeout      int    `json:"timeout"`
}

type ProjectList struct {
	Items []Project `json:"items"`
}

type Project struct {
	Id           string `json:"id"`
	Name         string `json:"name"`
	Description  string `json:"description"`
	Namespace    string `json:"namespace"`
	StorageQuota int    `json:"storageQuota"`
	EmailAddress string `json:"email"`
	Password     string `json:"password"`
}

type ServiceList struct {
	Items []Service `json:"items"`
}

type Service struct {
	Id             string              `json:"id"`
	Key            string              `json:"key"`
	Label          string              `json:"label"`
	Description    string              `json:"description"`
	Maintainer     string              `json:"maintainer"`
	RequiresVolume bool                `json:"requiresVolume"`
	IsStack        bool                `json:"isStack"`
	IsService      bool                `json:"isService"`
	Tags           []string            `json:"tags"`
	Ports          []int               `json:"ports"`
	Dependencies   []ServiceDependency `json:"depends"`
	CreatedTime    int                 `json:"createdTime"`
	UpdatedTime    int                 `json:"updateTime"`
}

type ServiceDependency struct {
	DependencyKey string `json:"key"`
	Required      bool   `json:"required"`
}

type Stack struct {
	Id          string         `json:"id"`
	Key         string         `json:"key"`
	Name        string         `json:"name"`
	Services    []StackService `json:"services"`
	Status      string         `json:"status"`
	CreatedTime int            `json:"createdTime"`
	UpdatedTime int            `json:"updateTime"`
}

type StackService struct {
	Id          string   `json:"id"`
	Stack       string   `json:"stack"`
	Service     string   `json:"service"`
	Status      string   `json:"status"`
	Endpoints   []string `json:"endpoints,omitempty"`
	CreatedTime int      `json:"createdTime"`
	UpdatedTime int      `json:"updateTime"`
}

type Volume struct {
	Id          string `json:"id"`
	Name        string `json:"name"`
	Size        int    `json:"size"`
	SizeUnit    string `json:"sizeUnit"`
	Format      string `json:"format"`
	Attached    string `json:"attached"`
	Service     string `json:"service"`
	Status      string `json:"status"`
	Formatted   bool   `json:"formatted"`
	CreatedTime int    `json:"createdTime"`
	UpdatedTime int    `json:"updateTime"`
}
<|endoftext|>


<commit_before>package spdy

import (
	"net/http"
)

type httpResponseWriter struct {
	ResponseWriter
}

func (h *httpResponseWriter) Header() http.Header {
	return http.Header(h.ResponseWriter.Header())
}

func ServeFile(wrt ResponseWriter, req *Request, name string) {
	r := spdyRequestToHttpRequest(req)
	w := &httpResponseWriter{wrt}
	http.ServeFile(w, r, name)
}
<commit_msg>Fixed large data sending problem. See details<commit_after>package spdy

import (
  "bytes"
  "errors"
  "fmt"
  "io"
  "mime"
  "mime/multipart"
  "net/http"
  "net/textproto"
  "os"
  "path"
  "path/filepath"
  "strconv"
  "strings"
  "time"
)

// A Dir implements http.FileSystem using the native file
// system restricted to a specific directory tree.
//
// An empty Dir is treated as ".".
type Dir string

func (d Dir) Open(name string) (File, error) {
  if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
    strings.Contains(name, "\x00") {
    return nil, errors.New("http: invalid character in file path")
  }
  dir := string(d)
  if dir == "" {
    dir = "."
  }
  f, err := os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
  if err != nil {
    return nil, err
  }
  return f, nil
}

// A FileSystem implements access to a collection of named files.
// The elements in a file path are separated by slash ('/', U+002F)
// characters, regardless of host operating system convention.
type FileSystem interface {
  Open(name string) (File, error)
}

// A File is returned by a FileSystem's Open method and can be
// served by the FileServer implementation.
type File interface {
  Close() error
  Stat() (os.FileInfo, error)
  Readdir(count int) ([]os.FileInfo, error)
  Read([]byte) (int, error)
  Seek(offset int64, whence int) (int64, error)
}

func dirList(w ResponseWriter, f File) {
  w.Header().Set("Content-Type", "text/html; charset=utf-8")
  fmt.Fprintf(w, "
\n")
  for {
    dirs, err := f.Readdir(100)
    if err != nil || len(dirs) == 0 {
      break
    }
    for _, d := range dirs {
      name := d.Name()
      if d.IsDir() {
        name += "/"
      }
      // TODO htmlescape
      fmt.Fprintf(w, "%s\n", name, name)
    }
  }
  fmt.Fprintf(w, "
\n")
}

// ServeContent replies to the request using the content in the
// provided ReadSeeker.  The main benefit of ServeContent over io.Copy
// is that it handles Range requests properly, sets the MIME type, and
// handles If-Modified-Since requests.
//
// If the response's Content-Type header is not set, ServeContent
// first tries to deduce the type from name's file extension and,
// if that fails, falls back to reading the first block of the content
// and passing it to DetectContentType.
// The name is otherwise unused; in particular it can be empty and is
// never sent in the response.
//
// If modtime is not the zero time, ServeContent includes it in a
// Last-Modified header in the response.  If the request includes an
// If-Modified-Since header, ServeContent uses modtime to decide
// whether the content needs to be sent at all.
//
// The content's Seek method must work: ServeContent uses
// a seek to the end of the content to determine its size.
//
// If the caller has set w's ETag header, ServeContent uses it to
// handle requests using If-Range and If-None-Match.
//
// Note that *os.File implements the io.ReadSeeker interface.
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
  size, err := content.Seek(0, os.SEEK_END)
  if err != nil {
    Error(w, "seeker can't seek", http.StatusInternalServerError)
    return
  }
  _, err = content.Seek(0, os.SEEK_SET)
  if err != nil {
    Error(w, "seeker can't seek", http.StatusInternalServerError)
    return
  }
  serveContent(w, req, name, modtime, size, content)
}

// if name is empty, filename is unknown. (used for mime type, before sniffing)
// if modtime.IsZero(), modtime is unknown.
// content must be seeked to the beginning of the file.
func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, size int64, content io.ReadSeeker) {
  if checkLastModified(w, r, modtime) {
    return
  }
  rangeReq, done := checkETag(w, r)
  if done {
    return
  }

  code := http.StatusOK

  // If Content-Type isn't set, use the file's extension to find it.
  ctype := w.Header().Get("Content-Type")
  if ctype == "" {
    ctype = mime.TypeByExtension(filepath.Ext(name))
    if ctype == "" {
      // read a chunk to decide between utf-8 text and binary
      var buf [1024]byte
      n, _ := io.ReadFull(content, buf[:])
      b := buf[:n]
      ctype = http.DetectContentType(b)
      _, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file
      if err != nil {
        Error(w, "seeker can't seek", http.StatusInternalServerError)
        return
      }
    }
    w.Header().Set("Content-Type", ctype)
  }

  // handle Content-Range header.
  sendSize := size
  var sendContent io.Reader = content
  if size >= 0 {
    ranges, err := parseRange(rangeReq, size)
    if err != nil {
      Error(w, err.Error(), http.StatusRequestedRangeNotSatisfiable)
      return
    }
    if sumRangesSize(ranges) >= size {
      // The total number of bytes in all the ranges
      // is larger than the size of the file by
      // itself, so this is probably an attack, or a
      // dumb client.  Ignore the range request.
      ranges = nil
    }
    switch {
    case len(ranges) == 1:
      // RFC 2616, Section 14.16:
      // "When an HTTP message includes the content of a single
      // range (for example, a response to a request for a
      // single range, or to a request for a set of ranges
      // that overlap without any holes), this content is
      // transmitted with a Content-Range header, and a
      // Content-Length header showing the number of bytes
      // actually transferred.
      // ...
      // A response to a request for a single range MUST NOT
      // be sent using the multipart/byteranges media type."
      ra := ranges[0]
      if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
        Error(w, err.Error(), http.StatusRequestedRangeNotSatisfiable)
        return
      }
      sendSize = ra.length
      code = http.StatusPartialContent
      w.Header().Set("Content-Range", ra.contentRange(size))
    case len(ranges) > 1:
      for _, ra := range ranges {
        if ra.start > size {
          Error(w, err.Error(), http.StatusRequestedRangeNotSatisfiable)
          return
        }
      }
      sendSize = rangesMIMESize(ranges, ctype, size)
      code = http.StatusPartialContent

      pr, pw := io.Pipe()
      mw := multipart.NewWriter(pw)
      w.Header().Set("Content-Type", "multipart/byteranges; boundary="+mw.Boundary())
      sendContent = pr
      defer pr.Close() // cause writing goroutine to fail and exit if CopyN doesn't finish.
      go func() {
        for _, ra := range ranges {
          part, err := mw.CreatePart(ra.mimeHeader(ctype, size))
          if err != nil {
            pw.CloseWithError(err)
            return
          }
          if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
            pw.CloseWithError(err)
            return
          }
          if _, err := io.CopyN(part, content, ra.length); err != nil {
            pw.CloseWithError(err)
            return
          }
        }
        mw.Close()
        pw.Close()
      }()
    }

    w.Header().Set("Accept-Ranges", "bytes")
    if w.Header().Get("Content-Encoding") == "" {
      w.Header().Set("Content-Length", strconv.FormatInt(sendSize, 10))
    }
  }

  w.WriteHeader(code)

  if r.Method != "HEAD" {
    buf := new(bytes.Buffer)
    io.CopyN(buf, sendContent, sendSize)
    io.Copy(w, buf)
  }
}

// modtime is the modification time of the resource to be served, or IsZero().
// return value is whether this request is now complete.
func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
  if modtime.IsZero() {
    return false
  }

  // The Date-Modified header truncates sub-second precision, so
  // use mtime < t+1s instead of mtime <= t to check for unmodified.
  if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
    h := w.Header()
    delete(h, "Content-Type")
    delete(h, "Content-Length")
    w.WriteHeader(http.StatusNotModified)
    return true
  }
  w.Header().Set("Last-Modified", modtime.UTC().Format(TimeFormat))
  return false
}

// checkETag implements If-None-Match and If-Range checks.
// The ETag must have been previously set in the ResponseWriter's headers.
//
// The return value is the effective request "Range" header to use and
// whether this request is now considered done.
func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) {
  etag := w.Header().get("Etag")
  rangeReq = r.Header.get("Range")

  // Invalidate the range request if the entity doesn't match the one
  // the client was expecting.
  // "If-Range: version" means "ignore the Range: header unless version matches the
  // current file."
  // We only support ETag versions.
  // The caller must have set the ETag on the response already.
  if ir := r.Header.get("If-Range"); ir != "" && ir != etag {
    // TODO(bradfitz): handle If-Range requests with Last-Modified
    // times instead of ETags? I'd rather not, at least for
    // now. That seems like a bug/compromise in the RFC 2616, and
    // I've never heard of anybody caring about that (yet).
    rangeReq = ""
  }

  if inm := r.Header.get("If-None-Match"); inm != "" {
    // Must know ETag.
    if etag == "" {
      return rangeReq, false
    }

    // TODO(bradfitz): non-GET/HEAD requests require more work:
    // sending a different status code on matches, and
    // also can't use weak cache validators (those with a "W/
    // prefix).  But most users of ServeContent will be using
    // it on GET or HEAD, so only support those for now.
    if r.Method != "GET" && r.Method != "HEAD" {
      return rangeReq, false
    }

    // TODO(bradfitz): deal with comma-separated or multiple-valued
    // list of If-None-match values.  For now just handle the common
    // case of a single item.
    if inm == etag || inm == "*" {
      h := w.Header()
      delete(h, "Content-Type")
      delete(h, "Content-Length")
      w.WriteHeader(http.StatusNotModified)
      return "", true
    }
  }
  return rangeReq, false
}

// name is '/'-separated, not filepath.Separator.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) {
  const indexPage = "/index.html"

  // redirect .../index.html to .../
  // can't use Redirect() because that would make the path absolute,
  // which would be a problem running under StripPrefix
  if strings.HasSuffix(r.URL.Path, indexPage) {
    localRedirect(w, r, "./")
    return
  }

  f, err := fs.Open(name)
  if err != nil {
    // TODO expose actual error?
    NotFound(w, r)
    return
  }
  defer f.Close()

  d, err1 := f.Stat()
  if err1 != nil {
    // TODO expose actual error?
    NotFound(w, r)
    return
  }

  if redirect {
    // redirect to canonical path: / at end of directory url
    // r.URL.Path always begins with /
    url := r.URL.Path
    if d.IsDir() {
      if url[len(url)-1] != '/' {
        localRedirect(w, r, path.Base(url)+"/")
        return
      }
    } else {
      if url[len(url)-1] == '/' {
        localRedirect(w, r, "../"+path.Base(url))
        return
      }
    }
  }

  // use contents of index.html for directory, if present
  if d.IsDir() {
    index := name + indexPage
    ff, err := fs.Open(index)
    if err == nil {
      defer ff.Close()
      dd, err := ff.Stat()
      if err == nil {
        name = index
        d = dd
        f = ff
      }
    }
  }

  // Still a directory? (we didn't find an index.html file)
  if d.IsDir() {
    if checkLastModified(w, r, d.ModTime()) {
      return
    }
    dirList(w, f)
    return
  }

  // serverContent will check modification time
  serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f)
}

// localRedirect gives a Moved Permanently response.
// It does not convert relative paths to absolute paths like Redirect does.
func localRedirect(w ResponseWriter, r *Request, newPath string) {
  if q := r.URL.RawQuery; q != "" {
    newPath += "?" + q
  }
  w.Header().Set("Location", newPath)
  w.WriteHeader(http.StatusMovedPermanently)
}

// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(w ResponseWriter, r *Request, name string) {
  dir, file := filepath.Split(name)
  serveFile(w, r, Dir(dir), file, false)
}

type fileHandler struct {
  root FileSystem
}

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
//
// To use the operating system's file system implementation,
// use http.Dir:
//
//     http.Handle("/", http.FileServer(http.Dir("/tmp")))
func FileServer(root FileSystem) Handler {
  return &fileHandler{root}
}

func (f *fileHandler) ServeSPDY(w ResponseWriter, r *Request) {
  upath := r.URL.Path
  if !strings.HasPrefix(upath, "/") {
    upath = "/" + upath
    r.URL.Path = upath
  }
  serveFile(w, r, f.root, path.Clean(upath), true)
}

// httpRange specifies the byte range to be sent to the client.
type httpRange struct {
  start, length int64
}

func (r httpRange) contentRange(size int64) string {
  return fmt.Sprintf("bytes %d-%d/%d", r.start, r.start+r.length-1, size)
}

func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader {
  return textproto.MIMEHeader{
    "Content-Range": {r.contentRange(size)},
    "Content-Type":  {contentType},
  }
}

// parseRange parses a Range header string as per RFC 2616.
func parseRange(s string, size int64) ([]httpRange, error) {
  if s == "" {
    return nil, nil // header not present
  }
  const b = "bytes="
  if !strings.HasPrefix(s, b) {
    return nil, errors.New("invalid range")
  }
  var ranges []httpRange
  for _, ra := range strings.Split(s[len(b):], ",") {
    ra = strings.TrimSpace(ra)
    if ra == "" {
      continue
    }
    i := strings.Index(ra, "-")
    if i < 0 {
      return nil, errors.New("invalid range")
    }
    start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:])
    var r httpRange
    if start == "" {
      // If no start is specified, end specifies the
      // range start relative to the end of the file.
      i, err := strconv.ParseInt(end, 10, 64)
      if err != nil {
        return nil, errors.New("invalid range")
      }
      if i > size {
        i = size
      }
      r.start = size - i
      r.length = size - r.start
    } else {
      i, err := strconv.ParseInt(start, 10, 64)
      if err != nil || i > size || i < 0 {
        return nil, errors.New("invalid range")
      }
      r.start = i
      if end == "" {
        // If no end is specified, range extends to end of the file.
        r.length = size - r.start
      } else {
        i, err := strconv.ParseInt(end, 10, 64)
        if err != nil || r.start > i {
          return nil, errors.New("invalid range")
        }
        if i >= size {
          i = size - 1
        }
        r.length = i - r.start + 1
      }
    }
    ranges = append(ranges, r)
  }
  return ranges, nil
}

// countingWriter counts how many bytes have been written to it.
type countingWriter int64

func (w *countingWriter) Write(p []byte) (n int, err error) {
  *w += countingWriter(len(p))
  return len(p), nil
}

// rangesMIMESize returns the nunber of bytes it takes to encode the
// provided ranges as a multipart response.
func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) {
  var w countingWriter
  mw := multipart.NewWriter(&w)
  for _, ra := range ranges {
    mw.CreatePart(ra.mimeHeader(contentType, contentSize))
    encSize += ra.length
  }
  mw.Close()
  encSize += int64(w)
  return
}

func sumRangesSize(ranges []httpRange) (size int64) {
  for _, ra := range ranges {
    size += ra.length
  }
  return
}
<|endoftext|>