Example #1
0
// explorerHashHandler handles GET requests to /explorer/hash/:hash.
func (srv *Server) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// The hash is scanned as an address, because an address can be typecast to
	// all other necessary types, and will correctly decode hashes whether or
	// not they have a checksum.
	hash, err := scanAddress(ps.ByName("hash"))
	if err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Try the hash as a block id.
	block, height, exists := srv.explorer.Block(types.BlockID(hash))
	if exists {
		writeJSON(w, ExplorerHashGET{
			HashType: "blockid",
			Block:    srv.buildExplorerBlock(height, block),
		})
		return
	}

	// Try the hash as a transaction id.
	block, height, exists = srv.explorer.Transaction(types.TransactionID(hash))
	if exists {
		var txn types.Transaction
		for _, t := range block.Transactions {
			if t.ID() == types.TransactionID(hash) {
				txn = t
			}
		}
		writeJSON(w, ExplorerHashGET{
			HashType:    "transactionid",
			Transaction: srv.buildExplorerTransaction(height, block.ID(), txn),
		})
		return
	}

	// Try the hash as a siacoin output id.
	txids := srv.explorer.SiacoinOutputID(types.SiacoinOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := srv.buildTransactionSet(txids)
		writeJSON(w, ExplorerHashGET{
			HashType:     "siacoinoutputid",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a file contract id.
	txids = srv.explorer.FileContractID(types.FileContractID(hash))
	if len(txids) != 0 {
		txns, blocks := srv.buildTransactionSet(txids)
		writeJSON(w, ExplorerHashGET{
			HashType:     "filecontractid",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a siafund output id.
	txids = srv.explorer.SiafundOutputID(types.SiafundOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := srv.buildTransactionSet(txids)
		writeJSON(w, ExplorerHashGET{
			HashType:     "siafundoutputid",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as an unlock hash. Unlock hash is checked last because
	// unlock hashes do not have collision-free guarantees. Someone can create
	// an unlock hash that collides with another object id. They will not be
	// able to use the unlock hash, but they can disrupt the explorer. This is
	// handled by checking the unlock hash last. Anyone intentionally creating
	// a colliding unlock hash (such a collision can only happen if done
	// intentionally) will be unable to find their unlock hash in the
	// blockchain through the explorer hash lookup.
	txids = srv.explorer.UnlockHash(types.UnlockHash(hash))
	if len(txids) != 0 {
		txns, blocks := srv.buildTransactionSet(txids)
		writeJSON(w, ExplorerHashGET{
			HashType:     "unlockhash",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Hash not found, return an error.
	writeError(w, "unrecognized hash used as input to /explorer/hash", http.StatusBadRequest)
}
Example #2
0
// explorerHashHandler handles GET requests to /explorer/hash/:hash.
func (api *API) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Scan the hash as a hash. If that fails, try scanning the hash as an
	// address.
	hash, err := scanHash(ps.ByName("hash"))
	if err != nil {
		addr, err := scanAddress(ps.ByName("hash"))
		if err != nil {
			WriteError(w, Error{err.Error()}, http.StatusBadRequest)
			return
		}
		hash = crypto.Hash(addr)
	}

	// TODO: lookups on the zero hash are too expensive to allow. Need a
	// better way to handle this case.
	if hash == (crypto.Hash{}) {
		WriteError(w, Error{"can't lookup the empty unlock hash"}, http.StatusBadRequest)
		return
	}

	// Try the hash as a block id.
	block, height, exists := api.explorer.Block(types.BlockID(hash))
	if exists {
		WriteJSON(w, ExplorerHashGET{
			HashType: "blockid",
			Block:    api.buildExplorerBlock(height, block),
		})
		return
	}

	// Try the hash as a transaction id.
	block, height, exists = api.explorer.Transaction(types.TransactionID(hash))
	if exists {
		var txn types.Transaction
		for _, t := range block.Transactions {
			if t.ID() == types.TransactionID(hash) {
				txn = t
			}
		}
		WriteJSON(w, ExplorerHashGET{
			HashType:    "transactionid",
			Transaction: api.buildExplorerTransaction(height, block.ID(), txn),
		})
		return
	}

	// Try the hash as a siacoin output id.
	txids := api.explorer.SiacoinOutputID(types.SiacoinOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     "siacoinoutputid",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a file contract id.
	txids = api.explorer.FileContractID(types.FileContractID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     "filecontractid",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a siafund output id.
	txids = api.explorer.SiafundOutputID(types.SiafundOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     "siafundoutputid",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as an unlock hash. Unlock hash is checked last because
	// unlock hashes do not have collision-free guarantees. Someone can create
	// an unlock hash that collides with another object id. They will not be
	// able to use the unlock hash, but they can disrupt the explorer. This is
	// handled by checking the unlock hash last. Anyone intentionally creating
	// a colliding unlock hash (such a collision can only happen if done
	// intentionally) will be unable to find their unlock hash in the
	// blockchain through the explorer hash lookup.
	txids = api.explorer.UnlockHash(types.UnlockHash(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     "unlockhash",
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Hash not found, return an error.
	WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
}
Example #3
0
// consensusSetHash returns the Merkle root of the current state of consensus.
func (cs *ConsensusSet) consensusSetHash() crypto.Hash {
	// Check is too slow to be done on a full node.
	if build.Release == "standard" {
		return crypto.Hash{}
	}

	// Items of interest:
	// 1.	genesis block
	// 3.	current height
	// 4.	current target
	// 5.	current depth
	// 6.	current path + diffs
	// (7)	earliest allowed timestamp of next block
	// 8.	unspent siacoin outputs, sorted by id.
	// 9.	open file contracts, sorted by id.
	// 10.	unspent siafund outputs, sorted by id.
	// 11.	delayed siacoin outputs, sorted by height, then sorted by id.
	// 12.	siafund pool

	// Create a slice of hashes representing all items of interest.
	tree := crypto.NewTree()
	tree.PushObject(cs.blockRoot.Block)
	tree.PushObject(cs.height())
	tree.PushObject(cs.currentProcessedBlock().ChildTarget)
	tree.PushObject(cs.currentProcessedBlock().Depth)
	// tree.PushObject(cs.earliestChildTimestamp(cs.currentProcessedBlock()))

	// Add all the blocks in the current path TODO: along with their diffs.
	for i := 0; i < int(cs.db.pathHeight()); i++ {
		tree.PushObject(cs.db.getPath(types.BlockHeight(i)))
	}

	// Add all of the siacoin outputs, sorted by id.
	var openSiacoinOutputs crypto.HashSlice
	cs.db.forEachSiacoinOutputs(func(scoid types.SiacoinOutputID, sco types.SiacoinOutput) {
		openSiacoinOutputs = append(openSiacoinOutputs, crypto.Hash(scoid))
	})
	sort.Sort(openSiacoinOutputs)
	for _, id := range openSiacoinOutputs {
		sco := cs.db.getSiacoinOutputs(types.SiacoinOutputID(id))
		tree.PushObject(id)
		tree.PushObject(sco)
	}

	// Add all of the file contracts, sorted by id.
	var openFileContracts crypto.HashSlice
	cs.db.forEachFileContracts(func(fcid types.FileContractID, fc types.FileContract) {
		openFileContracts = append(openFileContracts, crypto.Hash(fcid))
	})
	sort.Sort(openFileContracts)
	for _, id := range openFileContracts {
		// Sanity Check - file contract should exist.
		fc := cs.db.getFileContracts(types.FileContractID(id))
		tree.PushObject(id)
		tree.PushObject(fc)
	}

	// Add all of the siafund outputs, sorted by id.
	var openSiafundOutputs crypto.HashSlice
	cs.db.forEachSiafundOutputs(func(sfoid types.SiafundOutputID, sfo types.SiafundOutput) {
		openSiafundOutputs = append(openSiafundOutputs, crypto.Hash(sfoid))
	})
	sort.Sort(openSiafundOutputs)
	for _, id := range openSiafundOutputs {
		sco := cs.db.getSiafundOutputs(types.SiafundOutputID(id))
		tree.PushObject(id)
		tree.PushObject(sco)
	}

	// Get the set of delayed siacoin outputs, sorted by maturity height then
	// sorted by id and add them.
	for i := cs.height() + 1; i <= cs.height()+types.MaturityDelay; i++ {
		var delayedSiacoinOutputs crypto.HashSlice
		if cs.db.inDelayedSiacoinOutputs(i) {
			cs.db.forEachDelayedSiacoinOutputsHeight(i, func(id types.SiacoinOutputID, output types.SiacoinOutput) {
				delayedSiacoinOutputs = append(delayedSiacoinOutputs, crypto.Hash(id))
			})
		}
		sort.Sort(delayedSiacoinOutputs)
		for _, delayedSiacoinOutputID := range delayedSiacoinOutputs {
			delayedSiacoinOutput := cs.db.getDelayedSiacoinOutputs(i, types.SiacoinOutputID(delayedSiacoinOutputID))
			tree.PushObject(delayedSiacoinOutput)
			tree.PushObject(delayedSiacoinOutputID)
		}
	}

	// Add the siafund pool
	var siafundPool types.Currency
	err := cs.db.Update(func(tx *bolt.Tx) error {
		siafundPool = getSiafundPool(tx)
		return nil
	})
	if err != nil {
		panic(err)
	}
	tree.PushObject(siafundPool)

	return tree.Root()
}
Example #4
0
// consensusSetHash returns the Merkle root of the current state of consensus.
func (cs *State) consensusSetHash() crypto.Hash {
	// Items of interest:
	// 1.	genesis block
	// 3.	current height
	// 4.	current target
	// 5.	current depth
	// 6.	earliest allowed timestamp of next block
	// 7.	current path, ordered by height.
	// 8.	unspent siacoin outputs, sorted by id.
	// 9.	open file contracts, sorted by id.
	// 10.	unspent siafund outputs, sorted by id.
	// 11.	delayed siacoin outputs, sorted by height, then sorted by id.
	// TODO: Add the diff set ?

	// Create a slice of hashes representing all items of interest.
	tree := crypto.NewTree()
	tree.PushObject(cs.blockRoot.block)
	tree.PushObject(cs.height())
	tree.PushObject(cs.currentBlockNode().childTarget)
	tree.PushObject(cs.currentBlockNode().depth)
	tree.PushObject(cs.currentBlockNode().earliestChildTimestamp())

	// Add all the blocks in the current path.
	for i := 0; i < len(cs.currentPath); i++ {
		tree.PushObject(cs.currentPath[types.BlockHeight(i)])
	}

	// Add all of the siacoin outputs, sorted by id.
	var openSiacoinOutputs crypto.HashSlice
	for siacoinOutputID, _ := range cs.siacoinOutputs {
		openSiacoinOutputs = append(openSiacoinOutputs, crypto.Hash(siacoinOutputID))
	}
	sort.Sort(openSiacoinOutputs)
	for _, id := range openSiacoinOutputs {
		sco, exists := cs.siacoinOutputs[types.SiacoinOutputID(id)]
		if !exists {
			panic("trying to push nonexistent siacoin output")
		}
		tree.PushObject(id)
		tree.PushObject(sco)
	}

	// Add all of the file contracts, sorted by id.
	var openFileContracts crypto.HashSlice
	for fileContractID, _ := range cs.fileContracts {
		openFileContracts = append(openFileContracts, crypto.Hash(fileContractID))
	}
	sort.Sort(openFileContracts)
	for _, id := range openFileContracts {
		// Sanity Check - file contract should exist.
		fc, exists := cs.fileContracts[types.FileContractID(id)]
		if !exists {
			panic("trying to push a nonexistent file contract")
		}
		tree.PushObject(id)
		tree.PushObject(fc)
	}

	// Add all of the siafund outputs, sorted by id.
	var openSiafundOutputs crypto.HashSlice
	for siafundOutputID, _ := range cs.siafundOutputs {
		openSiafundOutputs = append(openSiafundOutputs, crypto.Hash(siafundOutputID))
	}
	sort.Sort(openSiafundOutputs)
	for _, id := range openSiafundOutputs {
		sco, exists := cs.siafundOutputs[types.SiafundOutputID(id)]
		if !exists {
			panic("trying to push nonexistent siafund output")
		}
		tree.PushObject(id)
		tree.PushObject(sco)
	}

	// Get the set of delayed siacoin outputs, sorted by maturity height then
	// sorted by id and add them.
	for i := cs.height() + 1; i <= cs.height()+types.MaturityDelay; i++ {
		var delayedSiacoinOutputs crypto.HashSlice
		for id := range cs.delayedSiacoinOutputs[i] {
			delayedSiacoinOutputs = append(delayedSiacoinOutputs, crypto.Hash(id))
		}
		sort.Sort(delayedSiacoinOutputs)

		for _, delayedSiacoinOutputID := range delayedSiacoinOutputs {
			delayedSiacoinOutput, exists := cs.delayedSiacoinOutputs[i][types.SiacoinOutputID(delayedSiacoinOutputID)]
			if !exists {
				panic("trying to push nonexistent delayed siacoin output")
			}
			tree.PushObject(delayedSiacoinOutput)
			tree.PushObject(delayedSiacoinOutputID)
		}
	}

	return tree.Root()
}